fiddling with three layer
This commit is contained in:
parent
d393098fcd
commit
d53b212241
@ -249,6 +249,7 @@
|
|||||||
D264FA8C243BCD8E00D98315 /* CollectionTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D264FA8B243BCD8E00D98315 /* CollectionTemplateModel.swift */; };
|
D264FA8C243BCD8E00D98315 /* CollectionTemplateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D264FA8B243BCD8E00D98315 /* CollectionTemplateModel.swift */; };
|
||||||
D264FA8E243BCD9A00D98315 /* CollectionTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D264FA8D243BCD9A00D98315 /* CollectionTemplate.swift */; };
|
D264FA8E243BCD9A00D98315 /* CollectionTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D264FA8D243BCD9A00D98315 /* CollectionTemplate.swift */; };
|
||||||
D264FA90243BCE6800D98315 /* ThreeLayerCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D264FA8F243BCE6800D98315 /* ThreeLayerCollectionViewController.swift */; };
|
D264FA90243BCE6800D98315 /* ThreeLayerCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D264FA8F243BCE6800D98315 /* ThreeLayerCollectionViewController.swift */; };
|
||||||
|
D264FAA1243CF66B00D98315 /* ContainerCollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D264FAA0243CF66B00D98315 /* ContainerCollectionReusableView.swift */; };
|
||||||
D268C70C2386DFFD007F2C1C /* MoleculeStackItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB368A23609801006832FA /* MoleculeStackItemModel.swift */; };
|
D268C70C2386DFFD007F2C1C /* MoleculeStackItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EB368A23609801006832FA /* MoleculeStackItemModel.swift */; };
|
||||||
D268C70E238C22D7007F2C1C /* DropDownFilterTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D268C70D238C22D7007F2C1C /* DropDownFilterTableViewCell.swift */; };
|
D268C70E238C22D7007F2C1C /* DropDownFilterTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D268C70D238C22D7007F2C1C /* DropDownFilterTableViewCell.swift */; };
|
||||||
D26C5A6B23F4A40D007AEECE /* ListItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D26C5A6A23F4A40D007AEECE /* ListItemModel.swift */; };
|
D26C5A6B23F4A40D007AEECE /* ListItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D26C5A6A23F4A40D007AEECE /* ListItemModel.swift */; };
|
||||||
@ -654,6 +655,7 @@
|
|||||||
D264FA8B243BCD8E00D98315 /* CollectionTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionTemplateModel.swift; sourceTree = "<group>"; };
|
D264FA8B243BCD8E00D98315 /* CollectionTemplateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionTemplateModel.swift; sourceTree = "<group>"; };
|
||||||
D264FA8D243BCD9A00D98315 /* CollectionTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionTemplate.swift; sourceTree = "<group>"; };
|
D264FA8D243BCD9A00D98315 /* CollectionTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionTemplate.swift; sourceTree = "<group>"; };
|
||||||
D264FA8F243BCE6800D98315 /* ThreeLayerCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerCollectionViewController.swift; sourceTree = "<group>"; };
|
D264FA8F243BCE6800D98315 /* ThreeLayerCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerCollectionViewController.swift; sourceTree = "<group>"; };
|
||||||
|
D264FAA0243CF66B00D98315 /* ContainerCollectionReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerCollectionReusableView.swift; sourceTree = "<group>"; };
|
||||||
D268C70D238C22D7007F2C1C /* DropDownFilterTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropDownFilterTableViewCell.swift; sourceTree = "<group>"; };
|
D268C70D238C22D7007F2C1C /* DropDownFilterTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropDownFilterTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
D26C5A6A23F4A40D007AEECE /* ListItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListItemModel.swift; sourceTree = "<group>"; };
|
D26C5A6A23F4A40D007AEECE /* ListItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListItemModel.swift; sourceTree = "<group>"; };
|
||||||
D274CA322236A78900B01B62 /* FooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FooterView.swift; sourceTree = "<group>"; };
|
D274CA322236A78900B01B62 /* FooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FooterView.swift; sourceTree = "<group>"; };
|
||||||
@ -1477,6 +1479,7 @@
|
|||||||
D29DF2CD21E7C104003B2FB9 /* MFLoadingViewController.m */,
|
D29DF2CD21E7C104003B2FB9 /* MFLoadingViewController.m */,
|
||||||
D2A5146A2214905000345BFB /* ThreeLayerViewController.swift */,
|
D2A5146A2214905000345BFB /* ThreeLayerViewController.swift */,
|
||||||
D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */,
|
D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */,
|
||||||
|
D264FAA0243CF66B00D98315 /* ContainerCollectionReusableView.swift */,
|
||||||
D264FA8F243BCE6800D98315 /* ThreeLayerCollectionViewController.swift */,
|
D264FA8F243BCE6800D98315 /* ThreeLayerCollectionViewController.swift */,
|
||||||
D2C521A823EDE79E00CA2634 /* ViewController.swift */,
|
D2C521A823EDE79E00CA2634 /* ViewController.swift */,
|
||||||
D2A92881241AAB67004E01C6 /* ScrollingViewController.swift */,
|
D2A92881241AAB67004E01C6 /* ScrollingViewController.swift */,
|
||||||
@ -2029,6 +2032,7 @@
|
|||||||
D21B7F77243BB70700051ABF /* MoleculeCollectionItemModel.swift in Sources */,
|
D21B7F77243BB70700051ABF /* MoleculeCollectionItemModel.swift in Sources */,
|
||||||
D29DF2B421E7B76D003B2FB9 /* MFLoadingSpinner.m in Sources */,
|
D29DF2B421E7B76D003B2FB9 /* MFLoadingSpinner.m in Sources */,
|
||||||
011D9602240DA20A000E3791 /* FormRuleWatcherFieldProtocol.swift in Sources */,
|
011D9602240DA20A000E3791 /* FormRuleWatcherFieldProtocol.swift in Sources */,
|
||||||
|
D264FAA1243CF66B00D98315 /* ContainerCollectionReusableView.swift in Sources */,
|
||||||
D260106323D0C05000764D80 /* StackItemModel.swift in Sources */,
|
D260106323D0C05000764D80 /* StackItemModel.swift in Sources */,
|
||||||
D2E2A99823D8D63C000B42E6 /* ActionDetailWithImageModel.swift in Sources */,
|
D2E2A99823D8D63C000B42E6 /* ActionDetailWithImageModel.swift in Sources */,
|
||||||
D2E2A99D23DA3217000B42E6 /* UIStackViewAlignment+Extension.swift in Sources */,
|
D2E2A99D23DA3217000B42E6 /* UIStackViewAlignment+Extension.swift in Sources */,
|
||||||
|
|||||||
@ -112,7 +112,9 @@ import Foundation
|
|||||||
// Other Items
|
// Other Items
|
||||||
MoleculeObjectMapping.shared()?.register(viewClass: MoleculeStackItem.self, viewModelClass: MoleculeStackItemModel.self)
|
MoleculeObjectMapping.shared()?.register(viewClass: MoleculeStackItem.self, viewModelClass: MoleculeStackItemModel.self)
|
||||||
MoleculeObjectMapping.shared()?.register(viewClass: StackItem.self, viewModelClass: StackItemModel.self)
|
MoleculeObjectMapping.shared()?.register(viewClass: StackItem.self, viewModelClass: StackItemModel.self)
|
||||||
MoleculeObjectMapping.shared()?.register(viewClass: MoleculeCollectionViewCell.self, viewModelClass: CarouselItemModel.self)
|
MoleculeObjectMapping.shared()?.register(viewClass: MoleculeCollectionViewCell.self, viewModelClass: MoleculeCollectionItemModel.self)
|
||||||
|
MoleculeObjectMapping.shared()?.register(viewClass: CarouselItem.self, viewModelClass: CarouselItemModel.self)
|
||||||
|
|
||||||
|
|
||||||
// Other Container Molecules
|
// Other Container Molecules
|
||||||
MoleculeObjectMapping.shared()?.register(viewClass: MoleculeHeaderView.self, viewModelClass: MoleculeHeaderModel.self)
|
MoleculeObjectMapping.shared()?.register(viewClass: MoleculeHeaderView.self, viewModelClass: MoleculeHeaderModel.self)
|
||||||
|
|||||||
@ -35,4 +35,11 @@ import Foundation
|
|||||||
self.color = color
|
self.color = color
|
||||||
self.label = label
|
self.label = label
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case backgroundColor
|
||||||
|
case label
|
||||||
|
case percent
|
||||||
|
case color
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,5 +11,167 @@ import Foundation
|
|||||||
@objc open class CollectionTemplate: ThreeLayerCollectionViewController, TemplateProtocol {
|
@objc open class CollectionTemplate: ThreeLayerCollectionViewController, TemplateProtocol {
|
||||||
public typealias TemplateModel = CollectionTemplateModel
|
public typealias TemplateModel = CollectionTemplateModel
|
||||||
public var templateModel: CollectionTemplateModel?
|
public var templateModel: CollectionTemplateModel?
|
||||||
|
|
||||||
|
public var moleculesInfo: [(identifier: String, class: AnyClass, molecule: (CollectionItemModelProtocol & MoleculeModelProtocol))]?
|
||||||
|
|
||||||
|
var observer: NSKeyValueObservation?
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Computed Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
open override func parsePageJSON() throws {
|
||||||
|
try parseTemplate(json: loadObject?.pageJSON)
|
||||||
|
try super.parsePageJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
open override var loadObject: MVMCoreLoadObject? {
|
||||||
|
didSet {
|
||||||
|
guard loadObject != oldValue else { return }
|
||||||
|
|
||||||
|
updateRequiredModules()
|
||||||
|
observer?.invalidate()
|
||||||
|
if let newObject = loadObject {
|
||||||
|
observer = newObject.observe(\MVMCoreLoadObject.pageJSON, options: [.old, .new]) { [weak self] object, change in
|
||||||
|
self?.updateRequiredModules()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Methods
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
open override func viewForTop() -> UIView? {
|
||||||
|
guard let headerModel = templateModel?.header,
|
||||||
|
let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar)
|
||||||
|
else { return super.viewForTop() }
|
||||||
|
|
||||||
|
// Temporary, Default the horizontal padding
|
||||||
|
if var container = templateModel?.header as? ContainerModelProtocol, container.useHorizontalMargins == nil {
|
||||||
|
container.useHorizontalMargins = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return molecule
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func viewForBottom() -> UIView? {
|
||||||
|
guard let footerModel = templateModel?.footer,
|
||||||
|
let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar)
|
||||||
|
else { return super.viewForBottom() }
|
||||||
|
|
||||||
|
return molecule
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject>) -> Bool {
|
||||||
|
|
||||||
|
guard super.shouldFinishProcessingLoad(loadObject, error: error) else { return false }
|
||||||
|
|
||||||
|
// This template requires atleast one of the three layers.
|
||||||
|
if templateModel?.header == nil,
|
||||||
|
templateModel?.molecules?.count ?? 0 == 0,
|
||||||
|
templateModel?.footer == nil,
|
||||||
|
let errorObject = MVMCoreErrorObject(title: nil, message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess), messageToLog: "Collection template requires atleast one of the following: header, footer, molecules", code: CoreUIErrorCode.ErrorCodeListMolecule.rawValue, domain: ErrorDomainNative, location: String(describing: self)) {
|
||||||
|
error.pointee = errorObject
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
open override func handleNewData() {
|
||||||
|
super.handleNewData()
|
||||||
|
setup()
|
||||||
|
registerCells()
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Collection
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
open override func registerCells() {
|
||||||
|
super.registerCells()
|
||||||
|
guard let moleculesInfo = moleculesInfo else { return }
|
||||||
|
|
||||||
|
for moleculeInfo in moleculesInfo {
|
||||||
|
collectionView?.register(moleculeInfo.class, forCellWithReuseIdentifier: moleculeInfo.identifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return moleculesInfo?.count ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func numberOfSections(in collectionView: UICollectionView) -> Int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
|
guard let moleculeInfo = moleculesInfo?[indexPath.row]
|
||||||
|
else { return UICollectionViewCell() }
|
||||||
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: moleculeInfo.identifier, for: indexPath)
|
||||||
|
(cell as? MoleculeViewProtocol)?.reset()
|
||||||
|
(cell as? MoleculeViewProtocol)?.set(with: moleculeInfo.molecule, delegateObjectIVar, nil)
|
||||||
|
(cell as? MVMCoreViewProtocol)?.updateView(view.bounds.width)
|
||||||
|
|
||||||
|
// Neded to fix an apple defect where the cell is not the correct size on certain devices for certain cells
|
||||||
|
cell.layoutIfNeeded()
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Convenience
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
/// Returns the (identifier, class) of the molecule for the given map.
|
||||||
|
func getMoleculeInfo(with item: (CollectionItemModelProtocol & MoleculeModelProtocol)?) -> (identifier: String, class: AnyClass, molecule: CollectionItemModelProtocol & MoleculeModelProtocol)? {
|
||||||
|
guard let item = item,
|
||||||
|
let moleculeClass = MoleculeObjectMapping.shared()?.getMoleculeClass(item) else { return nil }
|
||||||
|
let moleculeName = moleculeClass.nameForReuse(with: item, delegateObjectIVar) ?? item.moleculeName
|
||||||
|
return (moleculeName, moleculeClass, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets up the molecule list and ensures no errors loading all content.
|
||||||
|
func getMoleculeInfoList() -> [(identifier: String, class: AnyClass, molecule: (CollectionItemModelProtocol & MoleculeModelProtocol))]? {
|
||||||
|
|
||||||
|
var moleculeList: [(identifier: String, class: AnyClass, molecule: CollectionItemModelProtocol & MoleculeModelProtocol)] = []
|
||||||
|
|
||||||
|
if let molecules = templateModel?.molecules {
|
||||||
|
for molecule in molecules {
|
||||||
|
if let info = getMoleculeInfo(with: molecule) {
|
||||||
|
moleculeList.append(info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return moleculeList.count > 0 ? moleculeList : nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets up the header, footer, molecule list and ensures no errors loading all content.
|
||||||
|
func setup() {
|
||||||
|
moleculesInfo = getMoleculeInfoList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds modules from requiredModules() to the MVMCoreViewControllerMapping.requiredModules map.
|
||||||
|
open func updateRequiredModules() {
|
||||||
|
if let requiredModules = requiredModules(), let pageType = pageType {
|
||||||
|
MVMCoreViewControllerMappingObject.shared()?.addRequiredModules(toMapping: requiredModules, forPageType: pageType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets modules required by the loadObject.pageJSON.
|
||||||
|
open func requiredModules() -> [Any]? {
|
||||||
|
var modules: [String]? = []
|
||||||
|
var errors: [MVMCoreErrorObject]? = nil
|
||||||
|
MoleculeObjectMapping.addRequiredModules(for: templateModel?.header, delegateObjectIVar, moduleList: &modules, errorList: &errors)
|
||||||
|
MoleculeObjectMapping.addRequiredModules(for: templateModel?.footer, delegateObjectIVar, moduleList: &modules, errorList: &errors)
|
||||||
|
|
||||||
|
if let molecules = templateModel?.molecules {
|
||||||
|
for molecule in molecules {
|
||||||
|
MoleculeObjectMapping.addRequiredModules(for: molecule, delegateObjectIVar, moduleList: &modules, errorList: &errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return modules
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// ContainerCollectionReusableView.swift
|
||||||
|
// MVMCoreUI
|
||||||
|
//
|
||||||
|
// Created by Scott Pfeil on 4/7/20.
|
||||||
|
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public class ContainerCollectionReusableView: UICollectionReusableView {
|
||||||
|
var view: UIView?
|
||||||
|
var topConstraint: NSLayoutConstraint?
|
||||||
|
var bottomConstraint: NSLayoutConstraint?
|
||||||
|
|
||||||
|
public func addAndContain(view: UIView) {
|
||||||
|
self.view?.removeFromSuperview()
|
||||||
|
view.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
|
addSubview(view)
|
||||||
|
self.view = view
|
||||||
|
let constraints = NSLayoutConstraint.constraintPinSubview(toSuperview: view)
|
||||||
|
topConstraint = constraints?[ConstraintTop] as? NSLayoutConstraint
|
||||||
|
bottomConstraint = constraints?[ConstraintBot] as? NSLayoutConstraint
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,19 +8,17 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
@objc open class ThreeLayerCollectionViewController: ScrollingViewController, UICollectionViewDataSource, UICollectionViewDelegate {
|
@objc open class ThreeLayerCollectionViewController: ScrollingViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
|
||||||
public var collectionView: UICollectionView?
|
public var collectionView: UICollectionView?
|
||||||
|
|
||||||
// The three main views
|
// The three main views
|
||||||
private var topView: UIView?
|
private var topView: UIView?
|
||||||
private var bottomView: UIView?
|
private var bottomView: UIView?
|
||||||
private var headerView: UIView?
|
private var headerView: ContainerCollectionReusableView?
|
||||||
private var footerView: UIView?
|
private var footerView: ContainerCollectionReusableView?
|
||||||
private var safeAreaView: UIView?
|
|
||||||
var useMargins: Bool = true
|
var useMargins: Bool = true
|
||||||
public var bottomViewOutsideOfScrollArea: Bool = false
|
private let headerID = "header"
|
||||||
private var topViewBottomConstraint: NSLayoutConstraint?
|
private let footerID = "footer"
|
||||||
private var bottomViewTopConstraint: NSLayoutConstraint?
|
|
||||||
|
|
||||||
//MARK: - MFViewController
|
//MARK: - MFViewController
|
||||||
open override func updateViews() {
|
open override func updateViews() {
|
||||||
@ -28,7 +26,7 @@ import Foundation
|
|||||||
let width = view.bounds.width
|
let width = view.bounds.width
|
||||||
if let topView = topView as? MVMCoreViewProtocol {
|
if let topView = topView as? MVMCoreViewProtocol {
|
||||||
topView.updateView(width)
|
topView.updateView(width)
|
||||||
//showHeader(width)
|
// showHeader(width)
|
||||||
}
|
}
|
||||||
if let bottomView = bottomView as? MVMCoreViewProtocol {
|
if let bottomView = bottomView as? MVMCoreViewProtocol {
|
||||||
bottomView.updateView(width)
|
bottomView.updateView(width)
|
||||||
@ -39,8 +37,8 @@ import Foundation
|
|||||||
|
|
||||||
open override func handleNewData() {
|
open override func handleNewData() {
|
||||||
super.handleNewData()
|
super.handleNewData()
|
||||||
//createViewForTableHeader()
|
createViewForTableHeader()
|
||||||
//createViewForTableFooter()
|
createViewForTableFooter()
|
||||||
collectionView?.reloadData()
|
collectionView?.reloadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,11 +72,11 @@ import Foundation
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/*open override func updateViewConstraints() {
|
open override func updateViewConstraints() {
|
||||||
super.updateViewConstraints()
|
super.updateViewConstraints()
|
||||||
|
|
||||||
guard let tableView = collectionView else { return }
|
guard let tableView = collectionView else { return }
|
||||||
|
|
||||||
let minimumSpace: CGFloat = minimumFillSpace()
|
let minimumSpace: CGFloat = minimumFillSpace()
|
||||||
var currentSpace: CGFloat = 0
|
var currentSpace: CGFloat = 0
|
||||||
var totalMinimumSpace: CGFloat = 0
|
var totalMinimumSpace: CGFloat = 0
|
||||||
@ -86,48 +84,43 @@ import Foundation
|
|||||||
var fillTop = false
|
var fillTop = false
|
||||||
if spaceBelowTopView() == nil, headerView != nil {
|
if spaceBelowTopView() == nil, headerView != nil {
|
||||||
fillTop = true
|
fillTop = true
|
||||||
currentSpace += topViewBottomConstraint?.constant ?? 0
|
currentSpace += headerView?.bottomConstraint?.constant ?? 0
|
||||||
totalMinimumSpace += minimumSpace
|
totalMinimumSpace += minimumSpace
|
||||||
}
|
}
|
||||||
|
|
||||||
var fillBottom = false
|
var fillBottom = false
|
||||||
if spaceAboveBottomView() == nil, !bottomViewOutsideOfScrollArea, footerView != nil {
|
if spaceAboveBottomView() == nil, footerView != nil {
|
||||||
fillBottom = true
|
fillBottom = true
|
||||||
currentSpace += bottomViewTopConstraint?.constant ?? 0
|
currentSpace += footerView?.topConstraint?.constant ?? 0
|
||||||
totalMinimumSpace += minimumSpace
|
totalMinimumSpace += minimumSpace
|
||||||
}
|
}
|
||||||
|
|
||||||
guard fillTop || fillBottom else { return }
|
guard fillTop || fillBottom else { return }
|
||||||
|
|
||||||
let newSpace = MVMCoreUIUtility.getVariableConstraintHeight(currentSpace, in: tableView, minimumHeight: totalMinimumSpace)
|
let newSpace = MVMCoreUIUtility.getVariableConstraintHeight(currentSpace, in: tableView, minimumHeight: totalMinimumSpace)
|
||||||
|
|
||||||
// If the bottom view is outside of the scroll, then only the top view constraint is being used, so we have to double it to account for the bottom constraint not being there when we compare to the new value.
|
// If the bottom view is outside of the scroll, then only the top view constraint is being used, so we have to double it to account for the bottom constraint not being there when we compare to the new value.
|
||||||
var currentSpaceForCompare: CGFloat = currentSpace
|
var currentSpaceForCompare: CGFloat = currentSpace
|
||||||
if fillTop && bottomViewOutsideOfScrollArea {
|
if fillTop {
|
||||||
currentSpaceForCompare = currentSpace * 2;
|
currentSpaceForCompare = currentSpace * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
let width = view.bounds.width
|
|
||||||
if !MVMCoreGetterUtility.cgfequalwiththreshold(newSpace, currentSpaceForCompare, 0.1) {
|
if !MVMCoreGetterUtility.cgfequalwiththreshold(newSpace, currentSpaceForCompare, 0.1) {
|
||||||
if fillTop && fillBottom {
|
if fillTop && fillBottom {
|
||||||
// space both
|
// space both
|
||||||
let half = newSpace / 2
|
let half = newSpace / 2
|
||||||
topViewBottomConstraint?.constant = half
|
headerView?.bottomConstraint?.constant = half
|
||||||
bottomViewTopConstraint?.constant = half
|
footerView?.topConstraint?.constant = half
|
||||||
showHeader(width)
|
collectionView?.invalidateIntrinsicContentSize()
|
||||||
showFooter(width)
|
|
||||||
} 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 (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.
|
||||||
if bottomViewOutsideOfScrollArea {
|
headerView?.bottomConstraint?.constant = newSpace
|
||||||
topViewBottomConstraint?.constant = newSpace / 2
|
collectionView?.invalidateIntrinsicContentSize()
|
||||||
} else {
|
|
||||||
topViewBottomConstraint?.constant = newSpace
|
|
||||||
}
|
|
||||||
showHeader(width)
|
|
||||||
} else if fillBottom {
|
} else if fillBottom {
|
||||||
// Only bottom is spaced.
|
// Only bottom is spaced.
|
||||||
bottomViewTopConstraint?.constant = newSpace
|
print("newSpace \(newSpace)")
|
||||||
showFooter(width)
|
footerView?.topConstraint?.constant = newSpace
|
||||||
|
collectionView?.invalidateIntrinsicContentSize()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,113 +128,99 @@ import Foundation
|
|||||||
//MARK: - Header Footer
|
//MARK: - Header Footer
|
||||||
/// Gets the top view and adds it to a spacing view, headerView, and then calls showHeader.
|
/// Gets the top view and adds it to a spacing view, headerView, and then calls showHeader.
|
||||||
open func createViewForTableHeader() {
|
open func createViewForTableHeader() {
|
||||||
let topView = viewForTop()
|
guard let topView = viewForTop() else {
|
||||||
|
self.topView = nil
|
||||||
|
self.headerView = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
self.topView = topView
|
self.topView = topView
|
||||||
|
|
||||||
let headerView = MVMCoreUICommonViewsUtility.commonView()
|
|
||||||
headerView.addSubview(topView)
|
|
||||||
topView.topAnchor.constraint(equalTo: headerView.topAnchor).isActive = true
|
|
||||||
topView.leftAnchor.constraint(equalTo: headerView.leftAnchor).isActive = true
|
|
||||||
headerView.rightAnchor.constraint(equalTo: topView.rightAnchor).isActive = true
|
|
||||||
topViewBottomConstraint = headerView.bottomAnchor.constraint(equalTo: topView.bottomAnchor, constant: spaceBelowTopView() ?? 0)
|
|
||||||
topViewBottomConstraint?.isActive = true
|
|
||||||
self.headerView = headerView
|
|
||||||
showHeader(nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the bottom view and adds it to a spacing view, footerView, and then calls showFooter.
|
/// Gets the bottom view and adds it to a spacing view, footerView, and then calls showFooter.
|
||||||
open func createViewForTableFooter() {
|
open func createViewForTableFooter() {
|
||||||
let bottomView = viewForBottom()
|
guard let bottomView = viewForBottom() else {
|
||||||
|
self.bottomView = nil
|
||||||
|
self.footerView = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
self.bottomView = bottomView
|
self.bottomView = bottomView
|
||||||
|
|
||||||
let footerView = MVMCoreUICommonViewsUtility.commonView()
|
|
||||||
footerView.addSubview(bottomView)
|
|
||||||
bottomViewTopConstraint = bottomView.topAnchor.constraint(equalTo: footerView.topAnchor, constant: spaceAboveBottomView() ?? 0)
|
|
||||||
bottomViewTopConstraint?.isActive = true
|
|
||||||
bottomView.leftAnchor.constraint(equalTo: footerView.leftAnchor).isActive = true
|
|
||||||
footerView.rightAnchor.constraint(equalTo: bottomView.rightAnchor).isActive = true
|
|
||||||
footerView.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor).isActive = true
|
|
||||||
self.footerView = footerView
|
|
||||||
showFooter(nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes the current headerView and adds it to the tableHeaderView
|
// /// Takes the current headerView and adds it to the tableHeaderView
|
||||||
func showHeader(_ sizingWidth: CGFloat?) {
|
// func showHeader(_ sizingWidth: CGFloat?) {
|
||||||
headerView?.removeFromSuperview()
|
// headerView?.removeFromSuperview()
|
||||||
tableView?.tableHeaderView = nil
|
// tableView?.tableHeaderView = nil
|
||||||
guard let headerView = headerView else {
|
// guard let headerView = headerView else {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// This extra view is needed because of the wonkiness of apple's table header. Things breaks if using autolayout.
|
// // This extra view is needed because of the wonkiness of apple's table header. Things breaks if using autolayout.
|
||||||
headerView.setNeedsLayout()
|
// headerView.setNeedsLayout()
|
||||||
headerView.layoutIfNeeded()
|
// headerView.layoutIfNeeded()
|
||||||
MVMCoreUIUtility.sizeView(toFit: headerView)
|
// MVMCoreUIUtility.sizeView(toFit: headerView)
|
||||||
let tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: MVMCoreUIUtility.getWidth(), height: headerView.frame.height))
|
// let tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: MVMCoreUIUtility.getWidth(), height: headerView.frame.height))
|
||||||
tableHeaderView.addSubview(headerView)
|
// tableHeaderView.addSubview(headerView)
|
||||||
NSLayoutConstraint.constraintPinSubview(toSuperview: headerView)
|
// NSLayoutConstraint.constraintPinSubview(toSuperview: headerView)
|
||||||
tableView?.tableHeaderView = tableHeaderView
|
// tableView?.tableHeaderView = tableHeaderView
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/// Takes the current footerView and adds it to the tableFooterView
|
// /// Takes the current footerView and adds it to the tableFooterView
|
||||||
func showFooter(_ sizingWidth: CGFloat?) {
|
// func showFooter(_ sizingWidth: CGFloat?) {
|
||||||
footerView?.removeFromSuperview()
|
// footerView?.removeFromSuperview()
|
||||||
safeAreaView?.removeFromSuperview()
|
// safeAreaView?.removeFromSuperview()
|
||||||
guard let footerView = footerView, let tableView = tableView else {
|
// guard let footerView = footerView, let tableView = tableView else {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if bottomViewOutsideOfScrollArea {
|
// if bottomViewOutsideOfScrollArea {
|
||||||
// put bottom view outside of scrolling area.
|
// // put bottom view outside of scrolling area.
|
||||||
bottomConstraint?.isActive = false
|
// bottomConstraint?.isActive = false
|
||||||
view.addSubview(footerView)
|
// view.addSubview(footerView)
|
||||||
footerView.topAnchor.constraint(equalTo: tableView.bottomAnchor).isActive = true
|
// footerView.topAnchor.constraint(equalTo: tableView.bottomAnchor).isActive = true
|
||||||
footerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
|
// footerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
|
||||||
view.rightAnchor.constraint(equalTo: footerView.rightAnchor).isActive = true
|
// view.rightAnchor.constraint(equalTo: footerView.rightAnchor).isActive = true
|
||||||
view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true
|
// view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true
|
||||||
safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: view)
|
// safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: view)
|
||||||
safeAreaView?.backgroundColor = bottomView?.backgroundColor
|
// safeAreaView?.backgroundColor = bottomView?.backgroundColor
|
||||||
} else {
|
// } else {
|
||||||
bottomConstraint?.isActive = true
|
// bottomConstraint?.isActive = true
|
||||||
var y: CGFloat?
|
// var y: CGFloat?
|
||||||
if let tableFooterView = tableView.tableFooterView {
|
// if let tableFooterView = tableView.tableFooterView {
|
||||||
// if footer already exists, use the same y location to avoid strange moving animation
|
// // if footer already exists, use the same y location to avoid strange moving animation
|
||||||
y = tableFooterView.frame.minY
|
// y = tableFooterView.frame.minY
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// This extra view is needed because of the wonkiness of apple's table footer. Things breaks if using autolayout.
|
// // This extra view is needed because of the wonkiness of apple's table footer. Things breaks if using autolayout.
|
||||||
MVMCoreUIUtility.sizeView(toFit: footerView)
|
// MVMCoreUIUtility.sizeView(toFit: footerView)
|
||||||
let tableFooterView = UIView(frame: CGRect(x: 0, y: y ?? 0, width: MVMCoreUIUtility.getWidth(), height: footerView.frame.height))
|
// let tableFooterView = UIView(frame: CGRect(x: 0, y: y ?? 0, width: MVMCoreUIUtility.getWidth(), height: footerView.frame.height))
|
||||||
tableFooterView.addSubview(footerView)
|
// tableFooterView.addSubview(footerView)
|
||||||
NSLayoutConstraint.constraintPinSubview(toSuperview: footerView)
|
// NSLayoutConstraint.constraintPinSubview(toSuperview: footerView)
|
||||||
tableView.tableFooterView = tableFooterView
|
// tableView.tableFooterView = tableFooterView
|
||||||
}
|
// }
|
||||||
}*/
|
// }
|
||||||
|
|
||||||
//MARK: - Functions to subclass
|
//MARK: - Functions to subclass
|
||||||
/// Subclass for a top view.
|
/// Subclass for a top view.
|
||||||
open func viewForTop() -> UIView {
|
open func viewForTop() -> UIView? {
|
||||||
let view = MVMCoreUICommonViewsUtility.commonView()
|
return nil
|
||||||
// Small height is needed to stop apple from adding padding for grouped tables when no header.
|
|
||||||
view.heightAnchor.constraint(equalToConstant: 1).isActive = true
|
|
||||||
return view
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Subclass for a bottom view.
|
/// Subclass for a bottom view.
|
||||||
open func viewForBottom() -> UIView {
|
open func viewForBottom() -> UIView? {
|
||||||
// Default spacing is standard when no buttons.
|
return nil
|
||||||
let view = MVMCoreUICommonViewsUtility.commonView()
|
}
|
||||||
view.heightAnchor.constraint(equalToConstant: PaddingDefaultVerticalSpacing).isActive = true
|
|
||||||
return view
|
open func createCollectionViewLayout() -> UICollectionViewLayout {
|
||||||
|
let layout = UICollectionViewFlowLayout()
|
||||||
|
layout.scrollDirection = .vertical
|
||||||
|
layout.minimumLineSpacing = 0
|
||||||
|
layout.minimumInteritemSpacing = 0
|
||||||
|
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
|
||||||
|
return layout
|
||||||
}
|
}
|
||||||
|
|
||||||
open func createCollectionView() -> UICollectionView {
|
open func createCollectionView() -> UICollectionView {
|
||||||
let layout = UICollectionViewFlowLayout()
|
let collection = UICollectionView(frame: .zero, collectionViewLayout: createCollectionViewLayout())
|
||||||
layout.scrollDirection = .vertical
|
|
||||||
layout.minimumLineSpacing = 1
|
|
||||||
layout.minimumInteritemSpacing = 0
|
|
||||||
|
|
||||||
let collection = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
|
||||||
collection.translatesAutoresizingMaskIntoConstraints = false
|
collection.translatesAutoresizingMaskIntoConstraints = false
|
||||||
collection.dataSource = self
|
collection.dataSource = self
|
||||||
collection.delegate = self
|
collection.delegate = self
|
||||||
@ -259,19 +238,20 @@ import Foundation
|
|||||||
|
|
||||||
//MARK: - Collection
|
//MARK: - Collection
|
||||||
|
|
||||||
public func registerCells() {
|
open func registerCells() {
|
||||||
collectionView?.register(MoleculeCollectionViewCell.self, forCellWithReuseIdentifier: "collectionItem")
|
collectionView?.register(ContainerCollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: headerID)
|
||||||
|
collectionView?.register(ContainerCollectionReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: footerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
return 2
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
public func numberOfSections(in collectionView: UICollectionView) -> Int {
|
open func numberOfSections(in collectionView: UICollectionView) -> Int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionItem", for: indexPath) as! MoleculeCollectionViewCell
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionItem", for: indexPath) as! MoleculeCollectionViewCell
|
||||||
let labelModel = LabelModel(text: "hello")
|
let labelModel = LabelModel(text: "hello")
|
||||||
let model = MoleculeCollectionItemModel(with: labelModel)
|
let model = MoleculeCollectionItemModel(with: labelModel)
|
||||||
@ -279,11 +259,42 @@ import Foundation
|
|||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
|
open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
|
||||||
return CGSize(width: 200, height: 200)
|
guard let view = headerView ?? topView,
|
||||||
|
section == 0 else { return .zero }
|
||||||
|
|
||||||
|
// Use this view to calculate the optimal size based on the collection view's width
|
||||||
|
return view.systemLayoutSizeFitting(CGSize(width: collectionView.frame.width, height: UIView.layoutFittingExpandedSize.height),
|
||||||
|
withHorizontalFittingPriority: .required, // Width is fixed
|
||||||
|
verticalFittingPriority: .fittingSizeLevel) // Height can be as large as needed
|
||||||
}
|
}
|
||||||
|
|
||||||
open func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
|
open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
|
||||||
(cell as? CarouselItem)?.setPeaking(false, animated: false)
|
guard let view = footerView ?? bottomView,
|
||||||
|
section == numberOfSections(in: collectionView) - 1 else { return .zero }
|
||||||
|
|
||||||
|
// Use this view to calculate the optimal size based on the collection view's width
|
||||||
|
let size = view.systemLayoutSizeFitting(CGSize(width: collectionView.frame.width, height: UIView.layoutFittingExpandedSize.height),
|
||||||
|
withHorizontalFittingPriority: .required, // Width is fixed
|
||||||
|
verticalFittingPriority: .fittingSizeLevel) // Height can be as large as needed
|
||||||
|
print("SIZEEE \(size.height) \(String(describing: footerView?.topConstraint?.constant))")
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
headerView.addAndContain(view: topView!)
|
||||||
|
headerView.bottomConstraint?.constant = spaceBelowTopView() ?? 0
|
||||||
|
self.headerView = headerView
|
||||||
|
return headerView
|
||||||
|
}
|
||||||
|
fatalError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -164,7 +164,7 @@
|
|||||||
CGFloat topInset = scrollview.contentInset.top;
|
CGFloat topInset = scrollview.contentInset.top;
|
||||||
CGFloat bottomInset = scrollview.contentInset.bottom;
|
CGFloat bottomInset = scrollview.contentInset.bottom;
|
||||||
|
|
||||||
if (scrollview.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) {
|
if (scrollview.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic || scrollview.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAlways) {
|
||||||
topInset = scrollview.adjustedContentInset.top;
|
topInset = scrollview.adjustedContentInset.top;
|
||||||
bottomInset = scrollview.adjustedContentInset.bottom;
|
bottomInset = scrollview.adjustedContentInset.bottom;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user