废话不多说,直接上代码,通过一个弹窗来展示效果
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
}
}