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
}
}
看都看了,码个代码也不容易,留个小心心嘛~~~