Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/ms-access/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
SwiftUI@State变量不是';t更新_Swiftui_Swiftui Navigationview - Fatal编程技术网

SwiftUI@State变量不是';t更新

SwiftUI@State变量不是';t更新,swiftui,swiftui-navigationview,Swiftui,Swiftui Navigationview,我的代码基本上与中的相同。我遇到的问题是,当tapperstate事件发生时,工作表显示(调用sheet代码),但调试显示showUserEditor为false(在这种情况下,工作表如何显示…?),selectedUserId仍为nil(因此在展开时崩溃…) 观点: struct UsersView: View { @Environment(\.managedObjectContext) private var viewContext @FetchRequest(

我的代码基本上与中的相同。我遇到的问题是,当
tapperstate
事件发生时,工作表显示(调用
sheet
代码),但调试显示
showUserEditor
为false(在这种情况下,工作表如何显示…?),
selectedUserId
仍为
nil
(因此在展开时崩溃…)

观点:

struct UsersView: View {
    @Environment(\.managedObjectContext)
    private var viewContext

    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \User.nickname, ascending: true)],
        animation: .default)
    private var users: FetchedResults<User>
    
    @State private var selectedUserId : NSManagedObjectID? = nil
    @State private var showUserEditor = false
    
    var body: some View {
        NavigationView {
            List {
                ForEach(users) { user in
                    UserRowView(user: user)
                        .onTapGesture {
                            self.selectedUserId = user.objectID
                            self.showUserEditor = true
                        }
                    }
                }
            }.sheet(isPresented: $showUserEditor) {
                UserEditorView(userId: self.selectedUserId!)
            }
        }
}
struct UsersView:View{
@环境(\.managedObjectContext)
私有变量viewContext
@获取请求(
SortDescriptor:[NSSortDescriptor(密钥路径:\User.昵称,升序:true)],
动画:。默认设置)
私有var用户:FetchedResults
@状态私有变量selectedUserId:NSManagedObjectID?=nil
@状态私有变量showUserEditor=false
var body:一些观点{
导航视图{
名单{
ForEach(用户){user in
UserRowView(用户:用户)
.ontapsigne{
self.selectedUserId=user.objectID
self.showUserEditor=true
}
}
}
}.sheet(显示:$showUserEditor){
UserEditorView(用户ID:self.selectedUserId!)
}
}
}

如果您愿意,我可以发布编辑器和行,但它们似乎与问题无关,因为魔术应该发生在视图中。

因此,我仍然没有弄清楚问题中发布的代码为什么不起作用,使用@loremipsum的指针,我通过使用另一个
.sheet()
方法获得了一个工作代码,接受可选项而不是布尔标志的项。代码现在看起来是这样的并且可以工作,但是如果有人能解释为什么发布的代码不能工作,我将不胜感激

struct UsersView: View {
    @Environment(\.managedObjectContext)
    private var viewContext

    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \User.nickname, ascending: true)],
        animation: .default)
    private var users: FetchedResults<User>
    
    @State private var selectedUser : User? = nil
    
    var body: some View {
        NavigationView {
            List {
                ForEach(users) { user in
                    UserRowView(user: user)
                        .onTapGesture {
                            self.selectedUser = user
                        }
                    }.onDelete(perform: deleteItems)
                }
        }.sheet(item: $selectedUser, onDismiss: nil) { user in
            UserEditorView(user: user)
        }
    }
}
struct UsersView:View{
@环境(\.managedObjectContext)
私有变量viewContext
@获取请求(
SortDescriptor:[NSSortDescriptor(密钥路径:\User.昵称,升序:true)],
动画:。默认设置)
私有var用户:FetchedResults
@状态私有变量selectedUser:用户?=nil
var body:一些观点{
导航视图{
名单{
ForEach(用户){user in
UserRowView(用户:用户)
.ontapsigne{
self.selectedUser=用户
}
}.onDelete(执行:删除项)
}
}.sheet(项目:$selectedUser,onDismiss:nil){中的用户
UserEditorView(用户:用户)
}
}
}

struct==immutable
和SwiftUI决定何时初始化并重新加载
struct

不建议使用依赖于SwiftUI在非常特定的时间更新非包装变量的代码。您无法控制此过程

