2017-12-28 4 views
2

私は基本的な配列を表すテーブルビューを持っています。セルには、配列のpercentageプロパティの値を示すラベルとスライダがあります。基本的な配列要素の変更に基づいてKVOを使用してtableViewCellを更新する方法

パーセンテージのプロパティが変更されるたびに、キー値の観測を使用してラベルを更新したいと考えています。 (私は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

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 
    } 
} 

は、あなたが今、あなたの細胞の基底クラスを持つことができる、ということ終われば(a)スライダが変化した場合にモデルオブジェクトを更新する。

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 
    } 
} 

得られます:詳細については

enter image description here

を、(b)は、この例ではそのdynamicプロパティへの変更、value変更はモデルオブジェクトで観察されているラベルを更新を観察Using Swift with Cocoa and Objective-C: Adopting Cocoa Patternsの「Key-Value Observing」セクションを参照してください。

+0

ありがとうございます@Rob。私はその事例を私の実装に取り​​入れています。そして、私はほとんどそれを取得します。それは完全にそれをgrokするためにそれ以上行くだろう。本当に助けていただきありがとうございます!スイフトはあなたと強いです。 – Sean

+0

[working](https://imgur.com/a/dyGtX) – Sean

0

HI @sean問題は、単にコードの下に

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() 
    } 

} 
を試しcell.lableとスライダーの値を渡す必要がすでにdiSetメソッドを作成しているので、あなたがいけないのUITableView細胞クラスであります
+0

お返事ありがとうございます。私はこれを試しても、私は更新するラベルを得ることができません。コードの変更を反映するために質問を編集しました。まだ間違っていることが見えることはありますか?ご協力いただきありがとうございます。 – Sean

関連する問題