如何在子类中调用UIViewController的方法?附代码场景咨询
如何在UILabel子类中调用所属UIViewController的方法?
先看你现在的代码,你在NewLabel的类方法里用NSTimer把target设为传入的view,想调用VC的callActionInVC方法,但这里有个问题:view是VC的视图对象,不是VC本身,而且这种写法耦合性太强,完全把Label和VC绑死了,扩展性很差。
下面给你几种靠谱的解决方案,按推荐程度排序:
1. 代理模式(最推荐,解耦首选)
代理是iOS里解耦父子组件交互的经典方式,能让Label和VC之间的依赖变得清晰:
首先在NewLabel.h里定义代理协议:
@protocol NewLabelDelegate <NSObject> - (void)callActionInVC; @end @interface NewLabel : UILabel @property (nonatomic, weak) id<NewLabelDelegate> delegate; + (NewLabel*)addLabelIntoView:(UIView*)view delegate:(id<NewLabelDelegate>)delegate; @end
然后修改NewLabel.m里的类方法,把代理传进来,并且让timer的target指向自己,回调里调用代理方法:
+ (NewLabel*)addLabelIntoView:(UIView*)view delegate:(id<NewLabelDelegate>)delegate { NewLabel *label = [[NewLabel alloc] init]; // some codes label.delegate = delegate; [view addSubview:label]; // 把timer的target设为label自己,避免直接依赖VC的view [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(timerFire:) userInfo:label repeats:NO]; return label; } + (void)timerFire:(NSTimer *)timer { NewLabel *label = timer.userInfo; if ([label.delegate respondsToSelector:@selector(callActionInVC)]) { [label.delegate callActionInVC]; } }
最后在VC里实现代理方法,调用Label的方法时传入自己作为代理:
- (void)viewDidLoad { [super viewDidLoad]; [NewLabel addLabelIntoView:self.view delegate:self]; } - (void)callActionInVC { // do some ... }
2. Block回调(灵活轻量)
如果不想写代理协议,Block是更轻量的选择,适合简单的回调场景:
先修改NewLabel.h的类方法,增加Block参数:
typedef void(^NewLabelActionBlock)(void); @interface NewLabel : UILabel + (NewLabel*)addLabelIntoView:(UIView*)view actionBlock:(NewLabelActionBlock)actionBlock; @end
然后在NewLabel.m里保存Block,timer回调时执行:
+ (NewLabel*)addLabelIntoView:(UIView*)view actionBlock:(NewLabelActionBlock)actionBlock { NewLabel *label = [[NewLabel alloc] init]; // some codes [view addSubview:label]; // 把Block存在userInfo里,注意用copy NewLabelActionBlock copiedBlock = [actionBlock copy]; [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(timerFireWithBlock:) userInfo:copiedBlock repeats:NO]; return label; } + (void)timerFireWithBlock:(NSTimer *)timer { NewLabelActionBlock actionBlock = timer.userInfo; if (actionBlock) { actionBlock(); } }
VC里调用时直接传入要执行的逻辑:
- (void)viewDidLoad { [super viewDidLoad]; [NewLabel addLabelIntoView:self.view actionBlock:^{ [self callActionInVC]; }]; } - (void)callActionInVC { // do some ... }
3. 直接获取View的控制器(不推荐,耦合性极高)
如果你只是临时测试,不想改太多代码,可以通过遍历View的响应链找到它的VC,但这种方法很脆弱,VC层级变化后就会失效,只适合临时场景:
修改NewLabel.m里的timer部分:
+ (NewLabel*)addLabelIntoView:(UIView*)view { NewLabel *label = [[NewLabel alloc] init]; // some codes [view addSubview:label]; // 遍历响应链找到VC UIViewController *vc = nil; UIView *currentView = view; while (currentView) { vc = currentView.nextResponder; if ([vc isKindOfClass:[UIViewController class]]) { break; } currentView = currentView.superview; } [NSTimer scheduledTimerWithTimeInterval:1.0f target:vc selector:@selector(callActionInVC) userInfo:nil repeats:NO]; return label; }
这种写法虽然不用改VC的代码,但Label直接依赖VC的方法名,后续VC改名或者方法变更都会导致崩溃,所以强烈建议用前两种方法。
内容的提问来源于stack exchange,提问作者markl
相关产品推荐
相关产品推荐

