Issues to note when customizing View

Focus of this article

  • About the initialization method of custom View
  • About addSubview
  • About frames and bounds

About the initialization method of custom View

Usually we create a private method createUI method to create the sub- View required by the current custom View . In which method do you put createUI?

  • init()?
  • initWithFrame()?

Verification instructions: First call the createUI method in the init method of CustomView .

- (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;
}

Create CustomView externally in the form of init:

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

CustomView and its subviews can be displayed normally.

But there is a problem: if the outside is created in the form of initWithFrame, the createUI method cannot be called, so the subview cannot be displayed .

The createUI method is called in both init and initWithFrame methods . Count the number of createUI method calls.

- (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

Finally I learned that createUI was called twice.

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

Will the init method of the custom View call the initWithFrame method by default?

1. Dynamically find the init method of CustomView

2. Call the [super init] method

3. What is executed internally in the super init method is [super initWithFrame:CGRectZero]

4. If super finds that CustomView implements the initWithFrame method

5. Turn to execute the initWithFrame method of self(CustomView)

6. Finally, execute the rest of init

A conclusion can also be verified here : super in OC actually allows a certain class to call the method of the parent class, rather than the parent class calling a certain method. The method dynamically calls the process sequence

Bottom-up (this is also the reason why createUI will not be executed multiple times only in the init method , because the initWithFrame of the parent class does not perform the createUI operation).

Conclusion: The createUI method is best called in initWithFrame. The createUI method can be executed normally using init or initWithFrame externally. Don't customize

Rewrite init and initWithFrame at the same time in View and execute the same view layout code. Will cause the layout code (createUI) to be executed multiple times.

About addSubview

Adding the same View multiple times will not create multi-level situations .

The documentation for addSubview states that View has one and only one parent view. If the new parent view is different from the original parent view, the View will be removed from the original view and added to the new 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>>】

As shown in the above print, CeshiView always has only one subview (testView)

The following assumptions can be made:

1. Remove the subview from the old parent view, and then add the subview to the parent view again.

2. Determine whether the old and new parent views are consistent. If they are consistent, no operation is performed.

Conclusion : If the parent view repeatedly adds the same subview, multi-level situations will not occur. Because testView is created in the form of lazy loading in this example , the same View is added every time by self . However, if it is created in the form of UIView *testView = [UIView alloc] initWithFrame in createUI , multiple views will be created. Hierarchical View .

Summary: It is best to create subviews of custom Views in the form of lazy loading to avoid exceptions caused by other improper writing.

About Frame and bound

There are two very important attributes regarding position in iOSUI controls, frame and bound.

[External link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-7He9Zeod-1642056027779)(/Users/yichen/Library/Application Support/typora-user-images/image-20220112172848089 .png)]

Frame:This is mainly where the View is equivalent to the parent view.

bound:This is the position of the View relative to the control itself.

center:This is the center position relative to the parent view control.

We can return the position and size of the upper left corner of the control through frame.origin/bounds.origin and frame.size/bounds.size

Guess you like

Origin blog.csdn.net/weixin_42357849/article/details/122473531