zoukankan      html  css  js  c++  java
  • 使用代码自定义UIView注意一二三

    文/CoderAO(简书作者)
    原文链接:http://www.jianshu.com/p/68b383b129f9
    著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

    当一撮样式一样的视图在工程中被多次使用的时候,为了方便使用,我们会想把他们抽成一个单独的类,进行视图的自定义.

    比如我们要做一个这样的东西:

    beauty.png
    beauty.png

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

    在MyView的.m文件里,你可以根据自己的意愿将两个子控件设置成MyView的属性或者成员变量,这里我们设置为属性。

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

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

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

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

    于是我们写成这样:

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

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

    - (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可能是没有值的.

    上面我们说过,如果外部调用了MyView的init方法,也会执行到这里,这时候frame还没有赋值.

    所以第二个注意:

    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方法)也是一个不错的选择.

  • 相关阅读:
    Python基础-EMS系统
    python基础-数据结构及数据读取操作
    python基础-猜数游戏
    python基础-质数判断及优化
    利用5次shift漏洞破解win7密码
    python基础-水仙花数判断
    pickle,json ;random,shelve
    block母版继承,include模板导入,inclusion_tag自定义模板
    多道技术,阻塞非阻塞,同步异步,udp,tcp,孤儿僵尸进程
    深度广度继承,抽象类接口,绑定方法,@propertry,继承封装接口多态鸭子类型关系,组合
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/5275996.html
Copyright © 2011-2022 走看看