iOS UICollectionView:具有交替网格alignment的圆形视图的单元格

我正在试图为UICollectionView的自定义单元格实现UICollectionView 。 现在默认情况下,圆形的排列方式与正常的方形单元相同:顶部圆和底部圆在同一条垂直线上。 我怎样才能改变这个alignment方式:上面的圆圈和下面的两个圆圈形成一个等边三angular形(顶部圆圈和底部圆圈的位置是按半径长度移动的)? 如下:

 from OOO OOO OOO to OOO OOO (no spacing among the circles) OOO 

基本的想法是创build一个自定义的UICollectionViewLayout实现:

  • collectionViewContentSize ,即集合视图的完整可滚动contentSize的大小是多less;

  • layoutAttributesForItem(at indexPath:) ,即特定单元的关键属性(即centersize )是什么; 和

  • layoutAttributesForElements(in rect:) ,也就是说,属于这个特定layoutAttributesForElements(in 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)) } .flatMap { self.layoutAttributesForItem(at: $0) } } 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 2012video高级集合视图和构build自定义布局 (大约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)).flatMap { item -> UICollectionViewLayoutAttributes? in 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以获取示例&#x3002;


请注意,您提到您希望“圆圈之间没有空格”,因此只需调整radius和/或itemSize即可获得所需的布局。