【问题标题】:iOS UICollectionView Horizontal Scrolling Rectangular Layout with different size of items?iOS UICollectionView水平滚动矩形布局具有不同大小的项目?
【发布时间】:2020-09-19 16:01:19
【问题描述】:

iOS UICollectionView 如何创建横向滚动矩形布局,里面有不同大小的项目。

我想使用 UICollectionView 创建一个矩形布局,如下所示。我该如何实现?

当我使用 CollectionView水平滚动时,1、2、3、4、5、6 网格将一起滚动以产生 7。

以下是 320*480 iPhone 分辨率的尺寸。更新了下面的屏幕。

前 6 件商品的尺寸低于 iPhone 5s 的尺寸。

Item 1 Size is - (213*148)
Item 2 Size is - (106*75)
Item 3 Size is - (106*74)
Item 4 Size is - (106*88)
Item 5 Size is - (106*88)
Item 6 Size is - (106*88)

item6 之后的尺寸与集合视图的宽度和高度相同,如下所示。

Item 7 Size is - (320*237)
Item 8 Size is - (320*237)
Item 9 Size is - (320*237)

如何使用具有水平滚动功能的集合视图创建简单的自定义布局?

必须感谢快速解决方案。提前致谢。

【问题讨论】:

  • 这是否意味着,当你滚动 1,2,3,4,5,6 时,网格会一起滚动,从右边移出 7?
  • @chetananand:是的
  • @chetananand:感谢您的快速回复。我正在使用 cocoacontrols.com/controls/squareflowlayout。我可以同时更新吗?
  • 我对这个库不是很熟悉,所以不确定这个库是否支持。也许您可以尝试对其进行调整以实现此目的。
  • 您应该使用自定义布局(参见stackoverflow.com/questions/42364859/… 示例)。只有一个自定义的应该能够在不使用技巧的情况下做你想做的事情(比如将一个单元格分成两部分等)。此外,最好在你的模式照片中显示应该是索引单元格的内容,因为它更难阅读他们目前。我已经研究过一个看起来像你的不同的,并且根据客户的不同,索引会有不同的逻辑......

标签: ios swift uicollectionview uicollectionviewlayout uicollectionviewflowlayout


【解决方案1】:

我建议在 CollectionViewCell(固定尺寸)中使用 StackView 来创建网格布局,如您的帖子中所示。

GridStackView 下方使用addCell(view: UIView) 方法根据添加的视图数量创建动态网格布局。

将此GridStackView 添加为 CollectionViewCell 的唯一子视图,将所有边缘固定到两侧,使其完全填充 CollectionViewCell。

在准备 CollectionViewCell 时,使用 addCell(view: UIView) 方法向其中添加平铺视图。

如果只添加一个视图,那么它将显示一个视图占据整个GridStackView 和整个 CollectionViewCell。 如果添加了多个视图,它会自动将它们布局在 CollectionViewCell 内部。

您可以调整下面的代码以获得计算行和列的所需布局。当前实现需要rowSize 在初始化我用于我的一个项目时提供,您需要对其进行一些修改以获得所需的布局。

class GridStackView: UIStackView {
    private var cells: [UIView] = []
    private var currentRow: UIStackView?
    var rowSize: Int = 3
    var defaultSpacing: CGFloat = 5

    init(rowSize: Int) {
        self.rowSize = rowSize
        super.init(frame: .zero)
        translatesAutoresizingMaskIntoConstraints = false
        axis = .vertical
        spacing = defaultSpacing
        distribution = .fillEqually
    }

    required init(coder: NSCoder) {
        super.init(coder: coder)
        translatesAutoresizingMaskIntoConstraints = false
        axis = .vertical
        spacing = defaultSpacing
        distribution = .fillEqually
    }

    private func preapreRow() -> UIStackView {
        let row = UIStackView(arrangedSubviews: [])
        row.spacing = defaultSpacing
        row.translatesAutoresizingMaskIntoConstraints = false
        row.axis = .horizontal
        row.distribution = .fillEqually
        return row
    }

    func removeAllCell() {
        for item in arrangedSubviews {
            item.removeFromSuperview()
        }
        cells.removeAll()
        currentRow = nil
    }

    func addCell(view: UIView) {
        let firstCellInRow = cells.count % rowSize == 0
        if currentRow == nil || firstCellInRow {
            currentRow = preapreRow()
            addArrangedSubview(currentRow!)
        }
        view.translatesAutoresizingMaskIntoConstraints = false
        cells.append(view)
        currentRow?.addArrangedSubview(view)
        setNeedsLayout()
    }
}

