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

SwiftUI网格中未绘制全部tile时如何避免重复边框?

SwiftUI 不完整网格Tile的重复边框解决方法

问题

在SwiftUI Grid布局中,仅绘制部分灰色Tile时,相邻灰色Tile间会出现重复边框(每个Tile独立绘制完整边框,叠加后变粗),而与白色Tile相邻的边框显示正常。

解决方案

方法1:按需绘制Tile边框(推荐适配零散Tile)

核心逻辑是:判断当前Tile的上下左右是否存在同类型Tile,仅绘制无相邻Tile的边,避免重复叠加。

首先添加辅助方法判断位置是否有Tile:

func hasTile(at position: (Int, Int)) -> Bool {
    tileList.contains(where: { $0 == position })
}

修改Tile绘制代码:

var grid: some View {
    Grid(horizontalSpacing: 0, verticalSpacing: 0) {
        ForEach((1...maxX).reversed(), id: \.self) { i in
            GridRow {
                ForEach(1...maxY, id: \.self) { j in
                    let currentPos = (i, j)
                    if hasTile(at: currentPos) {
                        Rectangle()
                            .foregroundColor(.gray)
                            .aspectRatio(1.0, contentMode: .fit)
                            // 绘制上边:上方无Tile时显示
                            .overlay(alignment: .top) {
                                if !hasTile(at: (i+1, j)) {
                                    Rectangle().frame(height: 1)
                                }
                            }
                            // 绘制下边:下方无Tile时显示
                            .overlay(alignment: .bottom) {
                                if !hasTile(at: (i-1, j)) {
                                    Rectangle().frame(height: 1)
                                }
                            }
                            // 绘制左边:左侧无Tile时显示
                            .overlay(alignment: .leading) {
                                if !hasTile(at: (i, j-1)) {
                                    Rectangle().frame(width: 1)
                                }
                            }
                            // 绘制右边:右侧无Tile时显示
                            .overlay(alignment: .trailing) {
                                if !hasTile(at: (i, j+1)) {
                                    Rectangle().frame(width: 1)
                                }
                            }
                    } else {
                        Rectangle()
                            .aspectRatio(1.0, contentMode: .fit)
                            .foregroundColor(.white)
                    }
                }
            }
        }
    }
    .padding()
}

方法2:统一网格外边框+Tile内部单边分隔

适合Tile为连续区域的场景,给整个Grid加外边框,Tile仅绘制右侧和下侧边框:

var grid: some View {
    Grid(horizontalSpacing: 0, verticalSpacing: 0) {
        ForEach((1...maxX).reversed(), id: \.self) { i in
            GridRow {
                ForEach(1...maxY, id: \.self) { j in
                    let currentPos = (i, j)
                    if hasTile(at: currentPos) {
                        Rectangle()
                            .foregroundColor(.gray)
                            .aspectRatio(1.0, contentMode: .fit)
                            // 右侧边框
                            .overlay(
                                HStack(spacing: 0) {
                                    Spacer()
                                    Rectangle().frame(width: 1)
                                }
                            )
                            // 下侧边框
                            .overlay(
                                VStack(spacing: 0) {
                                    Spacer()
                                    Rectangle().frame(height: 1)
                                }
                            )
                    } else {
                        Rectangle()
                            .aspectRatio(1.0, contentMode: .fit)
                            .foregroundColor(.white)
                    }
                }
            }
        }
    }
    // 网格外边框
    .overlay(Rectangle().strokeBorder(lineWidth: 1))
    .padding()
}

方法3:自定义Shape绘制边框

通过自定义Shape封装边框逻辑,代码更模块化:

struct TileBorder: Shape {
    var hasTopTile: Bool
    var hasBottomTile: Bool
    var hasLeftTile: Bool
    var hasRightTile: Bool
    
    func path(in rect: CGRect) -> Path {
        var path = Path()
        
        // 上边
        if !hasTopTile {
            path.move(to: CGPoint(x: rect.minX, y: rect.minY))
            path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
        }
        // 下边
        if !hasBottomTile {
            path.move(to: CGPoint(x: rect.minX, y: rect.maxY))
            path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
        }
        // 左边
        if !hasLeftTile {
            path.move(to: CGPoint(x: rect.minX, y: rect.minY))
            path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
        }
        // 右边
        if !hasRightTile {
            path.move(to: CGPoint(x: rect.maxX, y: rect.minY))
            path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
        }
        
        return path
    }
}

在Tile中使用该Shape:

if hasTile(at: currentPos) {
    Rectangle()
        .foregroundColor(.gray)
        .aspectRatio(1.0, contentMode: .fit)
        .overlay(
            TileBorder(
                hasTopTile: hasTile(at: (i+1, j)),
                hasBottomTile: hasTile(at: (i-1, j)),
                hasLeftTile: hasTile(at: (i, j-1)),
                hasRightTile: hasTile(at: (i, j+1))
            )
            .stroke(lineWidth: 1)
        )
}

效果

使用上述方法后,相邻灰色Tile间的边框不再重复,所有边框宽度统一,与白色Tile相邻的边框显示正常。

内容的提问来源于stack exchange,提问作者Trailblazer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 20:34:52