iOS 圓角放前,最后一次研究它了忿磅,真的

原文鏈接

對(duì)于目前手機(jī)的性能來(lái)說(shuō),對(duì)于少量圓角設(shè)置帶來(lái)的離屏渲染問(wèn)題凭语,帶來(lái)的卡頓問(wèn)題已經(jīng)并不是那么的明顯了贝乎,但是對(duì)于一些類型的app來(lái)說(shuō),項(xiàng)目中會(huì)有大量的圓角設(shè)置叽粹,同時(shí)又對(duì)性能要求比較高的話览效,那么避免圓角設(shè)置帶來(lái)的性能問(wèn)題,還是有必要解決的虫几。

iOS設(shè)置圓角方法以及指定位置設(shè)圓角
iOS圓角設(shè)置的幾種方法的對(duì)比以及性能檢測(cè)

上面兩篇文章中介紹了iOS常見(jiàn)的設(shè)置圓角的方法锤灿,以及對(duì)應(yīng)方法性能的檢測(cè)??。

然而在實(shí)際項(xiàng)目中仍然有其他問(wèn)題存在辆脸,也就是前面的總結(jié)仍有不足但校,下面提出兩個(gè)遇到的問(wèn)題

針對(duì)Masonry布局的控件,如何進(jìn)行圓角設(shè)置啡氢?

其實(shí)解決方案非常簡(jiǎn)單状囱,調(diào)用layoutIfNeeded 函數(shù)進(jìn)行布局,然后所約束的控件才會(huì)按照約束條件倘是,生成當(dāng)前布局相應(yīng)的framebounds亭枷,那么就可以進(jìn)行各種樣式的圓角設(shè)置

針對(duì)四個(gè)不同的角,設(shè)置不同的半徑搀崭,如何操作叨粘?

方法

實(shí)際上與之前文章里所描述的一種方法混合圖層一個(gè)道理。在所需切圓角的控件上加一層瘤睹,利用貝塞爾切割出不同角所需要的圓角半徑

思路

思路

如上圖所示升敲,標(biāo)記了幾個(gè)點(diǎn),進(jìn)行畫圓弧以及連線轰传,具體代碼如下

    [bezierPath moveToPoint:self.hLeftUpPoint];
    [bezierPath addLineToPoint:self.hRightUpPoint];
    [bezierPath addArcWithCenter:self.centerRightUp radius:self.radius_TopRight ?: self.radius startAngle:(CGFloat)(M_PI * 3 / 2) endAngle:(CGFloat)(M_PI * 2) clockwise: true];
    [bezierPath addLineToPoint:self.vRightDownPoint];
    [bezierPath addArcWithCenter:self.centerRightDown radius:self.radius_BottomRight ?: self.radius startAngle: 0 endAngle: (CGFloat)(M_PI / 2) clockwise: true];
    [bezierPath addLineToPoint:self.hLeftDownPoint];
    [bezierPath addArcWithCenter:self.centerLeftDown radius:self.radius_BottomLeft ?: self.radius startAngle: (CGFloat)(M_PI / 2) endAngle: (CGFloat)(M_PI) clockwise: true];
    [bezierPath addLineToPoint:self.vLeftUpPoint];
    [bezierPath addArcWithCenter:self.centerLeftUp radius:self.radius_TopLeft ?: self.radius startAngle: (CGFloat)(M_PI) endAngle: (CGFloat)(M_PI * 3 / 2) clockwise: true];
    [bezierPath addLineToPoint:self.hLeftUpPoint];
    [bezierPath closePath];
    
    [bezierPath moveToPoint:CGPointZero];
    [bezierPath addLineToPoint:CGPointMake(0, self.rectSize.height)];
    [bezierPath addLineToPoint:CGPointMake(self.rectSize.width, self.rectSize.height)];
    [bezierPath addLineToPoint:CGPointMake(self.rectSize.width, 0)];
    [bezierPath addLineToPoint:CGPointZero];
    [bezierPath closePath];

