以编程方式创建网格视图

Posted

技术标签:

【中文标题】以编程方式创建网格视图【英文标题】:Programatic creation of a grid view 【发布时间】:2014-12-05 02:44:06 【问题描述】:

我正在创建一个 10x10 网格 (GridView) 的视图 (CellView)。

我能够为单一尺寸的屏幕创建此解决方案,但迄今为止无法创建适用于 iPhone 上所有 3 个当前屏幕尺寸的解决方案。

gridView 是在自动布局中创建的,并且有一些限制:宽度和高度之间的比例相等,并且距离水平边缘 8 像素。

我有两种方法:

1.) 当调用 init() 来创建 CellView 时,我将它的框架基于 superview 的大小。

let cellSize = gridView.bounds.width/15 let cellRect = CGRect(origin: CGPoint(x: x, y: y), size: CGSize(width: cellSize, height: cellSize))

这种方法似乎不起作用,并且单元格并不都适合网格。根据除数的变化,它们要么太大要么太小。

2.) 我的第二种方法是对单元格使用自动布局。

创建 cellView 并将其作为子视图添加到 gridView 后,我添加了 2 个约束。

var width = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: self.superview, attribute: NSLayoutAttribute.Width, multiplier: 0.0666667, // 1/15 constant: 0) var height = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: self.superview, attribute: NSLayoutAttribute.Height, multiplier: 0.0666667, // 1/15 constant: 0)

不幸的是,这会导致 cellViews 没有出现在屏幕上。检查后这是由于宽度/高度为 0;然而,在运行时,它们在约束中的 0 值之下还有额外的宽度和高度 >0。

我不确定哪种方法最好。我正在以编程方式创建 cellView,并且需要一种适用于所有设备屏幕尺寸的方法。

非常感谢在我当前的方法中有错误的更好的解决方案或想法。

【问题讨论】:

【参考方案1】:

你可以在 layoutSubviews() 中布局视图:

class GridView 
  // number of rows and columns
  private let rows = 10
  private let cols = 10

  // two-dimensional array of your Cell views. You create them elsewhere and add 
  // all of them to GridView
  private let children: [[UIView]]
  // border, here 8 points on each side
  private let insets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
  // spacing between Cell views (not sure if you want this, and you could change it to have
  // different values for horizontal and vertical spacing)
  private let spacing: CGFloat = 1

  override func layoutSubviews() 
    // available size is the total of the widths and heights of your cell views:
    // bounds.width/bounds.height minus edge insets minus spacing between cells
    let availableSize = CGSize(
      width: bounds.width - insets.left - insets.right - CGFloat(cols - 1) * spacing,
      height: bounds.height - insets.top - insets.bottom - CGFloat(rows - 1) * spacing)

    // maximum width and height that will fit
    let maxChildWidth = floor(availableSize.width / CGFloat(cols))
    let maxChildHeight = floor(availableSize.height / CGFloat(rows))

    // childSize should be square
    let childSize = CGSize(
      width: min(maxChildWidth, maxChildHeight),
      height: min(maxChildWidth, maxChildHeight))

    // total area occupied by the cell views, including spacing inbetween
    let totalChildArea = CGSize(
      width: childSize.width * CGFloat(cols) + spacing * CGFloat(cols - 1),
      height: childSize.height * CGFloat(rows) + spacing * CGFloat(rows - 1))

    // center everything in GridView
    let topLeftCorner = CGPoint(
      x: floor((bounds.width - totalChildArea.width) / 2),
      y: floor((bounds.height - totalChildArea.height) / 2))

    for row in 0..<rows 
      for col in 0..<cols 
        let view = children[row][col]
        view.frame = CGRect(
          x: topLeftCorner.x + CGFloat(col) * (childSize.width + spacing),
          y: topLeftCorner.y + CGFloat(row) * (childSize.height + spacing),
          width: childSize.width,
          height: childSize.height)
      
    
  

除了 GridView 太小的一些边缘情况(childSize.widthchildSize.height 会得到负值),这将适用于任何屏幕尺寸。

【讨论】:

太棒了!非常感谢。关键是将我的代码移动到 layoutSubViews。 :)【参考方案2】:

也许这是一个扩展您知识的练习,但如果不是这样,您可以改用UICollectionView 为自己节省大量时间和麻烦。

Apple 有一些来自 WWDC 2012 when they introduced UICollectionView 的精彩视频

还有一些tutotials out there for getting you started with UICollectionView in Swift。

【讨论】:

当我开始这个项目时,我考虑过 UICollectionView,但它已经有将近几个月了,老实说,我不记得为什么我最终没有使用 UICollectionView。我做了一点突破(我在不正确的时间将 cellView 作为 subView 添加到 gridView)如果我无法取得更多进展,我可能会切换到 UICollecitonView。 UICollectionView 结合 UICollectionViewFlowLayout 用于在网格中布局视图。它非常易于使用,可以为您节省大量工作。

以上是关于以编程方式创建网格视图的主要内容,如果未能解决你的问题,请参考以下文章

尝试以编程方式使用嵌套的 Stack 视图制作网格

以编程方式将值添加到网格视图页脚

以编程方式删除数据网格视图中的行标题

以编程方式将新行添加到数据网格视图

网格视图组合框

创建网格线并允许用户打开/关闭网格线视图