2017-12-28 5 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) 슬라이더가 변경된 경우 모델 객체를 업데이트합니다. value 변경 모델 객체에서 관찰되는 경우와 (b)의 라벨을 갱신이 예제에서 dynamic 속성 변화를 관찰 :

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

자세한 내용 Using Swift with Cocoa and Objective-C: Adopting Cocoa Patterns의 '키 - 값 관측'섹션을 참조하십시오.

+0

고맙습니다. @ Rob. 구현에 예제를 적용하여 작동합니다. 그리고 나는 대부분 그것을 얻는다. 그것을 완전히 악화시키기 위해 더 많이 나아갈 것입니다. 정말 도움을 주셔서 감사합니다! 스위프트는 당신과 함께 강력합니다. – Sean

+0

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

0

하이 @sean 문제는 jQuery과 셀 클래스에 당신은 당신이 그냥 코드를 아래에 시도 cell.lable와 슬라이더 값을 전달하는 필요가없는, 그래서 diSet 방법을 이미

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

} 
+0

답변 해 주셔서 감사합니다. 나는 이것을 시도하고 여전히 업데이트 할 레이블을 얻을 수 없다.코드 변경 사항을 반영하도록 질문을 편집했습니다. 아직도 틀린 것을 볼 수있는 것이 있습니까? 당신의 도움을 주셔서 감사합니다. – Sean

관련 문제