上面的代碼具體是切割圓角驴党,具體圓角半徑與對(duì)應(yīng)點(diǎn)的位置有關(guān),合理的利用上圖所描述的點(diǎn)的坐標(biāo)获茬,可以切割出不同的弧度港庄,同時(shí)利用位移枚舉UIRectCorner可實(shí)現(xiàn)任意一個(gè)或多個(gè)角的切割。

具體代碼

第一步: 新增了一個(gè)UIVIew+CornerRadius的分類锦茁,.h文件里聲明了兩種切割圓角的方法攘轩。

@interface UIView (CornerRadius)

/// 繪制裁剪圓角,可選任意角進(jìn)行切割
/// @param radius 圓角半徑
/// @param fillColor 填充色
/// @param type 裁剪角
- (void)drawCircularBeadImageWithRadius:(float)radius fillColor:(UIColor *)fillColor CornerStyle:(UIRectCorner)type;

/// 繪制裁剪圓角码俩,可選任意角進(jìn)行切割度帮,且每個(gè)角可選切割任意半徑
/// @param radius_TL 左上角半徑
/// @param radius_TR 右上角半徑
/// @param radius_BL 左下角半徑
/// @param radius_BR 右下角半徑
/// @param fillColor 填充色
- (void)drawCircularBeadImageWithRadius_TL:(float)radius_TL
                                 radius_TR:(float)radius_TR
                                 radius_BL:(float)radius_BL
                                 radius_BR:(float)radius_BR
                                 fillColor:(UIColor *)fillColor;
@end

具體的實(shí)現(xiàn):

@implementation UIView (CornerRadius)

- (void)drawCircularBeadImageWithRadius:(float)radius fillColor:(UIColor *)fillColor CornerStyle:(UIRectCorner)type {
    UIImageView *imgView = [[UIImageView alloc] init];
    [self addSubview:imgView];
    [imgView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.width.height.equalTo(self);
    }];
    [self layoutIfNeeded];
    
    imgView.image = [ZBCornerRadiusTool drawAntiRoundedCornerImageWithRadius:radius rectSize:imgView.frame.size fillColor:fillColor cornerStyle:type];
}

- (void)drawCircularBeadImageWithRadius_TL:(float)radius_TL
                             radius_TR:(float)radius_TR
                             radius_BL:(float)radius_BL
                             radius_BR:(float)radius_BR
                             fillColor:(UIColor *)fillColor {
    UIImageView *imgView = [[UIImageView alloc] init];
    [self addSubview:imgView];
    [imgView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.width.height.equalTo(self);
    }];
    [self layoutIfNeeded];
    
    imgView.image = [ZBCornerRadiusTool drawAntiRoundedCornerWithRadius_TL:radius_TL radius_TR:radius_TR radius_BL:radius_BL radius_BR:radius_BR rectSize:imgView.frame.size fillColor:fillColor];
}

@end

從上面代碼可以看出,在這里我們?cè)谛枰懈顖A角的控件上,加了一層UIImageView笨篷,同時(shí)這里利用Masonry對(duì)新增的圖層進(jìn)行了約束瞳秽,最重要的一句代碼[self layoutIfNeeded];千萬(wàn)不能少,這句話率翅,就解決了上面所說(shuō)的第一個(gè)問(wèn)題练俐。

這里代碼均是針對(duì)利用Masonry布局約束的控件去實(shí)現(xiàn)圓角,如果是利用frame的也非常方面冕臭,直接利用控件的size便可腺晾。筆者只提供一下思路

第二步: 添加兩個(gè)工具類,先看ZBCornerRadiusTool

