SwiftUI uygulamam, bir `NavigationView '' içindeki bir navigationBarItems öğesinin içine bir` NavigationLink` yerleştirdikten sonra geriye doğru giderken neden çöküyor?


47

En az tekrarlanabilir örnek (Xcode 11.2 beta, bu Xcode 11.1'de çalışır):

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()
    }
}

Sorun , kök görünümü bir SwiftUI görünümünün içine yerleştirilmiş NavigationLinkbir navigationBarItemsdeğiştiricinin içine yerleştirmekten kaynaklanıyor gibi görünüyor NavigationView. Çökme raporu, ileri Childve sonra geri gittiğimde var olmayan bir görünüm denetleyicisine geçmeye çalıştığımı gösteriyor Parent.

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

Bunun yerine NavigationLink, aşağıdaki gibi görünümün gövdesine yerleştirirsem, gayet iyi çalışıyor.

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

Bu bir SwiftUI hatası mı yoksa beklenen bir davranış mı?

EDIT: Apple ile FB7423964kimse tartmak umurunda olması durumunda kimliği ile geri bildirim asistanı Apple ile bir sorun açtım :).

EDIT: Geri bildirim asistanındaki açık biletim, bildirilen 10'dan fazla benzer sorun olduğunu gösteriyor. Çözünürlüğü ile güncellediler Resolution: Potential fix identified - For a future OS update. Parmaklar, düzeltmenin yakında ineceğini söyledi.

EDIT: Bu iOS 13.3'te düzeltildi!


Yukarıda verdiğiniz örnek Xcode 11.2 beta ile gayet iyi çalışıyor. Burada bir şey mi kaçırıyoruz?
Subramanian Mariappan

@SubramanianMariappan 11.2 beta sürümünde benim için iyi çalışıyor.
Farhan Amjad

1
İlginçtir, her seferinde benim için çöküyor. Hatta yeni bir proje oluşturmayı ve yerine tam kodu kopyalamayı denedim ContentView.swift. Gönderi üzerinde bir düzenleme yapacağım, ancak kilitlenme yalnızca ileri ve geri gittiğinizde gerçekleşir.
Robert

Harika bir soru! Buradaki örneğiniz benim için her zaman çöküyor. Benim için çok iyi olan yeni bir cevap gönderdim. Sizin için de işe yarayıp yaramadığını bana bildirin. Teşekkürler.
Chuck H

1
Elma biletleri ile ilgili güncellemeler için teşekkürler!
malte

Yanıtlar:


20

Bu benim için oldukça acı bir nokta oldu! Uygulamamın çoğu tamamlanana kadar bıraktım ve çökme ile başa çıkmak için zihin alanı vardı.

Sanırım hepimiz SwifUI ile harika şeyler olduğunu ancak hata ayıklamanın zor olabileceğini kabul edebiliriz.

Bence bunun bir HATA olduğunu söyleyebilirim. İşte benim mantığım:

  • PresentationMode aramasını yaklaşık yarım saniyelik eşzamansız bir gecikmeyle kapatırsanız, programın artık çökmeyeceğini görmelisiniz.

    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        self.presentationMode.wrappedValue.dismiss()
    } 
    
  • Bu bana böcek çeşitli görünümleri yönetmek için SwiftUI diğer tüm UIKit kodu ile nasıl arayüz derin bir şekilde beklenmedik bir davranış olduğunu göstermektedir. Gerçek kodunuza bağlı olarak, görünümde küçük bir karmaşıklık varsa, çökmenin gerçekten olmayacağını görebilirsiniz. Örneğin, bir görünümden listeye sahip bir görünümden çıkarıyorsanız ve bu liste boşsa, eşzamansız gecikme olmadan bir kilitlenme alırsınız. Öte yandan, bu liste görünümünde yalnızca bir girişiniz varsa, üst görünümü oluşturmak için döngü yinelemesini zorlarsanız, kilitlenmenin gerçekleşmeyeceğini görürsünüz.

Bir aramayı bırakma çağrısını tamamlama çözümümün ne kadar sağlam olduğundan emin değilim. Daha çok test etmeliyim. Bu konuda fikirleriniz varsa lütfen bize bildirin! Sizden öğrenmekten çok mutlu olurum!


1
Çok zeki! Bunu düşünmemiştim. Umarım yakında düzelir!
Robert

1
@Robert Sorununuzu çözdü mü? Bulduğum ilgisiz bir sorun, alt gezinme görünümlerinde bir Seçici kullanıyor olduğundan, bu zor bir iştir. Segmentli bir seçici stili çalışırken, varsayılan değer, geri düğmesini tıklattığınızda aynı noktada kilitlenmeye neden oluyor gibi görünür. Hala keder veriyorsa daha fazla tartışabiliriz. PS. Çözümümden nefret ediyorum. Bu bir hack ama Apple zamanlama sorununu çözerse kod güncellemesi gerektirmeyen bir hack.
Justin Ngan

2
Zamanlama yönünün, 11.1'de iyi çalıştığı ve .navigationBarItems()bunun bir hata olduğu noktalarının dışında çalıştığı gerçeğiyle birlikte katılıyorum .
John M.

3
Evet, bunun bir hata olduğuna inanıyorum ve bu ödül ödülünün şu anki lider adayı. Ben bu yazı zamanından beri lütuf 4 gün kaldı beri herkes yeni bilgi ile birlikte gelmesi durumunda ben tutuyorum :).
Robert

1
Bu çok ilginç bir ipucuydu, bunun için teşekkürler! Ne yazık ki hala simülatörde zamanın% 100'ünde uygulamayı kilitliyorum: / Cihazda daha iyi çalışıyor, ancak hiç çökmeden değil. Ama gecikme olmadan da durum böyleydi.
Kilian

15

Bu da beni bir süredir hayal kırıklığına uğrattı. Geçtiğimiz birkaç ay boyunca, Xcode sürümüne, simülatör sürümüne ve gerçek cihaz türüne ve / veya sürümüne bağlı olarak, görünüşte rastgele gibi görünmekten tekrar çalışmamaya geçti. Ancak, son zamanlarda benim için sürekli başarısız oldu, bu yüzden dün derin bir dalış yaptım. Şu anda Xcode Sürüm 11.2.1 (11B500) kullanıyorum.

Sorun Nav Bar etrafında dönüyor ve düğmelerin ona eklenme şekli gibi görünüyor. Düğmenin kendisi için bir NavigationLink () kullanmak yerine, gizli bir NavigationLink'i etkinleştiren @State var ayarlayan bir eylemle standart bir Button () kullanmayı denedim. İşte Robert'ın Ebeveyn Görünümü'nün yerine:

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") }
             )
        }
    }
}

Benim için bu, tüm simülatörlerde ve tüm gerçek cihazlarda çok tutarlı bir şekilde çalışıyor.

İşte benim yardımcı görüşlerim:

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 } )
    }
}

Kullanımın bir örneği:

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()
            } )
        }
    }
}

Bu işleri (gerçekten bir kesmek için ;-) iyi) teyit edebilir! Apple'ın bunu en kısa sürede düzeltmesi gerekiyor. Xcode 11.2.1, Catalina 10.15.2 (beta), iOS 13.2.2
P. Ent

1
% 100 katılıyorum. Genel olarak, SwiftUI'de navigasyon ile ilgili olarak, kırık ya da sadece eksik olan çok şey var. Elbette bizi asıl soruna götürüyor. Apple'ın "gerçeğin kaynağı" (yani belgeler ve örnekler) yoktur, sadece bizim gibi kesmek. BTW, yukarıdaki tekniği çok kullanıyorum, okunabilirliğe çok yardımcı olan iki yardımcı program görünümü oluşturdum. Herhangi birinin ilgilenmesi durumunda bunları yanıtıma ekleyeceğim.
Chuck H

Geçici çözüm için teşekkürler, sadece işe yarıyor!
Stanislav Poslavsky

1
Bu benim için birden fazla navigasyonda işe yaramıyor. Önceki ekrana geri döndüğünüzde, görünmez bağlantı artık çalışmaz.
Jon Shier

1
13.3'te birkaç gerçek cihazım var (yapı 17C54) ve hepsi istendiği gibi çalışıyor. Testlerimin neredeyse tamamını gerçek cihazlarda yaptığım için simülatörü çok sık kullanmıyorum. Ancak test durumumu 13.3 simülatöründe denedim ve test başarısız oluyor. Xcode simülatöründeki iOS 13.3'ün genel güncellemeden daha eski bir yapı (17C45) olduğunu fark ettim. Birinin gerçek bir cihazdaki başarısız davranışı gözlemleyip gözlemlemediğini bilmek isterim.
Chuck H

12

Bu büyük bir hatadır ve bunun üzerinde çalışmak için uygun bir yol göremiyorum. İOS 13 / 13.1'de ancak 13.2 çökmelerinde iyi çalıştı.

Aslında çok daha basit bir şekilde çoğaltabilirsiniz (bu kod tam anlamıyla ihtiyacınız olan her şeydir).

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

Umarım Apple, SwiftUI uygulamalarını (benimki dahil) bir sürü keseceği için sıralar.


Haha ... Bu oldukça harika. SwiftUI'de bir görünüm olan bir Metin görünümüne geçtiniz! Evet, ebeveynine geri dönmeli, değil mi? Yine de öyle değil. Örneğinizdeki davranışın kullanıcı arayüzünü bozması, ancak aslında ölümcül bir çökmeye neden olmaması ilginçtir.
Justin Ngan

Evet, SwiftUI'nin (ve React Native / Flutter vb.) Birleştirilebilirliği inanılmaz. (En azından işe yaradığında) size çok fazla kontrol / esneklik sağlar.
James

1
Catalina (10.15.1), Xcode (11.2.1), iOS (13.2.2)
P. Ent

Artık 13.3'te çökmüyor, ancak navigasyon yalnızca ilk tetiklediğinizde çalışıyor gibi görünüyor 🤦‍♂️
James

6

Geçici bir çözüm olarak, Chuck H'nin yukarıdaki cevabına dayanarak, NavigationLink'i gizli bir öğe olarak kapsülledim:

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()
}
}

Ardından bir NavigationView (çok önemli) içinde kullanabilir ve gezinme çubuğundaki bir Düğmeden tetikleyebilirsiniz:

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

Bunu "// HACK" yorumlarına sarın, böylece Apple bunu düzelttiğinde değiştirebilirsiniz.


Bu sadece iOS 13.3'te ilk kullanımda işe yarıyor gibi görünüyor.
James

3

Sağladığınız bilgilere ve özellikle @Robert'in NavigationView'un nereye yerleştirildiği hakkında yaptığı bir yoruma dayanarak, sorunu en azından benim özel senaryomda çözmenin bir yolunu buldum.

Benim durumumda böyle bir NavigationView içine alınmış bir TabView vardı:

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)
        }
    }
  }
}

Bu kod herkes iOS 13.2'de bildirirken çöküyor ve iOS 13.1'de çalışıyor. Biraz araştırma yaptıktan sonra bu duruma bir çözüm buldum.

Temel olarak, NavigationView gibi her bir ekrana ayrı ayrı her ekrana taşıyorum:

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)
    }
  }
}

Bir şekilde SwiftUI basitlik öncülüne karşı çıkıyor, ancak iOS 13.2'de çalışıyor.


Bu işe yarar, ancak sorun NewView üzerindeki tabViews kaldırma.
CUMA

1
@FRIDDAY bu örnek 13.1 sürümünde çalışıyor, ancak 13.2 sürümünde çöküyor. Bilinen bir hatadır ve amacım aynı senaryoda birine bir
çözümle

1

Xcode 11.2.1 Hızlı 5

ANLADIM! Bunu bulmam birkaç günümü aldı ...

Benim durumumda SwiftUI kullanırken sadece listemin alt ekranın ötesine genişledi ve sonra herhangi bir liste öğeleri "taşımak" için bir çökme alıyorum. Ne buldum sonuç olarak List () altında çok fazla "şeyler" varsa o zaman hareket halinde çöküyor olmasıdır. Örneğin, Listemin () altında bir Text (), Spacer (), Button (), Spacer () Button () vardı. Bu nesnelerden herhangi birini TEK yorumladıysanız, aniden kazayı yeniden oluşturamadım. Sınırlamaların ne olduğundan emin değilim, ancak bu çökmeyi alıyorsanız, yardımcı olup olmadığını görmek için listenizin altındaki nesneleri kaldırmayı deneyin.


0

Herhangi bir kilitlenme göremiyorum, ancak kodunuzda bazı sorunlar var:

önde gelen öğeyi ayarlayarak gezinme geçişlerinin varsayılan davranışını öldürürsünüz. (çalışıp çalışmadığını görmek için ön taraftan kaydırmayı deneyin).

Orada bir düğmeye gerek yok. Sadece olduğu gibi bırakın ve ücretsiz bir geri düğmesine sahipsiniz.

Ve HIG'ye göre unutmayın , geri düğmesi başlığı ne olduğunu değil , nereye gittiğini göstermelidir ! Bu yüzden, ilk sayfaya, kendisine açılan herhangi bir geri düğmesinden birini göstermek için bir başlık ayarlamaya çalışın.

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
Hey, cevap için teşekkürler. Varsayılan geri düğmesi davranışını bırakmanın arzu edilir olduğunu kabul etsem de, yine de bir kilitlenme üretiyor.
Robert

Hangi sürümü kullanıyorsunuz? Göndermeden önce test ettim. Belki başka bir sorunun var. Örnek bir proje sağlayabilir lütfen?
Mojtaba Hosseini

1
Xcode 11.2 beta gibi soru söylüyor. Soruda verdiğim örnek, çökmeyi yeniden oluşturmak için ihtiyacınız olan tek şey.
Robert

Aynı sürümü ve aynı kodu kullanıyorum ama çökme yok Mo
Mojtaba Hosseini

1
Catalina (10.15.1), Xcode (11.2.1), iOS (13.2.2)
P. Ent

0

FWIW - Gizli bir NavigationLink Hack'i öneren yukarıdaki çözümler hala iOS 13.3b3'teki en iyi çözümdür. Ayrıca, gelecek nesiller için bir FB7386339 dosyaladım ve yukarıda bahsedilen diğer FB'lere benzer şekilde kapatıldım: "Potansiyel düzeltme belirlendi - Gelecekteki bir işletim sistemi güncellemesi için".

Parmak çarpı işareti.


Lütfen cevap olarak yorum eklemekten kaçının.
Karthick Ramesh

0

İOS 13.3'te çözüldü. İşletim sisteminizi ve xCode'unuzu güncelleyin.


1
10.15.2'deki Xcode 11.3 (11C29) benim için farklı bir davranışla sonuçlanıyor: Geriye doğru gezinme çalışıyor, ancak daha sonra NavigationLink'in artık bir işlevi yok. Tıklamak hiçbir şey yapmaz.
malte

@malte Bunun için yeni bir soru açmak daha iyidir. Kodunuzu kontrol etmeden önce NavigationLink .buttonStyle(PlainButtonStyle())değiştiricinizi verin ve tekrar deneyin. bir soru sorduysan bana haber ver.
CUMA

1
Haklısın. Zaten yeni bir soru olduğu ortaya çıktı: stackoverflow.com/questions/59279176/…
malte
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.