http://blog.csdn.net/johnzhjfly/article/details/41175015?utm_source=tuicool&utm_medium=referral
我們在做iOS開發的時候,往往需要實現不規則形狀的頭像,如:

那如何去實現?
通常圖片都是矩形的,如果想在客戶端去實現不規則的頭像,需要自己去實現。
1.使用layer去實現, 見http://blog.csdn.net/johnzhjfly/article/details/39993345
2.使用CAShapeLayer, CALayer如何去實現
我們來看看如何使用CAShapeLayer去實現,
定義一個ShapedImageView,繼承于UIView, 代碼如下:
[objc] view plain copy 
#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,%20CAShapedLayer有個path的屬性,將內容Layer的mask設置為maskLayer,%20就可以獲取到我們想要的形狀。path我們可以使用CAMutablePath任意的構造,上述的代碼運行想過如下:
![]()
如果將代碼改成
[objc] view plain copy 
_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]; 的效果:![]()
如果將代碼改成:
[objc] view plain copy 
CGMutablePathRef path = CGPathCreateMutable(); CGPoint origin = self.bounds.origin; CGFloat radius = CGRectGetWidth(self.bounds) / 2; CGPathMoveToPoint(path, NULL, origin.x, origin.y + 22 *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 + 22 * radius, origin.y, origin.x + 22 * radius, origin.y + radius, radius); CGPathAddArcToPoint(path, NULL, origin.x + 22 * radius, origin.y + 22 * radius, origin.x + radius, origin.y + 2 * radius, radius); CGPathAddLineToPoint(path, NULL, origin.x, origin.y + 22 * 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呢?答案是肯定的,代碼如下:
[objc] view plain copy 
_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畫出來,這里就不詳細講解了。示例代碼如下:
[objc] view plain copy 
- (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:33 * M_PI / 2 clockwise:TRUE]; [path moveToPoint:CGPointMake(fooImage.size.width, 0)]; [path addArcWithCenter:CGPointMake(fooImage.size.width - radius, radius) radius:_radius startAngle:33 * M_PI / 2 endAngle:22 * 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;