Работают ли onAppear и onDisappear в NavigationView приложения SwiftUI должным образом?

Я хотел бы знать, является ли поведение onAppear и onDisappear в SwiftUI (Xcode 11 beta 6, когда я писал это) более полезным для разработчика, или это просто больше проблема, чем функция.

Прямо сейчас, если мы будем использовать каскадную навигацию, как вы найдете в примере кода, который я прилагаю (который компилируется и отлично работает в Xcode 11b6), вывод консоли пользователя, перемещающегося вперед и назад, будет просто запускать onAppear только в случае новый вид нагрузки в прямом направлении (имеется в виду углубление).

В навигации: Root -> NestedView1 -> NestedView2 -> NestedView3, при добавлении помощника отладки на каждую стадию просмотра,

  .onAppear(perform: {print("onAppear level N")})
  .onDisappear(perform: {print("onDisappear level N")})

консоль отладки покажет

onAppear root level 0
onAppear level 1
onAppear level 2
onAppear level 3

(Нет триггера onDisappear)

но возвращаясь обратно Root ‹- NestedView1‹ - NestedView2 ‹- NestedView3

консоль отладки не покажет ... ничего

(Нет запуска onAppear или onDisappear)

struct NestedViewLevel3: View {

    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

    var body: some View {
        VStack {
            Spacer()
            Text("Level 3")
            Spacer()
            Button(action: {
                self.presentationMode.wrappedValue.dismiss()
            }) {
                Text("Back")
                    .padding(.horizontal, 15)
                    .padding(.vertical, 2)
                    .foregroundColor(Color.white)
                    .clipped(antialiased: true)
                    .background(
                        RoundedRectangle(cornerRadius: 20)
                            .foregroundColor(Color.blue)
                            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 40, alignment: .center)
                )
            }
            Spacer()
        }
        .navigationBarBackButtonHidden(false)
        .navigationBarTitle("Level 3", displayMode: .inline)
        .onAppear(perform: {print("onAppear level 3")})
        .onDisappear(perform: {print("onDisappear level 3")})

    }
}

struct NestedViewLevel2: View {

    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

    var body: some View {
        VStack {
            Spacer()
            NavigationLink(destination: NestedViewLevel3()) {
                Text("To level 3")
                    .padding(.horizontal, 15)
                    .padding(.vertical, 2)
                    .foregroundColor(Color.white)
                    .clipped(antialiased: true)
                    .background(
                        RoundedRectangle(cornerRadius: 20)
                            .foregroundColor(Color.gray)
                            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 40, alignment: .center)
                )
                    .shadow(radius: 10)
            }
            Spacer()
            Text("Level 2")
            Spacer()
            Button(action: {
                self.presentationMode.wrappedValue.dismiss()
            }) {
                Text("Back")
                    .padding(.horizontal, 15)
                    .padding(.vertical, 2)
                    .foregroundColor(Color.white)
                    .clipped(antialiased: true)
                    .background(
                        RoundedRectangle(cornerRadius: 20)
                            .foregroundColor(Color.blue)
                            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 40, alignment: .center)
                )
            }
            Spacer()
        }
        .navigationBarBackButtonHidden(false)
        .navigationBarTitle("Level 2", displayMode: .inline)
        .onAppear(perform: {print("onAppear level 2")})
        .onDisappear(perform: {print("onDisappear level 2")})
    }
}

struct NestedViewLevel1: View {

    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

    var body: some View {
        VStack {
            Spacer()
            NavigationLink(destination: NestedViewLevel2()) {
                Text("To level 2")
                    .padding(.horizontal, 15)
                    .padding(.vertical, 2)
                    .foregroundColor(Color.white)
                    .clipped(antialiased: true)
                    .background(
                        RoundedRectangle(cornerRadius: 20)
                            .foregroundColor(Color.gray)
                            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 40, alignment: .center)
                )
                    .shadow(radius: 10)
            }
            Spacer()
            Text("Level 1")
            Spacer()
            Button(action: {
                self.presentationMode.wrappedValue.dismiss()
            }) {
                Text("Back")
                    .padding(.horizontal, 15)
                    .padding(.vertical, 2)
                    .foregroundColor(Color.white)
                    .clipped(antialiased: true)
                    .background(
                        RoundedRectangle(cornerRadius: 20)
                            .foregroundColor(Color.blue)
                            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 40, alignment: .center)
                )
            }
            Spacer()
        }
        .navigationBarBackButtonHidden(false)
        .navigationBarTitle("Level 1", displayMode: .inline)
        .onAppear(perform: {print("onAppear level 1")})
        .onDisappear(perform: {print("onDisappear level 1")})
    }
}

