iOS UICollectionView:具有交替网格对齐的圆形视图的单元格
Posted
技术标签:
【中文标题】iOS UICollectionView:具有交替网格对齐的圆形视图的单元格【英文标题】:iOS UICollectionView: Cells with circular view in alternating grid alignment 【发布时间】:2017-01-24 19:19:29 【问题描述】:我正在尝试为圆形的自定义单元格实现UICollectionView
。现在,默认情况下,圆圈的对齐方式与普通方形单元格相同:顶部圆圈和底部圆圈位于同一垂直线上。如何将对齐方式更改为:顶部圆圈和其下方的两个圆圈形成等边三角形(顶部圆圈和底部圆圈的位置按半径长度移动)?如下:
from OOO
OOO
OOO
to O O O
O O O (no spacing among the circles)
O O O
【问题讨论】:
您需要创建自己的UICollectionViewLayout
,“告诉”您的单元格的框架是什么。
【参考方案1】:
基本思想是创建一个自定义的UICollectionViewLayout
,实现:
collectionViewContentSize
,即集合视图的完整可滚动contentSize
的大小;
layoutAttributesForItem(at indexPath:)
,即特定单元格的关键属性(即center
和size
)是什么;和
layoutAttributesForElements(in rect:)
,即属于该特定rect
的单元格的关键属性是什么...这将用于识别在任何给定时间点可见的单元格以及这些单元格的属性;这基本上是上一个方法中单元格的属性数组,过滤到仅在 rect
中的那些。
因此,在 Swift 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))
.compactMap self.layoutAttributesForItem(at: $0) // `flatMap` in Xcode versions before 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 多分钟)。见下文。
例如,在 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))
.compactMap item -> UICollectionViewLayoutAttributes? in // `flatMap` in Xcode versions prior to 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
以获得所需的布局。
【讨论】:
这很好,但你能告诉我如何让 collectionview 无休止地旋转,提前谢谢以上是关于iOS UICollectionView:具有交替网格对齐的圆形视图的单元格的主要内容,如果未能解决你的问题,请参考以下文章
具有单元格间距的 UICollectionView 存在滚动问题
具有可访问性的 UITableViewCell 中的 UICollectionView
具有自定义布局的 UICollectionView - 无法在情节提要中添加补充视图