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

Posted

技术标签:

【中文标题】在 StackView 内动态添加 ViewControllers - UIScrollView 内的 UIStackView【英文标题】:Adding dynamically ViewControllers inside StackView - UIStackView inside UIScrollView 【发布时间】:2016-10-31 14:48:33 【问题描述】:

我想在UIStackView 中动态添加视图控制器。 UIStackView 必须是可滚动的,所以我将它添加到 UIScrollView 中。

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

UIStackView 中每个控制器的大小为.zero

--

这是我在 StoryBoard 上的层次结构

约束:

ScrollView.leading = StackView.leading ScrollView.trailing = StackView.trailing ScrollView.top = StackView.top ScrollView.bottom = StackView.bottom ScrollView.width = StackView.width ScrollView.height = StackView.height

我的源代码:

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

【问题讨论】:

我希望子视图的大小与其内在内容一样。在我的情况下,每个控制器上的约束都设置得很好,但是 contentSizeUIScrollView 上不正确 【参考方案1】:

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

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

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

V:|-[标签]-[步进器]-|

您可能还需要一些东西来规定堆栈视图相对于滚动视图的父视图的宽度,否则宽度也会不明确(否则它可能会使其非常窄,靠左边缘)。

无论如何,完全约束子视图控制器的视图,会产生如下内容:

【讨论】:

我已经设置了所有的约束,我还设置了UILabelUIStepperContent Compression Resistance Vertical = 1000 我发布了示例,相同的层次结构,类..相同的错误:) github.com/djnivek/StackViewOnScrollView @thedjnivek - 我花了一点时间试图弄清楚那里发生了什么,并且对(a)用空的segue实例化所有这些子视图控制器感到困惑; (b) 手动实例化该ContainerView,它与情节提要中的任何视图无关。这一切都非常令人困惑,所以我斩断了难题,并简化了它,现在它可以工作了。见github.com/robertmryan/StackViewOnScrollView。 不过,最重要的是,问题不是故事板中表示的约束,而可能是(a)您如何实例化该场景;或 (b) 视图层次结构和视图控制器层次结构之间存在某种脱节。 太完美了!!它正在工作! ;) 我认为它与视图层次结构断开了!谢谢!

以上是关于在 StackView 内动态添加 ViewControllers - UIScrollView 内的 UIStackView的主要内容,如果未能解决你的问题,请参考以下文章

按钮在编程动态 stackView (Swift) 中变为非活动状态

如何向 StackView 的子视图内的视图添加约束

UITableViewCell 里面有一个 StackView,里面有动态添加的 WKWebViews 和 UIImageViews

动态调整大小的 stackView 以减少内容

StackView 在一个元素周围添加额外的空间

有没有办法在不删除视图并重新添加的情况下更新 StackView 中的视图?