Autolayout:如何压缩复杂的popover

时间:2015-08-14 15:26:55

标签: uitableview uiscrollview uipopovercontroller uicontainerview container-view

如果您想尝试使用源代码(非常欢迎您这样做),请查看我的Bitbucket repository

我有一个弹出窗口对话框,显示设置列表。这些设置列在多个UITableViews中。对于整体设置视图,UITableViews不可滚动。此外,弹出对话框应根据需要垂直占用屏幕,但应水平压缩。

因此,我构思了以下结构:

UIView => MySettingsViewController
- UIScrollView
  - UIView (Content View)
    - Container View1
      - UITableView (embedded) => MyTableViewController
    - Container View2
      - UITableView (embedded)

结构通过Interface Builder进行组装,Autolayout用于调整大小。

我有滚动视图,内容视图(我只用一个开始)和容器视图到他们各自的超级视图(或布局指南)。我以下列方式约束内容视图的大小:

contentView.width == (topmost) UIView.width
contentView.height == 200 // removed at build time

此外,我将表格视图的大小设置为其内容大小,因为否则弹出窗口显示为空:

class MyTableViewController: UITableViewController {
    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        // this is Cartography syntax - the intention should be clear
        layout(view, replace: ConstraintGroup()) { [unowned self] view in
            view.width == self.tableView.contentSize.width
            view.height == self.tableView.contentSize.height
        }
        view.setNeedsLayout()
    }
} 

设置弹出窗口中填充了内容,但其大小不太正确:

enter image description here

为了解决这个问题,我尝试了以下不起作用的方法:

class MySettingsViewController: UIViewController {
    override var preferredContentSize: CGSize {
        get {
            let compressedSize = view.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
            // this is always (0, 0) because the subviews are not resized, yet 
            return compressedSize 
        }
        set {
            super.preferredContentSize = newValue
        }
    }
}

总结:压缩不起作用。

1 个答案:

答案 0 :(得分:0)

所以我只是在看Bitbucket存储库时自己修复了问题。

现在,MyTableViewControllerMySettingsViewController都修复了布局。前一个现在看起来像这样:

class MyTableViewController: UITableViewController {

    var heightConstraint: NSLayoutConstraint?
    var tableViewEdgesConstraints: [NSLayoutConstraint]?

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        if let container = tableView.superview where tableViewEdgesConstraints == nil {
            layout(tableView, container, replace: ConstraintGroup()) { [unowned self] tableView, container in
                self.tableViewEdgesConstraints = tableView.edges == inset(container.edges, 0)
            }
        }
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        if let heightConstraint = heightConstraint {
            if Int(heightConstraint.constant) != Int(tableView.contentSize.height) {
                heightConstraint.constant = self.tableView.contentSize.height
            }
        } else {
            layout(view, replace: ConstraintGroup()) { [unowned self] view in
                if (self.tableView.contentSize.height > 0) {
                    self.heightConstraint = view.height == self.tableView.contentSize.height
                }
            }
        }
    }
} 

基本上,我将表格的高度约束到其内容的高度,并在内容的高度发生变化时更改约束。布置表格后立即执行此操作。此外,嵌套表视图的边缘固定在容器视图的边缘。我认为这是强制性的,因为我无法在Interface Builder中找到如何约束不同场景的两个视图。

MySettingsViewController中,只要知道此尺寸,就可以将滚动视图的尺寸设置为内容视图框架的大小(可通过插座访问)。此外,为了进行弹出式压缩,设置控制器的preferredContentSize相应地进行调整,当高度发生变化时(如果省略条件,你可能会自己进入布局无限循环。此外,我做了3件事,使得有可能导航控制器缠绕在MySettingsViewController上:

  1. 弹出窗口的宽度设置为固定值(否则有时会扩展到整个宽度)。
  2. 需要同等地设置presentViewController的preferredContentSize。
  3. 我必须将scrollView的insets设置为0以避免丑陋的垂直偏移 - 这个解决方案是次优的,因为它会破坏滚动视图体验。但它确实有效。
  4. 以下是代码:

    class MySettingsViewController: UIViewController {
    
        @IBOutlet weak var contentView: UIView!
        @IBOutlet weak var scrollView: UIScrollView!
    
        override func viewDidLayoutSubviews() {
            super.viewDidLayoutSubviews()
    
            scrollView.contentSize = contentView.frame.size
    
            if (preferredContentSize.height != scrollView.contentSize.height) {
                let newSize = CGSize(width: 400, height: scrollView.contentSize.height)
                preferredContentSize = newSize
                presentingViewController?.presentedViewController?.preferredContentSize = newSize
                scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
            }
        }
    }
    

    这就是结果:

    enter image description here