自定义View注意的问题

本文重点

  • 关于自定义View的初始化方法
  • 关于addSubview
  • 关于frame与bounds

关于自定义View的初始化方法

通常我们会创建私有方法createUI方法来创建当前自定义View所需要的子View。将createUI放在哪个方法中呢?

  • init()?
  • initWithFrame()?

通过验证说明:首先在CustomViewinit方法中调用createUI方法。

- (instancetype)init {
    if (self = [super init]) {
        [self createUI];
    }
    return self;
}

- (void)createUI {
    [self addSubview:self.testView];
}

- (UIView *)testView {
    if (!_testView) {
        _testView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
        _testView.backgroundColor = [UIColor redColor];
    }
    return _testView;
}

外部用init的形式进行创建CustomView:

    _ceshiView = [[CeShiView alloc] init];
    _ceshiView.frame = CGRectMake(100, 100, 200, 200);
    _ceshiView.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:_ceshiView];

CustomView与其子视图均可正常显示。

但有个问题是,如果外部以initWithFrame形式创建,无法调用createUI方法,因此子视图无法显示

initinitWithFrame方法中均调用createUI方法。计算createUI方法调用的次数。

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self createUI];
    }
    return self;
}

- (instancetype)init {
    if (self = [super init]) {
        [self createUI];
    }
    return self;
}

- (void)createUI {
    NSLog(@"SubViews Add");
    [self addSubview:self.testView];
      for (NSInteger i = 0; i < 100; i++) {
        [self addSubview:self.testView];
    }
    
    NSLog(@"subviewsCount = 【%ld】",self.subviews.count);
    for (UIView *view in self.subviews) {
           NSLog(@"subView 【%@】",view);
    }
}

- (UIView *)testView {
    if (!_testView) {
        _testView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
        _testView.backgroundColor = [UIColor redColor];
    }
    return _testView;
}

@end

最后得知,createUI被调用了两次。

2022-01-12 17:06:43.112130+0800 WJ_KVO[8774:258969] SubViews Add
2022-01-12 17:06:43.113792+0800 WJ_KVO[8774:258969] SubViews Add

自定义View的init方法是否会默认调用initWithFrame方法呢?

1、动态查找到CustomView的init方法

2、调用[super init]方法

3、super init方法内部执行的的是[super initWithFrame:CGRectZero]

4、若super发现CustomView实现了initWithFrame方法

5、转而执行self(CustomView)的initWithFrame方法

6、最后在执行init的其余部分

这里也可以验证一个结论OC中的super实际上是让某个类去调用父类的方法,而不是父类去调用某个方法,方法动态调用过程顺序

由下而上的(这也是为什么只在init方法中进行createUI不会执行多次的原因,因为父类的initWithFrame没做createUI操作)。

结论: createUI方法最好在initWithFrame中调用,外部使用init或initWithFrame均可以正常执行createUI方法。不要在自定

View中同时重写init与initWithFrame并执行相同视图布局代码。会导致布局代码(createUI)执行多次。

关于addSubview

重复多次添加同一个View并不会产生多层级的情况

addSubview的文档描述,View有且仅有一个父视图,如果新的父视图与原父视图不一样,会将View在原视图中移除,添加到新视图上。

22-01-12 17:22:35.726706+0800 WJ_KVO[8970:267845] subviewsCount = 【1】
2022-01-12 17:22:35.726799+0800 WJ_KVO[8970:267845] subView 【<UIView: 0x7f78a7f075f0; frame = (0 0; 100 100); layer = <CALayer: 0x6000013c9960>>】

如上面打印所示,CeshiView始终只有一个子视图(testView)

可以作出如下假设:

1、在旧父视图中移除子视图,再重新将子视图添加到父视图上

2、判断新旧父视图是否一致,若一致,不做任何操作。

结论:若父视图重复添加同一子视图,并不会产生多层级情况。因为此例中testView是以懒加载的形式创建,所以self每次添加的均为同一个View,但如果在createUI中以UIView *testView = [UIView alloc] initWithFrame的形式创建,那就会创建出多层级的View

总结:自定义View的子视图最好以懒加载形式创建,可避免因其他书写不当导致的异常

关于Frame与bound

iOSUI控件中关于位置主要是两个非常重要的属性,frame与bound.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7He9Zeod-1642056027779)(/Users/yichen/Library/Application Support/typora-user-images/image-20220112172848089.png)]

Frame: 这个主要是View相当于父视图所在的位置。

bound: 这个是View相当于相对于控件本身的位置。

center:这个相对于父视图控件的中心位置。

我们可以通过 frame.origin/bounds.originframe.size/bounds.size来进行返回控件左上角位置与大小

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值