![](/img/trans.png)
[英]How to implement static variables of associated types in protocol extensions in Swift 5.6?
[英]Swift protocol associated types and inheritance contraints
我想用UICollectionViewController
構建一個iOS應用,該應用總是每行具有相同數量的單元格。 因為我不想讓UICollectionViewController
處理太多事情,所以我重構了代碼並實現了有趣的事情,例如protocol associatedtype
和泛型。 現在,我的應用程序由4個不同的.swift
文件組成。
CustomFlowLayout.swift
CustomFlowLayout
是一個簡單的子類UICollectionViewFlowLayout
,讓我們來設置其minimumInteritemSpacing
, minimumLineSpacing
和sectionInset
依賴注入得益於便利的初始化與性能。
import UIKit
class CustomFlowLayout: UICollectionViewFlowLayout {
convenience init(minimumInteritemSpacing: CGFloat = 0,
minimumLineSpacing: CGFloat = 0,
sectionInset: UIEdgeInsets = .zero) {
self.init()
self.minimumInteritemSpacing = minimumInteritemSpacing
self.minimumLineSpacing = minimumLineSpacing
self.sectionInset = sectionInset
}
override init() {
super.init()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
ColumnDataSource.swift
ColumnDataSource
是NSObject
的子類,符合UICollectionViewDataSource
, UICollectionViewDelegate
和UICollectionViewDelegateFlowLayout
。 它實現collectionView(_:layout:sizeForItemAt:)
以便每行顯示正確的UICollectionViewCell
數量。 還要注意, ColumnDataSource
是一個通用類,要求我們在初始化時將其傳遞給類型參數。
import UIKit
class ColumnDataSource<FlowLayoutType: UICollectionViewFlowLayout>: NSObject, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
let cellsPerRow: Int
init(cellsPerRow: Int) {
self.cellsPerRow = cellsPerRow
super.init()
}
// MARK: - UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
}
// MARK: - UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let flowLayout = collectionView.collectionViewLayout as! FlowLayoutType
let marginsAndInsets = flowLayout.sectionInset.left + flowLayout.sectionInset.right + flowLayout.minimumInteritemSpacing * (CGFloat(cellsPerRow) - 1)
let itemWidth = (collectionView.bounds.size.width - marginsAndInsets) / CGFloat(cellsPerRow)
return CGSize(width: itemWidth, height: itemWidth)
}
}
ColumnFlowLayoutable.swift
ColumnFlowLayoutable
協議的目的是確保符合該協議的任何類都具有columnDataSource
和customFlowLayout
屬性,其中columnDataSource
類型的type參數與customFlowLayout
類型相匹配。
import UIKit
protocol ColumnFlowLayoutable {
associatedtype FlowLayoutType: UICollectionViewFlowLayout
var columnDataSource: ColumnDataSource<FlowLayoutType> { get }
var customFlowLayout: FlowLayoutType { get }
}
CollectionViewController.swift
CollectionViewController
是UICollectionViewController
的子類,它符合ColumnFlowLayoutable
協議。 它還實現了viewWillTransition(to:with:)
以便處理容器大小的更改。
import UIKit
class CollectionViewController: UICollectionViewController, ColumnFlowLayoutable {
let columnDataSource = ColumnDataSource<CustomFlowLayout>(cellsPerRow: 2)
let customFlowLayout = {
CustomFlowLayout(minimumInteritemSpacing: $0, minimumLineSpacing: $0, sectionInset: UIEdgeInsets(top: $0, left: $0, bottom: $0, right: $0))
}(10)
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.collectionViewLayout = customFlowLayout
collectionView?.dataSource = columnDataSource
collectionView?.delegate = columnDataSource
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
collectionView?.collectionViewLayout.invalidateLayout()
}
}
可以在以下Github存儲庫中找到完整的項目: CollectionViewColumnsProtocol 。
此代碼可以正常工作。 我可以將其與CustomFlowLayout
子類CustomFlowLayout
,並且仍然可以使用。 但是,我不能將其與ColumnDataSource
子類一起使用 。
如果我嘗試通過使用CollectionViewController
的ColumnDataSource
的子類(例如, class SubColumnDataSource: ColumnDataSource<CustomFlowLayout>
)來構建項目,則Xcode會引發以下構建時錯誤消息:
類型“ CollectionViewController”不符合協議“ ColumnFlowLayoutable”
為了允許CollectionViewController
使用ColumnDataSource
子類,我必須在ColumnFlowLayoutable
協議中進行哪些更改?
創建具有與您的布局類型關聯的類型的數據源協議:
protocol ColumnDataSourceProtocol: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
associatedtype Layout: UICollectionViewFlowLayout
}
使您的DataSource基類符合此協議。 如果編譯器無法推斷出關聯類型,則可能需要添加一個類型typealias
:
class ColumnDataSource<FlowLayoutType: UICollectionViewFlowLayout>: NSObject, ColumnDataSourceProtocol {
typealias Layout = FlowLayoutType
// the rest stays the same
}
調整ColumnFlowLayoutable
以關聯數據源類型而不是布局類型。 將其約束到ColumnDataSourceProtocol
,可以訪問其關聯的Layout
類型:
protocol ColumnFlowLayoutable {
associatedtype DataSource: ColumnDataSourceProtocol
var columnDataSource: DataSource { get }
var customFlowLayout: DataSource.Layout { get }
}
現在,您可以將ColumnDataSource
子類化:
class DataSource: ColumnDataSource<CustomFlowLayout> { /* ... */ }
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.