Fixed bugs, added comments and created new flow layout
This commit is contained in:
parent
8ed924c805
commit
cd85748a11
@ -22,6 +22,8 @@
|
|||||||
71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */; };
|
71C02B382B7BD98F00E93E66 /* NotificationChangeLog.txt in Resources */ = {isa = PBXBuildFile; fileRef = 71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */; };
|
||||||
71FC86DA2B96F44C00700965 /* PaginationButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86D92B96F44C00700965 /* PaginationButton.swift */; };
|
71FC86DA2B96F44C00700965 /* PaginationButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86D92B96F44C00700965 /* PaginationButton.swift */; };
|
||||||
71FC86DC2B96F4C800700965 /* PaginationCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */; };
|
71FC86DC2B96F4C800700965 /* PaginationCellItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */; };
|
||||||
|
71FC86E22B97483000700965 /* Clamping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86E12B97483000700965 /* Clamping.swift */; };
|
||||||
|
71FC86E42B9841AC00700965 /* PaginationFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71FC86E32B9841AC00700965 /* PaginationFlowLayout.swift */; };
|
||||||
EA0B18022A9E236900F2D0CD /* SelectorGroupBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */; };
|
EA0B18022A9E236900F2D0CD /* SelectorGroupBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */; };
|
||||||
EA0B18052A9E2D2D00F2D0CD /* SelectorBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */; };
|
EA0B18052A9E2D2D00F2D0CD /* SelectorBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */; };
|
||||||
EA0B18062A9E2D2D00F2D0CD /* SelectorItemBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */; };
|
EA0B18062A9E2D2D00F2D0CD /* SelectorItemBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */; };
|
||||||
@ -194,6 +196,8 @@
|
|||||||
71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = NotificationChangeLog.txt; sourceTree = "<group>"; };
|
71C02B372B7BD98F00E93E66 /* NotificationChangeLog.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = NotificationChangeLog.txt; sourceTree = "<group>"; };
|
||||||
71FC86D92B96F44C00700965 /* PaginationButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationButton.swift; sourceTree = "<group>"; };
|
71FC86D92B96F44C00700965 /* PaginationButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationButton.swift; sourceTree = "<group>"; };
|
||||||
71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationCellItem.swift; sourceTree = "<group>"; };
|
71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationCellItem.swift; sourceTree = "<group>"; };
|
||||||
|
71FC86E12B97483000700965 /* Clamping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Clamping.swift; sourceTree = "<group>"; };
|
||||||
|
71FC86E32B9841AC00700965 /* PaginationFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationFlowLayout.swift; sourceTree = "<group>"; };
|
||||||
EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorGroupBase.swift; sourceTree = "<group>"; };
|
EA0B18012A9E236900F2D0CD /* SelectorGroupBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorGroupBase.swift; sourceTree = "<group>"; };
|
||||||
EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorBase.swift; sourceTree = "<group>"; };
|
EA0B18032A9E2D2D00F2D0CD /* SelectorBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorBase.swift; sourceTree = "<group>"; };
|
||||||
EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorItemBase.swift; sourceTree = "<group>"; };
|
EA0B18042A9E2D2D00F2D0CD /* SelectorItemBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectorItemBase.swift; sourceTree = "<group>"; };
|
||||||
@ -398,6 +402,7 @@
|
|||||||
71B23C2C2B91FA690027F7D9 /* Pagination.swift */,
|
71B23C2C2B91FA690027F7D9 /* Pagination.swift */,
|
||||||
71FC86D92B96F44C00700965 /* PaginationButton.swift */,
|
71FC86D92B96F44C00700965 /* PaginationButton.swift */,
|
||||||
71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */,
|
71FC86DB2B96F4C800700965 /* PaginationCellItem.swift */,
|
||||||
|
71FC86E32B9841AC00700965 /* PaginationFlowLayout.swift */,
|
||||||
71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */,
|
71B5FCBA2B95A0CA00269BCC /* PaginationChangeLog.txt */,
|
||||||
);
|
);
|
||||||
path = Pagination;
|
path = Pagination;
|
||||||
@ -603,6 +608,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
EA3361BC288B2C760071C351 /* TypeAlias.swift */,
|
EA3361BC288B2C760071C351 /* TypeAlias.swift */,
|
||||||
|
71FC86E12B97483000700965 /* Clamping.swift */,
|
||||||
);
|
);
|
||||||
path = Utilities;
|
path = Utilities;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1022,6 +1028,7 @@
|
|||||||
EA3361C328902D960071C351 /* Toggle.swift in Sources */,
|
EA3361C328902D960071C351 /* Toggle.swift in Sources */,
|
||||||
EAF7F0A0289AB7EC00B287F5 /* View.swift in Sources */,
|
EAF7F0A0289AB7EC00B287F5 /* View.swift in Sources */,
|
||||||
EA89201328B568D8006B9984 /* RadioBoxItem.swift in Sources */,
|
EA89201328B568D8006B9984 /* RadioBoxItem.swift in Sources */,
|
||||||
|
71FC86E42B9841AC00700965 /* PaginationFlowLayout.swift in Sources */,
|
||||||
EAC9258C2911C9DE00091998 /* InputField.swift in Sources */,
|
EAC9258C2911C9DE00091998 /* InputField.swift in Sources */,
|
||||||
EA3362402892EF6C0071C351 /* Label.swift in Sources */,
|
EA3362402892EF6C0071C351 /* Label.swift in Sources */,
|
||||||
EAB2376229E9880400AABE9A /* TrailingTooltipLabel.swift in Sources */,
|
EAB2376229E9880400AABE9A /* TrailingTooltipLabel.swift in Sources */,
|
||||||
@ -1030,6 +1037,7 @@
|
|||||||
71BFA70A2B7F70E6000DCE33 /* Dropshadowable.swift in Sources */,
|
71BFA70A2B7F70E6000DCE33 /* Dropshadowable.swift in Sources */,
|
||||||
EA0D1C452A6AD73000E5C127 /* RawRepresentable.swift in Sources */,
|
EA0D1C452A6AD73000E5C127 /* RawRepresentable.swift in Sources */,
|
||||||
EA985C23296E033A00F2FF2E /* TextArea.swift in Sources */,
|
EA985C23296E033A00F2FF2E /* TextArea.swift in Sources */,
|
||||||
|
71FC86E22B97483000700965 /* Clamping.swift in Sources */,
|
||||||
EAF7F0B3289B1ADC00B287F5 /* ActionLabelAttribute.swift in Sources */,
|
EAF7F0B3289B1ADC00B287F5 /* ActionLabelAttribute.swift in Sources */,
|
||||||
EAC925832911B35400091998 /* TextLinkCaret.swift in Sources */,
|
EAC925832911B35400091998 /* TextLinkCaret.swift in Sources */,
|
||||||
EA33622E2891EA3C0071C351 /* DispatchQueue+Once.swift in Sources */,
|
EA33622E2891EA3C0071C351 /* DispatchQueue+Once.swift in Sources */,
|
||||||
|
|||||||
@ -9,58 +9,24 @@ import Foundation
|
|||||||
import VDSColorTokens
|
import VDSColorTokens
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
|
///Pagination is a control that enables customers to navigate multiple pages of content by selecting either a specific page or the next or previous set of four pages.
|
||||||
@objc(VDSPagination)
|
@objc(VDSPagination)
|
||||||
open class Pagination: View {
|
open class Pagination: View {
|
||||||
|
|
||||||
open var onPageDidSelect: ((Int) -> Void)?
|
//--------------------------------------------------
|
||||||
|
// MARK: - Private Properties
|
||||||
public let previousButton: PaginationButton = .init(type: .previous)
|
//--------------------------------------------------
|
||||||
public let nextButton: PaginationButton = .init(type: .next)
|
///Collectionview width anchor
|
||||||
|
private var collectionViewWidthAnchor: NSLayoutConstraint?
|
||||||
public var total: Int = 0 {
|
///Selected page index
|
||||||
didSet {
|
private var _selectedPageIndex: Int = 0
|
||||||
previousButton.isHidden = true
|
///Custom flow layout defined for the Pagination
|
||||||
nextButton.isHidden = total <= 1
|
private let flowLayout = PaginationFlowLayout()
|
||||||
_selectedPage = 0
|
///A root view for the pagination
|
||||||
setNeedsUpdate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public var selectedPage: Int {
|
|
||||||
set {
|
|
||||||
if newValue >= total {
|
|
||||||
_selectedPage = total - 1
|
|
||||||
} else if newValue < 0 {
|
|
||||||
_selectedPage = 0
|
|
||||||
} else {
|
|
||||||
_selectedPage = max(newValue - 1, 0)
|
|
||||||
}
|
|
||||||
setNeedsUpdate()
|
|
||||||
updateSelection()
|
|
||||||
}
|
|
||||||
get {
|
|
||||||
_selectedPage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var _selectedPage: Int = 0
|
|
||||||
private let pageItemCellSize: CGSize = .init(width: 20, height: 16)
|
|
||||||
private let spacingBetweenCell: CGFloat = VDSLayout.Spacing.space1X.value
|
|
||||||
|
|
||||||
private let containerView: View = View().with {
|
private let containerView: View = View().with {
|
||||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||||
}
|
}
|
||||||
|
///Collectionview to render pagination indexes
|
||||||
private lazy var flowLayout: UICollectionViewFlowLayout = {
|
|
||||||
let layout = UICollectionViewFlowLayout()
|
|
||||||
layout.scrollDirection = .horizontal
|
|
||||||
layout.minimumInteritemSpacing = spacingBetweenCell
|
|
||||||
layout.minimumLineSpacing = spacingBetweenCell
|
|
||||||
layout.sectionInset = .zero
|
|
||||||
layout.estimatedItemSize = pageItemCellSize
|
|
||||||
return layout
|
|
||||||
}()
|
|
||||||
|
|
||||||
private lazy var collectionView: UICollectionView = {
|
private lazy var collectionView: UICollectionView = {
|
||||||
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
|
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)
|
||||||
collectionView.isScrollEnabled = false
|
collectionView.isScrollEnabled = false
|
||||||
@ -74,6 +40,48 @@ open class Pagination: View {
|
|||||||
return collectionView
|
return collectionView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Public Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
///Previous button to select previous page
|
||||||
|
public let previousButton: PaginationButton = .init(type: .previous)
|
||||||
|
///Next button to select next page
|
||||||
|
public let nextButton: PaginationButton = .init(type: .next)
|
||||||
|
/// A callback when the page changes. Passes parameters (selectedPage).
|
||||||
|
public var onPageDidSelect: ((Int) -> Void)?
|
||||||
|
/// Total number of pages, allows limit ranging from 0 to 9999.
|
||||||
|
@Clamping(range: 0...9999)
|
||||||
|
public var total: Int {
|
||||||
|
didSet {
|
||||||
|
previousButton.isHidden = true
|
||||||
|
nextButton.isHidden = total <= 1
|
||||||
|
_selectedPageIndex = 0
|
||||||
|
setNeedsUpdate()
|
||||||
|
updateSelection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
///Selected active page number and clips to total pages if selected index is greater than the total pages.
|
||||||
|
public var selectedPage: Int {
|
||||||
|
set {
|
||||||
|
if newValue >= total {
|
||||||
|
_selectedPageIndex = total - 1
|
||||||
|
} else if newValue < 0 {
|
||||||
|
_selectedPageIndex = 0
|
||||||
|
} else {
|
||||||
|
_selectedPageIndex = max(newValue - 1, 0)
|
||||||
|
}
|
||||||
|
setNeedsUpdate()
|
||||||
|
updateSelection()
|
||||||
|
}
|
||||||
|
get {
|
||||||
|
_selectedPageIndex + 1 //Returns selected page value not index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Overrides
|
||||||
|
//--------------------------------------------------
|
||||||
|
/// Executed on initialization for this View.
|
||||||
open override func initialSetup() {
|
open override func initialSetup() {
|
||||||
super.initialSetup()
|
super.initialSetup()
|
||||||
|
|
||||||
@ -94,56 +102,100 @@ open class Pagination: View {
|
|||||||
collectionView.centerYAnchor.constraint(equalTo: centerYAnchor).activate()
|
collectionView.centerYAnchor.constraint(equalTo: centerYAnchor).activate()
|
||||||
collectionView.centerXAnchor.constraint(equalTo: centerXAnchor).activate()
|
collectionView.centerXAnchor.constraint(equalTo: centerXAnchor).activate()
|
||||||
collectionView.trailingAnchor.constraint(greaterThanOrEqualTo: nextButton.leadingAnchor).activate()
|
collectionView.trailingAnchor.constraint(greaterThanOrEqualTo: nextButton.leadingAnchor).activate()
|
||||||
collectionView.widthAnchor.constraint(equalToConstant: 92).activate()
|
collectionViewWidthAnchor = collectionView.widthAnchor.constraint(equalToConstant: 92)
|
||||||
|
collectionViewWidthAnchor?.activate()
|
||||||
|
|
||||||
nextButton
|
nextButton
|
||||||
.pinTop()
|
.pinTop()
|
||||||
.pinBottom()
|
.pinBottom()
|
||||||
.pinTrailing()
|
.pinTrailing()
|
||||||
|
|
||||||
nextButton.onClick = onbuttonTapped
|
nextButton.onClick = onbuttonTapped
|
||||||
previousButton.onClick = onbuttonTapped
|
previousButton.onClick = onbuttonTapped
|
||||||
previousButton.isHidden = true
|
previousButton.isHidden = true
|
||||||
|
|
||||||
|
flowLayout.$collectionViewWidth
|
||||||
|
.receive(on: RunLoop.main)
|
||||||
|
.sink { [weak self] value in
|
||||||
|
self?.collectionViewWidthAnchor?.constant = value
|
||||||
|
}.store(in: &subscribers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
open override func updateView() {
|
open override func updateView() {
|
||||||
super.updateView()
|
super.updateView()
|
||||||
|
|
||||||
nextButton.surface = surface
|
nextButton.surface = surface
|
||||||
previousButton.surface = surface
|
previousButton.surface = surface
|
||||||
collectionView.reloadData()
|
collectionView.reloadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Private Methods
|
||||||
|
//--------------------------------------------------
|
||||||
|
///When previous/next button is tapped
|
||||||
private func onbuttonTapped(_ sender: UIButton) {
|
private func onbuttonTapped(_ sender: UIButton) {
|
||||||
let isNextAction = sender == nextButton
|
let isNextAction = sender == nextButton
|
||||||
if isNextAction {
|
_selectedPageIndex = if isNextAction { _selectedPageIndex + 1 } else { _selectedPageIndex - 1 }
|
||||||
_selectedPage += 1
|
|
||||||
} else {
|
|
||||||
_selectedPage -= 1
|
|
||||||
}
|
|
||||||
updateSelection()
|
updateSelection()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Refreshing the UI based on the selected page
|
||||||
private func updateSelection() {
|
private func updateSelection() {
|
||||||
let indexPath = IndexPath(row: selectedPage, section: 0)
|
guard _selectedPageIndex < total else { return }
|
||||||
collectionView.scrollToItem(at: indexPath, at: .left, animated: false)
|
collectionView.scrollToItem(at: IndexPath(row: _selectedPageIndex, section: 0), at: .left, animated: false)
|
||||||
previousButton.isHidden = selectedPage == 0
|
previousButton.isHidden = _selectedPageIndex == 0
|
||||||
nextButton.isHidden = selectedPage == total - 1
|
nextButton.isHidden = _selectedPageIndex == total - 1
|
||||||
collectionView.reloadData()
|
collectionView.reloadData()
|
||||||
|
verifyIfMaxDigitChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
///Identifying if there is any change in the digits of upcoming page
|
||||||
|
func verifyIfMaxDigitChanged() {
|
||||||
|
let upperLimitPage = _selectedPageIndex + flowLayout.maxNumberOfColumns
|
||||||
|
let upperLimitDigits = upperLimitPage.digitCount //future value digits
|
||||||
|
switch (flowLayout.numberOfColumns, upperLimitDigits) {
|
||||||
|
case (_, 3), (_, 4):
|
||||||
|
flowLayout.numberOfColumns = 3
|
||||||
|
default:
|
||||||
|
flowLayout.numberOfColumns = 4
|
||||||
|
}
|
||||||
|
if upperLimitDigits != flowLayout.upperLimitDigits {
|
||||||
|
flowLayout.upperLimitDigits = upperLimitDigits
|
||||||
|
flowLayout.invalidateLayout()
|
||||||
|
collectionView.reloadData()
|
||||||
|
collectionView.scrollToItem(at: IndexPath(row: self._selectedPageIndex, section: 0), at: .left, animated: false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Pagination: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
|
extension Pagination: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - UICollectionView Delegate & Datasource
|
||||||
|
//--------------------------------------------------
|
||||||
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { total }
|
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { total }
|
||||||
|
|
||||||
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PaginationCellItem.identifier, for: indexPath) as? PaginationCellItem else { return UICollectionViewCell() }
|
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PaginationCellItem.identifier, for: indexPath) as? PaginationCellItem else { return UICollectionViewCell() }
|
||||||
cell.update(selectedPage, currentIndex: indexPath.row, surface: surface)
|
cell.update(_selectedPageIndex, currentIndex: indexPath.row, surface: surface)
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
_selectedPage = indexPath.row
|
_selectedPageIndex = indexPath.row
|
||||||
updateSelection()
|
updateSelection()
|
||||||
onPageDidSelect?(indexPath.row)
|
onPageDidSelect?(selectedPage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate extension Int {
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Extension on Int to identify number of digits in given number.
|
||||||
|
//--------------------------------------------------
|
||||||
|
var digitCount: Int {
|
||||||
|
numberOfDigits(in: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func numberOfDigits(in number: Int) -> Int {
|
||||||
|
number < 10 && number >= 0 ? 1 : 1 + numberOfDigits(in: number/10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,13 +8,20 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
import VDSColorTokens
|
import VDSColorTokens
|
||||||
|
|
||||||
|
///This is customised button for Pagination view
|
||||||
open class PaginationButton: ButtonBase {
|
open class PaginationButton: ButtonBase {
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Private Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
/// Type of the PaginationButton
|
||||||
|
private var type: Type = .next
|
||||||
|
/// Button tint color configuration
|
||||||
private let buttonTintColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite)
|
private let buttonTintColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite)
|
||||||
|
/// Button title color configuration
|
||||||
private let buttonTextColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite)
|
private let buttonTextColorConfiguration = SurfaceColorConfiguration(VDSColor.paletteBlack, VDSColor.paletteWhite)
|
||||||
|
/// Button configuration for iOS 15+
|
||||||
@available(iOS 15.0, *)
|
@available(iOS 15.0, *)
|
||||||
var buttonConfiguration: Button.Configuration {
|
private var buttonConfiguration: Button.Configuration {
|
||||||
var configuration = ButtonBase.Configuration.plain()
|
var configuration = ButtonBase.Configuration.plain()
|
||||||
configuration.imagePadding = VDSLayout.Spacing.space2X.value
|
configuration.imagePadding = VDSLayout.Spacing.space2X.value
|
||||||
configuration.imagePlacement = type == .next ? .trailing : .leading
|
configuration.imagePlacement = type == .next ? .trailing : .leading
|
||||||
@ -23,12 +30,17 @@ open class PaginationButton: ButtonBase {
|
|||||||
return configuration
|
return configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Public Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
/// TextStyle used on the titleLabel.
|
||||||
open override var textStyle: TextStyle { TextStyle.boldBodySmall }
|
open override var textStyle: TextStyle { TextStyle.boldBodySmall }
|
||||||
|
/// UIColor used on the titleLabel text.
|
||||||
open override var textColor: UIColor { buttonTextColorConfiguration.getColor(surface) }
|
open override var textColor: UIColor { buttonTextColorConfiguration.getColor(surface) }
|
||||||
|
|
||||||
private var type: Type = .next
|
//--------------------------------------------------
|
||||||
|
// MARK: - Initializers
|
||||||
|
//--------------------------------------------------
|
||||||
init(type: Type) {
|
init(type: Type) {
|
||||||
self.type = type
|
self.type = type
|
||||||
super.init()
|
super.init()
|
||||||
@ -42,6 +54,10 @@ open class PaginationButton: ButtonBase {
|
|||||||
super.init(coder: coder)
|
super.init(coder: coder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Overrides
|
||||||
|
//--------------------------------------------------
|
||||||
|
/// Executed on initialization for this View.
|
||||||
open override func initialSetup() {
|
open override func initialSetup() {
|
||||||
super.initialSetup()
|
super.initialSetup()
|
||||||
if #available(iOS 15.0, *) {
|
if #available(iOS 15.0, *) {
|
||||||
@ -53,6 +69,7 @@ open class PaginationButton: ButtonBase {
|
|||||||
contentHorizontalAlignment = type == .next ? .trailing : .leading
|
contentHorizontalAlignment = type == .next ? .trailing : .leading
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used to make changes to the View based off a change events or from local properties.
|
||||||
open override func updateView() {
|
open override func updateView() {
|
||||||
text = type.title
|
text = type.title
|
||||||
setImage(type.image, for: .normal)
|
setImage(type.image, for: .normal)
|
||||||
@ -62,7 +79,9 @@ open class PaginationButton: ButtonBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension PaginationButton {
|
extension PaginationButton {
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Enum to configure PaginationButton
|
||||||
|
//--------------------------------------------------
|
||||||
enum `Type` {
|
enum `Type` {
|
||||||
case previous, next
|
case previous, next
|
||||||
|
|
||||||
@ -74,7 +93,7 @@ extension PaginationButton {
|
|||||||
"Previous"
|
"Previous"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
///Image for the configuration type
|
||||||
var image: UIImage? {
|
var image: UIImage? {
|
||||||
switch self {
|
switch self {
|
||||||
case .previous:
|
case .previous:
|
||||||
|
|||||||
@ -8,18 +8,27 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
import VDSColorTokens
|
import VDSColorTokens
|
||||||
|
|
||||||
|
///This is customised view for Pagination cell item
|
||||||
final class PaginationCellItem: UICollectionViewCell {
|
final class PaginationCellItem: UICollectionViewCell {
|
||||||
|
|
||||||
|
///Identifier for the PaginationCellItem
|
||||||
static let identifier: String = String(describing: PaginationCellItem.self)
|
static let identifier: String = String(describing: PaginationCellItem.self)
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Private Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
///Text color configuration for the element
|
||||||
private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark)
|
private let textColorConfiguration = SurfaceColorConfiguration(VDSColor.elementsPrimaryOnlight, VDSColor.elementsPrimaryOndark)
|
||||||
|
///Pagination index label
|
||||||
private var indexLabel: Label = Label().with {
|
private var indexLabel: Label = Label().with {
|
||||||
$0.translatesAutoresizingMaskIntoConstraints = false
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||||
$0.textAlignment = .center
|
$0.textAlignment = .center
|
||||||
$0.numberOfLines = 1
|
$0.numberOfLines = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Initializers
|
||||||
|
//--------------------------------------------------
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
setUp()
|
setUp()
|
||||||
@ -30,6 +39,7 @@ final class PaginationCellItem: UICollectionViewCell {
|
|||||||
setUp()
|
setUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Configuring the cell with default setup
|
||||||
private func setUp() {
|
private func setUp() {
|
||||||
let containerView = View()
|
let containerView = View()
|
||||||
containerView.translatesAutoresizingMaskIntoConstraints = false
|
containerView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
@ -43,6 +53,7 @@ final class PaginationCellItem: UICollectionViewCell {
|
|||||||
indexLabel.backgroundColor = .clear
|
indexLabel.backgroundColor = .clear
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Updating UI based on selected index, current index along with surface
|
||||||
func update(_ selectedIndex: Int, currentIndex: Int, surface: Surface) {
|
func update(_ selectedIndex: Int, currentIndex: Int, surface: Surface) {
|
||||||
indexLabel.textStyle = selectedIndex == currentIndex ? .boldBodySmall : .bodySmall
|
indexLabel.textStyle = selectedIndex == currentIndex ? .boldBodySmall : .bodySmall
|
||||||
indexLabel.text = "\(currentIndex + 1)"
|
indexLabel.text = "\(currentIndex + 1)"
|
||||||
|
|||||||
91
VDS/Components/Pagination/PaginationFlowLayout.swift
Normal file
91
VDS/Components/Pagination/PaginationFlowLayout.swift
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
//
|
||||||
|
// PaginationFlowLayout.swift
|
||||||
|
// VDS
|
||||||
|
//
|
||||||
|
// Created by Bandaru, Krishna Kishore on 06/03/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
///Customised flow layout for Pagination view
|
||||||
|
final class PaginationFlowLayout : UICollectionViewLayout {
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Private Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
///Spacing between the pagination cells
|
||||||
|
private let spacingBetweenCell: CGFloat = VDSLayout.Spacing.space1X.value
|
||||||
|
///Pre-defined sizes of the pagination cell based on number of digits.
|
||||||
|
private var upperLimitSize: CGSize {
|
||||||
|
switch upperLimitDigits {
|
||||||
|
case 3: .init(width: 28, height: 16)
|
||||||
|
case 4: .init(width: 34, height: 16)
|
||||||
|
default: .init(width: 20, height: 16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
///Property to store the defined layout attributes.
|
||||||
|
private var itemCache : [UICollectionViewLayoutAttributes] = []
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Internal Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
///Maximum number of page indexes shown on UI
|
||||||
|
let maxNumberOfColumns: Int = 4
|
||||||
|
///Number of digits of the maximum page index.
|
||||||
|
var upperLimitDigits: Int = 0
|
||||||
|
///Number of page indexes shown on UI.
|
||||||
|
var numberOfColumns: Int = 4
|
||||||
|
///A property that publishes when there is change in collection view width.
|
||||||
|
@Published var collectionViewWidth: CGFloat = 0
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Overrides
|
||||||
|
//--------------------------------------------------
|
||||||
|
///Preparing the layout collection attributes for pagination and updating the collectionview width.
|
||||||
|
override func prepare() {
|
||||||
|
|
||||||
|
guard let collectionView else { return }
|
||||||
|
|
||||||
|
itemCache.removeAll()
|
||||||
|
var xPos : CGFloat = 0
|
||||||
|
for item in 0..<collectionView.numberOfItems(inSection: 0) {
|
||||||
|
let indexPath = IndexPath(item: item, section: 0)
|
||||||
|
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
|
||||||
|
attributes.frame = .init(origin: .init(x: xPos, y: 0), size: upperLimitSize)
|
||||||
|
xPos += (spacingBetweenCell + upperLimitSize.width)
|
||||||
|
itemCache.append(attributes)
|
||||||
|
}
|
||||||
|
guard !itemCache.isEmpty else { return }
|
||||||
|
var rightMostVisibleItem: UICollectionViewLayoutAttributes?
|
||||||
|
if numberOfColumns < itemCache.count {
|
||||||
|
rightMostVisibleItem = itemCache[max(numberOfColumns - 1, 0)]
|
||||||
|
} else {
|
||||||
|
rightMostVisibleItem = itemCache.last
|
||||||
|
}
|
||||||
|
if let rightMostVisibleItem {
|
||||||
|
collectionViewWidth = rightMostVisibleItem.frame.minX + rightMostVisibleItem.frame.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///This will return the layout attributes for the elements in the defined rect
|
||||||
|
override func layoutAttributesForElements(in rect: CGRect)-> [UICollectionViewLayoutAttributes]? {
|
||||||
|
var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = []
|
||||||
|
for attributes in itemCache {
|
||||||
|
if attributes.frame.intersects(rect) {
|
||||||
|
visibleLayoutAttributes.append(attributes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return visibleLayoutAttributes
|
||||||
|
}
|
||||||
|
|
||||||
|
///This will return the layout attributes at particular indexPath
|
||||||
|
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
|
||||||
|
return itemCache[indexPath.row]
|
||||||
|
}
|
||||||
|
|
||||||
|
///Returns the collectionview content size
|
||||||
|
override var collectionViewContentSize: CGSize {
|
||||||
|
guard let lastAttribute = itemCache.last else { return super.collectionViewContentSize }
|
||||||
|
return .init(width: lastAttribute.frame.width + lastAttribute.frame.origin.x, height: 16)
|
||||||
|
}
|
||||||
|
}
|
||||||
24
VDS/Utilities/Clamping.swift
Normal file
24
VDS/Utilities/Clamping.swift
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// Clamping.swift
|
||||||
|
// VDS
|
||||||
|
//
|
||||||
|
// Created by Bandaru, Krishna Kishore on 05/03/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
@propertyWrapper public struct Clamping<Value: Comparable> {
|
||||||
|
|
||||||
|
private var value: Value
|
||||||
|
private let range: ClosedRange<Value>
|
||||||
|
|
||||||
|
public init(range: ClosedRange<Value>) {
|
||||||
|
self.value = range.lowerBound
|
||||||
|
self.range = range
|
||||||
|
}
|
||||||
|
|
||||||
|
public var wrappedValue: Value {
|
||||||
|
get { value }
|
||||||
|
set { value = min(max(range.lowerBound, newValue), range.upperBound) }
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user