Merge branch 'develop' of gitlab.verizon.com:BPHV_MIPS/mvm_core_ui into develop
This commit is contained in:
commit
331dfb3475
@ -14,6 +14,37 @@ open class BarsIndicatorView: CarouselIndicator {
|
|||||||
// MARK: - Stored Properties
|
// MARK: - Stored Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
/// Represents the data of each individual bar.
|
||||||
|
private class IndicatorBar: View {
|
||||||
|
// Dimensions are based on InVision Design Guidelines.
|
||||||
|
static let width: CGFloat = 24
|
||||||
|
static let selectedHeight: CGFloat = 4
|
||||||
|
static let unselectedHeight: CGFloat = 1
|
||||||
|
|
||||||
|
var constraint: NSLayoutConstraint?
|
||||||
|
|
||||||
|
init() {
|
||||||
|
super.init(frame: .zero)
|
||||||
|
}
|
||||||
|
|
||||||
|
public required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
public required init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
|
||||||
|
fatalError("init(model:_:_:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func setupView() {
|
||||||
|
super.setupView()
|
||||||
|
isAccessibilityElement = true
|
||||||
|
accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "AccTabHint")
|
||||||
|
widthAnchor.constraint(equalToConstant: BarsIndicatorView.IndicatorBar.width).isActive = true
|
||||||
|
accessibilityTraits = .button
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Structured container to hold and layout the indicator bars.
|
||||||
public let stackView: UIStackView = {
|
public let stackView: UIStackView = {
|
||||||
let stackView = UIStackView()
|
let stackView = UIStackView()
|
||||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
@ -25,11 +56,8 @@ open class BarsIndicatorView: CarouselIndicator {
|
|||||||
return stackView
|
return stackView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
public var barReferences: [(view: View, constraint: NSLayoutConstraint)] = []
|
/// Reference to each bar displayed.
|
||||||
|
private var barReferences: [IndicatorBar] = []
|
||||||
// Dimensions are based on InVision Design Guidelines.
|
|
||||||
public static let indicatorBarWidth: CGFloat = 24
|
|
||||||
public static let indicatorBarHeight: (selected: CGFloat, unselected: CGFloat) = (selected: 4, unselected: 1)
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Computed Properties
|
// MARK: - Computed Properties
|
||||||
@ -37,16 +65,16 @@ open class BarsIndicatorView: CarouselIndicator {
|
|||||||
|
|
||||||
/// Convenience to access the model.
|
/// Convenience to access the model.
|
||||||
public var barsCarouselIndicatorModel: BarsCarouselIndicatorModel? {
|
public var barsCarouselIndicatorModel: BarsCarouselIndicatorModel? {
|
||||||
return model as? BarsCarouselIndicatorModel
|
model as? BarsCarouselIndicatorModel
|
||||||
}
|
}
|
||||||
|
|
||||||
open override var isEnabled: Bool {
|
open override var isEnabled: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
for (i, bar) in barReferences.enumerated() {
|
for (i, indicatorBar) in barReferences.enumerated() {
|
||||||
if i == currentIndex {
|
if i == currentIndex {
|
||||||
bar.view.backgroundColor = isEnabled ? currentIndicatorColor : disabledIndicatorColor
|
indicatorBar.backgroundColor = isEnabled ? currentIndicatorColor : disabledIndicatorColor
|
||||||
} else {
|
} else {
|
||||||
bar.view.backgroundColor = isEnabled ? indicatorColor : disabledIndicatorColor
|
indicatorBar.backgroundColor = isEnabled ? indicatorColor : disabledIndicatorColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,21 +82,22 @@ open class BarsIndicatorView: CarouselIndicator {
|
|||||||
|
|
||||||
/// Colors the currently selected index, unique from other indicators
|
/// Colors the currently selected index, unique from other indicators
|
||||||
public var currentIndicatorColor: UIColor {
|
public var currentIndicatorColor: UIColor {
|
||||||
get { return barsCarouselIndicatorModel?.currentIndicatorColor.uiColor ?? indicatorColor }
|
get { barsCarouselIndicatorModel?.currentIndicatorColor.uiColor ?? indicatorColor }
|
||||||
set (newColor) {
|
set {
|
||||||
barsCarouselIndicatorModel?.currentIndicatorColor = Color(uiColor: newColor)
|
barsCarouselIndicatorModel?.currentIndicatorColor = Color(uiColor: newValue)
|
||||||
|
|
||||||
if isEnabled && !barReferences.isEmpty {
|
if isEnabled && !barReferences.isEmpty {
|
||||||
barReferences[currentIndex].view.backgroundColor = newColor
|
barReferences[currentIndex].backgroundColor = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The color of the indicator bars.
|
||||||
public override var indicatorColor: UIColor {
|
public override var indicatorColor: UIColor {
|
||||||
get { return super.indicatorColor }
|
get { super.indicatorColor }
|
||||||
set (newColor) {
|
set {
|
||||||
super.indicatorColor = newColor
|
super.indicatorColor = newValue
|
||||||
refreshBarColors(with: newColor)
|
refreshBarColors(with: newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +113,7 @@ open class BarsIndicatorView: CarouselIndicator {
|
|||||||
isAccessibilityElement = false
|
isAccessibilityElement = false
|
||||||
|
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
stackView.heightAnchor.constraint(equalToConstant: 4),
|
stackView.heightAnchor.constraint(equalToConstant: Padding.One),
|
||||||
heightAnchor.constraint(equalTo: stackView.heightAnchor),
|
heightAnchor.constraint(equalTo: stackView.heightAnchor),
|
||||||
stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||||
stackView.topAnchor.constraint(equalTo: topAnchor),
|
stackView.topAnchor.constraint(equalTo: topAnchor),
|
||||||
@ -97,44 +126,95 @@ open class BarsIndicatorView: CarouselIndicator {
|
|||||||
// MARK: - Methods
|
// MARK: - Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
/// Updates the color of all indicator bars.
|
||||||
private func refreshBarColors(with color: UIColor) {
|
private func refreshBarColors(with color: UIColor) {
|
||||||
|
|
||||||
if isEnabled {
|
guard isEnabled else { return }
|
||||||
for (i, barTuple) in barReferences.enumerated() {
|
|
||||||
barTuple.view.backgroundColor = i == currentIndex ? currentIndicatorColor : color
|
for (i, indicatorBar) in barReferences.enumerated() {
|
||||||
}
|
indicatorBar.backgroundColor = i == currentIndex ? currentIndicatorColor : color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates all the indicato bars for display.
|
||||||
func generateBars() {
|
func generateBars() {
|
||||||
|
|
||||||
var bars = [(View, NSLayoutConstraint)]()
|
var bars = [IndicatorBar]()
|
||||||
|
|
||||||
for i in 0..<numberOfPages {
|
for i in 0..<numberOfPages {
|
||||||
let bar = View()
|
let indicatorBar = createIndicatorBar(index: i)
|
||||||
bar.isAccessibilityElement = true
|
stackView.addArrangedSubview(indicatorBar)
|
||||||
if let accessibleValueFormat = accessibilityValueFormat, let accessibleIndex = MVMCoreUIUtility.getOrdinalString(forIndex: NSNumber(value: i + 1)) {
|
bars.append(indicatorBar)
|
||||||
bar.accessibilityLabel = String(format: accessibleValueFormat, accessibleIndex, numberOfPages)
|
|
||||||
}
|
|
||||||
bar.accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "AccTabHint")
|
|
||||||
bar.accessibilityTraits = .button
|
|
||||||
bar.widthAnchor.constraint(equalToConstant: BarsIndicatorView.indicatorBarWidth).isActive = true
|
|
||||||
bar.backgroundColor = isEnabled ? (i == currentIndex ? currentIndicatorColor : indicatorColor) : disabledIndicatorColor
|
|
||||||
let barHeight = i == currentIndex ? BarsIndicatorView.indicatorBarHeight.selected : BarsIndicatorView.indicatorBarHeight.unselected
|
|
||||||
let heightConstraint = bar.heightAnchor.constraint(equalToConstant: barHeight)
|
|
||||||
heightConstraint.isActive = true
|
|
||||||
|
|
||||||
stackView.addArrangedSubview(bar)
|
|
||||||
bars.append((bar, heightConstraint))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
accessibilityElements = stackView.arrangedSubviews
|
accessibilityElements = stackView.arrangedSubviews
|
||||||
barReferences = bars
|
barReferences = bars
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes an indicator bar from the view.
|
||||||
|
private func removeBar() {
|
||||||
|
let lastView = barReferences.removeLast()
|
||||||
|
stackView.removeArrangedSubview(lastView)
|
||||||
|
lastView.removeFromSuperview()
|
||||||
|
accessibilityElements = stackView.arrangedSubviews
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Based of the sign of the difference indicator bars will be removed or added.
|
||||||
|
private func balanceBarCount(_ difference: Int) {
|
||||||
|
|
||||||
|
if difference > 0 {
|
||||||
|
// If positive, add n bars.
|
||||||
|
var copyBars = barReferences
|
||||||
|
|
||||||
|
for _ in 0..<difference {
|
||||||
|
let indicatorBar = createIndicatorBar(index: barReferences.count)
|
||||||
|
indicatorBar.constraint?.constant = IndicatorBar.unselectedHeight
|
||||||
|
stackView.addArrangedSubview(indicatorBar)
|
||||||
|
copyBars.append(indicatorBar)
|
||||||
|
}
|
||||||
|
|
||||||
|
accessibilityElements = stackView.arrangedSubviews
|
||||||
|
barReferences = copyBars
|
||||||
|
|
||||||
|
} else if difference < 0 {
|
||||||
|
// If negative, remove n bars.
|
||||||
|
for _ in 0..<(difference * -1) {
|
||||||
|
removeBar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createIndicatorBar(index: Int) -> IndicatorBar {
|
||||||
|
|
||||||
|
let bar = IndicatorBar()
|
||||||
|
setAccessibilityLabel(view: bar, index: index)
|
||||||
|
bar.backgroundColor = isEnabled ? (index == currentIndex ? currentIndicatorColor : indicatorColor) : disabledIndicatorColor
|
||||||
|
let barHeight = index == currentIndex ? BarsIndicatorView.IndicatorBar.selectedHeight : BarsIndicatorView.IndicatorBar.unselectedHeight
|
||||||
|
let heightConstraint = bar.heightAnchor.constraint(equalToConstant: barHeight)
|
||||||
|
heightConstraint.isActive = true
|
||||||
|
bar.constraint = heightConstraint
|
||||||
|
|
||||||
|
return bar
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Refreshes the accessibility labels to read "x of n".
|
||||||
|
private func refreshAccessibilityLabels() {
|
||||||
|
for i in 0..<barReferences.count {
|
||||||
|
setAccessibilityLabel(view: barReferences[i], index: i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setAccessibilityLabel(view: UIView, index: Int) {
|
||||||
|
guard let accessibleValueFormat = accessibilityValueFormat,
|
||||||
|
let accessibleIndex = MVMCoreUIUtility.getOrdinalString(forIndex: NSNumber(value: index + 1))
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
view.accessibilityLabel = String(format: accessibleValueFormat, accessibleIndex, numberOfPages)
|
||||||
|
}
|
||||||
|
|
||||||
public override func assessTouchOf(_ touchPoint_X: CGFloat) {
|
public override func assessTouchOf(_ touchPoint_X: CGFloat) {
|
||||||
|
|
||||||
currentIndex = barReferences.firstIndex { $0.0.frame.maxX >= touchPoint_X && $0.0.frame.minX <= touchPoint_X } ?? 0
|
currentIndex = barReferences.firstIndex { $0.frame.maxX >= touchPoint_X && $0.frame.minX <= touchPoint_X } ?? 0
|
||||||
performAction()
|
performAction()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,27 +232,40 @@ open class BarsIndicatorView: CarouselIndicator {
|
|||||||
|
|
||||||
public override func reset() {
|
public override func reset() {
|
||||||
super.reset()
|
super.reset()
|
||||||
barReferences.forEach { $0.view.removeFromSuperview() }
|
barReferences.forEach { $0.removeFromSuperview() }
|
||||||
barReferences = []
|
barReferences = []
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func updateUI(previousIndex: Int, newIndex: Int, totalCount: Int, isAnimated: Bool) {
|
public override func updateUI(previousIndex: Int, newIndex: Int, totalCount: Int, isAnimated: Bool) {
|
||||||
|
|
||||||
guard newIndex < totalCount else { return }
|
// Ensure that at least one bar exists.
|
||||||
|
|
||||||
guard !barReferences.isEmpty else {
|
guard !barReferences.isEmpty else {
|
||||||
generateBars()
|
generateBars()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let expression = {
|
guard newIndex < totalCount else { return }
|
||||||
self.barReferences[previousIndex].view.backgroundColor = self.isEnabled ? self.indicatorColor : self.disabledIndicatorColor
|
|
||||||
self.barReferences[newIndex].view.backgroundColor = self.isEnabled ? self.currentIndicatorColor : self.disabledIndicatorColor
|
// Ensure the number of pages matches the number of bar references.
|
||||||
self.barReferences[previousIndex].constraint.constant = BarsIndicatorView.indicatorBarHeight.unselected
|
if (totalCount - barReferences.count) != 0 {
|
||||||
self.barReferences[newIndex].constraint.constant = BarsIndicatorView.indicatorBarHeight.selected
|
barReferences.forEach {
|
||||||
self.layoutIfNeeded()
|
$0.backgroundColor = isEnabled ? indicatorColor : disabledIndicatorColor
|
||||||
|
$0.constraint?.constant = IndicatorBar.unselectedHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
balanceBarCount(numberOfPages - barReferences.count)
|
||||||
|
refreshAccessibilityLabels()
|
||||||
}
|
}
|
||||||
|
|
||||||
isAnimated ? UIView.animate(withDuration: 0.3) { expression() } : expression()
|
let expression = { [self] in
|
||||||
|
barReferences[previousIndex].backgroundColor = isEnabled ? indicatorColor : disabledIndicatorColor
|
||||||
|
barReferences[previousIndex].constraint?.constant = IndicatorBar.unselectedHeight
|
||||||
|
barReferences[newIndex].backgroundColor = isEnabled ? currentIndicatorColor : disabledIndicatorColor
|
||||||
|
barReferences[newIndex].constraint?.constant = IndicatorBar.selectedHeight
|
||||||
|
layoutIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the animation.
|
||||||
|
isAnimated ? UIView.animate(withDuration: 0.25) { expression() } : expression()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,8 +6,6 @@
|
|||||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
|
|
||||||
open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -25,7 +23,7 @@ open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
|||||||
|
|
||||||
/// Convenience to access the model.
|
/// Convenience to access the model.
|
||||||
public var carouselIndicatorModel: CarouselIndicatorModel? {
|
public var carouselIndicatorModel: CarouselIndicatorModel? {
|
||||||
return model as? CarouselIndicatorModel
|
model as? CarouselIndicatorModel
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
@ -43,7 +41,7 @@ open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
|||||||
private(set) var previousIndex = 0
|
private(set) var previousIndex = 0
|
||||||
|
|
||||||
public var currentIndex: Int {
|
public var currentIndex: Int {
|
||||||
get { return carouselIndicatorModel?.currentIndex ?? 0 }
|
get { carouselIndicatorModel?.currentIndex ?? 0 }
|
||||||
set (newIndex) {
|
set (newIndex) {
|
||||||
previousIndex = currentIndex
|
previousIndex = currentIndex
|
||||||
carouselIndicatorModel?.currentIndex = newIndex
|
carouselIndicatorModel?.currentIndex = newIndex
|
||||||
@ -58,22 +56,22 @@ open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
|||||||
/// Holds the total number of pages displayed by the carousel.
|
/// Holds the total number of pages displayed by the carousel.
|
||||||
/// Updating this property will potentially update the UI.
|
/// Updating this property will potentially update the UI.
|
||||||
public var numberOfPages: Int {
|
public var numberOfPages: Int {
|
||||||
get { return carouselIndicatorModel?.numberOfPages ?? 0 }
|
get { carouselIndicatorModel?.numberOfPages ?? 0 }
|
||||||
set (newTotal) {
|
set {
|
||||||
guard numberOfPages != newTotal else { return }
|
guard numberOfPages != newValue else { return }
|
||||||
|
|
||||||
carouselIndicatorModel?.numberOfPages = newTotal
|
carouselIndicatorModel?.numberOfPages = newValue
|
||||||
reset()
|
reset()
|
||||||
isHidden = (carouselIndicatorModel?.hidesForSinglePage ?? false) && newTotal <= 1
|
isHidden = (carouselIndicatorModel?.hidesForSinglePage ?? false) && newValue <= 1
|
||||||
updateUI(previousIndex: previousIndex,
|
updateUI(previousIndex: previousIndex,
|
||||||
newIndex: currentIndex,
|
newIndex: currentIndex,
|
||||||
totalCount: newTotal,
|
totalCount: newValue,
|
||||||
isAnimated: carouselIndicatorModel?.animated ?? true)
|
isAnimated: carouselIndicatorModel?.animated ?? true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var disabledIndicatorColor: UIColor {
|
public var disabledIndicatorColor: UIColor {
|
||||||
get { return carouselIndicatorModel?.disabledIndicatorColor.uiColor ?? .mvmCoolGray3 }
|
get { carouselIndicatorModel?.disabledIndicatorColor.uiColor ?? .mvmCoolGray3 }
|
||||||
set { carouselIndicatorModel?.disabledIndicatorColor = Color(uiColor: newValue) }
|
set { carouselIndicatorModel?.disabledIndicatorColor = Color(uiColor: newValue) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +90,7 @@ open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var accessibilityValueFormat: String? {
|
var accessibilityValueFormat: String? {
|
||||||
return MVMCoreUIUtility.hardcodedString(withKey: (carouselIndicatorModel?.accessibilityHasSlidesInsteadOfPage ?? false) ? "MVMCoreUIPageControlslides_currentpage_index" : "MVMCoreUIPageControl_currentpage_index")
|
MVMCoreUIUtility.hardcodedString(withKey: (carouselIndicatorModel?.accessibilityHasSlidesInsteadOfPage ?? false) ? "MVMCoreUIPageControlslides_currentpage_index" : "MVMCoreUIPageControl_currentpage_index")
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -175,12 +173,14 @@ open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
|||||||
assessTouchOf(touchPoint_X)
|
assessTouchOf(touchPoint_X)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determines where a touch in the indicator translate to an index selection.
|
||||||
func assessTouchOf(_ touchPoint_X: CGFloat) { }
|
func assessTouchOf(_ touchPoint_X: CGFloat) { }
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Methods
|
// MARK: - Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
/// Called to update the Indicator UI with the latest values.
|
||||||
open func updateUI(previousIndex: Int, newIndex: Int, totalCount: Int, isAnimated: Bool) { }
|
open func updateUI(previousIndex: Int, newIndex: Int, totalCount: Int, isAnimated: Bool) { }
|
||||||
|
|
||||||
public func performAction() {
|
public func performAction() {
|
||||||
@ -200,6 +200,7 @@ open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
|||||||
|
|
||||||
guard let model = model as? CarouselIndicatorModel else { return }
|
guard let model = model as? CarouselIndicatorModel else { return }
|
||||||
|
|
||||||
|
previousIndex = 0
|
||||||
indicatorColor = model.inverted ? model.indicatorColor_inverted.uiColor : model.indicatorColor.uiColor
|
indicatorColor = model.inverted ? model.indicatorColor_inverted.uiColor : model.indicatorColor.uiColor
|
||||||
disabledIndicatorColor = model.disabledIndicatorColor.uiColor
|
disabledIndicatorColor = model.disabledIndicatorColor.uiColor
|
||||||
currentIndex = model.currentIndex
|
currentIndex = model.currentIndex
|
||||||
@ -214,20 +215,18 @@ open class CarouselIndicator: Control, CarouselPageControlProtocol {
|
|||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
open override func accessibilityIncrement() {
|
open override func accessibilityIncrement() {
|
||||||
|
|
||||||
adjustAccessibility(toPage: currentIndex + 1)
|
adjustAccessibility(toPage: currentIndex + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func accessibilityDecrement() {
|
open override func accessibilityDecrement() {
|
||||||
|
|
||||||
adjustAccessibility(toPage: currentIndex - 1)
|
adjustAccessibility(toPage: currentIndex - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatAccessibilityValue(index: Int, total: Int) {
|
func formatAccessibilityValue(index: Int, total: Int) {
|
||||||
|
|
||||||
guard let accessibleFormat = accessibilityValueFormat,
|
guard let accessibleFormat = accessibilityValueFormat,
|
||||||
let accessibleIndex = MVMCoreUIUtility.getOrdinalString(forIndex: NSNumber(value: index))
|
let accessibleIndex = MVMCoreUIUtility.getOrdinalString(forIndex: NSNumber(value: index))
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
accessibilityValue = String(format: accessibleFormat, accessibleIndex, total)
|
accessibilityValue = String(format: accessibleFormat, accessibleIndex, total)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,7 +47,7 @@ open class NumericIndicatorView: CarouselIndicator {
|
|||||||
|
|
||||||
/// Sets the color for pageCount text, left arrow and right arrow.
|
/// Sets the color for pageCount text, left arrow and right arrow.
|
||||||
public override var indicatorColor: UIColor {
|
public override var indicatorColor: UIColor {
|
||||||
get { return super.indicatorColor }
|
get { super.indicatorColor }
|
||||||
set (newColor) {
|
set (newColor) {
|
||||||
super.indicatorColor = newColor
|
super.indicatorColor = newColor
|
||||||
|
|
||||||
|
|||||||
@ -22,12 +22,6 @@ open class HeaderView: Container {
|
|||||||
addSubview(molecule)
|
addSubview(molecule)
|
||||||
containerHelper.constrainView(molecule)
|
containerHelper.constrainView(molecule)
|
||||||
self.molecule = molecule
|
self.molecule = molecule
|
||||||
guard let margins = molecule.superview?.layoutMarginsGuide else { return }
|
|
||||||
containerHelper.rightConstraint?.isActive = false
|
|
||||||
containerHelper.rightConstraint = margins.rightAnchor.constraint(equalTo: molecule.rightAnchor)
|
|
||||||
containerHelper.rightConstraint?.priority = .defaultHigh
|
|
||||||
containerHelper.rightConstraint?.isActive = true
|
|
||||||
molecule.widthAnchor.constraint(equalToConstant: HeaderMaxWidth).isActive = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MVMCoreViewProtocol
|
// MARK: - MVMCoreViewProtocol
|
||||||
|
|||||||
@ -24,16 +24,6 @@ public class MoleculeHeaderView: MoleculeContainer {
|
|||||||
// MARK: - MVMCoreViewProtocol
|
// MARK: - MVMCoreViewProtocol
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public override func addMolecule(_ molecule: UIView) {
|
|
||||||
super.addMolecule(molecule)
|
|
||||||
guard let margins = molecule.superview?.layoutMarginsGuide else { return }
|
|
||||||
containerHelper.rightConstraint?.isActive = false
|
|
||||||
containerHelper.rightConstraint = margins.rightAnchor.constraint(equalTo: molecule.rightAnchor)
|
|
||||||
containerHelper.rightConstraint?.priority = .defaultHigh
|
|
||||||
containerHelper.rightConstraint?.isActive = true
|
|
||||||
molecule.widthAnchor.constraint(lessThanOrEqualToConstant: HeaderMaxWidth).isActive = true
|
|
||||||
}
|
|
||||||
|
|
||||||
open override func updateView(_ size: CGFloat) {
|
open override func updateView(_ size: CGFloat) {
|
||||||
super.updateView(size)
|
super.updateView(size)
|
||||||
line.updateView(size)
|
line.updateView(size)
|
||||||
|
|||||||
@ -53,7 +53,7 @@ open class Carousel: View {
|
|||||||
public var collectionViewHeight: NSLayoutConstraint?
|
public var collectionViewHeight: NSLayoutConstraint?
|
||||||
|
|
||||||
/// The view that we use for paging
|
/// The view that we use for paging
|
||||||
public var pagingView: (UIView & CarouselPageControlProtocol)?
|
public var pagingView: (MoleculeViewProtocol & 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.
|
||||||
public var loop = false
|
public var loop = false
|
||||||
@ -88,7 +88,7 @@ open class Carousel: View {
|
|||||||
|
|
||||||
// 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,
|
guard let model = model as? CarouselModel,
|
||||||
(model.paging == true || loop == true) else { return }
|
(model.paging == true || 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()
|
||||||
@ -207,12 +207,25 @@ open class Carousel: View {
|
|||||||
pageIndex = 0
|
pageIndex = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var pagingMoleculeName: String?
|
||||||
|
|
||||||
/// Sets up the paging molecule
|
/// Sets up the paging molecule
|
||||||
open func setupPagingMolecule(_ molecule: (CarouselPagingModelProtocol & MoleculeModelProtocol)?, delegateObject: MVMCoreUIDelegateObject?) {
|
open func setupPagingMolecule(_ molecule: (CarouselPagingModelProtocol & MoleculeModelProtocol)?, delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
var pagingView: (UIView & CarouselPageControlProtocol)? = nil
|
|
||||||
if let molecule = molecule,
|
if let molecule = molecule,
|
||||||
(!molecule.hidesForSinglePage || numberOfPages > 1) {
|
molecule.moleculeName == pagingMoleculeName {
|
||||||
pagingView = ModelRegistry.createMolecule(molecule, delegateObject: delegateObject) as? (UIView & CarouselPageControlProtocol)
|
pagingView?.set(with: molecule, delegateObject, nil)
|
||||||
|
pagingView?.numberOfPages = numberOfPages
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var pagingView: (MoleculeViewProtocol & CarouselPageControlProtocol)? = nil
|
||||||
|
if let molecule = molecule,
|
||||||
|
(!molecule.hidesForSinglePage || numberOfPages > 1) {
|
||||||
|
pagingView = ModelRegistry.createMolecule(molecule, delegateObject: delegateObject) as? (MoleculeViewProtocol & CarouselPageControlProtocol)
|
||||||
|
pagingMoleculeName = molecule.moleculeName
|
||||||
|
} else {
|
||||||
|
pagingMoleculeName = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
addPaging(view: pagingView, position: molecule?.position ?? 20)
|
addPaging(view: pagingView, position: molecule?.position ?? 20)
|
||||||
@ -239,7 +252,7 @@ open class Carousel: 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.
|
/// 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 & CarouselPageControlProtocol)?, position: CGFloat) {
|
open func addPaging(view: (MoleculeViewProtocol & CarouselPageControlProtocol)?, position: CGFloat) {
|
||||||
|
|
||||||
pagingView?.removeFromSuperview()
|
pagingView?.removeFromSuperview()
|
||||||
bottomPin?.isActive = false
|
bottomPin?.isActive = false
|
||||||
@ -296,7 +309,7 @@ open class Carousel: View {
|
|||||||
|
|
||||||
func trackSwipeActionAnalyticsforIndex(_ index : Int){
|
func trackSwipeActionAnalyticsforIndex(_ index : Int){
|
||||||
guard let itemModel = molecules?[index],
|
guard let itemModel = molecules?[index],
|
||||||
let viewControllerObject = delegateObject?.moleculeDelegate as? MVMCoreViewControllerProtocol else { return }
|
let viewControllerObject = delegateObject?.moleculeDelegate as? MVMCoreViewControllerProtocol else { return }
|
||||||
MVMCoreUILoggingHandler.shared()?.defaultLogAction(forController: viewControllerObject, actionInformation: itemModel.toJSON(), additionalData: nil)
|
MVMCoreUILoggingHandler.shared()?.defaultLogAction(forController: viewControllerObject, actionInformation: itemModel.toJSON(), additionalData: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,8 +394,8 @@ extension Carousel: UICollectionViewDataSource {
|
|||||||
|
|
||||||
open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
guard let molecule = molecules?[indexPath.row],
|
guard let molecule = molecules?[indexPath.row],
|
||||||
let moleculeInfo = getMoleculeInfo(with: molecule, delegateObject: nil)
|
let moleculeInfo = getMoleculeInfo(with: molecule, delegateObject: nil)
|
||||||
else { return UICollectionViewCell() }
|
else { return UICollectionViewCell() }
|
||||||
|
|
||||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: moleculeInfo.identifier, for: indexPath)
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: moleculeInfo.identifier, for: indexPath)
|
||||||
if let protocolCell = cell as? MoleculeViewProtocol {
|
if let protocolCell = cell as? MoleculeViewProtocol {
|
||||||
@ -445,15 +458,15 @@ extension Carousel: UIScrollViewDelegate {
|
|||||||
if translatedPoint > 0 {
|
if translatedPoint > 0 {
|
||||||
// Moving left, see if we are moving passed the first left buffer card and adjust
|
// Moving left, see if we are moving passed the first left buffer card and adjust
|
||||||
if let threshold = collectionView.layoutAttributesForItem(at: IndexPath(item: 1, section: 0))?.frame.minX,
|
if let threshold = collectionView.layoutAttributesForItem(at: IndexPath(item: 1, section: 0))?.frame.minX,
|
||||||
scrollView.contentOffset.x < threshold,
|
scrollView.contentOffset.x < threshold,
|
||||||
let newOffset = collectionView.layoutAttributesForItem(at: IndexPath(item: numberOfPages + 1, section: 0))?.frame.minX {
|
let newOffset = collectionView.layoutAttributesForItem(at: IndexPath(item: numberOfPages + 1, section: 0))?.frame.minX {
|
||||||
scrollView.contentOffset.x = newOffset
|
scrollView.contentOffset.x = newOffset
|
||||||
}
|
}
|
||||||
} else if translatedPoint < 0 {
|
} else if translatedPoint < 0 {
|
||||||
// Moving right, see if we are moving passed the first right buffer card and adjust
|
// Moving right, see if we are moving passed the first right buffer card and adjust
|
||||||
if let threshold = collectionView.layoutAttributesForItem(at: IndexPath(item: numberOfPages + 2, section: 0))?.frame.maxX,
|
if let threshold = collectionView.layoutAttributesForItem(at: IndexPath(item: numberOfPages + 2, section: 0))?.frame.maxX,
|
||||||
scrollView.contentOffset.x + scrollView.bounds.width > threshold,
|
scrollView.contentOffset.x + scrollView.bounds.width > threshold,
|
||||||
let newEndOffset = collectionView.layoutAttributesForItem(at: IndexPath(item: 2, section: 0))?.frame.maxX {
|
let newEndOffset = collectionView.layoutAttributesForItem(at: IndexPath(item: 2, section: 0))?.frame.maxX {
|
||||||
scrollView.contentOffset.x = newEndOffset - scrollView.bounds.width
|
scrollView.contentOffset.x = newEndOffset - scrollView.bounds.width
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -484,7 +497,7 @@ extension Carousel: UIScrollViewDelegate {
|
|||||||
|
|
||||||
// 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.
|
// 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 (model as? CarouselModel)?.paging == true,
|
guard (model as? CarouselModel)?.paging == true,
|
||||||
let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return }
|
let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return }
|
||||||
|
|
||||||
let separatorWidth = layout.minimumLineSpacing
|
let separatorWidth = layout.minimumLineSpacing
|
||||||
let itemWidth = collectionView.bounds.width * itemWidthPercent
|
let itemWidth = collectionView.bounds.width * itemWidthPercent
|
||||||
@ -553,7 +566,7 @@ class CarouselAccessibilityElement: UIAccessibilityElement {
|
|||||||
override var accessibilityLabel: String? {
|
override var accessibilityLabel: String? {
|
||||||
get {
|
get {
|
||||||
guard let containerView = accessibilityContainer as? Carousel,
|
guard let containerView = accessibilityContainer as? Carousel,
|
||||||
let accessibilityLabel = containerView.accessibilityLabel else { return super.accessibilityLabel }
|
let accessibilityLabel = containerView.accessibilityLabel else { return super.accessibilityLabel }
|
||||||
return accessibilityLabel
|
return accessibilityLabel
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
@ -565,8 +578,8 @@ class CarouselAccessibilityElement: UIAccessibilityElement {
|
|||||||
get {
|
get {
|
||||||
// Read which card we are on.
|
// Read which card we are on.
|
||||||
guard let containerView = accessibilityContainer as? Carousel,
|
guard let containerView = accessibilityContainer as? Carousel,
|
||||||
let format = MVMCoreUIUtility.hardcodedString(withKey: "index_string_of_total"),
|
let format = MVMCoreUIUtility.hardcodedString(withKey: "index_string_of_total"),
|
||||||
let indexString = MVMCoreUIUtility.getOrdinalString(forIndex: NSNumber(value: containerView.currentIndex + 1)) else {
|
let indexString = MVMCoreUIUtility.getOrdinalString(forIndex: NSNumber(value: containerView.currentIndex + 1)) else {
|
||||||
return super.accessibilityValue
|
return super.accessibilityValue
|
||||||
}
|
}
|
||||||
return String(format: format, indexString, containerView.numberOfPages)
|
return String(format: format, indexString, containerView.numberOfPages)
|
||||||
@ -589,9 +602,9 @@ class CarouselAccessibilityElement: UIAccessibilityElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A convenience for forward scrolling in both `accessibilityIncrement` and `accessibilityScroll`.
|
A convenience for forward scrolling in both `accessibilityIncrement` and `accessibilityScroll`.
|
||||||
It returns a `Bool` because `accessibilityScroll` needs to know if the scroll was successful.
|
It returns a `Bool` because `accessibilityScroll` needs to know if the scroll was successful.
|
||||||
*/
|
*/
|
||||||
func accessibilityScrollForward() -> Bool {
|
func accessibilityScrollForward() -> Bool {
|
||||||
guard let containerView = accessibilityContainer as? Carousel else { return false }
|
guard let containerView = accessibilityContainer as? Carousel else { return false }
|
||||||
|
|
||||||
@ -604,9 +617,9 @@ class CarouselAccessibilityElement: UIAccessibilityElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A convenience for backward scrolling in both `accessibilityIncrement` and `accessibilityScroll`.
|
A convenience for backward scrolling in both `accessibilityIncrement` and `accessibilityScroll`.
|
||||||
It returns a `Bool` because `accessibilityScroll` needs to know if the scroll was successful.
|
It returns a `Bool` because `accessibilityScroll` needs to know if the scroll was successful.
|
||||||
*/
|
*/
|
||||||
func accessibilityScrollBackward() -> Bool {
|
func accessibilityScrollBackward() -> Bool {
|
||||||
guard let containerView = accessibilityContainer as? Carousel else { return false }
|
guard let containerView = accessibilityContainer as? Carousel else { return false }
|
||||||
|
|
||||||
@ -619,9 +632,9 @@ class CarouselAccessibilityElement: UIAccessibilityElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Overriding the following two methods allows the user to perform increment and decrement actions
|
Overriding the following two methods allows the user to perform increment and decrement actions
|
||||||
(done by swiping up or down).
|
(done by swiping up or down).
|
||||||
*/
|
*/
|
||||||
/// - Tag: accessibility_increment_decrement
|
/// - Tag: accessibility_increment_decrement
|
||||||
override func accessibilityIncrement() {
|
override func accessibilityIncrement() {
|
||||||
// This causes the picker to move forward one if the user swipes up.
|
// This causes the picker to move forward one if the user swipes up.
|
||||||
@ -634,10 +647,10 @@ class CarouselAccessibilityElement: UIAccessibilityElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This will cause the picker to move forward or backwards on when the user does a 3-finger swipe,
|
This will cause the picker to move forward or backwards on when the user does a 3-finger swipe,
|
||||||
depending on the direction of the swipe. The return value indicates whether or not the scroll was successful,
|
depending on the direction of the swipe. The return value indicates whether or not the scroll was successful,
|
||||||
so that VoiceOver can alert the user if it was not.
|
so that VoiceOver can alert the user if it was not.
|
||||||
*/
|
*/
|
||||||
override func accessibilityScroll(_ direction: UIAccessibilityScrollDirection) -> Bool {
|
override func accessibilityScroll(_ direction: UIAccessibilityScrollDirection) -> Bool {
|
||||||
if direction == .left {
|
if direction == .left {
|
||||||
return accessibilityScrollForward()
|
return accessibilityScrollForward()
|
||||||
|
|||||||
@ -41,7 +41,20 @@ public class PageGetContactBehavior: PageVisibilityBehavior {
|
|||||||
// Tell template to update
|
// Tell template to update
|
||||||
MVMCoreDispatchUtility.performBlock(onMainThread: {
|
MVMCoreDispatchUtility.performBlock(onMainThread: {
|
||||||
// TODO: move to protocol function instead
|
// TODO: move to protocol function instead
|
||||||
(self?.delegate?.moleculeDelegate as? ViewController)?.handleNewDataAndUpdateUI()
|
guard let controller = self?.delegate?.moleculeDelegate as? ViewController else { return }
|
||||||
|
controller.handleNewDataAndUpdateUI()
|
||||||
|
|
||||||
|
if MVMCoreUIUtility.getCurrentVisibleController() == controller {
|
||||||
|
// Update navigation bar if showing.
|
||||||
|
controller.setNavigationBar()
|
||||||
|
controller.manager?.refreshNavigationUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update splitview properties
|
||||||
|
if controller == MVMCoreUISplitViewController.main()?.getCurrentDetailViewController() {
|
||||||
|
MVMCoreUISplitViewController.main()?.setBottomProgressBarProgress(controller.bottomProgress() ?? 0)
|
||||||
|
controller.updateTabBar()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user