• iOS——Core Animation 知识摘抄(三)


    原文地址:http://www.cocoachina.com/ios/20150105/10827.html

    CAShapeLayer

    CAShapeLayer是一个通过矢量图形而不是bitmap来绘制的图层子类。你指定诸如颜色和线宽等属性,用CGPath来定义想要绘制的图形,最后CAShapeLayer就自动渲染出来了。当然,你也可以用Core Graphics直接向原始的CALayer的内容中绘制一个路径,相比直下,使用CAShapeLayer有以下一些优点:

    • 渲染快速。CAShapeLayer使用了硬件加速,绘制同一图形会比用Core Graphics快很多

    • 高效使用内存。一个CAShapeLayer需要像普通CALayer一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存。

    • 不会被图层边界剪裁掉。一个CAShapeLayer可以在边界之外绘制。你的图层路径不会像在使用Core Graphics的普通CALayer一样被剪裁掉(如我们在第二章所见)。

    • 不会出现像素化。当你给CAShapeLayer做3D变换时,它不像一个有寄宿图的普通图层一样变得像素化。

    事实上你可以在一个图层上绘制好几个不同的形状。你可以控制一些属性比如lineWith(线宽,用点表示单位),lineCap(线条结尾的样子),和lineJoin(线条之间的结合点的样子);但是在图层层面你只有一次机会设置这些属性。如果你想用不同颜色或风格来绘制多个形状,就不得不为每个形状准备一个图层了

    CAShapeLayer属性是CGPathRef类型,但是我们用UIBezierPath帮助类创建了图层路径,这样我们就不用考虑人工释放CGPath了

    #import "DrawingView.h"
    #import @interface ViewController ()
    @property (nonatomic, weak) IBOutlet UIView *containerView;
    @end
    @implementation ViewController
    - (void)viewDidLoad
    {
      [super viewDidLoad];
      //create path
      UIBezierPath *path = [[UIBezierPath alloc] init];
      [path moveToPoint:CGPointMake(175, 100)];
      ?
      [path addArcWithCenter:CGPointMake(150, 100) radius:25 startAngle:0 endAngle:2*M_PI clockwise:YES];
      [path moveToPoint:CGPointMake(150, 125)];
      [path addLineToPoint:CGPointMake(150, 175)];
      [path addLineToPoint:CGPointMake(125, 225)];
      [path moveToPoint:CGPointMake(150, 175)];
      [path addLineToPoint:CGPointMake(175, 225)];
      [path moveToPoint:CGPointMake(100, 150)];
      [path addLineToPoint:CGPointMake(200, 150)];
      //create shape layer
      CAShapeLayer *shapeLayer = [CAShapeLayer layer];
      shapeLayer.strokeColor = [UIColor redColor].CGColor;
      shapeLayer.fillColor = [UIColor clearColor].CGColor;
      shapeLayer.lineWidth = 5;
      shapeLayer.lineJoin = kCALineJoinRound;
      shapeLayer.lineCap = kCALineCapRound;
      shapeLayer.path = path.CGPath;
      //add it to our view
      [self.containerView.layer addSublayer:shapeLayer];
    }
    @end

    第二章里面提到了CAShapeLayer为创建圆角视图提供了一个方法,就是CALayer的cornerRadius属性(译者注:其实是在第四章提到的)。虽然使用CAShapeLayer类需要更多的工作,但是它有一个优势就是可以单独指定每个角。

    我们创建圆角举行其实就是人工绘制单独的直线和弧度,但是事实上UIBezierPath有自动绘制圆角矩形的构造方法,下面这段代码绘制了一个有三个圆角一个直角的矩形

    //define path parameters
    CGRect rect = CGRectMake(50, 50, 100, 100);
    CGSize radii = CGSizeMake(20, 20);
    UIRectCorner corners = UIRectCornerTopRight | UIRectCornerBottomRight | UIRectCornerBottomLeft;
    //create path
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:corners cornerRadii:radii];

    CATextLayer

    Core Animation提供了一个CALayer的子类CATextLayer,它以图层的形式包含了UILabel几乎所有的绘制特性,并且额外提供了一些新的特性。

    CATextLayer也要比UILabel渲染得快得多。很少有人知道在iOS 6及之前的版本,UILabel其实是通过WebKit来实现绘制的,这样就造成了当有很多文字的时候就会有极大的性能压力。而CATextLayer使用了Core text,并且渲染得非常快。用CATextLayer来实现一个UILabel:

    @interface ViewController ()
    @property (nonatomic, weak) IBOutlet UIView *labelView;
    @end
    @implementation ViewController
    - (void)viewDidLoad
    {
      [super viewDidLoad];
      //create a text layer
      CATextLayer *textLayer = [CATextLayer layer];
      textLayer.frame = self.labelView.bounds;
      [self.labelView.layer addSublayer:textLayer];
      //set text attributes
      textLayer.foregroundColor = [UIColor blackColor].CGColor;
      textLayer.alignmentMode = kCAAlignmentJustified;
      textLayer.wrapped = YES;
      //choose a font
      UIFont *font = [UIFont systemFontOfSize:15];
      //set layer font
      CFStringRef fontName = (__bridge CFStringRef)font.fontName;
      CGFontRef fontRef = CGFontCreateWithFontName(fontName);
      textLayer.font = fontRef;
      textLayer.fontSize = font.pointSize;
      CGFontRelease(fontRef);
      //choose some text
      NSString *text = @"Lorem ipsum dolor sit amet, consectetur adipiscing  elit. Quisque massa arcu, eleifend vel varius in, facilisis pulvinar  leo. Nunc quis nunc at mauris pharetra condimentum ut ac neque. Nunc elementum, libero ut porttitor dictum, diam odio congue lacus, vel  fringilla sapien diam at purus. Etiam suscipit pretium nunc sit amet  lobortis";
      //set layer text
      textLayer.string = text;
    }
    @end

    如果你自习看这个文本,你会发现一个奇怪的地方:这些文本有一些像素化了。这是因为并没有以Retina的方式渲染,第二章提到了这个contentScale属性,用来决定图层内容应该以怎样的分辨率来渲染。contentsScale并不关心屏幕的拉伸因素而总是默认为1.0。如果我们想以Retina的质量来显示文字,我们就得手动地设置CATextLayer的contentsScale属性,如下:textLayer.contentsScale = [UIScreen mainScreen].scale;

    富文本

    UIKit 的 UILabel 允许你通过在 IB 中简单的拖曳添加文本,但你不能改变文本的颜色和其中的单词。
        Core Graphics/Quartz几乎允许你做任何系统允许的事情,但你需要为每个字形计算位置,并画在屏幕上。


        CoreText正结合了这两者!你自己可以完全控制位置、布局、类似文本大小和颜色这样的属性,CoreText将帮你完善其它的东西??类似文本换行、字体呈现等等。


        然而,CoreText.framework本身非常庞大,学习成本较高,使用起来也不是很方便,所以一般不是特殊需要,很少会有人去使用它。


        随着iOS6 API的发布,文字显示的API越来越完善,其中一个重要的更新是在UITextField,UITextView和UILabel中加入了对AttributedString的支持,实现行距控制,字距控制,段落控制等高级功能也不必再去使用深奥的CoreText框架。


        而iOS7的发布,苹果又引入了TextKit,TextKit是一个快速而又现代化的文字排版和渲染引擎。

        TextKit并没有新增类,只是在原有的文本显示控件上进行了封装,可以在平时我们最喜欢使用的UILabel,UITextField,UITextView等控件里面使用,其最主要的作用就是为程序提供文字排版和渲染的功能。
        苹果引入TextKit的目的并非要取代已有的CoreText框架,虽然CoreText的主要作用也是用于文字的排版和渲染,但它是一种先进而又处于底层技术,如果我们需要将文本内容直接渲染到图形上下文(Graphics context)时,从性能和易用性来考虑,最佳方案就是使用CoreText。而如果我们需要直接利用苹果提供的一些控件(如UITextView、UILabel和UITextField等)对文字进行排版,那么借助于UIKit中TextKit提供的API无疑更为方便快捷。
        TextKit在文字处理方面具有非常强大的功能,并且开发者可以对TextKit进行定制和扩展。据悉,苹果利用了2年的时间来开发TextKit,相信这对许多开发者来说都是福音。

        然而,无论CoreText还是TextKit都是非常庞大的体系,而我们的需求通过一个简单小巧的AttributedString就可以轻松搞定,所以本文的关注点只有一个,那就是AttributedString。

    方法一:

    NSString *originStr = @"Hello,中秋节!";
        
        //方式一
        
        //创建 NSMutableAttributedString
        NSMutableAttributedString *attributedStr01 = [[NSMutableAttributedString alloc] initWithString: originStr];
        
        //添加属性
        
        //给所有字符设置字体为Zapfino,字体高度为15像素
        [attributedStr01 addAttribute: NSFontAttributeName value: [UIFont fontWithName: @"Zapfino" size: 15]
                                                           range: NSMakeRange(0, originStr.length)];
        //分段控制,最开始4个字符颜色设置成蓝色
        [attributedStr01 addAttribute: NSForegroundColorAttributeName value: [UIColor blueColor] range: NSMakeRange(0, 4)];
        //分段控制,第5个字符开始的3个字符,即第5、6、7字符设置为红色
        [attributedStr01 addAttribute: NSForegroundColorAttributeName value: [UIColor redColor] range: NSMakeRange(4, 3)];
        
        //赋值给显示控件label01的 attributedText
        _label01.attributedText = attributedStr01;

    方法二:

    //方式二的分段处理
        //第一段
        NSDictionary *attrDict1 = @{ NSFontAttributeName: [UIFont fontWithName: @"Zapfino" size: 15],
                                     NSForegroundColorAttributeName: [UIColor blueColor] };
        NSAttributedString *attrStr1 = [[NSAttributedString alloc] initWithString: [originStr substringWithRange: NSMakeRange(0, 4)] attributes: attrDict1];
        
        //第二段
        NSDictionary *attrDict2 = @{ NSFontAttributeName: [UIFont fontWithName: @"Zapfino" size: 15],
                                     NSForegroundColorAttributeName: [UIColor redColor] };
        NSAttributedString *attrStr2 = [[NSAttributedString alloc] initWithString: [originStr substringWithRange: NSMakeRange(4, 3)] attributes: attrDict2];
        
        //第三段
        NSDictionary *attrDict3 = @{ NSFontAttributeName: [UIFont fontWithName: @"Zapfino" size: 15],
                                     NSForegroundColorAttributeName: [UIColor blackColor] };
        NSAttributedString *attrStr3 = [[NSAttributedString alloc] initWithString: [originStr substringWithRange:
                                                                                    NSMakeRange(7, originStr.length - 4 - 3)] attributes: attrDict3];
        //合并
        NSMutableAttributedString *attributedStr03 = [[NSMutableAttributedString alloc] initWithAttributedString: attrStr1];
        [attributedStr03 appendAttributedString: attrStr2];
        [attributedStr03 appendAttributedString: attrStr3];
        
        _label03.attributedText = attributedStr03;

    AttributedString可设置的属性:

    // NSFontAttributeName                设置字体属性,默认值:字体:Helvetica(Neue) 字号:12
    // NSForegroundColorAttributeNam      设置字体颜色,取值为 UIColor对象,默认值为黑色
    // NSBackgroundColorAttributeName     设置字体所在区域背景颜色,取值为 UIColor对象,默认值为nil, 透明色
    // NSLigatureAttributeName            设置连体属性,取值为NSNumber 对象(整数),0 表示没有连体字符,1 表示使用默认的连体字符
    // NSKernAttributeName                设定字符间距,取值为 NSNumber 对象(整数),正值间距加宽,负值间距变窄
    // NSStrikethroughStyleAttributeName  设置删除线,取值为 NSNumber 对象(整数)
    // NSStrikethroughColorAttributeName  设置删除线颜色,取值为 UIColor 对象,默认值为黑色
    // NSUnderlineStyleAttributeName      设置下划线,取值为 NSNumber 对象(整数),枚举常量 NSUnderlineStyle中的值,与删除线类似
    // NSUnderlineColorAttributeName      设置下划线颜色,取值为 UIColor 对象,默认值为黑色
    // NSStrokeWidthAttributeName         设置笔画宽度,取值为 NSNumber 对象(整数),负值填充效果,正值中空效果
    // NSStrokeColorAttributeName         填充部分颜色,不是字体颜色,取值为 UIColor 对象
    // NSShadowAttributeName              设置阴影属性,取值为 NSShadow 对象
    // NSTextEffectAttributeName          设置文本特殊效果,取值为 NSString 对象,目前只有图版印刷效果可用:
    // NSBaselineOffsetAttributeName      设置基线偏移值,取值为 NSNumber (float),正值上偏,负值下偏
    // NSObliquenessAttributeName         设置字形倾斜度,取值为 NSNumber (float),正值右倾,负值左倾
    // NSExpansionAttributeName           设置文本横向拉伸属性,取值为 NSNumber (float),正值横向拉伸文本,负值横向压缩文本
    // NSWritingDirectionAttributeName    设置文字书写方向,从左向右书写或者从右向左书写
    // NSVerticalGlyphFormAttributeName   设置文字排版方向,取值为 NSNumber 对象(整数),0 表示横排文本,1 表示竖排文本
    // NSLinkAttributeName                设置链接属性,点击后调用浏览器打开指定URL地址
    // NSAttachmentAttributeName          设置文本附件,取值为NSTextAttachment对象,常用于文字图片混排
    // NSParagraphStyleAttributeName      设置文本段落排版格式,取值为 NSParagraphStyle 对象

    富文本的前半段原文地址为:http://blog.csdn.net/mylizh/article/details/39024353

    UILabel 的替代品

    我们已经证实了CATextLayer比UILabel有着更好的性能表现,同时还有额外的布局选项并且在iOS 5上支持富文本。但是与一般的标签比较而言会更加繁琐一些。如果我们真的在需求一个UILabel的可用替代品,最好是能够在Interface Builder上创建我们的标签,而且尽可能地像一般的视图一样正常工作。

    我们应该继承UILabel,然后添加一个子图层CATextLayer并重写显示文本的方法。但是仍然会有由UILabel的-drawRect:方法创建的空寄宿图。而且由于CALayer不支持自动缩放和自动布局,子视图并不是主动跟踪视图边界的大小,所以每次视图大小被更改,我们不得不手动更新子图层的边界

    我们真正想要的是一个用CATextLayer作为宿主图层的UILabel子类,这样就可以随着视图自动调整大小而且也没有冗余的寄宿图啦。

    #import "LayerLabel.h"
    #import @implementation LayerLabel
    + (Class)layerClass
    {
      //this makes our label create a CATextLayer //instead of a regular CALayer for its backing layer
      return [CATextLayer class];
    }
    - (CATextLayer *)textLayer
    {
      return (CATextLayer *)self.layer;
    }
    - (void)setUp
    {
      //set defaults from UILabel settings
      self.text = self.text;
      self.textColor = self.textColor;
      self.font = self.font;
      //we should really derive these from the UILabel settings too
      //but that's complicated, so for now we'll just hard-code them
      [self textLayer].alignmentMode = kCAAlignmentJustified;
      ?
      [self textLayer].wrapped = YES;
      [self.layer display];
    }
    - (id)initWithFrame:(CGRect)frame
    {
      //called when creating label programmatically
      if (self = [super initWithFrame:frame]) {
        [self setUp];
      }
      return self;
    }
    - (void)awakeFromNib
    {
      //called when creating label using Interface Builder
      [self setUp];
    }
    - (void)setText:(NSString *)text
    {
      super.text = text;
      //set layer text
      [self textLayer].string = text;
    }
    - (void)setTextColor:(UIColor *)textColor
    {
      super.textColor = textColor;
      //set layer text color
      [self textLayer].foregroundColor = textColor.CGColor;
    }
    - (void)setFont:(UIFont *)font
    {
      super.font = font;
      //set layer font
      CFStringRef fontName = (__bridge CFStringRef)font.fontName;
      CGFontRef fontRef = CGFontCreateWithFontName(fontName);
      [self textLayer].font = fontRef;
      [self textLayer].fontSize = font.pointSize;
      ?
      CGFontRelease(fontRef);
    }
    @end

    上面代码演示了一个UILabel子类LayerLabel用CATextLayer绘制它的问题,而不是调用一般的UILabel使用的较慢的-drawRect:方法。LayerLabel示例既可以用代码实现,也可以在Interface Builder实现,只要把普通的标签拖入视图之中,然后设置它的类是LayerLabel就可以了

    如果你运行代码,你会发现文本并没有像素化,而我们也没有设置contentsScale属性。把CATextLayer作为宿主图层的另一好处就是视图自动设置了contentsScale属性。

    如果你打算支持iOS 6及以上,基于CATextLayer的标签可能就有有些局限性。但是总得来说,如果想在app里面充分利用CALayer子类,用+layerClass来创建基于不同图层的视图是一个简单可复用的方法。

  • 相关阅读:
    为什么一个字节可以表示的有符号整数的范围是-128~+127?
    redis入门指南(一) ——简介及入门相关
    C Primer Plus(二)
    lua程序设计(一)
    证明:S = 1 + 1/2 + 1/4 + 1/8 + 1/16 + ·······,求证 S = 2
    C Primer Plus (一)
    C语言打印年历
    Spring IoC 自定义标签解析
    CentOS 7 Nacos 集群搭建
    CentOS 7 Zookeeper 和 Kafka 集群搭建
  • 原文地址:https://www.cnblogs.com/lihaiyin/p/4450790.html
Copyright © 2020-2023  润新知