Анимированные переходы и содержимое в ScrollView в SwiftUI
Я не совсем ветеран SwiftUI, но у меня есть пара приложений средней сложности. Тем не менее, я не могу утверждать, что полностью понимаю это, и я надеюсь, что кто-то с более глубокими знаниями сможет пролить свет на эту проблему:
У меня есть некоторый контент, который я хочу включать и выключать, в отличие от .sheet (), но мне нужен больший контроль над ним. Вот некоторый "реконструированный" код, но он должен уловить суть:
struct ContentView: View {
@State private var isShown = false
var body: some View {
GeometryReader { g in
VStack {
ZStack(alignment: .top) {
// This element "holds" the size
// while the content is hidden
Color.clear
// Content to be toggled
if self.isShown {
ScrollView {
Rectangle()
.aspectRatio(1, contentMode: .fit)
.frame(width: g.size.width) // This is a "work-around"
} // ScrollView
.transition(.move(edge: .bottom))
.animation(.easeOut)
}
} // ZStack
// Button to show / hide the content
Button(action: {
self.isShown.toggle()
}) {
Text(self.isShown ? "Hide" : "Show")
}
} // VStack
} // GeometryReader
}
}
Что он делает, так это то, что он включает и выключает некоторый блок содержимого (представленный здесь прямоугольником в ScrollView). Когда это происходит, представление содержимого меняется, перемещаясь снизу с некоторой анимацией. Обратное происходит при повторном нажатии кнопки.
Этот конкретный фрагмент кода работает так, как задумано, но только благодаря этой строке:
.frame(width: g.size.width) // This is a "work-around"
Что, в свою очередь, требует дополнительного GeometryReader, в противном случае ширина содержимого анимируется, создавая нежелательный эффект (еще одно обнаруженное мною "исправление" - использование модификатора .fixedSize(), но для получения разумных эффектов требуется контент, который принимает собственную ширину, например текст)
Мой вопрос к мудрым: можно ли красиво перейти к контенту, инкапсулированному в ScrollView, без использования таких "исправлений"? В качестве альтернативы, есть ли более элегантное решение для этого?
Быстрое добавление к вопросу, следующему за ответом @Asperi: содержимое должно оставаться анимированным. Ты моя единственная надежда,
–Баглан
1 ответ
Вот решение (обновлено body
без GeometryReader
). Протестировано с Xcode 11.4 / iOS 13.4
var body: some View {
VStack {
ZStack(alignment: .top) {
// This element "holds" the size
// while the content is hidden
Color.clear
// Content to be toggled
if self.isShown {
ScrollView {
Rectangle()
.aspectRatio(1, contentMode: .fit)
.animation(nil) // << here !!
} // ScrollView
.transition(.move(edge: .bottom))
.animation(.easeOut)
}
} // ZStack
// Button to show / hide the content
Button(action: {
self.isShown.toggle()
}) {
Text(self.isShown ? "Hide" : "Show")
}
} // VStack
}