我正在寻找一种方式,当一个普通的UIView中添加或可见视图层次结构中移除通知。志愿看起来完美的东西在这种情况下使用,但观察更改视图的窗口或上海华属性不会做任何事情。似乎像帧,或工作的backgroundColor属性,但预期改变有关视图层次性的变化并没有调用过observeValueForKeyPath。
我检查,看看是否UIView的调用automaticallyNotifiesObserversForKey支持这些特性志愿和UIView的报道是两个,留下我不知所措。所以我的问题是:
1)是否有使用KVO通知有关添加视图/移除视图层次事件的一种方式?
2)如果不是有没有被通知不涉及子类的UIView此类事件的另一种方式?
这是一种方式。它是毛?是。我建议这样的行为?不可以,但我们这里都是成年人。
要点是,你用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);
});
重写此方法:
- (void)didMoveToSuperview
{
UIView *superView = [self superview];
}
你可以覆盖其他使用自定义视图这些方法:
- (void)willMoveToSuperview:(UIView *)newSuperview;
- (void)didMoveToSuperview;
- (void)willMoveToWindow:(UIWindow *)newWindow;
- (void)didMoveToWindow;
基于由@道格 - 理查德森的代码,为什么不是干净了一点,让志愿为上海华财产?
//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);
这里是我的解决方案,使用的想法上面,修复了一些错误,并使其扩展。您可以使用这款平板来监控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];
});
}