观察更改一个UIView的窗口和上海华性质

问题描述 投票:23回答:4

我正在寻找一种方式,当一个普通的UIView中添加或可见视图层次结构中移除通知。志愿看起来完美的东西在这种情况下使用,但观察更改视图的窗口或上海华属性不会做任何事情。似乎像帧,或工作的backgroundColor属性,但预期改变有关视图层次性的变化并没有调用过observeValueForKeyPath。

我检查,看看是否UIView的调用automaticallyNotifiesObserversForKey支持这些特性志愿和UIView的报道是两个,留下我不知所措。所以我的问题是:

1)是否有使用KVO通知有关添加视图/移除视图层次事件的一种方式?

2)如果不是有没有被通知不涉及子类的UIView此类事件的另一种方式?

uiview key-value-observing
4个回答
10
投票

这是一种方式。它是毛?是。我建议这样的行为?不可以,但我们这里都是成年人。

要点是,你用method_setImplementation更改实施 - [UIView的didAddSubview:]等你拿,只要它被称为通知(你会做同样的事情willRemoveSubview :)。不幸的是,你会得到要求的所有视图层次的变化。你必须添加自己的过滤,以找到你感兴趣的具体意见。

static void InstallAddSubviewListener(void (^listener)(id _self, UIView* subview))
{
    if ( listener == NULL )
    {
        NSLog(@"listener cannot be NULL.");
        return;
    }

    Method addSubviewMethod = class_getInstanceMethod([UIView class], @selector(didAddSubview:));
    IMP originalImp = method_getImplementation(addSubviewMethod);

    void (^block)(id, UIView*) = ^(id _self, UIView* subview) {
        originalImp(_self, @selector(didAddSubview:), subview);
        listener(_self, subview);
    };

    IMP newImp = imp_implementationWithBlock((__bridge void*)block);
    method_setImplementation(addSubviewMethod, newImp);
}

要使用,这样做:

InstallAddSubviewListener(^(id _self, UIView *subview) {
    NSLog(@"-[UIView didAddSubview:]   self=%@, view=%@", _self, subview);
});

11
投票

重写此方法:

- (void)didMoveToSuperview
{
  UIView *superView = [self superview];
}

你可以覆盖其他使用自定义视图这些方法:

- (void)willMoveToSuperview:(UIView *)newSuperview;
- (void)didMoveToSuperview;
- (void)willMoveToWindow:(UIWindow *)newWindow;
- (void)didMoveToWindow;

2
投票

基于由@道格 - 理查德森的代码,为什么不是干净了一点,让志愿为上海华财产?

//Make views announce their change of superviews
    Method method = class_getInstanceMethod([UIView class], @selector(willMoveToSuperview:));
    IMP originalImp = method_getImplementation(method);

    void (^block)(id, UIView*) = ^(id _self, UIView* superview) {
        [_self willChangeValueForKey:@"superview"];
        originalImp(_self, @selector(willMoveToSuperview:), superview);
        [_self didChangeValueForKey:@"superview"];
    };

    IMP newImp = imp_implementationWithBlock((__bridge void*)block);
    method_setImplementation(method, newImp);

1
投票

这里是我的解决方案,使用的想法上面,修复了一些错误,并使其扩展。您可以使用这款平板来监控SuperView把一些类的志愿和它的子类变化的,它应该是即插即用。

用C写的是简单易懂,快捷。你可以修改这些是NSObject中的一些华而不实的类别。

使用示例:

add_superview_kvo(UILabel.class);

然后,你就必须自己观察者添加到实例按正常使用。

// simple datatype to neatly manage the various runtime elements
typedef struct {
    Class target;
    SEL cmd;
    Method method;
    IMP imp;
} override_t;

// call to initialize an override struct
static override_t
_override_init(Class target, SEL sel) {
    BOOL instance = YES; // should be able to handle class methods by changing this
    override_t o = {
        .target = target,
        .cmd = sel,
        // note this can return a method from the superclass
        .method = instance ? class_getInstanceMethod(target,sel) : class_getClassMethod(target,sel),
        .imp = method_getImplementation(o.method)
    };
    return o;
};

// applies the runtime patch
static void
_override_patch(override_t o, id _Nonnull block) {
    IMP imp = imp_implementationWithBlock(block);
    // first we try to add the method to the class, if we are
    // dealing with an inherited method from a superclass, our
    // new method will drop right in
    if (!class_addMethod(o.target, o.cmd, imp, method_getTypeEncoding(o.method))){
        // this means we got the original method from the
        // class we're manipulating, so we just overwrite
        // its version
        method_setImplementation(o.method,imp);
    }
}

// pass the class in here that you want to monitor for superview changes
// if you pass in UIView.class it will monitor all views... this may
// generate unnecessary overhead, so you can focus it on a class that
// you want (you will get that class and all its subclasses)
void
add_superview_kvo(Class target)
{
    NSString *keyPath = @"superview";

    override_t override = _override_init(target,@selector(willMoveToSuperview:));
    _override_patch(override,^void(id _self, UIView *superview) {
        [_self willChangeValueForKey:keyPath];
        // note that this is the correct way to call an imp, it must be cast
        ((void(*)(id,SEL,id))override.imp)(_self, override.cmd, superview);
    });

    override = _override_init(target,@selector(didMoveToSuperview));
    _override_patch(override,^void(id _self) {
        ((void(*)(id,SEL))override.imp)(_self, override.cmd);
        [_self didChangeValueForKey:keyPath];
    });
}
© www.soinside.com 2019 - 2024. All rights reserved.