`NavigationView`の` navigationBarItems`内に `NavigationLink`を配置した後に後方に移動すると、SwiftUIアプリがクラッシュするのはなぜですか?


47

最小限の再現可能な例(Xcode 11.2ベータ、これはXcode 11.1で動作します):

struct Parent: View {
    var body: some View {
        NavigationView {
            Text("Hello World")
                .navigationBarItems(
                    trailing: NavigationLink(destination: Child(), label: { Text("Next") })
                )
        }
    }
}

struct Child: View {
    @Environment(\.presentationMode) var presentation
    var body: some View {
        Text("Hello, World!")
            .navigationBarItems(
                leading: Button(
                    action: {
                        self.presentation.wrappedValue.dismiss()
                    },
                    label: { Text("Back") }
                )
            )
    }
}

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

問題は、ルートビューがであるSwiftUIビュー内にネストされてNavigationLinkいるnavigationBarItemsモディファイアの内側に配置することにあるようNavigationViewです。クラッシュレポートは、に移動してChildからに戻ったときに、存在しないView Controllerにポップしようとしていることを示していParentます。

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Tried to pop to a view controller that doesn't exist.'
*** First throw call stack:

代わりにNavigationLink、以下のようにビューの本文に配置すると、問題なく動作します。

struct Parent: View {
    var body: some View {
        NavigationView {
            NavigationLink(destination: Child(), label: { Text("Next") })
        }
    }
}

これはSwiftUIのバグですか、それとも予想される動作ですか?

編集:私はフィードバックアシスタントでAppleの問題をIDで公開しました FB7423964をからの誰かがそこに量り込みたいている場合に備えて:)。

編集:フィードバックアシスタントの未解決のチケットは、10件以上の同様の報告された問題があることを示しています。彼らは解像度を更新しましたResolution: Potential fix identified - For a future OS update。修正はすぐに着陸することを指が交差しました。

編集:これはiOS 13.3で修正されています!


上記で提供した例は、Xcode 11.2ベータで問題なく動作します。ここで何か不足していますか?
Subramanian Mariappan

@SubramanianMariappan 11.2ベータ版でも問題なく動作します。
Farhan Amjad

1
興味深いことに、毎回クラッシュします。新しいプロジェクトを作成し、その正確なコードをの代わりにコピーしてみましたContentView.swift。投稿を編集しますが、クラッシュが発生するのは、前に移動してから戻った場合のみです。
ロバート

すばらしい質問です。ここでの例は、毎回私にもクラッシュします。私は私にとって非常にうまくいく新しい答えを投稿しました。うまくいくかどうか教えてください。ありがとう。
チャックH

1
アップルチケットに関する更新をありがとう!
モルト

回答:


20

これは私にとってかなり苦痛でした!ほとんどのアプリが完成し、クラッシュに対処するためのマインドスペースができるまで、私はそれを残しました。

SwifUIにはかなり素晴らしいものがあるが、デバッグが難しい場合があることは皆同意できると思います。

私の意見では、これはバグだと思います。ここに私の理論的根拠があります:

  • 約0.5秒の非同期遅延でpresentationMode dismiss呼び出しをラップすると、プログラムがクラッシュしないことがわかります。

    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        self.presentationMode.wrappedValue.dismiss()
    } 
  • これは、このバグがSwiftUIが他のすべてのUIKitコードとインターフェイスしてさまざまなビューを管理する方法のかなり下にある予期しない動作であることを私に示唆しています。実際のコードによっては、ビューに多少の複雑さがある場合、実際にはクラッシュが発生しない場合があります。たとえば、ビューからリストのあるビューを却下し、そのリストが空の場合、非同期遅延なしでクラッシュします。一方、リストビューにエントリが1つしかない場合は、ループを繰り返し実行して親ビューを生成すると、クラッシュが発生しないことがわかります。

dismiss呼び出しを遅延でラップする私のソリューションがどれほど堅牢であるかはよくわかりません。もっとテストしなければならない。これについてアイデアがあれば教えてください!私はあなたから学ぶことをとても嬉しく思います!


1
非常に賢い!私はそのことを考えていませんでした。それがすぐに修正されることを願っています!
ロバート

