SwiftUI динамически добавляет subview, но анимация не работает
Я хотел бы создать представление в SwiftUI, которое добавляло бы подпредставление динамически и с анимацией.
struct ContentView : View {
@State private var isButtonVisible = false
var body: some View {
VStack {
Toggle(isOn: $isButtonVisible.animation()) {
Text("add view button")
}
if isButtonVisible {
AnyView(DetailView())
.transition(.move(edge: .trailing))
.animation(Animation.linear(duration: 2))
}else{
AnyView(Text("test"))
}
}
}
}
Приведенный выше код отлично работает с анимацией. однако, когда я перемещаю часть выбора представления в функцию, анимация больше не работает (поскольку я хочу добавлять разные представления динамически, поэтому я помещаю логику в функцию).
struct ContentView : View {
@State private var isButtonVisible = false
var body: some View {
VStack {
Toggle(isOn: $isButtonVisible.animation()) {
Text("add view button")
}
subView().transition(.move(edge: .trailing))
.animation(Animation.linear(duration: 2))
}
func subView() -> some View {
if isButtonVisible {
return AnyView(DetailView())
}else{
return AnyView(Text("test"))
}
}
}
Мне это кажется совершенно одинаковым, однако я не понимаю, почему у них другой результат. Может кто-нибудь объяснить мне, почему? и какие-нибудь лучшие решения? большое спасибо!
3 ответа
Вот ваш код, измененный так, чтобы он работал:
struct ContentView : View {
@State private var isButtonVisible = false
var body: some View {
VStack {
Toggle(isOn: $isButtonVisible.animation()) {
Text("add view button")
}
subView()
.transition(.move(edge: .trailing))
.animation(Animation.linear(duration: 2))
}
}
func subView() -> some View {
Group {
if isButtonVisible {
DetailView()
} else {
Text("test")
}
}
}
}
Обратите внимание на две вещи:
- Ваши два приведенных выше примера разные, поэтому вы получаете разные результаты. Первый применяет переход и анимацию к DetailView, а затем стирает их с помощью AnyView. Второй тип стирает DetailView с AnyView, затем применяет переход и анимацию.
- Скорее, используя AnyView и стирание типа, я предпочитаю инкапсулировать условную логику внутри
Group
Посмотреть. Тогда тип, который вы возвращаете, будетGroup
, который будет правильно анимирован. - Если вам нужны разные анимации для двух возможностей для вашего подпредставления, теперь вы можете применить их непосредственно к
DetailView()
илиText("test")
.
Обновить
В Group
метод будет работать только с if
, elseif
, а также else
заявления. Если вы хотите использовать переключатель, вам придется обернуть каждую ветвь вAnyView()
. Однако это нарушает переходы / анимацию. С помощьюswitch
и установка пользовательской анимации в настоящее время невозможна.
Мне удалось заставить его работать с оператором switch, заключив функцию, которая возвращает AnyView
в VStack
. Мне также пришлось датьAnyView
ан .id
поэтому SwiftUI может знать, когда он изменится. Это в Xcode 11.3 и iOS 13.3
struct EnumView: View {
@ObservedObject var viewModel: ViewModel
var body: some View {
VStack {
view(for: viewModel.viewState)
.id(viewModel.viewState)
.transition(.opacity)
}
}
func view(for viewState: ViewModel.ViewState) -> AnyView {
switch viewState {
case .loading:
return AnyView(LoadingStateView(viewModel: self.viewModel))
case .dataLoaded:
return AnyView(LoadedStateView(viewModel: self.viewModel))
case let .error(error):
return AnyView(ErrorView(error: error, viewModel: viewModel))
}
}
}
Также для моего примера в ViewModel
Мне нужно завернуть viewState
изменения в withAnimation
блокировать
withAnimation {
self.viewState = .loading
}
В iOS 14 добавили возможность использовать if let
а также switch
операторы в построителях функций. Может быть, это поможет в ваших проблемах:
https://www.hackingwithswift.com/articles/221/whats-new-in-swiftui-for-ios-14 (внизу статьи)