美文网首页
如何给TextView添加占位字段,看这篇就够了(各种方法集合)

如何给TextView添加占位字段,看这篇就够了(各种方法集合)

作者: 大大大大de好人 | 来源:发表于2018-04-12 21:48 被阅读0次

前言:在开发过程当中,我们很多时候会遇到在textView中像textFiled用到占位符的情况,也就是一个placeHold。但是系统并没有给textView提供这个属性,所以我们需要自己来实现。正好写项目遇到这种情况,就把我知道的几种能实现的方式都实现了出来。

  • 第一种方式 简单粗暴

这种方法的特点是,当用户点击了TextView的,占位符占位文字就会立马消失,官方的占位符是当系统监听到用户输入了文字后占位符才会消失.

//    1.把UITextView的文本属性当成“placeholder”使用
//    2.在开始编辑的代理方法里清除“placeholder”
//    3.在结束编辑的代理方法里根据条件设置“placeholder”。

 // 创建textView
    UITextView *textView =[[UITextView alloc]initWithFrame:CGRectMake(20,70,[UIScreen mainScreen].bounds.size.width-40,200)];
    textView.backgroundColor= [UIColor grayColor];
    textView.text = @"这种方法的特点是,当用户点击了TextView的,占位符占位文字就会立马消失,官方的占位符是当系统监听到用户输入了文字后占位符才会消失";
    textView.textColor = [UIColor lightGrayColor];
    textView.delegate = self;
    [self.view addSubview:textView];

代理方法:

#pragma mark - UITextViewDelegate
- (void)textViewDidEndEditing:(UITextView *)textView
{
    if(textView.text.length < 1){
        textView.text = @"这种方法的特点是,当用户点击了TextView的,占位符占位文字就会立马消失,官方的占位符是当系统监听到用户输入了文字后占位符才会消失";
        textView.textColor = [UIColor grayColor];
    }
}
- (void)textViewDidBeginEditing:(UITextView *)textView
{
    if([textView.text isEqualToString:@"这种方法的特点是,当用户点击了TextView的,占位符占位文字就会立马消失,官方的占位符是当系统监听到用户输入了文字后占位符才会消失"]){
        textView.text=@"";
        textView.textColor=[UIColor blackColor];
    }
}
  • 第二种

该方法同样也可以实现类似于占位符的功能相比较方法一,方法二可以实现动态监听文本的改变,并非弹出键盘就立即清除占位符,只有当用户开始输入文本的时候.placeholder才会消失。同样,当用户清空文本的时候,占位符又会重新显示出来。

这种方法最致命的弊端在于要设置textView的偏移来设置label的位置,不好控制

//    1.创建textView
    UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(10, 74, [UIScreen mainScreen].bounds.size.width - 20, 200)];
    textView.backgroundColor = [UIColor grayColor];
    
    [self.view addSubview:textView];
    self.textView = textView;
    self.textView.delegate = self;
    
    textView.contentInset = UIEdgeInsetsMake(-30, 0, 0, 0);

//    2.给textView添加一个UILabel子控件,作为占位符
    UILabel *placeHolder = [[UILabel alloc] initWithFrame:CGRectMake(15, 0, [UIScreen mainScreen].bounds.size.width - 50, 200)];
    self.placeHolder = placeHolder;
    placeHolder.text = @"该方法同样也可以实现类似于占位符的功能相比较方法一,方法二可以实现动态监听文本的改变,并非弹出键盘就立即清除占位符,只有当用户开始输入文本的时候.placeholder才会消失。同样,当用户清空文本的时候,占位符又会重新显示出来。";
    placeHolder.textColor = [UIColor lightGrayColor];
    placeHolder.numberOfLines = 0;
    placeHolder.contentMode = UIViewContentModeTop;
    [self.textView addSubview:placeHolder];

代理方法

#pragma mark - UITextViewDelegate
- (void)textViewDidChange:(UITextView *)textView
{
    if (!textView.text.length) {
        self.placeHolder.alpha = 1;
    } else {
        self.placeHolder.alpha = 0;
    }
}
  • 第三种

相比计较上面两种方法,这种方法可移植性、拓展性更好,这种方法,不仅乐意随意通过我们添加的placeholder属性设置默认文字,还可以通过我们添加的placeholderColor设置默认文字的颜色。今后,我们只需要写好这么一个自定义UITextView,·就可以一劳永逸。

