Add action to collection cells

make paging configurable for carousel.
add insets to carousel
use button helper function for collection and table cell.
This commit is contained in:
Pfeil, Scott Robert 2020-06-04 11:46:37 -04:00
parent ce5b5a77ca
commit 57d7a5f7cc
10 changed files with 182 additions and 121 deletions

View File

@ -33,7 +33,7 @@ open class CarouselIndicatorModel: CarouselPagingModelProtocol, MoleculeModelPro
public var disabledIndicatorColor: Color = Color(uiColor: .mvmCoolGray3) public var disabledIndicatorColor: Color = Color(uiColor: .mvmCoolGray3)
public var indicatorColor: Color = Color(uiColor: .mvmBlack) public var indicatorColor: Color = Color(uiColor: .mvmBlack)
public var indicatorColor_inverted: Color = Color(uiColor: .mvmWhite) public var indicatorColor_inverted: Color = Color(uiColor: .mvmWhite)
public var position: Float? public var position: CGFloat?
/// Allows sendActions() to trigger even if index is already at min/max index. /// Allows sendActions() to trigger even if index is already at min/max index.
public var alwaysSendAction = false public var alwaysSendAction = false
@ -79,7 +79,7 @@ open class CarouselIndicatorModel: CarouselPagingModelProtocol, MoleculeModelPro
self.inverted = inverted self.inverted = inverted
} }
if let position = try typeContainer.decodeIfPresent(Float.self, forKey: .position) { if let position = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .position) {
self.position = position self.position = position
} }

View File

@ -14,6 +14,12 @@ import Foundation
return "collectionItem" return "collectionItem"
} }
public var action: ActionModelProtocol?
private enum CodingKeys: String, CodingKey {
case action
}
/// Defaults to set /// Defaults to set
public override func setDefaults() { public override func setDefaults() {
if useHorizontalMargins == nil { if useHorizontalMargins == nil {
@ -35,10 +41,14 @@ import Foundation
} }
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
action = try typeContainer.decodeModelIfPresent(codingKey: .action)
try super.init(from: decoder) try super.init(from: decoder)
} }
public override func encode(to encoder: Encoder) throws { public override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeModelIfPresent(action, forKey: .action)
try super.encode(to: encoder) try super.encode(to: encoder)
} }
} }

View File

