Начиная с этого примера: два представления в 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 отдельно/без хитрости с иерархией представлений?)
Вы написали:
кажется, переход должен происходить на замененном узле просмотра
Это правильно.
Поведение в ваших примерах можно объяснить следующим образом:
В первой версии модификатор .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() для запуска внутреннего перехода. Хорошая находка!
Несколько связанный вопрос: stackoverflow.com/questions/67860214/…