/// 繪制裁剪圓角后圖片
/// @param radius 圓角
/// @param rectSize 視圖尺寸
/// @param fillColor 填充色
/// @param cornerStyle 圓角位置
+ (UIImage *)drawAntiRoundedCornerImageWithRadius:(float)radius rectSize:(CGSize)rectSize fillColor:(UIColor *)fillColor cornerStyle:(UIRectCorner)cornerStyle {
    UIGraphicsBeginImageContextWithOptions(rectSize, false, [UIScreen mainScreen].scale);
    CGContextRef contextRef = UIGraphicsGetCurrentContext();
    UIBezierPath *bezierPath = [UIBezierPath bezierPath];
    [ZBCornerRadiusTool configPathWithBezierPath:&bezierPath Radius:radius rectSize:rectSize cornerStyle:cornerStyle fillColor:fillColor];
    CGContextDrawPath(contextRef, kCGPathFillStroke);
    UIImage *antiRoundedCornerImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return antiRoundedCornerImage;
}


/// 繪制裁剪圓角后圖片
/// @param radius_TL 左上角半徑
/// @param radius_TR 右上角半徑
/// @param radius_BL 左下角半徑
/// @param radius_BR 右下角半徑
/// @param rectSize 視圖尺寸
/// @param fillColor 填充色
+ (UIImage *)drawAntiRoundedCornerWithRadius_TL:(float)radius_TL
                                      radius_TR:(float)radius_TR
                                      radius_BL:(float)radius_BL
                                      radius_BR:(float)radius_BR
                                       rectSize:(CGSize)rectSize
                                      fillColor:(UIColor *)fillColor {
    UIGraphicsBeginImageContextWithOptions(rectSize, false, [UIScreen mainScreen].scale);
    CGContextRef contextRef = UIGraphicsGetCurrentContext();
    UIBezierPath *bezierPath = [UIBezierPath bezierPath];
    [ZBCornerRadiusTool configPathWithBezierPath:&bezierPath radius_TL:radius_TL radius_TR:radius_TR radius_BL:radius_BL radius_BR:radius_BR rectSize:rectSize fillColor:fillColor];
    CGContextDrawPath(contextRef, kCGPathFillStroke);
    UIImage *antiRoundedCornerImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return antiRoundedCornerImage;
}

關(guān)鍵方法:

// 對(duì)任意一個(gè)或多個(gè)角設(shè)置圓角
+ (void)configPathWithBezierPath:(UIBezierPath **)bezier Radius:(float)radius rectSize:(CGSize)rectSize cornerStyle:(UIRectCorner)cornerStyle fillColor:(UIColor *)fillColor {
    UIBezierPath *bezierPath = *bezier;
    BezierPathTool *tool = [[BezierPathTool alloc]initWithRadius:radius rectSize:rectSize fillColor:fillColor];
    
    if (cornerStyle == UIRectCornerAllCorners) {
        [tool configAllCornerPoint];
    }else {
        if (cornerStyle == UIRectCornerTopLeft) {
            [tool configTopLeftPoint];
        }
        if (cornerStyle == UIRectCornerTopRight) {
            [tool configTopRightPoint];
        }
        if (cornerStyle == UIRectCornerBottomLeft) {
            [tool configBottomLeftPoint];
        }
        if (cornerStyle == UIRectCornerBottomRight) {
            [tool configBottomRightPoint];
        }
    }
    bezierPath = [tool configCornerBezierPath:bezierPath];
}

// 對(duì)四個(gè)角分別設(shè)置圓角辜贵,0即為不進(jìn)行圓角設(shè)置
+ (void)configPathWithBezierPath:(UIBezierPath **)bezier
                       radius_TL:(float)radius_TL
                       radius_TR:(float)radius_TR
                       radius_BL:(float)radius_BL
                       radius_BR:(float)radius_BR
                        rectSize:(CGSize)rectSize
                       fillColor:(UIColor *)fillColor {
    UIBezierPath *bezierPath = *bezier;
    BezierPathTool *tool = [[BezierPathTool alloc]initWithRadius_TopLeft:radius_TL radius_TopRight:radius_TR radius_BottomLeft:radius_BL radius_BottomRight:radius_BR rectSize:rectSize fillColor:fillColor];
    bezierPath = [tool configCornerBezierPath:bezierPath];
}

