使用代码自定义UIView注意一二三

beauty.png

这一块由两个东西组成:一个imageView和一个label。首先咱们新建一个继承自UIView的类MyView.布局

在MyView的.m文件里,你能够根据本身的意愿将两个子控件设置成MyView的属性或者成员变量,这里咱们设置为属性。atom

@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) UILabel *label;

那接下来,就是要向自定义的view里面添加控件咯。
一般的思路是重写UIView的构造方法。那么这里要说第一个注意了:debug

1.要重写UIView的initWithFrame:方法而不是init方法code

为何呢?由于当外部调用init的方法的时候,其内部也会默默地调用initWithFrame:方法,你不能保证别的同事在调用你的类的时候不会直接调用initWithFrame:方法,这时若是你仅重写了init方法,那么两个子控件便无从建立.继承

因而咱们写成这样:get

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        /* 添加子控件的代码*/
    }
    return self;
}

接下开始添加子控件,不知道还会不会有小伙伴是这样写的:it

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self.imageView = [[UIImageView alloc]init];
        self.imageView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.width);
        [self addSubview:self.imageView];
    }
    return self;
}

这样有什么问题吗?若是所有写好运行起来,能看到imageView吗?答案是不肯定的.问题出如今给imageView的frame赋值那里.
imageView的宽和高直接用self.frame.size.width,但这个时候self.frame多是没有值的.table

上面咱们说过,若是外部调用了MyView的init方法,也会执行到这里,这时候frame尚未赋值.变量

因此第二个注意:bug

2.不要在构造方法里面直接取自身(self,或者说本视图)的宽高,这时候取到的宽高是不许的.

我想初学自定义tableViewCell的小伙伴都遇到过相似这样的问题:
重写cell的初始化方法向cell内添加子控件时
(假设cell的高度设为100,想要添加一个label在cell的底部),

因而这样写:
label.frame = CGRectMake(0,self.frame.size.height - 20, 100, 20),

运行出来却发现添加的label并不在咱们指望的位置(底部),
而是在cell比较偏上的位置(实际y的值是44-20而不是100-20).

而后在debug的时候发现:虽然cell的高度已经设定成为100,但在初始化方法里面取到的cell的高度仍然是默认的44.

这其实也是刚才说的缘由致使的:咱们不能在控件的构造方法里面取其frame或者bounds,这时候取值是不许确的.

因此在从新构造方法的时候,咱们只须要把控件放进去,暂时先不用考虑他们在什么位置:

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self.imageView = [[UIImageView alloc]init];
        [self addSubview:self.imageView];

        self.label = [[UILabel alloc]init];
        self.label.textAlignment = NSTextAlignmentCenter;
        [self addSubview:self.label];
    }
    return self;
}

那么在何时设置子控件的frame呢?

第三个注意:

3.在layoutSubViews方法里面布局子控件

以下:

- (void)layoutSubviews {
    // 必定要调用super的方法
    [super layoutSubviews];

    // 肯定子控件的frame(这里获得的self的frame/bounds才是准确的)
    CGFloat width = self.bounds.size.width;
    CGFloat height = self.bounds.size.height;
    self.imageView.frame = CGRectMake(0, 0, width, width);
    self.label.frame = CGRectMake(0, width, width, height - width);
}

这里要注意的就是须要在布局以前必定要先调用父类的layoutSubviews方法.

因为在这个方法里能够获取MyView准确的宽和高,咱们直接取它的宽高来设置imageView和label的宽高就能够

固然,子控件的建立不必定要写在MyView的构造方法里面,既然声明成为属性,使用懒加载(重写属性的get方法)也是一个不错的选择.

相关文章
相关标签/搜索