繁体   English   中英

SwiftUI 中的高级动画

[英]Advanced animations in SwiftUI

我正在尝试从斯坦福的一系列讲座中学习 SwiftUI( https://cs193p.sites.stanford.edu/ )并且在动画方面遇到了一些问题。 我正在创建游戏集,这是一个匹配游戏,您在其中 select 三张牌,如果它们匹配,则将它们替换为新牌等。

游戏现在的样子

目前为了显示一张卡片已被选中,边框颜色变为橙色。 The issue that im having is that when three cards are selected and match I would like the border color to flash green if matched or red if not matched. 这是绘制卡片网格的代码

//MARK: -SettGameView Struct
struct SettGameView: View {
    
    //Game var
    @ObservedObject var game = SettGameInterpreter()
    
    //Get color scheme
    @Environment(\.colorScheme) var colorScheme
    
    //MARK: -Body view
    var body: some View {
        withAnimation {
            VStack {
                
                //Grid with cards
                Grid(game.cardsOnTableArray) { card in
                    
                    //ZStack that creates indiviual card
                    ZStack {

                        RoundedRectangle(cornerRadius: SettGameView.roundedRectangleCornerRaduis)
                            .fill()
                            .foregroundColor(self.colorScheme == .light ? Color.white : Color.black)
                        CardSymbol(card).padding() //Seperate struct that draws the card symbol
                        RoundedRectangle(cornerRadius: SettGameView.roundedRectangleCornerRaduis)
                            .stroke(lineWidth: SettGameView.cardStroke)
                            .foregroundColor(card.isSelected ? Color.orange : self.colorScheme == .light ? Color.black : Color.white)

                    }
                    .aspectRatio(3/4, contentMode: .fit)
                    .scaleEffect(card.isSelected ? 1 : 0.875)
                    .transition(.asymmetric(insertion: .offset(x: -200, y: 0), removal: .offset(x: 200, y: 0)))
                    .onTapGesture {
                        withAnimation(.linear(duration: 0.15)) {
                            self.game.selectCard(card)
                        }
                    }
                }
                
                //Add three cards button
                Button("Add More Cards") { self.game.addCards() }.padding()
            }.padding()
        }
    }

我是否需要更改我的代码结构以允许这样做,还是我很愚蠢并且缺少明显的东西。 以防万一这里存在代码结构问题,是直接处理此问题的代码的 rest。

SettGame.swift

//
//  SettGame.swift
//  Sett
//
//  Created by Marcus Q on 7/9/20.
//  Copyright © 2020 Marcus Q. All rights reserved.
//

import Foundation

//MARK: -SettGane / Model Struct
struct SettGame {
    
    //Relevant vars
    var cardsArray: [Card] = []
    let numberOfCardsOnTable: Int
    
    //MARK: -Cards On Table Array Var
    //Usage: gets all the cards on display
    var cardsOnTableArray: [Card] {
        var returnArray: [Card] = []
        for card in cardsArray {
            if card.displayNumber != nil { returnArray.append(card) }
        }
        returnArray.sort { cardOne, cardTwo in cardOne.displayNumber! < cardTwo.displayNumber! }
        return returnArray
    }
    
    //MARK: -Selected Card Indexes Var
    //Usage: get an array of all the cards that are selected
    var selectedCardIndexes: [Int] {
        var returnArray: [Int] = []
        for cardIndex in 0..<cardsArray.count {
            if cardsArray[cardIndex].isSelected == true {
                returnArray.append(cardIndex)
            }
        }
        return returnArray
    }
    
    //MARK: -Init
    //Input: number of cards on the table
    //Output: None
    init(numberOfCardsOnTable: Int) {
        
        //Define numberOfCardsOnTable
        self.numberOfCardsOnTable = numberOfCardsOnTable
        
        //currentId var
        var currentId: Int = 0
        
        //For each variation of the card create a card
        for shape in SettGame.SymbolShape.allCases {
            for color in SettGame.SymbolColor.allCases{
                for shade in SettGame.SymbolShade.allCases {
                    for shapeCount in SettGame.SymbolShapeCount.allCases {
                        
                        //Add one to the currentId
                        currentId += 1
                        
                        //Create card and append to the cardsArray
                        cardsArray.append(
                            Card(symbolShape: shape, symbolShapeColor: color, symbolShapeShadeing: shade, symbolShapeCount: shapeCount, id: currentId)
                        )
                        
                    }
                }
            }
        }
        
        //Shuffle cards
        cardsArray.shuffle()
        
        //Select the first numberOfCardsOnTable to be on screen
        for index in 0..<numberOfCardsOnTable {
            cardsArray[index].displayNumber = index
        }
    }
    
    //MARK: -SelectCard Function
    //Input: card that was slected
    //Output: None
    //Usage: play the game through selecting cards
    mutating func selectCard(_ selectedCard: Card) {
        
        //Get index of selected card
        let indexOfSelectedCard = cardsArray.firstIndex(of: selectedCard)!
        
        //Toggle cards selected state
        cardsArray[indexOfSelectedCard].isSelected = cardsArray[indexOfSelectedCard].isSelected ? false : true
        
        //If there are three card that are selected
        if selectedCardIndexes.count == 3 {
            
            if(elementsMatch(selectedCardIndexes)) {
                //For all cards that are now matched
                while(0 < selectedCardIndexes.count) {
                    //Add a card
                    addCardToDisplay(cardDisplayNumber: cardsArray[selectedCardIndexes[0]].displayNumber!)
                    //Remove matched card by setting matched to true
                    cardsArray[selectedCardIndexes[0]].isMatched = true
                }
            } else {
                //Deselect all cards
                for cardIndex in 0..<cardsArray.count { cardsArray[cardIndex].isSelected = false }
            }
        }
        
    }
    
    //MARK: -AddCardsToDisplay Function
    //Input: the cards index in the "display" array, if nil then create a new card
    //Output: None
    //Usage: add a card to the display, if given a card display number replace the old card if not then create a new card
    mutating func addCardToDisplay(cardDisplayNumber: Int? = nil) {
        
        //MARK: Next Free Index Var
        //Usage: returns the index of the next card that has not yet been displayed / matched
        var nextFreeIndex: Int? {
            for index in 0..<cardsArray.count {
                if cardsArray[index].displayNumber == nil && cardsArray[index].isMatched == false { return index }
                
            }
            return nil
        }
        
        //Make sure that there are stil cards in the deck
        if let nextFreeIndexInt = nextFreeIndex {
            //If user gave a card display number to set the new card to
            if let cardDisplayNumber = cardDisplayNumber {
                //Replace the old card with the new card via card display index
                cardsArray[nextFreeIndexInt].displayNumber = cardDisplayNumber
            } else {
                //Make another card
                cardsArray[nextFreeIndexInt].displayNumber = cardsOnTableArray.count + 1
            }
        }
    }
    
    //MARK: -ElementsMatch Function
    //Input: selected card indexes
    //Output: bool weather the elements match
    //Usage: check to see if the three selected cards match
    private func elementsMatch(_ selectedCardIndexes: [Int]) -> Bool {
        let cardOne = cardsArray[selectedCardIndexes[0]]
        let cardTwo = cardsArray[selectedCardIndexes[1]]
        let cardThree = cardsArray[selectedCardIndexes[2]]
        
        print(cardOne)
        print(cardTwo)
        print(cardThree)
        
        var check: Int = 0
        
        //Yes this is totaly shit in terms of coding style, I just wanted to test a concept and will am arleady wroking on fixing it
        if cardOne.symbolShape == cardTwo.symbolShape && cardTwo.symbolShape == cardThree.symbolShape && cardThree.symbolShape == cardOne.symbolShape { check += 1 }
        if cardOne.symbolShape != cardTwo.symbolShape && cardTwo.symbolShape != cardThree.symbolShape && cardThree.symbolShape != cardOne.symbolShape { check += 1 }
        print("Card Shape \(check)")
        if cardOne.symbolShapeColor == cardTwo.symbolShapeColor && cardTwo.symbolShapeColor == cardThree.symbolShapeColor && cardThree.symbolShapeColor == cardOne.symbolShapeColor { check += 1 }
        if cardOne.symbolShapeColor != cardTwo.symbolShapeColor && cardTwo.symbolShapeColor != cardThree.symbolShapeColor && cardThree.symbolShapeColor != cardOne.symbolShapeColor { check += 1 }
        print("Shape Color \(check)")
        if cardOne.symbolShapeCount == cardTwo.symbolShapeCount && cardTwo.symbolShapeCount == cardThree.symbolShapeCount && cardThree.symbolShapeCount == cardOne.symbolShapeCount { check += 1 }
        if cardOne.symbolShapeCount != cardTwo.symbolShapeCount && cardTwo.symbolShapeCount != cardThree.symbolShapeCount && cardThree.symbolShapeCount != cardOne.symbolShapeCount { check += 1 }
        print("Card Shape Count \(check)")
        if cardOne.symbolShapeShadeing == cardTwo.symbolShapeShadeing && cardTwo.symbolShapeShadeing == cardThree.symbolShapeShadeing && cardThree.symbolShapeShadeing == cardOne.symbolShapeShadeing { check += 1 }
        if cardOne.symbolShapeShadeing != cardTwo.symbolShapeShadeing && cardTwo.symbolShapeShadeing != cardThree.symbolShapeShadeing && cardThree.symbolShapeShadeing != cardOne.symbolShapeShadeing { check += 1 }
        print("Card Shape Shadeing \(check)")
        
        return check == 4 ? true : false
    }
    
    //MARK: -Enums
    enum SymbolShape: CaseIterable { case diamond, capsule, rectangle }
    enum SymbolShade: CaseIterable { case solid, opaque, transparent }
    enum SymbolColor: CaseIterable { case red, blue, yellow }
    enum SymbolShapeCount: CaseIterable { case one, two, three }
    
}

//MARK: -Card Struct
struct Card: Identifiable {
    //Card display vars
    var isSelected: Bool = false
    var displayNumber: Int? = nil
    var isMatched: Bool = false {
        didSet {
            self.isSelected = false
            self.displayNumber = nil
        }
    }
    
    //Card type vars
    var symbolShape: SettGame.SymbolShape
    var symbolShapeColor: SettGame.SymbolColor
    var symbolShapeShadeing: SettGame.SymbolShade
    var symbolShapeCount: SettGame.SymbolShapeCount
    
    //Id
    var id: Int
}

SettGameInterpreter.swift

//
//  SettGameInterpreter.swift
//  Sett
//
//  Created by Marcus Q on 7/9/20.
//  Copyright © 2020 Marcus Q. All rights reserved.
//

import SwiftUI

//MARK: -SettGameInterperter Struct
class SettGameInterpreter: ObservableObject {
    
    //MARK: -Init Function
    //Input: None
    //Output: None
    //Usage: define settGame and cardsArray
    init() {
        gameInfo = SettGame(numberOfCardsOnTable: numberOfCardsOnTable)
    }
    
    //MARK: -Game Constants
    let numberOfCardsOnTable: Int = 12
    
    //MARK: -Observed var
    @Published private var gameInfo: SettGame
    
    //MARK: -Access for view to model
    var cardsArray: [Card] { gameInfo.cardsArray }
    var cardsOnTableArray: [Card] { gameInfo.cardsOnTableArray }
    
    func selectCard(_ selectedCard: Card) { gameInfo.selectCard(selectedCard) }
    func addCards() { for _ in 0..<3 { gameInfo.addCardToDisplay() } }
    
}

SettGameView.swift

//
//  ContentView.swift
//  Sett
//
//  Created by Marcus Q on 7/9/20.
//  Copyright © 2020 Marcus Q. All rights reserved.
//

import SwiftUI

//MARK: -SettGameView Struct
struct SettGameView: View {
    
    //Game var
    @ObservedObject var game = SettGameInterpreter()
    
    //Get color scheme
    @Environment(\.colorScheme) var colorScheme
    
    //MARK: -Body view
    var body: some View {
        withAnimation {
            VStack {
                
                //Grid with cards
                Grid(game.cardsOnTableArray) { card in
                    
                    //ZStack that creates indiviual card
                    ZStack {
                        RoundedRectangle(cornerRadius: SettGameView.roundedRectangleCornerRaduis)
                            .fill()
                            .foregroundColor(self.colorScheme == .light ? Color.white : Color.black)
                        CardSymbol(card).padding() //Seperate struct that draws the card symbol
                        RoundedRectangle(cornerRadius: SettGameView.roundedRectangleCornerRaduis)
                            .stroke(lineWidth: SettGameView.cardStroke)
                            .foregroundColor(card.isSelected ? Color.orange : self.colorScheme == .light ? Color.black : Color.white)
                    }
                    .aspectRatio(3/4, contentMode: .fit)
                    .scaleEffect(card.isSelected ? 1 : 0.875)
                    .transition(.asymmetric(insertion: .offset(x: -200, y: 0), removal: .offset(x: 200, y: 0)))
                    .onTapGesture {
                        withAnimation(.linear(duration: 0.15)) {
                            self.game.selectCard(card)
                        }
                    }
                }
                
                //Add three cards button
                Button("Add More Cards") { self.game.addCards() }.padding()
            }.padding()
        }
    }
    
    //MARK: -SettGameView Drawing Constants
    static let themeColor = Color.blue
    static let spacer: CGFloat = 5
    static let roundedRectangleCornerRaduis: CGFloat = 10
    static let cardStroke: CGFloat = 5
    
}

//MARK: -Ignore this
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        SettGameView()
    }
}

最后三个文件是总项目代码,可能与问题无关,除非存在代码重组问题

我认为问题在于更改颜色并在一段时间内将其更改回来。 如果是这种情况,请使用DispatchQueue.asyncAfter(deadline: DispatchTime) {}

用法:

func calledWhenNotMatching() {
    self.borderColor = .red
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {//0.1 seconds
         self.borderColor = .white
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM