前言
之前有在一些項目中用到過一些帶箭頭的彈出菜單,其實這個樣式的UI組件還是比較常見的,QQ和微信,支付寶等等很多App都有類似的UI組件,所以我把之前項目中的相關代碼抽取出來,然後做了個封裝,所有就有了今天這篇內容。
其實也不算直接封裝之前的代碼,之前的實現方式是用modal出控制器,然後自定義轉場動畫,同時在轉場代理中通過 UIPresentationController 改變視圖的frame實現的,同時箭頭也是用一個帶箭頭的背景圖實現的。不過當前封裝的這個UI視圖則是放棄了帶控制器的這種方式,是用純視圖的組合封裝來實現的。
實現效果
運行效果.gif
使用方式
使用方式很簡單,先將CWPopMenu.h和CWPopMenu.m這兩個文件拖到項目裏。
導入頭文件之後直接初始化並調用showMenu顯示
self.menu = [[CWPopMenu alloc]initWithArrow:CGPointMake(self.view.frame.size.width-25, _sumHeight) menuSize:CGSizeMake(130, 200) arrowStyle:CWPopMenuArrowTopfooter];
//代理是UITableView的代理方法
_menu.dataSource = self;
_menu.delegate = self;
//設置菜單的背景色(默認是白色)
_menu.menuViewBgColor = [UIColor whiteColor];
//YES代表使用動畫
[_menu showMenu:YES];
方法以及參數的意義
/**
@param point 箭頭箭尖的座標
@param size 菜單的視圖的大小
@param position 箭頭的方位(同時決定縮放動畫的錨點)
*/
- (instancetype)initWithArrow:(CGPoint)point menuSize:(CGSize)size arrowStyle:(CWPopMenuArrowPosition)position;
//顯示 *屬性設置需要在show之前設置纔會生效*
- (void)showMenu:(BOOL)animated;
//關閉
- (void)closeMenu:(BOOL)animated;
部分可設置的屬性
//箭頭在上或者下邊的前中後位置枚舉(從左到右)
typedef NS_ENUM(NSInteger, CWPopMenuArrowPosition) {
CWPopMenuArrowTopHeader = 0,//默認從0開始
CWPopMenuArrowTopCenter,
CWPopMenuArrowTopfooter,
CWPopMenuArrowBottomHeader,
CWPopMenuArrowBottomCenter,
CWPopMenuArrowBottomfooter,
};
//箭頭方位的枚舉會決定箭頭在菜單視圖的位置,以及縮放動畫的錨點位置
//菜單蒙版的透明度 default bgViewAlpha = 0.2;設置alpha屬性時會被bgAlpha接管
@property (nonatomic, assign)CGFloat bgAlpha;
//菜單背景色 default menuViewBgColor = [UIColor whiteColor];
@property (nonatomic, assign)UIColor *menuViewBgColor;
//菜單圓角 default menuRadius = 5.f;
@property (nonatomic, assign)CGFloat menuRadius;
//箭頭寬度 default arrowWidth = 15.f;
@property (nonatomic, assign)CGFloat arrowWidth;
//箭頭高度 default arrowHeight = 10.f;
@property (nonatomic, assign)CGFloat arrowHeight;
其實這個UI視圖是沒什麼難度的東西,菜單核心視圖就是個UITableView,就是箭頭繪製要進行各種判斷寫起來稍微麻煩點。
核心方法
//根據位置枚舉以及箭頭高度和寬度計算繪製箭頭的點
- (void)computeArrowPosition:(CWPopMenuArrowPosition)arrowPosition
{
CGRect _menuFrame = _menuView.frame;
CGFloat menuX = _menuFrame.origin.x;
CGFloat menuY = _menuFrame.origin.y;
CGFloat menuWidth = _menuFrame.size.width;
CGFloat menuHeight = _menuFrame.size.height;
switch (_arrowPosition) {
case CWPopMenuArrowTopHeader:
{
CGPoint origin = CGPointMake(menuX+distance, menuY);
CGPoint peak = CGPointMake(menuX+_arrowWidth*0.5 +distance, menuY-_arrowHeight);
CGPoint terminus = CGPointMake(menuX+_arrowWidth+distance, menuY);
[self drawArrowInLayer:origin peak:peak terminus:terminus];
}
break;
case CWPopMenuArrowTopCenter:
{
CGPoint origin = CGPointMake(menuX+(menuWidth-_arrowWidth)*0.5, menuY);
CGPoint peak = CGPointMake(menuX+menuWidth*0.5, menuY-_arrowHeight);
CGPoint terminus = CGPointMake(menuX+(menuWidth+_arrowWidth)*0.5, menuY);
[self drawArrowInLayer:origin peak:peak terminus:terminus];
}
break;
case CWPopMenuArrowTopfooter:
{
CGPoint origin = CGPointMake(menuX+menuWidth-_arrowWidth-distance, menuY);
CGPoint peak = CGPointMake(menuX+menuWidth-_arrowWidth*0.5-distance, menuY-_arrowHeight);
CGPoint terminus = CGPointMake(menuX+menuWidth-distance, menuY);
[self drawArrowInLayer:origin peak:peak terminus:terminus];
}
break;
case CWPopMenuArrowBottomHeader:
{
CGPoint origin = CGPointMake(menuX+distance, menuY+menuHeight);
CGPoint peak = CGPointMake(menuX+_arrowWidth*0.5+distance, menuY+menuHeight+_arrowHeight);
CGPoint terminus = CGPointMake(menuX+_arrowWidth+distance, menuY+menuHeight);
[self drawArrowInLayer:origin peak:peak terminus:terminus];
}
break;
case CWPopMenuArrowBottomCenter:
{
CGPoint origin = CGPointMake(menuX+(menuWidth-_arrowWidth)*0.5, menuY+menuHeight);
CGPoint peak = CGPointMake(menuX+menuWidth*0.5, menuY+menuHeight+_arrowHeight);
CGPoint terminus = CGPointMake(menuX+(menuWidth+_arrowWidth)*0.5, menuY+menuHeight);
[self drawArrowInLayer:origin peak:peak terminus:terminus];
}
break;
case CWPopMenuArrowBottomfooter:
{
CGPoint origin = CGPointMake(menuX+menuWidth-_arrowWidth-distance, menuY+menuHeight);
CGPoint peak = CGPointMake(menuX+menuWidth-_arrowWidth*0.5-distance, menuY+menuHeight+_arrowHeight);
CGPoint terminus = CGPointMake(menuX+menuWidth-distance, menuY+menuHeight);
[self drawArrowInLayer:origin peak:peak terminus:terminus];
}
break;
default:
break;
}
}
//繪製箭頭
- (void)drawArrowInLayer:(CGPoint)origin peak:(CGPoint)peak terminus:(CGPoint)terminus{
//定義畫圖的path
self.shLayer = [[CAShapeLayer alloc]init];
UIBezierPath *path = [[UIBezierPath alloc] init];
_shLayer.fillColor = self.menuViewBgColor.CGColor;
//path移動到開始畫圖的位置
[path moveToPoint:origin];
[path addLineToPoint:peak];
[path addLineToPoint:terminus];
_shLayer.path = [path CGPath];
//關閉path
[path closePath];
[self.layer addSublayer:_shLayer];
}
寫在最後
希望對看到的人有些幫助,如果各位看官覺得有所幫助請點個贊。
PS:附上demo傳送門