// 1.自定义UITextView(继承UITextView)
// 2.给UITextView添加placeholder和placeholderColor属性
// 3.重写initWithFrame方法
// 4.添加通知监听文字改变
// 5.重写drawRect:方法
// 6.重写相关属性的set方法

@interface SYJThirdTextView : UITextView
/** 占位文字 */
@property (nonatomic, copy) NSString *placeholder;
/** 占位文字颜色 */
@property (nonatomic, strong) UIColor *placeholderColor;
@end

#.m中实现

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

- (void)textDidChange:(NSNotification *)note
{
    // 会重新调用drawRect:方法
    [self setNeedsDisplay];
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

/**
 * 每次调用drawRect:方法,都会将以前画的东西清除掉
 */
- (void)drawRect:(CGRect)rect
{
    // 如果有文字,就直接返回,不需要画占位文字
    if (self.hasText) return;
    
    // 属性
    NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
    attrs[NSFontAttributeName] = self.font;
    attrs[NSForegroundColorAttributeName] = self.placeholderColor;
    
    // 画文字
    rect.origin.x = 5;
    rect.origin.y = 8;
    rect.size.width -= 2 * rect.origin.x;
    [self.placeholder drawInRect:rect withAttributes:attrs];
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    [self setNeedsDisplay];
}

#pragma mark - setter
- (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];
}

- (void)setText:(NSString *)text
{
    [super setText:text];
    
    [self setNeedsDisplay];
}

- (void)setAttributedText:(NSAttributedString *)attributedText
{
    [super setAttributedText:attributedText];
    
    [self setNeedsDisplay];
}

    SYJThirdTextView *textView = [[SYJThirdTextView alloc]init];
    [self.view addSubview:textView];
    textView.frame = CGRectMake(10, 100, [UIScreen mainScreen].bounds.size.width - 20, 240);
    textView.placeholder = @"相比计较上面两种方法,这种方法可移植性、拓展性更好,这种方法,不仅乐意随意通过我们添加的placeholder属性设置默认文字,还可以通过我们添加的placeholderColor设置默认文字的颜色。今后,我们只需要写好这么一个自定义UITextView,就可以一劳永逸。";
    textView.backgroundColor = [UIColor grayColor];
    //占位符颜色
    textView.placeholderColor = [UIColor lightGrayColor];
  • 第四种

这个方法的和方法三很相似,只是没有利用通知来监听文本的改变,需要配合textViewDidChanged:这个文本改变的代理方法使用。

@interface SYJFourTextView : UITextView

//1.自定义UITextView
//2.给UITextView添加placeholder和placeholderColor属性
//3.重写initWithFrame方法
//4.重写drawRect:方法
//5.重写相关属性的set方法

/** 占位文字 */
@property (nonatomic,copy) NSString *placeholder;
/** 占位文字颜色 */
@property (nonatomic,strong) UIColor *placeholderColor;

@end

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        self.font = [UIFont systemFontOfSize:15];
        self.placeholderColor = [UIColor lightGrayColor];
        self.placeholder = @"请输入内容";
    }
    return self;
}

// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
    NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
    attrs[NSFontAttributeName] = self.font;
    attrs[NSForegroundColorAttributeName] = self.placeholderColor;
    
    [self.placeholder drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height) withAttributes:attrs];
}

// 布局子控件的时候需要重绘
- (void)layoutSubviews
{
    [super layoutSubviews];
    [self setNeedsDisplay];
    
}
// 设置属性的时候需要重绘,所以需要重写相关属性的set方法
- (void)setPlaceholder:(NSString *)placeholder
{
    _placeholder = placeholder;
    [self setNeedsDisplay];
}

- (void)setPlaceholderColor:(UIColor *)placeholderColor
{
    _placeholderColor = placeholderColor;
    [self setNeedsDisplay];
    
}

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

- (void)setText:(NSString *)text
{
    [super setText:text];
    if (text.length) {
        
    // 因为是在文本改变的代理方法中判断是否显示placeholder,而通过代码设置text的方式又不会调用文本改变的代理方法,所以再此根据text是否不为空判断是否显示placeholder。
        self.placeholder = @"";
    }
    [self setNeedsDisplay];
}

- (void)setAttributedText:(NSAttributedString *)attributedText
{
    [super setAttributedText:attributedText];
    if (attributedText.length) {
        self.placeholder = @"";
    }
    [self setNeedsDisplay];
}