1
@ロバートそれはあなたの問題を解決しましたか?私が見つけた無関係な問題が子ナビゲーションビュー内でピッカーを使用しているため、これは難しい問題です。セグメント化されたピッカースタイルは機能しますが、デフォルトでは、[戻る]ボタンをクリックすると、同じポイントでクラッシュが発生するようです。それでもあなたに悲しみを与えるなら、私たちはさらに議論することができます。PS。私の解決策は嫌いです。これはハックですが、Appleがタイミングの問題を修正した場合、コードを更新する必要はありません。
Justin Ngan

2
11.1では問題なく動作.navigationBarItems()し、これがバグであるという点以外でも機能するという事実とともに、タイミングの側面にも同意します。
John M.

3
はい、私はそれがバグであると信じています、そしてこれは私の賞金賞の現在の主要な候補者です。この記事の執筆時点で賞金が残っているのは4日間なので、誰かが新しい情報を持ってきた場合に備えて、私は保留します:)。
ロバート

1
これは非常に興味深いヒントでした。ありがとうございます。残念ながら、私は依然として100%の確率でシミュレーターでアプリを確実にクラッシュしています:/デバイス上でよりよく機能しますが、まったくクラッシュしないわけではありません。しかし、それは遅れのないケースでもありました。
キリアン

15

これもかなり長い間私を苛立たせてきました。過去数か月間、Xcodeのバージョン、シミュレーターのバージョン、実際のデバイスのタイプやバージョンに応じて、ランダムに動作するようになり、正常に機能しなくなり、再び機能するようになりました。しかし、最近は一貫して失敗しているので、昨日は深く掘り下げました。現在Xcodeバージョン11.2.1(11B500)を使用しています。

問題はナビゲーションバーと、ボタンが追加された方法に関係しているようです。したがって、ボタン自体にNavigationLink()を使用する代わりに、非表示のNavigationLinkをアクティブにする@State変数を設定するアクションで標準のButton()を使用してみました。これはロバートの親ビューの代わりです:

struct Parent: View {
    @State private var showingChildView = false
    var body: some View {
        NavigationView {
            VStack {
                Text("Hello World")
                NavigationLink(destination: Child(),
                               isActive: self.$showingChildView)
                { EmptyView() }
                    .frame(width: 0, height: 0)
                    .disabled(true)
                    .hidden()            
             }
             .navigationBarItems(
                 trailing: Button(action:{ self.showingChildView = true }) { Text("Next") }
             )
        }
    }
}

私にとって、これはすべてのシミュレータとすべての実際のデバイスで非常に一貫して機能します。

ここに私のヘルパービューがあります:

struct HiddenNavigationLink<Destination : View>: View {

    public var destination:  Destination
    public var isActive: Binding<Bool>

    var body: some View {

        NavigationLink(destination: self.destination, isActive: self.isActive)
        { EmptyView() }
            .frame(width: 0, height: 0)
            .disabled(true)
            .hidden()
    }
}

struct ActivateButton<Label> : View where Label : View {

    public var activates: Binding<Bool>
    public var label: Label

    public init(activates: Binding<Bool>, @ViewBuilder label: () -> Label) {
        self.activates = activates
        self.label = label()
    }

    var body: some View {
        Button(action: { self.activates.wrappedValue = true }, label: { self.label } )
    }
}

次に使用例を示します。

struct ContentView: View {
    @State private var showingAddView: Bool = false
    var body: some View {
        NavigationView {
            VStack {
                Text("Hello, World!")
                HiddenNavigationLink(destination: AddView(), isActive: self.$showingAddView)
            }
            .navigationBarItems(trailing:
                HStack {
                    ActivateButton(activates: self.$showingAddView) { Image(uiImage: UIImage(systemName: "plus")!) }
                    EditButton()
            } )
        }
    }
}

私はこれが動作することを確認できます(ハッキングには本当にうまくいきます;-)!Appleはこれをできるだけ早く修正する必要があります。Xcode 11.2.1、Catalina 10.15.2(ベータ版)、iOS 13.2.2
P. Ent

1
100%同意します。一般的に、SwiftUIでのナビゲーションに関しては、壊れているか、単純に欠けているものがたくさんあります。もちろん、どちらが私たちを本当の問題に導きます。Appleからの「真実の源」(つまり、ドキュメントと例)はなく、私たちのようなハッキングだけです。ところで、私は上記のテクニックを非常に使用しています。読みやすくするために役立つ2つのユーティリティビューを作成しました。誰かが興味を持っている場合に備えて、それらを私の回答に追加します。
チャックH

