您的位置:首页 > 移动开发 > 微信开发

封装一个自适应高度的 textview

2015-10-25 15:49 871 查看
这期做组内项目的时候需要用到一个可以自适应高度的textview,类似微信的输入框那样的。有时候又需要在textview里面添加一些提示性的placeholder,但是系统自带的textview不同于UITextFiled,没有placeholder属性的,这就需要我们自定义做一个placeholder,我的做法是在textview上面添加一个label,当开始编辑的时候将其隐藏,在一开始创建就未初始化placeholderString的时候则不创建这个label。

首先,确定下需要提供给外部使用的功能,能够自定义的字体上下间距up_LowSpace,最大字数maxWorlds,最小字数minWorlds,最大高度maxHei,最小高度minHei,通过定义了三个不同功能的block,提供多个block回调方法,可以实现更多自定义功能。同时还可以提供字数统计,能自定义字数统计的文案和字数的字体和颜色,以及实时提示当前已输入字数:@“您已输入xx字”或者还可输入的字数:@“您还可以输入xx字”。

总结下功能如下:

1/带有沉底文字和字数统计,可以自定义沉底文字的样式,字数统计的frame;

2/自定义textview最大高度和宽度,还可以让textview根据输入的字数多少动态改变高度;

3/自定义可以输入的最大字数,当达到最大字数的时候通过block回调自定义动作;

4/提供多个对外多block接口,支持多种情况下的操作,例如,开始编辑的时候,结束编辑的时候。

另:

textview的几个代理方法

- (BOOL)textViewShouldBeginEditing:(UITextView *)textView;
//return YES:开始编辑 return NO:不允许编辑 
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView;
//return YES:开始编辑 return NO:不允许编辑
- (void)textViewDidBeginEditing:(UITextView *)textView;
//已经开始编辑
- (void)textViewDidEndEditing:(UITextView *)textView;
//已经结束编辑
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
//这个方法是讲你即将输入的字符放入textview中去处理,
//return YES:允许输入,也就是将你原来最后一个字符替换为你即将输入的字符  return NO:不允许输入,此时无论你键盘如何输入,textview中的text都是不会改变的。
- (void)textViewDidChangeSelection:(UITextView *)textView;
//这个方法不是很了解具体是什么意思,大概的意思是移动光标位置
- (BOOL)textViewShouldEndEditing:(UITextView *)textView;
//return YES:结束编辑 return NO:不允许结束编辑
- (void)textViewDidChange:(UITextView *)textView;
//textview中的text已经发生了改变


输入过程中,方法顺序大致为:
1/

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
2/

- (void)textViewDidChangeSelection:(UITextView *)textView;
3/

- (void)textViewDidChange:(UITextView *)textView;


好,上代码:

AHTextView.h文件

#import <UIKit/UIKit.h>
@class AHTextView;
typedef BOOL (^shouldBlock) (AHTextView *textView);
typedef void (^didBlock)    (AHTextView *textView);
typedef void (^changeHeightBlock) (AHTextView *textView,CGFloat height);

@interface AHTextView : UIView

@property (nonatomic,assign) CGFloat up_LowSpace;//字体上下间距
@property (nonatomic,assign) CGFloat maxWorlds;  //最多字数
@property (nonatomic,assign) CGFloat minWorlds;  //最少字数
@property (nonatomic,assign) CGFloat maxHei;     //最大高度
@property (nonatomic,assign) CGFloat minHei;     //最小高度

/**
*  textview delegate方法block
*/
@property (nonatomic,assign) shouldBlock shouldBeginEditing;
@property (nonatomic,assign) shouldBlock shouldEndEditing;
@property (nonatomic,assign) didBlock didChangeSelection;
@property (nonatomic,assign) didBlock didBeginEditing;
@property (nonatomic,assign) didBlock didEndEditing;
@property (nonatomic,assign) didBlock textDidChanged;

@property (nonatomic,assign) didBlock comeToMaxWorlds;//达到最大字数自动方法block
@property (nonatomic,assign) didBlock tapReturn;      //点击回车键自定方法block

@property (nonatomic,assign) changeHeightBlock changeHeightOption;//高度改变block

/**
@pram placeholderString:沉底文字
@pram placeholderAttribute:沉底文字属性
@pram cornerRadius:圆角半径,小于零将被设置为圆形
*/
+ (instancetype)createTextViewWithFrame: (CGRect)frame
backgroundColor: (UIColor *)color
backImageName: (NSString *)imgName
textAttribute: (NSMutableDictionary *)textAttribute
up_LowSpace: (CGFloat)up_LowSpace
maxWorlds: (CGFloat)maxWorlds
minWorlds: (CGFloat)minWolrds
maxHeight: (CGFloat)maxHei
minHeight: (CGFloat)minHei
placeholderString: (NSString *)placeholderString
placeholderAttribute: (NSMutableDictionary *)placeholderAttribute
cornerRadius: (CGFloat)cornerRadius;
@end


AHTextView.h文件
实现初始化类方法:

