iOS】帶箭頭的彈出菜單

前言

之前有在一些項目中用到過一些帶箭頭的彈出菜單,其實這個樣式的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傳送門

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章