自定义View控件(手写,xib)须知
1. 目的 :提高代码的复用性,屏蔽内部的实现细节
2. 步骤 :
* 1> 自定义一个类继承于UIView
* 2> 在initWithFrame方法中添加子控件
* 3> 在layoutSubviews中设置子控件的位置
* 4> 提供一个属性保存外界传入的数据(模型对象), 重写setter方法设置子控件的数据
3. 类工厂方法(便利构造器):
* 按照苹果的风格和规范, 一般情况一个用于创建对象的对象方法会对应一个类方法
* 可以通过类工厂方法, 快速的根据数据创建一个对象
4. 注意点:
* 返回值一定要使用instancetype, 不要使用id
* 在类工厂方法中创建对象, 使用self, 不要使用类名
5. 误区:
/* 1. 手动写代码创建自定义控件,不建议在自定义控件init中设置自定义控件的子控件frame,建议使用 layoutSubviews 方法来设置子控件的位置frame*/
// layoutSubviews方法:专门用于设置子控件的frame
// 比如: ShopView *shopView = [[ShopView alloc] init];
// 后设置 frame,然后你在调用自定义控件的init方法时,自定义控件的frame还没有值,所有子控件无法根据自定义控件的frame来,设置其子控件的frame
shopView.frame = CGRectMake(100, 100, 100, 100);
因为你可能调用init方法时,自定义的控件的frame还没有设置,所有在
自定义的控件的init方法,无法根据父控件位置设置其子控件的位置。
// 解决方案:
所有我们一般重写 - (void)layoutSubviews方法中,设置自定义控件的子控件的位置.
注意:别忘了在layoutSubviews方法中调用[super layoutSubviews];然后设置子控件的frame
/* 2. 子控件设置成weak不会释放,*/
是因为它添加到父控件中,父控件会将其添加到subViews数组中,
数组强引用了它,所有就算设置成weak,还有强指针指向就不会释放.
/* 3. 假如自定义了一个自定义控件,建议使用init初始化自定义控件,而在自定义控件中用 initWithFrame添加自定义控件的子控件:*/
initWithFrame初始化自定义控件,而自定义控件中使用的是init方法添加子控件,这样子控件不会显示出来,
因为调用自定义控件中的initWithFrame方法,不会调用自定义控件中的init方法。
然而如果,自定义控件用init初始化,而自定义控件中用initWithFrame方法添加子控件,这样子控件就会在自定义控件中显示,
因为:初始化自定义控件使用init方法是会调用子控件initWithFrame方法。(调用init方法时,而initWithFrame方法是会在init方法中调用)
/* 4. layoutSubviews方法 */
// layoutSubviews方法:专门用于设置子控件的frame
// layoutSubViews什么时候调用
// 注意:别忘了在layoutSubviews方法中调用[super layoutSubviews];然后设置子控件的frame
1. 第一次创建控件的时候调用
2. 修改了控件的frame时调用
3. 修改位置x,y坐标时不会调用(注)
4. 设置时,控件frame一样时不会调用(注)
/* 5. xib - 》xcode -> User Interface -> empty/View */
1. 什么是xib:xib和storyboard一样都是用来描述界面的
2. xib是storyboard的前身
3. xib是用来描述一个简单的界面或者局部界面
4. storyboard是来描述整体,界面间跳转,全局界面
5. 加载xib的两种方式:
// 1>loadNibNamed
NSArray * res = [[NSBundle mainBundle] loadNibNamed: .... ]; UIView *view = views.firstObject; // 2> [UINib nibWithName:....]
UINib *nib = [UINib nibWithNme:....]; NSArray *views = [nib instantiateWithOwner...... ]; UIView *view = views.firstObject;
6. 规律: 一般情况下苹果的方法中要求传入一个bundle参数, 直接传nil就代表mainBundle
/* 6. xib创建自定义控件注意点 */
注意:如果是通过xib或者storyboard创建一个控件,不会调用initWithFrame方法,
而是在“创建时”会调用initWithCoder方法,调用该方法时,控件不一定被创建好了,如果我们打算给xib的控件设置属性,不一定会起作用
而在“创建后”会调用aweakFromNib方法,所有我们可以在两个方法中都拿到xib控件设置属性即可。
/* 7. 利用xib创建自定义View 步骤 */
* 1.新建一个Xib描述界面
* 2.新建一个继承于Xib界面中父控件类型的类来管理界面
* 3.在xib中关联界面和类
* 4.重写awakeFromNib方法, 进行一些初始化
* 5.提供一个属性保存外界传入的数据(模型对象), 重写setter方法设置子控件的数据
- 注意点:
- 不应该在控制器中加载xib, 应该将加载xib的操作封装到自定义view中
+ (instancetype)shopView { return [[[NSBundle mainBundle] loadNibNamed:@"XMGShopView" owner:nil options:nil] firstObject]; }
- 方法的执行顺序
+ 如果是通过xib或者Storyboard创建一个控件, 不会调用initWithFrame方法
+ 在"创建时"会调用initWithCoder方法
* 控件不一定被创建好了
+ 在"创建后"会调用awakeFromNib
* 控件一定被创建好了
/* 8. xib启动原理 */
+ 1. 根据custom class创建对象
XMGShopView *shopView = [XMGShopView alloc] init];
+ 2. 根据xib中的设置, 设置控件的相关属性
shopView.backgroundColor = [UIColor redColor]; shopView.frame = CGRectMake(0, 0, 70, 100);
+ 3. 创建所有子控件, 并且设置子控件的属性
UIImageView *iv = [[UIImageView alloc] init]; iv.frame = CGRectMake(0, 0, 70, 70); UILabel *label = [[UILabel alloc] init]; label.frame = CGRectMake(0, 70, 70, 30);
+ 4. 检查子控件是否有连线, 如果有就进行关联
self.iconView = iv; self.nameLabel = label;
+ 5.将所有子控件添加到父控件中
[shopView addSubview:iv]; [shopView addSubview:label];