回避策をありがとう、それはうまくいきます!
Stanislav Poslavsky

1
これは、複数のナビゲーションでは機能しません。前の画面に戻ると、非表示のリンクは機能しなくなります。
Jon Shier

1
13.3(ビルド17C54)に実際のデバイスがいくつかあり、それらはすべて希望どおりに機能します。私は実際のデバイスでほとんどすべてのテストを行うので、シミュレータをあまり使用しません。しかし、13.3シミュレータでテストケースを試したところ、テストは失敗しました。XcodeシミュレーターのiOS 13.3は、パブリックアップデートよりも古いビルド(17C45)であることに気付きました。実際のデバイスで失敗した動作を誰かが観察したかどうかを知りたいです。
チャックH

12

これは大きなバグであり、適切に回避する方法がわかりません。iOS 13 / 13.1では問題なく動作しましたが、13.2はクラッシュします。

実際にそれをはるかに簡単な方法で複製できます(このコードは文字通り必要なすべてです)。

struct ContentView: View {
    var body: some View {
        NavigationView {
            Text("Hello, World!").navigationBarTitle("To Do App")
                .navigationBarItems(leading: NavigationLink(destination: Text("Hi")) {
                    Text("Nav")
                    }
            )
        }
    }
}

AppleがSwiftUIアプリ(私のものを含む)のロードを確実に破壊するので、それを整理してほしい。


はは…それはすごい SwiftUIではビューであるテキストビューに移動しました。ええ、それはその親に戻るはずですよね?しかし、そうではありません。興味深いのは、例の動作がUIを壊しますが、実際には致命的なクラッシュを引き起こさないということです。
Justin Ngan

ええ、SwiftUI(およびReact Native / Flutterなど)の構成可能性は信じられないほどです。非常に多くの制御/柔軟性を提供します(少なくとも機能する場合)。
ジェームズ

1
Catalina(10.15.1)、Xcode(11.2.1)、iOS(13.2.2)でこのクラッシュを確認
P. Ent

13.3ではクラッシュしなくなりましたが、ナビゲーションは初めてトリガーしたときにのみ機能するようです🤦‍♂️
James

6

回避策として、上記のチャックHの回答に基づいて、NavigationLinkを非表示要素としてカプセル化しました。

struct HiddenNavigationLink<Content: View>: View {
var destination: Content
@Binding var activateLink: Bool

var body: some View {
    NavigationLink(destination: destination, isActive: self.$activateLink) {
        EmptyView()
    }
    .frame(width: 0, height: 0)
    .disabled(true)
    .hidden()
}
}

次に、それをNavigationView(これは重要です)内で使用し、ナビゲーションバーのボタンからトリガーできます。

VStack {
    HiddenNavigationList(destination: SearchView(), activateLink: self.$searchActivated)
    ...
}
.navigationBarItems(trailing: 
    Button("Search") { self.searchActivated = true }
)

これを「// HACK」コメントで囲み、Appleがこれを修正したときに置き換えられるようにします。


これは、iOS 13.3の最初の使用でのみ機能するようです。
James

3

皆さんから提供された情報と、特にNavigationViewが配置されている場所について@Robertが作成したコメントに基づいて、少なくとも私の特定のシナリオで問題を回避する方法を見つけました。

私の場合、次のようにNavigationViewで囲まれたTabViewがありました。

struct ContentViewThatCrashes: View {
@State private var selection = 0

var body: some View {
    NavigationView{
        TabView(selection: $selection){
            NavigationLink(destination: NewView()){
                Text("First View")
                    .font(.title)
            }
            .tabItem {
                VStack {
                    Image("first")
                    Text("First")
                }
            }
            .tag(0)
            NavigationLink(destination: NewView()){
                Text("Second View")
                    .font(.title)
            }
            .tabItem {
                VStack {
                    Image("second")
                    Text("Second")
                }
            }
            .tag(1)
        }
    }
  }
}

誰もがiOS 13.2でレポートし、iOS 13.1で動作するため、このコードはクラッシュします。いくつかの調査の後、私はこの状況の回避策を見つけました。

基本的に、私は次のように、NavigationViewを各タブの各画面に個別に移動しています。

