latest carousel state
This commit is contained in:
parent
5558cc8324
commit
faa9c94f90
@ -15,15 +15,6 @@ public protocol IndicatorViewProtocol {
|
|||||||
var isEnabled: Bool { get set }
|
var isEnabled: Bool { get set }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contracts behavior between carousel and its page control.
|
|
||||||
public protocol CarouselPageControlProtocol {
|
|
||||||
typealias PagingTouchBlock = ((CarouselPageControlProtocol)) -> ()
|
|
||||||
var currentIndex: Int { get set }
|
|
||||||
var numberOfPages: Int { get set }
|
|
||||||
var indicatorTouchAction: PagingTouchBlock? { get set }
|
|
||||||
func scrollViewDidScroll(_ collectionView: UICollectionView)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -83,7 +74,7 @@ open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
|||||||
|
|
||||||
/// Set this closure to perform an action when a different indicator was selected.
|
/// Set this closure to perform an action when a different indicator was selected.
|
||||||
/// Passes through oldInde and newIndex, respectively.
|
/// Passes through oldInde and newIndex, respectively.
|
||||||
public var indicatorTouchAction: CarouselIndicator.PagingTouchBlock?
|
public var indicatorTouchAction: ((CarouselPageControlProtocol) -> ())?
|
||||||
|
|
||||||
/// 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
|
||||||
@ -107,7 +98,7 @@ open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
|||||||
indicatorView?.isEnabled = isEnabled
|
indicatorView?.isEnabled = isEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Computed Properties
|
// MARK: - Computed Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -160,7 +151,7 @@ open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
|||||||
_indicatorTintColor = newColor
|
_indicatorTintColor = newColor
|
||||||
|
|
||||||
if isBarIndicator(), let barIndicator = indicatorView as? BarsIndicatorView {
|
if isBarIndicator(), let barIndicator = indicatorView as? BarsIndicatorView {
|
||||||
for (i, barTuple) in barIndicator.barsReference.enumerated() where i != currentIndex {
|
for (i, barTuple) in barIndicator.barReferences.enumerated() where i != currentIndex {
|
||||||
barTuple.view.backgroundColor = newColor
|
barTuple.view.backgroundColor = newColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -177,7 +168,7 @@ open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
|||||||
|
|
||||||
if isBarIndicator() {
|
if isBarIndicator() {
|
||||||
if let barIndicator = indicatorView as? BarsIndicatorView {
|
if let barIndicator = indicatorView as? BarsIndicatorView {
|
||||||
barIndicator.barsReference[currentIndex].view.backgroundColor = newColor
|
barIndicator.barReferences[currentIndex].view.backgroundColor = newColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -265,9 +256,11 @@ open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
|||||||
let touchPoint = tapGesture?.location(in: self)
|
let touchPoint = tapGesture?.location(in: self)
|
||||||
let touchPoint_X = touchPoint?.x ?? 0.0
|
let touchPoint_X = touchPoint?.x ?? 0.0
|
||||||
|
|
||||||
if isEnabled, indicatorType == .bar, let bars = (indicatorView as? BarsIndicatorView)?.barsReference {
|
if isBarIndicator(), let bars = (indicatorView as? BarsIndicatorView)?.barReferences {
|
||||||
currentIndex = bars.firstIndex { $0.0.frame.maxX >= touchPoint_X && $0.0.frame.minX <= touchPoint_X } ?? 0
|
currentIndex = bars.firstIndex { $0.0.frame.maxX >= touchPoint_X && $0.0.frame.minX <= touchPoint_X } ?? 0
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
// Determine which half of the view was touched.
|
||||||
if touchPoint_X > bounds.width / 2 {
|
if touchPoint_X > bounds.width / 2 {
|
||||||
incrementCurrentIndex()
|
incrementCurrentIndex()
|
||||||
} else {
|
} else {
|
||||||
@ -289,7 +282,7 @@ open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func performAction() {
|
public func performAction() {
|
||||||
|
|
||||||
sendActions(for: .valueChanged)
|
sendActions(for: .valueChanged)
|
||||||
indicatorTouchAction?(self)
|
indicatorTouchAction?(self)
|
||||||
}
|
}
|
||||||
@ -311,13 +304,11 @@ open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
|||||||
|
|
||||||
/// Convenience to determine if current view is displaying bars.
|
/// Convenience to determine if current view is displaying bars.
|
||||||
func isBarIndicator() -> Bool {
|
func isBarIndicator() -> Bool {
|
||||||
|
|
||||||
return indicatorType != .bar && numberOfPages > hybridThreshold
|
return indicatorType != .bar && numberOfPages > hybridThreshold
|
||||||
}
|
}
|
||||||
|
|
||||||
public func scrollViewDidScroll(_ collectionView: UICollectionView) {
|
public func scrollViewDidScroll(_ collectionView: UICollectionView) { }
|
||||||
|
|
||||||
}
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - MoleculeViewProtocol
|
// MARK: - MoleculeViewProtocol
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|||||||
@ -15,7 +15,7 @@ public class CarouselIndicatorModel: MoleculeModelProtocol {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public var backgroundColor: Color?
|
public var backgroundColor: Color?
|
||||||
|
|
||||||
public static var identifier: String {
|
public static var identifier: String {
|
||||||
return "carouselIndicator"
|
return "carouselIndicator"
|
||||||
}
|
}
|
||||||
@ -33,8 +33,9 @@ public class CarouselIndicatorModel: MoleculeModelProtocol {
|
|||||||
public var accessibilityHasSlidesInsteadOfPage: Bool? = false
|
public var accessibilityHasSlidesInsteadOfPage: Bool? = false
|
||||||
public var isEnabled: Bool? = false
|
public var isEnabled: Bool? = false
|
||||||
public var disabledIndicatorColor: Color? = Color(uiColor: .mvmCoolGray3)
|
public var disabledIndicatorColor: Color? = Color(uiColor: .mvmCoolGray3)
|
||||||
public var indicatorTintColor: Color? = Color(uiColor: .black)
|
public var indicatorTintColor: Color? = Color(uiColor: .mvmBlack)
|
||||||
public var currentIndicatorColor: Color? = Color(uiColor: .black)
|
public var currentIndicatorColor: Color? = Color(uiColor: .mvmBlack)
|
||||||
|
// public var position: Float?
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Keys
|
// MARK: - Keys
|
||||||
@ -44,8 +45,19 @@ public class CarouselIndicatorModel: MoleculeModelProtocol {
|
|||||||
case moleculeName
|
case moleculeName
|
||||||
case backgroundColor
|
case backgroundColor
|
||||||
case type
|
case type
|
||||||
|
case hybridThreshold
|
||||||
case barsColor
|
case barsColor
|
||||||
case currentBarColor
|
case currentBarColor
|
||||||
|
case currentIndex
|
||||||
|
case numberOfPages
|
||||||
|
case alwaysSendEvent
|
||||||
|
case isAnimated
|
||||||
|
case hidesForSinglePage
|
||||||
|
case accessibilityHasSlidesInsteadOfPage
|
||||||
|
case isEnabled
|
||||||
|
case disabledIndicatorColor
|
||||||
|
case indicatorTintColor
|
||||||
|
case currentIndicatorColor
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -54,18 +66,45 @@ public class CarouselIndicatorModel: MoleculeModelProtocol {
|
|||||||
|
|
||||||
required public init(from decoder: Decoder) throws {
|
required public init(from decoder: Decoder) throws {
|
||||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
moleculeName = try typeContainer.decodeIfPresent(String.self, forKey: .moleculeName)
|
||||||
currentBarColor = try typeContainer.decodeIfPresent(Color.self, forKey: .currentBarColor)
|
currentBarColor = try typeContainer.decodeIfPresent(Color.self, forKey: .currentBarColor)
|
||||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||||
barsColor = try typeContainer.decodeIfPresent(Color.self, forKey: .barsColor)
|
barsColor = try typeContainer.decodeIfPresent(Color.self, forKey: .barsColor)
|
||||||
type = try typeContainer.decodeIfPresent(String.self, forKey: .type) ?? "hybrid"
|
type = try typeContainer.decodeIfPresent(String.self, forKey: .type) ?? "hybrid"
|
||||||
|
hybridThreshold = try typeContainer.decodeIfPresent(Int.self, forKey: .hybridThreshold) ?? 5
|
||||||
|
barsColor = try typeContainer.decodeIfPresent(Color.self, forKey: .barsColor)
|
||||||
|
currentBarColor = try typeContainer.decodeIfPresent(Color.self, forKey: .currentBarColor)
|
||||||
|
currentIndex = try typeContainer.decodeIfPresent(Int.self, forKey: .currentIndex) ?? 0
|
||||||
|
numberOfPages = try typeContainer.decodeIfPresent(Int.self, forKey: .numberOfPages) ?? 0
|
||||||
|
alwaysSendEvent = try typeContainer.decodeIfPresent(Bool.self, forKey: .alwaysSendEvent) ?? false
|
||||||
|
isAnimated = try typeContainer.decodeIfPresent(Bool.self, forKey: .isAnimated) ?? true
|
||||||
|
hidesForSinglePage = try typeContainer.decodeIfPresent(Bool.self, forKey: .hidesForSinglePage) ?? false
|
||||||
|
accessibilityHasSlidesInsteadOfPage = try typeContainer.decodeIfPresent(Bool.self, forKey: .accessibilityHasSlidesInsteadOfPage) ?? false
|
||||||
|
isEnabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .isEnabled) ?? false
|
||||||
|
disabledIndicatorColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledIndicatorColor) ?? Color(uiColor: .mvmCoolGray3)
|
||||||
|
indicatorTintColor = try typeContainer.decodeIfPresent(Color.self, forKey: .indicatorTintColor) ?? Color(uiColor: .mvmBlack)
|
||||||
|
currentIndicatorColor = try typeContainer.decodeIfPresent(Color.self, forKey: .currentIndicatorColor) ?? Color(uiColor: .mvmBlack)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||||
|
try container.encode(moleculeName, forKey: .moleculeName)
|
||||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||||
try container.encodeIfPresent(barsColor, forKey: .barsColor)
|
try container.encodeIfPresent(barsColor, forKey: .barsColor)
|
||||||
try container.encodeIfPresent(currentBarColor, forKey: .currentBarColor)
|
try container.encodeIfPresent(currentBarColor, forKey: .currentBarColor)
|
||||||
try container.encode(moleculeName, forKey: .moleculeName)
|
|
||||||
try container.encodeIfPresent(type, forKey: .type)
|
try container.encodeIfPresent(type, forKey: .type)
|
||||||
|
try container.encodeIfPresent(hybridThreshold, forKey: .hybridThreshold)
|
||||||
|
try container.encodeIfPresent(barsColor, forKey: .barsColor)
|
||||||
|
try container.encodeIfPresent(currentBarColor, forKey: .currentBarColor)
|
||||||
|
try container.encodeIfPresent(currentIndex, forKey: .currentIndex)
|
||||||
|
try container.encodeIfPresent(numberOfPages, forKey: .numberOfPages)
|
||||||
|
try container.encodeIfPresent(alwaysSendEvent, forKey: .alwaysSendEvent)
|
||||||
|
try container.encodeIfPresent(isAnimated, forKey: .isAnimated)
|
||||||
|
try container.encodeIfPresent(hidesForSinglePage, forKey: .hidesForSinglePage)
|
||||||
|
try container.encodeIfPresent(accessibilityHasSlidesInsteadOfPage, forKey: .accessibilityHasSlidesInsteadOfPage)
|
||||||
|
try container.encodeIfPresent(isEnabled, forKey: .isEnabled)
|
||||||
|
try container.encodeIfPresent(disabledIndicatorColor, forKey: .disabledIndicatorColor)
|
||||||
|
try container.encodeIfPresent(indicatorTintColor, forKey: .indicatorTintColor)
|
||||||
|
try container.encodeIfPresent(currentIndicatorColor, forKey: .currentIndicatorColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import UIKit
|
|||||||
|
|
||||||
open class BarsIndicatorView: View, IndicatorViewProtocol {
|
open class BarsIndicatorView: View, IndicatorViewProtocol {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Stored Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
let stackView: StackView = {
|
let stackView: StackView = {
|
||||||
@ -19,40 +19,49 @@ open class BarsIndicatorView: View, IndicatorViewProtocol {
|
|||||||
stackView.axis = .horizontal
|
stackView.axis = .horizontal
|
||||||
stackView.distribution = .equalSpacing
|
stackView.distribution = .equalSpacing
|
||||||
stackView.spacing = 6
|
stackView.spacing = 6
|
||||||
|
stackView.heightAnchor.constraint(greaterThanOrEqualToConstant: BarsIndicatorView.indicatorBarHeight.selected).isActive = true
|
||||||
return stackView
|
return stackView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
public var barsReference: [(view: View, constraint: NSLayoutConstraint)] = []
|
public var barReferences: [(view: View, constraint: NSLayoutConstraint)] = []
|
||||||
|
|
||||||
// Dimensions are based on InVision Design Guidelines.
|
// Dimensions are based on InVision Design Guidelines.
|
||||||
public static let indicatorBarWidth: CGFloat = 24
|
public static let indicatorBarWidth: CGFloat = 24
|
||||||
public static let indicatorBarHeight: (selected: CGFloat, unselected: CGFloat) = (selected: 4, unselected: 1)
|
public static let indicatorBarHeight: (selected: CGFloat, unselected: CGFloat) = (selected: 4, unselected: 1)
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Computed Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
public var parentCarouselIndicator: CarouselIndicator? {
|
||||||
|
return superview as? CarouselIndicator
|
||||||
|
}
|
||||||
|
|
||||||
public var enabledColor: UIColor {
|
public var enabledColor: UIColor {
|
||||||
return (superview as? CarouselIndicator)?.indicatorTintColor ?? .black
|
return parentCarouselIndicator?.indicatorTintColor ?? .mvmBlack
|
||||||
}
|
}
|
||||||
|
|
||||||
public var currentIndexColor: UIColor {
|
public var currentIndexColor: UIColor {
|
||||||
return (superview as? CarouselIndicator)?.currentIndicatorColor ?? .black
|
return parentCarouselIndicator?.currentIndicatorColor ?? .mvmBlack
|
||||||
}
|
}
|
||||||
|
|
||||||
public var disabledColor: UIColor {
|
public var disabledColor: UIColor {
|
||||||
return (superview as? CarouselIndicator)?.disabledIndicatorColor ?? .mvmCoolGray3
|
return parentCarouselIndicator?.disabledIndicatorColor ?? .mvmCoolGray3
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the numberOfPages count from its parent CarouselIndicator.
|
/// Returns the numberOfPages count from its parent CarouselIndicator.
|
||||||
public var numberOfPages: Int? {
|
public var numberOfPages: Int? {
|
||||||
return (superview as? CarouselIndicator)?.numberOfPages
|
return parentCarouselIndicator?.numberOfPages
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the numberOfPages count from its parent CarouselIndicator.
|
/// Returns the numberOfPages count from its parent CarouselIndicator.
|
||||||
public var currentIndex: Int? {
|
public var currentIndex: Int? {
|
||||||
return (superview as? CarouselIndicator)?.currentIndex
|
return parentCarouselIndicator?.currentIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
open var isEnabled: Bool = true {
|
open var isEnabled: Bool = true {
|
||||||
didSet {
|
didSet {
|
||||||
barsReference.forEach { view, heightConstraint in
|
barReferences.forEach { view, heightConstraint in
|
||||||
view.backgroundColor = isEnabled ? enabledColor : disabledColor
|
view.backgroundColor = isEnabled ? enabledColor : disabledColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,7 +91,7 @@ open class BarsIndicatorView: View, IndicatorViewProtocol {
|
|||||||
|
|
||||||
stackView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
|
stackView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
|
||||||
stackView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor).isActive = true
|
stackView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor).isActive = true
|
||||||
trailingAnchor.constraint(lessThanOrEqualTo: stackView.trailingAnchor).isActive = true
|
trailingAnchor.constraint(greaterThanOrEqualTo: stackView.trailingAnchor).isActive = true
|
||||||
|
|
||||||
generateBars()
|
generateBars()
|
||||||
}
|
}
|
||||||
@ -102,7 +111,7 @@ open class BarsIndicatorView: View, IndicatorViewProtocol {
|
|||||||
for i in 0..<numberOfPages {
|
for i in 0..<numberOfPages {
|
||||||
let bar = View()
|
let bar = View()
|
||||||
bar.widthAnchor.constraint(equalToConstant: BarsIndicatorView.indicatorBarWidth).isActive = true
|
bar.widthAnchor.constraint(equalToConstant: BarsIndicatorView.indicatorBarWidth).isActive = true
|
||||||
bar.backgroundColor = enabledColor
|
bar.backgroundColor = isEnabled ? enabledColor : disabledColor
|
||||||
let barHeight = i == currentIndex ? BarsIndicatorView.indicatorBarHeight.selected : BarsIndicatorView.indicatorBarHeight.unselected
|
let barHeight = i == currentIndex ? BarsIndicatorView.indicatorBarHeight.selected : BarsIndicatorView.indicatorBarHeight.unselected
|
||||||
let heightConstraint = bar.heightAnchor.constraint(equalToConstant: barHeight)
|
let heightConstraint = bar.heightAnchor.constraint(equalToConstant: barHeight)
|
||||||
heightConstraint.isActive = true
|
heightConstraint.isActive = true
|
||||||
@ -111,7 +120,7 @@ open class BarsIndicatorView: View, IndicatorViewProtocol {
|
|||||||
bars.append((bar, heightConstraint))
|
bars.append((bar, heightConstraint))
|
||||||
}
|
}
|
||||||
|
|
||||||
barsReference = bars
|
barReferences = bars
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -120,17 +129,17 @@ open class BarsIndicatorView: View, IndicatorViewProtocol {
|
|||||||
|
|
||||||
public override func reset() {
|
public override func reset() {
|
||||||
super.reset()
|
super.reset()
|
||||||
barsReference.forEach { $0.view.removeFromSuperview() }
|
barReferences.forEach { $0.view.removeFromSuperview() }
|
||||||
barsReference = []
|
barReferences = []
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateUI(previousIndex: Int, newIndex: Int, totalCount: Int, isAnimated: Bool) {
|
public func updateUI(previousIndex: Int, newIndex: Int, totalCount: Int, isAnimated: Bool) {
|
||||||
|
|
||||||
let expression = {
|
let expression = {
|
||||||
self.barsReference[previousIndex].view.backgroundColor = self.enabledColor
|
self.barReferences[previousIndex].view.backgroundColor = self.enabledColor
|
||||||
self.barsReference[newIndex].view.backgroundColor = self.currentIndexColor
|
self.barReferences[newIndex].view.backgroundColor = self.currentIndexColor
|
||||||
self.barsReference[previousIndex].constraint.constant = BarsIndicatorView.indicatorBarHeight.unselected
|
self.barReferences[previousIndex].constraint.constant = BarsIndicatorView.indicatorBarHeight.unselected
|
||||||
self.barsReference[newIndex].constraint.constant = BarsIndicatorView.indicatorBarHeight.selected
|
self.barReferences[newIndex].constraint.constant = BarsIndicatorView.indicatorBarHeight.selected
|
||||||
self.layoutIfNeeded()
|
self.layoutIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ open class NumericIndicatorView: View, IndicatorViewProtocol {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
/// Text to display the current count of total pages for viewing.
|
/// Text to display the current count of total pages for viewing.
|
||||||
open var titleLabel: Label = {
|
open var pageCountLabel: Label = {
|
||||||
let label = Label.commonLabelB2(true)
|
let label = Label.commonLabelB2(true)
|
||||||
label.setContentCompressionResistancePriority(.required, for: .vertical)
|
label.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
label.textAlignment = .center
|
label.textAlignment = .center
|
||||||
@ -25,41 +25,43 @@ open class NumericIndicatorView: View, IndicatorViewProtocol {
|
|||||||
let leftArrow: ImageView = {
|
let leftArrow: ImageView = {
|
||||||
let arrow = UIImage(named: "peakingRightArrow")?.withRenderingMode(.alwaysTemplate).withHorizontallyFlippedOrientation()
|
let arrow = UIImage(named: "peakingRightArrow")?.withRenderingMode(.alwaysTemplate).withHorizontallyFlippedOrientation()
|
||||||
let imageView = ImageView(image: arrow)
|
let imageView = ImageView(image: arrow)
|
||||||
imageView.isUserInteractionEnabled = true
|
|
||||||
imageView.tintColor = .mvmBlack
|
imageView.tintColor = .mvmBlack
|
||||||
|
imageView.heightAnchor.constraint(equalToConstant: PaddingTwo).isActive = true
|
||||||
|
imageView.widthAnchor.constraint(equalToConstant: PaddingTwo).isActive = true
|
||||||
return imageView
|
return imageView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
let rightArrow: ImageView = {
|
let rightArrow: ImageView = {
|
||||||
let arrow = UIImage(named: "peakingRightArrow")?.withRenderingMode(.alwaysTemplate)
|
let arrow = UIImage(named: "peakingRightArrow")?.withRenderingMode(.alwaysTemplate)
|
||||||
let imageView = ImageView(image: arrow)
|
let imageView = ImageView(image: arrow)
|
||||||
imageView.isUserInteractionEnabled = true
|
|
||||||
imageView.tintColor = .mvmBlack
|
imageView.tintColor = .mvmBlack
|
||||||
|
imageView.heightAnchor.constraint(equalToConstant: PaddingTwo).isActive = true
|
||||||
|
imageView.widthAnchor.constraint(equalToConstant: PaddingTwo).isActive = true
|
||||||
return imageView
|
return imageView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Computed Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
open var isEnabled: Bool = true {
|
open var isEnabled: Bool = true {
|
||||||
didSet {
|
didSet {
|
||||||
titleLabel.isEnabled = isEnabled
|
pageCountLabel.textColor = isEnabled ? enabledColor : disabledColor
|
||||||
leftArrow.tintColor = isEnabled ? enabledColor : disabledColor
|
leftArrow.tintColor = isEnabled ? enabledColor : disabledColor
|
||||||
rightArrow.tintColor = isEnabled ? enabledColor : disabledColor
|
rightArrow.tintColor = isEnabled ? enabledColor : disabledColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var parentCarouselIndicator: CarouselIndicator? {
|
||||||
|
return superview as? CarouselIndicator
|
||||||
|
}
|
||||||
|
|
||||||
public var enabledColor: UIColor {
|
public var enabledColor: UIColor {
|
||||||
return (superview as? CarouselIndicator)?.indicatorTintColor ?? .black
|
return parentCarouselIndicator?.indicatorTintColor ?? .mvmBlack
|
||||||
}
|
}
|
||||||
|
|
||||||
public var disabledColor: UIColor {
|
public var disabledColor: UIColor {
|
||||||
return (superview as? CarouselIndicator)?.disabledIndicatorColor ?? .mvmCoolGray3
|
return parentCarouselIndicator?.disabledIndicatorColor ?? .mvmCoolGray3
|
||||||
}
|
|
||||||
|
|
||||||
public var parentCarouselIndicator: CarouselIndicator? {
|
|
||||||
return superview as? CarouselIndicator
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the currentIndex from its parent CarouselIndicator.
|
/// Returns the currentIndex from its parent CarouselIndicator.
|
||||||
@ -98,7 +100,7 @@ open class NumericIndicatorView: View, IndicatorViewProtocol {
|
|||||||
|
|
||||||
open override func updateView(_ size: CGFloat) {
|
open override func updateView(_ size: CGFloat) {
|
||||||
super.updateView(size)
|
super.updateView(size)
|
||||||
titleLabel.updateView(size)
|
pageCountLabel.updateView(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -109,29 +111,24 @@ open class NumericIndicatorView: View, IndicatorViewProtocol {
|
|||||||
super.setupView()
|
super.setupView()
|
||||||
|
|
||||||
isUserInteractionEnabled = false
|
isUserInteractionEnabled = false
|
||||||
leftArrow.tintColor = isEnabled ? enabledColor : disabledColor
|
|
||||||
rightArrow.tintColor = isEnabled ? enabledColor : disabledColor
|
|
||||||
|
|
||||||
addSubview(titleLabel)
|
addSubview(pageCountLabel)
|
||||||
titleLabel.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
|
pageCountLabel.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
|
||||||
NSLayoutConstraint.constraintPinSubview(titleLabel, pinTop: true, pinBottom: true, pinLeft: false, pinRight: false)
|
pageCountLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true
|
||||||
|
bottomAnchor.constraint(equalTo: pageCountLabel.bottomAnchor).isActive = true
|
||||||
|
|
||||||
addSubview(leftArrow)
|
addSubview(leftArrow)
|
||||||
NSLayoutConstraint.constraintPinView(leftArrow, heightConstraint: true, heightConstant: PaddingTwo, widthConstraint: true, widthConstant: PaddingTwo)
|
|
||||||
leftArrow.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
|
leftArrow.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
|
||||||
|
|
||||||
addSubview(rightArrow)
|
addSubview(rightArrow)
|
||||||
|
|
||||||
NSLayoutConstraint.constraintPinView(rightArrow, heightConstraint: true, heightConstant: PaddingTwo, widthConstraint: true, widthConstant: PaddingTwo)
|
|
||||||
rightArrow.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
|
rightArrow.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
|
||||||
rightArrow.isUserInteractionEnabled = true
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[leftArrowView]-(padding)-[titleLabel]-(padding)-[rightArrowView]-0-|",
|
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|[leftArrow]-(padding)-[pageCountLabel]-(padding)-[rightArrow]|",
|
||||||
options: .directionLeadingToTrailing,
|
options: .directionLeadingToTrailing,
|
||||||
metrics: ["padding": PaddingOne],
|
metrics: ["padding": PaddingOne],
|
||||||
views: ["leftArrowView": leftArrow,
|
views: ["leftArrow": leftArrow,
|
||||||
"titleLabel": titleLabel,
|
"pageCountLabel": pageCountLabel,
|
||||||
"rightArrowView": rightArrow]))
|
"rightArrow": rightArrow]))
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -143,7 +140,7 @@ open class NumericIndicatorView: View, IndicatorViewProtocol {
|
|||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
self.titleLabel.text = "\(newIndex)/\(totalCount)"
|
self.pageCountLabel.text = "\(newIndex)/\(totalCount)"
|
||||||
self.layoutIfNeeded()
|
self.layoutIfNeeded()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,16 +8,16 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class ImageView: UIImageView, ModelMoleculeViewProtocol {
|
open class ImageView: UIImageView, ModelMoleculeViewProtocol {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
open var json: [AnyHashable: Any]?
|
open var json: [AnyHashable: Any]?
|
||||||
open var model: MoleculeModelProtocol?
|
open var model: MoleculeModelProtocol?
|
||||||
|
|
||||||
private var initialSetupPerformed = false
|
private var initialSetupPerformed = false
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Initialization
|
// MARK: - Initialization
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -31,7 +31,7 @@ class ImageView: UIImageView, ModelMoleculeViewProtocol {
|
|||||||
super.init(image: image)
|
super.init(image: image)
|
||||||
initialSetup()
|
initialSetup()
|
||||||
}
|
}
|
||||||
|
|
||||||
public convenience init() {
|
public convenience init() {
|
||||||
self.init(frame: .zero)
|
self.init(frame: .zero)
|
||||||
}
|
}
|
||||||
@ -41,6 +41,10 @@ class ImageView: UIImageView, ModelMoleculeViewProtocol {
|
|||||||
initialSetup()
|
initialSetup()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Lifecycle
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
public func initialSetup() {
|
public func initialSetup() {
|
||||||
if !initialSetupPerformed {
|
if !initialSetupPerformed {
|
||||||
initialSetupPerformed = true
|
initialSetupPerformed = true
|
||||||
@ -48,12 +52,15 @@ class ImageView: UIImageView, ModelMoleculeViewProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK:- ModelMoleculeViewProtocol
|
//--------------------------------------------------
|
||||||
|
// MARK: - ModelMoleculeViewProtocol
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
open func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
open func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||||
self.model = model
|
self.model = model
|
||||||
if let backgroundColor = model?.backgroundColor {
|
if let backgroundColor = model?.backgroundColor {
|
||||||
self.backgroundColor = backgroundColor.uiColor
|
self.backgroundColor = backgroundColor.uiColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open class func nameForReuse(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
open class func nameForReuse(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
||||||
@ -72,8 +79,8 @@ class ImageView: UIImageView, ModelMoleculeViewProtocol {
|
|||||||
// MARK:- MVMCoreViewProtocol
|
// MARK:- MVMCoreViewProtocol
|
||||||
extension ImageView: MVMCoreViewProtocol {
|
extension ImageView: MVMCoreViewProtocol {
|
||||||
|
|
||||||
open func updateView(_ size: CGFloat) {}
|
open func updateView(_ size: CGFloat) { }
|
||||||
|
|
||||||
/// Will be called only once.
|
/// Will be called only once.
|
||||||
open func setupView() {
|
open func setupView() {
|
||||||
translatesAutoresizingMaskIntoConstraints = false
|
translatesAutoresizingMaskIntoConstraints = false
|
||||||
@ -99,4 +106,3 @@ extension ImageView: MVMCoreUIMoleculeViewProtocol {
|
|||||||
|
|
||||||
open func setAsMolecule() { }
|
open func setAsMolecule() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,17 +9,29 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class MoleculeContainerModel: ContainerModel {
|
public class MoleculeContainerModel: ContainerModel {
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
public var molecule: MoleculeModelProtocol
|
public var molecule: MoleculeModelProtocol
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case molecule
|
case molecule
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Initializer
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
public init(with moleculeModel: MoleculeModelProtocol) {
|
public init(with moleculeModel: MoleculeModelProtocol) {
|
||||||
molecule = moleculeModel
|
molecule = moleculeModel
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Codec
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
required public init(from decoder: Decoder) throws {
|
required public init(from decoder: Decoder) throws {
|
||||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
molecule = try typeContainer.decodeMolecule(codingKey: .molecule)
|
molecule = try typeContainer.decodeMolecule(codingKey: .molecule)
|
||||||
|
|||||||
@ -8,6 +8,15 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
/// Contracts behavior between carousel and its page control.
|
||||||
|
public protocol CarouselPageControlProtocol {
|
||||||
|
var currentIndex: Int { get set }
|
||||||
|
var numberOfPages: Int { get set }
|
||||||
|
var indicatorTouchAction: ((CarouselPageControlProtocol) -> ())? { get set }
|
||||||
|
func scrollViewDidScroll(_ collectionView: UICollectionView)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
open class Carousel: View {
|
open class Carousel: View {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
@ -33,7 +42,7 @@ open class Carousel: View {
|
|||||||
var molecules: [MoleculeModelProtocol]?
|
var molecules: [MoleculeModelProtocol]?
|
||||||
|
|
||||||
/// 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%.
|
||||||
var itemAlignment = UICollectionView.ScrollPosition.left
|
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.
|
||||||
var itemWidthPercent: Float = 1
|
var itemWidthPercent: Float = 1
|
||||||
@ -42,10 +51,11 @@ open class Carousel: View {
|
|||||||
var collectionViewHeight: NSLayoutConstraint?
|
var collectionViewHeight: NSLayoutConstraint?
|
||||||
|
|
||||||
/// The view that we use for paging
|
/// The view that we use for paging
|
||||||
var pagingView: (UIView & MVMCoreUIPagingProtocol)?
|
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
|
var loop = false
|
||||||
|
|
||||||
private var dragging = false
|
private var dragging = false
|
||||||
|
|
||||||
/// For adding pager
|
/// For adding pager
|
||||||
@ -141,9 +151,17 @@ open class Carousel: View {
|
|||||||
if carouselModel?.loop ?? false && newMolecules.count > 2 {
|
if carouselModel?.loop ?? false && newMolecules.count > 2 {
|
||||||
// Sets up the row data with buffer cells on each side (for illusion of endless scroll... also has one more buffer cell on each side in case we can peek that cell).
|
// Sets up the row data with buffer cells on each side (for illusion of endless scroll... also has one more buffer cell on each side in case we can peek that cell).
|
||||||
loop = true
|
loop = true
|
||||||
molecules?.insert(newMolecules.last!, at: 0)
|
|
||||||
|
if let last = newMolecules.last {
|
||||||
|
molecules?.insert(last, at: 0)
|
||||||
|
}
|
||||||
|
|
||||||
molecules?.insert(newMolecules[(newMolecules.count - 2)], at: 0)
|
molecules?.insert(newMolecules[(newMolecules.count - 2)], at: 0)
|
||||||
molecules?.append(newMolecules.first!)
|
|
||||||
|
if let first = newMolecules.first {
|
||||||
|
molecules?.append(first)
|
||||||
|
}
|
||||||
|
|
||||||
molecules?.append(newMolecules[1])
|
molecules?.append(newMolecules[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,9 +171,10 @@ open class Carousel: View {
|
|||||||
/// Sets up the paging molecule
|
/// Sets up the paging molecule
|
||||||
open func setupPagingMolecule(_ molecule: CarouselPagingModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) {
|
open func setupPagingMolecule(_ molecule: CarouselPagingModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
|
||||||
var pagingView: (UIView & MVMCoreUIPagingProtocol)? = nil
|
var pagingView: (UIView & CarouselPageControlProtocol)? = nil
|
||||||
|
|
||||||
if let molecule = molecule {
|
if let molecule = molecule {
|
||||||
pagingView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(molecule, delegateObject, false) as? (UIView & MVMCoreUIPagingProtocol)
|
pagingView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(molecule, delegateObject, false) as? (UIView & CarouselPageControlProtocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
addPaging(view: pagingView, position: (CGFloat(molecule?.position ?? 20)))
|
addPaging(view: pagingView, position: (CGFloat(molecule?.position ?? 20)))
|
||||||
@ -163,6 +182,7 @@ open class Carousel: View {
|
|||||||
|
|
||||||
/// Registers the cells with the collection view
|
/// Registers the cells with the collection view
|
||||||
func registerCells(with carouselModel: CarouselModel, delegateObject: MVMCoreUIDelegateObject?) {
|
func registerCells(with carouselModel: CarouselModel, delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
|
||||||
for molecule in carouselModel.molecules {
|
for molecule in carouselModel.molecules {
|
||||||
if let info = getMoleculeInfo(with: molecule, delegateObject: delegateObject) {
|
if let info = getMoleculeInfo(with: molecule, delegateObject: delegateObject) {
|
||||||
collectionView.register(info.class, forCellWithReuseIdentifier: info.identifier)
|
collectionView.register(info.class, forCellWithReuseIdentifier: info.identifier)
|
||||||
@ -176,6 +196,7 @@ open class Carousel: View {
|
|||||||
|
|
||||||
/// Returns the (identifier, class) of the molecule for the given map.
|
/// Returns the (identifier, class) of the molecule for the given map.
|
||||||
func getMoleculeInfo(with molecule: MoleculeModelProtocol, delegateObject: MVMCoreUIDelegateObject?) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? {
|
func getMoleculeInfo(with molecule: MoleculeModelProtocol, delegateObject: MVMCoreUIDelegateObject?) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? {
|
||||||
|
|
||||||
guard let className = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(molecule),
|
guard let className = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(molecule),
|
||||||
let moleculeName = (className as? ModelMoleculeViewProtocol.Type)?.nameForReuse(molecule, delegateObject) ?? molecule.moleculeName
|
let moleculeName = (className as? ModelMoleculeViewProtocol.Type)?.nameForReuse(molecule, delegateObject) ?? molecule.moleculeName
|
||||||
else { return nil }
|
else { return nil }
|
||||||
@ -189,26 +210,29 @@ open class Carousel: View {
|
|||||||
switch string {
|
switch string {
|
||||||
case "leading":
|
case "leading":
|
||||||
itemAlignment = .left
|
itemAlignment = .left
|
||||||
|
|
||||||
case "trailing":
|
case "trailing":
|
||||||
itemAlignment = .right
|
itemAlignment = .right
|
||||||
|
|
||||||
case "center":
|
case "center":
|
||||||
itemAlignment = .centeredHorizontally
|
itemAlignment = .centeredHorizontally
|
||||||
default: break
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a paging view. Centers it horizontally with the collection view. The position is the vertical distance from the center of the page view to the bottom of the collection view.
|
/// Adds a paging view. Centers it horizontally with the collection view. The position is the vertical distance from the center of the page view to the bottom of the collection view.
|
||||||
open func addPaging(view: (UIView & MVMCoreUIPagingProtocol)?, position: CGFloat) {
|
open func addPaging(view: (UIView & CarouselPageControlProtocol)?, position: CGFloat) {
|
||||||
|
|
||||||
pagingView?.removeFromSuperview()
|
pagingView?.removeFromSuperview()
|
||||||
guard let pagingView = view else {
|
guard var pagingView = view else {
|
||||||
bottomPin?.isActive = false
|
bottomPin?.isActive = false
|
||||||
bottomPin = bottomAnchor.constraint(equalTo: collectionView.bottomAnchor)
|
bottomPin = bottomAnchor.constraint(equalTo: collectionView.bottomAnchor)
|
||||||
bottomPin?.isActive = true
|
bottomPin?.isActive = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pagingView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
addSubview(pagingView)
|
addSubview(pagingView)
|
||||||
pagingView.centerXAnchor.constraint(equalTo: collectionView.centerXAnchor).isActive = true
|
pagingView.centerXAnchor.constraint(equalTo: collectionView.centerXAnchor).isActive = true
|
||||||
collectionView.bottomAnchor.constraint(equalTo: pagingView.centerYAnchor, constant: position).isActive = true
|
collectionView.bottomAnchor.constraint(equalTo: pagingView.centerYAnchor, constant: position).isActive = true
|
||||||
@ -218,16 +242,17 @@ open class Carousel: View {
|
|||||||
bottomPin?.priority = .defaultLow
|
bottomPin?.priority = .defaultLow
|
||||||
bottomPin?.isActive = true
|
bottomPin?.isActive = true
|
||||||
|
|
||||||
pagingView.setNumberOfPages(numberOfPages)
|
pagingView.numberOfPages = numberOfPages
|
||||||
(pagingView as? MVMCoreUIViewConstrainingProtocol)?.alignHorizontal?(.fill)
|
(pagingView as? MVMCoreUIViewConstrainingProtocol)?.alignHorizontal?(.fill)
|
||||||
pagingView.setPagingTouch { [weak self] pager in
|
pagingView.indicatorTouchAction = { [weak self] pager in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
let currentPage = pager.currentPage()
|
let currentPage = pager.currentIndex
|
||||||
self.pageIndex = currentPage
|
self.pageIndex = currentPage
|
||||||
self.goTo(self.currentIndex, animated: !UIAccessibility.isVoiceOverRunning)
|
self.goTo(self.currentIndex, animated: !UIAccessibility.isVoiceOverRunning)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pagingView = pagingView
|
self.pagingView = pagingView
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,6 +261,7 @@ open class Carousel: View {
|
|||||||
if peaking && !UIAccessibility.isVoiceOverRunning {
|
if peaking && !UIAccessibility.isVoiceOverRunning {
|
||||||
// Show overlay and arrow in peaking Cell
|
// Show overlay and arrow in peaking Cell
|
||||||
let visibleItemsPaths = collectionView.indexPathsForVisibleItems.sorted { $0.row < $1.row }
|
let visibleItemsPaths = collectionView.indexPathsForVisibleItems.sorted { $0.row < $1.row }
|
||||||
|
|
||||||
if let firstItem = visibleItemsPaths.first, firstItem.row != currentIndex {
|
if let firstItem = visibleItemsPaths.first, firstItem.row != currentIndex {
|
||||||
(collectionView.cellForItem(at: firstItem) as? MoleculeCollectionViewCell)?.setPeaking(true, animated: true)
|
(collectionView.cellForItem(at: firstItem) as? MoleculeCollectionViewCell)?.setPeaking(true, animated: true)
|
||||||
}
|
}
|
||||||
@ -257,10 +283,12 @@ open class Carousel: View {
|
|||||||
cell.accessibilityElementsHidden = false
|
cell.accessibilityElementsHidden = false
|
||||||
var array = cell.accessibilityElements
|
var array = cell.accessibilityElements
|
||||||
|
|
||||||
if let acc = pagingView?.accessibilityElements {
|
if let pagingView = pagingView {
|
||||||
array?.append(contentsOf: acc)
|
if let acc = pagingView.accessibilityElements {
|
||||||
} else {
|
array?.append(contentsOf: acc)
|
||||||
array?.append(pagingView!)
|
} else {
|
||||||
|
array?.append(pagingView)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
accessibilityElements = array
|
accessibilityElements = array
|
||||||
@ -328,7 +356,7 @@ extension Carousel: UIScrollViewDelegate {
|
|||||||
let goToIndex = { (index: Int) in
|
let goToIndex = { (index: Int) in
|
||||||
self.goTo(index, animated: false)
|
self.goTo(index, animated: false)
|
||||||
self.collectionView.layoutIfNeeded()
|
self.collectionView.layoutIfNeeded()
|
||||||
self.pagingView?.setPage(self.pageIndex)
|
self.pagingView?.currentIndex = self.pageIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentIndex < 2 {
|
if currentIndex < 2 {
|
||||||
@ -367,7 +395,7 @@ extension Carousel: UIScrollViewDelegate {
|
|||||||
checkForDraggingOutOfBounds(scrollView)
|
checkForDraggingOutOfBounds(scrollView)
|
||||||
|
|
||||||
// Let the pager know our progress if needed.
|
// Let the pager know our progress if needed.
|
||||||
pagingView?.scrollViewDidScroll?(collectionView)
|
pagingView?.scrollViewDidScroll(collectionView)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||||
@ -386,12 +414,13 @@ extension Carousel: UIScrollViewDelegate {
|
|||||||
|
|
||||||
// We switch cards if we pass the velocity threshold or position threshold (currently 50%).
|
// We switch cards if we pass the velocity threshold or position threshold (currently 50%).
|
||||||
let itemWidth = collectionView.bounds.width * CGFloat(itemWidthPercent)
|
let itemWidth = collectionView.bounds.width * CGFloat(itemWidthPercent)
|
||||||
var cellToSwipeTo = Int(scrollView.contentOffset.x/(itemWidth + separatorWidth) + 0.5)
|
var cellToSwipeTo = Int(scrollView.contentOffset.x / (itemWidth + separatorWidth) + 0.5)
|
||||||
let lastCellIndex = collectionView(collectionView, numberOfItemsInSection: 0) - 1
|
let lastCellIndex = collectionView(collectionView, numberOfItemsInSection: 0) - 1
|
||||||
let velocityThreshold: CGFloat = 1.1
|
let velocityThreshold: CGFloat = 1.1
|
||||||
|
|
||||||
if velocity.x > velocityThreshold {
|
if velocity.x > velocityThreshold {
|
||||||
cellToSwipeTo = currentIndex + 1
|
cellToSwipeTo = currentIndex + 1
|
||||||
|
|
||||||
} else if velocity.x < -velocityThreshold {
|
} else if velocity.x < -velocityThreshold {
|
||||||
cellToSwipeTo = currentIndex - 1
|
cellToSwipeTo = currentIndex - 1
|
||||||
}
|
}
|
||||||
@ -404,7 +433,7 @@ extension Carousel: UIScrollViewDelegate {
|
|||||||
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()
|
handleUserOnBufferCell()
|
||||||
pagingView?.setPage(pageIndex)
|
pagingView?.currentIndex = pageIndex
|
||||||
showPeaking(true)
|
showPeaking(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,10 @@ import UIKit
|
|||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public static var identifier: String = "carousel"
|
public static var identifier: String {
|
||||||
|
return "carousel"
|
||||||
|
}
|
||||||
|
|
||||||
public var backgroundColor: Color?
|
public var backgroundColor: Color?
|
||||||
public var molecules: [CarouselItemModel]
|
public var molecules: [CarouselItemModel]
|
||||||
public var moleculeName: String?
|
public var moleculeName: String?
|
||||||
@ -29,7 +32,7 @@ import UIKit
|
|||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public init(molecules: [CarouselItemModel]){
|
public init(molecules: [CarouselItemModel]) {
|
||||||
self.molecules = molecules
|
self.molecules = molecules
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,6 +59,7 @@ import UIKit
|
|||||||
|
|
||||||
required public init(from decoder: Decoder) throws {
|
required public init(from decoder: Decoder) throws {
|
||||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
moleculeName = try typeContainer.decodeIfPresent(String.self, forKey: .moleculeName)
|
||||||
molecules = try typeContainer.decode([CarouselItemModel].self, forKey: .molecules)
|
molecules = try typeContainer.decode([CarouselItemModel].self, forKey: .molecules)
|
||||||
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(Float.self, forKey: .spacing)
|
||||||
|
|||||||
@ -19,6 +19,7 @@ public extension MVMCoreUIMoleculeMappingObject {
|
|||||||
if let moleculeName = model.moleculeName {
|
if let moleculeName = model.moleculeName {
|
||||||
return moleculeMapping.object(forKey: moleculeName) as? AnyClass
|
return moleculeMapping.object(forKey: moleculeName) as? AnyClass
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ public extension MVMCoreUIMoleculeMappingObject {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let setData = {() in
|
let setData = {
|
||||||
if let molecule = molecule as? ModelMoleculeViewProtocol {
|
if let molecule = molecule as? ModelMoleculeViewProtocol {
|
||||||
molecule.setWithModel(model, delegateObject, nil)
|
molecule.setWithModel(model, delegateObject, nil)
|
||||||
} else {
|
} else {
|
||||||
@ -45,6 +46,7 @@ public extension MVMCoreUIMoleculeMappingObject {
|
|||||||
let view = ViewConstrainingView(molecule: molecule, alignment: castMolecule.horizontalAlignment?() ?? .fill)
|
let view = ViewConstrainingView(molecule: molecule, alignment: castMolecule.horizontalAlignment?() ?? .fill)
|
||||||
setData()
|
setData()
|
||||||
return view
|
return view
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
setData()
|
setData()
|
||||||
return molecule
|
return molecule
|
||||||
|
|||||||
@ -8,7 +8,9 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
@objcMembers public class MoleculeObjectMapping: NSObject {
|
@objcMembers public class MoleculeObjectMapping: NSObject {
|
||||||
|
|
||||||
public static func registerObjects() {
|
public static func registerObjects() {
|
||||||
// Stacks
|
// Stacks
|
||||||
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: MoleculeStackView.self, viewModelClass: MoleculeStackModel.self)
|
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: MoleculeStackView.self, viewModelClass: MoleculeStackModel.self)
|
||||||
@ -95,7 +97,6 @@ import Foundation
|
|||||||
|
|
||||||
// Other Organisms
|
// Other Organisms
|
||||||
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: Carousel.self, viewModelClass: CarouselModel.self)
|
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: Carousel.self, viewModelClass: CarouselModel.self)
|
||||||
|
|
||||||
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: CarouselIndicator.self, viewModelClass: CarouselIndicatorModel.self)
|
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: CarouselIndicator.self, viewModelClass: CarouselIndicatorModel.self)
|
||||||
|
|
||||||
// TODO: Need model
|
// TODO: Need model
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user