代码片段UILabel相关

Core Text系列3 - CTLineDraw绘制细节控制

2016-05-12  本文已影响903人  离子来了

CTLineDraw细节绘制

首先得了解字形,如下图:


字形图.png

步骤

1.<code>CTFrameGetLines</code>函数获取行数
2.<code>CTFrameGetLineOrigins</code>函数获取每行的坐标
3.<code>CTLineGetTypographicBounds(line as! CTLine, &lineAscent, &lineDescent, &lineLeading)</code>获得每行的字形
4.<code>
CGContextSetTextPosition(ctx, lineOrigin.x, lineOrigin.y)</code>根据实际情况微调坐标
5.<code>CTLineDraw(line as! CTLine, ctx)</code>绘制某一行。

首先绘制大多数情况都要需要高度,那么现在来一个高度计算的大致思路:

/*
     根据属性字符串计算绘制所需要的高度
     思路就是 lineAscent+lineDescent +lineLeading代表一行的高度,分别对每一行进行高度累加
     */
 func getDisplayHeight(attributeStr: NSAttributedString,width:CGFloat) -> CGFloat{
    
    let ctFrameSetter = CTFramesetterCreateWithAttributedString(attributeStr)
        
    //建议的宽高
    let suggestSize   =  CTFramesetterSuggestFrameSizeWithConstraints(ctFrameSetter, CFRangeMake(0, attributeStr.length), nil, CGSize(width: width, height: CGFloat.max), nil)
        
    let path =  CGPathCreateMutable()
        
    CGPathAddRect(path, nil, CGRect(x: 0, y: 0, width: width, height: suggestSize.height*2))//2是假的,只是防止画布不够大,计算有误,下面才开始精确计算高度
               
    let ctFrame = CTFramesetterCreateFrame(ctFrameSetter, CFRangeMake(0, 0), path, nil)
        
    let lines =  CTFrameGetLines(ctFrame) as Array
        
    var lineOrigins = Array<CGPoint>(count: lines.count, repeatedValue: CGPointZero)//获取每一个行的坐标
  
    CTFrameGetLineOrigins(ctFrame, CFRangeMake(0, 0), &lineOrigins)
    var lineAscent:CGFloat      = 0
    var lineDescent:CGFloat     = 0
    var lineLeading:CGFloat     = 0
    var lineTotalHeight:CGFloat = 0
        
    for (_,line) in lines.enumerate() {
          CTLineGetTypographicBounds(line as! CTLine, &lineAscent, &lineDescent, &lineLeading)
           let oneLineHeight = lineAscent+lineDescent + lineLeading//这里可以接上细节微调,来返回高度
           lineTotalHeight += oneLineHeight
        }
        //高度就这么计算好了
        return lineTotalHeight
    
    }

CTLineDraw绘制,就是将CTFrameDraw拆分而已

