美文网首页
UITextView探究

UITextView探究

作者: 落叶兮兮 | 来源:发表于2020-10-14 22:58 被阅读0次

iOS开发中,UITextView是一个我们需要经常使用的组件,每次使用都需要默认占位文字,用于告诉用户需要输入什么内容,但是UITextView没有placeHolder这个属性,这篇文章主要介绍相应的解决办法
最终的效果图为:

最终效果图
Demo地址

1.使用UITextView的开始编辑和结束编辑的协议实现

项目中对应的实现文件为:FirstSolutionViewController
UITextViewDelegate中有两个协议方法

//开始编辑时调用
- (void)textViewDidBeginEditing:(UITextView *)textView;
//结束编辑时调用
- (void)textViewDidEndEditing:(UITextView *)textView;

这个方法就是通过这两个方法实现相应的效果

//UITextView懒加载
- (UITextView *)textView {
    if (_textView) {
        return _textView;
    }
    _textView = [[UITextView alloc] initWithFrame:CGRectZero];
    _textView.font = [UIFont systemFontOfSize:18];
    _textView.textColor = [UIColor whiteColor];
    _textView.backgroundColor = [UIColor greenColor];
    _textView.textContainerInset = UIEdgeInsetsMake(25, 10, 10, 10);
    _textView.text = @"请输入相应的内容";
    _textView.delegate = self;
    //这个属性的作用是在键盘上添加一个完成按钮,点击完成按钮可以收起键盘
    _textView.shouldShowHideKeyBoardBtn = YES;
    return _textView;
}

- (void)textViewDidBeginEditing:(UITextView *)textView {
    //判断开始编辑时,文本的内容是不是和自己预设的相同,相同的话将其清空
    if ([textView.text isEqualToString:@"请输入相应的内容"]) {
        textView.text = @"";
    }
}

- (void)textViewDidEndEditing:(UITextView *)textView {
    //当结束编辑时,如果text长度小于1,那么显示原本的placeHolder文字(只有收起键盘才算编辑结束)
    if (textView.text.length < 1) {
        textView.text = @"请输入相应的内容";
    }
}

最终效果图为:


最终效果图

这种方法的缺陷很大,一方面是在没有点击完成按钮,但是text已经被清空的情况下,占位文字不会出现,而且通用性也不是很好,每次使用UILabel都需要这么写(不推荐使用这种方法)

2.添加Label,用来展示placeHolder占位文字

我们可以在UITextView上添加一个UILabel,用label来展示占位文字,同时通过对输入的文本进行判断,来控制label的显示于隐藏,相应的实现文件是SecondSolutionViewController

定义相应的UITextView和UILabel

@property (nonatomic, strong) UITextView *textView;
@property (nonatomic, strong) UILabel *placeHolder;

相应的懒加载为:

- (UITextView *)textView {
    if (_textView) {
        return _textView;
    }
    _textView = [[UITextView alloc] initWithFrame:CGRectZero];
    _textView.font = [UIFont systemFontOfSize:18];
    _textView.textColor = [UIColor whiteColor];
    _textView.backgroundColor = [UIColor greenColor];
    _textView.textContainerInset = UIEdgeInsetsMake(25, 10, 10, 10); 
    //键盘上添加一个完成按钮
    _textView.shouldShowHideKeyBoardBtn = YES;
    _textView.delegate = self;
    return _textView;
}

- (UILabel *)placeHolder {
    if (_placeHolder) {
        return _placeHolder;
    }
    _placeHolder = [[UILabel alloc] initWithFrame:CGRectZero];
    _placeHolder.font = [UIFont systemFontOfSize:18];
    _placeHolder.textColor = [UIColor lightGrayColor];
    _placeHolder.text = @"请输入相应的内容";
    _placeHolder.textAlignment = NSTextAlignmentLeft;
    return _placeHolder;
}

viewDidload中完成相应的添加与布局

- (void)viewDidload {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.view.backgroundColor = [UIColor whiteColor];
    self.title = @"第二种解决办法";
    self.navigationController.navigationBar.translucent = NO;
    [self.view addSubview:self.textView];
    [self.textView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.top.equalTo(@0);
        make.height.equalTo(@300);
    }];
    [self.textView addSubview:self.placeHolder];
    [self.placeHolder mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.textView).offset(15);
        make.top.equalTo(self.textView).offset(25);
        make.width.greaterThanOrEqualTo(@0);
        make.height.greaterThanOrEqualTo(@0);
    }];
}

通过UITextViewDelegate协议中的textViewDidChange方法来控制label的显示与隐藏

