Merge branch 'develop' of gitlab.verizon.com:BPHV_MIPS/mvm_core_ui into develop

This commit is contained in:
Kevin G Christiano 2021-05-26 14:28:01 -04:00
commit 331dfb3475
7 changed files with 242 additions and 140 deletions

View File

@ -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()
} }
} }

View File

@ -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)
} }

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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()
}
}) })
} }
} }