struct ContentViewThatWorks: View {
@State private var selection = 0

var body: some View {
    TabView(selection: $selection){
        NavigationView{
            NavigationLink(destination: NewView()){
                Text("First View")
                    .font(.title)
            }
        }
        .tabItem {
            VStack {
                Image("first")
                Text("First")
            }
        }
        .tag(0)
        NavigationView{
            NavigationLink(destination: NewView()){
                Text("Second View")
                    .font(.title)
            }
        }
        .tabItem {
            VStack {
                Image("second")
                Text("Second")
            }
        }
        .tag(1)
    }
  }
}

どういうわけか、SwiftUIのシンプルさの前提に反していますが、iOS 13.2で動作します。


これは機能しますが、問題はNewViewのtabViewsを削除することです。
金曜日

1
@FRIDDAYこの例は13.1で機能しますが、13.2でクラッシュします。これは既知のバグであり、私の意図は、回避策を使用して同じシナリオで誰かを助けようとすることでした
Julio Bailon

1

Xcode 11.2.1 Swift 5

とった!これを理解するのに数日かかりました...

私の場合、SwiftUIを使用しているときに、リストの下部が画面からはみ出し、リストアイテムを「移動」しようとした場合にのみ、クラッシュが発生します。最終的に見つけたのは、List()の下に "もの"が多すぎると、移動中にクラッシュするということです。たとえば、私のList()の下には、Text()、Spacer()、Button()、Spacer()Button()がありました。これらのオブジェクトのいずれか1つをコメントアウトした場合、突然クラッシュを再現できなくなりました。どのような制限があるのか​​はわかりませんが、このクラッシュが発生する場合は、リストの下にあるオブジェクトを削除して、効果があるかどうかを確認してください。


0

クラッシュは確認できませんが、コードに問題があります:

先頭のアイテムを設定することで、ナビゲーション遷移のデフォルトの動作を強制終了します。(それが機能するかどうかを確認するために先頭側からスワイプしてみてください)。

したがって、そこにボタンを配置する必要はありません。そのままにしておくと、無料の戻るボタンがあります。

そしてHIGに従って忘れないでください。戻るボタンのタイトルは、それがどこにあるかではなく、どこにあるかを示すべきです!したがって、最初のページのタイトルを設定して、ポップアップする戻るボタンを表示してみてください。

struct Parent: View {
    var body: some View {
        NavigationView {
            Text("Hello World")
                .navigationBarItems(
                    trailing: NavigationLink(destination: Child(), label: { Text("Next") })
                )
                .navigationBarTitle("First Page",displayMode: .inline)
        }
    }
}

struct Child: View {
    @Environment(\.presentationMode) var presentation
    var body: some View {
        Text("Hello, World!")
    }
}

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

1
ねえ、答えてくれてありがとう。デフォルトの[戻る]ボタンの動作をそのままにしておくことが望ましいと私は同意しますが、それでもクラッシュします。
ロバート

どのバージョンを使用していますか?送信前にテストしました。多分あなたは別の問題を抱えています。サンプルプロジェクトを提供できますか?
Mojtaba Hosseini、

1
質問のようにXcode 11.2ベータは言っています。質問で提供した例は、クラッシュを再現するために必要なすべてです。
ロバート

私は🤔同じバージョンと同じコードが、ノークラッシュを使用しています
モタバ・ホセイーニ

1
Catalina(10.15.1)、Xcode(11.2.1)、iOS(13.2.2)でこのクラッシュを確認
P. Ent

0

FWIW-非表示のNavigationLinkハックを示唆する上記のソリューションは、iOS 13.3b3での最善の回避策です。私も後世のためにFB7386339を提出し、前述の他のFBと同様に「潜在的な修正が特定されました-将来のOSアップデートのために」閉鎖されました。

成功を祈っている。


回答としてのコメントの追加は避けてください。
Karthick Ramesh

0

iOS 13.3で修正されています。OSとxCodeを更新するだけです。


1
Xcode 11.3(11C29)を10.15.2で実行すると、動作が異なります。後方ナビゲーションは機能していますが、その後はNavigationLinkが機能しなくなります。それをクリックしても何も起こりません。
モルト

@malteそのための新しい質問を開くことをお勧めします。コードを確認する前に、NavigationLink .buttonStyle(PlainButtonStyle())修飾子を指定して、もう一度試してください。質問があったら教えてください。
FRIDDAY、

1
あなたが正しい。:新しい質問すでにある判明stackoverflow.com/questions/59279176/...
マルタ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.