封装一个自适应高度的 textview

这期做组内项目的时候需要用到一个可以自适应高度的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
                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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值