一個方法實現:添加一個帶箭頭還有圓角的邊框

/** 邊框位置 */
typedef NS_ENUM(NSInteger, FCBorderPosition) {
    FCBorderPositionTop     = 1 << 0,
    FCBorderPositionLeft    = 1 << 1,
    FCBorderPositionBottom  = 1 << 2,
    FCBorderPositionRight   = 1 << 3,
    FCBorderPositionAll     = FCBorderPositionTop | FCBorderPositionLeft | FCBorderPositionBottom | FCBorderPositionRight,
};

/**
 給view添加一個帶箭頭的邊框
 
 @param direction 箭頭朝向
 @param offset 箭頭的座標,若是是在左右朝向,傳箭頭中心位置的y值;若是是上下朝向,傳箭頭中心位置x值
 @param width 箭頭的寬度
 @param height 箭頭的高度
 @param cornerRadius 圓角半徑,<=0不設圓角
 @param borderWidth 邊框寬度
 @param borderColor 邊框顏色
 */
- (void)addArrowBorderAt:(FCBorderPosition)direction 
offset:(CGFloat)offset 
width:(CGFloat)width 
height:(CGFloat)height 
cornerRadius:(CGFloat)cornerRadius 
borderWidth:(CGFloat)borderWidth 
borderColor:(UIColor *)borderColor;
複製代碼

這個方法很長,由於確實要求複雜:git

  • 要箭頭
  • 要圓角
  • 還要有邊框

來張圖解釋一下效果:github

這時仿的QQ的彈框效果。bash

各個參數的意思註釋裏應該很清楚了。下面是實現代碼:函數

-(void)addArrowBorderAt:(FCBorderPosition)direction offset:(CGFloat)offset width:(CGFloat)width height:(CGFloat)height cornerRadius:(CGFloat)cornerRadius borderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor{
    
    [self removeFCBorder];
    
    //只有一個mask層
    CAShapeLayer *mask = [[CAShapeLayer alloc] init];
    mask.frame = self.bounds;
    mask.name = FCBorderMaskName;
    self.layer.mask = mask;
    
    UIBezierPath *path = [[UIBezierPath alloc] init];
    
    CGFloat minX = 0, minY = 0, maxX = self.bounds.size.width, maxY = self.bounds.size.height;
    if (direction == FCBorderPositionTop) {
        minY = height;
    }else if (direction == FCBorderPositionRight){
        maxX -= height;
    }else if (direction == FCBorderPositionLeft){
        minX += height;
    }else if (direction == FCBorderPositionBottom){
        maxY -= height;
    }
    
    //上邊
    [path moveToPoint:CGPointMake(minX+cornerRadius, minY)];
    if (direction == FCBorderPositionTop) {
        [path addLineToPoint:CGPointMake(offset-width/2, minY)];
        [path addLineToPoint:CGPointMake(offset, minY-height)];
        [path addLineToPoint:CGPointMake(offset+width/2, minY)];
    }
    [path addLineToPoint:CGPointMake(maxX-cornerRadius, minY)];
    
    //右上角
    if (cornerRadius>0) {
        [path addArcWithCenter:CGPointMake(maxX-cornerRadius, minY+cornerRadius) radius:cornerRadius startAngle:-M_PI_2 endAngle:0 clockwise:YES];
    }
    
    
    
    //右邊
    if (direction == FCBorderPositionRight) {
        [path addLineToPoint:CGPointMake(maxX, offset-width/2)];
        [path addLineToPoint:CGPointMake(maxX+height, offset)];
        [path addLineToPoint:CGPointMake(maxX, offset+width/2)];
    }
    [path addLineToPoint:CGPointMake(maxX, maxY-cornerRadius)];
    
    //右下角
    if (cornerRadius>0) {
        [path addArcWithCenter:CGPointMake(maxX-cornerRadius, maxY-cornerRadius) radius:cornerRadius startAngle:0 endAngle:M_PI_2 clockwise:YES];
    }
    
    
    
    //下邊
    if (direction == FCBorderPositionBottom) {
        [path addLineToPoint:CGPointMake(offset-width/2, maxY)];
        [path addLineToPoint:CGPointMake(offset, maxY+height)];
        [path addLineToPoint:CGPointMake(offset+width/2, maxY)];
    }
    [path addLineToPoint:CGPointMake(minX+cornerRadius, maxY)];
    
    //左下角
    if (cornerRadius>0) {
        [path addArcWithCenter:CGPointMake(minX+cornerRadius, maxY-cornerRadius) radius:cornerRadius startAngle:M_PI_2 endAngle:M_PI clockwise:YES];
    }
    
    
    //右邊
    if (direction == FCBorderPositionLeft) {
        [path addLineToPoint:CGPointMake(minX, offset-width/2)];
        [path addLineToPoint:CGPointMake(minX-height, offset)];
        [path addLineToPoint:CGPointMake(minX, offset+width/2)];
    }
    [path addLineToPoint:CGPointMake(minX, minY+cornerRadius)];
    
    //右下角
    if (cornerRadius>0) {
        [path addArcWithCenter:CGPointMake(minX+cornerRadius, minY+cornerRadius) radius:cornerRadius startAngle:M_PI endAngle:M_PI_2*3 clockwise:YES];
    }
    
    mask.path = [path CGPath];
    
    if (borderWidth>0) {
        CAShapeLayer *border = [[CAShapeLayer alloc] init];
        border.path = [path CGPath];
        border.strokeColor = borderColor.CGColor;
        border.lineWidth = borderWidth*2;
        border.fillColor = [UIColor clearColor].CGColor;
        [self.layer addSublayer:border];
        
        [self markFCBorder:border];
    }
}

