You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在子类中调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:51:13