弹性布局无法与子视图控制器一起使用

时间:2019-02-17 21:26:30

标签: ios swift uiscrollview uicollectionview snapkit

我正在尝试遵循此处描述的示例来制作包含UIImageViewUIScrollView的伸缩布局。 https://github.com/TwoLivesLeft/StretchyLayout/tree/Step-6

唯一的区别是,我将示例中使用的UILabel替换为本身包含UIViewController的孩子UICollectionView的视图。这是我的布局的样子-蓝色项目是UICollectionViewCellenter image description here

这是我的代码:

import UIKit
import SnapKit

class HomeController: UIViewController, UIScrollViewDelegate {

private let scrollView = UIScrollView()
private let imageView = UIImageView()
private let contentContainer = UIView()
private let collectionViewController = CollectionViewController()

override var preferredStatusBarStyle: UIStatusBarStyle {
    return .lightContent
}

override func viewDidLoad() {
    super.viewDidLoad()


    scrollView.contentInsetAdjustmentBehavior = .never
    scrollView.delegate = self

    imageView.image = UIImage(named: "burger")
    imageView.contentMode = .scaleAspectFill
    imageView.clipsToBounds = true

    let imageContainer = UIView()
    imageContainer.backgroundColor = .darkGray

    contentContainer.backgroundColor = .clear

    let textBacking = UIView()
    textBacking.backgroundColor = #colorLiteral(red: 0.7450980544, green: 0.1235740449, blue: 0.2699040081, alpha: 1)

    view.addSubview(scrollView)

    scrollView.addSubview(imageContainer)
    scrollView.addSubview(textBacking)
    scrollView.addSubview(contentContainer)
    scrollView.addSubview(imageView)

    self.addChild(collectionViewController)
    contentContainer.addSubview(collectionViewController.view)
    collectionViewController.didMove(toParent: self)


    scrollView.snp.makeConstraints {
        make in

        make.edges.equalTo(view)
    }

    imageContainer.snp.makeConstraints {
        make in

        make.top.equalTo(scrollView)
        make.left.right.equalTo(view)
        make.height.equalTo(imageContainer.snp.width).multipliedBy(0.7)
    }

    imageView.snp.makeConstraints {
        make in

        make.left.right.equalTo(imageContainer)

        //** Note the priorities
        make.top.equalTo(view).priority(.high)

        //** We add a height constraint too
        make.height.greaterThanOrEqualTo(imageContainer.snp.height).priority(.required)

        //** And keep the bottom constraint
        make.bottom.equalTo(imageContainer.snp.bottom)
    }

    contentContainer.snp.makeConstraints {
        make in

        make.top.equalTo(imageContainer.snp.bottom)
        make.left.right.equalTo(view)
        make.bottom.equalTo(scrollView)
    }

    textBacking.snp.makeConstraints {
        make in

        make.left.right.equalTo(view)
        make.top.equalTo(contentContainer)
        make.bottom.equalTo(view)
    }

    collectionViewController.view.snp.makeConstraints {
        make in

        make.left.right.equalTo(view)
        make.top.equalTo(contentContainer)
        make.bottom.equalTo(view)
    }

}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    scrollView.scrollIndicatorInsets = view.safeAreaInsets
    scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: view.safeAreaInsets.bottom, right: 0)
}

//MARK: - Scroll View Delegate

private var previousStatusBarHidden = false

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if previousStatusBarHidden != shouldHideStatusBar {

        UIView.animate(withDuration: 0.2, animations: {
            self.setNeedsStatusBarAppearanceUpdate()
        })

        previousStatusBarHidden = shouldHideStatusBar
    }
}

//MARK: - Status Bar Appearance

override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
    return .slide
}

override var prefersStatusBarHidden: Bool {
    return shouldHideStatusBar
}

private var shouldHideStatusBar: Bool {
    let frame = contentContainer.convert(contentContainer.bounds, to: nil)
    return frame.minY < view.safeAreaInsets.top
}

}

所有内容均与此文件中的内容相同:https://github.com/TwoLivesLeft/StretchyLayout/blob/Step-6/StretchyLayouts/StretchyViewController.swift,但用我的innerText代替了CollectionViewController

如您所见,UICollectionView正确显示-但是我无法再向上或向下滚动。我不确定我的错误在哪里。

1 个答案:

答案 0 :(得分:1)

It looks like you are constraining the size of your collection view to fit within the bounds of the parent view containing the collection view's container view and the image view. As a result, the container scrollView has no contentSize to scroll over, and that's why you can't scroll. You need to ensure your collection view's content size is reflected in the parent scroll view's content size.

In the example you gave, this behavior was achieved by the length of the label requiring a height greater than the height between the image view and the rest of the view. In your case, the collection view container needs to behave as if it's larger than that area.

Edit: More precisely you need to pass the collectionView.contentSize up to your scrollView.contentSize. A scrollview's contentSize is settable, so you just need to increase the scrollView.contentSize by the collectionView.contentSize - collectionView.height (since your scrollView's current contentSize currently includes the collectionView's height). I'm not sure how you are adding your child view controller, but at the point you do that, I would increment your scrollView's contentSize accordingly. If your collectionView's size changes after that, though, you'll also need to ensure you delegate that change up to your scrollView. This could be accomplished by having a protocol such as:

protocol InnerCollectionViewHeightUpdated {
  func collectionViewContentHeightChanged(newSize: CGSize)
}

and then making the controller containing the scrollView implement this protocol and update the scrollView contentSize accordingly. From your collectionView child controller, you would have a delegate property for this protocol (set this when creating the child view controller, setting the delegate as self, the controller containing the child VC and also the scrollView). Then whenever the collectionView height changes (if you add cells, for example) you can do delegate.collectionViewContentHeightChanged(... to ensure your scroll behavior will continue to function.