UITextView link跳转

废话不多说,直接上代码,通过一个弹窗来展示效果

import UIKit
import FZBaseKit
import SnapKit
import RxSwift
@objcMembers
/// 新的弹窗alert,支持富文本点击跳转,支持左侧取消按钮,右侧确定按钮;支持中间一个确定按钮
open class FZAlertView: UIView {
    //按钮点击回调,取消按钮false 确认按钮 true
    typealias hander = (_ result: Bool)->()
    typealias strHander = (_ index: Int, _ linkStr: String)->()
    //按钮点击回调属性
    private var callBlock:hander?
    //富文本点击回调属性
    private var strCallBlock:strHander?
    private var attributedArr: Array?
    private let bag = DisposeBag.init()
    //白色承载体
    lazy var bgView: UIView = {
        let view = UIView.init()
        view.backgroundColor = .white
        view.layer.cornerRadius = CGFloat(KRatio(15))
        view.clipsToBounds = true
        return view
    }()
    
    lazy var contentTextView: UITextView = {
        let textView = UITextView.init()
        textView.textAlignment = .center
        textView.font = KFont(CGFloat(KRatio(15)))
        textView.textColor = UIColor.hex("#3D3838")
        textView.backgroundColor = .white
        textView.delegate = self
        textView.isEditable = false //禁止编辑
        textView.isScrollEnabled = false //禁止滚动
        return textView
    }()
    
    lazy var cancelBtn: UIButton = {
        let btn = UIButton.initButton(title: "", titleColor: UIColor.hex("#3D3838"), fontSize: 18, bgColor: UIColor.clear, imageName: "")
        btn.layer.masksToBounds = true
        btn.layer.cornerRadius = CGFloat(KRatio(22))
        btn.layer.borderWidth = 0.5
        btn.layer.borderColor = UIColor.hex("#918B8B").cgColor
        return btn
    }()
    
    lazy var confirmBtn: UIButton = {
        let btn = UIButton.initButton(title: "", titleColor: UIColor.white, fontSize: 18, bgColor: UIColor.hex("#6249EE"), imageName: "")
        btn.layer.masksToBounds = true
        btn.layer.cornerRadius = CGFloat(KRatio(22))
        return btn
    }()
    
    /// 创建alertView
    /// - Parameters:
    ///   - message: 文本信息
    ///   - sureBtnTitle: 确认按钮标题
    ///   - cancelBtnTitle: 取消按钮标题,如果无取消按钮该值不传
    ///   - supperVC: 推出alertview的VC,如果为空,就通过KAppwindow弹出
    ///   - btnActionCallBack: 取消、确认按钮点击回调
    ///   - attributedArr: 富文本点击数组,数组内部字典组成为,无富文本点击可不传
    ///   - attributedClickActionCallBack: 富文本点击回调
    /// - Returns: 返回alertView
   public static func showAlertView(message: String, supperVC: UIViewController?, sureBtnTitle: String, cancelBtnTitle: String = "", attributedArr: Array = [], btnActionCallBack: @escaping (_ result: Bool)->(), attributedClickActionCallBack: @escaping (_ index: Int, _ linkStr: String)->()) {
        let frame = supperVC == nil ? CGRect.init(x: 0, y: 0, width: KWIDTH_SCREEN, height: KHEIGHT_SCREEN) : supperVC!.view.bounds
        let alertView = FZAlertView.init(frame: frame)
        alertView.callBlock = btnActionCallBack
        alertView.strCallBlock = attributedClickActionCallBack
        alertView.attributedArr = attributedArr
        alertView.initUI(supperVC: supperVC)
        alertView.setData(message: message, sureBtnTitle: sureBtnTitle, cancelTitle: cancelBtnTitle)
        alertView.layoutUI(message: message, cancelTitle: cancelBtnTitle)
    }
    
    //UI构建
    private func initUI(supperVC: UIViewController?) {
        self.backgroundColor = UIColor.RGBA(0, 0, 0, 0.6)
        self.addSubview(bgView)
        bgView.addSubview(contentTextView)
        bgView.addSubview(confirmBtn)
        bgView.addSubview(cancelBtn)
        if supperVC == nil {
            KAppWindow?.addSubview(self)
        } else {
            supperVC?.view.addSubview(self)
            supperVC?.view.bringSubviewToFront(self)
        }
    }
    
