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