Tusker/Packages/TTTKit/Sources/TTTKit/UI/BoardView.swift

89 lines
2.8 KiB
Swift

//
// BoardView.swift
// TTTKit
//
// Created by Shadowfacts on 12/21/22.
//
import SwiftUI
@available(iOS 16.0, *)
struct BoardView<Cell: View>: View {
let board: any Board
@Binding var cellSize: CGFloat
let spacing: CGFloat
let cellProvider: (_ column: Int, _ row: Int) -> Cell
init(board: any Board, cellSize: Binding<CGFloat>, spacing: CGFloat, @ViewBuilder cellProvider: @escaping (Int, Int) -> Cell) {
self.board = board
self._cellSize = cellSize
self.spacing = spacing
self.cellProvider = cellProvider
}
var body: some View {
ZStack {
if let win = board.win {
winOverlay(win)
}
Grid(horizontalSpacing: 10, verticalSpacing: 10) {
GridRow {
cellProvider(0, 0)
.background(GeometryReader { proxy in
Color.clear
.preference(key: MarkSizePrefKey.self, value: proxy.size.width)
.onPreferenceChange(MarkSizePrefKey.self) { newValue in
cellSize = newValue
}
})
cellProvider(1, 0)
cellProvider(2, 0)
}
GridRow {
cellProvider(0, 1)
cellProvider(1, 1)
cellProvider(2, 1)
}
GridRow {
cellProvider(0, 2)
cellProvider(1, 2)
cellProvider(2, 2)
}
}
let sepOffset = (cellSize + spacing) / 2
Separator(axis: .vertical)
.offset(x: -sepOffset)
Separator(axis: .vertical)
.offset(x: sepOffset)
Separator(axis: .horizontal)
.offset(y: -sepOffset)
Separator(axis: .horizontal)
.offset(y: sepOffset)
}
.padding(.all, spacing / 2)
.aspectRatio(1, contentMode: .fit)
}
private func winOverlay(_ win: Win) -> some View {
let pointsWithIndices = win.points.map { ($0, $0.row * 3 + $0.column) }
let cellSize = cellSize + spacing
return ForEach(pointsWithIndices, id: \.1) { (point, _) in
Rectangle()
.foregroundColor(Color(UIColor.green))
.opacity(0.5)
.frame(width: cellSize, height: cellSize)
.offset(x: CGFloat(point.column - 1) * cellSize, y: CGFloat(point.row - 1) * cellSize)
}
}
}
private struct MarkSizePrefKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}