// // Carousel.swift // MVMCoreUI // // Created by Scott Pfeil on 7/2/19. // Copyright © 2019 Verizon Wireless. All rights reserved. // import UIKit open class Carousel: ViewConstrainingView { let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) var currentIndex = 1 var numberOfCards = 0 var molecules: [[AnyHashable : Any]]? var collectionViewHeight: NSLayoutConstraint? open override func setupView() { super.setupView() guard collectionView.superview == nil else { return } collectionView.translatesAutoresizingMaskIntoConstraints = false collectionView.dataSource = self collectionView.delegate = self collectionView.showsHorizontalScrollIndicator = false collectionView.backgroundColor = .clear addSubview(collectionView) pinView(toSuperView: collectionView) collectionViewHeight = collectionView.heightAnchor.constraint(equalToConstant: 300) collectionViewHeight?.isActive = true } /// Registers the cells with the collection view func registerCells(with json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) { if let molecules = json?.optionalArrayForKey(KeyMolecules) as? [[AnyHashable: Any]] { for molecule in molecules { if let info = getMoleculeInfo(with: molecule, delegateObject: delegateObject) { collectionView.register(info.class, forCellWithReuseIdentifier: info.identifier) } } } } /// Updates the layout being used func setupLayout(with json:[AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .horizontal layout.minimumLineSpacing = json?["spacing"] as? CGFloat ?? 0 layout.minimumInteritemSpacing = 0 //layout.itemSize = CGSize(width: 300, height: 200) collectionView.collectionViewLayout = layout } func prepareMolecules(_ json: [AnyHashable : Any]?) { guard let newMolecules = json?.optionalArrayForKey(KeyMolecules) as? [[AnyHashable: Any]] else { numberOfCards = 0 molecules = nil return } numberOfCards = newMolecules.count molecules = newMolecules if json?.boolForKey("loop") ?? false && newMolecules.count > 2 { // Sets up the row data with a buffer cell on each side (for illusion of endless scroll... also has one more buffer cell on right since we can peek that cell). molecules?.insert(newMolecules.last!, at: 0) molecules?.append(newMolecules.first!) molecules?.append(newMolecules[1]) } } open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) collectionViewHeight?.constant = json?.optionalCGFloatForKey("height") ?? 300 registerCells(with: json, delegateObject: delegateObject) setupLayout(with: json, delegateObject: delegateObject) prepareMolecules(json) collectionView.reloadData() // Go to starting cell. layoutIfNeeded is needed otherwise cellForItem returns nil for peaking logic. collectionView.scrollToItem(at: IndexPath(row: currentIndex, section: 0), at: .left, animated: false) collectionView.layoutIfNeeded() } open override func setAsMolecule() { super.setAsMolecule() updateViewHorizontalDefaults = false } // MARK: - Convenience /// Returns the (identifier, class) of the molecule for the given map. func getMoleculeInfo(with molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> (identifier: String, class: AnyClass, molecule: [AnyHashable: Any])? { guard let molecule = molecule, let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: molecule), let moleculeName = moleculeClass.name?(forReuse: molecule, delegateObject: delegateObject) ?? molecule.optionalStringForKey(KeyMoleculeName) else { return nil } return (moleculeName, moleculeClass, molecule) } } extension Carousel: UICollectionViewDelegate { } extension Carousel: UICollectionViewDataSource { public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return molecules?.count ?? 0 } public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let molecule = molecules?[indexPath.row], let moleculeInfo = getMoleculeInfo(with: molecule, delegateObject: nil) else { return UICollectionViewCell() } let cell = collectionView.dequeueReusableCell(withReuseIdentifier: moleculeInfo.identifier, for: indexPath) if let protocolCell = cell as? MVMCoreUIMoleculeViewProtocol { protocolCell.reset?() protocolCell.setWithJSON(moleculeInfo.molecule, delegateObject: nil, additionalData: nil) protocolCell.updateView(collectionView.bounds.width) } return cell } }