Update HPGrowingTextView for iOS7 compatibility.

From https://github.com/HansPinckaers/GrowingTextView
This commit is contained in:
Guillaume BIENKOWSKI 2013-10-04 17:14:53 +02:00
parent dd5e1d7245
commit 217350708c
4 changed files with 181 additions and 29 deletions

View file

@ -27,6 +27,12 @@
#import <UIKit/UIKit.h>
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 60000
// UITextAlignment is deprecated in iOS 6.0+, use NSTextAlignment instead.
// Reference: https://developer.apple.com/library/ios/documentation/uikit/reference/NSString_UIKit_Additions/Reference/Reference.html
#define NSTextAlignment UITextAlignment
#endif
@class HPGrowingTextView;
@class HPTextViewInternal;
@ -60,10 +66,11 @@
int minNumberOfLines;
BOOL animateHeightChange;
NSTimeInterval animationDuration;
//uitextview properties
NSObject <HPGrowingTextViewDelegate> *__unsafe_unretained delegate;
NSTextAlignment textAlignment;
NSTextAlignment textAlignment;
NSRange selectedRange;
BOOL editable;
UIDataDetectorTypes dataDetectorTypes;
@ -75,7 +82,12 @@
//real class properties
@property int maxNumberOfLines;
@property int minNumberOfLines;
@property (nonatomic) int maxHeight;
@property (nonatomic) int minHeight;
@property BOOL animateHeightChange;
@property NSTimeInterval animationDuration;
@property (nonatomic, strong) NSString *placeholder;
@property (nonatomic, strong) UIColor *placeholderColor;
@property (nonatomic, strong) UITextView *internalTextView;
@ -84,12 +96,13 @@
@property(nonatomic,strong) NSString *text;
@property(nonatomic,strong) UIFont *font;
@property(nonatomic,strong) UIColor *textColor;
@property(nonatomic) NSTextAlignment textAlignment; // default is UITextAlignmentLeft
@property(nonatomic) NSTextAlignment textAlignment; // default is NSTextAlignmentLeft
@property(nonatomic) NSRange selectedRange; // only ranges of length 0 are supported
@property(nonatomic,getter=isEditable) BOOL editable;
@property(nonatomic) UIDataDetectorTypes dataDetectorTypes __OSX_AVAILABLE_STARTING(__MAC_NA, __IPHONE_3_0);
@property (nonatomic) UIReturnKeyType returnKeyType;
@property (assign) UIEdgeInsets contentInset;
@property (nonatomic) BOOL isScrollable;
@property(nonatomic) BOOL enablesReturnKeyAutomatically;
//uitextview methods
@ -101,4 +114,7 @@
- (BOOL)hasText;
- (void)scrollRangeToVisible:(NSRange)range;
// call to force a height change (e.g. after you change max/min lines)
- (void)refreshHeight;
@end

View file

