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
相关产品推荐
相关产品推荐

