如何在UIView的帧上进行键值观察并获得KVO回调?

hfo*_*sli 77 iphone objective-c key-value-observing uiview ios

我想看了在变化UIViewframe,boundscenter财产.我如何使用Key-Value Observing来实现这一目标?

hfo*_*sli 68

通常会有KVO不受支持的通知或其他可观察事件.即使文档说"不",观察CALayer支持UIView表面上也是安全的.观察CALayer在实践中是有效的,因为它广泛使用KVO和适当的访问器(而不是ivar操作).它不能保证继续工作.

无论如何,视图的框架只是其他属性的产物.因此我们需要观察那些:

[self.view addObserver:self forKeyPath:@"frame" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"bounds" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"transform" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"position" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"zPosition" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"anchorPoint" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"anchorPointZ" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"frame" options:0 context:NULL];
Run Code Online (Sandbox Code Playgroud)

请参阅此处的完整示例 https://gist.github.com/hfossli/7234623

注意:据说文档不支持这个版本,但是到目前为止所有iOS版本都适用(目前iOS 2 - > iOS 11)

注意:请注意,在达到最终值之前,您将收到多个回调.例如,更改视图或图层的框架将导致图层更改positionbounds(按此顺序).


使用ReactiveCocoa,您可以做到

RACSignal *signal = [RACSignal merge:@[
  RACObserve(view, frame),
  RACObserve(view, layer.bounds),
  RACObserve(view, layer.transform),
  RACObserve(view, layer.position),
  RACObserve(view, layer.zPosition),
  RACObserve(view, layer.anchorPoint),
  RACObserve(view, layer.anchorPointZ),
  RACObserve(view, layer.frame),
  ]];

[signal subscribeNext:^(id x) {
    NSLog(@"View probably changed its geometry");
}];
Run Code Online (Sandbox Code Playgroud)

如果您只想知道何时bounds可以进行更改

@weakify(view);
RACSignal *boundsChanged = [[signal map:^id(id value) {
    @strongify(view);
    return [NSValue valueWithCGRect:view.bounds];
}] distinctUntilChanged];

[boundsChanged subscribeNext:^(id ignore) {
    NSLog(@"View bounds changed its geometry");
}];
Run Code Online (Sandbox Code Playgroud)

如果您只想知道何时frame可以进行更改

@weakify(view);
RACSignal *frameChanged = [[signal map:^id(id value) {
    @strongify(view);
    return [NSValue valueWithCGRect:view.frame];
}] distinctUntilChanged];

[frameChanged subscribeNext:^(id ignore) {
    NSLog(@"View frame changed its geometry");
}];
Run Code Online (Sandbox Code Playgroud)

  • 请传递有效的上下文指针.这样做可以让您区分您的观察结果和其他对象的观察结果.不这样做会导致未定义的行为,特别是在删除观察者时. (5认同)
  • 好吧CALayer.h说"CALayer为类及其子类定义的所有Objective C属性实现了标准的NSKeyValueCoding协议......"所以你去吧.:)他们是可观察的. (4认同)
  • 我正在低估以引起人们对使用KVO的方法所带来的问题的关注.我试图解释一个工作示例不支持如何在代码中正确执行某些操作的建议.如果你不相信这里的另一个参考是[不可能观察任意的UIKit属性](http://stackoverflow.com/a/6612561/104790). (3认同)
  • 你是对的核心动画对象是[记录](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreAnimation_guide/Key-ValueCodingExtensions/Key-ValueCodingExtensions.html#//apple_ref/doc/ uid/TP40004514-CH12-SW2)符合KVC标准.但这并未说明KVO的合规性.KVC和KVO只是不同的东西(尽管KVC合规性是KVO合规性的先决条件). (2认同)

hfo*_*sli 62

编辑:我不认为这个解决方案足够彻底.这个答案是出于历史原因而保留的.请在此处查看我的最新答案:https://stackoverflow.com/a/19687115/202451


你必须在frame-property上做KVO.在这种情况下,"self"是一个UIViewController.

添加观察者(通常在viewDidLoad中完成):

[self addObserver:self forKeyPath:@"view.frame" options:NSKeyValueObservingOptionOld context:NULL];
Run Code Online (Sandbox Code Playgroud)

删除观察者(通常在dealloc或viewDidDisappear :)中完成:

[self removeObserver:self forKeyPath:@"view.frame"];
Run Code Online (Sandbox Code Playgroud)

获取有关更改的信息

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if([keyPath isEqualToString:@"view.frame"]) {
        CGRect oldFrame = CGRectNull;
        CGRect newFrame = CGRectNull;
        if([change objectForKey:@"old"] != [NSNull null]) {
            oldFrame = [[change objectForKey:@"old"] CGRectValue];
        }
        if([object valueForKeyPath:keyPath] != [NSNull null]) {
            newFrame = [[object valueForKeyPath:keyPath] CGRectValue];
        }
    }
}

?
Run Code Online (Sandbox Code Playgroud)

  • 确认,也适合我.UIView.frame是可以正确观察的.有趣的是,UIView.bounds不是. (3认同)
  • -1:既不是`UIViewController`声明`view`也不是`UIView`声明`frame`是KVO兼容键.Cocoa和Cocoa-touch不允许任意观察键.必须正确记录所有可观察的密钥.它似乎工作的事实并没有使这个有效(生产安全)的方式来观察视图上的帧变化. (3认同)

Nik*_*uhe 7

目前,无法使用KVO观察视图的帧.属性必须符合KVO才能被观察到.遗憾的是,与任何其他系统框架一样,UIKit框架的属性通常是不可观察的.

文档:

注意:虽然UIKit框架的类通常不支持KVO,但您仍可以在应用程序的自定义对象中实现它,包括自定义视图.

此规则有一些例外,例如NSOperationQueue的operations属性,但必须明确记录.

即使在视图的属性上使用KVO当前可能有效,我也不建议在运输代码中使用它.这是一种脆弱的方法,依赖于无证件的行为.