You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 11:27:38