@ -37,15 +37,19 @@
@implementation HPGrowingTextView
@synthesize internalTextView;
@synthesize delegate;
@synthesize maxHeight;
@synthesize minHeight;
@synthesize font;
@synthesize textColor;
@synthesize textAlignment;
@synthesize selectedRange;
@synthesize editable;
@synthesize dataDetectorTypes;
@synthesize dataDetectorTypes;
@synthesize animateHeightChange;
@synthesize animationDuration;
@synthesize returnKeyType;
@dynamic placeholder;
@dynamic placeholderColor;
// having initwithcoder allows us to use HPGrowingTextView in a Nib. -- aob, 9/2011
- (id)initWithCoder:(NSCoder *)aDecoder
@ -82,10 +86,14 @@
minNumberOfLines = 1;
animateHeightChange = YES;
animationDuration = 0.1f;
internalTextView.text = @"";
[self setMaxNumberOfLines:3];
[self setPlaceholderColor:[UIColor lightGrayColor]];
internalTextView.displayPlaceHolder = YES;
}
-(CGSize)sizeThatFits:(CGSize)size
@ -101,10 +109,9 @@
[super layoutSubviews];
CGRect r = self.bounds;
r.origin.y = contentInset.top;
r.origin.x = contentInset.left;
r.origin.y = 0;
r.origin.x = contentInset.left;
r.size.width -= contentInset.left + contentInset.right;
r.size.height -= contentInset.top + contentInset.bottom;
internalTextView.frame = r;
}
@ -114,10 +121,9 @@
contentInset = inset;
CGRect r = self.frame;
r.origin.y = contentInset.top;
r.origin.x = contentInset.left;
r.size.width -= contentInset.left + contentInset.right;
r.size.height -= contentInset.top + contentInset.bottom;
r.origin.y = inset.top - inset.bottom;
r.origin.x = inset.left;
r.size.width -= inset.left + inset.right;
internalTextView.frame = r;
@ -132,6 +138,8 @@
-(void)setMaxNumberOfLines:(int)n
{
if(n == 0 && maxHeight > 0) return; // the user specified a maxHeight themselves.
// Use internalTextView for height calculations, thanks to Gwynne <http://blog.darkrainfall.org/>
NSString *saveText = internalTextView.text, *newText = @"-";
@ -143,13 +151,13 @@
internalTextView.text = newText;
maxHeight = internalTextView.contentSize.height;
maxHeight = [self measureHeight];
internalTextView.text = saveText;
internalTextView.hidden = NO;
internalTextView.delegate = self;
//[self sizeToFit];
[self sizeToFit];
maxNumberOfLines = n;
}
@ -159,8 +167,16 @@
return maxNumberOfLines;
}
- (void)setMaxHeight:(int)height
{
maxHeight = height;
maxNumberOfLines = 0;
}
-(void)setMinNumberOfLines:(int)m
{
if(m == 0 && minHeight > 0) return; // the user specified a minHeight themselves.
// Use internalTextView for height calculations, thanks to Gwynne <http://blog.darkrainfall.org/>
NSString *saveText = internalTextView.text, *newText = @"-";
@ -172,13 +188,13 @@
internalTextView.text = newText;
minHeight = internalTextView.contentSize.height;
minHeight = [self measureHeight];
internalTextView.text = saveText;
internalTextView.hidden = NO;
internalTextView.delegate = self;
//[self sizeToFit];
[self sizeToFit];
minNumberOfLines = m;
}
@ -188,13 +204,43 @@
return minNumberOfLines;
}
- (void)setMinHeight:(int)height
{
minHeight = height;
minNumberOfLines = 0;
}
- (NSString *)placeholder
{
return internalTextView.placeholder;
}
- (void)setPlaceholder:(NSString *)placeholder
{
[internalTextView setPlaceholder:placeholder];
}
- (UIColor *)placeholderColor
{
return internalTextView.placeholderColor;
}
- (void)setPlaceholderColor:(UIColor *)placeholderColor
{
[internalTextView setPlaceholderColor:placeholderColor];
}
- (void)textViewDidChange:(UITextView *)textView
{
{
[self refreshHeight];
}
- (void)refreshHeight
{
//size of content, so we can set the frame of self
NSInteger newSizeH = internalTextView.contentSize.height;
NSInteger newSizeH = [self measureHeight];
if(newSizeH < minHeight || !internalTextView.hasText) newSizeH = minHeight; //not smalles than minHeight
if(newSizeH > maxHeight) newSizeH = maxHeight; // not taller than maxHeight
if (internalTextView.frame.size.height > maxHeight) newSizeH = maxHeight; // not taller than maxHeight
if (internalTextView.frame.size.height != newSizeH)
{
@ -212,7 +258,7 @@
if ([UIView resolveClassMethod:@selector(animateWithDuration:animations:)]) {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000
[UIView animateWithDuration:0.1f
[UIView animateWithDuration:animationDuration
delay:0
options:(UIViewAnimationOptionAllowUserInteraction|
UIViewAnimationOptionBeginFromCurrentState)
@ -227,7 +273,7 @@
#endif
} else {
[UIView beginAnimations:@"" context:nil];
[UIView setAnimationDuration:0.1f];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(growDidStop)];
[UIView setAnimationBeginsFromCurrentState:YES];
@ -244,7 +290,6 @@
}
}
}
// if our new height is greater than the maxHeight
// sets not set the height or move things
@ -260,15 +305,77 @@
internalTextView.scrollEnabled = NO;
}
// scroll to caret (needed on iOS7)
if ([self respondsToSelector:@selector(snapshotViewAfterScreenUpdates:)])
{
CGRect r = [internalTextView caretRectForPosition:internalTextView.selectedTextRange.end];
CGFloat caretY = MAX(r.origin.y - internalTextView.frame.size.height + r.size.height + 8, 0);
if(internalTextView.contentOffset.y < caretY && r.origin.y != INFINITY)
internalTextView.contentOffset = CGPointMake(0, MIN(caretY, internalTextView.contentSize.height));
}
}
// Display (or not) the placeholder string
BOOL wasDisplayingPlaceholder = internalTextView.displayPlaceHolder;
internalTextView.displayPlaceHolder = self.internalTextView.text.length == 0;
if (wasDisplayingPlaceholder != internalTextView.displayPlaceHolder) {
[internalTextView setNeedsDisplay];
}
// Tell the delegate that the text view changed
if ([delegate respondsToSelector:@selector(growingTextViewDidChange:)]) {
if ([delegate respondsToSelector:@selector(growingTextViewDidChange:)]) {
[delegate growingTextViewDidChange:self];
}
}
// Code from apple developer forum - @Steve Krulewitz, @Mark Marszal, @Eric Silverberg
- (CGFloat)measureHeight
{
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
if ([self respondsToSelector:@selector(snapshotViewAfterScreenUpdates:)])
{
CGRect frame = internalTextView.bounds;
CGSize fudgeFactor;
// The padding added around the text on iOS6 and iOS7 is different.
fudgeFactor = CGSizeMake(10.0, 16.0);
frame.size.height -= fudgeFactor.height;
frame.size.width -= fudgeFactor.width;
NSMutableAttributedString* textToMeasure;
if(internalTextView.attributedText && internalTextView.attributedText.length > 0){
textToMeasure = [[NSMutableAttributedString alloc] initWithAttributedString:internalTextView.attributedText];
}
else{
textToMeasure = [[NSMutableAttributedString alloc] initWithString:internalTextView.text];
[textToMeasure addAttribute:NSFontAttributeName value:internalTextView.font range:NSMakeRange(0, textToMeasure.length)];
}
if ([textToMeasure.string hasSuffix:@"\n"])
{
[textToMeasure appendAttributedString:[[NSAttributedString alloc] initWithString:@"-" attributes:@{NSFontAttributeName: internalTextView.font}]];
}
// NSAttributedString class method: boundingRectWithSize:options:context is
// available only on ios7.0 sdk.
CGRect size = [textToMeasure boundingRectWithSize:CGSizeMake(CGRectGetWidth(frame), MAXFLOAT)
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
return CGRectGetHeight(size) + fudgeFactor.height;
}
else
{
return self.internalTextView.contentSize.height;
}
#else
return self.internalTextView.contentSize.height;
#endif
}
-(void)resizeTextView:(NSInteger)newSizeH
{
if ([delegate respondsToSelector:@selector(growingTextView:willChangeHeight:)]) {
@ -283,15 +390,14 @@
internalTextViewFrame.origin.x = contentInset.left;
internalTextViewFrame.size.width = internalTextView.contentSize.width;
internalTextView.frame = internalTextViewFrame;
if(!CGRectEqualToRect(internalTextView.frame, internalTextViewFrame)) internalTextView.frame = internalTextViewFrame;
}
-(void)growDidStop
- (void)growDidStop
{
if ([delegate respondsToSelector:@selector(growingTextView:didChangeHeight:)]) {
[delegate growingTextView:self didChangeHeight:self.frame.size.height];
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
@ -401,6 +507,18 @@
///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)setIsScrollable:(BOOL)isScrollable
{
internalTextView.scrollEnabled = isScrollable;
}
- (BOOL)isScrollable
{
return internalTextView.scrollEnabled;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
-(void)setEditable:(BOOL)beditable
{
internalTextView.editable = beditable;

View file

@ -28,7 +28,10 @@
#import <UIKit/UIKit.h>
@interface HPTextViewInternal : UITextView {
}
@interface HPTextViewInternal : UITextView
@property (nonatomic, strong) NSString *placeholder;
@property (nonatomic, strong) UIColor *placeholderColor;
@property (nonatomic) BOOL displayPlaceHolder;
@end

View file

@ -30,6 +30,10 @@
@implementation HPTextViewInternal
@synthesize placeholder;
@synthesize placeholderColor;
@synthesize displayPlaceHolder;
-(void)setText:(NSString *)text
{
BOOL originalValue = self.scrollEnabled;
@ -41,6 +45,11 @@
[self setScrollEnabled:originalValue];
}
- (void)setScrollable:(BOOL)isScrollable
{
[super setScrollEnabled:isScrollable];
}
-(void)setContentOffset:(CGPoint)s
{
if(self.tracking || self.decelerating){
@ -89,7 +98,13 @@
[super setContentSize:contentSize];
}
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
if (displayPlaceHolder && placeholder && placeholderColor) {
[placeholderColor set];
[placeholder drawInRect:CGRectMake(8.0f, 8.0f, self.frame.size.width - 16.0f, self.frame.size.height - 16.0f) withFont:self.font];
}
}
@end