iOS

【iOS/Swift】UITextViewに含まれるURLのみをリンク化

2022年11月2日


こんにちは、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インスタンスにリンク化部分として追加します。

view raw HyperLink.swift hosted with ❤ by GitHub

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の便利なクラスについての知識が深まり、
とても成長を感じることができました。

これからも精進していきます。

この記事を書いた人

大石

大石

2022年にエンジニアとして新卒入社しました。
iOSアプリ開発とハイボールが好きです。