zoukankan      html  css  js  c++  java
  • coretext简单使用

    相对于UIKit,使用coretext绘制文本效率高,具有更高的自由度,可随时插入图片,增加文本点击事件等。

    1.增加文本的点击事件

    思路:定义UILabel子类,设置可点击的富文本range及其他属性(颜色、字体),touchBegin方法中根据点击位置判断所在行所在index,最后判断index是否在range内,若在,则响应事件。

      首先定义可点击的文本model,该model主要含有3个属性,string-用于回调显示,range-用于判断位置,attributes-用于绘制文本

    class LinkAttributesModel:NSObject {
        var string:String!
        var range:NSRange!
        var attributes:Dictionary<NSAttributedStringKey,Any>!
    }
    

       接着在label子类实现方法setString(string:String,attributes:Dictionary<NSAttributedStringKey,Any>?,linkArr:Array<LinkAttributesModel>),第一个参数是显示string,第二个参数是string的属性,第三个参数是一个可点击文本的集合,需要手动赋值

    // 声明
    func setString(string:String,attributes:Dictionary<NSAttributedStringKey,Any>?,linkArr:Array<LinkAttributesModel>) {
            locLinkArr = linkArr;
            let attributedString = NSMutableAttributedString(string: string, attributes: attributes)
            for linkMdl in linkArr {
                attributedString.addAttributes(linkMdl.attributes, range: linkMdl.range)
            }
    //        self.attributedText = attributedString
            tmpAttributedString = attributedString
            
            // create frameRef
            let frameSetter = CTFramesetterCreateWithAttributedString(attributedString)
            let path = CGMutablePath()
            path.addRect(self.bounds)
            frameRef = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, string.count), path, nil)
            
            self.isUserInteractionEnabled = true
        }
    
    // 调用
    // 构造属性model
            var mdlArr = [LinkAttributesModel]()
            for i in 0...2 {
                let mdl = LinkAttributesModel()
                let loc = i*10
                let range = NSMakeRange(loc, 5)
                mdl.range = range
                let startIdx = showString.index(showString.startIndex, offsetBy: range.location)
                let endIdx = showString.index(startIdx, offsetBy: range.length)
                mdl.string = String(showString[startIdx...endIdx])
                let randomColor = UIColor(red: CGFloat(arc4random_uniform(256))/255.0, green: CGFloat(arc4random_uniform(256))/255.0, blue: CGFloat(arc4random_uniform(256))/255.0, alpha: 1)
                mdl.attributes = [NSAttributedStringKey.foregroundColor:randomColor]
                mdlArr.append(mdl)
            }
            
            let lbl = Taplabel(frame:self.view.bounds.insetBy(dx: 0, dy: 50))
            let style = NSMutableParagraphStyle()
            style.lineSpacing = 10
            lbl.setString(string: showString, attributes: [NSAttributedStringKey.font:UIFont.systemFont(ofSize: 20),NSAttributedStringKey.paragraphStyle:style], linkArr: mdlArr)
    

       最后根据属性文本创建对应的frameRef,然后遍历其CTLine数组,判断点击的位置在哪行内,再根据x获取当前行的偏移位置index,最后遍历linkAttributesModel,判断range是否包含index,如果包含,则触发回调事件

    func touchEvents(pt:CGPoint) {
            if (frameRef == nil) {return}
            let lines:[CTLine] = CTFrameGetLines(frameRef!) as! [CTLine]
            let lineCount = lines.count
            if (lineCount == 0) {return}
    //        let flipTransform = CGAffineTransform(translationX: 0, y: self.bounds.size.height).scaledBy(x: 1, y: -1)
            for i in 0..<lineCount {
                let line = lines[i]//CFArrayGetValueAtIndex(lines, i) as! CTLine
                var ascent:CGFloat = 0
                var descent:CGFloat = 0
                var leading:CGFloat = 0
                let width =  CGFloat(CTLineGetTypographicBounds(line, &ascent, &descent, &leading))
                let height = ascent + abs(descent) + leading
                
                var rect = CGRect(x: pt.x, y: pt.y,  width, height: height)
    //            var rect = flippedRect.applying(flipTransform)
                
                var lineSpace:CGFloat = 0;
                if let style = tmpAttributedString?.attribute(NSAttributedStringKey.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle{
                    lineSpace = style.lineSpacing
                }
                
                rect.origin.y = (height+lineSpace)*CGFloat(i);
                
                if rect.contains(pt) {
                    var stringIndex = CTLineGetStringIndexForPosition(line, pt)
                    var offset:CGFloat = 0
                    CTLineGetOffsetForStringIndex(line, stringIndex, &offset)
                    if (offset > pt.x) {
                        stringIndex = stringIndex - 1
                    }
                    
                    for linkMdl in locLinkArr! {
                        if linkMdl.range.contains(stringIndex) {
                            tapBlock!(linkMdl.string)
                        }
                    }
                }
            }
        }
    

       上述代码的主要核心在于

       a.算行高  CTLineGetTypographicBounds(line,&asent,&descent,&leading)

       b.算行内偏移  CTLineGetStringIndexForPosition(line,pt)  如果根据该index算出的偏移量大于pt.x,则index-1

       c.判断range包含  range.contains(index)

    2.图文绘制

    思路:NSAttributedString中插入占位空白符-关联图片属性。图片宽高由CTRunDelegate,图片可藏于追加属性中。ctx.draw(image,in:drawRect)时遍历CTLine,寻找含有CTRunDelegate的CTRun,如果存在,则获取图片的相应数组,构造drawRect和image绘制图片。

      追加图片属性

    func appendImage(attrStr:NSAttributedString)->NSAttributedString {
            var callBacks = CTRunDelegateCallbacks(version: kCTRunDelegateVersion1, dealloc: { (pointer) in
            }, getAscent: { (ref) -> CGFloat in
                let dic = ref.assumingMemoryBound(to: NSDictionary.self)
                return dic.pointee.object(forKey: "height") as! CGFloat
            }, getDescent: { (ref) -> CGFloat in
                return 0
            }) { (ref) -> CGFloat in
                let dic = ref.assumingMemoryBound(to: NSDictionary.self)
                return dic.pointee.object(forKey: "width") as! CGFloat
            }
            
            let imageData:Dictionary<String,Any> = ["height":100,"width":200]
            let imageDataPointer = UnsafeMutablePointer<Dictionary<String,Any>>.allocate(capacity: 1)
            imageDataPointer.initialize(to: imageData)
            let runDelegate = CTRunDelegateCreate(&callBacks, imageDataPointer)
            
            let runAttributes:[NSAttributedStringKey:Any] = [kCTRunDelegateAttributeName as NSAttributedStringKey:runDelegate as Any]
            let runAttributeStr = NSMutableAttributedString(string: " ", attributes: runAttributes)
            runAttributeStr.addAttribute(NSAttributedStringKey(rawValue: "imageName"), value: "shot", range: NSMakeRange(0, 1))
            
            let mutableAttrStr = NSMutableAttributedString(attributedString: attrStr)
            let whiteSpaceStr = createAttributedString(str: "临时换行
    ")
            let suffixStr = createAttributedString(str: "
    haha insert image succeed")
            mutableAttrStr.append(whiteSpaceStr)
            mutableAttrStr.append(runAttributeStr)
            mutableAttrStr.append(suffixStr)
            return mutableAttrStr
        }
    

       绘制追加的图片属性

    func drawImage(frame:CTFrame,ctx:CGContext) {
            let lines = CTFrameGetLines(frame) as Array
            var lineOrigins:Array<CGPoint> = Array(repeating: .zero, count: lines.count)
            CTFrameGetLineOrigins(frame, CFRange(location: 0, length: 0), &lineOrigins)
            for i in 0..<lines.count {
                let line = lines[i] as! CTLine
                let runs = CTLineGetGlyphRuns(line) as Array
                print("lineCount == (lines.count)  i == (i)
    ")
                for j in 0..<runs.count {
                    let run = runs[j] as! CTRun
                    let runAttribute = CTRunGetAttributes(run) as NSDictionary
    //                if runAttribute == nil {return}
                    print("runCount == (runs.count)    j == (j) runattribute == (runAttribute)")
                    let runDelegate = runAttribute.object(forKey: kCTRunDelegateAttributeName as String)
                    if (runDelegate == nil) {continue}
                    var ascent:CGFloat = 0
                    var descent:CGFloat = 0
                    let CGFloat = CGFloat(CTRunGetTypographicBounds(run, CFRange(location: 0, length: 0), &ascent, &descent, nil))
                    let xoffset = lineOrigins[i].x + CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, nil)
                    let yoffset = lineOrigins[i].y
                    let drawRect = CGRect(x: xoffset, y: yoffset,  width, height: ascent+descent)
                    if let imgName = runAttribute.object(forKey: "imageName") as? String {
                        let img = UIImage(named: imgName)
                        ctx.draw((img?.cgImage)!, in: drawRect)
                    }
                }
            }
        }
    

       关键代码是

      a.获取lineOrigin  CTFrameGetLineOrigins(frame, CFRange(location: 0, length: 0), &lineOrigins)

      b.获取width,height  CTRunGetTypographicBounds(run, CFRange(location: 0, length: 0), &ascent, &descent, nil)  //图片忽略leading属性

      c.获取x偏移量  lineOrigins[i].x+CTLineGetOffsetForStringIndex(line,CTRunGetStringRange(run).location,nil)

      d.获取图片  runAttributes.object(forKey:"imageName")

  • 相关阅读:
    PHP trim() 函数
    php 计算2个日期的相差天数
    php date('Y-n-j')的和date('Y-m-d')的区别
    转移服务器
    Invalid argument supplied for foreach()解决办法
    wordpress 后台忘记密码怎么办
    qrcode js插件生成微信二维码
    thinkphp5 注释
    tp5 新增完数据,获取id
    resstFul服务文件上传下载
  • 原文地址:https://www.cnblogs.com/xiaoerheiwatu/p/10036147.html
Copyright © 2011-2022 走看看