struct RootViewLevel0: View {

    var body: some View {
        NavigationView {
        VStack {
            Spacer()
            NavigationLink(destination: NestedViewLevel1()) {
            Text("To level 1")
                .padding(.horizontal, 15)
                .padding(.vertical, 2)
                .foregroundColor(Color.white)
                .clipped(antialiased: true)
                .background(
                    RoundedRectangle(cornerRadius: 20)
                    .foregroundColor(Color.gray)
                    .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 40, alignment: .center)
                )
                .shadow(radius: 10)
        }

            Spacer()

            }
    }

        .navigationBarTitle("Root level 0", displayMode: .inline)
        .navigationBarBackButtonHidden(false)
        .navigationViewStyle(StackNavigationViewStyle())
        .onAppear(perform: {print("onAppear root level 0")})
        .onDisappear(perform: {print("onDisappear root level 0")})

    }

}


struct ContentView: View {
    var body: some View {
        RootViewLevel0()
    }
}


}

Теперь разработчик предпочел бы onAppear и onDisappear:

1) Запускается с целью запуска действий, которые необходимо выполнить только один раз и только тогда, когда пользователь движется вперед, как в текущем наблюдаемом поведении.

2) Срабатывает каждый раз, когда появляется представление, больше похоже на название действия, которое, по-видимому, означает, будь то назад, вперед и любое количество раз.

Я бы выбрал вариант 2, простой и жестокий (и то, что мне сейчас нужно), но я довольно наивный новичок в NavigationView, и вариант 2 может нарушить многие устоявшиеся парадигмы, которые я не принимаю во внимание.

Ваш отзыв поможет мне узнать, обоснован ли соответствующий случай использования Feedback Assistant для SwiftUI на законных основаниях.


person jpelayo    schedule 26.08.2019    source источник
comment
Я хотел бы иметь такое же функционирование, как у ViewWillAppear и ViewWillDisappear. Ваша консоль будет показывать, что каждый из них появляется один раз (при первой загрузке), а затем исчезает один раз.   -  person ShadowDES    schedule 26.08.2019
comment
Определенно, это похоже на ошибку, а не на функцию, поэтому я бы сказал, что onAppear / onDisappear не работает с Xcode11b6.   -  person jpelayo    schedule 27.08.2019
comment
Что касается тестирования XCODE11b7 с приложением watchOS, onAppear работает, а onDisappear - нет. Скорее всего, ошибка.   -  person Sid    schedule 30.08.2019


Ответы (3)


Это была ошибка Apple.

.onAppear() теперь работает так, как предполагается в iOS 13.1 и Xcode 11 Beta 7.

При переходе вперед и назад к NavigationView запускается .onAppear().

person txagPman    schedule 28.08.2019
comment
Работает так, как нам обоим нравится (режим 2) на XCode 11 (11A420a) и iOS 13.1 (17A844). Действительно, был баг. Спасибо! - person jpelayo; 25.09.2019
comment
onDisappear() не вызывается предсказуемо даже на iOS 13.5! - person Zorayr; 27.06.2020
comment
Еще даже не близко к работе, ищите onAppear на форумах разработчиков developer.apple.com/ форумы / поиск /? q = onappear - person malhal; 15.09.2020
comment
Как ни странно, кто-то упомянул, что .onAppear(perform: viewModel.onAppear), кажется, работает лучше, чем .onAppear { viewModel.onAppear() }, и вы знаете, что действительно работает. По крайней мере, для проблемы, с которой я столкнулся, переключение на .onAppear(perform:), похоже, работает. Источник: developer.apple.com/forums/thread/655338?answerId=650030022 < / а> - person Manav; 05.02.2021

Начиная с бета-версии Xcode 11.2 (выпущенной 10 февраля 2019 г.), оба метода (включая .onDisappear()) запускаются правильно.

Обратите внимание, что .onDisappear() не запускался до этой бета-версии.

person Arshia    schedule 03.10.2019

Обе команды: .onDisappear (выполнить:) и .onAppear (выполнить:)

ошибка функции NavigationView (.navigationBarTitle):

    .navigationBarTitle("Opciones", displayMode: .inline)

При использовании navigationBarTitle удаляется или не работает в представлении, в котором вы его используете. Это происходит с использованием ОБА: .onDisappear (выполнить:) .onAppear (выполнить:)

Использование Xcode версии 12.2 (12B45b)

person Ronald Steinvorth B.    schedule 27.11.2020