
こんにちは、22新卒の大石です。
今回は、実務で実装した際に調査したことについて紹介します。
新卒1年目からスマホアプリの実装のタスクを任せられ、とても楽しく働いています!!
UITextViewに含まれるURLをリンク化する
本来、UITextViewに含まれるURLをリンク化するのはとても簡単です。
以下の画像の通り、BehaviorのSelectableをチェック、DataDetectorのLinkをチェックします。
この設定だけで、含まれるURLは自動で検知されリンク化されるのですが、
この方法には大きな欠点があります。
それは、メールアドレスもリンク化されてしまうのです。
さらに、リンク化されたメールアドレスをタップするとアプリがクラッシュします。
厳密にURLのみをリンク化したい場合、自前でリンク化のロジックを用意する必要があります。
本記事は、その方法の紹介になります。
URLのリンク化
完成形
実装
今回の実装部分全体です。
import UIKit | |
import SafariServices | |
class ViewController: UIViewController { | |
@IBOutlet weak var textView: UITextView! | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
textView.delegate = self | |
self.setupHyperlink(text: "以下のリンクからアクセス\nhttps://www.yahoo.co.jp/") | |
} | |
// 1.TextViewに表示するテキストに含まれるURLを抽出 | |
private func detectUrls(text: String) -> [String] { | |
guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else { | |
return [] | |
} | |
let enableLinkTuples = detector.matches(in: text, range: NSRange(location: 0, length: text.count)) | |
var detectedUrls = enableLinkTuples.map { checkingResult -> String in | |
return (text as NSString).substring(with: checkingResult.range) | |
} | |
detectedUrls.removeAll(where: {!($0.contains("http://") || $0.contains("https://"))}) | |
return detectedUrls | |
} | |
private func setupHyperlink(text: String) { | |
let urls = self.detectUrls(text: text) | |
if urls.isEmpty { | |
self.textView.text = text | |
return | |
} | |
// 2.TextViewに表示するテキストのNSAttributedStringインスタンスを作成 | |
let attributedString = NSMutableAttributedString(string: text) | |
for url in urls { | |
// 3.テキスト内のURLをNSRangeで指定してリンク化 | |
let range = NSString(string: text).range(of: url) | |
attributedString.addAttribute(NSAttributedString.Key.link, value: url, range: range) | |
} | |
// 4.TextView.attributedTextに代入 | |
self.textView.attributedText = attributedString | |
self.textView.font = UIFont.boldSystemFont(ofSize: 20) | |
self.textView.linkTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.systemBlue] | |
} | |
} | |
extension ViewController: UITextViewDelegate { | |
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { | |
let controller = SFSafariViewController(url: URL) | |
self.present(controller, animated: true) | |
return false | |
} | |
} |
コメントの番号順に進めます。
1.TextViewに表示するテキストに含まれるURLを抽出
Swiftに用意されているNSDataDetectorを利用してURLを抽出します。
http、httpsスキームのみを抽出するかたちで実装しました。
// 引数 text : TextViewで表示するテキスト | |
// 戻り値 [String] : 検知されたURL | |
func detectUrls(text: String) -> [String] { | |
guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else { | |
return [] | |
} | |
let enableLinkTuples = detector.matches(in: text, range: NSRange(location: 0, length: text.count)) | |
var detectedUrls = enableLinkTuples.map { checkingResult -> String in | |
return (text as NSString).substring(with: checkingResult.range) | |
} | |
detectedUrls.removeAll(where: {!($0.contains("http://") || $0.contains("https://"))}) | |
return detectedUrls | |
} |
2.TextViewに表示するテキストのNSAttributedStringインスタンスを作成
NSAttributedStringはリンク化以外にも、一部の文字の大きさや色を指定でき、
細かい文字の装飾に利用できます。
TextViewで表示するテキストを引数にインスタンスを作成します。
let attributedString = NSMutableAttributedString(string: text) |
3.テキスト内のURLをNSRangeで指定してリンク化
テキスト内での出現位置と長さを表す構造体のNSRangeを抽出したURL分作成し、
NSAttributedStringインスタンスにリンク化部分として追加します。
for url in urls { | |
// 3.テキスト内のURLをNSRangeで指定してリンク化 | |
let range = NSString(string: text).range(of: url) | |
attributedString.addAttribute(NSAttributedString.Key.link, value: url, range: range) | |
} |
4.TextView.attributedTextに代入
TextViewのattributedTextプロパティにNSAttributedStringインスタンスを代入します。
この時、コードの最下段でリンク化部分の文字色も指定しました。
self.textView.attributedText = attributedString | |
self.textView.font = UIFont.boldSystemFont(ofSize: 20) | |
self.textView.linkTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.systemBlue] |
この実装方法で、TextViewに含まれるURLをリンク化できたと思います。
まとめ
今回は、実際に実務で行なった実装についての記事を書きました。
要件に合致する最適な実装方法を調べるうちに、Swiftの便利なクラスについての知識が深まり、
とても成長を感じることができました。
これからも精進していきます。
同じカテゴリーの記事
同じカテゴリの記事を読む