的基本想法是創建一個自定義UICollectionViewLayout
實現:
collectionViewContentSize
,即什麼是集合視圖的完整,滾動contentSize
的大小;
layoutAttributesForItem(at indexPath:)
,即什麼是特定小區的關鍵屬性(即center
和size
);和
layoutAttributesForElements(in rect:)
,即什麼是細胞屬於這一特定rect
內的關鍵屬性...這將被用來識別哪些細胞在任何給定時間點,以及這些單元格屬性可見;這基本上是來自上一個方法的單元格屬性的一個數組,被過濾爲rect
中的單元格的屬性。
因此,斯威夫特3,你可以這樣做:
class AlternatingGridLayout: UICollectionViewLayout {
private var itemSize: CGSize!
private var numberOfItems: Int!
private var itemsPerRow: Int!
private var rows: Int!
private var circleViewCenterOffset: CGPoint!
private var radiusOfCircleViews: CGFloat!
private var insets: UIEdgeInsets!
override func prepare() {
super.prepare()
guard let collectionView = collectionView else { return }
radiusOfCircleViews = CGFloat(40.0)
itemSize = CGSize(width: radiusOfCircleViews * 2, height: radiusOfCircleViews * 2)
circleViewCenterOffset = CGPoint(x: 2 * radiusOfCircleViews * cos(.pi/3),
y: 2 * radiusOfCircleViews * sin(.pi/3))
numberOfItems = collectionView.numberOfItems(inSection: 0)
itemsPerRow = Int(floor((collectionView.bounds.width - radiusOfCircleViews)/CGFloat(2 * radiusOfCircleViews)) + 0.5)
rows = (numberOfItems - 1)/itemsPerRow + 1
let excess = collectionView.bounds.width - (CGFloat(itemsPerRow) * radiusOfCircleViews * 2 + circleViewCenterOffset.x)
insets = UIEdgeInsets(top: 10, left: excess/2, bottom: 10, right: excess/2)
}
override var collectionViewContentSize: CGSize {
return CGSize(width: collectionView!.bounds.width,
height: 2 * radiusOfCircleViews + CGFloat(rows - 1) * circleViewCenterOffset.y + insets.top + insets.bottom)
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.center = centerForItem(at: indexPath)
attributes.size = itemSize
return attributes
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return (0 ..< collectionView!.numberOfItems(inSection: 0)).map { IndexPath(item: $0, section: 0) }
.filter { rect.intersects(rectForItem(at: $0)) }
.flatMap { self.layoutAttributesForItem(at: $0) } // `compactMap` in Xcode 9.3
}
private func centerForItem(at indexPath: IndexPath) -> CGPoint {
let row = indexPath.item/itemsPerRow
let col = indexPath.item - row * itemsPerRow
var x: CGFloat = radiusOfCircleViews + CGFloat(col) * (radiusOfCircleViews * 2)
let y: CGFloat = radiusOfCircleViews + CGFloat(row) * (circleViewCenterOffset.y)
if row % 2 == 0 {
x += circleViewCenterOffset.x
}
return CGPoint(x: x + insets.left, y: y + insets.top)
}
private func rectForItem(at indexPath: IndexPath) -> CGRect {
let center = centerForItem(at: indexPath)
return CGRect(x: center.x - radiusOfCircleViews, y: center.y - radiusOfCircleViews, width: radiusOfCircleViews * 2, height: radiusOfCircleViews * 2)
}
}
國債收益率:
顯然,自定義這個您認爲合適的,但它說明了基本的想法。
在我原來的答覆,下面,我以爲你想看看這些細胞轉了一圈,如圖WWDC 2012的視頻Advanced Collection Views and Building Custom Layouts(約40多分鐘到視頻)。見下面。
例如,在斯威夫特3:
class CircleLayout: UICollectionViewLayout {
private var center: CGPoint!
private var itemSize: CGSize!
private var radius: CGFloat!
private var numberOfItems: Int!
override func prepare() {
super.prepare()
guard let collectionView = collectionView else { return }
center = CGPoint(x: collectionView.bounds.midX, y: collectionView.bounds.midY)
let shortestAxisLength = min(collectionView.bounds.width, collectionView.bounds.height)
itemSize = CGSize(width: shortestAxisLength * 0.1, height: shortestAxisLength * 0.1)
radius = shortestAxisLength * 0.4
numberOfItems = collectionView.numberOfItems(inSection: 0)
}
override var collectionViewContentSize: CGSize {
return collectionView!.bounds.size
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
let angle = 2 * .pi * CGFloat(indexPath.item)/CGFloat(numberOfItems)
attributes.center = CGPoint(x: center.x + radius * cos(angle), y: center.y + radius * sin(angle))
attributes.size = itemSize
return attributes
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return (0 ..< collectionView!.numberOfItems(inSection: 0))
.flatMap { item -> UICollectionViewLayoutAttributes? in // `compactMap` in Xcode 9.3
self.layoutAttributesForItem(at: IndexPath(item: item, section: 0))
}
}
}
然後,你可以簡單地設置collectionViewLayout
,然後實現標準UICollectionViewDataSource
方法。
class ViewController: UICollectionViewController {
var numberOfCells = 10
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.collectionViewLayout = CircleLayout()
// just for giggles and grins, let's show the insertion of a cell
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.collectionView?.performBatchUpdates({
self.numberOfCells += 1
self.collectionView?.insertItems(at: [IndexPath(item: 0, section: 0)])
}, completion: nil)
}
}
}
// MARK: UICollectionViewDataSource
extension ViewController {
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return numberOfCells
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CircleCell", for: indexPath)
return cell
}
}
即產率:
參見https://github.com/robertmryan/CircularCollectionView樣品。
注意,你提到你想要的「圈子中間沒有間隔」,所以才調整radius
和/或itemSize
相應地得到你想要的佈局。
您需要創建自己的'UICollectionViewLayout',「告訴」您的單元格的框架是什麼。 – Larme