代表级联和劫持代表在Objective-C中的回调

时间:2014-09-22 20:03:01

标签: ios objective-c delegates delegation

假设我编写了一个UITextField子类,并希望控制用户写入的文本。我将输入字段的委托设置为我自己并实现-textField:shouldChangeCharactersInRange:replacementString:

但是,我仍然希望允许代码使用我作为文本字段的任何部分来实现通常的委托方法。一种方法是存储第二个委托引用并将它们映射为:

- (id)init {
    self = [super init];
    super.delegate = self;
    return self;
}

- (void)setDelegate:(id)delegate {
    self.nextDelegate = delegate;
}

- (id)delegate {
    return self.nextDelegate;
}

然后我将继续实现所有 UITextFieldDelegate方法,并根据需要将它们转发给下一个委托。显然,我可能想要在将一些参数传递给下一个委托之前修改它们,比如-textField:shouldChangeCharactersInRange:replacementString:。 我想到的另一个问题是当用户将nextDelegate设置为文本字段本身时(无论出于何种原因),导致无限循环。

是否有一种更优雅的方式来劫持委托回调,就像我发布的示例代码一样?

3 个答案:

答案 0 :(得分:2)

您的方法存在的问题是被覆盖的delegate访问者:无法保证Apple的代码始终直接使用delegate ivar而不会使用getter访问委托。在这种情况下,它只会调用nextDelegate,绕过你潜入的self代表。

您可能已经检查过您的方法是否适用于当前的实现,但在未来的UIKit版本中也可能会发生变化。

  

是否有一种更优雅的方式来劫持委托回调,就像我发布的示例代码一样?

不,我不知道任何优雅的解决方案。您无法覆盖委托访问者,而是设置辅助委托(您必须手动传递所有委托消息)。

要解决过滤文本输入的实际问题,可能值得研究

- (void)replaceRange:(UITextRange *)range withText:(NSString *)text;

此方法由UITextField实现(因为它采用UITextInput)并且可以重写以过滤text参数。

答案 1 :(得分:1)

我认为你正确地考虑了这一点,你所概述的方法会很好(我已经完成了)。

没有循环问题,因为你不应该在子类的公共接口中公开nextDelegate,所以没有调用者有机会设置一个循环。 (您也可以在设置器中测试delegate != self

但是,如果你能完全避免这种情况,那会更好。例如,如果您只想在文本字段文本发生更改时进行调整,则可以获取控件事件:

[self addTarget:self action:@selector(didChange:) forControlEvents:UIControlEventEditingChanged];

然后,

- (void)textFieldDidChange:(id)sender {
    self.text = [self alteredText];  
}

- (NSString *)alteredText {
    // do whatever transform to user input you wish, like change user input 'a' to 'x'
    return [self.text stringByReplacingOccurrencesOfString:@"a" withString:@"x"];
}

这也可以,但是由于奇怪的副作用,委托将不会在shouldChangeCharactersInRange:中看到changedText。这可以通过使alteredText公开并让班级客户称之而不是标准的getter来解决。

答案 2 :(得分:0)

使用不同的拦截委托消息的方法可以避免子类化的所有问题:“委托代理”。

这个想法是使用一个中间对象(派生自NSProxy),它响应委托消息或将其传递给下一个委托。它基本上是你通过子类化UITextField所做的,但不是使用文本字段对象,我们将使用一个自定义对象来处理一些委托消息的拦截。

这些自定义委托代理构成了一组可重用的构建块,它们只需相互插入即可自定义使用委派的任何对象的行为。

以下是一系列代表的示例(code on github):

UITextField -> TextFilterDelegate -> SomeViewController

UITextField将委托消息传递给TextFilterDelegatetextField:shouldChangeCharactersInRange:replacementString:响应{{1}}并将其他委托消息传递给自己的委托(视图控制器)。