今天被反馈了一个 UIPageControl 莫名错位的线上问题, 本应在屏幕底部中间的指示器, 突然跑到了屏幕的右下角, 经过定位, 是 pageControl的宽度由 428 变成了 856 导致的, hook 了 pageControl 的 setFrame:
后, 断点停在了自动布局的堆栈, 系统自动布局计算宽度就是 856, 一番 Google 后最后发现是 autoresizingMask
的问题.
出错现象:
出错原因
UIPageControl 的 autoresizingMask
属性默认是 UIViewAutoresizingFlexibleWidth
, 即为与 superView
左右边距保持一致.
出 bug 的原因在于我们将 pageControl
加到 superView
时, 做了一些特殊的操作, 导致此时 superView
的 frame
是 CGRectZero
, 而 pageControl
的宽是 428, 因此此时的规则是
pageControl
左边与superView
一样pageControl
右边比与superView
大 428pageControl
宽度是 428, 并且宽度可变
当布局后 superView
宽度设置为正常的 428, 执行以上三条规则后, pageControl
宽度就变成了 856. 即pageControl
左边距与superView
一致, 右边距比superView
右边距大 428, superView
此时宽度是428, 计算得到 pageControl
为 428+428.
autoresizesSubviews && autoresizingMask
UIView.autoresizesSubviews
决定 autoresizingMask
属性是否生效, 默认YES
生效.
根据官方文档描述autoresizingMask
When a view’s bounds change, that view automatically resizes its subviews according to each subview’s autoresizing mask.
当视图的边界发生变化时,该视图会根据每个子视图的自动布局属性autoresizingMask
调整其子视图的大小。
autoresizingMask
默认值一般是 UIViewAutoresizingNone
具体定义如下
1 | typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) { |
简单解释下具体含义
属性值 | 含义 |
---|---|
UIViewAutoresizingNone | (默认值)不进行自动布局 |
UIViewAutoresizingFlexibleLeftMargin | 自动调整与父视图左边距, 保持右边距不变 |
UIViewAutoresizingFlexibleWidth | 自动调整本身的宽度, 保持和父视图的左右边距不变 |
UIViewAutoresizingFlexibleRightMargin | 自动调整与父视图右边距, 保持左边距不变 |
UIViewAutoresizingFlexibleTopMargin | 自动调整与父视图上边距, 保持下边距不变 |
UIViewAutoresizingFlexibleHeight | 自动调整本身的高度, 保持上边距和下边距不变 |
UIViewAutoresizingFlexibleBottomMargin | 自动调整与父视图的下边距, 保持上边距不变 |
同时这些属性是可以组合的, 比如保持在父视图右下角相对固定位置可以设置为
1 | view.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin |UIViewAutoresizingFlexibleTopMargin; |
而比较特殊的是,几乎所有的 UIView 的子类autoresizingMask 默认值都是UIViewAutoresizingNone, 除了 UIPageControl 默认值是UIViewAutoresizingFlexibleWidth