Скажем, у меня есть такой простой экран профиля:

class Model: ObservableObject {
    @Published var isSignedIn = false
    
    init() {}
    
    func login() {
        //Some networking here
        isSignedIn = true
    }
    func logout() {
        //Some networking here
        isSignedIn = false
    }
}

struct ContentView: View {
    @ObservedObject var model = Model()

    var body: some View {
        ZStack {
            //ProfileView
            VStack {
                //Some Views with WithAnimation inside
                // ...
 
                Text("Hello, Dear User!")
                Button(action: {
                    self.model.logout()
                }) {
                    Text("Sign Out")
                }
            }
            .opacity(model.isSignedIn ? 1 : 0)

            //LoginView
            VStack {
                Text("Hello, Stranger")
                Button(action: {
                    self.model.login()
                }) {
                    Text("Sign In")
                }
            }
            .opacity(model.isSignedIn ? 0 : 1)
        }
    }
}

И я хочу применить анимацию к изменению непрозрачности.

Первый подход - использовать модификатор .animation. Но у него есть определенные недостатки: он не работает должным образом, если внутренний вид имеет WithAnimation - он отменяет анимацию, установленную с помощью WithAnimation.

Мой второй подход к использованию .onReceive:

class Model: ObservableObject {
    @Published var isSignedIn = false
    
    init() {}
    
    func login() {
        isSignedIn = true
    }
    func logout() {
        isSignedIn = false
    }
}

struct ContentView: View {
    @ObservedObject var model = Model()
    
    @State var isSignedIn = false

    var body: some View {
        ZStack {
            //ProfileView
            VStack {
                Text("Hello, Dear User!")
                Button(action: {
                    self.model.logout()
                }) {
                    Text("Sign Out")
                }
            }
            .opacity(model.isSignedIn ? 1 : 0)

            //LoginView
            VStack {
                Text("Hello, Stranger")
                Button(action: {
                    self.model.login()
                }) {
                    Text("Sign In")
                }
            }
            .opacity(model.isSignedIn ? 0 : 1)
        }
        .onReceive(self.model.$isSignedIn) { value in
            withAnimation(Animation.easeIn) {
                self.isSignedIn = value
            }
        }
    }
}

Есть некоторые проблемы (на мой взгляд):

  • Еще одна переменная @State требуется для обработки изменений в модели.
  • Каждый блок WithAnimation требует отдельного .onReceive

Итак, вопрос: правильно ли это применить WithAnimation к @ObservedObject или есть лучшее решение?

1
Triff 30 Авг 2020 в 01:53

2 ответа

Лучший ответ

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

Button(action: {
    withAnimation(.easeInOut) { // add animation
        self.model.logout()
    }
}) {
    Text("Sign Out")
}
0
pawello2222 29 Авг 2020 в 23:09

Ваш первый подход можно изменить, поэтому вам не нужно использовать дополнительное свойство @State.

class Model: ObservableObject {
    @Published var isSignedIn = false
    
    init() {}
    
    func login() {
        withAnimation(Animation.easeIn) {
        isSignedIn = true
        }
    }
    func logout() {
        withAnimation(Animation.easeIn) {
        isSignedIn = false
        }
    }
}

struct SView: View {
    @ObservedObject var model = Model()

    var body: some View {
        ZStack {
            //ProfileView
            VStack {
                Rectangle()
                Text("Hello, Dear User!")
                Button(action: {
                    self.model.logout()
                }) {
                    Text("Sign Out")
                }
            }
            .opacity(model.isSignedIn ? 1 : 0)

            //LoginView
            VStack {
                Rectangle()
                Text("Hello, Stranger")
                Button(action: {
                    self.model.login()
                }) {
                    Text("Sign In")
                }
            }
            .opacity(model.isSignedIn ? 0 : 1)
        }

    }
}
struct SwiftUIView_Previews: PreviewProvider {
    static var previews: some View {
        SView()
    }
}

Во втором подходе вам нужно изменить

.opacity(model.isSignedIn ? 1 : 0)

Кому

.opacity(self.isSignedIn ? 1 : 0)
1
Dharman 29 Авг 2020 в 23:21