背景

我想要一个视图,将允许用户创建一个新的核心数据项.该项目将具有到另一个核心数据项目的链接,用户将从选取器中 Select 该核心数据项目,该选取器将预先填充在视图第一次加载时 Select 的默认项目(与该用户最相关的项目).

##问题

但是,当我在.onspecar(in the actual code it's a modified version on .onAppear that only runs the code the first time .onAppear shows but that's good enough for this question)期间更改选取器的"Selection"变量时,它不会更改选取器的选定值.

如果我再次更改它,比如使用运行相同 Select 代码的按钮,它确实会更改选取器.

另外,如果我添加引用选取器的 Select 变量的内容(它是@State.参见示例代码)它可以完美地工作!

如何复制这一问题

要复制在Xcode(14.3)中创建新项目的问题,请 Select App并勾选Use Core Data.

现在用以下代码替换整个ContentView:

import SwiftUI
import CoreData

struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext
    
    @FetchRequest(
        sortDescriptors: []
    ) var items: FetchedResults<Item>
    
    @State private var selectedItem: Item?
    
    var body: some View {
        Form {
            // ---------------------------------------------
            // Uncommenting the next line fixes the pickers!
            //let _ = selectedItem
            // ---------------------------------------------
            Picker ("Part of Item Session", selection: $selectedItem) {
                Text("New Item").tag(nil as Item?)
                ForEach(items) { item in
                    Text("\(item.timestamp!.description)").tag(item as Item?)
                }
            }
            Button {
                ContentView.addItems(withViewContext: viewContext)
            } label: {
                Text("Add Items")
            }
            Button {
                selectedItem = ContentView.getItem(withViewContext: viewContext)
            } label: {
                Text("Select Random Item")
            }
            Button {
                if (selectedItem != nil) {
                    print("Item: \(String(describing: selectedItem!.timestamp))")
                } else {
                    print("Item is nil!")
                }
            } label: {
                Text("View Item (print to console)")
            }
        }
        .onAppear {
            selectedItem = ContentView.getItem(withViewContext: viewContext)
            if (selectedItem != nil) {
                print("Item: \(String(describing: selectedItem!.timestamp))")
            } else {
                print("Item is nil!")
            }
        }
    }
    
    static func addItems(withViewContext viewContext: NSManagedObjectContext) {
        for _ in 0..<10 {
            let newItem = Item(context: viewContext)
            newItem.timestamp = Date().addingTimeInterval(-Double(Int.random(in: 1..<5000)))
        }
        do {
            try viewContext.save()
        } catch {
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
    }
    
    static func getItem(withViewContext viewContext: NSManagedObjectContext) -> Item? {
        let fetchRequest: NSFetchRequest<Item> = Item.fetchRequest()
        fetchRequest.sortDescriptors = []
        
        var items: [Item] = []
        do {
            // Fetch
            items = try viewContext.fetch(fetchRequest)
            
            // Pick a random(ish) item
            if (items.count > 0) {
                let randomInt = Int.random(in: 0..<items.count)
                print("Setting Item: '\(items[randomInt].timestamp!.description)'")
                // Return the random(ish) item
                return items[randomInt]
            }
        } catch {
            print("Unable to Fetch Item, (\(error))")
        }
        
        return nil
    }
}

然后:

  • iPhone 14 Pro Max模拟器中运行该项目
  • 单击Add Items按钮将一些示例添加到您的数据库
  • 从Xcode中重新运行模拟器中的应用程序以重新启动它,您将看到选取器仍然 Select 了New Item,但如果您单击View Item ...按钮,它将打印 Select 的实际值,而不是nil(New Item的值)!
  • 如果您更改 Select 使用Select Random Item(它只是为拾取器 Select 另一个随机项目),它将正确地更改拾取器.

此外,如果您:

  • 再次重新启动应用程序
  • 点击View Item按钮
  • 从控制台中打印的选取器中 Select 相同的项目,它不会更改选取器.
  • 但是,如果您将选取器更改为任何其他项目,它将更改选取器!

修复它的奇怪的黑客

为了看到选取器按我预期的那样工作,取消对第let _ = selectedItem行(第17行)的注释并重新运行应用程序……现在,选取器立刻是正确的!

问题

这是怎么回事.是SWIFT的漏洞,还是我做错了什么?

推荐答案

SwiftUI仅为设置的状态调用Body,如果它们之前已被读取,即它们的getter已被调用.这是SwiftUI的依赖跟踪系统.你做假读的方式很好;这里有另一种方式:

.onAppear { [selectedItem] in

Ios相关问答推荐

如何使用超薄material ,但没有浅色/填充?

如何将Safari调试器附加到Turbo-iOS原生应用中的模拟器

SWIFT AVFoundation翻转相机功能不起作用

如何在HStack中将sfsymbol与Text视图对齐?

封装 Swift 导入

swiftui 图像放大时无法正确拖动

如何链式设置 AttributeContainer 的 UIKit 属性?

SKScene 的更新功能似乎不起作用

为什么 Swift 不在启动的同一线程上恢复异步函数?

.frame() 修饰符的位置如何影响视图?

Swift如何表示单位转换的标准大气压

本月使用 iOS 的天数?

在 Swift 中将 HTML 转换为纯文本

如何使用 sendAsynchronousRequest:queue:completionHandler:

针对 Xcode 中的 iPad 选项

如果已安装,则重定向到应用程序,否则重定向到 App Store

如何识别导航堆栈中的前一个视图控制器

在 iOS 上编写文件

iOS:如何从 URL 调试新启动应用程序

UISegmentedControl 以编程方式更改段数