@ -19,7 +19,13 @@ public protocol CarouselPageControlProtocol {
open class Carousel: View { open class Carousel: View {
public let collectionView = CollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) public let collectionView: CollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
return CollectionView(frame: .zero, collectionViewLayout: layout)
}()
/// The current index of the collection view. Includes dummy cells when looping. /// The current index of the collection view. Includes dummy cells when looping.
public var currentIndex = 0 public var currentIndex = 0
@ -36,13 +42,13 @@ open class Carousel: View {
open var numberOfPages = 0 open var numberOfPages = 0
/// The models for the molecules. /// The models for the molecules.
var molecules: [MoleculeModelProtocol & CarouselItemModelProtocol]? public var molecules: [MoleculeModelProtocol & CarouselItemModelProtocol]?
/// The horizontal alignment of the cell in the collection view. Only noticeable if the itemWidthPercent is less than 100%. /// The horizontal alignment of the cell in the collection view. Only noticeable if the itemWidthPercent is less than 100%.
public var itemAlignment = UICollectionView.ScrollPosition.left public var itemAlignment = UICollectionView.ScrollPosition.left
/// From 0-1. The item width as a percent of the carousel width. /// From 0-1. The item width as a percent of the carousel width.
public var itemWidthPercent: Float = 1 public var itemWidthPercent: CGFloat = 1
/// The height of the carousel. Default is 300. /// The height of the carousel. Default is 300.
public var collectionViewHeight: NSLayoutConstraint? public var collectionViewHeight: NSLayoutConstraint?
@ -51,7 +57,7 @@ open class Carousel: View {
public var pagingView: (UIView & CarouselPageControlProtocol)? public var pagingView: (UIView & CarouselPageControlProtocol)?
/// If the carousel should loop after scrolling past the first and final cells. /// If the carousel should loop after scrolling past the first and final cells.
var loop = false public var loop = false
private var dragging = false private var dragging = false
@ -81,6 +87,8 @@ open class Carousel: View {
showPeaking(false) showPeaking(false)
// Go to current cell. layoutIfNeeded is needed otherwise cellForItem returns nil for peaking logic. The dispatch is a sad way to ensure the collection view is ready to be scrolled. // Go to current cell. layoutIfNeeded is needed otherwise cellForItem returns nil for peaking logic. The dispatch is a sad way to ensure the collection view is ready to be scrolled.
guard let model = model as? CarouselModel,
(model.paging == true || model.loop == true) else { return }
DispatchQueue.main.async { DispatchQueue.main.async {
self.collectionView.scrollToItem(at: IndexPath(row: self.currentIndex, section: 0), at: self.itemAlignment, animated: false) self.collectionView.scrollToItem(at: IndexPath(row: self.currentIndex, section: 0), at: self.itemAlignment, animated: false)
self.collectionView.layoutIfNeeded() self.collectionView.layoutIfNeeded()
@ -98,15 +106,23 @@ open class Carousel: View {
collectionView.delegate = self collectionView.delegate = self
addSubview(collectionView) addSubview(collectionView)
bottomPin = NSLayoutConstraint.constraintPinSubview(toSuperview: collectionView)?[ConstraintBot] as? NSLayoutConstraint bottomPin = NSLayoutConstraint.constraintPinSubview(toSuperview: collectionView)?[ConstraintBot] as? NSLayoutConstraint
collectionViewHeight = collectionView.heightAnchor.constraint(equalToConstant: 300) collectionViewHeight = collectionView.heightAnchor.constraint(equalToConstant: 300)
collectionViewHeight?.isActive = false collectionViewHeight?.isActive = true
} }
open override func updateView(_ size: CGFloat) { open override func updateView(_ size: CGFloat) {
super.updateView(size) super.updateView(size)
self.size = size self.size = size
// Set insets for the carousel.
var inset = UIEdgeInsets.zero
let carouselModel = model as? CarouselModel
if carouselModel?.useHorizontalMargins ?? false {
inset.left = carouselModel?.leftPadding ?? Padding.Component.horizontalPaddingForSize(size)
inset.right = carouselModel?.rightPadding ?? Padding.Component.horizontalPaddingForSize(size)
}
(collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.sectionInset = inset
// Update cells and re-layout. // Update cells and re-layout.
for cell in collectionView.visibleCells { for cell in collectionView.visibleCells {
(cell as? MVMCoreViewProtocol)?.updateView(size) (cell as? MVMCoreViewProtocol)?.updateView(size)
@ -128,20 +144,20 @@ open class Carousel: View {
collectionView.layer.borderColor = backgroundColor?.cgColor collectionView.layer.borderColor = backgroundColor?.cgColor
collectionView.layer.borderWidth = (carouselModel.border ?? false) ? 1 : 0 collectionView.layer.borderWidth = (carouselModel.border ?? false) ? 1 : 0
backgroundColor = .white backgroundColor = .white
(collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.minimumLineSpacing = carouselModel.spacing ?? 0
registerCells(with: carouselModel, delegateObject: delegateObject) itemWidthPercent = carouselModel.itemWidthPercent / 100.0
setupLayout(with: carouselModel)
prepareMolecules(with: carouselModel)
itemWidthPercent = (carouselModel.itemWidthPercent ?? 100) / 100
if let alignment = carouselModel.itemAlignment { if let alignment = carouselModel.itemAlignment {
itemAlignment = alignment itemAlignment = alignment
} }
if let height = carouselModel.height { if let height = carouselModel.height {
collectionViewHeight?.constant = CGFloat(height) collectionViewHeight?.constant = height
collectionViewHeight?.isActive = true
} }
registerCells(with: carouselModel, delegateObject: delegateObject)
prepareMolecules(with: carouselModel)
setupPagingMolecule(carouselModel.pagingMolecule, delegateObject: delegateObject) setupPagingMolecule(carouselModel.pagingMolecule, delegateObject: delegateObject)
pageIndex = carouselModel.index pageIndex = carouselModel.index
@ -153,16 +169,6 @@ open class Carousel: View {
// MARK: - JSON Setters // MARK: - JSON Setters
//-------------------------------------------------- //--------------------------------------------------
/// Updates the layout being used
func setupLayout(with carouselModel: CarouselModel?) {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = CGFloat(carouselModel?.spacing ?? 1)
layout.minimumInteritemSpacing = 0
collectionView.collectionViewLayout = layout
}
func prepareMolecules(with carouselModel: CarouselModel?) { func prepareMolecules(with carouselModel: CarouselModel?) {
guard let newMolecules = carouselModel?.molecules else { guard let newMolecules = carouselModel?.molecules else {
numberOfPages = 0 numberOfPages = 0
@ -191,7 +197,7 @@ open class Carousel: View {
pagingView = MoleculeObjectMapping.shared()?.createMolecule(molecule, delegateObject: delegateObject) as? (UIView & CarouselPageControlProtocol) pagingView = MoleculeObjectMapping.shared()?.createMolecule(molecule, delegateObject: delegateObject) as? (UIView & CarouselPageControlProtocol)
} }
addPaging(view: pagingView, position: (CGFloat(molecule?.position ?? 20))) addPaging(view: pagingView, position: molecule?.position ?? 20)
} }
/// Registers the cells with the collection view /// Registers the cells with the collection view
@ -294,7 +300,7 @@ open class Carousel: View {
extension Carousel: UICollectionViewDelegateFlowLayout { extension Carousel: UICollectionViewDelegateFlowLayout {
open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let itemWidth = collectionView.bounds.width * CGFloat(itemWidthPercent) let itemWidth = collectionView.bounds.width * itemWidthPercent
return CGSize(width: itemWidth, height: collectionView.bounds.height) return CGSize(width: itemWidth, height: collectionView.bounds.height)
} }
@ -324,8 +330,15 @@ extension Carousel: UICollectionViewDataSource {
} }
} }
extension Carousel: UICollectionViewDelegate {
open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
(collectionView.cellForItem(at: indexPath) as? CollectionTemplateItemProtocol)?.didSelectCell(at: indexPath, delegateObject: delegateObject, additionalData: nil)
}
}
extension Carousel: UIScrollViewDelegate { extension Carousel: UIScrollViewDelegate {
/// Go to the cell at the specified index.
func goTo(_ index: Int, animated: Bool) { func goTo(_ index: Int, animated: Bool) {
showPeaking(false) showPeaking(false)
@ -339,51 +352,33 @@ extension Carousel: UIScrollViewDelegate {
} }
} }
func handleUserOnBufferCell() { /// Adjusts the current contentOffset if we are going onto buffer cells while looping to help with the endless scrolling appearance.
guard loop else { return } func adjustOffsetForLooping(_ scrollView: UIScrollView) {
let translatedPoint = scrollView.panGestureRecognizer.translation(in: scrollView.superview).x
let lastPageIndex = numberOfPages + 1 if translatedPoint > 0 {
let goToIndex = { (index: Int) in // Moving left, see if we are moving passed the first left buffer card and adjust
self.goTo(index, animated: false) if let threshold = collectionView.layoutAttributesForItem(at: IndexPath(item: 1, section: 0))?.frame.minX,
self.collectionView.layoutIfNeeded() scrollView.contentOffset.x < threshold,
self.pagingView?.currentIndex = self.pageIndex let newOffset = collectionView.layoutAttributesForItem(at: IndexPath(item: numberOfPages + 1, section: 0))?.frame.minX {
} scrollView.contentOffset.x = newOffset
}
if currentIndex < 2 { } else if translatedPoint < 0 {
// If on a "buffer" last row (which is the first index), go to the real last row secretly. layoutIfNeeded is needed otherwise cellForItem returns nil for peaking. // Moving right, see if we are moving passed the first right buffer card and adjust
goToIndex(lastPageIndex) if let threshold = collectionView.layoutAttributesForItem(at: IndexPath(item: numberOfPages + 2, section: 0))?.frame.maxX,
} else if currentIndex > lastPageIndex { scrollView.contentOffset.x + scrollView.bounds.width > threshold,
// If on the "buffer" first row (which is the index after the real last row), go to the real first row secretly. let newEndOffset = collectionView.layoutAttributesForItem(at: IndexPath(item: 2, section: 0))?.frame.maxX {
goToIndex(2) scrollView.contentOffset.x = newEndOffset - scrollView.bounds.width
}
}
func checkForDraggingOutOfBounds(_ scrollView: UIScrollView) {
guard loop, dragging else { return }
// Checks if the user is not paging but attempting to drag endlessly and goes out of bounds. Caps the index.
if let separatorWidth = (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.minimumLineSpacing {
let itemWidth = collectionView.bounds.width * CGFloat(itemWidthPercent)
let index = scrollView.contentOffset.x / (itemWidth + separatorWidth)
let lastCellIndex = collectionView(collectionView, numberOfItemsInSection: 0) - 1
if index < 1 {
currentIndex = 0
updateModelIndex()
} else if index > CGFloat(lastCellIndex - 1) {
currentIndex = lastCellIndex
updateModelIndex()
} }
} }
handleUserOnBufferCell()
} }
open func scrollViewDidScroll(_ scrollView: UIScrollView) { open func scrollViewDidScroll(_ scrollView: UIScrollView) {
// Check if the user is dragging the card even further past the next card. // Adjust for looping
//checkForDraggingOutOfBounds(scrollView) if let model = model as? CarouselModel,
model.loop == true {
adjustOffsetForLooping(scrollView)
}
// Let the pager know our progress if needed. // Let the pager know our progress if needed.
pagingView?.scrollViewDidScroll(collectionView) pagingView?.scrollViewDidScroll(collectionView)
@ -391,6 +386,7 @@ extension Carousel: UIScrollViewDelegate {
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
// Disable peaking when dragging.
dragging = true dragging = true
showPeaking(false) showPeaking(false)
} }
@ -398,32 +394,63 @@ extension Carousel: UIScrollViewDelegate {
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
dragging = false dragging = false
targetContentOffset.pointee = scrollView.contentOffset
// This is for setting up smooth custom paging. (Since UICollectionView only handles paging based on collection view size and not cell size). // This is for setting up smooth custom paging. (Since UICollectionView only handles paging based on collection view size and not cell size). Math requires that we are using UICollectionViewFlowLayout.
guard let separatorWidth = (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.minimumLineSpacing else { return } guard (model as? CarouselModel)?.paging == true,
let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return }
// We switch cards if we pass the velocity threshold or position threshold (currently 50%). let separatorWidth = layout.minimumLineSpacing
let itemWidth = collectionView.bounds.width * CGFloat(itemWidthPercent) let itemWidth = collectionView.bounds.width * itemWidthPercent
var cellToSwipeTo = Int(scrollView.contentOffset.x / (itemWidth + separatorWidth) + 0.5) let width = itemWidth + separatorWidth
let lastCellIndex = collectionView(collectionView, numberOfItemsInSection: 0) - 1
// Adjusts the offset for the contentInset. Adds imaginary half separator to the left of the first card, which is necessary for determining the percent of a given card we are currently at.
let adjustedOffset = scrollView.contentOffset.x - layout.sectionInset.left + (separatorWidth / 2)
// Calculates the offset per card depending on the alignment.
var offsetByCard: CGFloat
switch itemAlignment {
case .right:
offsetByCard = ((adjustedOffset + scrollView.bounds.width) / width) - 1
case .centeredHorizontally:
offsetByCard = ((adjustedOffset + (scrollView.bounds.width / 2)) / width) - 0.5
default:
offsetByCard = adjustedOffset / width
}
// Adjust card for velocity impact.
let velocityThreshold: CGFloat = 1.1 let velocityThreshold: CGFloat = 1.1
var cellToSwipeTo: Int
if velocity.x > velocityThreshold { if velocity.x > velocityThreshold {
cellToSwipeTo = currentIndex + 1 cellToSwipeTo = Int(ceil(offsetByCard))
} else if velocity.x < -velocityThreshold { } else if velocity.x < -velocityThreshold {
cellToSwipeTo = currentIndex - 1 cellToSwipeTo = Int(floor(offsetByCard))
} else {
cellToSwipeTo = Int(round(offsetByCard))
}
// If we are swiping to a buffer cell, change to real cell before beginning animation so we don't go out of bounds.
if cellToSwipeTo < 2 {
let newOffset = scrollView.contentOffset.x + (width * CGFloat(numberOfPages))
scrollView.contentOffset.x = newOffset
targetContentOffset.pointee.x = newOffset
cellToSwipeTo = cellToSwipeTo + numberOfPages
} else if cellToSwipeTo > numberOfPages + 1 {
let newOffset = scrollView.contentOffset.x - (width * CGFloat(numberOfPages))
scrollView.contentOffset.x = newOffset
targetContentOffset.pointee.x = newOffset
cellToSwipeTo = cellToSwipeTo - numberOfPages
} else {
targetContentOffset.pointee = scrollView.contentOffset
} }
// Cap the index. // Cap the index.
let lastCellIndex = collectionView(collectionView, numberOfItemsInSection: 0) - 1
goTo(min(max(cellToSwipeTo, 0), lastCellIndex), animated: true) goTo(min(max(cellToSwipeTo, 0), lastCellIndex), animated: true)
} }
// To give the illusion of endless scrolling. Since we are always calling scrollToItem we can assume finished paging in here. // To give the illusion of endless scrolling. Since we are always calling scrollToItem we can assume finished paging in here.
public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
// Cycle to other end if on buffer cell. // Cycle to other end if on buffer cell.
handleUserOnBufferCell()
pagingView?.currentIndex = pageIndex pagingView?.currentIndex = pageIndex
showPeaking(true) showPeaking(true)
} }

View File

@ -21,13 +21,17 @@ import UIKit
public var backgroundColor: Color? public var backgroundColor: Color?
public var molecules: [MoleculeModelProtocol & CarouselItemModelProtocol] public var molecules: [MoleculeModelProtocol & CarouselItemModelProtocol]
public var index: Int = 0 public var index: Int = 0
public var spacing: Float? public var spacing: CGFloat?
public var border: Bool? public var border: Bool?
public var loop: Bool? public var loop: Bool?
public var height: Float? public var height: CGFloat?
public var itemWidthPercent: Float? @Percent public var itemWidthPercent = 100
public var itemAlignment: UICollectionView.ScrollPosition? public var itemAlignment: UICollectionView.ScrollPosition?
public var pagingMolecule: (CarouselPagingModelProtocol & MoleculeModelProtocol)? public var pagingMolecule: (CarouselPagingModelProtocol & MoleculeModelProtocol)?
public var paging: Bool = true
public var useHorizontalMargins: Bool?
public var leftPadding: CGFloat?
public var rightPadding: CGFloat?
public init(molecules: [MoleculeModelProtocol & CarouselItemModelProtocol]) { public init(molecules: [MoleculeModelProtocol & CarouselItemModelProtocol]) {
self.molecules = molecules self.molecules = molecules
@ -49,6 +53,10 @@ import UIKit
case itemWidthPercent case itemWidthPercent
case itemAlignment case itemAlignment
case pagingMolecule case pagingMolecule
case paging
case useHorizontalMargins
case leftPadding
case rightPadding
} }
//-------------------------------------------------- //--------------------------------------------------
@ -60,13 +68,21 @@ import UIKit
molecules = try typeContainer.decodeModels(codingKey: .molecules) molecules = try typeContainer.decodeModels(codingKey: .molecules)
index = try typeContainer.decodeIfPresent(Int.self, forKey: .index) ?? 0 index = try typeContainer.decodeIfPresent(Int.self, forKey: .index) ?? 0
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
spacing = try typeContainer.decodeIfPresent(Float.self, forKey: .spacing) spacing = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .spacing)
border = try typeContainer.decodeIfPresent(Bool.self, forKey: .border) border = try typeContainer.decodeIfPresent(Bool.self, forKey: .border)
loop = try typeContainer.decodeIfPresent(Bool.self, forKey: .loop) loop = try typeContainer.decodeIfPresent(Bool.self, forKey: .loop)
height = try typeContainer.decodeIfPresent(Float.self, forKey: .height) height = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .height)
itemWidthPercent = try typeContainer.decodeIfPresent(Float.self, forKey: .itemWidthPercent) if let itemWidthPercent = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .itemWidthPercent) {
self.itemWidthPercent = itemWidthPercent
}
itemAlignment = try typeContainer.decodeIfPresent(UICollectionView.ScrollPosition.self, forKey: .itemAlignment) itemAlignment = try typeContainer.decodeIfPresent(UICollectionView.ScrollPosition.self, forKey: .itemAlignment)
pagingMolecule = try typeContainer.decodeModelIfPresent(codingKey: .pagingMolecule) pagingMolecule = try typeContainer.decodeModelIfPresent(codingKey: .pagingMolecule)
if let paging = try typeContainer.decodeIfPresent(Bool.self, forKey: .paging) {
self.paging = paging
}
useHorizontalMargins = try typeContainer.decodeIfPresent(Bool.self, forKey: .useHorizontalMargins)
leftPadding = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .leftPadding)
rightPadding = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .rightPadding)
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
@ -74,12 +90,16 @@ import UIKit
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeModels(molecules, forKey: .molecules) try container.encodeModels(molecules, forKey: .molecules)
try container.encode(spacing, forKey: .spacing) try container.encodeIfPresent(spacing, forKey: .spacing)
try container.encode(border, forKey: .border) try container.encodeIfPresent(border, forKey: .border)
try container.encode(loop, forKey: .loop) try container.encodeIfPresent(loop, forKey: .loop)
try container.encode(height, forKey: .height) try container.encodeIfPresent(height, forKey: .height)
try container.encode(itemWidthPercent, forKey: .itemWidthPercent) try container.encode(itemWidthPercent, forKey: .itemWidthPercent)
try container.encode(itemAlignment, forKey: .itemAlignment) try container.encodeIfPresent(itemAlignment, forKey: .itemAlignment)
try container.encodeModelIfPresent(pagingMolecule, forKey: .pagingMolecule) try container.encodeModelIfPresent(pagingMolecule, forKey: .pagingMolecule)
try container.encode(paging, forKey: .paging)
try container.encodeIfPresent(useHorizontalMargins, forKey: .useHorizontalMargins)
try container.encodeIfPresent(leftPadding, forKey: .leftPadding)
try container.encodeIfPresent(rightPadding, forKey: .rightPadding)
} }
} }

View File

@ -10,5 +10,5 @@ import Foundation
public protocol CarouselPagingModelProtocol { public protocol CarouselPagingModelProtocol {
var position: Float? { get } var position: CGFloat? { get }
} }

View File

@ -131,7 +131,7 @@ import Foundation
return cell return cell
} }
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { public override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
(collectionView.cellForItem(at: indexPath) as? CollectionTemplateItemProtocol)?.didSelectCell(at: indexPath, delegateObject: delegateObjectIVar, additionalData: nil) (collectionView.cellForItem(at: indexPath) as? CollectionTemplateItemProtocol)?.didSelectCell(at: indexPath, delegateObject: delegateObjectIVar, additionalData: nil)
} }

