[英]Advanced animations in SwiftUI
I'm trying to learn SwiftUI from a series of lectures from Stanford( https://cs193p.sites.stanford.edu/ ) and am having some issues with animations.我正在尝试从斯坦福的一系列讲座中学习 SwiftUI( https://cs193p.sites.stanford.edu/ )并且在动画方面遇到了一些问题。 I'm creating the game Set which is a matching game where you select three cards and if they match then they are replaced with new cards etc.我正在创建游戏集,这是一个匹配游戏,您在其中 select 三张牌,如果它们匹配,则将它们替换为新牌等。
Currently to show that a card has been selected the border color turns orange.目前为了显示一张卡片已被选中,边框颜色变为橙色。 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. 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. This is the code that that draws the grid of cards这是绘制卡片网格的代码
//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()
}
}
Do I need to change my code structure to allow for this or am I being stupid and missing somthing obvious.我是否需要更改我的代码结构以允许这样做,还是我很愚蠢并且缺少明显的东西。 Just In case there is a code structure issue here is the rest of the code that directly deals with this.以防万一这里存在代码结构问题,是直接处理此问题的代码的 rest。
SettGame.swift 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
//
// 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 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()
}
}
Last three files are the total project code and might not be relevant to the question unless there is a code restructure issue最后三个文件是总项目代码,可能与问题无关,除非存在代码重组问题
I assume the problem is with changing the color and changing it back in some amount of time.我认为问题在于更改颜色并在一段时间内将其更改回来。 If that is the case, use DispatchQueue.asyncAfter(deadline: DispatchTime) {}
.如果是这种情况,请使用DispatchQueue.asyncAfter(deadline: DispatchTime) {}
。
usage:用法:
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.