[iOS]ハイパーリンク | Cocoa練習帳

[iOS]ハイパーリンク

Twitterの詳細画面のように。テキスト中のハイパーリンクの文字色等を変更して、選ばれたら、そのURLをブラウザで開くようにするにはどうすればいいのか?詳しく調べていないが、一つはテキストをHTML化して、UIWebViewで表示するという案を思い浮かんだ。


OS Xの場合は、Technical Q&A QA1487「Embedding Hyperlinks in NSTextField and NSTextView」で説明されている。


iOSの場合、NSString関連のメソッドに差がある為、OS Xと同様な方法が適用できるのか分からない為、試行錯誤してみた。




そもそも、OS Xの場合は、どうするのか確認してみる。以下は、QA1487のコードそのままだ。




/* NSAttributedStringを拡張するカテゴリとして実装 */
@interface NSAttributedString (Hyperlink)
    +(id)hyperlinkFromString:(NSString*)inString withURL:(NSURL*)aURL;
@end
 
@implementation NSAttributedString (Hyperlink)
/*
 * NSTextFieldの場合、属性と選択を可能にしておく必要がある。
 * [テキストフィールド setAllowsEditingTextAttributes:YES];
 * [テキストフィールド setSelectable:YES]
 */
+(id)hyperlinkFromString:(NSString*)inString withURL:(NSURL*)aURL
{
    NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString: inString];
    NSRange range = NSMakeRange(0, [attrString length]);
 
    [attrString beginEditing];
 
    /* ハイパーリンクを設定 */
    [attrString addAttribute:NSLinkAttributeName value:[aURL absoluteString] range:range];
 
    // make the text appear in blue
    [attrString addAttribute:NSForegroundColorAttributeName value:[NSColor blueColor] range:range];
 
    // next make the text appear with an underline
    [attrString addAttribute:
            NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:NSSingleUnderlineStyle] range:range];
 
    [attrString endEditing];
 
    return [attrString autorelease];
}
@end



iOSの場合になるが、iOS 3.2以降、NSRegularExpressionSearchで正規表現が扱えるようになったので、これでURLを見つけて、上記のような方法でハイパーリンクを設定すればいいのか考えてた。


が、iOSの場合、以外と安易な解決策を見つけてしまった。




iOS 3以降、UIDataDetectorTypesのUIDataDetectorTypeLinkを指定すれば、リンクを作成できる。試してみよう。




self.textView.editable = NO;
self.textView.dataDetectorTypes = UIDataDetectorTypeLink;
self.textView.text = @"This is a demonstration.\nhttp://www.bitz.co.jp/\nThank you.";



あっけなかった。簡単だ。




ただ、この場合は、URLを開く流れを制御できない。例えば、ちょっとしたアプリケーション固有の情報を追加するとか、Safariでなく自身のUIWebViewに表示するとか。




URLを開くのは、UIApplicationの- openURL:メソッドの呼び出しによってなので、このメソッドを捕まえて、差し替えれば良いのでは?




- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.openURLMethod = class_getInstanceMethod([UIApplication class], @selector(openURL:));
    Method  myOpenURLMethod = class_getInstanceMethod([AppDelegate class], @selector(myOpenURL:));
    method_exchangeImplementations(self.openURLMethod,  myOpenURLMethod);
    return YES;
}
 
- (BOOL)myOpenURL:(NSURL *)url
{
    NSLog(@"%s, url(%@)", __func__, url);
    return YES;
}



うまく捕まえる事はできた。ただ、どうすれば、オリジナルの- openURL:メソッドを呼んでいいのか分からなかったり、出来ても、変数のアクセス等、素直に実装できそうにないので、これは諦める事にした。




結局、UIApplicationのサブクラスを作成して、- opneURL:をオーバーライドすることにした。




@interface MyApplication : UIApplication
@end
    ....
@implementation MyApplication
- (BOOL)openURL:(NSURL *)url
{
    NSLog(@"%s, url(%@)", __func__, url);
    return [super openURL:url];
}
@end



main.mを変更して、この独自のサブクラスが呼ばれるようにする。




int main(int argc, char *argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv,
            NSStringFromClass([MyApplication class]),
            NSStringFromClass([AppDelegate class]));
    }
}



UIApplicationMainの第三引数がnilになっていたと思うが、そこにMyApplcationを設定する。




ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/Hyperlinks - GitHub


関連情報
Technical Q&A QA1487

Embedding Hyperlinks in NSTextField and NSTextView

Technical Q&A QA1629

Launching the App Store from an iPhone application

Text, Web, and Editing Programming Guide for iOS

Developerサイトの情報。