美文网首页
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