+ (instancetype)creatTextVeiwWithFrame: (CGRect)frame
backgroundColor: (UIColor *)color
backImageName: (NSString *)imgName
textAttribute: (NSMutableDictionary *)textAttribute
up_LowSpace: (CGFloat)up_LowSpace
maxWorlds: (CGFloat)maxWorlds
minWorlds: (CGFloat)minWolrds
maxHeight: (CGFloat)maxHei
minHeight: (CGFloat)minHei
placeholderString: (NSString *)placeholderString
placeholderAttribute: (NSMutableDictionary *)placeholderAttribute
cornerRadius: (CGFloat)cornerRadius
{
return [[self alloc] initWithFrame:frame backgroundColor:color backImageName:imgName textAttribute:textAttribute up_LowSpace:up_LowSpace maxWorlds:maxWorlds minWorlds:minWorlds maxHeight:maxHei minHeight:minHei placeholderString:placeholderString placeholderAttribute:placeholderAttribute cornerRadius: (CGFloat)cornerRadius];
}


定义并实现自己定义的initWith方法,记得调用[super init],最后调用自定义的创建textview的方法

- (void)initialzeTextViewWithFrame: textAttribute: up_LowSpace: placeholderString: placeholderAttribute: cornerRadius:

- (instancetype)initWithFrame: (CGRect)frame
backgroundColor: (UIColor *)color
backImageName: (NSString *)imgName

4000
textAttribute: (NSMutableDictionary *)textAttribute
up_LowSpace: (CGFloat)up_LowSpace
maxWorlds: (CGFloat)maxWorlds
minWorlds: (CGFloat)minWolrds
maxHeight: (CGFloat)maxHei
minHeight: (CGFloat)minHei
placeholderString: (NSString *)placeholderString
placeholderAttribute: (NSMutableDictionary *)placeholderAttribute
cornerRadius: (CGFloat)cornerRadius

{
//计算一行的高度
NSString *tempContent = @"hah";
UIFont *textFont = [textAttribute objectForKey:NSFontAttributeName];
NSDictionary *tempAttr;
//设置了字体大小就使用自定义的,否则使用默认值15.0f
if (textFont)
{
tempAttr = @{NSFontAttributeName:textFont};
}
else
{
tempAttr = @{NSFontAttributeName:[UIFont systemFontOfSize:15.0f]};
}
CGSize textSize = [tempContent boundingRectWithSize:CGSizeMake(frame.size.width, maxHei) options:NSStringDrawingUsesFontLeading attributes:tempAttr context:nil].size;

// 只有单行时,textview的高度
CGFloat singleLineH = textSize.height + 2*up_LowSpace;
if (minHei < singleLineH)
{
minHei = singleLineH;
}

//根据最大最小高度调整frame.size.height
if (frame.size.height < minHei)
{
frame.size.height = minHei;

}
else if (frame.size.height <= maxHei)
{
minHei = frame.size.width;
}
else
{
frame.size.height = maxHei;
}

//处理最大最小字数
if (minWolrds < 0)
{
minWolrds = 0;
}
else if (maxWorlds < minWolrds)
{
maxWorlds = minWolrds + 20;
}

self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = color;
self.maxWorlds = maxWorlds;
self.minWolrds = minWolrds;
self.maxHei = maxHei;
self.minHei = minHei;
[self initialzeTextViewWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height) textAttribute:textAttribute up_LowSpace:up_LowSpace placeholderString:placeholderString placeholderAttribute:placeholderAttribute cornerRadius:cornerRadius];
}
return self;
}


实现textview的初始化方法,如果有设置placehoderString,则在textview上面添加一个label用于显示沉底文字,否则不创建label:

- (void)initialzeTextViewWithFrame: (CGRect)frame
textAttribute: (NSMutableDictionary *)textAttribute
up_LowSpace: (CGFloat)up_LowSpace
placeholderString: (NSString *)placeholderString
placeholderAttribute: (NSMutableDictionary *)placeholderAttribute
cornerRadius: (CGFloat)cornerRadius
{
UITextView *textView = [[UITextView alloc] initWithFrame:frame];

//根据属性字典设置字体属性,取不到的情况下使用默认值:黑色 15号
UIFont  *textFont  = [textAttribute objectForKey:NSFontAttributeName];
UIColor *textColor = [textAttribute objectForKey:NSForegroundColorAttributeName];
if (textFont)
{
textView.font = textFont;
}
else
{
textView.font = [UIFont systemFontOfSize:15];
}

if (textColor)                                                                                                                    {
textView.textColor = textColor;
}
else
{
textView.textColor = [UIColor blackColor];
}

//设置上下间距
textView.textContainerInset = UIEdgeInsetsMake(up_LowSpace, 0, up_LowSpace, 0);
textView.backgroundColor = [UIColor whiteColor];

//小于0设置为圆形
cornerRadius = cornerRadius < 0?  frame.size.height/2:cornerRadius;
textView.layer.cornerRadius = cornerRadius;
textView.layer.masksToBounds = YES;
textView.delegate = self;
[self addSubview:textView];

//有placeholderString才创建label
if (placeholderString && ![placeholderString isEqualToString:@""] && ![placeholderString isEqualToString:@" "])
{
[self initialzePlaceholderViewWithString:placeholderString stringAttribute:placeholderAttribute superView:textView];
}

self.textView = textView;
}

