three layer collection anchoring

This commit is contained in:
Pfeil, Scott Robert 2020-04-22 16:33:13 -04:00
parent e0d17f7270
commit a97132c897
4 changed files with 50 additions and 17 deletions

View File

@ -79,6 +79,8 @@ import Foundation
open override func handleNewData() { open override func handleNewData() {
topViewOutsideOfScrollArea = templateModel?.anchorHeader ?? false
bottomViewOutsideOfScrollArea = templateModel?.anchorFooter ?? false
setup() setup()
registerCells() registerCells()
super.handleNewData() super.handleNewData()

View File

@ -12,6 +12,8 @@ import Foundation
@objc open class ProgrammaticCollectionViewController: ScrollingViewController { @objc open class ProgrammaticCollectionViewController: ScrollingViewController {
public var collectionView: UICollectionView? public var collectionView: UICollectionView?
public var topConstraint: NSLayoutConstraint?
public var bottomConstraint: NSLayoutConstraint?
open override func loadView() { open override func loadView() {
let view = UIView() let view = UIView()
@ -19,7 +21,9 @@ import Foundation
let collection = createCollectionView() let collection = createCollectionView()
view.addSubview(collection) 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 collectionView = collection
scrollView = collectionView scrollView = collectionView

View File

@ -52,16 +52,34 @@ import Foundation
footerView?.topConstraint?.constant = half footerView?.topConstraint?.constant = half
collectionView?.collectionViewLayout.invalidateLayout() collectionView?.collectionViewLayout.invalidateLayout()
} else if fillTop { } 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 headerView?.bottomConstraint?.constant = newSpace
collectionView?.collectionViewLayout.invalidateLayout() collectionView?.collectionViewLayout.invalidateLayout()
} else if fillBottom { } else if fillBottom {
// Only bottom is spaced. // Only bottom is spaced.
footerView?.topConstraint?.constant = newSpace 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 //MARK: - ViewController
open override func updateViews() { open override func updateViews() {
@ -86,8 +104,22 @@ import Foundation
open override func handleNewData() { open override func handleNewData() {
super.handleNewData() super.handleNewData()
topView?.removeFromSuperview()
bottomView?.removeFromSuperview()
topView = viewForTop() topView = viewForTop()
bottomView = viewForBottom() bottomView = viewForBottom()
if topViewOutsideOfScrollArea {
topConstraint?.isActive = false
addTopViewOutside()
} else {
topConstraint?.isActive = true
}
if bottomViewOutsideOfScrollArea {
bottomConstraint?.isActive = false
addBottomViewOutside()
} else {
bottomConstraint?.isActive = true
}
reloadCollectionData() reloadCollectionData()
} }
@ -176,14 +208,14 @@ import Foundation
open func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { open func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if kind == UICollectionView.elementKindSectionFooter, if kind == UICollectionView.elementKindSectionFooter,
let footerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: footerID, for: indexPath) as? ContainerCollectionReusableView { 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.addAndContain(view: bottomView)
footerView.topConstraint?.constant = spaceAboveBottomView() ?? 0 footerView.topConstraint?.constant = spaceAboveBottomView() ?? 0
self.footerView = footerView self.footerView = footerView
return footerView return footerView
} else if kind == UICollectionView.elementKindSectionHeader, } else if kind == UICollectionView.elementKindSectionHeader,
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerID, for: indexPath) as? ContainerCollectionReusableView { 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.addAndContain(view: topView)
headerView.bottomConstraint?.constant = spaceBelowTopView() ?? 0 headerView.bottomConstraint?.constant = spaceBelowTopView() ?? 0
self.headerView = headerView self.headerView = headerView

View File

@ -117,22 +117,20 @@ extension ThreeLayerViewController {
if let topView = viewForTop() { if let topView = viewForTop() {
self.topView = topView self.topView = topView
} else { } else {
topView = MVMCoreUICommonViewsUtility.commonView() topView = MVMCoreUICommonViewsUtility.getView(with: 0)
topView?.heightAnchor.constraint(equalToConstant: 0).isActive = true
} }
guard var topView = topView else { return nil } guard var topView = topView else { return nil }
// Adds the top view outside the scroll if directed. // Adds the top view outside the scroll if directed.
if topViewOutsideOfScroll { if topViewOutsideOfScroll {
topConstraint?.isActive = false; topConstraint?.isActive = false
addViewOutsideOfScrollViewTop(topView) addViewOutsideOfScrollViewTop(topView)
// Adds and returns an empty view to use for the internal logic. // Adds and returns an empty view to use for the internal logic.
topView = MVMCoreUICommonViewsUtility.commonView() topView = MVMCoreUICommonViewsUtility.getView(with: 0)
topView.heightAnchor.constraint(equalToConstant: 0).isActive = true
addViewInsideOfScrollViewTop(topView) addViewInsideOfScrollViewTop(topView)
} else { } else {
topConstraint?.isActive = true; topConstraint?.isActive = true
addViewInsideOfScrollViewTop(topView) addViewInsideOfScrollViewTop(topView)
} }
return topView return topView
@ -142,8 +140,7 @@ extension ThreeLayerViewController {
if let middleView = viewForMiddle() { if let middleView = viewForMiddle() {
self.middleView = middleView self.middleView = middleView
} else { } else {
middleView = MVMCoreUICommonViewsUtility.commonView() middleView = MVMCoreUICommonViewsUtility.getView(with: 0)
middleView?.heightAnchor.constraint(equalToConstant: 0).isActive = true
} }
guard let middleView = middleView, let contentView = contentView else { return nil } guard let middleView = middleView, let contentView = contentView else { return nil }
contentView.addSubview(middleView) contentView.addSubview(middleView)
@ -157,8 +154,7 @@ extension ThreeLayerViewController {
if let bottomView = viewForBottom() { if let bottomView = viewForBottom() {
self.bottomView = bottomView self.bottomView = bottomView
} else { } else {
bottomView = MVMCoreUICommonViewsUtility.commonView() bottomView = MVMCoreUICommonViewsUtility.getView(with: 0)
bottomView?.heightAnchor.constraint(equalToConstant: 0).isActive = true
} }
guard var bottomView = bottomView else { return nil } guard var bottomView = bottomView else { return nil }
@ -168,8 +164,7 @@ extension ThreeLayerViewController {
addViewOutsideOfScrollViewBottom(bottomView) addViewOutsideOfScrollViewBottom(bottomView)
// Adds and returns an empty view to use for the internal logic. // Adds and returns an empty view to use for the internal logic.
bottomView = MVMCoreUICommonViewsUtility.commonView() bottomView = MVMCoreUICommonViewsUtility.getView(with: 0)
bottomView.heightAnchor.constraint(equalToConstant: 0).isActive = true
addViewInsideOfScrollViewBottom(bottomView) addViewInsideOfScrollViewBottom(bottomView)
} else { } else {
bottomConstraint?.isActive = true; bottomConstraint?.isActive = true;