Swift中如何将UIView网格点击位置映射至二维数组行列
嘿,这个需求我之前做类似棋盘类项目时碰到过,分享几个比直接用touchesBegin更可靠的方案给你:
方案1:给网格视图设置Tag(最简单直接的静态网格方案)
因为你的网格是固定10×10,我们可以给每个单元格视图设置一个唯一的Tag,用行号×10 + 列号的规则生成,点击时通过Tag反推出行列索引。
代码示例:
// 假设container是承载网格的父视图 let gridCount = 10 let cellSize = container.bounds.width / CGFloat(gridCount) // 创建网格 for row in 0..<gridCount { for column in 0..<gridCount { let cellFrame = CGRect( x: CGFloat(column) * cellSize, y: CGFloat(row) * cellSize, width: cellSize, height: cellSize ) let cell = UIImageView(frame: cellFrame) cell.tag = row * gridCount + column // 核心:设置唯一Tag cell.isUserInteractionEnabled = true // UIImageView默认关闭交互,必须打开 // 添加点击手势 let tap = UITapGestureRecognizer(target: self, action: #selector(onCellTapped(_:))) cell.addGestureRecognizer(tap) // 其他样式/图片设置... container.addSubview(cell) } } // 处理点击事件 @objc private func onCellTapped(_ gesture: UITapGestureRecognizer) { guard let cell = gesture.view else { return } let row = cell.tag / 10 let column = cell.tag % 10 // 更新二维数组 yourTwoDimensionalArray[row][column] = 1 // 这里可以同步更新单元格的图片状态 }
这个方案的优势是实现成本极低,适合固定尺寸的静态网格,几乎不会出错。
方案2:使用UICollectionView(最具扩展性的原生方案)
如果你的网格未来可能需要滚动、复用单元格,或者调整行列数,UICollectionView是iOS原生的最佳选择,它的代理方法可以直接返回点击位置的索引。
代码示例:
// 初始化CollectionView和布局 let layout = UICollectionViewFlowLayout() let cellSize = container.bounds.width / 10 layout.itemSize = CGSize(width: cellSize, height: cellSize) layout.minimumLineSpacing = 0 layout.minimumInteritemSpacing = 0 let collectionView = UICollectionView(frame: container.bounds, collectionViewLayout: layout) collectionView.delegate = self collectionView.dataSource = self collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "GridCell") container.addSubview(collectionView) // 实现数据源和代理 extension YourViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 100 // 10×10的总单元格数 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "GridCell", for: indexPath) // 设置单元格的图片或样式,比如根据数组状态显示不同图片 let row = indexPath.item / 10 let column = indexPath.item % 10 if yourTwoDimensionalArray[row][column] == 1 { (cell.contentView.subviews.first as? UIImageView)?.image = selectedImage } return cell } } extension YourViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let row = indexPath.item / 10 let column = indexPath.item % 10 yourTwoDimensionalArray[row][column] = 1 // 刷新对应单元格的UI collectionView.reloadItems(at: [indexPath]) } }
这个方案的优势是自带单元格复用机制,性能更好,后续扩展功能(比如添加动画、调整网格大小)也更方便。
方案3:优化你的
touchesBegin方案(无额外依赖的轻量方案) 如果你不想用Tag或CollectionView,也可以优化原始的坐标计算方法,避免浮点误差导致的索引错误:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesBegan(touches, with: event) guard let touch = touches.first, let container = self.gridContainerView else { return } let touchPoint = touch.location(in: container) let gridCount = 10 let cellSize = container.bounds.width / CGFloat(gridCount) // 用floor确保浮点计算不会出现越界(比如9.999被当成10) let column = Int(floor(touchPoint.x / cellSize)) let row = Int(floor(touchPoint.y / cellSize)) // 边界检查,防止点击到网格外的区域导致数组越界 guard row >= 0, row < gridCount, column >= 0, column < gridCount else { return } yourTwoDimensionalArray[row][column] = 1 // 更新对应单元格的图片 let targetCell = container.subviews[row * gridCount + column] as? UIImageView targetCell?.image = selectedImage }
这个方法要注意确保容器视图的frame是准确的,并且一定要加边界检查,避免数组越界崩溃。
内容的提问来源于stack exchange,提问作者NSD1105
相关产品推荐
相关产品推荐

