7

UICollectionView行と列の整合性を維持しながら、幅と高さの両方で表示されるフレームよりも潜在的に大きいグリッドを表示する必要があります。デフォルトでは、セクションUICollectionViewFlowLayoutを画面外にスクロールできますが、項目をセクション内にラップしてすべての項目を画面上に保持するため、グリッドが台無しになります。

UICollectionViewのサブクラスであることを認識してUIScrollView、viewDidLoad でコレクション ビューのコンテンツ サイズ プロパティを手動で設定してみました。

self.collectionView.contentSize = CGSizeMake((columns * (cellWidth + itemSpacingX), (rows * (cellHeight + itemSpacingY));

しかし、これは効果がありませんでした。質問:

  • カスタムレイアウトを構築せずにこれを達成する簡単な方法はありますか?
  • カスタム レイアウトを使用してcollectionViewContentSizeメソッドをオーバーライドすると、コレクション ビューでアイテムのラップを停止し、両方向にスクロールすることに成功しますか?
  • カスタム レイアウトを構築する必要がある場合 (学習に時間を費やす必要があります)、 をサブクラス化しますか?それともUICollectionViewLayoutサブクラス化するとUICollectionViewFlowLayout時間を節約できますか?

更新: UICollectionView を UIScrollView のサブビューとして埋め込んでみました。コレクション ビュー自体は正しく動作しています。スクロール ビューの端で行が折り返されておらず、設定した UIScrollView コンテンツ サイズを満たしていることがわかります。ただし、スクロール ビューは水平方向にはパンしません。つまり、コレクション ビュー自体が行う垂直方向のスクロールのみです。それでまた立ち往生。レスポンダー チェーンに問題がある可能性はありますか?

4

3 に答える 3

5

これを行う2つの方法を考え出しました。どちらもカス​​タム レイアウトが必要でした。問題は、デフォルトのフロー レイアウト (コレクション ビュー プログラミング ガイドから、これは部分的にフロー レイアウトの定義であることがわかりました) が、スーパービューの境界に基づいてセル レイアウト属性を生成し、アイテムをスクロールが1つの軸でのみ発生するように、それらを境界内に保持するセクション。難しいことではないので、コードの詳細はスキップします。私の問題は主に、どのアプローチを取るべきかについての混乱でした。

簡単な方法: UIScrollView とサブクラス 'UICollectionViewFlowLayout' を使用します。を に埋め込みUICollectionViewますUIScrollView。スクロール ビューの contentSize プロパティを、viewDiDLoadコレクション ビューが占有するフル サイズに一致するように設定します (これにより、デフォルトのフロー レイアウトでアイテムを折り返しなしでセクション内の 1 行に配置できます)。をサブクラス化UICollectionViewFlowLayoutし、そのオブジェクトをコレクション ビューのカスタム レイアウトとして設定します。collectionViewContentSizeカスタム フロー レイアウトで、コレクション ビュー マトリックスのフル サイズを返すようにオーバーライドします。このアプローチでは、フロー レイアウトを使用しますが、ラップされていないセクションを表示するために両方向にスクロールできます。欠点は、かなり制限されたフロー レイアウトがまだ残っていることです。さらに、UICollectionViewコレクションビュー自体が持つべき機能を取得するためだけに、独自のスーパークラスのインスタンス内で。

より難しい方法ですが、より用途が広くエレガントです: UICollectionViewLayout をサブクラス化します。このチュートリアル を使用して、完全なカスタム レイアウトを実装する方法を学びました。ここは必要ありませんUIScrollView。フロー レイアウト、サブクラスを放棄し、UICollectionViewLayoutそれをカスタム レイアウトとして設定すると、マトリックスを構築して、コレクション ビュー自体から適切な動作を得ることができます。すべてのレイアウト属性を生成する必要があるため、より多くの作業が必要になりますが、コレクション ビューで必要なことを実行できるようになります。

私の意見では、Apple は、ラッピングを抑制するプロパティをデフォルトのフロー レイアウトに追加する必要があります。そのままの行と列を含む 2D マトリックスをデバイスに表示させることは、特殊な機能ではなく、より簡単に実行できるように思われます。

于 2014-03-02T20:40:02.970 に答える
4

Swift 4に更新された AndrewK のコードのバージョンは次のとおりです。

import UIKit

class CollectionViewMatrixLayout: UICollectionViewLayout {

  var itemSize: CGSize
  var interItemSpacingY: CGFloat
  var interItemSpacingX: CGFloat
  var layoutInfo: [IndexPath: UICollectionViewLayoutAttributes]

  override init() {
    itemSize = CGSize(width: 50, height: 50)
    interItemSpacingY = 1
    interItemSpacingX = 1
    layoutInfo = [IndexPath: UICollectionViewLayoutAttributes]()

    super.init()
  }

  required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  override func prepare() {
    guard let collectionView = self.collectionView else {
      return
    }

    var cellLayoutInfo = [IndexPath: UICollectionViewLayoutAttributes]()
    var indexPath = IndexPath(item: 0, section: 0)

    let sectionCount = collectionView.numberOfSections
    for section in 0..<sectionCount {

      let itemCount = collectionView.numberOfItems(inSection: section)
      for item in 0..<itemCount {
        indexPath = IndexPath(item: item, section: section)
        let itemAttributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
        itemAttributes.frame = frameForCell(at: indexPath)
        cellLayoutInfo[indexPath] = itemAttributes
      }

      self.layoutInfo = cellLayoutInfo
    }
  }

  func frameForCell(at indexPath: IndexPath) -> CGRect {
    let row = indexPath.section
    let column = indexPath.item

    let originX = (itemSize.width + interItemSpacingX) * CGFloat(column)
    let originY = (itemSize.height + interItemSpacingY) * CGFloat(row)

    return CGRect(x: originX, y: originY, width: itemSize.width, height: itemSize.height)
  }

  override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
  {
    var allAttributes = Array<UICollectionViewLayoutAttributes>()

    for (_, attributes) in self.layoutInfo {
      if (rect.intersects(attributes.frame)) {
        allAttributes.append(attributes)
      }
    }
    return allAttributes
  }

  override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    return self.layoutInfo[indexPath]
  }

  override var collectionViewContentSize: CGSize {
    guard let collectionView = self.collectionView else {
      return .zero
    }

    let sectionCount = collectionView.numberOfSections
    let height = (itemSize.height + interItemSpacingY) * CGFloat(sectionCount)

    let itemCount = Array(0..<sectionCount)
      .map { collectionView.numberOfItems(inSection: $0) }
      .max() ?? 0

    let width  = (itemSize.width + interItemSpacingX) * CGFloat(itemCount)

    return CGSize(width: width, height: height)

  }
}
于 2018-04-20T17:20:23.033 に答える
1

完全なマトリックス customLayout は次のとおりです。

import UIKit

class MatrixLayout: UICollectionViewLayout {

    var itemSize: CGSize!
    var interItemSpacingY: CGFloat!
    var interItemSpacingX: CGFloat!
    var layoutInfo: Dictionary<NSIndexPath, UICollectionViewLayoutAttributes>!

    required init?(coder aDecoder: NSCoder) {

        super.init(coder: aDecoder)

        itemSize = CGSizeMake(50.0, 50.0)
        interItemSpacingY = 1.0
        interItemSpacingX = 1.0
    }

    override func prepareLayout() {

        var cellLayoutInfo = Dictionary<NSIndexPath, UICollectionViewLayoutAttributes>()

        let sectionCount = self.collectionView?.numberOfSections()
        var indexPath = NSIndexPath(forItem: 0, inSection: 0)

        for (var section = 0; section < sectionCount; section += 1)
        {
            let itemCount = self.collectionView?.numberOfItemsInSection(section)

            for (var item = 0; item < itemCount; item += 1)
            {
                indexPath = NSIndexPath(forItem:item, inSection: section)
                let itemAttributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
                itemAttributes.frame = frameForCellAtIndexPath(indexPath)
                cellLayoutInfo[indexPath] = itemAttributes
            }

            self.layoutInfo = cellLayoutInfo
        }
    }

    func frameForCellAtIndexPath(indexPath: NSIndexPath) -> CGRect
    {
        let row = indexPath.section
        let column = indexPath.item

        let originX = (self.itemSize.width + self.interItemSpacingX) * CGFloat(column)
        let originY = (self.itemSize.height + self.interItemSpacingY) * CGFloat(row)

        return CGRectMake(originX, originY, self.itemSize.width, self.itemSize.height)
    }

    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]?
    {
        var allAttributes = Array<UICollectionViewLayoutAttributes>()

        for (index, attributes) in self.layoutInfo
        {
            if (CGRectIntersectsRect(rect, attributes.frame))
            {
                allAttributes.append(attributes)
            }
        }
        return allAttributes
    }

    override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
        return self.layoutInfo[indexPath]
    }


    override func collectionViewContentSize() -> CGSize {

        let width:CGFloat = (self.itemSize.width + self.interItemSpacingX) * CGFloat((self.collectionView?.numberOfItemsInSection(0))!)
        let height:CGFloat = (self.itemSize.height + self.interItemSpacingY) * CGFloat((self.collectionView?.numberOfSections())!)

        return CGSizeMake(width, height)
    }
}

セクションは行、アイテムは列

于 2016-04-14T14:57:35.357 に答える