简体   繁体   English

UITextView作为自己的委托意味着无限循环

[英]UITextView as own delegate means infinite loop

I have a subclass of a UITextView and I would like to make it it's own delegate. 我有一个UITextView的子类,我想让它成为自己的委托。 So that I can override the textView:shouldChangeTextInRange:replacementText: method, and prevent consecutive spaces being entered. 这样我就可以覆盖textView:shouldChangeTextInRange:replacementText:方法,并防止输入连续的空格。

In [SATextView awakeFromNib] (SATextView is my subclass of UITextView), I do [self setDelegate:self]; [SATextView awakeFromNib] (SATextView是我的UITextView的子类)中,我做[self setDelegate:self]; . When I press on the textview to start editing everything freezes and eventually stops, and that backtrace shows that there has been an infinite loop. 当我按下textview开始编辑时,所有内容都冻结并最终停止,并且该回溯显示存在无限循环。

It doesn't matter if I implement all delegate methods, just one, or none. 如果我实现所有委托方法,只有一个或没有,这没关系。 Nor does it matter if those methods are empty. 如果这些方法是空的也没关系。

Why does this cause an infinite loop? 为什么会导致无限循环? It only seems to happen if with UITextView (other objects you can subclass and set the delegate to self, and it won't have this problem). 只有使用UITextView(其他对象可以子类化并将委托设置为self,并且它不会出现此问题),似乎只会发生这种情况。 And how can I stop it? 我怎么能阻止它? Or is there a better way to have this subclass not able to have consecutive spaces, 或者是否有更好的方法让这个子类不能有连续的空格,

An idea... You could make a delegate Class that acts as a middle man between the real delegate and the UITextView (because you'll probably need to set the delegate after some time). 一个想法......你可以创建一个委托类,作为真正的委托和UITextView之间的中间人(因为你可能需要在一段时间后设置委托)。 So this new class will implement the delegate protocol, but it will also have a property for it's own delegate, so that you can forward textView:shouldChangeTextInRange:replacementText:, and still do the work of editing the spaces in your middleMan class. 所以这个新类将实现委托协议,但它也有一个属性为它自己的委托,这样你就可以转发textView:shouldChangeTextInRange:replacementText:,并且仍然可以在middleMan类中编辑空间。

It is possible to subscribe any numbers of observers to an object. 可以将任意数量的观察者订阅到对象。 So it is possible to subscribe itself: 所以可以订阅自己:

@implementation MyTextView 

-(id) initWithFrame:(CGRect)frame // or initWithCoder: for loading from nib
{
    self = [super initWithFrame:frame];
    if(self) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewTextDidChangeNotification:) name:UITextViewTextDidChangeNotification object:self];
    }
    return self;
}

-(void)textViewTextDidChangeNotification:(NSNotification*)n
{
    self.text = [self.text stringByReplacingOccurrencesOfString:@"  " withString:@" "];
}

Here is an example of how I have successfully done this. 以下是我成功完成此操作的示例。 I was only interested in preventing certain characters from being inputted so I only implement textView:shouldChangeTextInRange:replacementText: and then pass the rest on. 我只对防止输入某些字符感兴趣所以我只实现了textView:shouldChangeTextInRange:replacementText:然后传递其余的字符。

#import "INFTextView.h"

@interface INFTextView () <UITextViewDelegate>

@property (nonatomic, weak) id<UITextViewDelegate> externalDelegate;

@end

@implementation INFTextView

- (id)init {
    self = [super init];
    if (!self) {
        return nil;
    }

    self.delegate = self;

    return self;
}

- (void)awakeFromNib {
    self.delegate = self;
}

- (void)setDelegate:(id<UITextViewDelegate>)delegate {
    // we always want self to be the delegate, if someone is interested in delegate calls we will forward those on if applicable
    if (delegate == self) {
        [super setDelegate:self];
        return;
    } else {
        // capture that someone else is interested in delegate calls
        _externalDelegate = delegate;
    }
}

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    NSCharacterSet *unsupportedCharacterSet = [[NSCharacterSet characterSetWithCharactersInString:kINFSupportedCharacters] invertedSet];

    NSRange unsupportedCharacterRange = [text rangeOfCharacterFromSet:unsupportedCharacterSet];
    if (unsupportedCharacterRange.location == NSNotFound) {
        return YES;
    } else {
        return NO;
    }
}

- (BOOL)textViewShouldBeginEditing:(UITextView *)textView {
    if ([_externalDelegate respondsToSelector:@selector(textViewShouldBeginEditing:)]) {
        return [_externalDelegate textViewShouldBeginEditing:textView];
    }

    return YES;
}

- (BOOL)textViewShouldEndEditing:(UITextView *)textView {
    if ([_externalDelegate respondsToSelector:@selector(textViewShouldEndEditing:)]) {
        return [_externalDelegate textViewShouldEndEditing:textView];
    }

    return YES;
}

- (void)textViewDidBeginEditing:(UITextView *)textView {
    if ([_externalDelegate respondsToSelector:@selector(textViewDidBeginEditing:)]) {
        [_externalDelegate textViewDidBeginEditing:textView];
    }
}

- (void)textViewDidEndEditing:(UITextView *)textView {
    if ([_externalDelegate respondsToSelector:@selector(textViewDidEndEditing:)]) {
        [_externalDelegate textViewDidEndEditing:textView];
    }
}

- (void)textViewDidChange:(UITextView *)textView {
    if ([_externalDelegate respondsToSelector:@selector(textViewDidChange:)]) {
        [_externalDelegate textViewDidChange:textView];
    }
}

- (void)textViewDidChangeSelection:(UITextView *)textView {
    if ([_externalDelegate respondsToSelector:@selector(textViewDidChangeSelection:)]) {
        [_externalDelegate textViewDidChangeSelection:textView];
    }
}

@end

One big 'gotcha' which led me to this question was I was trying to override delegate to return _externalDelegate but that caused some strange side effects (there must be internal code that depends on the actual delegate to be returned). 导致我回答这个问题的一个重要问题是我试图覆盖delegate以返回_externalDelegate但这会产生一些奇怪的副作用(必须有内部代码依赖于要返回的实际委托)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM