如何关联结构体与HexagonView以实现相邻视图检测?
Hey there! Let's figure out the best way to link your Hexagon structs to their corresponding views so you can easily detect overlapping or adjacent relationships. Based on your code and the problem requirements, here are a few optimized approaches:
This is the most straightforward approach—embed the view's frame information right into the model struct that drives your views. Since you're already calculating the frame with getFrameFor, you can persist that data to avoid recalculating it every time you need to check relationships.
Step 1: Update the Hexagon Struct
Add a property to hold the view's frame:
struct Hexagon: Identifiable { let id: Int var isSelected: Bool var frame: CGRect = .zero // New property to store layout frame // Add any other properties you already have (like grid position if applicable) }
Step 2: Cache the Frame During View Rendering
Use a binding to update the Hexagon's frame when you calculate it in your view. Modify your ForEach to use a binding so you can write back to the view model's array:
ForEach($viewModel.hexagons) { $hexagon in let hexagonFrame = getFrameFor(hexagon: hexagon, minWidth: smallSide) // Save the calculated frame back to the struct $hexagon.frame = hexagonFrame // Rest of your HexagonView and Text code remains the same... }
Step 3: Detect Overlaps/Adjacency in Your ViewModel
Now you can easily iterate through your hexagons to find overlaps or adjacent views. For basic rectangle overlap checks:
extension YourViewModel { func findOverlappingHexagons(with targetHex: Hexagon) -> [Hexagon] { hexagons.filter { hex in // Skip the target hex itself guard hex.id != targetHex.id else { return false } // Check if frames intersect return hex.frame.intersects(targetHex.frame) } } // If you're working with a hexagonal grid, replace the above with grid-based adjacency checks func findAdjacentHexagons(to targetHex: Hexagon) -> [Hexagon] { // Assuming your Hexagon has row/column properties for grid position let adjacentOffsets = [(-1,0), (1,0), (0,-1), (0,1), (-1,1), (1,-1)] // Adjust based on your grid layout return hexagons.filter { hex in adjacentOffsets.contains { offset in hex.row == targetHex.row + offset.0 && hex.column == targetHex.column + offset.1 } } } }
If you don't want to pollute your model struct with view-specific layout data, you can maintain a separate dictionary in your view model that maps each Hexagon's id to its frame.
Step 1: Add the Dictionary to Your ViewModel
class YourViewModel: ObservableObject { @Published var hexagons: [Hexagon] = [] @Published var hexagonFrames: [Int: CGRect] = [:] // ID-to-frame mapping // Rest of your view model code... }
Step 2: Update the Dictionary During View Rendering
ForEach(viewModel.hexagons) { hexagon in let hexagonFrame = getFrameFor(hexagon: hexagon, minWidth: smallSide) // Update the frame mapping viewModel.hexagonFrames[hexagon.id] = hexagonFrame // Rest of your view code... }
Step 3: Look Up Frames for Relationship Checks
extension YourViewModel { func findOverlappingHexagons(with targetHex: Hexagon) -> [Hexagon] { guard let targetFrame = hexagonFrames[targetHex.id] else { return [] } return hexagons.filter { hex in guard hex.id != targetHex.id, let frame = hexagonFrames[hex.id] else { return false } return frame.intersects(targetFrame) } } }
If your hexagon positions change frequently (e.g., during animations), you can use GeometryReader to fetch frames on-demand. However, this is less efficient than caching, so only use it if caching isn't feasible.
You can attach a GeometryReader to each HexagonView to capture its frame, then pass that data back to your view model:
ZStack() { HexagonView(strokeColor: colorArray[0]) .onTapGesture { viewModel.chooseHexagon(hexagon: hexagon) } // Capture the view's frame .background( GeometryReader { geo in Color.clear.onAppear { viewModel.updateHexagonFrame(for: hexagon.id, frame: geo.frame(in: .scrollView)) } } ) // Text overlay code... }
Which Approach Is Best?
- Option 1 is ideal if your model can safely include view layout data—it keeps everything tied together and is easy to maintain.
- Option 2 is great for separating model concerns from view layout, keeping your Hexagon struct clean.
- Option 3 works for highly dynamic layouts, but be mindful of performance since it recalculates frames on every layout change.
For hexagonal grids specifically, adding grid coordinates (row/column) to your Hexagon struct is far more reliable for detecting adjacency than frame-based checks—hexagons can overlap slightly in their frame bounds without being actually adjacent, so grid position is the way to go if your layout follows a grid pattern.
内容的提问来源于stack exchange,提问作者HillInHarwich

