UIView绘制网格选中方块时alpha值显示不一致问题排查
问题分析与解决
问题现象
实现棋盘样式UIView,重写draw()通过双层循环绘制网格,拖动时在选中方块上方绘制alpha=0.15的黄色标记,但标记的透明度表现异常:越靠近左上的方块,黄色标记越接近不透明,右下的方块则符合设置的alpha值。
错误原因
你的drawBoard()方法存在逻辑错误:在4×4的嵌套循环内部,每次循环都会绘制fromCol/fromRow和toCol/toRow对应的黄色方块。以左上角(0,0)方块为例,它会在循环的16次迭代中被重复绘制16次,每次叠加0.15的透明度,最终视觉上接近完全不透明;而右下角的方块仅在最后几次循环中被绘制,叠加次数少,透明度更接近设置值。
解决方案
将绘制黄色选中标记的代码从棋盘网格的循环外部执行,确保每个选中方块只被绘制一次,避免透明度叠加。同时可以优化棋盘的绘制逻辑,用8×8的循环实现标准棋盘样式(原代码中重复绘制相同位置的灰色方块,属于冗余操作)。
修正后的完整代码
import UIKit final class BoardView: UIView { // MARK: - Properties private var cellSide: CGFloat = .zero private var fromCol: Int? private var fromRow: Int? private var toCol: Int? private var toRow: Int? // MARK: - Lifecycle override func didMoveToSuperview() { super.didMoveToSuperview() backgroundColor = .clear } // MARK: - Draw override func draw(_ rect: CGRect) { super.draw(rect) backgroundColor = .clear cellSide = bounds.width / 8 drawBoard() } private func drawBoard() { // 先绘制棋盘网格(8×8标准棋盘) for row in 0..<8 { for col in 0..<8 { // 棋盘色:行列和为偶数用深灰,奇数用浅灰 let color = (col + row) % 2 == 0 ? UIColor.darkGray : UIColor.lightGray drawSquare(col: col, row: row, color: color, isSelected: false) } } // 再绘制选中的黄色标记(只执行一次,避免重复叠加) if let fromCol = fromCol, let fromRow = fromRow { drawSquare(col: fromCol, row: fromRow, color: .yellow, isSelected: true) } if let toCol = toCol, let toRow = toRow { drawSquare(col: toCol, row: toRow, color: .yellow, isSelected: true) } } private func drawSquare(col: Int, row: Int, color: UIColor, isSelected: Bool) { let rect = CGRect(x: CGFloat(col) * cellSide, y: CGFloat(row) * cellSide, width: cellSide, height: cellSide) let path = UIBezierPath(rect: rect) if isSelected { color.withAlphaComponent(0.15).setFill() } else { color.setFill() } path.fill() } // MARK: - Touches override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesBegan(touches, with: event) guard let first = touches.first else { return } let fingerLocation = first.location(in: self) fromCol = Int(fingerLocation.x / cellSide) fromRow = Int(fingerLocation.y / cellSide) setNeedsDisplay() // 修复:调用此方法让初始选中标记立即显示 } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesMoved(touches, with: event) guard let first = touches.first else { return } let fingerLocation = first.location(in: self) toCol = Int(fingerLocation.x / cellSide) toRow = Int(fingerLocation.y / cellSide) setNeedsDisplay() } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesEnded(touches, with: event) fromCol = nil fromRow = nil toCol = nil toRow = nil setNeedsDisplay() } }
额外优化点
- 修复了
touchesBegan中未调用setNeedsDisplay()的问题,确保初始选中的方块能立即显示标记 - 优化棋盘绘制逻辑,用8×8循环实现标准黑白棋盘样式,避免原代码中重复绘制同一位置的冗余操作
- 简化了
drawSquare的透明度设置逻辑,更直观
内容的提问来源于stack exchange,提问作者oneshot
相关产品推荐
相关产品推荐