最后一步: OK悯蝉,最后一步的實(shí)現(xiàn)都寫在了BezierPathTool類里,對(duì)應(yīng)兩個(gè)初始化方法托慨,分別有不同的初始化

- (instancetype)initWithRadius:(float)radius rectSize:(CGSize)rectSize fillColor:(UIColor *)fillColor {
    if (self = [super init]) {
        _radius = radius;
        _rectSize = rectSize;
        _fillColor = fillColor;
        [self configData]; // 默認(rèn)每個(gè)點(diǎn)都是在四個(gè)拐角
    }
    return self;
}

- (instancetype)initWithRadius_TopLeft:(float)radius_TL
                       radius_TopRight:(float)radius_TR
                     radius_BottomLeft:(float)radius_BL
                    radius_BottomRight:(float)radius_BR
                              rectSize:(CGSize)rectSize
                             fillColor:(UIColor *)fillColor {
    if (self = [super init]) {
        _radius_TopLeft = radius_TL;
        _radius_TopRight = radius_TR;
        _radius_BottomLeft = radius_BL;
        _radius_BottomRight = radius_BR;
        _rectSize = rectSize;
        _fillColor = fillColor;
        [self configCornerPointData];
    }
    return self;
}

- (void)configData {
    _hLeftUpPoint = CGPointMake(0, 0);
    _hRightUpPoint = CGPointMake(_rectSize.width, 0);
    _hLeftDownPoint = CGPointMake(0, _rectSize.height);
    
    _vLeftUpPoint = CGPointMake(0, 0);
    _vRightDownPoint = CGPointMake(_rectSize.width, _rectSize.height);
    
    _centerLeftUp = CGPointMake(0, 0);
    _centerRightUp = CGPointMake(_rectSize.width, 0);
    _centerLeftDown = CGPointMake(0, _rectSize.height);
    _centerRightDown = CGPointMake(_rectSize.width, _rectSize.height);
}

- (void)configCornerPointData {
    [self configTopLeftPoint];
    [self configTopRightPoint];
    [self configBottomLeftPoint];
    [self configBottomRightPoint];
}

對(duì)于第一種初始化鼻由,對(duì)于需要標(biāo)記的點(diǎn)的默認(rèn)坐標(biāo)分別對(duì)應(yīng)四個(gè)角,目的是為了方便可以對(duì)任意一個(gè)或多個(gè)角進(jìn)行切割厚棵,如下代碼

//四個(gè)角均切割
- (void)configAllCornerPoint {
    _hLeftUpPoint = CGPointMake(_radius, 0);//(10,0)
    _hRightUpPoint = CGPointMake(_rectSize.width - _radius, 0);//(30,0)
    _hLeftDownPoint = CGPointMake(_radius, _rectSize.height);//(10,40)
    
    _vLeftUpPoint = CGPointMake(0, _radius);//(0,10)
    _vRightDownPoint = CGPointMake(_rectSize.width, _rectSize.height - _radius);//(40,30)
    
    _centerLeftUp = CGPointMake(_radius, _radius);//(10,10)
    _centerRightUp = CGPointMake(_rectSize.width - _radius, _radius);//(30,10)
    _centerLeftDown = CGPointMake(_radius, _rectSize.height - _radius);//(10,30)
    _centerRightDown = CGPointMake(_rectSize.width - _radius, _rectSize.height - _radius);//(30,30)
}

