SwiftUI Picker 多个错误,核心数据,布局

Posted

技术标签:

【中文标题】SwiftUI Picker 多个错误,核心数据,布局【英文标题】:SwiftUI Picker Multiple Errors, Core Data, Layout 【发布时间】:2019-11-17 01:37:18 【问题描述】:

我一直在努力让表单布局中的多个选取器在 SwiftUI 中工作。

作为测试,我创建了一个简单的视图,其中包含一些文本标签、一些文本字段和 两个采摘者。这是一个核心数据应用程序。第一个选择器是通过首先创建一个 大批。第二个是直接从 Core Data 填充的。好像没有 功能差异或错误。

该功能似乎可以正常工作,但我收到的这些错误看起来像是真实的 问题。单击选择器后,我会毫无问题地转换到选择器列表。 单击列表项后,选择器列表将被关闭,然后返回 ContentView with the appropriate choice listed in the picker.但是,在两种选择器情况下,我都得到 这些错误:

1。 ForEach, Int, Text> count (10) != 它的初始计数 (1)。 ForEach(_:content:) 只能用于常量数据。而是将数据符合Identifiable 或使用ForEach(_:id:content:) 并提供明确的id

2。 [TableView] 仅警告一次:UITableView 被告知布局其可见单元格和其他内容,而不是在视图层次结构中(表视图或其超级视图之一尚未添加到窗口中)。这可能会通过强制表格视图中的视图在没有准确信息的情况下加载和执行布局而导致错误......以及更多的冗长。

这是主视图:

struct ContentView: View 

    @Environment(\.managedObjectContext) var managedObjectContext
    @FetchRequest(fetchRequest: Clinic.getAllClinics()) var clinics: FetchedResults<Clinic>

    @State var nameArray = Clinic.makeClinicNameArray()

    @State private var selectArrayItem = 0
    @State private var selectCoreDataItem = 0
    @State private var tfOne = "TextField One"
    @State private var tfTwo = "TextField Two"
    @State private var tfThree = "TextField Three"
    @State private var tfFour = "TextField Four"

    var body: some View 
        NavigationView 

            Form 

            //I have include groups because in the real app I have many more than
            //ten views in the parent view
                Group 
                    TextField("enter a value for tfOne", text: $tfOne).foregroundColor(.blue)
                

                Section(header: Text("From Generated Array:"), footer: Text("Section End")) 
                    Picker(selection: $selectArrayItem, label: Text("Choose Array Clinic")) 
                        ForEach(0 ..< nameArray.count) 
                            Text(self.nameArray[$0])
                        
                    
                    .foregroundColor(.red)

                    Picker(selection: $selectCoreDataItem, label: Text("Choose Core Data Clinic")) 
                        ForEach(0 ..< clinics.count) 
                            Text("\(self.clinics[$0].name ?? "Nameless")")
                        
                    .foregroundColor(.orange)

                    Text("$selectArrayItem is \(selectArrayItem)")
                    Text("item name is \(self.nameArray[selectArrayItem])")
                //section 1

                Group 
                    TextField("enter a value for tfTwo", text: $tfTwo).foregroundColor(.blue)
                    TextField("enter a value for tfThree", text: $tfThree).foregroundColor(.blue)
                

                Section(header: Text("From Core Data:"), footer: Text("Section End")) 
                    Text("$selectCoreDataItem is \(selectCoreDataItem)")
                    Text("item name is \(self.clinics[selectCoreDataItem].name ?? "Nameless")")
                //section two

                TextField("enter a value for tfFour", text: $tfFour).foregroundColor(.blue)

            //Form
            .navigationBarTitle(Text("Spinners"))

        //nav view
    
 

在阅读了许多 SO 帖子和 Apple 文档后,我仍然没有找到任何相关的内容 我的情况。

作为参考,我在 SO 58784465 中发布了一个类似的问题。在这种情况下改变 从 ScrollView 到 Form 的外部包装器工作 - 有点 - 但仅适用于一个 选择器。添加第二个选择器破坏了整个视图和数据处理。以上 应用就是为了解决这些问题而创建的。

还有 ManagedObject:

public class Clinic : NSManagedObject, Identifiable 
    @NSManaged public var myID: UUID
    @NSManaged public var name: String?
    @NSManaged public var comment: String?
    @NSManaged public var isShown: Bool


