web-dev-qa-db-ja.com

SwiftUIアプリのNavigationViewのonAppearとonDisappearは期待どおりに動作していますか?

SwiftUI(私がこれを書いているときのXcode 11ベータ6)のonAppearとDisappearの動作が、開発者がより便利であるか、または機能よりも問題であるかどうかを知りたいです。

現在、添付したサンプルコード(Xcode 11b6でコンパイルして正常に実行される)にあるようにカスケードナビゲーションを使用すると、ユーザーが前後にナビゲートするコンソール出力がonAppearをトリガーするのは、順方向の新しいビューの読み込み(より深く行くことを意味します)。

ナビゲーション:ルート-> 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トリガーなし)

ルートに戻る<-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)現在実行されている動作のように、ユーザーが前進するときに1回だけ実行する必要があるアクションを起動する目的でトリガーされます。

2)ビューが表示されるたびにトリガーされます。アクション名が意味するように見えるのは、後方、前方、および任意の回数です。

私はオプション2、シンプルで残忍な(そして私が現在必要としているもの)を採用しますが、NavigationViewの初心者であり、オプション2は、考慮していない多くの確立されたパラダイムを破る可能性があります。

あなたのフィードバックは、SwiftUIの対応するフィードバックアシスタントのケースが正当な根拠にあるかどうかを確認するのに役立ちます。

9
jpelayo

これはApple側のバグでした。

.onAppear()がiOS 13.1およびXCode 11 Beta 7で想定されているとおりに機能するようになりました。

NavigationViewに順方向および逆方向にナビゲートすると、.onAppear()がトリガーされます。

2
txagPman

XCode 11.2ベータ(2019年2月10日リリース)以降、両方のメソッド(.onDisappear()を含む)が正しくトリガーされます。

.onDisappear()は、このベータ版がリリースされるまでトリガーされなかったことに注意してください。

5
Arshia