我的布局已损坏。我在运行时添加了一个布局,并使用以下代码将其锚定到所有4个方面:
func anchorAllSides(to parentView:UIView, identifier: String? = nil ) {
self.translatesAutoresizingMaskIntoConstraints = false
let top = self.topAnchor.constraint(equalTo: parentView.topAnchor)
let bottom = self.bottomAnchor.constraint(equalTo: parentView.bottomAnchor)
let left = self.leadingAnchor.constraint(equalTo: parentView.leadingAnchor)
let right = self.trailingAnchor.constraint(equalTo: parentView.trailingAnchor)
if let identifierString = identifier {
top.identifier = "\(identifierString) - top"
bottom.identifier = "\(identifierString) - bottom"
left.identifier = "\(identifierString) - left"
right.identifier = "\(identifierString) - right"
}
NSLayoutConstraint.activate([top, bottom, left, right])
}
在控制台中,我可以看到2个视图具有约束所有4个侧面的约束,但是它们的大小也完全不同。
Printing description of $20:
<UIView: 0x7f93084220c0; frame = (0 0; 375 205.333); autoresize = W+H; gestureRecognizers = <NSArray: 0x600001956ac0>; layer = <CALayer: 0x60000176b320>>
Printing description of $21:
<UIView: 0x7f930840ed40; frame = (0 528.667; 375 528.667); autoresize = RM+BM; layer = <CALayer: 0x6000017698e0>>
(lldb) po [0x7f930840ed40 constraints]
<__NSArrayI 0x600000650330>(
<NSLayoutConstraint:0x60000346a760 'drawerControllerView <-> rolePageDrawerView - bottom' UIView:0x7f93084220c0.bottom == UIView:0x7f930840ed40.bottom (active)>,
<NSLayoutConstraint:0x60000346a7b0 'drawerControllerView <-> rolePageDrawerView - left' H:|-(0)-[UIView:0x7f93084220c0] (active, names: '|':UIView:0x7f930840ed40 )>,
<NSLayoutConstraint:0x60000346a800 'drawerControllerView <-> rolePageDrawerView - right' UIView:0x7f93084220c0.trailing == UIView:0x7f930840ed40.trailing (active)>,
<NSLayoutConstraint:0x60000346a710 'drawerControllerView <-> rolePageDrawerView - top' V:|-(0)-[UIView:0x7f93084220c0] (active, names: '|':UIView:0x7f930840ed40 )>
)
通过在Xcode的视图层次结构调试器中查看视图来布局视图后,将打印上述信息。因此,所有约束都处于活动状态,此时应该使它们的大小相等。
如果将它们约束为相等,那么帧大小如何不同?
更新
为了缩小这一范围,我在父VC(角色vc)和子VC(日历vc)的layoutSubviews()函数中使用了断点日志记录。在这两种方法中,我都记录了抽屉的父视图的大小。在子级VC中,我记录了抽屉视图和self.view的大小,这是子项到抽屉的位置,并在所有侧面固定到抽屉。我还记录了抽屉视图实例,以确保它们相同。这是另一次运行的日志(因此,实例地址与上面的日志记录相比有所更改):
role page vc drawerSize (width = 375, height = 190.5)
role page vc drawer 0x00007f7ffd516d50
calendar vc drawerSize (width = 375, height = 222.66666666666666)
calendar vc view size (width = 375, height = 222.66666666666666)
calendar vc drawer is 0x00007f7ffd516d50
role page vc drawerSize (width = 375, height = 589.33333333333337)
role page vc drawer 0x00007f7ffd516d50
role page vc drawerSize (width = 375, height = 589.33333333333337)
role page vc drawer 0x00007f7ffd516d50
calendar vc drawerSize (width = 375, height = 205.33333333333334)
calendar vc view size (width = 375, height = 205.33333333333334)
calendar vc drawer is 0x00007f7ffd516d50
如您所见,抽屉实例是相同的。但是,日历vc报告将其插入和固定在其中的抽屉视图的大小不同。日历vc认为其视图和抽屉视图具有相同的大小,但是抽屉视图的父vc报告的是较大的大小,即显示时的大小。
答案 0 :(得分:2)
如果将它们约束为相等,那么帧大小如何不同?
因为约束仅仅是指令。在执行这些说明之前,它们仅是说明而已。就像购物清单;您可以看到牛奶在清单上,但是您没有说“为什么冰箱里没有牛奶?”您还必须购买牛奶。
好吧,运行时要等到您打印说明后再购买牛奶。布局不会立即发生;它发生在布局时,这是在您的所有代码停止运行并且提交了当前的CATransaction之后。发生这种情况后,两个视图将具有相同的大小。
请注意,我不仅在谈论 first 时间安排的发生。随着视图触发事件的发生,布局在视图的整个生命周期中会间歇性地发生。但是,在这两个时刻之间,其他代码很有可能会出现,并手动将视图的框架设置为其他内容。在重新触发布局之前,它不会变回到约束条件。
(当然包括layoutSubviews
和viewDidLayoutSubviews
本身。此时会发生自动布局,但是您的代码可以出现并覆盖自动布局的内容。)
只是为了说明问题,这是一个应用程序的完整代码,它使我们进入您所描述的情况:
func delay(_ delay:Double, closure:@escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let v = UIView()
v.backgroundColor = .red
v.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(v)
NSLayoutConstraint.activate([
v.topAnchor.constraint(equalTo:self.view.topAnchor),
v.leadingAnchor.constraint(equalTo:self.view.leadingAnchor),
v.trailingAnchor.constraint(equalTo:self.view.trailingAnchor),
v.bottomAnchor.constraint(equalTo:self.view.bottomAnchor),
])
delay(5) {
v.frame = v.frame.insetBy(dx: 30, dy: 30)
print(v.frame)
print(self.view.frame)
print(self.view.constraints)
}
}
}
注意打印输出(为清楚起见,我已经编辑了一些约束):
(30.0, 30.0, 354.0, 676.0)
(0.0, 0.0, 414.0, 736.0)
[
<NSLayoutConstraint:0x600000083570 V:|-(0)-[UIView:0x7fede9d07190] (active, names: '|':UIView:0x7fede9f0d230 )>,
<NSLayoutConstraint:0x6000000957c0 H:|-(0)-[UIView:0x7fede9d07190] (active, names: '|':UIView:0x7fede9f0d230 )>,
<NSLayoutConstraint:0x600000094c30 UIView:0x7fede9d07190.trailing == UIView:0x7fede9f0d230.trailing (active)>,
<NSLayoutConstraint:0x600000095f40 UIView:0x7fede9d07190.bottom == UIView:0x7fede9f0d230.bottom (active)>,
...
]
这就像您的打印输出:约束条件说,两个视图的边缘应该相等,但是它们的框架大小不同。因此,您举报的确实非常有可能。
答案 1 :(得分:0)
@matt 的答案恰好描述了我在评论中想要的内容。示例:
override func viewDidLoad() {
super.viewDidLoad()
let subview = UIView()
subview.backgroundColor = .orange
view.addSubview(subview)
subview.anchorAllSides(to: view)
print("BEFORE layoutIfNeeded")
print(view.frame)
print(subview.frame)
view.layoutIfNeeded() // forces the view to update its layout immediately
print("AFTER layoutIfNeeded")
print(view.frame)
print(subview.frame)
}
此打印:
BEFORE layoutIfNeeded
(0.0, 0.0, 375.0, 812.0)
(0.0, 0.0, 0.0, 0.0)
AFTER layoutIfNeeded
(0.0, 0.0, 375.0, 812.0)
(0.0, 0.0, 375.0, 812.0)