From a97132c8971618438ce02d0563f20127a0e00dd6 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 22 Apr 2020 16:33:13 -0400 Subject: [PATCH] three layer collection anchoring --- .../Atomic/Templates/CollectionTemplate.swift | 2 + ...ProgrammaticCollectionViewController.swift | 6 ++- .../ThreeLayerCollectionViewController.swift | 40 +++++++++++++++++-- .../ThreeLayerViewController.swift | 19 ++++----- 4 files changed, 50 insertions(+), 17 deletions(-) diff --git a/MVMCoreUI/Atomic/Templates/CollectionTemplate.swift b/MVMCoreUI/Atomic/Templates/CollectionTemplate.swift index 1d2f07a6..be4273df 100644 --- a/MVMCoreUI/Atomic/Templates/CollectionTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/CollectionTemplate.swift @@ -79,6 +79,8 @@ import Foundation open override func handleNewData() { + topViewOutsideOfScrollArea = templateModel?.anchorHeader ?? false + bottomViewOutsideOfScrollArea = templateModel?.anchorFooter ?? false setup() registerCells() super.handleNewData() diff --git a/MVMCoreUI/BaseControllers/ProgrammaticCollectionViewController.swift b/MVMCoreUI/BaseControllers/ProgrammaticCollectionViewController.swift index 5e7b87b0..c70a3ef1 100644 --- a/MVMCoreUI/BaseControllers/ProgrammaticCollectionViewController.swift +++ b/MVMCoreUI/BaseControllers/ProgrammaticCollectionViewController.swift @@ -12,6 +12,8 @@ import Foundation @objc open class ProgrammaticCollectionViewController: ScrollingViewController { public var collectionView: UICollectionView? + public var topConstraint: NSLayoutConstraint? + public var bottomConstraint: NSLayoutConstraint? open override func loadView() { let view = UIView() @@ -19,7 +21,9 @@ import Foundation let collection = createCollectionView() view.addSubview(collection) - NSLayoutConstraint.constraintPinSubview(toSuperview: collection) + let constraints = NSLayoutConstraint.constraintPinSubview(toSuperview: collection) + topConstraint = constraints?[ConstraintTop] as? NSLayoutConstraint + bottomConstraint = constraints?[ConstraintBot] as? NSLayoutConstraint collectionView = collection scrollView = collectionView diff --git a/MVMCoreUI/BaseControllers/ThreeLayerCollectionViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerCollectionViewController.swift index e16d760f..63f86ac2 100644 --- a/MVMCoreUI/BaseControllers/ThreeLayerCollectionViewController.swift +++ b/MVMCoreUI/BaseControllers/ThreeLayerCollectionViewController.swift @@ -52,16 +52,34 @@ import Foundation footerView?.topConstraint?.constant = half collectionView?.collectionViewLayout.invalidateLayout() } else if fillTop { - // Only top is spaced (half the size if the bottom view is out of the scroll because it needs to be sized as if there are two spacers but there is only one. + // Only top is spaced. headerView?.bottomConstraint?.constant = newSpace collectionView?.collectionViewLayout.invalidateLayout() } else if fillBottom { // Only bottom is spaced. footerView?.topConstraint?.constant = newSpace - collectionView?.collectionViewLayout.invalidateLayout() + collectionView.collectionViewLayout.invalidateLayout() } } } + + func addTopViewOutside() { + guard let collectionView = collectionView, let topView = topView else { return } + view.addSubview(topView) + topView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true + topView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true + view.safeAreaLayoutGuide.rightAnchor.constraint(equalTo: topView.rightAnchor).isActive = true + collectionView.topAnchor.constraint(equalTo: topView.bottomAnchor).isActive = true + } + + func addBottomViewOutside() { + guard let collectionView = collectionView, let bottomView = bottomView else { return } + view.addSubview(bottomView) + bottomView.topAnchor.constraint(equalTo: collectionView.bottomAnchor).isActive = true + bottomView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true + view.safeAreaLayoutGuide.rightAnchor.constraint(equalTo: bottomView.rightAnchor).isActive = true + view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor).isActive = true + } //MARK: - ViewController open override func updateViews() { @@ -86,8 +104,22 @@ import Foundation open override func handleNewData() { super.handleNewData() + topView?.removeFromSuperview() + bottomView?.removeFromSuperview() topView = viewForTop() bottomView = viewForBottom() + if topViewOutsideOfScrollArea { + topConstraint?.isActive = false + addTopViewOutside() + } else { + topConstraint?.isActive = true + } + if bottomViewOutsideOfScrollArea { + bottomConstraint?.isActive = false + addBottomViewOutside() + } else { + bottomConstraint?.isActive = true + } reloadCollectionData() } @@ -176,14 +208,14 @@ import Foundation open func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { if kind == UICollectionView.elementKindSectionFooter, let footerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: footerID, for: indexPath) as? ContainerCollectionReusableView { - let bottomView = self.bottomView ?? MVMCoreUICommonViewsUtility.getView(with: 0) + let bottomView = (bottomViewOutsideOfScrollArea ? nil : self.bottomView) ?? MVMCoreUICommonViewsUtility.getView(with: 0.5) footerView.addAndContain(view: bottomView) footerView.topConstraint?.constant = spaceAboveBottomView() ?? 0 self.footerView = footerView return footerView } else if kind == UICollectionView.elementKindSectionHeader, let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerID, for: indexPath) as? ContainerCollectionReusableView { - let topView = self.topView ?? MVMCoreUICommonViewsUtility.getView(with: 0) + let topView = (topViewOutsideOfScrollArea ? nil : self.topView) ?? MVMCoreUICommonViewsUtility.getView(with: 0.5) headerView.addAndContain(view: topView) headerView.bottomConstraint?.constant = spaceBelowTopView() ?? 0 self.headerView = headerView diff --git a/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift index 91791109..2b0d60ca 100644 --- a/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift +++ b/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift @@ -117,22 +117,20 @@ extension ThreeLayerViewController { if let topView = viewForTop() { self.topView = topView } else { - topView = MVMCoreUICommonViewsUtility.commonView() - topView?.heightAnchor.constraint(equalToConstant: 0).isActive = true + topView = MVMCoreUICommonViewsUtility.getView(with: 0) } guard var topView = topView else { return nil } // Adds the top view outside the scroll if directed. if topViewOutsideOfScroll { - topConstraint?.isActive = false; + topConstraint?.isActive = false addViewOutsideOfScrollViewTop(topView) // Adds and returns an empty view to use for the internal logic. - topView = MVMCoreUICommonViewsUtility.commonView() - topView.heightAnchor.constraint(equalToConstant: 0).isActive = true + topView = MVMCoreUICommonViewsUtility.getView(with: 0) addViewInsideOfScrollViewTop(topView) } else { - topConstraint?.isActive = true; + topConstraint?.isActive = true addViewInsideOfScrollViewTop(topView) } return topView @@ -142,8 +140,7 @@ extension ThreeLayerViewController { if let middleView = viewForMiddle() { self.middleView = middleView } else { - middleView = MVMCoreUICommonViewsUtility.commonView() - middleView?.heightAnchor.constraint(equalToConstant: 0).isActive = true + middleView = MVMCoreUICommonViewsUtility.getView(with: 0) } guard let middleView = middleView, let contentView = contentView else { return nil } contentView.addSubview(middleView) @@ -157,8 +154,7 @@ extension ThreeLayerViewController { if let bottomView = viewForBottom() { self.bottomView = bottomView } else { - bottomView = MVMCoreUICommonViewsUtility.commonView() - bottomView?.heightAnchor.constraint(equalToConstant: 0).isActive = true + bottomView = MVMCoreUICommonViewsUtility.getView(with: 0) } guard var bottomView = bottomView else { return nil } @@ -168,8 +164,7 @@ extension ThreeLayerViewController { addViewOutsideOfScrollViewBottom(bottomView) // Adds and returns an empty view to use for the internal logic. - bottomView = MVMCoreUICommonViewsUtility.commonView() - bottomView.heightAnchor.constraint(equalToConstant: 0).isActive = true + bottomView = MVMCoreUICommonViewsUtility.getView(with: 0) addViewInsideOfScrollViewBottom(bottomView) } else { bottomConstraint?.isActive = true;