extension Clinic 

    static func getAllClinics() -> NSFetchRequest<Clinic> 
        let request: NSFetchRequest<Clinic> = Clinic.fetchRequest() as! NSFetchRequest<Clinic>
        let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
        request.sortDescriptors = [sortDescriptor]
        return request
    

    static func makeClinicArray() -> [Clinic] 
        let kAppDelegate = UIApplication.shared.delegate as! AppDelegate
        let request: NSFetchRequest<Clinic> = Clinic.fetchRequest() as! NSFetchRequest<Clinic>
        let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
        request.sortDescriptors = [sortDescriptor]

        do 
            let results = try kAppDelegate.persistentContainer.viewContext.fetch(request)
            return results
         catch 
            print("error retrieving filtered picker1s")
        
        return [Clinic]()
    

    static func makeClinicNameArray() -> [String] 

        var returnedArray = [String]()
        let kAppDelegate = UIApplication.shared.delegate as! AppDelegate
        let request: NSFetchRequest<Clinic> = Clinic.fetchRequest() as! NSFetchRequest<Clinic>
        let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
        request.sortDescriptors = [sortDescriptor]

        do 
            let results = try kAppDelegate.persistentContainer.viewContext.fetch(request)
            for result in results 
                returnedArray.append(result.name ?? "Nameless")
            
         catch 
            print("error retrieving filtered picker1s")
        
        return returnedArray
    


Xcode 版本 11.2.1 (11B500) 任何指导将不胜感激。

编辑:在 SceneDelegate 中添加核心数据设置(AppDelegate 代码是严格的库存。

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) 

    let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

    if let windowScene = scene as? UIWindowScene 
        let window = UIWindow(windowScene: windowScene)

        let tabby = TabBar().environment(\.managedObjectContext, managedObjectContext)

        window.rootViewController = UIHostingController(rootView: tabby)
        self.window = window
        window.makeKeyAndVisible()
    

还有非常简单的 TabBar: 结构标签栏:查看

@Environment(\.managedObjectContext) var managedObjectContext
@State private var selectionValue = 1

var body: some View 
    TabView(selection : $selectionValue) 
        ContentView().tabItem (
            Image(systemName: "house")
            Text("Home")
        )
        .tag(1)

        Utilities().tabItem (
            Image(systemName: "hammer")
            Text("Utilities")
        )
        .tag(2)

        StartView().tabItem (
            Image(systemName: "forward.fill")
            Text("Start")
        )
        .tag(3)
    

【问题讨论】:

错误1解释了两种解决方案,你试过了吗? 是的。没有喜悦。如果我特别说明了 id: 那么选择器不会返回所选项目。看起来很奇怪。无论如何,ManagedObject 是可识别的。 【参考方案1】:

#1 的答案几乎可以肯定是您需要使用带有 id 参数的 ForEach 初始化程序,并且您的 selection 的类型必须与您的 id 的类型匹配。

比如说

@State private var selectArrayItem = 0

...

Picker(selection: $selectArrayItem, label: Text("Choose Array Clinic")) 
    ForEach(0 ..< nameArray.count, id: \.self) 
        Text(self.nameArray[$0])
    

会起作用,因为selectArrayItemid 都是Int,或者

@State private var selectName = "" 

...

Picker(selection: $selectArrayItem, label: Text("Choose Array Clinic")) 
    ForEach(nameArray, id: \.self) 
        Text($0)
    

会起作用,因为 selectNameid 都是 String

至于 #2,我几乎可以肯定它是 SwiftUI 中的一个错误。当我单击“Choose Array Clinic”行时,我看到打印了上述错误消息,并显示了 Clinics 列表。您会注意到,在选择器视图被推送到导航堆栈后,表格行在出现后不久就会略微跳跃。

错误消息和视觉跳转让我推测 SwiftUI 正试图在它在技术上成为导航堆栈的一部分之前布局行,它会打印错误,然后一旦添加,视图行偏移量是重新计算,导致跳转。这些都不在您的控制之下,并且发生在最简单的 Form/Picker 演示代码中。最好的办法是将report it 作为一个错误并暂时忍受它。除了视觉跳跃外,目前似乎没有任何功能问题。

【讨论】:

我明白了。我不明白 ForEach 初始化程序和 Picker 选择必须是同一类型。当我尝试 id: 我使用的是 UUID 的 myID 属性。使数组和核心数据版本都具有 id: \.self 解决了这个问题,现在两者都产生了正确的结果,没有上面的错误号 1。您对错误 2 - TableView 布局有什么想法吗?我的核心数据实现几乎是样板,但我将编辑问题并添加它。 我将此问题标记为已回答,因为错误 1 ​​更为重要。如果有人可以提供帮助,我仍然希望问题 2 的 cmets。 我什么时候可以回到我的电脑上看看。 我在最后添加了一点关于第二个错误的内容。 TL;DR 可能是一个 SwiftUI 错误。 明白。我会报告的。谢谢。

以上是关于SwiftUI Picker 多个错误,核心数据,布局的主要内容,如果未能解决你的问题,请参考以下文章

在 SwiftUI Picker 中为 NSManagedObject 设置默认值?

选择后的 SwiftUI Picker 动画错误

将核心数据对象提取到 SwiftUI 选择器中

SwiftUI 中的 Picker 适用于 ForEach 的一个版本,但不适用于另一个版本 - 错误或预期行为?

禁用 SwiftUI SegmentedPickerStyle Picker 中的段?

如何让 SwiftUI Picker 在子视图中工作? (变灰)