Post on 14-Jun-2015
transcript
Multi-Level KVOJakub Hladík
Jakub Hladíkjakub@hippotaps.com
@ku33ing, @hippotaps
iOS app developerhippotaps co-founder
iOS teacher at FIT CTU Prague
Key-Value Observing What?
• Key-value observing is a mechanism that allows objects to be notified of changes to specified properties of other objects.
• alternatives you should already know:
• delegate/protocol
• NSNotification
• callback using @selector or block
9
Key-Value Coding First
• mechanism that allows accessing object properties indirectly by a name/key (string)
• NSKeyValueCoding protocol
• NSObject provides default implementation (getters/setters, …)
10
KVC How?
- (id)tableView:(NSTableView *)tableview objectValueForTableColumn:(id)column row:(NSInteger)row{ ChildObject *child = [childrenArray objectAtIndex:row]; if ([[column identifier] isEqualToString:@"name"]) { return [child name]; }
if ([[column identifier] isEqualToString:@"age"]) { return [child age]; }
if ([[column identifier] isEqualToString:@"favoriteColor"]) { return [child favoriteColor]; }}
11
KVC How?
- (id)tableView:(NSTableView *)tableviewobjectValueForTableColumn:(id)column row:(NSInteger)row{ ChildObject *child = [childrenArray objectAtIndex:row]; return [child valueForKey:[column identifier]]; }
12
KVC Terminology
• properties:
• attributes (scalar, string, NSNumber, …)
• to-one relationships (self.otherObject, self.superView, …)
• to-many relationships (collection of objects – NSArray, NSSet, …)
• key (@”age”) – identifies object property
• path (@”superView.frame”, @”address.street”)
– dot.separated keys specifying a sequence of objects to traverse
13
Getting/Setting Attributes
– valueForKey:– valueForKeyPath:– dictionaryWithValuesForKeys:– valueForUndefinedKey:…
– setValue:forKeyPath:– setValuesForKeysWithDictionary:– setNilValueForKey:– setValue:forKey:– setValue:forUndefinedKey:…
14
Getting/Setting Attributes
@interface MyClass
@property NSString *stringProperty;@property NSInteger integerProperty;@property MyClass *linkedInstance;
@end
MyClass *myInstance = [[MyClass alloc] init];NSString *string = [myInstance valueForKey:@"stringProperty"];[myInstance setValue:@2 forKey:@"integerProperty"];
MyClass *anotherInstance = [[MyClass alloc] init];myInstance.linkedInstance = anotherInstance;[myInstance setValue:@2 forKeyPath:@"linkedInstance.integerProperty"];
15
Finally KVO
– observeValueForKeyPath:ofObject:change:context:
– addObserver:forKeyPath:options:context:– removeObserver:forKeyPath:– removeObserver:forKeyPath:context:…
– willChangeValueForKey:– didChangeValueForKey:– willChange:valuesAtIndexes:forKey:– didChange:valuesAtIndexes:forKey:…
16
KVO
17
KVO
18
Simple Observing[self setValue:[[self randomPhoneArray] mutableCopy] forKey:@"dataArray"];
// self.dataArray = [[self randomPhoneArray] mutableCopy];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ if ([keyPath isEqualToString:@"dataArray"]) { self.dataArray = change[NSKeyValueChangeNewKey]; [self.tableView reloadData]; }}
19
DemoSimple Observing
HPT-Multi-Level-KVObranch stage1
20
NSMutableArray Observing
- (void)insertDataObject:(id)object atIndex:(NSUInteger)index{ [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"dataArray"]; [self.dataArray insertObject:object atIndex:index]; [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"dataArray"];}
21
NSMutableArray Observing
- (void)removeDataObjectAtIndex:(NSUInteger)index{ [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"dataArray"]; [self.dataArray removeObjectAtIndex:index]; [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:[NSIndexSet indexSetWithIndex:index] forKey:@"dataArray"];}
22
NSMutableArray Observing
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ if ([keyPath isEqualToString:@"dataArray"]) { NSIndexSet *set = change[NSKeyValueChangeIndexesKey]; NSKeyValueChange valueChange = [change[NSKeyValueChangeKindKey] unsignedIntegerValue]; NSArray *new = change[NSKeyValueChangeNewKey];
switch (valueChange) { case NSKeyValueChangeInsertion: [self addObject:new.lastObject atIndex:set.lastIndex]; break; case NSKeyValueChangeRemoval: [self removeObjectAtIndex:set.lastIndex]; break; case NSKeyValueChangeSetting: self.dataArray = [new mutableCopy]; [self.tableView reloadData]; break; default: break; } }}
23
DemoNSMutableArray Change
Observing
HPT-Multi-Level-KVObranch stage2
24
Array Observing Simpler
- (void)insertDataObject:(id)object atIndex:(NSUInteger)index{ NSMutableArray *array = [self mutableArrayValueForKey:@"dataArray"]; [array insertObject:object atIndex:index];}
- (void)removeDataObjectAtIndex:(NSUInteger)index{ NSMutableArray *array = [self mutableArrayValueForKey:@"dataArray"]; [array removeObjectAtIndex:index];}
25
DemoNSMutableArray Change Observing Done Better
HPT-Multi-Level-KVObranch stage3
26
Nested Object Observing
• NSMutableArray in NSMutableArray ):
• Not possible without ugly hacks…
• NSNotifications… (:
[[NSNotificationCenter defaultCenter] postNotificationName:@"objectInsertedAtIndexPath" object:self userInfo:@{ @"object" : object, @"indexPath" : indexPath }];
27
Multi-Level Demo
Nested NSMutableArray Change Observing using NSNotification
HPT-Multi-Level-KVObranch stage4
28
Is That It?
29
No Way!
30
- (id)valueForUndefinedKey:(NSString *)key{ NSUInteger i = [key integerValue]; return self.dataArray[i];}
Model Change- (id)valueForUndefinedKey:(NSString *)key{ NSUInteger i = [key integerValue]; return self.dataArray[i];}
- (void)insertDataObject:(id)object atIndexPath:(NSIndexPath *)indexPath{ if (indexPath.section == self.dataArray.count) { [self insertDataObject:[NSMutableArray array] atIndex:indexPath.section]; } [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:indexPath.row] forKey:[@(indexPath.section) description]]; NSMutableArray *array = self.dataArray[indexPath.section]; [array insertObject:object atIndex:indexPath.row]; [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:[NSIndexSet indexSetWithIndex:indexPath.row] forKey:[@(indexPath.section) description]];}
31
Observer Change 1
- (void)addObserverForKey:(NSString *)key{ [[HPTDataService sharedService] addObserver:self forKeyPath:key options:NSKeyValueObservingOptionNew context:NULL];}
- (void)removeObserverForLastKey{ [[HPTDataService sharedService] removeObserver:self forKeyPath:[@(self.dataArray.count-1) description] context:NULL];}
32
Observer Change 2
- (void)addObject:(id)object atIndex:(NSUInteger)index{ [self.dataArray insertObject:object atIndex:index]; [self addObserverForKey:[@(self.dataArray.count-1) description]]; [self.tableView beginUpdates]; [self.tableView insertSections:[NSIndexSet indexSetWithIndex:index] withRowAnimation:UITableViewRowAnimationRight]; [self.tableView endUpdates];}
33
Final Working Multi-Level KVO Demo
YEAH!
HPT-Multi-Level-KVObranch stage5
34
The End
• Key-Value Observing Programming Guide
• Key-Value Coding Programming Guide
• https://github.com/kubbing/HPT-Multi-Level-KVO
• http://www.slideshare.net/JakubHladk/for-mobile-513
35