在表视图单元格中使用KVO跟踪Swift 3中对类实例的特定属性的更改 [英] Using KVO within a tableview cell to track changes to specific properties of a class instance in Swift 3

查看:111
本文介绍了在表视图单元格中使用KVO跟踪Swift 3中对类实例的特定属性的更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用KVO跟踪对我的Account对象的属性的更改,该对象的重要部分如下所示:

I'm trying to use KVO to track changes to the properties of my Account object, the important part of which looks like this:

class Account: NSObject {
    var battleTagLabel: String!
    dynamic var onlineStatusIcon: String!
    dynamic var currentGameIcon: String!
    dynamic var currentStatusLabel: String!

当这三个属性的值发生变化时,我想在我的表视图单元格中得到通知.我的tableview单元格类:

I want to be notified within my tableview cell when those three properties change in value. My tableview cell class:

import Foundation
import UIKit

private var observerContext = 0

class FriendAccountCell: UITableViewCell {

    @IBOutlet weak var onlineStatusIcon: UIImageView!
    @IBOutlet weak var battleTag: UILabel!
    @IBOutlet weak var currentGameIcon: UIImageView!
    @IBOutlet weak var currentStatusLabel: UILabel!

    weak var tableView: UITableView!
    weak var delegate: CustomCellDelegate?
    var onlineStatusIconFlag = false
    var currentStatusLabelFlag = false
    var currentGameIconFlag = false

