環境: Swift3
iOS10
UILabel ではなく UITextView の attributedText に NSLinkAttributeName をセットすることで簡単にできた。
クリック時にデフォルトでは Safari が起動して設定したURLのページが表示された。
クリック時の挙動を変更したい場合は UITextViewDelegate の
func textView(UITextView, shouldInteractWith: URL, in: NSRange, interaction: UITextItemInteraction)
(iOS 10から)
または
func textView(UITextView, shouldInteractWith: URL, in: NSRange)
(iOS7,8,9はこちら。iOS 10でDeprecated)
を実装して制御する。
今回は SFSafariViewController で開いてみた。
以下が実装例。
import UIKit import SafariServices class ViewController: UIViewController, UITextViewDelegate { @IBOutlet weak var textView: UITextView! override func viewDidLoad() { super.viewDidLoad() setupTextView() } func setupTextView() { let text = "詳細はこちらをご覧ください" textView.delegate = self textView.isSelectable = true textView.isEditable = false textView.textContainer.lineFragmentPadding = 0 textView.textContainerInset = .zero textView.isScrollEnabled = false // これが必要な理由は後述 let attributedString = NSMutableAttributedString(string: text) let range = NSString(string: text).range(of: "こちら") attributedString.addAttribute( NSLinkAttributeName, value: "https://www.google.co.jp/", range: range) textView.attributedText = attributedString textView.linkTextAttributes = [NSForegroundColorAttributeName: UIColor.red] } // MARK: - UITextViewDelegate // この Delegate の実装しない場合はデフォルトで URL を Safari で開く。 func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { // UIApplication.shared.open(URL) let controller = SFSafariViewController(url: URL) self.present(controller, animated: true) return false } }
これでテキストの一部をハイパーリンク化できる。
UITextView のレイアウト制約について
ここからはハイパーリンク化とは関係のない UITextView の挙動でちょっとハマった話。
上記例での UITextView のレイアウトは Storyboard の AutoLayout を使って行っていたのだが、UITextView の高さの制約を指定しないと制約エラーになった。
デバイスによって横幅が変わるので、高さの制約はテキストの長さに応じて自動的に設定して欲しいところ。
UILabel であればテキストが長くて複数行に渡る場合でも intrinsicContentSize が自動的に設定されるので AutoLayout で高さの制約は指定しなくてもよい。
UITextView も同じような挙動を期待していたが、 intrinsicContentSize を見てみたところ、-1(UIViewNoIntrinsicMetric)でサイズ不定となっていたため、高さの制約指定が必要であった。
ただし、これは UITextView の isScrollEnabled プロパティを false とすること(デフォルトはtrue)で intrinsicContentSize が自動設定され、高さの制約指定は不要になることがわかった。
つまり、UITextView は UIScrollView のサブクラスであるため、isScrollEnabled=true
の場合は、枠となる矩形のサイズと、内部のスクロールさせる矩形のサイズは違うため、枠となる矩形側の高さの制約指定は必要となり、isScrollEnabled=false
にすればスクロール不要となるので UILabel と同様に高さが自動的に設定されるのだと思われる。
UITextView の下側のテキストが切れてしまう件
これも余談だが、UITextView に複数行に渡る長いテキストが設定されている場合に下側のテキストが切れてすべて表示されないことがあった。
これはデフォルトのシステムフォントを使っている場合には起きないのだが、ヒラギノフォントを使った場合に発生した。
解決法としては
textView.isScrollEnabled = false textView.isScrollEnabled = true
というように一旦、isScrollEnabled = false
すればよいらしい。
たしかにこれで解消された。
追記
Swift 5バージョン
xyk.hatenablog.com