SYJFourTextView *textView = [[SYJFourTextView alloc] initWithFrame:CGRectMake(10, 80, self.view.frame.size.width - 20, 240)];
    textView.backgroundColor = [UIColor grayColor];
    textView.placeholder = @"这个方法的和方法三很相似,只是没有利用通知来监听文本的改变,需要配合textViewDidChanged:这个文本改变的代理方法使用。";
    textView.placeholderColor = [UIColor lightGrayColor];
    textView.delegate = self;
    [self.view addSubview:textView];

#pragma mark - UITextViewDelegate
- (void)textViewDidChange:(SYJFourTextView *)textView // 此处取巧,把代理方法参数类型直接改成自定义的SYJFourTextView类型,为了可以使用自定义的placeholder属性,省去了通过给控制器SYJFourTextView类型属性这样一步。
{
    if (textView.hasText) { // textView.text.length
        textView.placeholder = @"";
        
    } else {
        textView.placeholder = @"这个方法的和方法三很相似,只是没有利用通知来监听文本的改变,需要配合textViewDidChanged:这个文本改变的代理方法使用";
        
    }
}

  • 第五种 <推荐>

相对于上面的4种方法,这种方法更加取巧,虽然Apple官方没有给我们开发者提供类似于placeholder的属性,但是通过运行时,我们遍历出了一个placeHolderLabel的私有变量。这种方法简单易懂,代码量少,推荐大家使用这种方法。

//    通过runtime,我们发现,UITextView内部有一个名为“_placeHolderLabel”的私有成员变量。大家知道,Objective-C没有绝对的私有变量,因为我们可以通过KVC来访问私有变量。

    UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(10, 100, [UIScreen mainScreen].bounds.size.width -20, 240)];
    [textView setBackgroundColor:[UIColor greenColor]];
    [self.view addSubview:textView];
    
    // _placeholderLabel
    UILabel *placeHolderLabel = [[UILabel alloc] init];
    placeHolderLabel.text = @"相对于上面的4种方法,这种方法更加取巧,虽然Apple官方没有给我们开发者提供类似于placeholder的属性,但是通过运行时,我们遍历出了一个placeHolderLabel的私有变量。这种方法简单易懂,代码量少,推荐大家使用这种方法。";
    placeHolderLabel.numberOfLines = 0;
    placeHolderLabel.textColor = [UIColor lightGrayColor];
    [placeHolderLabel sizeToFit];
    [textView addSubview:placeHolderLabel];
    
    // same font
    textView.font = [UIFont systemFontOfSize:13.f];
    placeHolderLabel.font = [UIFont systemFontOfSize:13.f];
    
    [textView setValue:placeHolderLabel forKey:@"_placeholderLabel"];
                
  • 第六种(高大上)

这种方法就是比较高大上的一种了,也是第五种深入思考。既然我们可以通过runtime找到一个属性,那么我们也可以通过runtime给他动态添加属性。可以为TextView分类动态添加属性,无需继承,用法方便。

@interface UITextView (SYJText)

/**
 *  UITextView+placeholder
 */
@property (nonatomic, copy) NSString *syj_placeHolder;
/**
 *  IQKeyboardManager等第三方框架会读取placeholder属性并创建UIToolbar展示
 */
@property (nonatomic, copy) NSString *placeholder;
/**
 *  placeHolder颜色
 */
@property (nonatomic, strong) UIColor *syj_placeHolderColor;


@end

#.m

static const void *syj_placeHolderKey;
@interface UITextView ()
@property (nonatomic, readonly) UILabel *syj_placeHolderLabel;
@end

@implementation UITextView (SYJText)

+(void)load{
    [super load];
    
    //方法交换
    method_exchangeImplementations(class_getInstanceMethod(self.class, NSSelectorFromString(@"layoutSubviews")),
                                   class_getInstanceMethod(self.class, @selector(SYJPlaceHolder_swizzling_layoutSubviews)));
    method_exchangeImplementations(class_getInstanceMethod(self.class, NSSelectorFromString(@"dealloc")),
                                   class_getInstanceMethod(self.class, @selector(SYJPlaceHolder_swizzled_dealloc)));
    method_exchangeImplementations(class_getInstanceMethod(self.class, NSSelectorFromString(@"setText:")),
                                   class_getInstanceMethod(self.class, @selector(SYJPlaceHolder_swizzled_setText:)));
}

#pragma mark - swizzling

