// // BoardView.swift // TTTKit // // Created by Shadowfacts on 12/21/22. // import SwiftUI @available(iOS 16.0, *) struct BoardView: 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, 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() } }