Переходы во внутренних представлениях при полной замене внешнего вида

Начиная с этого примера: два представления в VStack, приходящие и уходящие с разными переходами:

struct ContentView: View {
    @State var value = false
    
    var body: some View {
        VStack {
            ZStack {
                value ? Color.red : Color.green
                
                VStack {
                    Text(String(describing: value))
                    Button("Toggle") {
                        value.toggle()
                    }
                }
            }
            .id(value)
            .transition(.move(edge: .leading))
            
            ZStack {
                value ? Color.yellow : Color.orange
                Text(String(value))
            }
            .frame(height: 200)
            .id(value)
            .transition(.move(edge: .bottom))
        }
        .animation(.easeInOut, value: self.value)
    }
}

Это работает нормально. Что меня удивило, так это то, что когда вы убираете модификатор .id («заменяете внешний вид»), внутренние виды больше не выполняют переходы:

struct ContentView: View {
    @State var value = false
    
    var body: some View {
        VStack {
            ZStack {
                value ? Color.red : Color.green
                
                VStack {
                    Text(String(describing: value))
                    Button("Change") {
                        value.toggle()
                    }
                }
            }
            // .id(value)   <-- not here
            .transition(.move(edge: .leading))
            
            ZStack {
                value ? Color.yellow : Color.orange
                Text(String(value))
            }
            .frame(height: 200)
            // .id(value)   <-- not here
            .transition(.move(edge: .bottom))
        }
        .id(value)   // <-- moved here
        .animation(.easeInOut, value: self.value)
    }
}

Похоже, именно так работают переходы в SwiftUI. Кажется, переход должен происходить на замененном узле View. Если представление с переходом появляется и исчезает из-за того, что его родительское представление приходит и уходит, это не запускает переход.

Чтобы прояснить пример, вот тот же пример без использования .id(), где представление контейнера хочет, чтобы части его содержимого появлялись и исчезали с переходом:

struct View1: View {
    @Binding var value: Bool
    var body: some View {
        VStack {
            ZStack {
                value ? Color.red : Color.green
                
                VStack {
                    Text(String(describing: value))
                    Button("Change") {
                        value.toggle()
                    }
                }
            }
            .transition(.move(edge: .leading))

            ZStack {
                value ? Color.yellow : Color.orange
                Text(String(value))
            }
            .frame(height: 200)
            .transition(.move(edge: .bottom))
        }
    }
}

struct View2: View {
    @Binding var value: Bool
    var body: some View {
        Button("Change") {
            value.toggle()
        }
    }
}


struct ContentView: View {
    @State var value = false
    
    var body: some View {
        ZStack {
         
            if value {
                View1(value: $value)
            } else {
                View2(value: $value)
            }
            
        }
        .animation(.easeInOut, value: self.value)
    }
}

Вопрос: Можно ли добавить переходы к внутренним представлениям в SwiftUI, которые запускаются при замене внешнего представления? (например: может ли последний пример иметь переходы, сохраняя при этом View1/View2 отдельно/без хитрости с иерархией представлений?)

Несколько связанный вопрос: stackoverflow.com/questions/67860214/…

Ralf Ebert 29.08.2024 22:30
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы написали:

кажется, переход должен происходить на замененном узле просмотра

Это правильно.

Поведение в ваших примерах можно объяснить следующим образом:

  • В первой версии модификатор .transition находился на контейнере (ZStack). Переходы работали, потому что идентификатор контейнера менялся при каждом изменении value. Из-за этого каждый раз казалось, что это другой контейнер.

  • Во второй версии идентификатор остается прежним, поэтому, хотя содержимое контейнера меняется, сам контейнер остается прежним. Вот почему для контейнера не происходит перехода.

Для вашей переработанной версии с использованием View1 и View2 вы можете заставить ее работать со следующими изменениями:

  • В View1 переместите модификатор .transition из контейнера к фактическим цветам, поскольку именно эти виды действительно меняются.

  • Кажется, это не работает при переключении представлений с помощью тернарного оператора, поэтому вместо этого используйте переключатель if-else.

  • Вам также необходимо переключить флаг withAnimation или добавить модификатор .animation к содержащему представлению.

struct View1: View {
    @Binding var value: Bool
    var body: some View {
        VStack {
            ZStack {
                if value {
                    Color.red
                        .transition(.move(edge: .leading))
                } else {
                    Color.green
                        .transition(.move(edge: .leading))
                }
                VStack {
                    Text(String(describing: value))
                    Button("Change") {
                        value.toggle()
                    }
                }
            }

            ZStack {
                if value {
                    Color.yellow
                        .transition(.move(edge: .bottom))
                } else {
                    Color.orange
                        .transition(.move(edge: .bottom))
                }
                Text(String(value))
            }
            .frame(height: 200)
        }
        .animation(.easeInOut, value: value) // 👈 ADDED
    }
}

Спасибо, это привело к решению, которое избавило меня от многих уродливых обходных путей. Таким образом, можно вкладывать переходы, но вам нужен отдельный .animation() для запуска внутреннего перехода. Хорошая находка!

Ralf Ebert 29.08.2024 22:30

Другие вопросы по теме