View File

@ -10,7 +10,7 @@ import Foundation
/// A base collection view cell with basic mvm functionality. /// A base collection view cell with basic mvm functionality.
open class CollectionViewCell: UICollectionViewCell, MoleculeViewProtocol, MVMCoreViewProtocol, CollectionTemplateItemProtocol { open class CollectionViewCell: UICollectionViewCell, MoleculeViewProtocol, MVMCoreViewProtocol, CollectionTemplateItemProtocol, MFButtonProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
@ -25,10 +25,6 @@ open class CollectionViewCell: UICollectionViewCell, MoleculeViewProtocol, MVMCo
private var initialSetupPerformed = false private var initialSetupPerformed = false
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
// MARK: - Inits // MARK: - Inits
public override init(frame: CGRect) { public override init(frame: CGRect) {
super.init(frame: .zero) super.init(frame: .zero)
@ -47,10 +43,6 @@ open class CollectionViewCell: UICollectionViewCell, MoleculeViewProtocol, MVMCo
} }
} }
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
// MARK: - MVMCoreViewProtocol // MARK: - MVMCoreViewProtocol
open func setupView() { open func setupView() {
isAccessibilityElement = false isAccessibilityElement = false
@ -68,16 +60,6 @@ open class CollectionViewCell: UICollectionViewCell, MoleculeViewProtocol, MVMCo
(molecule as? MVMCoreViewProtocol)?.updateView(size) (molecule as? MVMCoreViewProtocol)?.updateView(size)
} }
open func reset() {
molecule?.reset()
backgroundColor = .mvmWhite
width = nil
}
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
// MARK: - MoleculeViewProtocol // MARK: - MoleculeViewProtocol
open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
guard let model = model as? CollectionItemModelProtocol else { return } guard let model = model as? CollectionItemModelProtocol else { return }
@ -94,6 +76,12 @@ open class CollectionViewCell: UICollectionViewCell, MoleculeViewProtocol, MVMCo
} }
} }
open func reset() {
molecule?.reset()
backgroundColor = .mvmWhite
width = nil
}
/// Convenience function. Adds a molecule to the view. /// Convenience function. Adds a molecule to the view.
open func addMolecule(_ molecule: MoleculeViewProtocol) { open func addMolecule(_ molecule: MoleculeViewProtocol) {
contentView.addSubview(molecule) contentView.addSubview(molecule)
@ -109,6 +97,12 @@ open class CollectionViewCell: UICollectionViewCell, MoleculeViewProtocol, MVMCo
self.width = width self.width = width
} }
// MARK: - Override
public func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
guard let action = model?.action else { return }
Button.performButtonAction(with: action, button: self, delegateObject: delegateObject, additionalData: additionalData)
}
// Column logic, set width. // Column logic, set width.
override open func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes { override open func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
let autoLayoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes) let autoLayoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes)

