如何使用KVO根据底层数组元素更改更新tableViewCells?

时间:2017-12-28 15:13:41

标签: ios swift key-value-observing

我有一个表示底层数组的表视图。单元格有一个标签和一个滑块,它应该显示数组的percentage属性的值。

我想使用键值观察来在百分比属性更改时更新标签。 (我知道KVO在这个例子中有点过分,但最终滑动一个滑块会影响其他单元格,包括滑块的位置,底层数组将从应用程序中的多个位置设置,随时都可以使用KVO。 )

我有一堆帮助from this answer,但我无法启动并更新标签。我在这里包含了我的所有代码。不知道我哪里出错了。

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, CustomCellDelegate {

    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self
        tableView.delegate = self

        for i in 0...4 {
            items.append(Items(ID: i, percentage: 50))
        }
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items.count
    }

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

        if let cell = tableView.dequeueReusableCell(withIdentifier: myTableViewCell.ID) as? myTableViewCell {
            cell.object = items[indexPath.row]
            cell.mySlider.tag = indexPath.row

            return cell

        } else {
            return UITableViewCell()
        }

    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100
    }


    @IBAction func sliderValueChanged(_ sender: UISlider) {
        items[sender.tag].percentage = Double(sender.value)
        print("percentage at \(items[sender.tag].ID) is \(items[sender.tag].percentage)")
    }

    func didUpdateObject(for cell: UITableViewCell) {
        if let indexPath = tableView.indexPath(for: cell) {
            tableView.reloadRows(at: [indexPath], with: .automatic)
            print("hello")
        }
    }
}

class myTableViewCell: UITableViewCell {
    static let ID = "myCell"
    weak var delegate: CustomCellDelegate?
    private var token: NSKeyValueObservation?

    var object: Items? {
        willSet {
            token?.invalidate()
        }
        didSet {
            myLabel.text = "\(object?.percentage ?? 0)"
            token = object?.observe(\.percentage) { [weak self] object, change in
                if let cell = self {
                    cell.delegate?.didUpdateObject(for: cell)
                }
            }
        }
    }

    override func awakeFromNib() {
        super.awakeFromNib()
    }

    @IBOutlet weak var myLabel: UILabel!
    @IBOutlet weak var mySlider: UISlider!

}

class Items: NSObject {
    let ID: Int
    @objc dynamic var percentage: Double

    init(ID: Int, percentage: Double){
        self.ID = ID
        self.percentage = percentage
        super.init()
    }
}

var items: [Items] = []


protocol CustomCellDelegate: class {
    func didUpdateObject(for cell: UITableViewCell)
}

Label not updating

2 个答案:

答案 0 :(得分:2)

要在Swift 4中执行KVO,您必须将该属性声明为dynamic并在该对象上调用observe(_:options:changeHandler:),从而保存生成的NSKeyValueObservation令牌。当该令牌超出范围(或被另一个令牌替换)时,将自动删除原始观察者。

在您的情况下,您让观察者调用委托,然后重新加载单元格。但是你似乎永远不会设置delegate属性,所以我怀疑该方法没有被调用。

但这一切似乎有点脆弱。我倾向于直接在观察者的changeHandler中更新标签。我还认为你可以更直接地更新单元格(我将“值已更改”的IBAction放在单元格中,而不是表格视图中),并消除了tag使用class CustomObject: NSObject { let name: String @objc dynamic var value: Float // this is the property that the custom cell will observe init(name: String, value: Float) { self.name = name self.value = value super.init() } } 来识别哪个非常笨拙模型数组中的行更新了滑块(如果插入或删除行,则可能会出现问题)。

所以考虑一下这个对象:

objects

然后,您可以拥有一个表视图控制器,该控制器使用此模型类型的实例填充class ViewController: UITableViewController { var objects: [CustomObject]! override func viewDidLoad() { super.viewDidLoad() // self sizing cells tableView.estimatedRowHeight = 60 tableView.rowHeight = UITableViewAutomaticDimension // populate model with random data let formatter = NumberFormatter() formatter.numberStyle = .spellOut objects = (0 ..< 1000).map { CustomObject(name: formatter.string(for: $0)!, value: 0.5) } } } // MARK: - UITableViewDataSource extension ViewController { override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return objects?.count ?? 0 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell cell.object = objects[indexPath.row] return cell } } 数组。这里的细节与观察结果基本无关(我们将在下面介绍),但我将其包括在内只是为了提供一个完整的例子:

dynamic

完成后,您现在可以拥有单元格的基类(a)如果滑块更改,则更新模型对象; (b)观察对value属性的更改,在此示例中,在模型对象中观察到class CustomCell: UITableViewCell { @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var valueLabel: UILabel! @IBOutlet weak var valueSlider: UISlider! static private let formatter: NumberFormatter = { let _formatter = NumberFormatter() _formatter.maximumFractionDigits = 2 _formatter.minimumFractionDigits = 2 _formatter.minimumIntegerDigits = 1 return _formatter }() private var token: NSKeyValueObservation? weak var object: CustomObject? { didSet { let value = object?.value ?? 0 nameLabel.text = object?.name valueLabel.text = CustomCell.formatter.string(for: value) valueSlider.value = value token = object?.observe(\.value) { [weak self] object, change in self?.valueLabel.text = CustomCell.formatter.string(for: object.value) } } } @IBAction func didChangeSlider(_ slider: UISlider) { object?.value = slider.value } } 更改时更新标签:

#define mykey 12345
#define perms 0666

struct pdata{
    int ppid;
    char ptype;
    char *pname;
    unsigned long pgenome;
};

int main(int argc, char **argv){

    int shmid;
    char *args[] = {"test2", NULL};
    struct pdata *ap;
    struct pdata p0={12, 'A', "PIPPO", 100};
    shmid = shmget(mykey, sizeof(struct pdata) * 1, perms | IPC_CREAT | IPC_EXCL);
    ap = (struct pdata*) shmat(shmid, NULL, 0);
    ap[0] = p0;

    printf("%s\n", ap[0].pname);
    if(execve("test2", args, NULL) == -1){
        printf("Errore execve\n");
    }

    shmdt(ap);
    shmctl(shmid, IPC_RMID, 0);

    return 0;
}

产量:

enter image description here

有关详细信息,请参阅Using Swift with Cocoa and Objective-C: Adopting Cocoa Patterns的“键值观察”部分。

答案 1 :(得分:0)

嗨@sean问题在UITableview单元类中你已经制作了diSet方法,所以你不需要为cell.lable和slider传递值只是尝试下面的代码

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

    if let cell = tableView.dequeueReusableCell(withIdentifier: myTableViewCell.ID) as? myTableViewCell {
        //pass the object to which you wanna add observer to cell
        cell.object = items[indexPath.row]
        return cell
    } else {
        return UITableViewCell()
    }

}