`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내부 에 배치하는 것으로 보입니다 . 충돌 보고서는 앞으로 탐색 한 다음 다시 탐색 할 때 존재하지 않는 뷰 컨트롤러에 팝업하려고한다는 것을 나타냅니다 .navigationBarItemsNavigationViewChildParent

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 버그입니까 아니면 예상되는 동작입니까?

편집 : FB7423964Apple의 외부 담당자가 무게를 측정 해야하는 경우를 대비 하여 ID와 함께 피드백 지원에서 Apple과 관련된 문제를 열었습니다 . :)

편집 : 피드백 도우미의 열린 티켓은 10 가지 이상의 유사한보고 된 문제가 있음을 나타냅니다. 그들은로 해상도를 업데이트했습니다 Resolution: Potential fix identified - For a future OS update. 손가락이 교차하여 수정이 곧 착륙했습니다.

편집 : 이것은 iOS 13.3에서 수정되었습니다!


위에서 제공 한 예제는 Xcode 11.2 베타에서 잘 작동합니다. 여기에 뭔가 빠졌습니까?
Subramanian Mariappan

@SubramanianMariappan 11.2 베타에서도 잘 작동합니다.
Farhan Amjad '8

1
흥미 롭습니다. 매번 저에게 충돌합니다. 심지어 새로운 프로젝트를 만들고 대신 정확한 코드를 복사하려고했습니다 ContentView.swift. 게시물을 수정하겠습니다. 크래시는 앞뒤로 탐색 할 때만 발생합니다.
Robert

좋은 질문입니다! 당신의 예제는 매번 나에게 충돌합니다. 방금 나에게 잘 맞는 새로운 답변을 게시했습니다. 그것이 당신에게도 효과가 있는지 알려주세요. 감사.
척 H

1
사과 표에 관한 업데이트에 감사드립니다!
malte

답변:


20

이것은 나에게 큰 고통이었다! 대부분의 앱이 완성 될 때까지 그대로두고 충돌을 처리 할 수있는 공간이있었습니다.

SwifUI에는 멋진 기능이 있지만 디버깅이 어려울 수 있다는 데 모두 동의 할 수 있습니다.

제 생각에는 이것이 버그입니다. 여기 내 근거가 있습니다 :

  • presentationMode dismiss 호출을 약 0.5 초의 비동기 지연으로 랩핑하면 프로그램이 더 이상 충돌하지 않습니다.

    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        self.presentationMode.wrappedValue.dismiss()
    } 
  • 이것은 버그가 SwiftUI가 다른 모든 UIKit 코드와 인터페이스하여 다양한 뷰를 관리하는 방식에 깊은 예상치 못한 동작이라는 것을 나타냅니다. 실제 코드에 따라보기에 약간의 복잡성이 있으면 충돌이 실제로 발생하지 않을 수 있습니다. 예를 들어,보기에서 목록이있는보기로 닫고 해당 목록이 비어 있으면 비동기 지연없이 충돌이 발생합니다. 반면, 해당 목록보기에 하나의 항목 만 있으면 루프 반복이 상위보기를 생성하도록 강제 실행하면 충돌이 발생하지 않습니다.

지연 호출을 래핑하는 솔루션이 얼마나 강력한 지 잘 모르겠습니다. 나는 그것을 훨씬 더 테스트해야합니다. 이것에 대한 아이디어가 있으면 알려주십시오! 당신에게서 배우게되어 매우 기쁩니다!


1
매우 영리한! 나는 그것을 생각하지 않았다. 곧 해결되기를 바라고 있습니다!
Robert

1
@Robert 문제가 해결 되었습니까? 내가 찾은 관련이없는 문제가 자식 탐색보기에서 Picker를 사용하고 있기 때문에 이것은 힘든 일입니다. 세그먼트 피커 스타일이 작동하는 동안 뒤로 버튼을 클릭하면 기본적으로 같은 지점에서 충돌이 발생합니다. 여전히 슬픔을 겪고 있다면 더 논의 할 수 있습니다. 추신. 내 솔루션이 싫어. 그것은 해킹이지만 Apple이 타이밍 문제를 해결하면 코드 업데이트가 필요하지 않은 것입니다.
저스틴 Ngan

2
나는 타이밍 측면이 11.1에서 잘 작동 .navigationBarItems()했으며 버그가 아니라는 점에서 작동한다는 사실과 동의합니다 .
John M.

3
그렇습니다, 나는 이것이 버그라고 생각하며 이것은 현상금 상을 수상한 현재의 주요 후보입니다. 이 글을 쓰는 시점에서 현상금이 4 일 남았으므로 누군가가 새로운 정보와 함께 오는 경우를 대비하여 개최됩니다. :).
Robert

1
이것은 매우 흥미로운 팁이었습니다. 감사합니다! 불행히도 여전히 시뮬레이터에서 앱을 100 % 확실하게 충돌시키고 있습니다 : / 장치에서 더 잘 작동하지만 전혀 충돌하지는 않습니다. 그러나 지연이없는 경우도 마찬가지였습니다.
Kilian

15

이것은 또한 꽤 오랫동안 나를 좌절시켰다. 지난 몇 개월 동안 Xcode 버전, 시뮬레이터 버전 및 실제 장치 유형 및 / 또는 버전에 따라 무작위로 작동하지 않고 다시 작동하지 않는 것으로 바뀌 었습니다. 그러나 최근에 그것은 나에게 지속적으로 실패하고 있었으므로 어제 나는 그것에 깊이 빠져 들었습니다. 현재 Xcode 버전 11.2.1 (11B500)을 사용하고 있습니다.

Nav Bar와 버튼이 추가 된 방식을 중심으로 문제가 발생하는 것처럼 보입니다. 따라서 버튼 자체에 NavigationLink ()를 사용하는 대신 숨겨진 NavigationLink를 활성화하는 @State var를 설정하는 작업과 함께 표준 Button ()을 사용해 보았습니다. 다음은 Robert의 부모보기를 대체합니다.

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

나는 이것이 작동하는지 확인할 수있다 (정말 해킹 ;-))! 애플은이 문제를 최대한 빨리 해결해야한다. Xcode 11.2.1, Catalina 10.15.2 (베타), iOS 13.2.2
P. Ent

1
100 % 동의합니다. 일반적으로 SwiftUI의 탐색과 관련하여 손상되었거나 평범하지 않은 부분이 많이 있습니다. 물론 우리를 진짜 문제로 이끄는 것은 무엇입니까? Apple의 "진실의 출처"(즉, 문서 및 예제)는 없으며 우리와 같은 해킹 만 있습니다. BTW, 위의 기술을 너무 많이 사용하여 가독성을 높이는 데 도움이되는 두 가지 유틸리티보기를 만들었습니다. 관심이 있으신 분을 위해 답변에 추가하겠습니다.
척 H

해결 방법에 감사드립니다.
Stanislav Poslavsky 1

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 등)의 구성 가능성은 놀랍습니다. (최소한 작동 할 때) 제어 / 유연성이 뛰어납니다.
James

1
Catalina (10.15.1), Xcode (11.2.1), iOS (13.2.2)에서 충돌이 발생했는지 확인하십시오.
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에서 처음 사용할 때만 작동하는 것 같습니다.
제임스

3

귀하가 제공 한 정보와 특히 @Robert가 NavigationView가 배치 된 위치에 대해 작성한 의견을 바탕으로 적어도 특정 시나리오에서 문제를 해결할 수있는 방법을 찾았습니다.

내 경우에는 다음과 같이 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에서 tabView를 제거하는 것입니다.
FRIDDAY

1
@FRIDDAY이 예제는 13.1에서 작동하지만 13.2에서 충돌합니다. 그것은 알려진 버그이며 내 의도는 해결 방법으로 같은 시나리오에서 누군가를 도우려고 시도한 것입니다.
Julio Bailon

1

Xcode 11.2.1 스위프트 5

알았다! 이것을 알아내는 데 며칠이 걸렸습니다 ...

SwiftUI를 사용할 때 내 목록의 맨 아래가 화면을 넘어 확장 된 경우에만 목록 항목을 "이동"하려고하면 충돌이 발생합니다. 내가 찾은 것은 List () 아래에 너무 많은 "stuff"가 있으면 이동 중에 충돌한다는 것입니다. 예를 들어 내 List () 아래에 Text (), Spacer (), Button (), Spacer () Button ()이 있습니다. 그 객체 중 하나를 주석 처리하면 갑자기 충돌을 다시 만들 수 없었습니다. 제한 사항이 무엇인지 확실하지 않지만이 충돌이 발생하면 목록 아래의 객체를 제거하여 도움이되는지 확인하십시오.


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
대답 해줘서 고마워 기본 뒤로 버튼 동작을 유지하는 것이 바람직하지만 여전히 충돌을 일으킨다는 데 동의합니다.
Robert

어떤 버전을 사용하고 있습니까? 보내기 전에 테스트했습니다. 다른 문제가있을 수 있습니다. 샘플 프로젝트를 제공해 주시겠습니까?
Mojtaba Hosseini

1
질문과 같은 Xcode 11.2 베타는 말합니다. 질문에 제공된 예제는 충돌을 재현하는 데 필요한 전부입니다.
Robert

동일한 버전과 동일한 코드를 사용하고 있지만 충돌이 없습니다. 🤔
Hosseini

1
Catalina (10.15.1), Xcode (11.2.1), iOS (13.2.2)에서 충돌이 발생했는지 확인하십시오.
Ent

0

FWIW-숨겨진 NavigationLink 핵을 제안하는 위의 솔루션은 여전히 ​​iOS 13.3b3에서 가장 좋은 해결 방법입니다. 또한 후손을 위해 FB7386339를 제출했으며 다른 언급 된 FB와 유사하게 폐쇄되었습니다. "잠재적 수정 사항 확인-향후 OS 업데이트 용".

손가락이 건. 다.


답변으로 의견을 추가하지 마십시오.
Karthick Ramesh

0

iOS 13.3에서 해결되었습니다. OS와 xCode 만 업데이트하십시오.


1
10.15.2의 Xcode 11.3 (11C29)은 다른 동작을합니다. 뒤로 탐색이 작동하지만 나중에 NavigationLink가 더 이상 작동하지 않습니다. 그것을 클릭하면 아무것도하지 않습니다.
malte

@malte 새로운 질문을 여는 것이 좋습니다. 코드를 확인하기 전에 NavigationLink .buttonStyle(PlainButtonStyle())수정자를 지정하고 다시 시도하십시오. 질문이 있으면 알려주십시오.
FRIDDAY

1
네가 옳아. 이미 새로운 질문이 있음이 밝혀졌습니다 : stackoverflow.com/questions/59279176/…
malte
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.