Pregunta

I have UIView category that defines methods to manipulate attributedText property of UILabel and UITextView.

@implementation UIView (replaceAttrText)
-(void) replaceAttrText: (NSString *)str {
    if ([self respondsToSelector: @selector(setAttributedText)]) {
        NSMutableAttributedString *labelText = [self template];

        // change it

        [self performSelector:@selector(setAttributedText) withObject: labelText];
    }
}
@end

respondsToSelector returns false for both UILabel and UITextView (although they respond to setAttributedText) and if setAttributedText is executed directly without respondsToSelector check an exception is raised.

When category is implemented directly on UILabel (without selectors) everything works, but unfortunately UILabel and UITextView don't have common ancestor that has attributedText property.

What am I doing wrong? Thanks!

¿Fue útil?

Solución

UILabel and UITextView do not have a method named setAttributedText. But they do have a method named setAttributedText:. Note the colon. The colon is part of the method name. Having it or not represents two completely separate methods.

Change your code to:

-(void) replaceAttrText: (NSString *)str {
    if ([self respondsToSelector: @selector(setAttributedText:)]) {
        NSMutableAttributedString *labelText = [self template];

        // change it

        [self performSelector:@selector(setAttributedText:) withObject: labelText];
    }
}

In other words, add a colon to both references to setAttributedText.

Otros consejos

@maddy has the right specific answer. This addresses what is considered an anti-pattern.

In general, you shouldn't extend Apple provided classes using categories. Doing so means that your code is effectively intertwined with the system frameworks. This makes maintenance and enhancements more difficult in that you'll end up having to not only deal with your own classes, but also refactor your code that -- through intertwinement -- has an implementation that is in the patterns of both the class it extends and the classes that use it.

This is why Apple generally recommends against these kinds of patterns. If you do extend Apple classes, you should never override existing methods (breaks implementation details) and you should always prefix your methods with a prefix such that future releases of the OS -- even updates -- don't happen to include a method that collides with yours (it has happened before -- addObjectIfAbsent: on NSMutableArray was the most notable such event).

As well, the sometimes-does-the-work-sometimes-does-not behavior of checking isKindOfClass: or respondsToSelector: is another anti-pattern. A well architected application should generally avoid passing around types so generic that the receiver of the type has to figure out what it is before it can operate on it. Doing this defeats the compilers ability to double-check code correctness, amongst other things.

I would suggest that you refactor your application such that your UI objects that need attributed text are accessible via one means and those that don't by another. I.e. whatever calls replaceAttrText: (also, in Objective-C, abbreviations are rarely used. The IDE's completion makes it rare that you need to type anything out and the lack of abbreviations leads to code clarity) would only do so on objects that truly need to have their attributed text adjusted. If you are dynamically or programmatically generating your user interface, you might have a controller object that sits between the model and the views that handles this, for example.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top