@Environment(\.dismiss) 错误导致弹出视图在 iOS 15 中加载自身的新版本
@Environment(\.dismiss) bug causes popped view to load a new version of itself in iOS 15
导航到 INCR: 3 并点击导航栏后退按钮或关闭按钮,您会注意到再次调用相同的视图,但这次是新版本,因为 onAppear
firstLoad = true
和 rand
是不同的值。
如果您注释掉 @Environment(\.dismiss) var dismiss
和 dismiss()
,一切都会像在 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.
中发生了变化
导航到 INCR: 3 并点击导航栏后退按钮或关闭按钮,您会注意到再次调用相同的视图,但这次是新版本,因为 onAppear
firstLoad = true
和 rand
是不同的值。
如果您注释掉 @Environment(\.dismiss) var dismiss
和 dismiss()
,一切都会像在 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.