#swift #swiftui
#swift #swiftui
Вопрос:
Я пытаюсь загрузить удаленный ресурс, поэтому я использую .onAppear
для этого. Однако, когда я использую if/else
внутри представления, это приводит к его срабатыванию дважды.
class FooVM: ObservableObject {
@Published var remoteText: String? = nil
func load() {
DispatchQueue.main.asyncAfter(deadline: .now() 2) {
self.remoteText = "(Date())"
}
}
}
struct Foo: View {
@StateObject private var vm = FooVM()
var body: some View {
content
.onAppear(perform: vm.load)
}
@ViewBuilder
private var content: some View {
if let text = vm.remoteText {
Text(text)
} else {
Circle()
}
}
}
struct ContentView: View {
var body: some View {
TabView {
Foo()
.tabItem {
Image(systemName: "circle.fill")
Text("Home")
}
}
}
}
РЕДАКТИРОВАТЬ: оказывается, он воспроизводится только тогда, когда содержимое находится внутри TabView
. Я обновил приведенный выше код
Почему onAppear
(и, следовательно load
,) запускается дважды? На самом деле, на игровой площадке он срабатывает повторно каждые 2 секунды, но в симуляторе срабатывает только дважды.
Как мне этого избежать? Имейте в виду, что это упрощенный пример.
Интересно, что если бы это был не выбор между Text
и Circle
, а просто другой текст внутри Text
, тогда он сработал бы только один раз:
Text(vm.remoteText ?? "loading...")
Комментарии:
1. С Xcode 12.1 / iOS 14.1
load
вызывается только один раз с предоставленным кодом. Какую среду вы использовали?2. Xcode 12.2 / iOS 14.2
3. Я нахожусь на 12.2 / iOS 14.2 и вижу, что load вызывается только один раз. Вы пробовали на симуляторе / за пределами игровой площадки?
4. @nicksarno, да — пробую это на симуляторе на самом деле
5. Ну, я не могу воспроизвести это с текущим кодом. Единственное, что приходит на ум, это, возможно, представление будет «появляться» при переключении содержимого. Попробуйте внедрить ‘content’ в ZStack и вместо этого вызвать .onAppear в ZStack. Таким образом, ZStack сохраняется, пока содержимое снова появляется? Просто догадываюсь, ха-ха
Ответ №1:
Когда ваш контент переключается в операторе if, Foo () должен полностью появиться снова. Решение состоит в том, чтобы встроить содержимое Foo () в ZStack (или аналогичный), чтобы ему не нужно было повторно отображать все представление и снова вызывать .onAppear. Здесь ZStack остается отображаемым при переключении содержимого.
var body: some View {
ZStack {
content
}
.onAppear(perform: vm.load)
}
Комментарии:
1. Действительно, использование
VStack
orZStack
(но неGroup
) решает эту проблему. Спасибо! Я не думаю, однако, что причина, которую вы указали, т. Е.Foo
Требуется повторный рендеринг, верна, посколькуFoo
не требуется повторный рендеринг, когда он не находится внутри aTabView
. Кроме того, почемуVStack
/ZStack
устраняет необходимость повторного рендеринга, но нет_ConditionalContent<Text, Circle>
(чтоcontent
и есть). Если вы могли бы изменить объяснение, я приму это как ответ. Спасибо2. Я думаю, повторный рендеринг был неправильным словом. Похоже, что это больше связано с размещением .onAppear, чем с TabView. Если у вас есть полное объяснение этому, не стесняйтесь добавлять свой собственный ответ выше моего.
3. Спасибо @nicksarno за ваше решение, оно помогло мне. Просто добавляю небольшое примечание, поскольку кто-то может столкнуться с моей проблемой, когда я добавлял встроенный мой контент в ZStack, у него было большое пространство сверху, поэтому я добавил (alignment: .top) в ZStack.