xyk blog

最近は iOS 開発の記事が多めです。

【Swift5】UITextViewの任意の文字列をタップ可能なリンクにする

検証環境:
Xcode 11.3
Swift 5.1.3

UITextView の任意の文字列のタップ可能なリンクにする方法についてです。
まず NSAttributedString.link 属性を使用してリンク化する text と range を設定します。
そして UITextViewDelegate の
textView(_:shouldInteractWith:in:interaction:)
を実装するとリンク箇所をタップしたタイミングで呼び出されるので、このデリゲートメソッド内に画面遷移させる処理などを書けばOKです。

以下のようなリンクを実装する例です。

リンク化する文字列の色変更、アンダーラインを追加します。
今回は UITextView のサブクラスとして実装し、IBDesignable と IBInspectable を使って Storyboard で使い回せるようなクラスにしました。
UITextView の text プロパティにまず全文を設定し、その後リンク化したい部分を linkText に設定します。

import UIKit

@IBDesignable
class LinkTextView: UITextView, UITextViewDelegate {
    
    @IBInspectable
    var fontSize: CGFloat = 14
    
    @IBInspectable
    var isBold: Bool = false
    
    @IBInspectable
    var linkText: String = ""
    
    @IBInspectable
    var linkColor: UIColor = .red
    
    @IBInspectable
    var linkURL: String = ""

    override func awakeFromNib() {
        super.awakeFromNib()
        configure()
    }
    
    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        configure()
    }
    
    private func configure() {
        
        let systemFont: UIFont = isBold ? .boldSystemFont(ofSize: fontSize) : .systemFont(ofSize: fontSize)
        let attributedString = NSMutableAttributedString(string: text)
        let attrs: [NSAttributedString.Key: Any] =  [
            .font: systemFont,
            .foregroundColor: textColor ?? .black
        ]
        attributedString.setAttributes(attrs, range: NSString(string: text).range(of: text))
        let linkAttrs: [NSAttributedString.Key: Any] =  [
            .font: systemFont,
            .link: linkURL // リンク先URL
        ]
        attributedString.setAttributes(linkAttrs, range: NSString(string: text).range(of: linkText))
        attributedText = attributedString
        linkTextAttributes = [
            .foregroundColor: linkColor, // リンクの色
            .underlineStyle: NSUnderlineStyle.single.rawValue // アンダーラインを追加
        ]
        
        textContainerInset = .zero
        textContainer.lineFragmentPadding = 0
        backgroundColor = .clear
        isSelectable = true
        isEditable = false
        isScrollEnabled = false
        delegate = self
    }
    
    // MARK: - UITextViewDelegate
    
    func textView(_ textView: UITextView,
                  shouldInteractWith URL: URL,
                  in characterRange: NSRange,
                  interaction: UITextItemInteraction) -> Bool {
        
        UIApplication.shared.open(URL)
        return false
    }
}

以前の記事

xyk.hatenablog.com