I am learning how to use KVO. I created two classes, Truck and Driver as shown below.
the Truck class has a textfield and a button, the text should contain the current truck speed, and when the button is pressed, prepareForSegue
should be called and it contains the code posted below.
the Driver class contain a textfield that should be filled with the truck speed. the truck speed in Truck class will be passed to the textfield in Driver class through KVO as shown in the code.
the problem I have or what I am trying to do is, when the user enters a speed of the truck in the Truck class and presses the button, I want to display the truck speed entered in the textfield in the Driver class through KVO
the result I am getting according to the code posted below is, an empty textfield in the Driver class
please let me know why the textfield in the driver class is empty. and how should I pass the value of the truck speed from Truck class to Driver class through KVO
Truck.m
#import "TruckViewController.h"
#import "DriverViewController.h"
#import "ServerViewController.h"
@interface TruckViewController ()
@property (strong, nonatomic) IBOutlet UITextField *textFieldCurrenSpeed;
@property (strong, nonatomic) IBOutlet UIButton *buttonBroadcast;
@property (strong, nonatomic) IBOutlet UIButton
*buttonToDriverViewController;
@property (strong, nonatomic) IBOutlet UIButton
*buttonToServerViewController;
@property (strong, nonatomic) TruckViewController *truck;
@property (strong, nonatomic) DriverViewController *driver;
@property (strong, nonatomic) ServerViewController *server;
@end
@implementation TruckViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
self.truck = [[TruckViewController alloc] init];
self.driver = [[DriverViewController alloc] init];
self.server = [[ServerViewController alloc] init];
if ([segue.identifier isEqualToString:@"segueToDriver"]) {
[self.truck addObserver:self.driver
forKeyPath:@"currentSpeedOfTheTruck"
options:NSKeyValueObservingOptionNew
context:NULL];
self.truck.currentSpeedOfTheTruck = [self.textFieldCurrenSpeed
text];
NSLog(@"prepareForSegue: %@", self.truck.currentSpeedOfTheTruck);
}
if ([segue.identifier isEqualToString:@"segueToServer"]) {
[self.truck addObserver:self.server
forKeyPath:@"currentSpeedOfTheTruck"
options:NSKeyValueObservingOptionNew
context:NULL];
self.truck.currentSpeedOfTheTruck = [self.textFieldCurrenSpeed
text];
NSLog(@"text entered: %@", self.truck.currentSpeedOfTheTruck);
}
}
- (void)dealloc
{
[self.truck removeObserver:self.driver
forKeyPath:@"currentSpeedOfTheTruck"];
[self.truck removeObserver:self.server
forKeyPath:@"currentSpeedOfTheTruck"];
}
Driver.m
#import "DriverViewController.h"
@interface DriverViewController ()
@property (strong, nonatomic) IBOutlet UITextField
*textFieldCurrentSpeed;
@end
-(void) observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id>
*)change
context:(void *)context {
if ([keyPath isEqualToString:@"currentSpeedOfTheTruck"]) {
NSLog(@"DriverViewController->currentSpeedOfTheTruck: %@",
[object valueForKey:keyPath]);
NSLog(@"DriverViewController->currentSpeedOfTheTruck: %@",
[change objectForKey:NSKeyValueChangeNewKey]);
self.receivedCurrentSpeed = [change
objectForKey:NSKeyValueChangeNewKey];
[self.textFieldCurrentSpeed setText:self.receivedCurrentSpeed];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"DriverViewController->viewDidLoad");
[self.textFieldCurrentSpeed setText:self.receivedCurrentSpeed];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
log output:
2017-08-13 19:53:55.293 KVC-1[91529:2346883] DriverViewController-
>currentSpeedOfTheTruck: 45
2017-08-13 19:53:55.294 KVC-1[91529:2346883] DriverViewController-
>currentSpeedOfTheTruck: 45
2017-08-13 19:53:55.294 KVC-1[91529:2346883] prepareForSegue: 45
It appears that you're setting the text content of textFieldCurrentSpeed
in -viewDidLoad
and nowhere else, so it's not surprising that it never changes after the view is loaded.
The pattern for using -observeValueForKeyPath:object:change:context:
is incorrect. You need to use a context variable, check for that, and call super's implementation if it doesn't match. See: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVOBasics.html
The problem is with these lines of code
self.truck = [[TruckViewController alloc] init];
self.driver = [[DriverViewController alloc] init];
self.server = [[ServerViewController alloc] init];
Since your creating another object rather than one is created before preparing for segue, you should use segue.destinationController
to transfer the values
Change the above line of code to
self.truck = (TruckViewController *) segue.destinationViewController;
Accordingly to all viewcontrollers.
There is two mistake:
truck
you add observer driver
, but then you removed it immediately, so in driver
you can't receive notify of the value change for currentSpeedOfTheTruck
. remove it when you don't need observer value changes, but must before driver dealloc
.currentSpeedOfTheTruck
, you only set value for receivedCurrentSpeed
, not self.textFieldCurrentSpeed.text
, so the text
of self.textFieldCurrentSpeed
is empty.
Move [self.textFieldCurrentSpeed setText:receivedCurrentSpeed]
to func observeValueForKeyPath:
or override setter
of receivedCurrentSpeed
Truck.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"segueToServer"]) {
if ([segue.destinationViewController isKindOfClass:[DriverViewController class]]) {
((DriverViewController *)segue.destinationViewController).sourceViewController = self;
[self addObserver:segue.destinationViewController forKeyPath:@"currentSpeedOfTheTruck" options:NSKeyValueObservingOptionNew context:nil];
}
}
}
Driver.h
@property (nonatomic, weak) TrunkViewController *sourceViewController;
Driver.m
- (void)dealloc {
[self.sourceViewController removeObserver:self forKeyPath:@"currentSpeedOfTheTruck"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:@"currentSpeedOfTheTruck"]) {
id newValue = [change objectForKey:NSKeyValueChangeNewKey];
if ([newValue isKindOfClass:NSString.class]) {
self.receivedCurrentSpeed = newValue
[self.textFieldCurrentSpeed setText:self.receivedCurrentSpeed];
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With