【问题标题】:Set UILabel Text at Completion of CloudKit Asyncronous Operation in Swift在 Swift 中完成 CloudKit 异步操作时设置 UILabel 文本
【发布时间】:2017-11-09 03:09:58
【问题描述】:

我是 Swift 的新手,通常是异步代码,所以告诉我这是否可行。基本上我想:

  1. 打开应用程序
  2. 触发读取 CloudKit 记录
  3. 读取完成后,UILabel 将显示检索到的记录数

这本身显然没有用,但作为一个原则,它将帮助我理解异步代码操作,以及如何在它们完成时触发操作。

// In ViewController Swift file:

class ViewController: UIViewController{
    
    override func viewDidLoad() {
        super.viewDidLoad()
        readDatabase()
    }
    @IBOutlet weak var myLabel: UILabel!
}

let VC=ViewController()


//In Another Swift file:

func readDatabase() {
    let predicate = NSPredicate(value: true)
    let query = CKQuery(recordType: "myRecord", predicate: predicate)
    let container = CKContainer.default()
    let privateDB = container.privateCloudDatabase

    privateDB.perform(query, inZoneWith:nil) { (allRecs, err) in
        VC.myLabel.text = ("\(allRecs?.count) records retreived")
    
    /*
    ERROR OCCURS IN LINE ABOVE:
       CONSOLE: fatal error: unexpectedly found nil while unwrapping an Optional value
       BY CODE LINE: Thread 8:EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) 
    */

    }
}

我可以在 viewDidLoad 函数中设置文本字段,那么为什么不能从该函数中调用的函数中设置呢?

我尝试过的其他一些事情:

  • 使用异步调度将其放在线程 1 上
  • 在 ViewController 类中实现一个 var with,使用一个设置文本的 didSet,在 privateDB.perform 代码中将 var 设置为所需的值以触发更改

这些都会产生与上述相同的问题。

是的,我知道 .perform 中没有任何错误处理,是的,有记录。如果我在视图加载几秒钟后手动触发将 UILabel 文本设置为记录计数,它可以正常工作。

所以问题是…… 如何使用数据库读取完成作为触发器将记录的属性加载到视图中?

谢谢


编辑

这里实际发生的是 VC 是全局创建的,但从未呈现 - 因为从未调用过 loadView,所以 myLabel 不存在,并且作为强制解包的属性,当它发生时导致崩溃被引用了

【问题讨论】:

    标签: ios swift swift3


    【解决方案1】:

    问题在于这一行:let VC=ViewController()。在这里,您实例化 ViewController 类的一个新实例,并尝试在该新创建的实例上设置标签。但是,您可能希望在当前显示的 viewController 实例上设置标签。

    只需将这一行 VC.myLabel.text = ("\(allRecs?.count) records retreived") 更改为 self.myLabel.text = ("\(allRecs?.count) records retreived") 即可。

    【讨论】:

    • 您也可能(不确定,因为我从未使用过这个框架)更新主线程中的标签(闭包在后台线程中我不会感到惊讶)。
    • 将其更改为 self.mylabel 会导致“使用未解析的标识符 'self'”错误。那是因为 readDatabase() 函数超出了 viewController 范围吗?
    • 是的,这就是原因,我没有看到关于它在一个单独的类中的评论,对不起
    【解决方案2】:

    知道了,解决方案如下:

    // In ViewController Swift file:
    
    typealias CompletionHandler = (_ recCount:Int,_ err:Error?) -> Void
    
    class ViewController: UIViewController{
    
        override func viewDidLoad() {
            super.viewDidLoad()
            readDatabase(completionHandler: { (recCount,success) -> Void in
                if err == nil {
                     self.myLabel.text = "\(recCount) records loaded"
                } else {
                     self.myLabel.text = "load failed: \(err)"     
                }
            })
        }
        @IBOutlet weak var myLabel: UILabel!
    }
    
    
    //In Another Swift file:
    
    func readDatabase() {
        let predicate = NSPredicate(value: true)
        let query = CKQuery(recordType: "myRecord", predicate: predicate)
        let container = CKContainer.default()
        let privateDB = container.privateCloudDatabase
    
        privateDB.perform(query, inZoneWith:nil) { (allRecs, err) in
            if let recCount = allRecs?.count {
                completionHandler(recCount,err)
            } else {
                completionHandler(0,err)
            }
        }
    }
    

    this 和原来的区别在于 this 在函数调用中使用 CompletionHandler 类型别名来加载数据库记录,它返回记录的计数和一个可选的错误。

    完成操作现在可以存在于 ViewController 类中,并使用 self.myLabel 访问 UILabel,这解决了之前发生的错误,同时将数据库加载代码与 ViewController 类分开。

    这个版本的代码也有基本的错误处理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-15
      • 1970-01-01
      • 2016-09-06
      相关资源
      最近更新 更多