在StackView中动态添加ViewControllers - UIScrollView中的UIStackView

时间:2016-10-31 14:48:34

标签: ios swift uiscrollview uistackview

我想在UIStackView内动态添加视图控制器。 UIStackView必须可滚动,因此我将其添加到UIScrollView

我有一个UI错误,内容大小不明确,我尝试使用Debug View Hierarchy进行调试,结果就是:

Bug Content Size is ambiguous UIScrollView UIStackView

UIStackView内每个控制器的尺寸为.zero

Debug View Hierarchy - UIScrollView UIStackView

-

这是我在StoryBoard上的层次结构

Architecture StackView inside ScrollView

UIStackView inside UIScrollView

约束:

  • ScrollViewleading = StackViewleading
  • ScrollViewtrailing = StackViewtrailing
  • ScrollViewtop = StackViewtop
  • ScrollViewbottom = StackViewbottom
  • ScrollViewwidth = StackViewwidth
  • ScrollViewheight = StackViewheight

我的源代码:

class ScrollViewController: UIViewController {


    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var stackView: UIStackView!

    private var strings = ["echo", "hola", "allo"]

    private var containerViews = [ContainerView]()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.setupViews()
    }

    private func setupViews() {
        self.setupContainers()
    }

    fileprivate func removeContainers() {
        for container in containerViews {
            container.uninstall()
        }
        containerViews.removeAll()
    }

    fileprivate func setupContainers() {
        removeContainers()
        for string in strings {
          let viewController = // get the view Controller from StoryBoard
          add(viewController)
        }
    }

    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let viewController = segue.destination
        let index = strings.index(of: sender as! String)!
    }

    fileprivate func add(_ viewController: UIViewController) {
        let containerView = ContainerView(parentController: self)
        containerView.install(viewController)
        stackView.addArrangedSubview(containerView)
    }

}


class ContainerView<T:UIViewController>: UIView {

    unowned var parentViewController: UIViewController
    weak var currentController: T?

    init(parentController: UIViewController) {
        self.parentViewController = parentController
        super.init(frame: CGRect.zero)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func install(_ viewController: T) {
        pushViewController(viewController, animated: false)
    }

    func uninstall() {
        if let controller = currentController {
            removeViewController(controller)
            currentController = nil
        }
    }

    fileprivate func setUpViewController(_ targetViewController: T?, animated: Bool) {
        if let viewController = targetViewController {
            parentViewController.addChildViewController(viewController)
            viewController.view.frame = self.bounds
            self.addSubview(viewController.view)
            viewController.didMove(toParentViewController: parentViewController)
        }
    }

    fileprivate func removeViewController(_ viewController: T?) {
        if let _viewController = currentController {
            _viewController.willMove(toParentViewController: nil)
            _viewController.view.removeFromSuperview()
            _viewController.removeFromParentViewController()
        }
    }

    fileprivate func pushViewController(_ controller: T, animated: Bool) {
        removeViewController(currentController)
        currentController = controller
        setUpViewController(controller, animated: false)
    }

}

我无法滚动UIScrollView,因为内容大小设置不正确。有谁知道如何解决这个错误?

编辑:您可以在这里看到带有错误示例的git: GitHub StackViewOnScrollView

1 个答案:

答案 0 :(得分:2)

您向我们展示了scrollview和堆栈视图之间的约束。那些看起来很好。这些约束将定义滚动视图的内容大小(假设子项的约束是完全限定的,如下所述)。有关滚动视图的约束细节,请参阅https://stackoverflow.com/a/16843937/1271826

问题可能是ContainerView内的限制。我怀疑,鉴于您的错误消息,您还没有完全限定子视图控制器的约束。

通常在IB中设计场景时,我们不必完全定义垂直约束(因为视图控制器的根视图的高度受到约束,因此我们关注顶部约束,但不关注底部约束) 。但在这种情况下,由于您将使用子控件的隐式高度,因此您需要完全限定所有约束,实际上等效于以下内容。注意,不仅有顶部约束,还有底部约束。 (我将在VFL中展示,因为这是定义约束的简明方法,但显然你可以在IB中定义这些,不一定是以编程方式。)

V:|-[label]-[stepper]-|

你也可能想要一些相对于滚动视图的超视图来指示堆栈视图宽度的东西,否则宽度也会模糊不清(否则它可能会使它非常窄,相对于左边缘)。

无论如何,完全约束子视图控制器的视图,产生类似于:

的内容

screen snapshot