2017-01-24 42 views
2

我正在嘗試爲圓形形狀的自定義單元格實現UICollectionView。現在默認情況下,這些圓對齊方式與普通方形單元相同:頂部圓和底部圓位於同一垂直線上。我該如何改變對齊方式:頂部圓圈和它下面的兩個圓圈形成一個等邊三角形(頂部圓圈和底部圓圈的位置按半徑長度移動)?如下圖所示:iOS UICollectionView:交替網格對齊的圓形視圖的單元格

from OOO 
    OOO 
    OOO 

to O O O 
    O O O (no spacing among the circles) 
    O O O 
+0

您需要創建自己的'UICollectionViewLayout',「告訴」您的單元格的框架是什麼。 – Larme

回答

8

的基本想法是創建一個自定義UICollectionViewLayout實現:

  • collectionViewContentSize,即什麼是集合視圖的完整,滾動contentSize的大小;

  • layoutAttributesForItem(at indexPath:),即什麼是特定小區的關鍵屬性(即centersize);和

  • 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) 
    }   
} 

國債收益率:

enter image description here

顯然,自定義這個您認爲合適的,但它說明了基本的想法。


在我原來的答覆,下面,我以爲你想看看這些細胞轉了一圈,如圖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 
    } 
} 

即產率:

circle view


參見https://github.com/robertmryan/CircularCollectionView樣品。


注意,你提到你想要的「圈子中間沒有間隔」,所以才調整radius和/或itemSize相應地得到你想要的佈局。