複製代碼

首先說思路:layer的mask能夠把不須要的地方遮住,因此繪製一個特定的mask給layer就能夠把圓角和箭頭切割出來。而後使用一個另一個layer,它的圖形跟mask同樣,可是用來繪製邊框,把這個layer加上去邊框就有了。工具

大部分的工做都是在繪製mask的路徑,分爲4個邊一步一步的加,看一個邊的狀況就能夠了解了:spa

//上邊
    //從左上角開始
    [path moveToPoint:CGPointMake(minX+cornerRadius, minY)];
    if (direction == FCBorderPositionTop) {
        //多增長3個點,路線饒了一下
        [path addLineToPoint:CGPointMake(offset-width/2, minY)];
        [path addLineToPoint:CGPointMake(offset, minY-height)];
        [path addLineToPoint:CGPointMake(offset+width/2, minY)];
    }
    //到達右上角
    [path addLineToPoint:CGPointMake(maxX-cornerRadius, minY)];
    
    //右上角
    if (cornerRadius>0) {
        [path addArcWithCenter:CGPointMake(maxX-cornerRadius, minY+cornerRadius) radius:cornerRadius startAngle:-M_PI_2 endAngle:0 clockwise:YES];
    }
複製代碼

整個過程就是從左上角畫線,畫到右上角。 若是箭頭在上邊,則在上邊加入箭頭,加入箭頭其實就是多加3個點:箭頭的左下角、頂點和右上角。這樣走了一下折線。code

若是有圓角就再繪製圓角。cdn

邊框就是一個CAShapeLayer,沒什麼可說的,有個問題是它使用了和mask一樣的path,這樣它的線條有一半是在mask以外,會被切掉,因此lineWidth作了一個乘2的處理:border.lineWidth = borderWidth*2;對象

最後[self removeFCBorder];[self markFCBorder:border];是爲了標記加入的邊框layer,保證它的惟一,不然這個方法多調用幾回就會有多個邊框疊加,特別是frame發生改變後,就會出現奇怪的邊框了。blog

static NSString *FCBorderLayerKey = @"FCBorderLayerKey";
static NSString *FCBorderMaskName = @"FCBorderMaskName";

-(void)markFCBorder:(CALayer *)layer{
    
    objc_setAssociatedObject(self, &FCBorderLayerKey, layer, OBJC_ASSOCIATION_RETAIN);
}

-(void)removeFCBorder{
    if ([self.layer.mask.name isEqualToString:FCBorderMaskName]) {
        self.layer.mask = nil;
    }
    
    CAShapeLayer *oldLayer = objc_getAssociatedObject(self, &FCBorderLayerKey);
    if (oldLayer) [oldLayer removeFromSuperlayer];
}
複製代碼

這裏使用了runtime的關聯對象函數作了聯繫。

另:寫了一個彈框的工具類,歡迎查看FCPopActionView

相關文章
相關標籤/搜索