mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-05-07 05:53:06 +00:00
Update HPGrowingTextView for iOS7 compatibility.
From https://github.com/HansPinckaers/GrowingTextView
This commit is contained in:
parent
dd5e1d7245
commit
217350708c
4 changed files with 181 additions and 29 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue