preferisco una soluzione in cui l'UITableViewCell
fa tutto il KVO da solo. La mia configurazione è la seguente:
Nella sottoclasse della mia cella, ho una proprietà che mantiene un forte riferimento alla mia classe del modello da cui recupero i miei dati, e un metodo che chiamo quando voglio allegare un nuovo oggetto alla proprietà:
@interface MyTableViewCell : UITableViewCell
@property (atomic) id object;
- (void)populateFromObject:(id)object;
Implementazione:
- (void)awakeFromNib {
[super awakeFromNib];
self.contentView.hidden = YES;// avoid displaying an unpopulated cell
}
- (void)populateFromObject:(id)object {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0),^{// handle KVO on a bg thread
if (object && (self.object != object)) {// if new object differs from property...
[self unregisterFromKVO];// ...unregister from old object and...
self.object = object;
for (NSString *keyToObserve in [[object class] displayKeys]) {// ...register to new object
[object addObserver:self forKeyPath:keyToObserve options:0 context:nil];
}
}
});
dispatch_async(dispatch_get_main_queue(), ^{// UI updates on main thread only
// update your outlets here
self.contentView.hidden = NO;// finally display the cell now that it is properly populated
});
}
// ===========
#pragma mark - KVO
// ===========
// KVO notification
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
[self populateFromObject:object];
}
- (void)unregisterFromKVO {
for (NSString *keyToObserve in [YourModelObject displayKeys]) {
[self.object removeObserver:self forKeyPath:keyToObserve];
}
}
- (void)dealloc {
[self unregisterFromKVO];
}
si noti che l'attuale KVO è gestito su un thread in background per evitare di bloccare il thread principale durante lo scorrimento. Si noti inoltre che restituisce immediatamente e pertanto visualizzerà una cella non popolata. Per evitare ciò, nascondiamo la vista del contenuto fino a quando la cella non è completamente popolata. Ora l'unica cosa che resta da implementare è un metodo di classe su YourModelObject
che restituisce un array di chiavi che si desidera KVO:
+ (NSArray<NSString *> *)displayKeys {
return @[@"name",@"Street", @"ZipCode"];
}
..e in UITableViewController
:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"reuseid" forIndexPath:indexPath];
YourModelObject *obj = [myModelArray objectAtIndex:indexPath.row];
[cell populateFromObject:obj];
return cell;
}
Il riferimento forte da la cella dell'oggetto modello assicura che l'oggetto non venga deallocato mentre la cella osserva ancora una delle sue proprietà, cioè è visibile. Una volta che la cellula è stata deallocata, la KVO non è registrata e solo allora l'oggetto del modello sarà deallocato. Per comodità, ho anche un debole riferimento dall'oggetto modello alla cella che può tornare utile quando si implementano i metodi delegati UITableView
.
Pensa di aver capito, grazie al tuo aiuto. Inserito codice di seguito, ma ti darà credito con la risposta. –
È un errore osservare qualsiasi percorso chiave non esplicitamente documentato come conforme a KVO. Non è possibile osservare il 'testo' di un'etichetta. –
Se non sbaglio, non si deve osservare la 'cella', ma l'oggetto' Persona'! Perché l'oggetto potrebbe cambiare e la cella dovrebbe rispondere! –