Die Grundidee ist eine benutzerdefinierte UICollectionViewLayout
zu erstellen, die implementiert:
collectionViewContentSize
, das heißt, was die Größe des Voll, scrollbaren contentSize
der Sammlung Ansicht ist;
layoutAttributesForItem(at indexPath:)
, d.h. was sind die Schlüsselattribute (nämlich center
und size
) einer bestimmten Zelle; und
layoutAttributesForElements(in rect:)
, das heißt, was die wichtigsten Attribute für Zellen sind, die in dieser speziellen rect
fallen ... wird diese verwendet werden, um festzustellen, welche Zellen sind zu einem bestimmten Zeitpunkt sowie die Attribute für diese Zellen ; Dies ist im Grunde ein Array der Attribute für die Zellen aus der vorherigen Methode, gefiltert auf nur diejenigen innerhalb der rect
.
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)
}
}
Das ergibt::
So in Swift 3 Sie so etwas wie tun könnte
ist klar, diese anpassen, wie Sie sehen, passen, aber es zeigt die Grundidee.
In meiner ursprünglichen Antwort, unten, nahm ich an Sie, diese Zellen in einem Kreis sehen wollten, wie es in der WWDC 2012 Video Advanced Collection Views and Building Custom Layouts (ca. 40+ Minuten im Video) gezeigt. Siehe das unten.
Zum Beispiel in Swift 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))
}
}
}
Dann setzen Sie einfach die collectionViewLayout
und dann die Standard UICollectionViewDataSource
Methoden implementieren.
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
}
}
Das ergibt:
Siehe https://github.com/robertmryan/CircularCollectionView für Probe.
Hinweis, Sie erwähnten, dass Sie „kein Abstand zwischen den Kreisen“ wollen, so justieren Sie einfach die radius
und/oder itemSize
dementsprechend das gewünschte Layout zu erhalten.
Sie müssen Ihre eigenen 'UICollectionViewLayout' erstellen," sagen ", was ist der Rahmen für Ihre Zellen. – Larme