Mettre une url dans IB

muqaddarmuqaddar Administrateur
09:26 modifié dans API AppKit #1
Salut,

Je cherche à  faire un lien sur une URL dans mon interface. Et donc qu'elle soit cliquable et que ça la passe en bleu. Et quand on clique que ça lançe safari avec l'url.

IB, Xcode ou un contrôle AppleScript ?

Merci
«13

Réponses

  • BruBru Membre
    septembre 2004 modifié #2
    C'est assez simple si tu passes par les NSTextView...

    Dans ce cas, il suffit de modifier l'attribut de la chaà®ne de caractère qui représente ton URL en positionnant l'attribut NSLinkAttributeName.

    Ensuite, il faut implémenter un delegate de ta NSTextView qui sera appelé quand tu cliqueras sur ton URL.
    Le delegate est :
    - (BOOL)textView:(NSTextView *)aTextView clickedOnLink:(id)link atIndex:(unsigned)charIndex
    


    Dans le delegate, tu récupères ton URL pour ensuite "l'ouvrir" via la méthode openUrl de la classe NSWorkspace.

    Il est aussi possible de stocker dans la valeur de l'attribut NSLinkAttributeName un objet NSUrl contenant ton URL. Dans ce cas, je crois que le delegate n'est pas nécessaire, le système ouvrant de lui même l'URL avec l'application par défaut (mais je n'ai jamais testé cette possibilité).

    A noter que peut-être dans la dernière version de IB, tu peux modifier l'attribut NSLinkAttributeName dans le texte de ta NSView. Sinon, il faut le faire par programme.

    .
  • muqaddarmuqaddar Administrateur
    09:26 modifié #3
    Je voulais faire un truc du genre :

    -(void)urlAttributes {
    NSString *chaineLue = [urlClicable value];
    NSMutableAttributedString *url;
    url = [[NSMutableAttributedString alloc] initWithString: chaineLue];
    [url addAttribute: NSLinkAttributeName value:(id)value range:(NSRange)aRange];
    [urlClicable setAttributedStringValue:url];
    }

    Mais je ne sais que mettre ds la 4è ligne.... [url addAttribute....[/url]
  • muqaddarmuqaddar Administrateur
    09:26 modifié #4
    Doc Apple :
    The link attribute specifies an arbitrary object that is passed to the NSTextView method clickedOnLink:atIndex: when the user clicks in the text range associated with the NSLinkAttributeName attribute. The text view?s delegate object can implement textView:clickedOnLink:atIndex: or textView:clickedOnLink: to process the link object. Otherwise, the default implementation checks whether the link object is an NSURL and, if so, opens it in the URL?s default application.

    Moi, c'est un NSTextField aussi...
  • ClicCoolClicCool Membre
    09:26 modifié #5
    Si tu dois utiliser un NSTextFied, assures tois qu'il gère les attributs (allowsEditingTextAttributes et setAllowsEditingTextAttributes)

    Pour pas compliquer, teste peut-être dans un premier temps dans un textView qui gère tout ça très bien ... ;)
  • ClicCoolClicCool Membre
    09:26 modifié #6
    dans 1094565914:

    Mais je ne sais que mettre ds la 4è ligne.... [url addAttribute....

    Je crois que tu dois passer dans value un NSURL que tu dois pouvoir obtenir avec:
    [NSURL URLWithString: @"http://www.objective-cocoa.org"];
    
  • muqaddarmuqaddar Administrateur
    09:26 modifié #7
    J'ai peut-être une solution avec un transformeur :

    #import &quot;SetURLTransformer.h&quot;<br /><br />@implementation SetURLTransformer<br /><br />+ (BOOL)allowsreverseTransformation {<br />      return NO;<br />}<br /><br />+ (Class)transformedValueClass {<br />     return [NSString class];<br />}<br /><br />- (id)transformedValue:(id)texteURL<br />{   <br />  NSString *monURL = [texteURL stringValue];<br />        return [NSURL URLWithString: monURL];<br />}<br /><br />@end
    


    texteURL nous vient du binding sur le NSTextField, seulement, je me fais jeter :

    *** Uncaught exception: <NSInvalidArgumentException> *** -[NSConstantString stringValue]: selector not recognized

    Je peux pas faire de stringValue apparemment, mais pourquoi ?
  • ClicCoolClicCool Membre
    septembre 2004 modifié #8
    Salut Osxitan ;)
    J'suis encore au boulot et j'en proffite pour te répondre.

    Décidemment les NSTransformers ont la cote en ce momment ;)

    Pour ton pb, quelle est la nature de l'objet texteURL passé à  ton transformer ?
    Si c'est bien comme ton message d'erreur le dit, c'est une NSString déja.
    NSString n'a pas de méthode stringValue ;D
    essaies donc tout simplement ça:
    return [NSURL URLWithString: texteURL ];
    


    @+

    [EDIT] et bien sur enlèves la ligne:
    NSString *monURL = [texteURL stringValue];<br />
    
    8)
  • muqaddarmuqaddar Administrateur
    09:26 modifié #9
    Effectivement ça marche... plus d'erreurs mais rien ne se passe sur le texte...:(
    ça doit pas être la bonne méthode pour créer un hyperlien.
  • muqaddarmuqaddar Administrateur
    09:26 modifié #10
    J'ai vu ça aussi :
    NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@&quot;http://www.foo.com&quot;;

    ça lance l'url tout de suite... dommage
  • ClicCoolClicCool Membre
    09:26 modifié #11
    Et bien, il est probable que même si ton transformer passe bien une URL, le NSTextField n'en extrait qu'une NString en ne lui demandant que sa string value.
    Autrement dit ton transformer a converti une chaine en URL que le TextField converti dans la foulée en NSString !!! ;D

    L'hyperlien ne s'affichera qu'avec une NSAttibutedString.

    Essaies la même chose avec une NSTextView, si ça marche, on réfléchira à  comment obliger un NSTextField a agir de même. :)
  • muqaddarmuqaddar Administrateur
    09:26 modifié #12
    Ouh là , j'ai eu que des bugs avec NSTextView, ça m'a fait planté IB... :( pas glops
  • ClicCoolClicCool Membre
    09:26 modifié #13
    ???
  • muqaddarmuqaddar Administrateur
    septembre 2004 modifié #14
    ça m'a créé des "inconsistancies" un truc du genre... j'avais juste binder data pourtant... :(

    Par ailleurs, je comprends pas que ce soit si dur de transformer uen chaà®ne en url, qd je pense à  tous les sharewares qui ont leur signature et leur site dans "A propos"...  :crackboom:-

    L'erreur qd je binde le data :

    2004-09-14 18:49:57.814 webmasterMemory[13560] Cannot create NSData from object <_NSControllerObjectProxy: 0x361ae0> of class _NSControllerObjectProxy
  • ClicCoolClicCool Membre
    09:26 modifié #15
    Ah ben oui,
    une NSTextView attend une NSData ;D

    Il faut que ton transformer créer l'Url, la transforme en NSAttribute String qu'elle transforme ensuite en NSData...

    A la réflexion, c'est vrai que c'est assez coton, surtout si tu veux insérer ton lien dans un texte existant ...  :-\
  • BruBru Membre
    09:26 modifié #16
    J'ai enfin retrouvé le bout de code que j'utilisais à  l'époque...

    Dans IB, j'utilise une textview (celui qui contient le texte "lorem ipsum dolor..." dans la palette de IB). Dans l'exemple, je mets le texte "Merci au site Objective-Cocoa.". Ensuite je lie cette textview à  un outlet (dans l'exemple, cet outlet se nomme tv).

    Enfin dans le awakeFromNib du controleur, je fais ça :
    <br />- (void)awakeFromNib<br />{<br />    [[tv textStorage]<br />        addAttributes:[NSDictionary dictionaryWithObjectsAndKeys:<br />                                   [NSURL URLWithString:@&quot;http://www.objective-cocoa.org&quot;],<br />                                   NSLinkAttributeName,<br />                                   NULL]<br />        range:NSMakeRange(14, 15)];<br />}<br />
    


    .
  • muqaddarmuqaddar Administrateur
    09:26 modifié #17
    Merci Bru, ça semble marcher.

    J'ai 2 questions néanmoins.

    1) on peut mettre quoi comme valeurs dans NSMakeRange pour que ça crée l'url sur tous les caractères ou sur aucun (voilà  le genre de chose qu'on trouve pas ds la doc apple... ?)

    2) mon outlet tv prendra une valeur qui provient d'un dico : mondico.mavaleur or un NSTextView gère les NSData et uniquement ça non ? Donc, comment lui faire avaler mon NSData, qui plus est en binding, et que ça marche aussi ? Si la fonction est ds le awakeFromNib, ça marchera aussi avec une valeur non fixe à  la place de @"http://www.objective-cocoa.org"; ?
  • muqaddarmuqaddar Administrateur
    09:26 modifié #18
    J'ai trouvé une partie de mon bonheur :

    http://www.cocoadev.com/index.pl?ClickableUrlInTextView
  • BruBru Membre
    09:26 modifié #19
    dans 1095674064:
    1) on peut mettre quoi comme valeurs dans NSMakeRange pour que ça crée l'url sur tous les caractères

    Réponse : NSMakeRange(0, [[tv textStorage] length])

    ou sur aucun (voilà  le genre de chose qu'on trouve pas ds la doc apple... ?)

    Je ne vois pas à  quoi ça peut servir...

    or un NSTextView gère les NSData et uniquement ça non ?

    Une NSTextView ne gère que l'affichage d'un texte... Le texte lui-même est stocké dans une NSTextStorage.

    Donc, comment lui faire avaler mon NSData

    Ton texte est dans une NSData ? alors essaie quelque chose comme ça :
    [tt]
    NSString *s;
    NSAttributedString *as;

    s=[NSString alloc] initWithData:ma_data encoding:NSUTF8StringEncoding autorelease];
    as=[[[NSAttributedString alloc] initWithString:s] autorelease];
    [[tv textStorage] setAttributedString:as];
    [/tt]

    ça marchera aussi avec une valeur non fixe à  la place de @"http://www.objective-cocoa.org";

    Oui, tu peux mettre ce que tu veux comme url du moment que ce soit un objet NSString qui contient l'url.


    J'ai pas compris toutes les questions, aussi n'hésite pas à  détailler ce que tu veux vraiment faire...

    .
  • muqaddarmuqaddar Administrateur
    09:26 modifié #20
    Ah, en fait, je ne connaissais pas NSTextStorage, je croyais que NSTextView n'acceptait que du NSData... :(

    Mon pb général est de placer ton code dans une méthode qui serait appelée quand je tape mon texte dans le NSTextView, et que ça crée le lien avec la bonne url (donc celle du texte tapé au fur et à  mesure).

    Merci.
  • mpergandmpergand Membre
    09:26 modifié #21
    La première idée que me vient, c'est d'utiliser une méthode déléguée comme textDidEndEditing ou similaire et de tester si c'est une url valide (contient http ?)

    Plus simple serait de faire un menu contextuel permettant de créer une url à  partir de la sélection.
  • ClicCoolClicCool Membre
    09:26 modifié #22
    Pour ma part je trouve très bien ton idée d'un délégate, plus fluide à  l'usage qu'un menu contextuel.

    la méthode textDidEndEditing férifie s'il s'agit d'une URL valide et, si c'est la cas met en place le lien.  :)
  • BruBru Membre
    09:26 modifié #23
    dans 1095680161:

    Ah, en fait, je ne connaissais pas NSTextStorage, je croyais que NSTextView n'acceptait que du NSData... :(

    Mon pb général est de placer ton code dans une méthode qui serait appelée quand je tape mon texte dans le NSTextView, et que ça crée le lien avec la bonne url (donc celle du texte tapé au fur et à  mesure).

    Merci.


    Dans ce cas, le code dans le lien que tu as fourni vers cocoadev est exactement ce qu'il te faut. Et comme mpergand le suggère, tu appelles la méthode de cocoadev dans le délégué qui va bien.

    .
  • muqaddarmuqaddar Administrateur
    septembre 2004 modifié #24
    Merci les gars.

    J'ai d'abord défini mon AppControlleur comme delegate de File's owner. Ensuite, j'ai défini mon AppControlleur comme delegate de mon textView.

    Et j'ai ajouté ceci dans mon contrôleur :

    ///////////////<br />//HILITES URL//<br />///////////////<br /><br />- (void)textDidEndEditing:(NSNotification *)aNotification<br />{<br />    [self hiliteAndActivateURLs:websiteURL];<br />}<br /><br /><br />- (void)hiliteAndActivateURLs:(NSTextView*)textView <br />{<br />     NSTextStorage* textStorage=[textView textStorage];<br />        NSString* string=[textStorage string];<br />    NSRange searchRange=NSMakeRange(0, [string length]);<br />      NSRange foundRange;<br /><br /> [textStorage beginEditing];<br />       do {<br />              //We assume that all URLs start with http://<br />              foundRange=[string rangeOfString:@&quot;http://&quot; options:0 range:searchRange];<br />               //Did we find a URL?<br />              if (foundRange.length &gt; 0) {<br />                   NSURL* theURL;<br />                    NSDictionary* linkAttributes;<br />                     NSRange endOfURLRange;<br /><br />                      //Restrict the searchRange so that it won&#39;t find the same string again<br />                       searchRange.location = foundRange.location + foundRange.length;<br />                   searchRange.length = [string length] - searchRange.location;<br /><br />                        //We assume the URL ends with whitespace<br />                  endOfURLRange=[string rangeOfCharacterFromSet:<br />                    [NSCharacterSet whitespaceAndNewlineCharacterSet]<br />                 options:0 range:searchRange];<br /><br />                       //The URL could also end at the end of the text.  The next line fixes it in case it does<br />                  if (endOfURLRange.length==0)  endOfURLRange.location=[string length]-1;<br /><br />                     //Set foundRange&#39;s length to the length of the URL<br />                   foundRange.length = endOfURLRange.location-foundRange.location+1;<br /><br />                   //grab the URL from the text<br />                      theURL=[NSURL URLWithString:[string substringWithRange:foundRange]];<br /><br />                        //Make the link attributes<br />                        linkAttributes= [NSDictionary dictionaryWithObjectsAndKeys: theURL, NSLinkAttributeName,<br />                                                  [NSNumber numberWithInt:NSSingleUnderlineStyle], NSUnderlineStyleAttributeName,<br />                                                   [NSColor blueColor], NSForegroundColorAttributeName,<br />                                                      NULL];<br /><br />                      //Finally, apply those attributes to the URL in the text<br />                  [textStorage addAttributes:linkAttributes range:foundRange];<br />              }<br /> } while (foundRange.length!=0); //repeat the do block until it no longer finds anything<br />   [textStorage endEditing];<br />}<br />
    


    Mon texte passe en lien clicable uniquement si http:// est tapé et quand je textView n'est plus FirstResponder. Y'a un petit soucis si j'efface mon texte ensuite et qu'il reste "htt" par exemple, ça reste une url...

    Sinon, mon autre pb est de binder convenablement ce textView puisque tout ce qui marche dans IB pour textField ne marche pas pour textView. Je demande donc au bindingologue, si je dois créer un transformer pour textStorage ou autre ?

    PS : IB vient de me planter 4 fois et a fini par corrompre mon nib qui ne faisait plus que 8 Ko au lieu de 64... J'ai repris l'ancien nib... :(
  • ClicCoolClicCool Membre
    09:26 modifié #25
    En théorie ta TextView peut-être bindée de façon naturelle si c'est pour la lier à  une clef d'un élément de ton NSArray.
    Au moment de sa sauvegarde le NSArray recevra un NSData en provenance du textStorage et le tout sera sauvegardé.
    De même si c'est pour en sauvegarder le contenu dans les préférences via le NSUserDefaultsController.

    Par contre si tu dois lire directement le contenu sur disque, il te faudra un NSUnarchiveFromData.

    Néanmoins, sii t'as un pb, n'hésites pas, je m'y pencherais de plus près  :)
  • muqaddarmuqaddar Administrateur
    09:26 modifié #26
    Euh...
    La textView devrait être liée à  une clé de dictionnaire et non de tableau : dicWebsites.name en l'occurence.
    En bindant directement, il em dit que ma classe n'est plus KVCompliant.
  • ClicCoolClicCool Membre
    09:26 modifié #27
    Quelle classe n'est pas KVC Compliante ? la NSTextView ?
  • muqaddarmuqaddar Administrateur
    09:26 modifié #28
    Argh ! Impossible de binder ce textView sans que IB me plante, au mieux il ne l'enregistre pas dans l'interface !!! Je vais passer par du code pour binder.
  • muqaddarmuqaddar Administrateur
    09:26 modifié #29
    Bon en bindant "on the fly", ça tourne :

    <br />//outlet url<br />[websiteURL bind: @&quot;value&quot; toObject: websitesController withKeyPath:@&quot;selection.dicWebsites.url&quot; options:nil];<br />[websiteURL bind: @&quot;editable&quot; toObject: editableController withKeyPath:@&quot;selection.websiteEditable&quot; options:nil];<br />
    


    :-) :rose!:

  • ClicCoolClicCool Membre
    09:26 modifié #30
  • muqaddarmuqaddar Administrateur
    09:26 modifié #31
    J'ai remarqué plusieurs choses.

    - Ne pas oublier d'enlever le vérificateur d'orthographe dans IB :-)
    - Quand je relance le prog, l'url n'est plus clicable, il faut aller ds son champ et cliquer ailleurs, normal, avec la méthode textDidEndEditing... j'ai pensé faire un appel à  la méthode qui crée l'url ds awakeFromNib, mais ça ne marche pas terrible.

    [self hiliteAndActivateURLs:websiteURL];

    Une suggestion ?
Connectez-vous ou Inscrivez-vous pour répondre.