    //数据处理
    private func setData(message: String, sureBtnTitle: String, cancelTitle: String) {
        self.confirmBtn.setTitle(sureBtnTitle, for: .normal)
        self.cancelBtn.setTitle(cancelTitle, for: .normal)
        //textView富文本
        contentTextView.attributedText = self.getContentTextViewAttributedText(message: message)
        //按钮点击
        cancelBtn.rx.tap.asObservable()
            .subscribe {[weak self] _ in
                guard let self = self else {return}
                if self.callBlock != nil {
                    self.callBlock!(false)
                    self.removeFromSuperview()
                }
            }
            .disposed(by: bag)
        
        confirmBtn.rx.tap.asObservable()
            .subscribe{[weak self] _ in
                guard let self = self else {return}
                if self.callBlock != nil {
                    self.callBlock!(true)
                    self.removeFromSuperview()
                }
            }
            .disposed(by: bag)
    }
    
    //布局构建
    private func layoutUI(message: String, cancelTitle: String) {
        let btnWidth = KRatio(105)
        let btnHeight = KRatio(44)
        var contentWidth = KRatio(275)//默认高度
        let textViewHeight = message.getStringHeight(CGFloat(KRatio(230)), KFont(CGFloat(KRatio(15)))) + CGFloat(KRatio(20))
        
        var contentHeight = Int(textViewHeight) + KRatio(20) + btnHeight + KRatio(40)
        if contentHeight < KRatio(175) {
            contentHeight = KRatio(175)
        }
        if contentHeight > KRatio(550) {
            //如果高度过高,那么设置最大高度,并且设置textView可滚动
            contentHeight = KRatio(550)
            contentTextView.isScrollEnabled = true
        }
        
        bgView.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.size.equalTo(CGSize.init(width: contentWidth, height: contentHeight))
        }
        contentTextView.snp_makeConstraints { make in
            make.left.equalTo(bgView).offset(KRatio(20))
            make.right.equalTo(bgView).offset(KRatio(-20))
            make.top.equalTo(bgView).offset(KRatio(20))
        }
        //按钮布局分为取消、确认按钮和只有确认按钮两种情况,根据cancelTitle来判断
        if cancelTitle.count == 0 {
            //隐藏cancelTitle
            cancelBtn.isHidden = true
            //surebtn据中
            confirmBtn.snp.makeConstraints { make in
                make.centerX.equalTo(bgView)
                make.size.equalTo(CGSize.init(width: btnWidth, height: btnHeight))
                make.bottom.equalTo(bgView).offset(KRatio(-17))
            }
        } else {
            cancelBtn.snp.makeConstraints { make in
                make.left.equalTo(bgView).offset(KRatio(20))
                make.bottom.equalTo(bgView).offset(KRatio(-17))
                make.size.equalTo(CGSize.init(width: btnWidth, height: btnHeight))
            }
            confirmBtn.snp.makeConstraints { make in
                make.bottom.size.equalTo(cancelBtn)
                make.right.equalTo(bgView).offset(KRatio(-20))
            }
        }
    }
}

extension FZAlertView: UITextViewDelegate {
    //对link富文本点击进行处理
    public func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
        KLog("scheme = \(URL.scheme?.description) --- range = \(characterRange.description) --- inter = \(interaction)")
        for (index, model) in self.attributedArr!.enumerated() {
            if URL.absoluteString.contains(model.linkStr) {
                //如果url包含model中的linkUrl,那么说明点击的是该link跳转,那么将index回传回去
                if self.strCallBlock != nil {
                    self.strCallBlock!(index, model.linkStr)
                }
                return false
            }
        }
        return true
    }
}

extension FZAlertView {
    //获取富文本
    private func getContentTextViewAttributedText(message: String) -> NSAttributedString {
        let attrStr = NSMutableAttributedString.init(string: message)
        attrStr.addAttribute(NSAttributedString.Key.font, value: KFont(CGFloat(KRatio(15))), range: NSRange.init(location: 0, length: message.count))
        if self.attributedArr!.count > 0 {
            for model in attributedArr! {
                //根据数组添加字体、颜色、link富文本
                //跳转链接如果不是http或https链接跳转,那么就在后面追加://
                attrStr.addAttributes([NSAttributedString.Key.font: KFont(CGFloat(KRatio(16))), NSAttributedString.Key.foregroundColor: model.linkColor, NSAttributedString.Key.link: model.linkStr.contains("http") ? model.linkStr : model.linkStr+"://"], range: model.linkRange)
            }
        }
        return attrStr
    }
}

//MARK: FZAlertViewModel
@objcMembers
open class FZAlertViewModel: NSObject {
    public let linkStr: String         //富文本link点击后跳转的urlStr
    public let linkRange: NSRange      //富文本可跳转的range范围
    public let linkColor: UIColor
    
    public init(linkStr: String, linkRange: NSRange, linkColor: UIColor = UIColor.hex("#333333")) {
        self.linkStr = linkStr
        self.linkRange = linkRange
        self.linkColor = linkColor
    }
}

你可能感兴趣的:(UITextView link跳转)