- (void)textViewDidChange:(UITextView *)textView {
    if (textView.text.length > 0) {
        self.placeHolder.hidden = YES;
    } else {
        self.placeHolder.hidden = NO;
    }
}

最终的效果图为:


添加UILabel的最终效果图

这种解决办法可以很好的符合我们的需求,但是通用性不是太好,后面每次使用UITextView时,都得为它们添加一个对应的label

3.使用自定义的UITextView添加占位文字(第一种实现方法)

对应的实现文件为XMYPlaceHolderTextView和ThirdSolutionViewController

该方面参考的是简书上的一位作者的文章,相应的链接为:
参考链接
该方法的思路是在自定义的UITextView的drawRect中根据一些逻辑判断添加相应文字

首先,新建一个继承自UITextView的文件,命名为XMYPlaceHolderTextView

在.h文件中定义两个属性,占位文字和占位文字的颜色,方面后面自定义的赋值

/**占位文字*/
@property (nonatomic, copy) NSString *placeHolder;

/**占位文字颜色*/
@property (nonatomic, strong) UIColor *placeHolderColor;

在- (instancetype)initWithFrame:(CGRect)frame方法中,添加关于文字变化的监听

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        //设置默认字体
        self.font = [UIFont systemFontOfSize:15];
        //设置默认字体颜色
        self.placeHolderColor = [UIColor grayColor];
        //使用通知监听文字变化
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewDidChange:) name:UITextViewTextDidChangeNotification object:nil];
    }
    return self;
}

每次文字发生变化会发出通知,调用方法textViewDidChange:

- (void)textViewDidChange:(NSNotification *)notification {
    //该方法会重新走一遍drawRect方法,实现刷新的效果
    [self setNeedsDisplay];
}

在drawRect中,我们将占位文字添加上去,这也是这种方法的核心,使用drawInRect:WithAttributes方法实现,(在后面介绍的第四中方法中,区别在于第四种方法是直接添加了UILabel,之后在自定义的textView中控制显示和隐藏,相比于第二种方法,通用性更好)

- (void)drawRect:(CGRect)rect {
    //如果有文字,那么占位文字不需要显示,直接返回
    if (self.hasText) {
        return;
    }
    //属性
    NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
    attrs[NSFontAttributeName] = self.font;
    attrs[NSForegroundColorAttributeName] = self.placeHolderColor;
    
    //计算相应的边框,画出,用于放置文字
    rect.origin.x = 15;
    rect.origin.y = 25;
    rect.size.width -= 2 * rect.origin.x;
    [self.placeHolder drawInRect:rect withAttributes:attrs];
}

之后,在属性的setter方法中添加[self setNeedsDisplay];
因为每次赋值可能会导致界面变化,所以需要重新调用drawRect

- (void)setPlaceHolder:(NSString *)placeHolder {
    _placeHolder = [placeHolder copy];
    [self setNeedsDisplay];
}

- (void)setPlaceHolderColor:(UIColor *)placeHolderColor {
    _placeHolderColor = placeHolderColor;
    [self setNeedsDisplay];
}

- (void)setFont:(UIFont *)font {
    [super setFont:font];
    [self setNeedsDisplay];
}

之后,在ThirdSolutionViewController中

//使用自定义UITextView
@property (nonatomic, strong) XMYPlaceHolderTextView *textView;

懒加载以及相应的布局

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.view.backgroundColor = [UIColor whiteColor];
    self.title = @"第三种解决办法";
    self.navigationController.navigationBar.translucent = NO;
    [self.view addSubview:self.textView];
    [self.textView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.right.equalTo(self.view);
        make.height.equalTo(@300);
    }];
}

- (XMYPlaceHolderTextView *)textView {
    if (_textView) {
        return _textView;
    }
    _textView = [[XMYPlaceHolderTextView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), 300)];
    _textView.backgroundColor = [UIColor greenColor];
    _textView.font = [UIFont systemFontOfSize:18];
    _textView.textColor = [UIColor whiteColor];
    _textView.textContainerInset = UIEdgeInsetsMake(25, 10, 10, 10);
    _textView.placeHolderColor = [UIColor lightGrayColor];
    _textView.shouldShowHideKeyBoardBtn = YES;
    _textView.placeHolder = @"请输入相应的内容";
    return _textView;
}

最终的效果图为:


最终效果图.png

这种方法,通用性比较好,后面再遇到类似UITextView的都可以使用

4.自定义UITextView实现placeHolder(第二种方法)

相应的实现文件为FourthSolutionViewController和XMYPlaceHolderNewTextView