接下来,根据placehoderstring和placeholderattribute创建placehoderlabel,并将label添加到textveiw上面去

#pragma mark placehloder
- (void)initialzePlaceholderViewWithString: (NSString *)placeholderString stringAttribute: (NSMutableDictionary *)placeholderAttribute superView: (UIView *)superView
{

UILabel *label = [[UILabel alloc] init];
CGRect textRect = self.textView.frame;
label.frame = CGRectMake(5, textRect.origin.y, textRect.size.width - 5, textRect.size.height);
label.backgroundColor = [UIColor clearColor];
label.numberOfLines = 0;
UIFont  *textFont  = [placeholderAttribute objectForKey:NSFontAttributeName];
UIColor *textColor = [placeholderAttribute objectForKey:NSForegroundColorAttributeName];
if (textFont)
{
label.font = textFont;
}
else
{
label.font = [UIFont systemFontOfSize:15];
}

if (textColor) {
label.textColor = textColor;
}
else
{
label.textColor = [UIColor grayColor];
}

//将placeholder显示文字置顶
CGFloat totalHeight = [placeholderString boundingRectWithSize:label.frame.size options:NSStringDrawingTruncatesLastVisibleLine attributes:placeholderAttribute context:nil].size.height;
CGFloat singleHeight = [placeholderString boundingRectWithSize:label.frame.size options:NSStringDrawingUsesFontLeading attributes:placeholderAttribute context:nil].size.height;
NSUInteger lineCount = (label.frame.size.height - totalHeight)/ singleHeight ;

for (int i = 0; i < lineCount; i ++) {
placeholderString = [placeholderString stringByAppendingString:@"\n "];
}
label.text = placeholderString;

label.hidden = NO;
[superView addSubview:label];
self.placeholderLabel = label;
}


创建字数统计label

#warning word count
- (void)initialzeWordsCountLabelWithFrame: (CGRect)countFrame stringAttribute: (NSMutableDictionary *)countAttr{
UILabel *countLabel = [[UILabel alloc] initWithFrame:countFrame];

[countLabel setBackgroundColor:[UIColor clearColor]];

if (!countAttr) {
countAttr = [NSMutableDictionary dictionaryWithDictionary:@{NSForegroundColorAttributeName:[UIColor grayColor],NSFontAttributeName:[UIFont systemFontOfSize:12]}];
}
self.wordsCountAttribute = countAttr;
NSMutableAttributedString *attributeStr = [[NSMutableAttributedString alloc] initWithString:@"您已输入0个字" attributes:countAttr];
[attributeStr addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:0.6 green:0 blue:0 alpha:1] range:NSMakeRange(4, 1)];
countLabel.attributedText = attributeStr;

UIFont  *textFont  = [countAttr objectForKey:NSFontAttributeName];
UIColor *textColor = [countAttr objectForKey:NSForegroundColorAttributeName];
if (textFont)
{
self.countFont = textFont;
}
else
{
self.countFont = [UIFont systemFontOfSize:12];
}

if (textColor) {
self.countColor = textColor;
}
else
{
self.countColor = [UIColor grayColor];
}
self.wordsCountLabel = countLabel;
[self addSubview:countLabel];

CGRect lineFrame = countFrame;
lineFrame.origin.x = self.bounds.origin.x;
lineFrame.origin.y = countFrame.origin.y + countFrame.size.height+1;
lineFrame.size.width = self.frame.size.width;
lineFrame.size.height = 0.5;
UIView *line = [[UIView alloc] initWithFrame:lineFrame];
line.backgroundColor = [UIColor grayColor];
[self addSubview:line];
}


所以的UI都创建完毕,接下来在添加 UITextViewDelegate,并实现代理方法。由于最开头就介绍过了各个代理方法的主要作用,这里主要给的是根据输入的文字自适应textview的高度

//动态计算高度
#pragma mark 高度调整
//获取textview中的文本输入框高度
CGFloat textContainerHei = [[self.textView layoutManager] usedRectForTextContainer:self.textView.textContainer].size.height + 2*self.up_LowSpace ;

// 获取textview的实际高度
CGRect textFrame = self.textView.frame ;
if (textContainerHei < self.minHei) {
textFrame.size.height = self.minHei;
}else if (textContainerHei > self.maxHei){
textFrame.size.height = self.maxHei;
}else{
textFrame.size.height = textContainerHei;
}

//获取自身大小
[UIView animateWithDuration:0.25 animations:^{
self.textView.frame = textFrame;
CGRect rect = self.frame;
if (self.wordsCountLabel) {
if (self.upwoardExtension) {
rect.origin.y = self.baseline - textFrame.size.height;
}
rect.size.height = textFrame.size.height + self.wordsCountHei;
NSLog(@"%@",NSStringFromCGRect(self.frame));
}else{
rect.size.height = textFrame.size.height;
}

self.frame = rect;


详细的代码可以查看https://github.com/hah1992/AHTextView
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息