UITextview окрашивает весь атрибутивный текст вместо заданного диапазона - PullRequest
0 голосов
/ 05 ноября 2019

Мне нужно изменить цвет текста ссылки и сделать его вставляемым. Я создал простой подкласс из UITextView. Во-первых, казалось, что все работает отлично. Но когда мне нужно было несколько раз изменить текст, он окрашивает весь текст в синий, а не в диапазон ссылок.

Я добавляю сюда код детской площадки. Если вы нажмете 3 раза на «TAP TAP», вы поймете, что я имею в виду.

import UIKit
import PlaygroundSupport

final class TextViewWithLinks: UITextView {
    typealias LinkParameters = [(displayText: String, urlPath: String)]

    override public var text: String! {
        get { attributedText.string }
        set { setupText(newValue) }
    }

    var linkParameters: LinkParameters = [] {
        didSet { setupText(text) }
    }

    init() {
        super.init(frame: .zero, textContainer: nil)
        setup()
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setup() {
        backgroundColor = nil
        textColor = .gray
        textAlignment = .center
        isSelectable = true
        isEditable = false
        isScrollEnabled = false
        delegate = self
    }

    private func setupText(_ text: String) {
        let newAttributedText = NSMutableAttributedString(string: text)

        let textColor = self.textColor ?? .gray
        let font = self.font ?? .systemFont(ofSize: 14)
        let paragraph = NSMutableParagraphStyle()
        paragraph.alignment = textAlignment
        newAttributedText.addAttributes(
            [.font: font, .foregroundColor: textColor, .paragraphStyle: paragraph],
            range: NSRange(location: 0, length: text.count)
        )

        for linkParameter in linkParameters {
            let url = URL(string: linkParameter.urlPath)
            let displayText = linkParameter.displayText.lowercased()
            newAttributedText.addAttributes(
                [.link: url ?? linkParameter.urlPath, .foregroundColor: UIColor.blue],
                range: (text.lowercased() as NSString).range(of: displayText)
            )
        }

        self.attributedText = newAttributedText
    }
}

extension TextViewWithLinks: UITextViewDelegate {
    func textView(
        _ textView: UITextView,
        shouldInteractWith URL: URL,
        in characterRange: NSRange,
        interaction: UITextItemInteraction) -> Bool {
        return true
    }
}


class MyViewController : UIViewController {
    private let button = UIButton()
    private let textView = TextViewWithLinks()

    override func loadView() {
        super.loadView()
        let view = UIView()
        view.backgroundColor = .white

        textView.text = "My text with a link"
        textView.linkParameters = [
            (displayText: "link", urlPath: "https://www.google.com/")
        ]

        button.setTitle("TAP TAP", for: .normal)
        button.setTitleColor(.black, for: .normal)
        button.addTarget(self, action: #selector(tappedButton), for: .touchUpInside)

        self.view = view
        layoutTextView()
        layoutButton()
    }

    @objc private func tappedButton() {
        textView.text = "\nNew line" + textView.text
    }

    private func layoutTextView() {
        view.addSubview(textView)
        textView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            textView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50),
            textView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            textView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)
        ])
    }

    private func layoutButton() {
        view.addSubview(button)
        button.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            button.topAnchor.constraint(equalTo: view.topAnchor, constant: 5),
            button.heightAnchor.constraint(equalToConstant: 30),
            button.widthAnchor.constraint(equalToConstant: 100),
            button.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        ])
    }
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

Единственное решение, которое я нашел, это удалить .foregroundColor: UIColor.blue и добавить только .link: url ?? linkParameter.urlPath для атрибутов ссылки. Но, с другой стороны, если мне нужно только покрасить его в синий цвет, он снова не справится с таким поведением.

Есть идеи, помогите?

...