这种方法和第三中方法中的区别是自定义UITextView中添加placeHolder是通过添加label实现的,其他的代码大致相同,下面只写XMYPlaceHolderNewTextView的实现

在.h文件中定义相应的属性

@property (nonatomic, copy) NSString *placeHolder;
@property (nonatomic, strong) UIColor *placeHolderColor;
@property (nonatomic, strong) UIFont *labelFont;

在.m文件中

@property (nonatomic, strong) UILabel *label;//用于展示placeHolder

懒加载

  • (UILabel *)label {
    if (_label) {
    return _label;
    }
    _label = [[UILabel alloc] initWithFrame:CGRectMake(5, 8, CGRectGetWidth(self.bounds) - 10, 15)];
    _label.font = [UIFont systemFontOfSize:18];
    _label.textColor = [UIColor lightGrayColor];
    _label.textAlignment = NSTextAlignmentLeft;
    _label.text = @"此处输入默认占位文字";
    return _label;
    }
    在初始化方法中,添加对文字改变的监听
- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self addSubview:self.label];
        //使用通知监听文字改变
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChange:) name:UITextViewTextDidChangeNotification object:nil];
    }
    return self;
}

//根据textView中是否有文字来控制label是否显示
- (void)textDidChange:(NSNotification *)notification {
    if (self.hasText) {
        self.label.hidden = YES;
    } else {
        self.label.hidden = NO;
    }
}

之后根据文字内容使label的大小适应长度

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
}

setter方法改变label的属性

- (void)setPlaceHolder:(NSString *)placeHolder {
    _placeHolder = placeHolder;
    self.label.text = placeHolder;
}

- (void)setPlaceHolderColor:(UIColor *)placeHolderColor {
    _placeHolderColor = placeHolderColor;
    self.label.textColor = placeHolderColor;
}

- (void)setLabelFont:(UIFont *)labelFont {
    _labelFont = labelFont;
    self.label.font = labelFont;
}

最终的效果图为:


最终效果图

这种方法,通用性比较好,后面再遇到类似UITextView的都可以使用

5.利用运行时特性给UITextView添加placeHolder占位文字

这种方法参考的简书的一篇文章,相应的链接为:
参考链接
对应的实现文件为FifthSolutionViewController

首先,我们需要先查找一下UITextView是否有placeHolder这个私有属性

在viewDidload中

//通过运行时,我们会发现UITextView有一个叫做"_placeHolderLabel"的变量
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([UITextView class], &count);
    for (int i = 0; i < count; i++) {
        Ivar ivar = ivars[i];
        const char *name = ivar_getName(ivar);
        NSString *objcName = [NSString stringWithUTF8String:name];
        NSLog(@"%d : %@",i,objcName);
    }

通过运行时,我们发现UITextView是有一个叫做“_placeHolderLabel“的属性

之后我们可以通过运行时给其复制

- (void)setupTextView {
    UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 300)];
    textView.shouldShowHideKeyBoardBtn = YES;
    [textView setBackgroundColor:[UIColor greenColor]];
    [textView setFont:[UIFont systemFontOfSize:18.f]];
    [self.view addSubview:textView];
    UILabel *placeHolderLabel = [[UILabel alloc] init];
    placeHolderLabel.text = @"请输入相应的内容";
    placeHolderLabel.numberOfLines = 0;
    placeHolderLabel.textColor = [UIColor lightGrayColor];
    [placeHolderLabel sizeToFit];
    /*
     必须得加这句话,不然的话显示不出来
     */
    [textView addSubview:placeHolderLabel];

    [textView setFont:[UIFont systemFontOfSize:18.f]];
    placeHolderLabel.font = [UIFont systemFontOfSize:18.f];
    [textView setValue:placeHolderLabel forKey:@"_placeholderLabel"];
}

最终的效果图为:


效果图

从上面的图中可以看出,存在一些问题,当UITextView设置了contentInset时,无法设置placeHolderLabel距离左边的间距

placeHolderLabel没有设置文字距离左边间距的属性,我们可以通过自定义的label来实现这个功能

新建一个继承UILabel的文件,命名为customLabel

在customLabel.h中定义属性UIEdgeInsets,这样我们可以通过这个属性设置文字距离左边的边距

在customLabel.m中,

- (void)drawTextInRect:(CGRect)rect {
    [super drawTextInRect:UIEdgeInsetsInsetRect(rect, self.edgeInsets)];
}

之后,将placeHolderlabel替换为custonLabel,并设置edgeInsets属性,最终的效果图为:


最终效果图

总结

Demo地址

相关文章

网友评论

      本文标题:UITextView探究

      本文链接:https://www.haomeiwen.com/subject/gcowpktx.html