SwiftUI - Как ограничить объем анимации только переходом onAppear

Я новичок в SwiftUI и работаю над некоторыми примерами проектов, чтобы понять его, и я застреваю в ограничении объема анимации, которую я установил для .transition для AnimationModifier, поэтому он влияет только на анимацию перехода и ничего больше в представлении.

Хотя отдельные переходы соблюдаются для onAppear() и еще один для onDisappear(). Анимация в AnimatableModifier отменяет удаление элемента из сетки даже при явном объявлении

Я пробовал явно установить для Animation переход.offset как в AnimatableModifier, так и для CardView в GameView, и когда я это делаю, анимация вообще не запускается:

.transition(AnyTransition.offset(CGSize.init(width: randomXLocation, height: -offset.height-50)).animation(Animation.easeInOut(duration: 1.25).delay(delay)))
                    

Итак, должен быть способ ограничить область действия или явно объявить анимацию для перехода или двух отдельных анимаций в модификаторе анимации, но я не нахожу никаких ресурсов о том, как двигаться вперед.

GameView.swift

struct GameView: View {
    @ObservedObject var viewModel: SetGameViewModel
    @State var delay: Double = 0.1
    
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Grid(newItems: self.viewModel.newCards,
                    items: self.viewModel.cards.itemsAtWithIds(ids: self.viewModel.idOfCardsToDisplay)) { card in
                        CardView(card: card, bodyGeoProxy: geometry, delay: self.delay).onTapGesture {
                            self.viewModel.choose(card: card)
                            }
                    .transition(AnyTransition.offset(CGSize.init(width: randomXLocation, height: -offset.height-50)))
                    .animation(Animation.easeInOut(duration: 1.25).delay(delay))
                        .onAppear() {
                            let maxDelay: Double = Double(self.viewModel.cards.itemsAtWithIds(ids: self.viewModel.idOfCardsToDisplay).count)*0.2 + 0.2
                            if self.delay < 2.5 {
                                self.delay = self.delay + 0.2
                            } else if self.delay >= maxDelay {
                                self.delay = 0.1
                            }
                        }
                    
                }
                
                HStack{
                    Button(action: {
                        self.viewModel.dealThreeCards()
                    }) {
                        Text("Hit Me")
                    }
                    Spacer()
                    Text("Score: \(self.viewModel.score)")
                    Spacer()
                    Button(action: {
                        self.viewModel.dealThreeCards()
                    }) {
                        Text("New Game")
                    }
                }
            }
        }
        
        
        
        
        
    }
} 

GameView.swift

struct CardView: View{
    var card: SetGame<SoloSetCardContent>.Card
    var bodyGeoProxy: GeometryProxy
    var delay: Double
    
    var body: some View {
        GeometryReader { geometry in
            self.body(for: geometry)
        }
        
    }
    
    init(card: SetGame<SoloSetCardContent>.Card, bodyGeoProxy: GeometryProxy, delay: Double) {
        self.card = card
        self.bodyGeoProxy = bodyGeoProxy
        self.delay = delay
    }
    
    
    @ViewBuilder
    func body(for geometryProxy: GeometryProxy) -> some View {
        
            ZStack {
                if card.isSelected {
                    RoundedRectangle(cornerRadius: 5)
                        .fill(Color.gray)
                        .frame(width: geometryProxy.size.width-4, height: geometryProxy.size.height-4, alignment: .center)
                        .border(Color.blue, width: 2)
                        .animation(nil)
                } else {
                    RoundedRectangle(cornerRadius: 5)
                        .fill(Color.gray)
                        .frame(width: geometryProxy.size.width-4, height: geometryProxy.size.height-4, alignment: .center)
                        .border(Color.red, width: 2)
                        .animation(nil)
                }
                VStack {
                    ForEach(0..<self.card.content.deckShapes.count) { index in
                        VStack {
                            Spacer(minLength: 5)
                            ShapeView(setShape: self.card.content.deckShapes[index])
                                .frame(width: (geometryProxy.size.width-geometryProxy.size.width/5), height: geometryProxy.size.height/5, alignment: .center)
                            Spacer(minLength: 5)
                        }
                    }
                }
            }
            .deal(delay: self.delay, offset: bodyGeoProxy.size)
        }
}

Dealer.Swift - AnimatableModifier

struct Dealer: AnimatableModifier {
    
    
    @State var show: Bool = false
    var delay: Double
    var offset: CGSize
    var randomXLocation: CGFloat {
        CGFloat.random(in: -offset.width ..< offset.width)
    }
    
    func body(content: Content) -> some View {
        ZStack {
            if show {
                content
                    .transition(AnyTransition.offset(CGSize.init(width: randomXLocation, height: -offset.height-450)))
                    .animation(Animation.easeInOut(duration: 1.25).delay(delay))
                    
            }
        }
            
        .onAppear {
            withAnimation {
                self.show = true
            }
        }
        .onDisappear {
            withAnimation {
                self.show = false
            }
        }

    }

}

extension View {
    func deal(delay: Double, offset: CGSize) -> some View {
        self.modifier(Dealer(delay: delay, offset: offset))
    }
}

1 ответ

Решение

Я смог решить эту проблему, удалив анимацию из содержимого тела (и в другом месте) и добавив в withAnimation часть .onAppear метод в body функция AnimationModifier

func body(content: Content) -> some View {
        ZStack {
            if show {
                content
                    .transition(.asymmetric(insertion: .offset(CGSize.init(width: randomXLocation, height: -offset.height-50)),
                                            removal: .offset(CGSize.init(width: randomXLocation, height: offset.height+50))))
            }
        }
        .onDisappear {
            withAnimation (Animation.easeInOut(duration: 1.25).delay(0))  {
                self.show = false
            }
        }
        .onAppear {
            withAnimation (Animation.easeInOut(duration: 1.25).delay(self.delay)) {
                self.show = true
            }
        }
    }
Другие вопросы по тегам