Сломанная анимация SwiftUI - PullRequest
1 голос
/ 29 февраля 2020

У меня изначально был этот вопрос здесь . Решение, предложенное @arsenius, работало на этом игрушечном примере. Однако мое приложение более сложное, и мне потребовалось целое время, чтобы выяснить, где анимация ломается. В примере я использовал два анимированных HStack. Но если я заменим эти HStack на два разных (!) пользовательских представления, анимация снова прекратится.

Вот код:

class State:ObservableObject{
    @Published var showSolution = false
}

struct ContentView: View {
    @EnvironmentObject var state:State

    var body:some View {
        VStack {
            if state.showSolution{
                CustomToggleOne()
                    .background(Color.red)
                    .id("one")
                    .animation(Animation.default)
                    .transition(.slide)
            } else {
                CustomToggleTwo()
                    .background(Color.yellow)
                    .id("two")
                    .animation(Animation.default.delay(2))
                    .transition(.slide)
            }
        }
    }
}

struct CustomToggleOne: View{
    @EnvironmentObject var state:State

    var body:some View{
        HStack{
            Spacer()
            Button(action:{
                withAnimation {
                    self.state.showSolution.toggle()
                }
            }){
                Text("Next")
            }.padding()
            Spacer()
        }
    }
}

struct CustomToggleTwo: View{
    @EnvironmentObject var state:State

    var body:some View{
        HStack{
            Spacer()
            Button(action:{
                withAnimation {
                    self.state.showSolution.toggle()
                }
            }){
                Text("Next")
            }.padding()
            Spacer()
        }
    }
}

I добавили экземпляр State в ContentView в SceneDelegate.swift как EnvironmentObject следующим образом:

let contentView = ContentView().environment(\.managedObjectContext, context).environmentObject(State())

Ожидаемую анимацию можно увидеть, если вместо ContentView дважды использовать CustomToggleOne() в *1019* CustomToggleTwo().

1 Ответ

1 голос
/ 29 февраля 2020

Вот правильный подход, некоторые пояснения приведены ниже и в комментариях. Протестировано с Xcode 11.2 / iOS 13.2

demo

Напоминание: не тестировать переходы в режиме предварительного просмотра - только на имитаторе или устройстве

// !! Don't name your types as API,
// State is SwiftUI type, might got unpredictable weird issues
class SolutionState:ObservableObject{
    @Published var showSolution = false
}

struct TestBrokenAnimation: View {
    @EnvironmentObject var state:SolutionState

    var body:some View {
        VStack {
            if state.showSolution{
                CustomToggleOne()
                    .background(Color.red)
                    .id("one")
                    .transition(.slide) // implicit animations confuse transition, don't use it
            } else {
                CustomToggleTwo()
                    .background(Color.yellow)
                    .id("two")
                    .transition(.slide)
            }
        }
    }
}
public func withAnimation<Result>(_ animation: Animation? = .default, 
              _ body: () throws -> Result) rethrows -> Result

, как он видел withAnimation не является состоянием, которое просто активирует анимации, определенные с помощью модификатора, оно явно применяет указанную собственную анимацию, поэтому наличие большего количества модификаторов определенно приведет к некоторым конфликтам .

Таким образом, используя только явные анимации с переходами.

struct CustomToggleOne: View{
    @EnvironmentObject var state:SolutionState

    var body:some View{
        HStack{
            Spacer()
            Button(action:{
                withAnimation(Animation.default.delay(2)) {
                    self.state.showSolution.toggle()
                }
            }){
                Text("Next")
            }.padding()
            Spacer()
        }
    }
}

struct CustomToggleTwo: View{
    @EnvironmentObject var state:SolutionState

    var body:some View{
        HStack{
            Spacer()
            Button(action:{
                withAnimation() {
                    self.state.showSolution.toggle()
                }
            }){
                Text("Next")
            }.padding()
            Spacer()
        }
    }
}
...