Swift中UILabel添加可点击字

import UIKit

//设置字体大小
var TAP_FONT:CGFloat = 14
//设置字体颜色
var TAP_COLOR:UIColor = UIColor.black
class XCTapLabel: UILabel{
    var specialStrings:[String]?
    lazy var specialRange:[NSRange] = []
    //行间距
    var line_Spacing:CGFloat = 2.0
    var reback:((String)->Void)?
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.font = adapterFont(font: TAP_FONT)
        self.textColor = TAP_COLOR     
        self.numberOfLines = 0
        self.isUserInteractionEnabled = true
        self.backgroundColor = .clear
        self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapAction)))//给label添加点击事项
        let longGes:UILongPressGestureRecognizer = UILongPressGestureRecognizer()
        longGes.minimumPressDuration = 0.5
        longGes.addTarget(self, action: #selector(longTap))
        self.addGestureRecognizer(longGes)
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    deinit {
        //移除观察者
        NotificationCenter.default.removeObserver(self)
    }
}

有长按label保存字体的需求可以加上

extension XCTapLabel{
  @objc func longTap(sender:UILongPressGestureRecognizer){
      if sender.state == .began{
          self.backgroundColor = UIColor.grayValue(value: 220)
          self.becomeFirstResponder()
          let menu = UIMenuController.shared
        
          //设置自定义菜单
          menu.menuItems = [UIMenuItem.init(title: "复制", action: #selector(xcCopy)) ]
          //菜单显示位置
          menu.setTargetRect(self.bounds, in: self)
          //显示菜单
          menu.setMenuVisible(true, animated: true)
          NotificationCenter.default.addObserver(self, selector: #selector(menuAction), name: UIMenuController.didHideMenuNotification, object: nil)
      }
  }
  @objc private func menuAction(){
      self.backgroundColor = UIColor.grayValue(value: 244)
      self.resignFirstResponder()
  }
  @objc private func xcCopy(){
      self.backgroundColor = UIColor.grayValue(value: 244)
      UIPasteboard.general.string = self.text
  }
  //MARK: 返回悬浮菜单中可以显示的选项
  override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
      if action == #selector(xcCopy){
          return true
      }
      return false
  }
}

单击响应,核心代码

extension XCTapLabel{
    @objc func tapAction(tapGesture:UITapGestureRecognizer){
        //可变字符为空,则没有点击效果
        if self.attributedText == nil {
            return
        }
        //点击的坐标
        let position = tapGesture.location(in: self)
        //段落样式
        let paragraphStyle:NSMutableParagraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineBreakMode = NSLineBreakMode.byWordWrapping
        paragraphStyle.lineSpacing = 2
        //字体样式
        let font:UIFont = UIFont.systemFont(ofSize: TAP_FONT)
        //新增字体样式
        let newAttStyle:[NSAttributedString.Key:Any] = [NSAttributedString.Key.font:font,NSAttributedString.Key.paragraphStyle:paragraphStyle]
    
        //要根据设置的paragraphStyle来计算高度,否则计算的不准确,没有计算行间距,CTFrameGetLines计算函数为0
        if let attText:NSMutableAttributedString = self.attributedText as? NSMutableAttributedString{
           attText.addAttributes(newAttStyle, range: NSMakeRange(0, attText.length))
        
           //CTFramesetter 是使用 Core Text 绘制时最重要的类。它管理您的字体引用和文本绘制帧。
           let framesetter:CTFramesetter = CTFramesetterCreateWithAttributedString(attText)
           //贝塞尔路径的尺寸
           let path:UIBezierPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height))
           //通过CTFramesetter获取尺寸
           let ctframe:CTFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path.cgPath, nil)
           //通过CTFrame获取有多少行的数组
           let lines:CFArray = CTFrameGetLines(ctframe)
           //行组的count
           let lineCount:CFIndex = CFArrayGetCount(lines)
           //获取label的高度,width是已知的
           let height:CGFloat = self.sizeThatFits(CGSize(width: self.width, height: 9999)).height
           //计算每行有多高
           let lineHeight:CGFloat = height/CGFloat(lineCount)
           //获取点击的行数
           let lineIndex:Int = Int(position.y / lineHeight)
           //点击的点
            let clickPoint:CGPoint = CGPoint(x: position.x, y: lineHeight - position.y)
            if lineIndex < lineCount {
                let clickLine:CTLine = unsafeBitCast(CFArrayGetValueAtIndex(lines, lineIndex), to: CTLine.self)
                let startIndex:CFIndex = CTLineGetStringIndexForPosition(clickLine, clickPoint)
                var content:String = ""
                for range in specialRange{
                    if startIndex >= range.location && startIndex <= range.location + range.length {
                        if let string:String = self.attributedText?.string{
                            let str = (string as NSString).substring(with: range)
                            content = str
                        }
                    }
                }
                self.reback?(content)
            }
        }
    }
    func setSpecialStrings(specialString:[String]?,attriString:NSAttributedString) -> Void{
        specialRange = []
        let string:String = attriString.string
        if let array:[String] = specialString{
            for str in array{
                let range:NSRange = (string as NSString).range(of: str)
                specialRange.append(range)
            }
        }
    }
}

