【问题标题】:How to apply constraints with width/height in Autolayout if they are 0 in init()?如果 init() 中的宽度/高度为 0,如何在 Autolayout 中应用具有宽度/高度的约束?
【发布时间】:2020-07-02 09:10:22
【问题描述】:

我正在努力应用以下约束:

documentTypeCircle.topAnchor.constraint(equalTo: middleContentScrollView.topAnchor, constant: self.frame.height * 0.03).isActive = true
documentStepTitle.topAnchor.constraint(equalTo: documentTypeCircle.bottomAnchor, constant: self.frame.height * 0.05).isActive = true
documentStepTableView.centerXAnchor.constraint(equalTo: self.centerXAnchor, constant: self.frame.width * 0.05).isActive = true
documentStepTableView.widthAnchor.constraint(equalToConstant: self.frame.width * 0.73).isActive = true
uploadImageView.topAnchor.constraint(equalTo: documentStepTableView.bottomAnchor, constant: self.frame.height * 0.05).isActive = true

我需要访问self.frame.width,但是值是0。

我在 ViewController 的 loadView() 中添加了这个视图,因为它是推荐的方法:

 override func loadView() {
    self.view = CustomView(frame: .zero)
 }

如何确保 self.frame.width 提供整个视图的宽度,而不是返回 0? 如果我将约束逻辑移动到 layoutSubviews() 它可以正常工作,但是 多次创建约束 这似乎是不干净的解决方案:

override open func layoutSubviews() {
documentTypeCircle.topAnchor.constraint(equalTo: middleContentScrollView.topAnchor, constant: self.frame.height * 0.03).isActive = true
documentStepTitle.topAnchor.constraint(equalTo: documentTypeCircle.bottomAnchor, constant: self.frame.height * 0.05).isActive = true
documentStepTableView.centerXAnchor.constraint(equalTo: self.centerXAnchor, constant: self.frame.width * 0.05).isActive = true
documentStepTableView.widthAnchor.constraint(equalToConstant: self.frame.width * 0.73).isActive = true
uploadImageView.topAnchor.constraint(equalTo: documentStepTableView.bottomAnchor, constant: self.frame.height * 0.05).isActive = true

}

但是,如果我使用 UIScreen.main.bounds 而不是 .zero 实例化视图,即使在 customView 的 init() 中它也能正常工作。但是,我很困惑为什么会使用 UIScreen.main.bounds 而不是 .zero 正确绘制自定义视图。即使使用 .zero,自动布局也不应该处理这个问题吗?

编辑

我找到了recommended approach,但无法使其适用于我们的案例。新布局看起来不像旧布局,所有约束似乎都无处不在

我的新约束和用框架注释的旧约束:

//documentTypeCircle.topAnchor.constraint(equalTo: middleContentScrollView.topAnchor, constant: self.frame.height * 0.03).isActive = true

      NSLayoutConstraint(item: documentTypeCircle, attribute: .top, relatedBy: .equal,
      toItem: middleContentScrollView, attribute: .top, multiplier: 0.03, constant: 0).isActive = true

//documentStepTitle.topAnchor.constraint(equalTo: documentTypeCircle.bottomAnchor, constant: self.frame.height * 0.05).isActive = true

      NSLayoutConstraint(item: documentStepTitle, attribute: .top, relatedBy: .equal,
      toItem: documentTypeCircle, attribute: .bottom, multiplier: 0.05, constant: 0).isActive = true

//documentStepTableView.centerXAnchor.constraint(equalTo: self.centerXAnchor, constant: self.frame.width * 0.05).isActive = true

      NSLayoutConstraint(item: documentStepTableView, attribute: .centerX, relatedBy: .equal,
      toItem: self, attribute: .centerX, multiplier: 0.05, constant: 0).isActive = true

//documentStepTableView.widthAnchor.constraint(equalToConstant: self.frame.width * 0.73).isActive = true

      NSLayoutConstraint(item: documentStepTableView, attribute: .width, relatedBy: .equal,
      toItem: self, attribute: .width, multiplier: 0.73, constant: 0).isActive = true


//uploadImageView.topAnchor.constraint(equalTo: documentStepTableView.bottomAnchor, constant: self.frame.height * 0.05).isActive = true
      NSLayoutConstraint(item: uploadImageView, attribute: .top, relatedBy: .equal,
      toItem: documentStepTableView, attribute: .bottom, multiplier: 0.05, constant: 0).isActive = true
Perhaps I am mixing anchor with NSLayoutConstraint, however I feel like it is impossible to achieve same layout behaviour with auto layout, without using frames.

【问题讨论】:

  • 您确定要让您的约束具有self.frame.height * 0.03 的常量吗?如果self.frame.height 发生变化怎么办?您是否希望约束的常量保持不变,而不是使用新的self.frame.height
  • @Sweeper 我不会以任何方式更改约束。我需要在开始时绘制整个 UI 并且不要更改它。问题是,如果我通过 .zero,那么我的滚动视图将不可见,因为我无法正确计算约束。如果我通过 UIScreen.main.bounds 我可以计算,但我不确定它是否是正确的方法,因为如果你使用自动布局,建议通过 .zero 。也许我需要在设置约束之前调用 layoutIfNeeded()?

标签: swift autolayout interface-builder


【解决方案1】:

问题在于,当您尝试在 loadView() 中设置您的框架的约束时,框架为 0,因为所有框架尚未初始化,它们只会在 layoutSubviews() 中

尽量不要sue框架,这样更难找到解决方案,但最好使用常规约束,它们会在正确的时间设置

为了更好的理解:https://medium.com/good-morning-swift/ios-view-controller-life-cycle-2a0f02e74ff5

【讨论】:

  • 我正在使用常规约束。我只是设置与宽度相关的约束常数
  • 是的,只是使用不同的值,与框架/边界无关或者您可以设置约束的区域:设置第一个大于等于或等于第二个为您的约束提供空间
  • 或者你可以使用 UIScreen.main.bounds 来获取屏幕宽度
  • UIScreen.main.bounds 在某种程度上不是动态的,它不能很好地与自动布局一起工作吗?或者我可以通过 UIScreen.main.bounds 而不是 .zero 就可以了?
  • 是的,这个答案是正确的,混合框架+约束不是一个好的鳕鱼风格。这就是为什么最好使用纵横比并将一侧与另一侧联系起来
【解决方案2】:

UIScreen.main.bounds 为您提供屏幕的边界,而 .zero 只是使用 CGRect(0, 0, 0 ,0) 初始化您的视图,CGRect(0, 0, 0 ,0) 是坐标 0,0 处高度为 0 宽度为 0 的矩形,它然后会反过来给你一个宽度 0 当然。我不清楚你在纠结什么,如果你只是想要整个屏幕宽度的参考,我相信你仍然可以使用 UIScreen.main.bounds.width。顺便说一句,我通常在 viewDidLoad 中设置约束,很确定很多人都这样做。

【讨论】:

  • 这很奇怪:(推荐的方法是使用 loadView,如果你的视图是用代码编写的,苹果文档也可以使用
  • ViewDidLoad 还不知道框架,使用 viewDidAppear 或(苹果推荐)layoutSubviews
猜你喜欢
  • 2018-02-25
  • 1970-01-01
  • 2013-02-16
  • 1970-01-01
  • 2020-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-08
相关资源
最近更新 更多