IOS-layoutSubviews方法的调用时机

前言

最近在开发项目的过程中,修改以前的布局代码,之前单独设置上、下、宽度、高度,的值是正常,但是我改成设置空间的frame之后,程序运行发生了死循环, 最后找到子控件的layoutSubviews方法中设置自身的高度,然后再父视图,我又设置了该控件的frame。

子控件的代码:
在这里插入图片描述

在运行之后,展示该控件时,发生了死循环, 自己对layoutSubviews的调用时机,也不是很清楚,所以这里想学习记录下layoutSubviews方法的调用时机

这里需要注意的是layoutSubviews本身不会做任何工作,它只是提供了一个调用时机,需要自己重写

layoutSubviews调用时机

  • 注意: 这里需要注意的是,我们创建控件,调用控件的init或则initWithFrame方法都是不会触发控件的layoutSubviews方法的调用的

在这里插入图片描述

setNeedsLayout 和 layoutIfNeeded

  • 调用view的setNeedsLayout方法时,这时候会触发该view的layoutSubviews方法的调用,这里需要是一一对应的关系,那个view调用setNeedsLayout方法就触发哪个viewlayoutSubviews方法

在这里插入图片描述

对此官方文档有一段描述:

You should not call this method directly. If you want to force a layout update, call the setNeedsLayout method instead to do so prior to the next drawing update. If you want to update the layout of your views immediately, call the layoutIfNeeded method.

大概的意思如下:

您不应该直接调用此方法。如果你想强制一个布局更新,调用setNeedsLayout方法,而不是在下一次绘图更新之前这样做。如果您想立即更新视图的布局,请调用layoutIfNeeded方法。

所以调用layoutIfNeeded方法,也会触发layoutSubviews方法的回调

在这里插入图片描述

addSubview

当触发addSubView时, view子viewlayoutSubviews方法会调用, 并且是先调用View的layoutSubviews方法在调用子view的, 而且就算是添加多个子view,那么view的layoutSubviews也只会调用一次, 而不是调用多次
在这里插入图片描述

上述情况指的是在同一个RunLoop中,也就是同一个线程中, 那么如果在不同的RunLoop(不同线程)中调用addSubview方法了, 那么都会触发多次, 输出结果如下:

在这里插入图片描述

注意: 如果view的size是0,那么及时调用addSubview,也不会触发大小为0的view的layoutSubviews方法(注意这是size为0.就是宽度和高度都为0,如果只有其中为0,还是会调用layoutSubviews方法的

在这里插入图片描述

在这里插入图片描述

改变view的size大小

当我们改变view的大小的时候,会触发view的layoutSubviews方法的调用, 但是如果没有触发size的改变,只是触发了位置的改变, 是不会触发layoutSubviews

  • 修改位置
    在这里插入图片描述

  • 改变size
    在这里插入图片描述

如果是改变子view的size,那么子view父viewlayoutSubviews方法都会调用
在这里插入图片描述

需要注意点

  • 旋转屏幕只会调用controllerself.viewlayoutSubviews
  • 关于在父viewlayoutSubviews里改变子viewframe不会循环调用的问题是因为一般情况下改变子viewframe的值都是固定的,而如果每次改变的值不同,也会重复调用父viewlayoutSubviews
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是手势解锁的Demo实现过程: 1. 首先创建一个UIView的子类,作为手势解锁的主体视图,我们称之为`GestureLockView`。 2. 在`GestureLockView`中创建一个数组`circleArray`,用于存储手势解锁的圆点。 ``` @property (nonatomic, strong) NSMutableArray *circleArray; ``` 3. 在`GestureLockView`的`layoutSubviews`方法中,创建9个圆点,并加入到`circleArray`中。 ``` - (void)layoutSubviews { [super layoutSubviews]; CGFloat margin = (self.frame.size.width - 3 * kCircleSize) / 4.0; for (int i = 0; i < 9; i++) { CGFloat x = margin + (i % 3) * (margin + kCircleSize); CGFloat y = margin + (i / 3) * (margin + kCircleSize); CGRect frame = CGRectMake(x, y, kCircleSize, kCircleSize); GestureLockCircle *circle = [[GestureLockCircle alloc] initWithFrame:frame]; circle.tag = i + 1; [self addSubview:circle]; [self.circleArray addObject:circle]; } } ``` 4. 在`GestureLockView`中创建一个数组`selectedArray`,用于存储用户选择的圆点。 ``` @property (nonatomic, strong) NSMutableArray *selectedArray; ``` 5. 在`GestureLockView`中实现手势识别的方法`touchesMoved:withEvent:`,通过判断触摸点是否在圆点内来确定用户选择的圆点,并绘制用户选择的线条。 ``` - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint point = [touch locationInView:self]; for (GestureLockCircle *circle in self.circleArray) { if (CGRectContainsPoint(circle.frame, point) && !circle.selected) { circle.selected = YES; [self.selectedArray addObject:circle]; break; } } self.currentPoint = point; [self setNeedsDisplay]; } ``` 6. 在`GestureLockView`中实现绘制方法`drawRect:`,根据用户选择的圆点绘制线条。 ``` - (void)drawRect:(CGRect)rect { if (self.selectedArray.count == 0) { return; } UIBezierPath *path = [UIBezierPath bezierPath]; path.lineWidth = kLineWidth; path.lineJoinStyle = kCGLineJoinRound; path.lineCapStyle = kCGLineCapRound; [[UIColor whiteColor] set]; for (int i = 0; i < self.selectedArray.count; i++) { GestureLockCircle *circle = self.selectedArray[i]; if (i == 0) { [path moveToPoint:circle.center]; } else { [path addLineToPoint:circle.center]; } } [path addLineToPoint:self.currentPoint]; [path stroke]; } ``` 7. 在`GestureLockView`中实现手势结束的方法`touchesEnded:withEvent:`,判断用户手势是否正确,并通过代理方法通知外部。 ``` - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSMutableString *password = [NSMutableString string]; for (GestureLockCircle *circle in self.selectedArray) { [password appendFormat:@"%ld", circle.tag]; } BOOL success = [password isEqualToString:self.password]; if (success) { for (GestureLockCircle *circle in self.selectedArray) { circle.selected = NO; } [self.selectedArray removeAllObjects]; [self setNeedsDisplay]; if (self.delegate && [self.delegate respondsToSelector:@selector(gestureLockView:didCompleteWithPassword:)]) { [self.delegate gestureLockView:self didCompleteWithPassword:password]; } } else { for (GestureLockCircle *circle in self.selectedArray) { circle.selected = NO; circle.error = YES; } [self.selectedArray removeAllObjects]; [self setNeedsDisplay]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kErrorDuration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ for (GestureLockCircle *circle in self.circleArray) { circle.error = NO; } [self setNeedsDisplay]; }); } } ``` 8. 在外部创建`GestureLockView`实例,并设置代理方法,实现手势解锁的逻辑。 ``` - (void)viewDidLoad { [super viewDidLoad]; GestureLockView *lockView = [[GestureLockView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenWidth)]; lockView.center = self.view.center; lockView.delegate = self; lockView.password = @"123456789"; [self.view addSubview:lockView]; } #pragma mark - GestureLockViewDelegate - (void)gestureLockView:(GestureLockView *)lockView didCompleteWithPassword:(NSString *)password { NSLog(@"password: %@", password); } ``` 至此,手势解锁的Demo已经完成了,你可以尝试在模拟器或真机上运行它。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值