View File

@ -9,5 +9,13 @@
import Foundation import Foundation
public protocol CollectionItemModelProtocol { public protocol CollectionItemModelProtocol {
var action: ActionModelProtocol? { get set }
}
// Not a strict requirement.
public extension CollectionItemModelProtocol {
var action: ActionModelProtocol? {
get { return nil }
set { }
}
} }

View File

@ -8,7 +8,7 @@
import UIKit import UIKit
@objcMembers open class TableViewCell: UITableViewCell, MoleculeViewProtocol, MoleculeListCellProtocol, MVMCoreViewProtocol { @objcMembers open class TableViewCell: UITableViewCell, MoleculeViewProtocol, MoleculeListCellProtocol, MVMCoreViewProtocol, MFButtonProtocol {
open var molecule: MoleculeViewProtocol? open var molecule: MoleculeViewProtocol?
open var listItemModel: ListItemModelProtocol? open var listItemModel: ListItemModelProtocol?
@ -267,10 +267,8 @@ import UIKit
} }
public func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { public func didSelectCell(at index: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
//TODO: Use object when handleAction is rewrote to handle action model guard let action = listItemModel?.action else { return }
if let actionMap = self.listItemModel?.action?.toJSON() { Button.performButtonAction(with: action, button: self, delegateObject: delegateObject, additionalData: additionalData)
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
}
} }
public func willDisplay() { public func willDisplay() {

View File

@ -233,4 +233,8 @@ import Foundation
} }
fatalError() fatalError()
} }
open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
(collectionView.cellForItem(at: indexPath) as? CollectionTemplateItemProtocol)?.didSelectCell(at: indexPath, delegateObject: delegateObjectIVar, additionalData: nil)
}
} }