Fixed bugs, added comments and created new flow layout

This commit is contained in:
Krishna Kishore Bandaru 2024-03-06 21:25:18 +05:30
parent 8ed924c805
commit cd85748a11
6 changed files with 276 additions and 71 deletions

View File

@ -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 */,

View File

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

View File

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

View File

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

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

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