======================具体用法======================
数据模型,这里面做富文本字体处理以及高度计算

class attModel: Mappable {
    //计算属性,height作为tableview的DataSource中行高,过程中赋值给富文本字符串(attString)以及可点击字符串数组(specialString)
    var height:CGFloat{
        let attStr:NSMutableAttributedString = NSMutableAttributedString()
        let label:UILabel = UILabel()
        label.numberOfLines = 0
        //这个是单人:张三:我真帅
        if self.to_nickname ?? "" == ""{
            let userStr:String = self.nickname ?? ""
            let attStr1:NSMutableAttributedString = XChandyAttributeString(str: userStr, font: 14, color: MAIN_COLOR)
            let contentStr:String = " : " + (self.lower_com_content ?? "")
            let attStr2:NSMutableAttributedString = XChandyAttributeString(str: contentStr, font: 14, color: UIColor.grayValue(value: 88))
            attStr.append(attStr1)
            attStr.append(attStr2)
            specialString = [userStr]
            attString = attStr
            label.attributedText = attStr
        }else{
            ///这个是有回复人的:张三回复李四:我真帅
            let userStr:String = self.nickname ?? ""
            let contentStr:String = " : " + (self.lower_com_content ?? "")
            let toSt:Stringr = self.to_nickname ?? ""
            let attStr1:NSMutableAttributedString = XChandyAttributeString(str: userStr, font: TAP_FONT, color: MAIN_COLOR)
            let attStr2:NSMutableAttributedString  = XChandyAttributeString(str: "回复", font: TAP_FONT, color: UIColor.grayValue(value: 88))
            let attStr3:NSMutableAttributedString  = XChandyAttributeString(str: toStr, font: TAP_FONT, color: MAIN_COLOR)
            let attStr4:NSMutableAttributedString  = XChandyAttributeString(str: contentStr, font: TAP_FONT, color: UIColor.grayValue(value: 88))
            attStr.append(attStr1)
            attStr.append(attStr2)
            attStr.append(attStr3)
            attStr.append(attStr4)
            specialString = [userStr,toStr]
            attString = attStr
            label.attributedText = attStr
        }
        let size:CGSize = label.sizeThatFits(CGSize(width: SCREEN_WIDTH-adapterWidth(70), height: 9999))
        return size.height+2     //加2是因为有时候获取不到行数
    }
    var attString:NSMutableAttributedString?
    var specialString:[String]?
    //json数据
    var nickname:String?
    var lower_com_content:String?
    var to_nickname:String?
    var toid:Int?
    var uid:Int?
    required init?(map: Map){}
    func mapping(map: Map){
        nickname <- map["nickname"]
        lower_com_content <- map["lower_com_content"]
        to_nickname <- map["to_nickname"]
        toid <- map["toid"]
        uid <- map["uid"]
    }
}

初始化及回调

let label:XCTapLabel = XCTapLabel()
label.reback = {[weak self] string in
//string就是点击的字
}

var model:attModel?{
    didSet{
        tapLabel.frame = CGRect(x: adapterWidth(5), y: adapterWidth(5), width: SCREEN_WIDTH-adapterWidth(70), height: 0)
        //调用model?.height为了给attString以及setSpecialStrings赋值,不然两者无值
        let height:CGFloat = model?.height ?? 0
        tapLabel.attributedText = model?.attString
        tapLabel.setSpecialStrings(specialString: model?.specialString, attriString:model?.attString ?? NSMutableAttributedString(string: ""))
        tapLabel.height = height
    }
}

看都看了,码个代码也不容易,留个小心心嘛~~~

你可能感兴趣的:(Swift中UILabel添加可点击字)