Created
May 10, 2014 23:28
-
-
Save tangphillip/818bdd6d916b62f607b7 to your computer and use it in GitHub Desktop.
Objective-C: Subclassing for delegate behavior!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A demonstration: fixing the UITextView return bug with a subclass | |
// | |
// Code for acutally fixing the bug from http://inessential.com/2014/01/07/uitextview_the_solution | |
#import <UIKit/UIKit.h> | |
@interface PSTTextView : UITextView | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "PSTTextView.h" | |
#import <objc/runtime.h> | |
@interface PSTTextView () <UITextViewDelegate> | |
@property (nonatomic, weak) id<UITextViewDelegate> externalDelegate; | |
@end | |
@implementation PSTTextView | |
- (id)initWithCoder:(NSCoder *)coder { | |
if (self = [super initWithCoder:coder]) { | |
[super setDelegate:self]; | |
} | |
return self; | |
} | |
- (instancetype)initWithFrame:(CGRect)frame textContainer:(NSTextContainer *)textContainer { | |
if (self = [super initWithFrame:frame textContainer:textContainer]) { | |
[super setDelegate:self]; | |
} | |
return self; | |
} | |
#pragma mark - Mutator | |
- (void)setDelegate:(id<UITextViewDelegate>)delegate { | |
self.externalDelegate = delegate; | |
} | |
#pragma mark - Method forwarding | |
+ (BOOL)isInstanceMethodSelector:(SEL)selector inProtocol:(Protocol *)protocol { | |
struct objc_method_description requiredMethod = protocol_getMethodDescription(protocol, selector, YES, YES); | |
struct objc_method_description optionalMethod = protocol_getMethodDescription(protocol, selector, NO, YES); | |
return (requiredMethod.name != NULL || optionalMethod.name != NULL); | |
} | |
- (id)forwardingTargetForSelector:(SEL)selector { | |
BOOL isDelegateSelector = [[self class] isInstanceMethodSelector:selector | |
inProtocol:@protocol(UITextViewDelegate)]; | |
if (isDelegateSelector && [self.externalDelegate respondsToSelector:selector]) { | |
return self.externalDelegate; | |
} | |
return [super forwardingTargetForSelector:selector]; | |
} | |
- (BOOL)respondsToSelector:(SEL)selector { | |
BOOL isDelegateSelector = [[self class] isInstanceMethodSelector:selector | |
inProtocol:@protocol(UITextViewDelegate)]; | |
if (isDelegateSelector && [self.externalDelegate respondsToSelector:selector]) { | |
return YES; | |
} | |
return [super respondsToSelector:selector]; | |
} | |
#pragma mark - Return bug fix | |
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { | |
if ([text isEqualToString:@"\n"] || [text isEqualToString:@""]) { | |
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(scrollToShowSelection) object:nil]; | |
[self performSelector:@selector(scrollToShowSelection) withObject:nil afterDelay:0.1]; /*Smaller delays are unreliable.*/ | |
} | |
if ([self.externalDelegate respondsToSelector:@selector(textView:shouldChangeTextInRange:replacementText:)]) { | |
return [self.externalDelegate textView:textView shouldChangeTextInRange:range replacementText:text]; | |
} | |
else return YES; | |
} | |
- (void)scrollToShowSelection { | |
if (self.selectedRange.location < self.text.length) { | |
return; | |
} | |
CGPoint bottomOffset = CGPointMake(0, MAX(0, self.contentSize.height - self.bounds.size.height)); | |
[self setContentOffset:bottomOffset animated:YES]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you for posting this. Wouldn't you also want to override
getDelegate:
to returnself.externalDelegate
?