日常开发过程中,经常会碰到自定义按钮的需求,例如:微信中的图片部分的尖角。本身就是根据聊天的气泡进行“塑形”的,如下图就是微信中图片“气泡化”显示。
要达成目标,我们需要解决的问题有下面几个:
1、点击没有图片的部分,不响应点击操作
2、根据指定的mask图片,进行图片合成&剪切
问题1:根据图片,设定响应区域
我们建立一个UIButton的子类,变量imageMask用来记录图片的形状,UIButton每次点击的时候,都会调用:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
来判断当前点击的按钮是否需要响应,所以,我们在UIButton的子类(KMAutoShapeButton)中响应该函数:
// 判断当前point对应的alpha是否在0.1以内,设置的阀值是0.1
- (BOOL)isAlphaVisibleAtPoint:(CGPoint)point forImage:(UIImage *)image
{
CGSize iSize = image.size;
CGSize bSize = self.bounds.size;
point.x *= (bSize.width != 0) ? (iSize.width / bSize.width) : 1;
point.y *= (bSize.height != 0) ? (iSize.height / bSize.height) : 1;
CGColorRef pixelColor = [[self.imageMask colorAtPixel:point] CGColor];
CGFloat alpha = CGColorGetAlpha(pixelColor);
return alpha >= kAlphaVisibleThreshold;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
// Return NO if even super returns NO (i.e., if point lies outside our bounds)
BOOL superResult = [super pointInside:point withEvent:event];
if (!superResult) {
return superResult;
}
if (CGPointEqualToPoint(self.previousTouchPoint, point)) {
return self.previousTouchHitTestResponse;
}
self.previousTouchPoint = point;
self.previousTouchHitTestResponse = [self isAlphaVisibleAtPoint:point forImage:self.imageMask];
return self.previousTouchHitTestResponse;
}
在-(BOOL)pointInside:withEvent: 中,先判断父类的结果并返回。
再次判断 当前点击位置是否等于上次的点击位置,如果是,返回上次的结果。在响应过程中一次点击可能会多次调用-(BOOL)pointInside:withEvent:
否则返回当前次判断返回结果。
问题2:根据制定的mask图片,进行图片合成&剪切
解决了问题1,我们只是能根据提供的mask图片检测响应区域,下面讨论怎么在:
- (void)setImage:(UIImage *)image forState:(UIControlState)state;
- (void)setBackgroundImage:(UIImage *)image forState:(UIControlState)state;
的函数里面对要设置的图片进行剪切。
在图片绘制CGContext系列函数里面, CGContextClipToMask 是根据指定mask图片的alpha值进行剪切,由此,我们可以通过mask图片&CGContextClipToMask 函数,将设置的Image进行剪切然后设置到UIButton中。
UIImage+shape.m剪切部分:
- (UIImage *)imageWithMask:(UIImage*)imageMask
{
// 转换坐标
imageMask = [imageMask flipVertical];
UIGraphicsBeginImageContext(CGSizeMake(self.size.width, self.size.height));
CGContextRef context = UIGraphicsGetCurrentContext();
CGImageRef maskRef = [imageMask fitSize:self.size].CGImage;
CGContextClipToMask(context, CGRectMake(0, 0, self.size.width, self.size.height), maskRef);
[self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
UIImage *reSizeImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return reSizeImage;
}
- (UIImage*)flipVertical
{
UIImage *image = nil;
switch (self.imageOrientation) {
case UIImageOrientationUp:
{
image = [UIImage imageWithCGImage:self.CGImage scale:1 orientation:UIImageOrientationDownMirrored];
break;
}
case UIImageOrientationDown:
{
image = [UIImage imageWithCGImage:self.CGImage scale:1 orientation:UIImageOrientationUpMirrored];
break;
}
case UIImageOrientationLeft:
{
image = [UIImage imageWithCGImage:self.CGImage scale:1 orientation:UIImageOrientationLeftMirrored];
break;
}
case UIImageOrientationRight:
{
image = [UIImage imageWithCGImage:self.CGImage scale:1 orientation:UIImageOrientationRightMirrored];
break;
}
case UIImageOrientationUpMirrored:
{
image = [UIImage imageWithCGImage:self.CGImage scale:1 orientation:UIImageOrientationDown];
break;
}
case UIImageOrientationDownMirrored:
{
image = [UIImage imageWithCGImage:self.CGImage scale:1 orientation:UIImageOrientationUp];
break;
}
case UIImageOrientationLeftMirrored:
{
image = [UIImage imageWithCGImage:self.CGImage scale:1 orientation:UIImageOrientationLeft];
break;
}
case UIImageOrientationRightMirrored:
{
image = [UIImage imageWithCGImage:self.CGImage scale:1 orientation:UIImageOrientationRight];
break;
}
default:
break;
}
return image;
}
KMAutoShapeButton.m重载图片设置部分:
// Reset the Hit Test Cache when a new image is assigned to the button
- (void)setImage:(UIImage *)image forState:(UIControlState)state
{
[super setImage:self.imageMask ? [image imageWithMask:self.imageMask] : image forState:state];
[self resetHitTestCache];
}
- (void)setBackgroundImage:(UIImage *)image forState:(UIControlState)state
{
[super setBackgroundImage:self.imageMask ? [image imageWithMask:self.imageMask] : image forState:state];
[self resetHitTestCache];
}
- (void)resetHitTestCache
{
self.previousTouchPoint = CGPointMake(CGFLOAT_MIN, CGFLOAT_MIN);
self.previousTouchHitTestResponse = NO;
}
通过 按钮点击检测、图片剪切 等步骤,可以得到我们最终需要的图片效果,并且在需要变换形状的时候,仅仅将mask图片替换掉即可。
代码下载:http://download.csdn.net/detail/u013494674/8046173