@Environment(\.dismiss) 错误导致弹出视图在 iOS 15 中加载自身的新版本

@Environment(\.dismiss) bug causes popped view to load a new version of itself in iOS 15

导航到 INCR: 3 并点击导航栏后退按钮或关闭按钮,您会注意到再次调用相同的视图,但这次是新版本,因为 onAppear firstLoad = truerand 是不同的值。

如果您注释掉 @Environment(\.dismiss) var dismissdismiss(),一切都会像在 iOS 14 中那样正常工作。@Environment(\.presentationMode) var presentationMode

也会出现此问题

不确定这是错误还是我犯了一个愚蠢的错误,但这个问题给我的应用程序带来了很多问题,因为我必须能够以编程方式关闭视图,所以任何输入都会不胜感激。

struct DetailView: View {
    
    @Environment(\.dismiss) var dismiss
    
    @State var isPresenting = false
    
    @State var incrInt = 0
    
    @State var firstLoad = true
    
    @State var rand = Int.random(in: 1..<500)
    
    var body: some View {
        
        Text("INCR: \(incrInt) RAND: \(rand)")
        
        Button("NAVIGATE"){
            isPresenting = true
        }
        Button("DISMISS"){
           dismiss()
        }
        
        .onAppear(perform: {
            
            if firstLoad{
                print("ON APPEAR FIRST LOAD")
                print(incrInt)
                print(rand)
                print("\n")
                firstLoad = false
            }
        })
        
        NavigationLink(destination: DetailView(incrInt: (incrInt + 1)), isActive: $isPresenting){}
        
    }
}

struct ContentView: View {
    var body: some View {
        NavigationView{
            VStack{
                DetailView()
            }
        }
    }
}

视频Link

https://i.imgur.com/qpu7NT7.mp4

更新 1:ViewModel 的真实来源

class DetailViewModel: ObservableObject {
    
    @Published var isPresenting = false
    
    var incr: Int
    
    var rand = Int.random(in: 1..<500)
    
    init(incr: Int){
        
        self.incr = incr
        
        print("INIT FIRST LOAD")
        print(incr)
        print(rand)
        print("\n")
    }
    
}

struct DetailView: View {
    
    @Environment(\.dismiss) var dismiss
    
    @StateObject var detailViewModel: DetailViewModel
    
    var body: some View {
        
        Text("INCR: \(detailViewModel.incr) RAND: \(detailViewModel.rand)")
        
        
        Button("NAVIGATE"){
            detailViewModel.isPresenting = true
        }
        Button("DISMISS"){
            dismiss()
        }
        
        
        NavigationLink(destination: DetailView(detailViewModel: DetailViewModel(incr: (detailViewModel.incr + 1))), isActive: $detailViewModel.isPresenting){}
         
        
    }
}

struct ContentView: View {
    var body: some View {
        NavigationView{
            VStack{
                DetailView(detailViewModel: DetailViewModel(incr: 0))
            }
        }
    }
}

在您的“更新”代码中,您没有使用单一的真实来源。 您正在创建一个新的 DetailViewModel 并将其传递给 DetailView 每次点击 NavigationLink。 仅使用 1 个 DetailViewModel,并传递它。另外,你正在改变 isPresenting,因此您所有依赖此的视图都将更新为“新”值。这个 级联不是你想要的。修改你的逻辑。使用 DetailViewModel 跨视图保持模型状态是个好主意。尝试这样的事情:

class DetailViewModel: ObservableObject {
   // @Published var isPresenting = false  // <-- not relevant
    
    var incr: Int
    var rand = Int.random(in: 1..<500)
    
    init(incr: Int) {
        self.incr = incr
        print("----> DetailViewModel init --> inc: \(incr) --> rand: \(rand) \n")
    }
    
    func doIncr(_ incr: Int) {
        self.incr = incr
        print("----> DetailViewModel doIncr --> inc: \(incr) --> rand: \(rand) \n")
    }
}

struct DetailView: View {
    @Environment(\.dismiss) var dismiss
    @ObservedObject var detailViewModel: DetailViewModel
    @State var showThyself = false  // <--- here
    
    var body: some View {
        Text("DetailView  INCR: \(detailViewModel.incr) RAND: \(detailViewModel.rand)")
        Button("NAVIGATE"){
            detailViewModel.doIncr(detailViewModel.incr + 1)
            showThyself = true
        }
        Button("DISMISS"){
            dismiss()
        }
        NavigationLink(destination: DetailView(detailViewModel: detailViewModel), isActive: $showThyself){}
        .onAppear {
            // do something with the current state of your DetailViewModel
            print("----> DetailView onAppear \n")
        }
    }
}

struct ContentView: View {
    var detailViewModel = DetailViewModel(incr: 0)  // <--- here
    var body: some View {
        NavigationView{
            VStack{
                DetailView(detailViewModel: detailViewModel)
            }
        }
    }
}  

我通过将 .navigationViewStyle(.stack) 添加到 NavigationView 来解决它。我认为那是 iOS 上的默认导航视图样式,但也许在 iOS 15.

中发生了变化