UIView 小结

1,564 阅读3分钟

1 生命周期

  1. UIView没有designated初始化方法。
  2. 初始化

3. 生命周期

updateConstraintslayoutSubviews的关系有点像,收集-实施的关系。(准备数据. 使用数据)

也就是说,可以认为,updateConstraints后,layoutSubviews就有了更准确的layout数据了。

简单说,就是如果可能触发自己或父视图的subviews的布局改变,就会调用。

  1. init初始化不会触发layoutSubviews。

  2. addSubview:、removeSubview:会触发layoutSubviews。

  3. 设置view的Frame会触发layoutSubviews,前提是frame的值设置前后有变化,且frame不是CGRectZero

  4. 滚动一个UIScrollView会触发layoutSubviews。

  5. 旋转Screen会触发父UIView上的layoutSubviews事件。

  6. 改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件。

  7. 直接调用setLayoutSubviews。

其中2、6是子视图造成;3、4是自身造成的;5是系统造成的;6是程序员直接影响后造成的。

2 代码组织

  1. initialize中 坐标计算,使用static变量只计算一次

  2. initWithFrame:中

    组装subview

  3. setModel中

    配置数据源,needLayout

  4. layoutSubviews中

    处理布局

注意有些第三方库的view,可能没有实现layoutSubviews,而是重写了setFrame:,如果我们的View以他们为subview了, 要注意:

3 注意事项

3.1 绘制顺序

深度优先: 父视图,有兄弟视图的情况下,先绘制父视图,然后绘制父视图的子视图,最后绘制兄弟视图。

4 常用API

4.1 获取controller

//比superview准确,用superview的话,要判断navigationController和tabbarController
- (UIViewController *)viewController {
    UIResponder *responder = self;
    while (![responder isKindOfClass:[UIViewController class]]) {
        responder = [responder nextResponder];
        if (nil == responder) {
            break;
        }
    }
    return (UIViewController *)responder;
}

4.2 一些属性

4.2.1 透明度相关

//1. alpha,hidden。较少用,opaque。
//2. opacity,layer的属性,相当于view.alpha,但设置为0,也不影响事件响应。

以上都会影响子视图(或子layer),如果希望父视图透明,子视图不透明。

//只对颜色有效,对图片无效
superView.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.5];

4.2.2 切边相关

//效果上没区别
view.clipsToBounds = YES;
layer.masksToBounds = YES;

4.2.3 其他

//1. sizeToFit,在autoLayout中无效了,要强行生效,可以写在viewDidLayoutSubviews中

//2. tintColor,默认背景色,会被子类继承

//3. multipleTouchEnabled,是否开启多点触摸,默认NO

//4. exclusiveTouch,事件独占,默认NO
表示事件独占,如果UIView是事件的firstResponder,那么第一个手指离开前,其他视图不会响应任何触摸事件。(仅Touch事件,不包括手势)
iOS 解决页面按钮同时点击,同时触发问题 设置所有UIButton的ExclusiveTouch属性为YES

-(void)setExclusiveTouchForButtons:(UIView *)myView {
    for (UIView * v in [myView subviews]) {
        if([v isKindOfClass:[UIButton class]])
            [((UIButton *)v) setExclusiveTouch:YES];
        else if ([v isKindOfClass:[UIView class]]){
            [self setExclusiveTouchForButtons:v];
        }
    }
}

//5. 根据tag拿view
viewWithTag:

//6. 是否为子视图
isDescendantOfView:

//7.层级、响应相关
insertSubview:belowSubview:
insertSubview:aboveSubview:

becomeFirstResponder
resignFirstResponder

bringSubviewToFront:
sendSubviewToBack:

//8. sizeToFit,会被autoLayout覆盖(除非在viewDidLayoutSubviews中调用,不推荐),不需要太在意这个属性
sizeToFit    //不要重写,可能会改变原始frame
siztThatFits    //重写这个,为sizeToFit提供数据,"照这个数据fit"

//9. 刷新相关
setNeedsUpdateConstraints
updateConstraintsIfNeeded

setNeedsLayout
layoutIfNeeded

setNeedsDisplay

5 其他

5.1 frame和bounds

  • 修改view's bounds.size,center不会变。origin会变。
  • 修改bounds.origin,这个view不会变,变的是他的subviews。因为相当于改了子视图frame所处的坐标系。UIScrollView用到了。