
SwiftUIでいこう!- スタンフォード大学Lecture 8: Animation Demonstration
カードを並べて2枚ずつ開けて行ってマッチすればカードを消していくという操作ができてきました。次にアニメーション をつけて見栄えを良くしていきます。
最終的にはカードの配置をする画面、カードを配るボタン、シャッフルボタン、リスタートのボタンを以下のように配置していきその内容を書いていきます。
gameBodyにはランダムに並んだカード。
deckBodyにはカードを配るように一枚が配置してあり、HStackにはランダムにカードを配置するShuffleボタンと再スタートするrestartボタンを配置しています。
表示部分のコードですが、
var body: some View {
VStack{
gameBody
deckBody
HStack{
shuffle
Spacer()
restart
}
.padding(.horizontal)
}
.padding()
}
VStack{}で縦に積んで、最後 HStack{}で平積みしています。
それぞれのコードは gameBody
var gameBody:some View{
AspectVGrid(items:game.cards,aspectRatio:2/3) {card in
if isundeal(card) || card.isMatched && !card.isFaceUp{
Color.clear
}else{
CardView(card:card)
.matchedGeometryEffect(id:card.id, in :dealingNamespace)
.padding(4)
.transition(AnyTransition.asymmetric(insertion: .identity, removal:.scale))
.zIndex(zIndex(of:card))
.onTapGesture {
withAnimation{
game.choose(card)
}
}
}
}
.foregroundColor(CardConstants.color)
}
トランジションでアニメーション 効果を出しています。
.transition(AnyTransition.asymmetric(insertion: .identity, removal:.scale))
asymmetricメソッドを使うと、Viewの表示と非表示で異なるトランジション効果を適用できます。
第1引数(insertion)で、表示時のトランジション効果を指定。
第2引数(removal)で、非表示時のトランジションを指定します。
View間で以下を使って同調させてアニメーション します。
.matchedGeometryEffect(id:card.id, in :dealingNamespace)
ちなみに、アニメーション には適用するものにより以下
Viewの形状やエフェクトの変化に対して、アニメーションを適用するには、animationモディファイア
Viewではなく、特定のプロパティの変化対してアニメーションを適用するには、withAnimation関数
を使い分けます。
deckBody
var deckBody:some View{
ZStack{
ForEach(game.cards.filter(isundeal)){card in
CardView(card: card)
.matchedGeometryEffect(id:card.id, in :dealingNamespace)
.transition(AnyTransition.asymmetric(insertion:.opacity , removal: .identity))
}
}
.frame(width: CardConstants.undealWidth, height: CardConstants.undealHeight)
.foregroundColor(CardConstants.color)
.onTapGesture {
for card in game.cards{
withAnimation(dealAnimation(for: card)){
deal(card)
}
}
}
}
shuffle
var shuffle:some View{
Button("Shuffle"){
withAnimation{
game.shuffle()
}
}
}
game.shuffle()は
func shuffle(){
model.shuffle()
}
mutating func shuffle(){
cards.shuffle()
}
に紐付いていいます。
restart
var restart:some View{
Button("restart"){
withAnimation{
dealt = []
game.restart()
}
}
}
game.restart()も同様に
func restart(){
model = EmojiGame.createMemoryGame()
}
となっており同じファイルの上部で定義している
static func createMemoryGame() -> Game<String>{
Game<String>(numberOfpairOfCards:10){pairIndex in
EmojiGame.emojis[pairIndex]
}
}
に紐付いています。
これらをサポートする構造体を作って使っていきます。
private struct CardConstants{
static let color = Color.red
static let aspectRatio:CGFloat = 2/3
static let dealDuration:Double = 0.5
static let totalDealDuration:Double = 2
static let undealHeight:CGFloat = 90
static let undealWidth:CGFloat = undealHeight * aspectRatio
}