iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど
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を設定する。