如何基于单个CardSubview类创建不同样式的iOS游戏卡片?
Hey there! Let's fix that issue with your Set-like iOS game where all cards are updating their styles together. The root problem here is using static properties (like your index1/index2/index3/index4) for your CardSubview—static values are shared across every instance of the class, so changing them updates every card at once. Here's how to refactor your code to get independent, unique card styles:
1. Replace Static Properties with Instance-Level Properties
First, ditch the static indices and give each CardSubview its own set of style properties. Instead of relying on class-wide values, define properties that belong to individual card instances. For clarity, use enums to represent your style options (this avoids magic numbers and makes your code more readable):
// Inside CardSubview.swift class CardSubview: UIView { // Define enums for each style dimension (matches Set's game rules) enum CardShape { case square, triangle, circle } enum CardColor { case red, green, purple } enum CardFill { case solid, striped, outlined } // Instance properties: each card has its own unique values var shape: CardShape = .square var color: CardColor = .red var fill: CardFill = .solid var shapeCount: Int = 1 // 1, 2, or 3 // ... rest of your view code (init, draw methods, etc.) }
2. Initialize Each Card with Unique Style Data
In your CardBoardView, generate all 80 unique style combinations first, then create a CardSubview for each combination and pass in its specific style values.
First, create a helper struct to organize style data neatly:
struct CardStyle { let shape: CardSubview.CardShape let color: CardSubview.CardColor let fill: CardSubview.CardFill let count: Int }
Then generate all possible unique styles (Set has 3^4 = 81 total styles—remove one to get 80):
// Inside CardBoardView.swift func generateAllUniqueCardStyles() -> [CardStyle] { let shapes: [CardSubview.CardShape] = [.square, .triangle, .circle] let colors: [CardSubview.CardColor] = [.red, .green, .purple] let fills: [CardSubview.CardFill] = [.solid, .striped, .outlined] let counts = [1, 2, 3] var allStyles = [CardStyle]() // Generate every possible combination for shape in shapes { for color in colors { for fill in fills { for count in counts { allStyles.append(CardStyle(shape: shape, color: color, fill: fill, count: count)) } } } } // Remove one random style to get 80 unique cards if let randomIndex = allStyles.indices.randomElement() { allStyles.remove(at: randomIndex) } return allStyles }
Now create and add each card to your board:
func setupCardBoard() { let uniqueStyles = generateAllUniqueCardStyles() for (index, style) in uniqueStyles.enumerated() { let card = CardSubview() // Assign the unique style to this card instance card.shape = style.shape card.color = style.color card.fill = style.fill card.shapeCount = style.count // Position the card (adjust layout logic to fit your board) let cardSize = CGSize(width: 80, height: 120) let x = (index % 8) * Int(cardSize.width) + 20 let y = (index / 8) * Int(cardSize.height) + 20 card.frame = CGRect(x: x, y: y, width: cardSize.width, height: cardSize.height) addSubview(card) } }
3. Update Card Drawing Logic to Use Instance Properties
Modify your draw(_ rect: CGRect) method in CardSubview to use the instance properties instead of static indices. For example:
override func draw(_ rect: CGRect) { super.draw(rect) // Draw based on this card's unique properties switch shape { case .square: drawSquare(with: color, fillStyle: fill, count: shapeCount, in: rect) case .triangle: drawTriangle(with: color, fillStyle: fill, count: shapeCount, in: rect) case .circle: drawCircle(with: color, fillStyle: fill, count: shapeCount, in: rect) } } // Example helper method for drawing squares private func drawSquare(with color: CardColor, fillStyle: CardFill, count: Int, in rect: CGRect) { // Convert enum values to actual UIColors/paths/fill logic let uiColor: UIColor = { switch color { case .red: return .red case .green: return .green case .purple: return .purple } }() // Draw 1, 2, or 3 squares with the correct fill style // ... your drawing implementation here }
4. Bonus: Dynamically Update Individual Card Styles (If Needed)
If you ever need to change a single card's style later (e.g., for game mechanics), add a method to CardSubview to update its properties and trigger a redraw:
func updateStyle(with newStyle: CardStyle) { self.shape = newStyle.shape self.color = newStyle.color self.fill = newStyle.fill self.shapeCount = newStyle.count setNeedsDisplay() // Tells the view to redraw with the new style }
With these changes, every CardSubview will have its own independent style. Creating 80 unique cards is as simple as generating the style combinations and passing them to each card instance, and modifying one card won't affect any others.
内容的提问来源于stack exchange,提问作者INFORMATIKER IM ALL

