Provides an iterator that allows for simple traversing over an dataSource's IndexPath's – including support for a header and footer per section.
public struct IndexPathIterator: IteratorProtocol { | |
public let indexPaths: [IndexPath] | |
private var index: Array<IndexPath>.Index | |
/// Creates a new iterator for the specified indexPaths | |
/// | |
/// - Parameter indexPaths: The indexPaths to iterate over | |
public init(indexPaths: Set<IndexPath>) { | |
self.indexPaths = indexPaths.sorted() | |
self.index = self.indexPaths.startIndex | |
} | |
/// Returns the next element. If there are no more elements, this returns nil. | |
/// | |
/// - Returns: Returns the next element. If the collection is empty, this returns nil | |
public mutating func next() -> Array<IndexPath>.Element? { | |
guard !indexPaths.isEmpty, index != indexPaths.endIndex else { return nil } | |
let result = indexPaths[index] | |
indexPaths.formIndex(after: &index) | |
return result | |
} | |
} |
public extension IndexPath { | |
enum Kind: Int { | |
case header | |
case footer | |
case item | |
} | |
struct KindMask: RawRepresentable, OptionSet { | |
public let rawValue: Int | |
public init(rawValue: Int) { | |
self.rawValue = rawValue | |
} | |
public static let header = KindMask(rawValue: 1 << Kind.header.rawValue) | |
public static let footer = KindMask(rawValue: 1 << Kind.footer.rawValue) | |
public static let item = KindMask(rawValue: 1 << Kind.item.rawValue) | |
public func contains(_ member: Kind) -> Bool { | |
if member == .header && contains(KindMask.header) { return true } | |
if member == .footer && contains(KindMask.footer) { return true } | |
if member == .item && contains(KindMask.item) { return true } | |
return false | |
} | |
} | |
init(kind: Kind, item: Int, section: Int) { | |
switch kind { | |
case .header: | |
self = [section, -1, 0] | |
case .footer: | |
self = [section, item, 1] | |
case .item: | |
self = IndexPath(item: item, section: section) | |
} | |
} | |
var kind: Kind { | |
guard count == 3 else { return .item } | |
return self[2] == 0 ? .header : .footer | |
} | |
var section: Int { | |
return self[0] | |
} | |
var item: Int { | |
return self[1] | |
} | |
} |
import Foundation | |
public extension Collection where Element == IndexPath { | |
/// Returns indexPaths of the specified kinds, that are in the given section | |
/// | |
/// - Parameters: | |
/// - kinds: The kinds to match against | |
/// - section: The section to match against | |
/// - Returns: An array of indexPaths, empty if no matches | |
func filter(include kinds: IndexPath.KindMask, inSections sections: Int...) -> [SubSequence.Element] { | |
return filter { sections.contains($0.section) && kinds.contains($0.kind) } | |
} | |
/// Returns indexPaths of the specified kinds, that are in the given section | |
/// | |
/// - Parameters: | |
/// - kinds: The kinds to match against | |
/// - section: The section to match against | |
/// - Returns: An array of indexPaths, empty if no matches | |
func filter(include kinds: IndexPath.KindMask, inSections sections: [Int]) -> [SubSequence.Element] { | |
return filter { sections.contains($0.section) && kinds.contains($0.kind) } | |
} | |
/// Returns indexPaths of the specified kinds, that are in the specified sections | |
/// | |
/// - Parameters: | |
/// - kinds: The kinds to match against | |
/// - range: The range of sections to match against | |
/// - Returns: An array of indexPaths, empty if no matches | |
func filter(include kinds: IndexPath.KindMask, inSections range: Range<Int>) -> [SubSequence.Element] { | |
return filter { range.contains($0.section) && kinds.contains($0.kind) } | |
} | |
/// Returns indexPaths of the specified kinds, that are in the specified sections | |
/// | |
/// - Parameters: | |
/// - kinds: The kinds to match against | |
/// - range: The range of sections to match against | |
/// - Returns: An array of indexPaths, empty if no matches | |
func filter(include kinds: IndexPath.KindMask, inSections range: ClosedRange<Int>) -> [SubSequence.Element] { | |
return filter { range.contains($0.section) && kinds.contains($0.kind) } | |
} | |
/// Returns indexPaths of the specified kinds, in any section | |
/// | |
/// - Parameter kinds: The kinds to match against | |
/// - Returns: an array of indexPaths, empty if no matches | |
func filter(include kinds: IndexPath.KindMask) -> [SubSequence.Element] { | |
return filter { kinds.contains($0.kind) } | |
} | |
/// Returns all header indexPaths only | |
var headers: [SubSequence.Element] { | |
return filter(include: .header) | |
} | |
/// Returns all footer indexPaths only | |
var footers: [SubSequence.Element] { | |
return filter(include: .footer) | |
} | |
/// Returns all item indexPaths only | |
var items: [SubSequence.Element] { | |
return filter(include: .item) | |
} | |
} |
public extension Set where Element == IndexPath { | |
init(from view: UICollectionView) { | |
var indexPaths: Set<IndexPath> = [] | |
for section in 0..<(view.dataSource?.numberOfSections?(in: view) ?? 0) { | |
indexPaths.insert(IndexPath(kind: .header, item: -1, section: section)) | |
let itemCount = view.dataSource?.collectionView(view, numberOfItemsInSection: section) ?? 0 | |
for item in 0..<itemCount { | |
indexPaths.insert(IndexPath(item: item, section: section)) | |
} | |
indexPaths.insert(IndexPath(kind: .footer, item: itemCount, section: section)) | |
} | |
self.init(indexPaths) | |
} | |
init(from view: UITableView) { | |
var indexPaths: Set<IndexPath> = [] | |
for section in 0..<(view.dataSource?.numberOfSections?(in: view) ?? 0) { | |
indexPaths.insert(IndexPath(kind: .header, item: -1, section: section)) | |
let itemCount = view.dataSource?.tableView(view, numberOfRowsInSection: section) ?? 0 | |
for item in 0..<itemCount { | |
indexPaths.insert(IndexPath(item: item, section: section)) | |
} | |
indexPaths.insert(IndexPath(kind: .footer, item: itemCount, section: section)) | |
} | |
self.init(indexPaths) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
Example: