使用滚动视图内的动态大小的控制器调整容器视图的大小

时间:2016-01-26 12:56:17

标签: ios uiscrollview autolayout uicontainerview

我试图在UIScrollView中创建一个具有动态高度的控制器的容器视图,并使用自动布局自动调整大小。

Storyboard illustrating the setup

视图控制器A是滚动视图,其中包含容器视图,以及下面的更多内容。

View Controller B是一个视图控制器,我希望它具有动态大小,并且所有内容都可以在View Controller A的滚动视图中以全高显示。

我在使用B的动态大小自动设置A中容器视图的大小时遇到​​一些问题。但是,如果我在A Container View in A to for example 250中的容器视图上设置了高度约束,

如果View Controller B也有250个高度,那么它将是预期的输出。它也适用于高度1000,所以据我所知,所有自动布局约束都正确设置。不幸的是,由于高度实际上应该是动态的,我想避免设置高度约束。

我不确定视图控制器B是否有任何设置我可以设置它根据其内容自动更新其大小,或者是否有任何其他技巧我错过了。任何帮助将不胜感激!

根据视图控制器B的大小没有设置高度约束,有没有办法在A中调整容器视图的大小?

5 个答案:

答案 0 :(得分:93)

是的,有。我设法在自己的一个项目中实现了这种行为。

您需要做的就是告诉系统它不应该在Interface Builder中添加模仿根视图的固定框架集的约束。执行此操作的最佳位置是在触发嵌入segue时的容器视图控制器中:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // You might want to check if this is your embed segue here
    // in case there are other segues triggered from this view controller. 
    segue.destinationViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
}

重要:

您必须确保加载到容器中的视图从上到下受到约束,并且您需要将其中一个垂直约束的优先级设置为低于1000的值。(这是一个总是使用的好习惯这个的底部约束。)这是必要的,因为否则Interface Builder会抱怨 - 有充分的理由:

在设计时,您的根视图具有固定大小(高度)。现在,如果所有子视图都具有固定高度并且与具有相同优先级的固定约束连接,则除非固定根视图的高度恰好与子视图的总高度和垂直约束完全匹配,否则无法满足所有这些要求。如果将其中一个约束的优先级降低到999,Interface Builder就知道要断开哪个约束。但是在运行时 - 当如上所述设置translatesAutoresizingMaskIntoConstraints属性时 - 根视图没有固定的框架,系统将使用您的999优先级约束。

Interface Builder Screenshot

答案 1 :(得分:22)

根据@Mischa的回答,我能够根据其内容做出容器视图动态的高度:

在containerView的viewController中写:

  override func loadView() {
    super.loadView()
    view.translatesAutoresizingMaskIntoConstraints = false
  }

注意IB中的垂直约束都已设定。这样做你不需要从视图控制器外部设置view.translatesAutoresizingMaskIntoConstraints = false。

在我的情况下,我试图将容器的大小调整为viewController中的tableView。因为tableView具有灵活的高度,具体取决于它的超级视图(所以对于IB都可以),我通过这样做完成了代码中的垂直约束:

  @IBOutlet private var tableView: UITableView! {
    didSet {
      tableView.addConstraint(tableViewHeight)
    }
  }
  private lazy var tableViewHeight: NSLayoutConstraint = NSLayoutConstraint(item: self.tableView, attribute: NSLayoutAttribute.Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 0)

然后观察tableview的contentSize高度,并在需要时以编程方式调整tableViewHeight约束的常量。

答案 2 :(得分:12)

Swift 4,Xcode 9

单独接受的答案并没有为我解决问题。

我的层次结构:ScrollView - >内容视图(UIView) - >意见|容器视图|其他观点。

我必须添加以下约束才能使ScrollView和Container动态调整:

  1. ScrollView :顶部,底部,领先,追踪到Superview(安全区域)
  2. 内容视图:ScrollView的顶部,底部,前导,尾随,相等宽度,还有等高(优先级较低的约束:250)
  3. 观看次数:正常的自动布局约束。
  4. 容器视图:顶部,底部到邻居视图,领先和追踪到安全区域。
  5. 容器视图的嵌入式VC :一切约束垂直连接,底部约束设置为较低的优先级 ,但大于内容视图的等高一个! < / strong>在这种情况下,优先级 500 就可以了。
  6. view.translatesAutoresizingMaskIntoConstraints = falseprepareForSegue()中设置loadView()作为其他答案。
  7. 现在我在自动调整大小的滚动视图中有一个动态可调的容器视图。

答案 3 :(得分:2)

在@Mischa解决此问题的出色方法的基础上,如果容器嵌入了动态大小的UITableView,则需要查看覆盖它的contentSize和intrinsicContentSize,并根据需要设置translatesAutoresizingMaskIntoConstraints = false接受的答案。

当父视图加载并设置容器视图时,每个UITableViewCells的固有高度被推断为0,因此UITableViewDelegate方法为:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

将不会被调用。这使UITableView看起来好像没有内容一样。

覆盖内部内容大小以返回实际内容大小意味着表视图以其内容所需的大小显示。

由EmilioPeláez撰写的出色article对这个话题进行了更深入的探讨。

答案 4 :(得分:1)

我尝试了这种解决方案并为我工作。
在目标(子)视图控制器中,尝试像这样访问父视图控制器:

if let parentVC = self.parent as? EmbededContinerViewController {               
   if let myParent = parentVC.parent as? ParentViewController {
      myParent.subViewHeight.constant += 2000
      myParent.subView.layoutIfNeeded()
   }

}

也许这不是正常的解决方案,但对我有用,并解决了我的问题。
我在 Swift 5.1 中尝试了此代码。