    var account: Account? {

        willSet {
            if onlineStatusIconFlag {
                print("onlineStatusIconFlag: \(onlineStatusIconFlag)")
                if newValue?.onlineStatusIcon != account?.onlineStatusIcon && account?.onlineStatusIcon != nil {
                    self.account?.removeObserver(self, forKeyPath: #keyPath(onlineStatusIcon))
                    onlineStatusIconFlag = false

                }
            }
            if currentStatusLabelFlag {
                if newValue?.currentStatusLabel != account?.currentStatusLabel && account?.currentStatusLabel != nil {
                    account?.removeObserver(self, forKeyPath: #keyPath(currentStatusLabel))
                    currentStatusLabelFlag = false
                }
            }

            if currentGameIconFlag {
                if newValue?.currentGameIcon != account?.currentGameIcon && account?.currentGameIcon != nil {
                    account?.removeObserver(self, forKeyPath: #keyPath(currentGameIcon))
                    currentGameIconFlag = false

                }
            }
        }

        didSet {
            if oldValue?.onlineStatusIcon != account?.onlineStatusIcon {
                if account?.onlineStatusIcon == "onine" {
                    self.onlineStatusIcon.image = UIImage(named: "20pxButtonGreen")
                } else if account?.onlineStatusIcon == "idle" {
                    self.onlineStatusIcon.image = UIImage(named: "20pxButtonYellow")
                } else if account?.onlineStatusIcon == "busy" {
                    self.onlineStatusIcon.image = UIImage(named: "20pxButtonRed")
                } else {
                    self.onlineStatusIcon.image = UIImage(named: "20pxButtonBlack")
                }
                account?.addObserver(self, forKeyPath: #keyPath(onlineStatusIcon), context: &observerContext)
                onlineStatusIconFlag = true
            }

            if oldValue?.currentStatusLabel != account?.currentStatusLabel {
                self.currentStatusLabel?.text = account?.currentStatusLabel
                account?.addObserver(self, forKeyPath: #keyPath(currentStatusLabel), context: &observerContext)
                currentStatusLabelFlag = true
            }

            if oldValue?.currentGameIcon != account?.currentGameIcon {
                if let currentGame = account?.currentGameIcon {
                    switch currentGame {
                    case "overwatch":
                        self.currentGameIcon.image = UIImage(named: "logo-ow")
                    case "hearthstone":
                        self.currentGameIcon.image = UIImage(named: "logo-hs")
                    case "worldOfWarcraft":
                        self.currentGameIcon.image = UIImage(named: "logo-wow")
                    case "diablo3":
                        self.currentGameIcon.image = UIImage(named: "logo-d3")
                    case "heroesOfTheStorm":
                        self.currentGameIcon.image = UIImage(named: "logo-heroes")
                    case "starCraft":
                        self.currentGameIcon.image = UIImage(named: "logo-sc")
                    case "starCraft2":
                        self.currentGameIcon.image = UIImage(named: "logo-sc2")
                    case "":
                        self.currentGameIcon.image = nil
                    default:
                        self.currentGameIcon.image = nil
                    }
                }
                account?.addObserver(self, forKeyPath: #keyPath(currentGameIcon), context: &observerContext)
                currentGameIconFlag = true
            }
        }

    }
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    guard context == &observerContext else {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        return
    }
    delegate?.didUpdateObject(cell: self)
}


deinit {
    print("deinit called")
    if onlineStatusIconFlag {
        account?.removeObserver(self, forKeyPath: #keyPath(onlineStatusIcon))
        onlineStatusIconFlag = false
    }
    if currentStatusLabelFlag {
        account?.removeObserver(self, forKeyPath: #keyPath(currentStatusLabel))
        currentStatusLabelFlag = false
    }
    if currentGameIconFlag {
        account?.removeObserver(self, forKeyPath: #keyPath(currentGameIcon))
        currentGameIconFlag = false
    }
}

这是我的tableview类的相关部分:

And here is the relevant section of my tableview class:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let identifier: String = "FriendAccountCell"
    var cell: FriendAccountCell
    if let friendCell = self.tableView.dequeueReusableCell(withIdentifier: identifier){
        cell = friendCell as! FriendAccountCell
    } else {
        cell = FriendAccountCell(style: .default, reuseIdentifier: identifier)
        cell.selectionStyle = .none
    }
    var filteredFriends = orderFriends(friendsArray: Array(MyAccountInfo.allFriends.values))

    cell.delegate = self
    cell.account = filteredFriends[indexPath.row]
    cell.battleTag.text = filteredFriends[indexPath.row].battleTagLabel
    cell.currentStatusLabel.text = filteredFriends[indexPath.row].currentStatusLabel

    return cell
}

(上面没有粘贴,但是我还在tableview类中实现了委托函数以重新加载特定的单元格.)

(It's not pasted above, but I also implement the delegate function in my tableview class to reload the specific cells.)

对这些特定属性的更改会在应用程序首次加载并且从服务器获取所有最新数据时迅速发生.之后,更改会更稳定,更缓慢地发生.

The changes to these specific properties happen quickly when the app first loads and all the most current data gets grabbed from the server. Afterward the changes happen more steadily and slowly.

尽管使用了标记和其他策略来尝试正确地跟踪观察者的添加和删除,但仍然出现无法删除键路径的观察者,因为它没有注册为观察者"错误.

Despite the flags and other strategies I've tried to properly track the addition and removal of observers, I'm still getting the "Cannot remove observer for the key path because it is not registered as an observer" error.

推荐答案

我建议简化添加/删除观察者逻辑.当前代码太复杂,并且提供了太多您可能会错过其中一个的路径.因此,只需删除willSet中的观察者,然后在didSet中添加观察者:

I'd suggest simplifying the add/remove observer logic. The current code is too complicated and offers too many paths where you might miss one or the other. So, just remove observers in willSet, and add observers in didSet:

var account: Account? {

    willSet {
        account?.removeObserver(self, forKeyPath: #keyPath(Account.onlineStatusIcon))
        account?.removeObserver(self, forKeyPath: #keyPath(Account.currentStatusLabel))
        account?.removeObserver(self, forKeyPath: #keyPath(Account.currentGameIcon))
    }

    didSet {
        account?.addObserver(self, forKeyPath: #keyPath(Account.onlineStatusIcon), context: &observerContext)
        account?.addObserver(self, forKeyPath: #keyPath(Account.currentStatusLabel), context: &observerContext)
        account?.addObserver(self, forKeyPath: #keyPath(Account.currentGameIcon), context: &observerContext)

        // do any additional logic here you want here
    }

}

deinit {
    account?.removeObserver(self, forKeyPath: #keyPath(Account.onlineStatusIcon))
    account?.removeObserver(self, forKeyPath: #keyPath(Account.currentStatusLabel))
    account?.removeObserver(self, forKeyPath: #keyPath(Account.currentGameIcon))
}

此外,如果您在init中设置了account,请记住,那时不会调用willSet,因此在这种情况下,您必须自己手动添加观察者.

Also, if you set account in init, remember that willSet is not called then, so you'll have to manually add the observers yourself in that one situation.

这篇关于在表视图单元格中使用KVO跟踪Swift 3中对类实例的特定属性的更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