圆角
圆角问题已经说了好久,总是说系统方法切圆角会有性能方面的问题,但是看了那么多零碎的文章,今天决定总结一下设置圆角的方法。
方法
-
Layer的 cornerRadius + masksToBounds
masksToBounds会触发离屏渲染,GPU会开辟一块新的渲染缓冲区来进行渲染,当圆角操作到达一定数量,会触发缓冲区的频繁合并和上下文频繁切换,性能上受到影响从而有掉帧卡顿的现象。
-
贝塞尔曲线UIBezierPath
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)]; imageView.image = [UIImage imageNamed:@"TestImage.jpg"]; // 开始对imageView进行画图 UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 0.0); // 使用贝塞尔曲线画出一个圆形图 [[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.frame.size.width] addClip]; [imageView drawRect:imageView.bounds]; imageView.image = UIGraphicsGetImageFromCurrentImageContext(); // 结束画图 UIGraphicsEndImageContext(); [self.view addSubview:imageView];
其中
UIGraphicsBeginImageContextWithOption(CGSize size, BOOL opaque, CGFloat scale)
各参数的含义:size:新创建的文图上下文大小
opaque:透明开关,如果图形完全不用透明,设置为YES以优化位图的存储
scale:缩放因子。虽然这里可以用
[UIScreen mainScreen].scale
来获取,但实际上设为0后,系统会自动设置正确的比例 -
Core Graphics框架画出一个圆角
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)]; imageView.image = [UIImage imageNamed:@"TestImage.jpg"]; // 开始对imageView进行画图 UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 0.0); // 获取图形上下文 CGContextRef ctx = UIGraphicsGetCurrentContext(); // 设置一个范围 CGRect rect = CGRectMake(0, 0, imageView.frame.size.width, imageView.frame.size.height); // 根据一个rect创建一个椭圆 CGContextAddEllipseInRect(ctx, rect); // 裁剪 CGContextClip(ctx); // 将原照片画到图形上下文 [imageView.image drawInRect:rect]; // 从上下文上获取裁剪后的照片 UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); // 关闭上下文 UIGraphicsEndImageContext(); imageView.image = image; [self.view addSubview:imageView];
-
CAShapeLayer和UIBezierPath设置圆角
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 200, 100, 100)]; imageView.image = [UIImage imageNamed:@"TestImage.jpg"]; UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:imageView.bounds.size]; CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init]; // 设置大小 maskLayer.frame = imageView.bounds; // 设置图形样子 maskLayer.path = maskPath.CGPath; imageView.layer.mask = maskLayer; [self.view addSubview:imageView];
这种方法可以指定需要的角为圆角:
+ (UIBezierPath *)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii; corners参数指定了要成为圆角的角, 枚举类型如下: typedef NS_OPTIONS(NSUInteger, UIRectCorner) { UIRectCornerTopLeft = 1 << 0, UIRectCornerTopRight = 1 << 1, UIRectCornerBottomLeft = 1 << 2, UIRectCornerBottomRight = 1 << 3, UIRectCornerAllCorners = ~0UL };
-
加遮罩
加一个圆形遮罩盖在上面,完美解决。
性能检测
Instrument 工具里的 Core Animation
方法1:
iOS 9.0之前,UIImageView
和 UIButton
使用layer设置圆角都会触发离屏渲染。
iOS 9.0之后,UIButton
会造成离屏渲染,而UIImageView的png格式不会触发离屏渲染,但如果设置其他效果如阴影等还是会触发离屏渲染。
对于文本视图实现圆角(UILabel, UIView, UITextField, UITextView
),均只进行cornerRadius
设置,不进行masksToBounds
的设置,前三个控件没有离屏渲染,UITextView产生了离屏渲染,猜测是因为UITextView继承自UIScrollView。
方法2 方法3
无离屏渲染。
方法4
掉帧严重
总结
对于具体项目里仅一两个圆角,用layer设置圆角省时省力,目前的手机处理性能完全能够支持;对于cell里需要展示大量圆角的项目,可以采用加遮罩的方式。