iOS 不规则的ImageView
我们在做iOS开发的时候,往往须要实现不规则形状的头像,如:
那怎样去实现?
通常图片都是矩形的,假设想在client去实现不规则的头像,须要自己去实现。
1.使用layer去实现, 见http://blog.csdn.net/johnzhjfly/article/details/39993345
2.使用CAShapeLayer, CALayer怎样去实现
我们来看看怎样使用CAShapeLayer去实现,
定义一个ShapedImageView。继承于UIView, 代码例如以下:
#import "ShapedImageView.h" @interface ShapedImageView() { CALayer *_contentLayer; CAShapeLayer *_maskLayer; } @end @implementation ShapedImageView - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self setup]; } return self; } - (void)setup { _maskLayer = [CAShapeLayer layer]; _maskLayer.path = [UIBezierPath bezierPathWithOvalInRect:self.bounds].CGPath; _maskLayer.fillColor = [UIColor blackColor].CGColor; _maskLayer.strokeColor = [UIColor redColor].CGColor; _maskLayer.frame = self.bounds; _maskLayer.contentsCenter = CGRectMake(0.5, 0.5, 0.1, 0.1); _maskLayer.contentsScale = [UIScreen mainScreen].scale; _contentLayer = [CALayer layer]; _contentLayer.mask = _maskLayer; _contentLayer.frame = self.bounds; [self.layer addSublayer:_contentLayer]; } - (void)setImage:(UIImage *)image { _contentLayer.contents = (id)image.CGImage; } @end声明了用于maskLayer个CAShapedLayer。 CAShapedLayer有个path的属性。将内容Layer的mask设置为maskLayer, 就能够获取到我们想要的形状。
path我们能够使用CAMutablePath随意的构造,上述的代码执行想过例如以下:
假设将代码改成
_maskLayer = [CAShapeLayer layer]; _maskLayer.path = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:20].CGPath; _maskLayer.fillColor = [UIColor blackColor].CGColor; _maskLayer.strokeColor = [UIColor redColor].CGColor; _maskLayer.frame = self.bounds; _maskLayer.contentsCenter = CGRectMake(0.5, 0.5, 0.1, 0.1); _maskLayer.contentsScale = [UIScreen mainScreen].scale; //很关键设置自己主动拉伸的效果且不变形 _contentLayer = [CALayer layer]; _contentLayer.mask = _maskLayer; _contentLayer.frame = self.bounds; [self.layer addSublayer:_contentLayer];的效果:
假设将代码改成:
CGMutablePathRef path = CGPathCreateMutable(); CGPoint origin = self.bounds.origin; CGFloat radius = CGRectGetWidth(self.bounds) / 2; CGPathMoveToPoint(path, NULL, origin.x, origin.y + 2 *radius); CGPathMoveToPoint(path, NULL, origin.x, origin.y + radius); CGPathAddArcToPoint(path, NULL, origin.x, origin.y, origin.x + radius, origin.y, radius); CGPathAddArcToPoint(path, NULL, origin.x + 2 * radius, origin.y, origin.x + 2 * radius, origin.y + radius, radius); CGPathAddArcToPoint(path, NULL, origin.x + 2 * radius, origin.y + 2 * radius, origin.x + radius, origin.y + 2 * radius, radius); CGPathAddLineToPoint(path, NULL, origin.x, origin.y + 2 * radius); _maskLayer = [CAShapeLayer layer]; _maskLayer.path = path; _maskLayer.fillColor = [UIColor blackColor].CGColor; _maskLayer.strokeColor = [UIColor clearColor].CGColor; _maskLayer.frame = self.bounds; _maskLayer.contentsCenter = CGRectMake(0.5, 0.5, 0.1, 0.1); _maskLayer.contentsScale = [UIScreen mainScreen].scale; //很关键设置自己主动拉伸的效果且不变形 _contentLayer = [CALayer layer]; _contentLayer.mask = _maskLayer; _contentLayer.frame = self.bounds; [self.layer addSublayer:_contentLayer];将是这个效果:
理论上我们能够构造出随意想要的形状。可是有些形状假设你不熟悉几何知识的话是构造不出正确的
path的,从代码上我们能够看到我们能够通过设置CALayer的contents属性来设置显示的内容,那我们
是不是能够通过设置CAShapedLayer的contents来设置maskLayer呢?答案是肯定的,代码例如以下:
_maskLayer = [CAShapeLayer layer]; _maskLayer.fillColor = [UIColor blackColor].CGColor; _maskLayer.strokeColor = [UIColor clearColor].CGColor; _maskLayer.frame = self.bounds; _maskLayer.contentsCenter = CGRectMake(0.5, 0.5, 0.1, 0.1); _maskLayer.contentsScale = [UIScreen mainScreen].scale; //很关键设置自己主动拉伸的效果且不变形 _maskLayer.contents = (id)[UIImage imageNamed:@"gray_bubble_right@2x.png"].CGImage; _contentLayer = [CALayer layer]; _contentLayer.mask = _maskLayer; _contentLayer.frame = self.bounds; [self.layer addSublayer:_contentLayer];
gray_bubble_right就是你想要的形状,执行效果例如以下:
不停的改变CALayer的一个坏处就是很的损耗性能,假设你有一个cell的列表。每一个列表有个头像的话。高速滑动的时候。你会发现很的卡。
此时理想的解决方式是使用CGPath或者UIBezierPath构建不规则的path,然后clip画出来。这里就不具体解说了。
演示样例代码例如以下:
- (UIImage *)maskImage { // start with an image UIImage * fooImage = self;//[UIImage imageNamed:@"foo.png"]; CGRect imageRect = CGRectMake(0, 0, fooImage.size.width, fooImage.size.height); // set the implicit graphics context ("canvas") to a bitmap context for images UIGraphicsBeginImageContextWithOptions(imageRect.size, NO, 0.0); // create a bezier path defining rounded corners UIBezierPath * path = [UIBezierPath bezierPathWithRect:imageRect]; CGFloat radius = fooImage.size.width / 2.5; CGFloat _radius = radius; //construct your shaped path [path moveToPoint:CGPointMake(0, 0)]; [path addArcWithCenter:CGPointMake(radius, radius) radius:_radius startAngle:M_PI endAngle:3 * M_PI / 2 clockwise:TRUE]; [path moveToPoint:CGPointMake(fooImage.size.width, 0)]; [path addArcWithCenter:CGPointMake(fooImage.size.width - radius, radius) radius:_radius startAngle:3 * M_PI / 2 endAngle:2 * M_PI clockwise:TRUE]; [path moveToPoint:CGPointMake(fooImage.size.width, fooImage.size.height)]; [path addArcWithCenter:CGPointMake(fooImage.size.width - radius, fooImage.size.height - radius) radius:_radius startAngle:0 endAngle:M_PI / 2 clockwise:TRUE]; [path moveToPoint:CGPointMake(0, fooImage.size.height)]; [path addArcWithCenter:CGPointMake(radius, fooImage.size.height - radius) radius:_radius startAngle:M_PI / 2 endAngle:M_PI clockwise:TRUE]; path.flatness = 1000; path.lineCapStyle = kCGLineCapRound; path.lineJoinStyle = kCGLineJoinRound; // use this path for clipping in the implicit context [path addClip]; // draw the image into the implicit context [fooImage drawInRect:imageRect]; // save the clipped image from the implicit context into an image UIImage *maskedImage = UIGraphicsGetImageFromCurrentImageContext(); // cleanup UIGraphicsEndImageContext(); return maskedImage; }