美文网首页专注iOS开发iOS开发 技术集锦iOS学习开发
iOS 裁剪View指定的某几个角为圆角以及遇到的问题

iOS 裁剪View指定的某几个角为圆角以及遇到的问题

作者: 陨之希留leo | 来源:发表于2017-04-14 14:48 被阅读2165次

    最近遇到一个需求,label样式设置如下,并不是四个圆角,而是右上和右下设置圆角。

    指定裁剪圆角

    代码很简单,写一个方法,需要裁剪的控件调用以下方法即可。

    -(void)changeLabelStyle:(UILabel *)label{
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:label.bounds byRoundingCorners:UIRectCornerTopRight | UIRectCornerBottomRight cornerRadii:CGSizeMake(20, 20)];
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
    maskLayer.frame = label.bounds;
    maskLayer.path = maskPath.CGPath;
    label.layer.mask = maskLayer;
    }
    
    • (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;在这个方法中,第二个参数UIRectCorner是一个枚举类型,即你需要指定裁剪为圆角的view的角。
    typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
    UIRectCornerTopLeft     = 1 << 0,  //左上角
    UIRectCornerTopRight    = 1 << 1, //右上角
    UIRectCornerBottomLeft  = 1 << 2, //左下角
    UIRectCornerBottomRight = 1 << 3, //右下角
    UIRectCornerAllCorners  = ~0UL   //四个角
    };
    

    如果你想裁剪多个不同的角,可以用"|"进行组合,传入多个即可。方法中的CAShapeLayer是一个神奇的子类,可以定制很多有趣的UI控件,具体可以参考简书里的这篇文章放肆的使用UIBezierPath和CAShapeLayer画各种图形


    从结果来看符合要求,但是这篇文章的重点不在这,而是我下面要说的事情,也是我实现这个需求过程中遇到的问题,记录一下。

    这个需求是要在两个页面实现的,方法和调用都一致。只是这两个页面的我创建label的方式不一样,这也才导致了问题的发生。第一个页面的我是用xib拖的label。第二个页面我是用纯代码创建的label。下面就分两种情况具体说一下。

    xib创建的label

    这种情形下很简单,我在xib相关联的.m文件中awakeFromNib方法中调用方法即可,运行结果也正常。

    - (void)awakeFromNib {
    [super awakeFromNib];
    // Initialization code
    [self changeLabelStyle:self.pic_tag_label1];
    [self changeLabelStyle:self.pic_tag_label2];
    [self changeLabelStyle:self.pic_tag_label3];
    }
    
    xib运行结果

    但是第二个页面用纯代码创建的label就出了问题了。

    纯代码创建的label

    ps:因为当时是在项目工程中发现的问题,已经解决上传了,所以我后来写了个简单的demo,效果跟我预期的差不多。

    代码如下,是创建一个tableview,自定义headerview,以下是自定义headerview代码。
    - (UIView *)creatHeaderView{

    UIView *topView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 240)];
    
    UILabel *label = [[UILabel alloc]init];
    label.backgroundColor = [UIColor redColor];
    label.text = @"圆角测试";
    label.textAlignment = NSTextAlignmentCenter;
    [topView addSubview:label];
    
    [label mas_makeConstraints:^(MASConstraintMaker *make) {
       
        make.size.mas_equalTo(CGSizeMake(100, 40));
        make.leading.equalTo(topView.mas_leading).offset(100);
        make.top.equalTo(topView.mas_top).offset(100);
        
    }];
    
    [self changeLabelStyle:label];
    
    return  topView;
    
    }
    

    运行结果

    label不能正常显示,接着切换3D视图查看。

    3D视图

    再打印label的信息,控制台显示如下

    Printing description of $3:
    <UILabel: 0x7f83fc413740; frame = (100 100; 100 40); text = '圆角测试';       userInteractionEnabled = NO; layer = <_UILabelLayer: 0x6080002877b0>>
    (lldb) 
    

    label其实是存在的,但是不能显示,自然就联想到了调用裁剪圆角的方法,肯定是这里出了问题,在这个方法里,传进的是label控件,涉及到label的frame,所以我就打印了一下label的frame,结果如下

    打印label结果

    再回过头来看上面的代码,我是用masonry设置完label的约束之后调用的方法,我在想,是不是masonry设置约束block中的处理是放在另一个线程中异步进行的,block还没执行完就已经走到了下面使用frame的代码,所以把方法的调用写在了设置约束的block里,但是label依然不能正常显示,label的frame打印结果依旧是上面的结果。

    后来和朋友说了这事,自己也在网上查了资料,才发现:
    使用masonry的实质还是调用了ios7以后的autolayout,如果要更新frame,需要调用layoutIfNeeded函数进行布局,然后所约束的控件才会按照约束条件,生成当前布局相应的frame和bounds。这样就可以利用这两个属性来进行图片圆角剪裁。而调用layoutIfNeeded的目的是让系统调用layoutSubviews方法,我们也可以直接在这个方法里获取frame,因为这时候开始layout subviews,Masonry已经计算出了真实的frame。

    最后我在调用裁剪圆角方法的前面调用了layoutIfNeeded方法,label成功显示,此时打印label的frame也是正确的。

    - (UIView *)creatHeaderView{
    
    UIView *topView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 240)];
    UILabel *label = [[UILabel alloc]init];
    label.backgroundColor = [UIColor redColor];
    label.text = @"圆角测试";
    label.textAlignment = NSTextAlignmentCenter;
    [topView addSubview:label];
    [label mas_makeConstraints:^(MASConstraintMaker *make) {
        make.size.mas_equalTo(CGSizeMake(100, 40));
        make.leading.equalTo(topView.mas_leading).offset(100);
        make.top.equalTo(topView.mas_top).offset(100);
    }];
    [label layoutIfNeeded];
    [self changeLabelStyle:label];
    
    return  topView;
    }
    

    label正常显示 此时label打印结果

    附上关于autolayout更新几个方法的区别:

    • setNeedsLayout:告知页面需要更新,但是不会立刻开始更新。执行后会立刻调用layoutSubviews。
    • layoutIfNeeded:告知页面布局立刻更新。所以一般都会和setNeedsLayout一起使用。如果希望立刻生成新的frame需要调用此方法,利用这点,动画可以在更新布局后直接使用这个方法让动画生效。
    • layoutSubviews:系统重写布局
    • setNeedsUpdateConstraints:告知需要更新约束,但是不会立刻开始
    • updateConstraintsIfNeeded:告知立刻更新约束
    • updateConstraints:系统更新约束

    相关文章

      网友评论

        本文标题:iOS 裁剪View指定的某几个角为圆角以及遇到的问题

        本文链接:https://www.haomeiwen.com/subject/frgbattx.html