對於大多數iOS開發來講,這個是不多遇到的,畢竟一個不規則的按鈕再移動端不常見,可是免不了會遇到某些特殊的需求,好比一個餅狀圖,好比一個扇形圖,好比中國地圖中的每一個省..... 咱們極可能在某一次開發中須要實現不規則形狀的按鈕,那麼咱們怎麼去實現呢? 其實這只是一個很小很小的知識點,卻有不少人不會去考慮,因此就遇到了這樣的狀況git
偶然有一天,朋友給了我一套產品原型,他說叫我幫他寫幾個按鈕,很簡單的按鈕,我當時感受有坑,可是也沒有想太多,想着原本這段時間有點閒,就幫下忙, 因而他發來原型圖 github
喲,就是很簡單的4+1個按鈕蠻 先寫4個正方形的九宮格式的按鈕,再在中心加一個圓形按鈕就行了呀. 因而接下來他又發來產品設計圖 bash
果真,有了設計以後,瞬間好看了好多, 可是想象中也不復雜呀. 先用一個view當底圖,而後5個按鈕加在這個view上,而後view來個半徑,不就結束了嗎? 可是事實真的是這麼簡單嗎?ide
顯然不是,一個很明顯的結果就是,你會發現你使用了圓形半徑的不顯示的部分仍然可以響應按鈕的點擊. 這個是什麼鬼,其實很簡單的原理,就是雖然咱們設置了半徑,讓圓形外的圖層不顯示,可是並非表明其不存在,其仍然是能夠點擊的,這樣在某些狀況下就不符合按鈕的設計標準了,由於咱們想要的是所見即所得工具
那麼所見即所得便成了我要實現的目標. 還得從按鈕上下手,不要想得這麼簡單,雖然這個案例是一個很簡單的比較偏正常形狀的一個按鈕,可是我須要將其想象成一個很複雜的不規則的圖形來處理動畫
1.由於是自定義形狀的按鈕,那麼說白了,它仍是一個按鈕,咱們沒有必要自定義複雜的控件,繼承UIButton便可ui
2.構思如何實現特殊形狀 其實這塊很容易想到,實現一個圓形按鈕,就是對layer進行修改形狀而已atom
若是你有閱讀過iOS核心動畫高級技巧的話,你很容易發現咱們想要的東西 spa
是的,這裏咱們能看到幾個關鍵點,一個原來的背景+一個mask遮罩 = 一個自定義形狀的圖形設計
引用到咱們想要的按鈕上,咱們就會發現一樣適用, 那麼咱們必需要在適用圖片來製做mask嗎? 很顯然,咱們在開發中不可能這麼去作,那麼咱們如何自定義一個形狀呢?
因而咱們可以很方便的使用UIBezierPath來繪製成咱們想要的形狀,而後使用CAShapeLayer來根據形狀建立圖層的路徑 建立完成以後,咱們使用這樣的圖層來當作按鈕的mask便可
因此對於咱們來講比較有用的就是一個貝塞爾曲線的定製 因而很簡單的一個不規則按鈕的類便可實現
#import <UIKit/UIKit.h>
@interface IrregularButton : UIButton
@property(nonatomic, strong)UIBezierPath *maskPath;
@end
#import "IrregularButton.h"
@implementation IrregularButton
-(void)setMaskPath:(UIBezierPath *)maskPath{
_maskPath = maskPath;
CAShapeLayer *layer = [[CAShapeLayer alloc]init];
layer.path = maskPath.CGPath;
self.layer.mask = layer;
}
@end
複製代碼
當咱們須要定製一個特殊按鈕的時候,咱們只要繼承該類 ,或者直接使用該類
,並給maskPath賦予一個咱們想要的路徑便可
因而上面的四個半圓按鈕甚至全圓形的按鈕咱們就能夠輕鬆實現
#import "IrregularButton.h"
typedef NS_ENUM(NSUInteger, QuarterCircleType) {
QuarterCircleTypeTopNone,
QuarterCircleTypeTopLeft,
QuarterCircleTypeTopRight,
QuarterCircleTypeBottomLeft,
QuarterCircleTypeBottomRight,
QuarterCircleTypeAllCircle,
};
@interface QuarterCircleButton : IrregularButton
@property(nonatomic, assign)QuarterCircleType circleType;
@end
#import "QuarterCircleButton.h"
@implementation QuarterCircleButton
-(void)setFrame:(CGRect)frame{
[super setFrame:frame];
self.circleType = self.circleType;
}
-(void)setCircleType:(QuarterCircleType)circleType{
_circleType = circleType;
CGFloat width = self.frame.size.width;
CGFloat height = self.frame.size.height;
switch (circleType) {
case QuarterCircleTypeTopLeft:{
CGPoint bottomRightPoint = CGPointMake(width, height);
CGPoint bottomLeftPoint = CGPointMake(0, height);
UIBezierPath *path = [[UIBezierPath alloc]init];
[path moveToPoint:bottomRightPoint];
[path addLineToPoint:bottomLeftPoint];
[path addArcWithCenter:bottomRightPoint radius:MAX(width, height) startAngle:M_PI endAngle:-M_PI_2 clockwise:YES];
[path addLineToPoint:bottomRightPoint];
[path closePath];
self.maskPath = path;
}
break;
case QuarterCircleTypeTopRight:{
CGPoint bottomLeftPoint = CGPointMake(0, height);
CGPoint topLeftPoint = CGPointMake(0, 0);
UIBezierPath *path = [[UIBezierPath alloc]init];
[path moveToPoint:bottomLeftPoint];
[path addLineToPoint:topLeftPoint];
[path addArcWithCenter:bottomLeftPoint radius:MAX(width, height) startAngle:-M_PI endAngle:0 clockwise:YES];
[path addLineToPoint:bottomLeftPoint];
[path closePath];
self.maskPath = path;
}
break;
case QuarterCircleTypeBottomLeft:{
CGPoint topRightPoint = CGPointMake(width, 0);
CGPoint bottomRightPoint = CGPointMake(width, height);
UIBezierPath *path = [[UIBezierPath alloc]init];
[path moveToPoint:topRightPoint];
[path addLineToPoint:bottomRightPoint];
[path addArcWithCenter:topRightPoint radius:MAX(width, height) startAngle:M_PI_2 endAngle:M_PI clockwise:YES];
[path addLineToPoint:topRightPoint];
[path closePath];
self.maskPath = path;
}
break;
case QuarterCircleTypeBottomRight:{
CGPoint topLeftPoint = CGPointMake(0, 0);
CGPoint topRightPoint = CGPointMake(width, 0);
UIBezierPath *path = [[UIBezierPath alloc]init];
[path moveToPoint:topLeftPoint];
[path addLineToPoint:topRightPoint];
[path addArcWithCenter:topLeftPoint radius:MAX(width, height) startAngle:0 endAngle:M_PI_2 clockwise:YES];
[path addLineToPoint:topLeftPoint];
[path closePath];
self.maskPath = path;
}
break;
case QuarterCircleTypeAllCircle:{
CGPoint centerPoint = CGPointMake(width/2, height/2);
UIBezierPath *path = [[UIBezierPath alloc]init];
[path addArcWithCenter:centerPoint radius:MAX(width/2, height/2) startAngle:0 endAngle:M_PI * 2 clockwise:YES];
[path closePath];
self.maskPath = path;
}
break;
default:
break;
}
}
複製代碼
因而,很輕鬆的我建立了這樣的圖形
可是問題仍然不可忽視的,又來了,在半圓形外面的四個區域仍然能夠相應按鈕點擊, 解釋下來也就是mask外面的區域雖然看不到,可是其仍然屬於按鈕的區域,仍然能夠響應點擊,
因此咱們須要規避 那麼如何規避呢? 其畢竟仍然是一個不規則的形狀啊? 其實咱們前期已經作好準備了....
3.規避不規則區域外的點擊 咱們只要簡單的實現這個點擊是否響應的方法便可 因而完整的不規則按鈕的實現是這樣的
#import <UIKit/UIKit.h>
@interface IrregularButton : UIButton
@property(nonatomic, strong)UIBezierPath *maskPath;
@end
#import "IrregularButton.h"
@implementation IrregularButton
-(void)setMaskPath:(UIBezierPath *)maskPath{
_maskPath = maskPath;
CAShapeLayer *layer = [[CAShapeLayer alloc]init];
layer.path = maskPath.CGPath;
self.layer.mask = layer;
}
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
BOOL res = [super pointInside:point withEvent:event];
if (res) {
if (!self.maskPath || [self.maskPath containsPoint:point]) {
return YES;
}
return NO;
}
return res;
}
@end
複製代碼
咱們只要保證點擊的point在咱們的mask的區域內便可(這裏要保證咱們的mask是一個封閉的區域),在mask區域內容許點擊,區域外則不容許點擊, 因而一個maskPath被咱們使用了兩次,第一次是設置遮罩顯示不規則按鈕,第二次是判斷點擊時候的響應區域
至此,可以適應各類自定義形狀的基類按鈕便完成了 當咱們想要實現的時候,咱們只要根據原型中的不規則形狀繪製咱們想要的圖形來創造一個貝塞爾曲線便可(這部分由於形狀不一樣,咱們只能本身繪製)
固然咱們也有第三方工具
若是你以爲繪製一個不規則形狀太麻煩的話(偷懶) paintCode也能夠幫你的忙,你只要動動鼠標既能夠跟相似ps同樣的繪製一個不規則圖形,而這個工具會自動幫你把貝塞爾曲線的代碼給寫好,你只要複製進去便可好了demo奉上吧... github.com/spicyShrimp…