//左上角圓角設(shè)置
- (void)configTopLeftPoint {
    float radius = _radius_TopLeft ?: _radius;
    _hLeftUpPoint = CGPointMake(radius, 0);//(10,0)
    _vLeftUpPoint = CGPointMake(0, radius);//(0,10)
    _centerLeftUp = CGPointMake(radius, radius);//(10,10)
}
//右上角圓角設(shè)置
- (void)configTopRightPoint {
    float radius = _radius_TopRight ?: _radius;
    _hRightUpPoint = CGPointMake(_rectSize.width - radius, 0);//(30,0)
    _centerRightUp = CGPointMake(_rectSize.width - radius, radius);//(30,10)
}
////左下角圓角設(shè)置
- (void)configBottomLeftPoint {
    float radius = _radius_BottomLeft ?: _radius;
    _hLeftDownPoint = CGPointMake(radius, _rectSize.height);//(10,40)
    _centerLeftDown = CGPointMake(radius, _rectSize.height - radius);//(10,30)
}
//右下角圓角設(shè)置
- (void)configBottomRightPoint {
    float radius = _radius_BottomRight ?: _radius;
    _vRightDownPoint = CGPointMake(_rectSize.width, _rectSize.height - radius);//(40,30)
    _centerRightDown = CGPointMake(_rectSize.width - radius, _rectSize.height - radius);//(30,30)
}

嗯蕉世,最后就是調(diào)用貝塞爾曲線方法進(jìn)行設(shè)置就行,代碼在最上面已經(jīng)給出婆硬,這里就不過(guò)多贅述了狠轻。

下面是分別對(duì)UIImageViewUIButton的切割效果圖,也不會(huì)產(chǎn)生離屏渲染

效果圖

上面描述柿祈,如有不正確之處哈误,希望大家指出,謝謝

問(wèn)題拋出

一個(gè)好的封裝躏嚎,不需要任何依賴或者說(shuō)需要減少對(duì)外界的依賴,那么問(wèn)題就來(lái)了菩貌,上面所封裝的依然依賴于控件的約束或者是frame卢佣。

思考了很久,這個(gè)問(wèn)題筆者依然沒(méi)有很好的解決方案箭阶,如果有好的方法虚茶,希望大家指出,再次感謝

demo傳送門

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末仇参,一起剝皮案震驚了整個(gè)濱河市嘹叫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诈乒,老刑警劉巖罩扇,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡喂饥,警方通過(guò)查閱死者的電腦和手機(jī)消约,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)员帮,“玉大人或粮,你說(shuō)我怎么就攤上這事±谈撸” “怎么了氯材?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)硝岗。 經(jīng)常有香客問(wèn)我氢哮,道長(zhǎng),這世上最難降的妖魔是什么辈讶? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任命浴,我火速辦了婚禮,結(jié)果婚禮上贱除,老公的妹妹穿的比我還像新娘生闲。我一直安慰自己,他們只是感情好月幌,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布碍讯。 她就那樣靜靜地躺著,像睡著了一般扯躺。 火紅的嫁衣襯著肌膚如雪捉兴。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天录语,我揣著相機(jī)與錄音倍啥,去河邊找鬼。 笑死澎埠,一個(gè)胖子當(dāng)著我的面吹牛虽缕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蒲稳,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼氮趋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了江耀?” 一聲冷哼從身側(cè)響起剩胁,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎祥国,沒(méi)想到半個(gè)月后昵观,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年索昂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了建车。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡椒惨,死狀恐怖缤至,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情康谆,我是刑警寧澤领斥,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站沃暗,受9級(jí)特大地震影響月洛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜孽锥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一嚼黔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惜辑,春花似錦唬涧、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至抵卫,卻和暖如春狮荔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背介粘。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工殖氏, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人姻采。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓受葛,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親偎谁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 今天是我日更的第12天铐望,收獲還談不上,習(xí)慣也還沒(méi)養(yǎng)成。 想想第一次日更正蛙,每天忙的磕磕碰碰督弓,緊緊張張,歪歪扭扭乒验,寫到...
    長(zhǎng)姐享受慢生活閱讀 245評(píng)論 4 4
  • 可記得 清晨的雨點(diǎn)兒 親吻著我們的頭發(fā) 可記得 我們翻山越嶺 去采那山谷里的野花 可記得 你說(shuō) 雨點(diǎn)再大我也不怕 ...
    orchidyuan閱讀 161評(píng)論 0 1