我正在学习如何使用KVO。我创建了两个类,卡车和司机,如下所示。
Truck 类有一个文本字段和一个按钮,文本应包含当前的卡车速度,按下按钮时,应调用prepareForSegue
,它包含下面发布的代码。
Driver 类包含一个应用卡车速度填充的文本字段。 卡车类中的卡车速度将通过 KVO 传递到驱动程序类中的文本字段,如代码所示。
我遇到的问题或我正在尝试做的是,当用户在卡车类中输入卡车的速度并按下按钮时,我想通过 KVO 显示在驱动程序类的文本字段中输入的卡车速度
根据下面发布的代码,我得到的结果是,Driver 类中的一个空文本字段
请让我知道为什么驱动程序类中的文本字段为空。 以及我应该如何通过 KVO 将卡车速度的值从卡车类传递到驱动程序类
卡车.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"];
}
司机.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.
}
日志输出:
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
问题出在这些代码行上
self.truck = [[TruckViewController alloc] init];
self.driver = [[DriverViewController alloc] init];
self.server = [[ServerViewController alloc] init];
由于创建另一个对象而不是一个对象是在准备 segue 之前创建的,因此您应该使用segue.destinationController
来传输值
将上面的代码行更改为
self.truck = (TruckViewController *) segue.destinationViewController;
对应于所有视图控制器。
-
您似乎正在
-viewDidLoad
而不是其他任何地方设置textFieldCurrentSpeed
的文本内容,因此加载视图后它永远不会更改也就不足为奇了。 -
使用
-observeValueForKeyPath:object:change:context:
的模式不正确。你需要使用上下文变量,检查它,如果不匹配,调用super的实现。请参阅:https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVOBasics.html
有两个错误:
- 在
truck
中,您添加了观察者driver
,但随后立即将其删除,因此在driver
中您无法收到有关currentSpeedOfTheTruck
的值更改的通知。 当您不需要观察器值更改时,但必须在driver dealloc
之前将其删除。 - 当你收到
currentSpeedOfTheTruck
通知时,你只设置receivedCurrentSpeed
的值,而不是self.textFieldCurrentSpeed.text
,所以self.textFieldCurrentSpeed
的text
是空的。 将[self.textFieldCurrentSpeed setText:receivedCurrentSpeed]
移动到功能observeValueForKeyPath:
或覆盖receivedCurrentSpeed
的setter
卡车.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];
}
}
}
司机.h
@property (nonatomic, weak) TrunkViewController *sourceViewController;
司机.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];
}
}