【讨论】:

    【解决方案2】:
    • 创建一个包含两个视图的新单元格。视图具有相等的宽度。
    • 相应地构建您的数据

    数据

    struct ItemData {
        var color: [UIColor]
    }
    
    // NOTICE: 2nd item contains two colors and the rest one.
    let data = [ItemData(color: [.red]), ItemData(color: [.blue, .purple]), ItemData(color: [.orange]),
                ItemData(color: [.cyan]), ItemData(color: [.green]), ItemData(color: [.magenta]),
                ItemData(color: [.systemPink]), ItemData(color: [.link]), ItemData(color: [.purple])] 
    

    细胞

    class CollectionViewCellOne: UICollectionViewCell {
        static let identifier = "CollectionViewCellOne"
    
        var item: ItemData? {
            didSet {
                if let item = item {
                    self.leadingLabel.backgroundColor = item.color.first!
                    self.trailingLabel.backgroundColor = item.color.last!
                }
            }
        }
    
        let leadingLabel = UILabel()
        let trailingLabel = UILabel()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            self.contentView.addSubview(leadingLabel)
            self.contentView.addSubview(trailingLabel)
    
            let width = self.frame.width / 2
    
            leadingLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
            leadingLabel.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
            leadingLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
            leadingLabel.widthAnchor.constraint(equalToConstant: width).isActive = true
            leadingLabel.translatesAutoresizingMaskIntoConstraints = false
    
            trailingLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
            trailingLabel.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
            trailingLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
            trailingLabel.widthAnchor.constraint(equalToConstant: width).isActive = true
            trailingLabel.translatesAutoresizingMaskIntoConstraints = false
    
        }
    
        required init?(coder: NSCoder) {
            fatalError()
        }
    }
    

    dequeueReusableCell

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        if indexPath.row == 1 {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewCellOne.identifier, for: indexPath) as! CollectionViewCellOne
            cell.item = data[indexPath.row]
            return cell
        } else {
    
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewCell.identifier, for: indexPath) as! CollectionViewCell
            if let color = data[indexPath.row].color.first {
                cell.backgroundColor = color
            }
    
            return cell
        }
    }
    

    【讨论】:

      【解决方案3】:

      我已尝试使用Mahan's Answer,并且我得到了部分正确的输出。但问题是, index1 具有两个项目的全宽。

      如何将索引 1 拆分为 index1 和 Index2?

      import UIKit
      
      class ViewController: UIViewController {
      
          override func viewDidLoad() {
              super.viewDidLoad()
              setUpCollectionView()
              // Do any additional setup after loading the view.
          }
          func setUpCollectionView() {
              self.view.backgroundColor = .white
              let layout = UICollectionViewFlowLayout()
      //        layout.minimumInteritemSpacing = 1
      //        layout.minimumLineSpacing = 1
              layout.scrollDirection = .horizontal
              let collectionView = CollectionView(frame: .zero, collectionViewLayout: layout)
              view.addSubview(collectionView)
              collectionView.bounces = false
              collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
              collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
              collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
              collectionView.heightAnchor.constraint(equalToConstant: 240).isActive = true
              collectionView.translatesAutoresizingMaskIntoConstraints = false
          }
      
      }
      class CollectionView: UICollectionView {
          override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
              super.init(frame: frame, collectionViewLayout: layout)
              self.register(CollectionViewCell.self, forCellWithReuseIdentifier: CollectionViewCell.identifier)
              self.dataSource = self
              self.delegate = self
              self.isPagingEnabled = true
      
          }
      
          required init?(coder: NSCoder) {
              fatalError()
          }
      
      }
      extension CollectionView: UICollectionViewDataSource {
          func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
              return 10
          }
      
          func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
              let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewCell.identifier, for: indexPath) as! CollectionViewCell
              cell.backgroundColor = .blue
              cell.label.text = "\(indexPath.row)"
      
              let row = indexPath.row
              switch row {
              case 0:
                  cell.backgroundColor = .red
              case 1:
                  cell.backgroundColor = .blue
              case 2:
                  cell.backgroundColor = .purple
              case 3:
                  cell.backgroundColor = .orange
              case 4:
                  cell.backgroundColor = .cyan
              case 5:
                  cell.backgroundColor = .green
              case 6:
                  cell.backgroundColor = .magenta
              case 7:
                  cell.backgroundColor = .white
              case 8:
                  cell.backgroundColor = .blue
              case 9:
                  cell.backgroundColor = .green
              default:
                  cell.backgroundColor = .systemPink
              }
              return cell
          }
      
      
      }
      
      extension  CollectionView: UICollectionViewDelegateFlowLayout {
          func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
              let row = indexPath.row
              let width = collectionView.frame.width
              let other = width / 3
      
              let height = collectionView.frame.height
              let o_height =  height / 3
              switch row {
              case 0:
                  return CGSize(width: other * 2, height: o_height * 2)
              case 1:
                  return CGSize(width: other * 2, height: o_height)
              case 2:
                  return CGSize(width: other, height: o_height)
              case 3:
                  return CGSize(width: other, height: o_height)
              case 4:
                  return CGSize(width: other, height: o_height)
              case 5, 6, 7:
                  return CGSize(width: other, height: o_height)
              default:
                  return CGSize(width: width, height: height)
              }
          }
      
          func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
              return .leastNormalMagnitude
          }
          func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
              return .leastNormalMagnitude
          }
      }
      
      
      
      class CollectionViewCell: UICollectionViewCell {
          static let identifier = "CollectionViewCell"
      
          let label = UILabel()
      
          override init(frame: CGRect) {
              super.init(frame: frame)
              self.contentView.addSubview(label)
              label.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
              label.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
              label.translatesAutoresizingMaskIntoConstraints = false
          }
      
          required init?(coder: NSCoder) {
              fatalError()
          }
      }
      

      如何将索引 1 划分为 index1 和 Index2,如下所示?

      提前致谢!

      【讨论】:

      • 如何将单元格1分为单元格1和单元格2?
      猜你喜欢
      • 2013-10-18
      • 1970-01-01
      • 2020-08-16
      • 1970-01-01
      • 2021-12-12
      • 1970-01-01
      • 1970-01-01
      • 2016-09-17
      相关资源
      最近更新 更多