UILabel显示HTML富文本,响应点击事件
importFoundation
protocol HtmlLabelDelegate {
funcclickUrl(url:NSURL)
funcclickImage(image:UIImage)
}
class HtmlLabel: UILabel {
//Mark --`TextKit` 的核心对象 绘制`textStorage`的文本内容
/// 属性文本存储
private lazy var textStorage = NSTextStorage()
/// 负责文本`字形`布局 1.绘制背景 2.绘制Glyphs 字形 3.获取点中字符的索引
private lazy var layoutManager = NSLayoutManager()
/// 设定文本绘制的范围
private lazy var textContainer = NSTextContainer()
var delegate:HtmlLabelDelegate?
var attachments:Array<AttachmentModel> = []
// Mark: 一旦内容变化 需要让 textStorge 响应变化
// Mark: --重写属性
override var text: String?{
didSet{
// 重新准备文本内容
prepareTextSystem()
}
}
override var attributedText: NSAttributedString?{
didSet{
// 重新准备文本内容
prepareTextSystem()
}
}
// Mark: -- 构造函数
overrideinit(frame:CGRect) {
super.init(frame: frame)
prepareTextSystem()
}
requiredinit?(coder aDecoder:NSCoder) {
super.init(coder: aDecoder)
prepareTextSystem()
}
// Mark: --交互
overridefunctouchesBegan(_touches:Set, with event:UIEvent?) {
guard let location = touches.first?.location(in:self)else{
return
}
for r in attachments{
if(r.rect?.contains(location))! {
ifr.image!=nil{
print("click image")
break
}else if r.url!=nil{
print("click url")
break
}
}else{
print("没戳着")
}
}
}
override func drawText(in rect:CGRect) {
letrange =NSRange(location:0, length:textStorage.length)
///绘制背景
layoutManager.drawBackground(forGlyphRange: range, at:CGPoint())
/// 绘制Glyphs 字形
/// CGPoint():从原点绘制
layoutManager.drawGlyphs(forGlyphRange: range, at:CGPoint())
}
override func layoutSubviews() {
super.layoutSubviews()
//指定绘制文本的区域
textContainer.size = bounds.size
}
}
private extension HtmlLabel{
///准备文本系统
func prepareTextSystem(){
// 0.开启交互
isUserInteractionEnabled = true
//1.准备文本内容
prepareTextContent()
//2.设置对象的关系
textStorage.addLayoutManager(layoutManager)
layoutManager.addTextContainer(textContainer)
}
/// 使用`textStorage`接管label内容
func prepareTextContent(){
ifletattributedText =attributedText{
textStorage.setAttributedString(attributedText)
}elseiflettext =text{
textStorage.setAttributedString(NSAttributedString(string: text))
}else{
textStorage.setAttributedString(NSAttributedString(string: ""))
}
getAttachments()
}
func getAttachments(){
if attributedText == nil {
return
}
attributedText!.enumerateAttributes(in: NSRange(location: 0, length: attributedText!.length), options: NSAttributedString.EnumerationOptions.longestEffectiveRangeNotRequired, using: { (dic, range, stop) in
if dic.keys.contains(NSAttributedString.Key.attachment) {
let attacment:NSTextAttachment = dic[NSAttributedString.Key.attachment] as! NSTextAttachment
ifattacment.fileWrapper!=nil&&attacment.fileWrapper!.regularFileContents!=nil{
letrect =boundingRectForCharacterRange(range: range)
letattachmentModel =AttachmentModel()
attachmentModel.image=UIImage(data: attacment.fileWrapper!.regularFileContents!)
attachmentModel.rect= rect
attachments.append(attachmentModel)
}
}else if dic.keys.contains(NSAttributedString.Key.link) {
leturl:NSURL= dic[NSAttributedString.Key.link]as!NSURL
letrect =boundingRectForCharacterRange(range: range)
letattachmentModel =AttachmentModel()
attachmentModel.url= url
attachmentModel.rect= rect
attachments.append(attachmentModel)
}
})
}
private func boundingRectForCharacterRange(range:NSRange) ->CGRect{
textContainer.lineFragmentPadding = 0
letglyphRange =layoutManager.characterRange(forGlyphRange: range, actualGlyphRange:nil)
letrect =layoutManager.boundingRect(forGlyphRange: glyphRange, in:textContainer)
returnrect
}
}
class AttachmentModel {
varimage:UIImage?
varurl:NSURL?
varrect:CGRect?
}