- (void)SYJPlaceHolder_swizzled_dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [self SYJPlaceHolder_swizzled_dealloc];
}

- (void)SYJPlaceHolder_swizzling_layoutSubviews{
    if (self.syj_placeHolder) {
        UIEdgeInsets textContainerInset = self.textContainerInset;
        CGFloat lineFragmentPadding = self.textContainer.lineFragmentPadding;
        CGFloat x = lineFragmentPadding + textContainerInset.left + self.layer.borderWidth;
        CGFloat y = textContainerInset.top + self.layer.borderWidth;
        CGFloat width = CGRectGetWidth(self.bounds) - x - textContainerInset.right - 2*self.layer.borderWidth;
        CGFloat height = [self.syj_placeHolderLabel sizeThatFits:CGSizeMake(width, 0)].height;
        self.syj_placeHolderLabel.frame = CGRectMake(x, y, width, height);
    }
    [self SYJPlaceHolder_swizzling_layoutSubviews];
}
- (void)SYJPlaceHolder_swizzled_setText:(NSString *)text{
    [self SYJPlaceHolder_swizzled_setText:text];
    if (self.syj_placeHolder) {
        [self updatePlaceHolder];
    }
}
#pragma mark - associated

//动态添加属性
//绑定syj_placeHolder属性,作为textview的占位符
-(NSString *)syj_placeHolder{
    return objc_getAssociatedObject(self, &syj_placeHolderKey);
}
-(void)setSyj_placeHolder:(NSString *)syj_placeHolder{
    objc_setAssociatedObject(self, &syj_placeHolderKey, syj_placeHolder, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self updatePlaceHolder];
}
-(UIColor *)syj_placeHolderColor{
    return self.syj_placeHolderLabel.textColor;
}
-(void)setSyj_placeHolderColor:(UIColor *)syj_placeHolderColor{
    self.syj_placeHolderLabel.textColor = syj_placeHolderColor;
}
-(NSString *)placeholder{
    return self.syj_placeHolder;
}
-(void)setPlaceholder:(NSString *)placeholder{
    self.syj_placeHolder = placeholder;
}

#pragma mark - update
- (void)updatePlaceHolder{
    if (self.text.length) {
        [self.syj_placeHolderLabel removeFromSuperview];
        return;
    }
    self.syj_placeHolderLabel.font = self.font?self.font:self.cacutDefaultFont;
    self.syj_placeHolderLabel.textAlignment = self.textAlignment;
    self.syj_placeHolderLabel.text = self.syj_placeHolder;
    [self insertSubview:self.syj_placeHolderLabel atIndex:0];
}

#pragma mark - lazzing
-(UILabel *)syj_placeHolderLabel{
    UILabel *placeHolderLab = objc_getAssociatedObject(self, @selector(syj_placeHolderLabel));
    if (!placeHolderLab) {
        placeHolderLab = [[UILabel alloc] init];
        placeHolderLab.numberOfLines = 0;
        placeHolderLab.textColor = [UIColor lightGrayColor];
        objc_setAssociatedObject(self, @selector(syj_placeHolderLabel), placeHolderLab, OBJC_ASSOCIATION_RETAIN);
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updatePlaceHolder) name:UITextViewTextDidChangeNotification object:self];
    }
    return placeHolderLab;
}
- (UIFont *)cacutDefaultFont{
    static UIFont *font = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        UITextView *textview = [[UITextView alloc] init];
        textview.text = @" ";
        font = textview.font;
    });
    return font;
}


@end

 //利用runtime黑魔法及动态添加属性对textview添加placeHolder属性
    UITextView *text = [[UITextView alloc]initWithFrame:CGRectMake(10, 100, [UIScreen mainScreen].bounds.size.width - 20, 240)];
    [self.view addSubview:text];
    text.backgroundColor = [UIColor grayColor];
    //设置大小要在font设置place前边
    text.font = [UIFont boldSystemFontOfSize:16];
    text.syj_placeHolder = @"这个方法更加的友好,只需引入头文件,所有文件就能直接使用占位符,不用集成其他类,建议使用这种方法";
    text.syj_placeHolderColor = [UIColor lightGrayColor];

看下各种的效果

TextView占位符

代码上传到github,有需要可以看一下。
https://github.com/SYJshang/TextViewPlaceHolder

相关文章

网友评论

      本文标题:如何给TextView添加占位字段,看这篇就够了(各种方法集合)

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