override func drawInContext(ctx: CGContext) {
       super.drawInContext(ctx)
       
       //坐标系转换
       CGContextSetTextMatrix(ctx, CGAffineTransformIdentity)
       CGContextTranslateCTM(ctx, 0, self.bounds.size.height)
       CGContextScaleCTM(ctx, 1.0, -1.0)
       
       //创建绘制的区域
       let path = CGPathCreateMutable()
       CGPathAddRect(path, nil, self.bounds)
       
       // 4.创建需要绘制的文字
       let attributed =  NSMutableAttributedString(string: "估后共和国开不开vbdkaph估后共和国开不开vbdkaph️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️这是我的第一个coreText demo,我是要给兵来自老白干I型那个饿哦个呢给个I类回滚igkhpwfh 评估后共和国开不开vbdkaphphohghg 的分工额好几个辽宁省更怕hi维护你不看hi好人佛【井柏然把饿哦个️");
       
       attributed.addAttribute(NSFontAttributeName, value: UIFont.systemFontOfSize(20), range: NSMakeRange(0, 5));
       
       attributed.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: NSMakeRange(3, 10));
       
       attributed.addAttribute(NSForegroundColorAttributeName, value: UIColor.orangeColor(), range: NSMakeRange(0, 2));
       
       //创建段落属性
       let paraStyle = NSMutableParagraphStyle()
       attributed.addAttribute(NSParagraphStyleAttributeName, value: paraStyle, range: NSMakeRange(0, attributed.length))
       
       //根据计算的高度画一个比边框 start
       let strockeHegith = self.getDisplayHeight(attributed, width: ScreenWidth)
       
       CGContextSaveGState(ctx)
       
       CGContextSetTextMatrix(ctx, CGAffineTransformIdentity)
       CGContextTranslateCTM(ctx, 0, self.bounds.size.height)
       CGContextScaleCTM(ctx, 1.0, -1.0)
       
       let strokePath = CGPathCreateMutable()
       
       CGPathAddRect(strokePath, nil, CGRect(x: 1, y: 1, width: ScreenWidth-2, height: strockeHegith))
       
       CGContextAddPath(ctx, strokePath)
       
       CGContextSetStrokeColorWithColor(ctx, UIColor.purpleColor().CGColor)
       
       CGContextStrokePath(ctx)
       
       CGContextRestoreGState(ctx)
       //根据计算的高度画一个比边框 end
       
       let ctFrameSetter = CTFramesetterCreateWithAttributedString(attributed)
       
       let ctFrame = CTFramesetterCreateFrame(ctFrameSetter, CFRangeMake(0, attributed.length), path, nil)
       
       
       let lines = CTFrameGetLines(ctFrame) as NSArray
       var originsArray = Array<CGPoint>(count: lines.count, repeatedValue: CGPointZero)//用于存储每一行的坐标
       
       CTFrameGetLineOrigins(ctFrame, CFRangeMake(0, 0), &originsArray)
       
       var frameY:CGFloat              = 0
       let kGlobalLineLeading:CGFloat  = 0
       
       for (i,line) in lines.enumerate() {
           var lineAscent:CGFloat      = 0
           var lineDescent:CGFloat     = 0
           var lineLeading:CGFloat     = 0
           CTLineGetTypographicBounds(line as! CTLine, &lineAscent, &lineDescent, &lineLeading)
           
           var   lineOrigin = originsArray[i];

           if (i > 0)
           {
           
               frameY = frameY - kGlobalLineLeading - lineAscent;//减去一个行间距,再减去第二行,字形的上部分 (循环)
               
               lineOrigin.y = frameY;
               
           } else
           {
              
               frameY = lineOrigin.y;
           }
           
           // 调整成所需要的坐标
           CGContextSetTextPosition(ctx, lineOrigin.x, lineOrigin.y);
           CTLineDraw(line as! CTLine, ctx);
           
           
           frameY = frameY - lineDescent//说明下这里,就是减去字形下面部分
           
       }
   }
效果图如下

总结:因为有中文,英文和表情的存在,默认情况下这三种的行高都是不等的,可以用上面的方法微调行高,使其所有情况下都是等高的。

为所需显示的最后一行添加省略号

步骤:
1.获取所需操作行的range
2.获取需要干掉字符的属性
3.将获取的属性,添加给省略号属性
4.剩下的字符串拼接上省略号
5.创建一个全新的CTLine绘制
6.绘制CTLine
mark:7.别傻傻的,省略号在这里就是一个表示,可以用是任何东西

//处理最后一行,将最后一个字符干掉,并且加上省略号
            let lCharacter = "……"
            if i == lines.count - 1 {
                let lastLineRange           = CTLineGetStringRange(line as! CTLine)
                let lastCharAttribute       =   attributed.attributesAtIndex(attributed.length - 1, effectiveRange: nil)
                let lastAppentAttributeStr  = NSAttributedString(string: lCharacter, attributes: lastCharAttribute)
                let foreAttributeStr        =  attributed.attributedSubstringFromRange(NSMakeRange(lastLineRange.location, lastLineRange.length - 2)) as! NSMutableAttributedString//这里减去2是因为后面有个结尾的符号,c语言里面的“/n”
                let afterAttributeStr        =  attributed.attributedSubstringFromRange(NSMakeRange(lastLineRange.location + lastLineRange.length - 1, 1)) as! NSMutableAttributedString
                print("after:\\(afterAttributeStr.string)测试")
                foreAttributeStr.appendAttributedString(lastAppentAttributeStr)
                let createLastline = CTLineCreateWithAttributedString(foreAttributeStr)
                CTLineDraw(createLastline, ctx)
            }else{
                CTLineDraw(line as! CTLine, ctx)
            }
上一篇下一篇

猜你喜欢

热点阅读