iOS 高效切圆角方法总结
ChenghuiBai Lv3

[TOC]

UIView设置圆角

四个圆角

对于 contents 无内容或者内容的背景透明(无涉及到圆角以外的区域)的layer,直接设置layer的 backgroundColor 和 cornerRadius 属性来绘制圆角:

  1. UIView的contents无内容可以直接通过设置cornerRadius达到效果。
  2. UILable的contents也一样,所以也可通过
    设置cornerRadius达到效果。不过label不能直接设置backgroundColor,因为这样设置的是contents的backgroundColor,需要设置layer. backgroundColor。
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor blackColor];
view.layer.cornerRadius = 3.f;
// 以下两行,任写一行
view.layer.masksToBounds = NO;
view.clipToBounds = NO;
// 以下两行,千万不要加!
view.layer.masksToBounds = YES;
view.clipToBounds = YES;

注意点:UIView 只要设置图层的 cornerRadius 属性即可(不明白的话,可以看看官方文档里对 cornerRadius 的描述),如果设置 layer.masksToBounds = YES,会造成不必要的离屏渲染。

单独某个方向的圆角/特殊情况需要设置layer.masksToBounds,就不要通过cornerRadius方式了
@implementation UIView (RounderCorner)

- (void)hh_addRounderCornerWithRadius:(CGFloat)radius size:(CGSize)size corners:(UIRectCorner)corner
{

//绘制一个圆角图片
UIImage *image = [UIImage hh_corver:.....];

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, size.width, size.height)];
[imageView setImage:image];
[self insertSubview:imageView atIndex:0];
}

文本类视图

UITextField

UITextField有两种实现方法

// 天然支持设置圆角边框
UITextField *textField = [[UITextField alloc] init];
textField.borderStyle = UITextBorderStyleRoundedRect;
// 与 UIView 类似
UITextField *textField = [[UITextField alloc] init];
textField.layer.cornerRadius = cornerRadius;
UITextView
// 与 UIView 类似
UITextView *textView = [[UITextView alloc] init];
textView.layer.cornerRadius = cornerRadius;
UILabel
UILabel *label = [[UILabel alloc] init];
// 重点在此!!设置视图的图层背景色,千万不要直接设置 label.backgroundColor
label.layer.backgroundColor = [UIColor grayColor].CGColor;
label.layer.cornerRadius = cornerRadius;

其它

UIButton

说明:UIButton 的背景图片,如果是复杂的图片,可以依靠 UI 切图来实现。如果是简单的纯色背景图片,可以利用代码绘制带圆角的图片。

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
// 设置 UIButton 的背景图片。
[button setBackgroundImage:image forState:UIControlStateNormal];

背景图片绘制方法

+ (UIImage *)pureColorImageWithSize:(CGSize)size color:(UIColor *)color cornRadius:(CGFloat)cornRadius {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, size.width, size.height)];
view.backgroundColor = color;
view.layer.cornerRadius = cornerRadius;
// 下面方法,第一个参数表示区域大小。第二个参数表示是否是非透明的。如果需要显示半透明效果,需要传NO,否则传YES。第三个参数是屏幕密度
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, [UIScreen mainScreen].scale);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

return image;
}
UIImageView

UIImageView 有四种方式实现圆角:

1、截取图片方式(性能较好,基本不掉帧)

+ (UIImage *)dx_imageByRoundCornerRadius:(CGFloat)radius
corners:(UIRectCorner)corners
size:(CGSize)size
fillColor:(UIColor *)fillColor
{
if (corners != UIRectCornerAllCorners) {
UIRectCorner tmp = 0;
if (corners & UIRectCornerTopLeft) tmp |= UIRectCornerBottomLeft;
if (corners & UIRectCornerTopRight) tmp |= UIRectCornerBottomRight;
if (corners & UIRectCornerBottomLeft) tmp |= UIRectCornerTopLeft;
if (corners & UIRectCornerBottomRight) tmp |= UIRectCornerTopRight;
corners = tmp;
}

UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect rect = CGRectMake(0, 0, size.width, size.height);
CGContextScaleCTM(context, 1, -1);
CGContextTranslateCTM(context, 0, -rect.size.height);

UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)];
[fillColor set];
[path fill];

[path addClip];
CGContextDrawPath(context, kCGPathFill);

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}

- (void)tx_addCornerWithRadius:(CGFloat)radius corner:(UIRectCorner)corner {
self.image = [self.image dx_imageAddCornerWithRadius:radius andSize:self.bounds.size];
}

实际开发中,网络图片最好截图之后存入缓存,下次直接使用,不用再裁减。YYWebImageView的设计方案即如此。
YYWebImage
fork SDWebImage库修改实现

2、贝塞尔曲线切割圆角(不推荐,掉帧严重)

- (UIImageView *)roundedRectImageViewWithCornerRadius:(CGFloat)cornerRadius {
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:cornerRadius];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.path = bezierPath.CGPath;
self.layer.mask = layer;

return self;
}

3、绘制四个角的遮罩(使用场景受限)

在 UIImageView 上添加一个四个角有内容,其它部分是透明的视图,只对 UIImageView 圆角部分进行遮挡。但要保证被遮挡的部分背景色要与周围背景相同,避免穿帮。所以当 UIImageView 处于一个复杂的背景时,是不适合使用这个方法的。

4、最不推荐做法(当一个页面只有少量圆角图片时才推荐使用)

UIImageView *imageView = [[UIImageView alloc] init];
imageView.layer.cornerRadius = 5.f;
imageView.layer.masksToBounds = YES;

扩展:其他会导致离屏渲染的解决方案

以下离屏渲染操作,按对性能影响等级从高到低进行排序:

1. shadows(阴影)

方案:在设置完layer的shadow属性之后,设置layer.shadowPath = [UIBezierPath pathWithCGRect:view.bounds].CGPath;

2.圆角(前边已解决过)
3.mask遮罩

方案:不用mask

4. allowsGroupOpacity(组不透明)

开启CALayer的 allowsGroupOpacity 属性后,子 layer 在视觉上的透明度的上限是其父 layer 的 opacity (对应UIView的 alpha ),并且从 iOS 7 以后默认全局开启了这个功能,这样做是为了让子视图与其容器视图保持同样的透明度。
方案:关闭 allowsGroupOpacity 属性,按产品需求自己控制layer透明度。

5. edge antialiasing(抗锯齿)

方案:不设置 allowsEdgeAntialiasing 属性为YES(默认为NO)

6. shouldRasterize(光栅化)

当视图内容是静态不变时,设置 shouldRasterize(光栅化)为YES,此方案最为实用方便。

view.layer.shouldRasterize = true;
view.layer.rasterizationScale = view.layer.contentsScale;

但当视图内容是动态变化(如后台下载图片完毕后切换到主线程设置)时,使用此方案反而为增加系统负荷。

7.Core Graphics API(核心绘图)

Core Graphics API(核心绘图)的绘制操作会导致CPU的离屏渲染。
方案:放到后台线程中进行。

Demo

demo

学习

https://www.jianshu.com/p/f8a3400836b5
https://www.jianshu.com/p/e879aeff93f3
https://www.jianshu.com/p/b9bef82eace1
https://www.jianshu.com/p/3141c1177d35

  • Post title:iOS 高效切圆角方法总结
  • Post author:ChenghuiBai
  • Create time:2017-07-01 21:21:24
  • Post link:https://baichenghui.github.io/2017/07/01/iOS 高效切圆角方法总结/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.