【问题标题】:Autolayout: How to compress a complex popover自动布局:如何压缩复杂的弹出框
【发布时间】:2015-11-07 22:00:20
【问题描述】:

如果您想尝试源代码(非常欢迎您这样做),请查看我的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()
    }
} 

设置弹窗里填满了内容,但大小不太合适:

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

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
        }
    }
}

总结:压缩不起作用。

【问题讨论】:

    标签: uitableview uiscrollview uipopovercontroller uicontainerview container-view


    【解决方案1】:

    所以我自己解决了这个问题,正如您在查看 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 以避免丑陋的垂直偏移 - 这个解决方案是次优的,因为它稍微破坏了滚动视图体验。但它有效。

    代码如下:

    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)
            }
        }
    }
    

    结果如下:

    【讨论】:

      猜你喜欢
      • 2012-04-17
      • 1970-01-01
      • 2010-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多