要使您的第一次安装工作正常,您需要为变量使用SwiftUI包装器

.sheet(isPresented: $showUserEditor) {
            //struct == immutable SwiftUI wrappers load the entire struct when there are changes
            //With your original setup this variable gets created/set when the body is loaded so the orginal value of nil is what is seen in the next View
            UserEditorView1(userId: $selectedUserId)
        }

struct UserEditorView1: View {
    //This is what you orginal View likely looks like it won't work because of the struct being immutable and SwiftUI controlling when the struct is reloaded

    //let userId: NSManagedObjectID? <---- Depends on specific reload steps

    //To make it work you would use a SwiftUI wrapper so the variable gets updated when SwiftUI descides to update it which is invisible to the user
    @Binding var userId: NSManagedObjectID?
    //This setup though now requres you to go fetch the object somehow and put it into the View so you can edit it.
    //It is unnecessary though because SwiftUI provides the .sheet init with item where the item that is set gets passed directly vs waiting for the SwiftUi update no optionals
    var body: some View {
        Text(userId?.description ?? "nil userId")
    }
}
现在开始使用易于编辑的CoreData对象编写代码

struct UsersView: View {
    @Environment(\.managedObjectContext)
    private var viewContext
    
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \User.nickname, ascending: true)],
        animation: .default)
    private var users: FetchedResults<User>
    //Your list view would use the CoreData object to trigger a sheet when the new value is available. When nil there will not be a sheet available for showing
    @State private var selectedUser : User? = nil
    
    var body: some View {
        NavigationView {
            List {
                ForEach(users) { user in
                    UserRowView(user: user)
                        .onTapGesture {
                            self.selectedUser = user
                        }
                }
            }
        }.sheet(item: $selectedUser, onDismiss: nil) { user in //This gives you a non-optional user so you don't have to compensate for nil in the next View
            UserEditorView3(user: user)
        }
    }
}

在将
showUserEditor
设置为true之前,至少将
selectedUserId
设置为true,交换行。或者将
工作表
init与中的
一起使用it@vadian我做到了。。。这是显而易见的方式,我改变了它,没有回头看。我将编辑问题以反映这一点。在任何情况下,据我所知,这对SwiftUI重画周期都不重要。另外,请注意,当我调试
工作表
代码时,即使是showUserEditor在打印变量时也是错误的,因此我在这里感到困惑。。。也许我不清楚调试@State变量时应该查找什么。@loremipsum-找到了您的意思:我试试看。有一个
.sheet
init,您可以在其中作为参数传递该项。当项目的
@状态
不是
nil
时,它会显示一张我可以键入答案的工作表。更好地解释一下,当工作表是在已经设置的变量中创建的,并且主体不会因为显示工作表而被重新加载时,结构是不可变的。
struct UsersView: View {
    @Environment(\.managedObjectContext)
    private var viewContext
    
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \User.nickname, ascending: true)],
        animation: .default)
    private var users: FetchedResults<User>
    //Your list view would use the CoreData object to trigger a sheet when the new value is available. When nil there will not be a sheet available for showing
    @State private var selectedUser : User? = nil
    
    var body: some View {
        NavigationView {
            List {
                ForEach(users) { user in
                    UserRowView(user: user)
                        .onTapGesture {
                            self.selectedUser = user
                        }
                }
            }
        }.sheet(item: $selectedUser, onDismiss: nil) { user in //This gives you a non-optional user so you don't have to compensate for nil in the next View
            UserEditorView3(user: user)
        }
    }
}
struct UserEditorView3: View {
    //I mentioned the ObservedObject in my comment 
    @ObservedObject var user: User
    var body: some View {
        //If your nickname is a String? you have to compensate for that optional but it is much simpler to do it from here
        TextField("nickname", text: $user.nickname.bound)
    }
}


//This comes from another very popular SO question (couldn't find it to quote it) that I could not find and is necessary when CoreData does not let you define a variable as non-optional and you want to use Binding for editing
extension Optional where Wrapped == String {
    var _bound: String? {
        get {
            return self
        }
        set {
            self = newValue
        }
    }
    public var bound: String {
        get {
            //This just give you an empty String when the variable is nil
            return _bound ?? ""
        }
        set {
            _bound = newValue.isEmpty ? nil : newValue
        }
    }
}