UITextView实现占位文字的两种方法

在Cocoa Touch框架中有两种系统自带的文本输入控件:UITextFiledUITextView

  • UITextFiled特点
    • 只显示一行文本输入
    • 高度(30)默认不能修改
    • 没有拖拽滚动(父控件是UIscrollView除外)
  • UITextView特点
    • 支持多行输入显示
    • 支持滚动(继承自UIscrollView)
    • 没有占位文字(placeholder)
    • 本质就是一个可调整的多行输入文本框

有时候我们希望给一个多行文本框增加一个占位文字的功能,来提示用户输入的内容。但是这两种都不能满足我们的要求,这样我们可以自定义一个这样的控件,实现我们的需要。

实现思路:

自定义一个类,继承自UITextView,在现有功能的基础上增加一个占位文字的功能

两种实现方法:
1. 实现UITextView内部的- (void)drawRect:(CGRect)rect方法,将占位文字文字画到UITextView控件上

1.1 在.h的头文件中给外界提供一个占位文字的属性

/**
 *  占位文字
 */
@property (weak, nonatomic) NSString *placeholder;
复制代码

1.2 将占位文字画到矩形框中

/**
 *  绘制占位文字
 */
- (void)drawRect:(CGRect)rect {
    
    NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
    attrs[NSFontAttributeName] = self.font;
    attrs[NSForegroundColorAttributeName] = [UIColor lightGrayColor];
    // 在textView的矩形框中绘制文字
    [self.placeholder drawInRect:CGRectMake(0, 64, self.frame.size.width, self.frame.size.height) withAttributes:attrs];
}
复制代码
  • 注:label的font要在初始化时确定,不然程序会奔溃

1.3 采用通知的方式监听键盘文字的改变。文字一旦改变,会发出一个UITextViewTextDidChangeNotification的通知,所以给TextView初始化后就添加一个监听器

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        // 添加监听器,监听自己的文字改变通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChange) name:UITextViewTextDidChangeNotification object:nil];
    }
    
    return self;
}
// 时刻监听文字键盘文字的变化,文字一旦改变便调用setNeedsDisplay方法
- (void)textDidChange
{
    // 该方法会调用drawRect:方法,立即重新绘制占位文字
    [self setNeedsDisplay];
}
复制代码

1.4 添加监听器就要重写dealloc方法,当控件被销毁时,移除监听器

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
复制代码

1.5 用户一旦输入了文字,就需要将绘制的占位文字删除掉,所以在drawRect:方法一进来就增加判断

// 如果一旦有输入文字,直接返回,不再绘制占位文字
if (self.hasText) return;
复制代码

因为drawRect:方法是每次调用会将上次绘制的内容删除掉,重新绘制,所以增加一个这样的判断,当有文字时,再重绘时直接返回,不进行后面的绘制,这样就可以办到有文字输入后,删除占位文字

但其实还有一些问题,控制器中拿到这个textView控件后,可能会随时修改占位文字的字体大小、占位文字的内容,所以避免这些问题,随时响应修改,就需要重写这些属性的setter方法 1.6 重写字体、占位文字等属性的setter方法

// 占位文字的setter方法
- (void)setPlaceholder:(NSString *)placeholder
{
    _placeholder = placeholder;
    // 文字一旦改变,立马重写绘制(内部会调drawRect:方法)
    [self setNeedsDisplay]; 

}
// 字体属性setter方法
- (void)setFont:(UIFont *)font
{
    [super setFont:font];
    [self setNeedsDisplay];
}
复制代码

这样,这个在系统自带控件基础上自定义的UITextView控件就有占位文字功能了。

2.给自定TextView控件增加一个label属性,用来显示占位文字

这里,相同的部分就不在贴代码,只阐述实现步骤和思路 2.1 在.m文件类扩展增加一个label属性,(写在类扩展中是为了将子控件私有化,不暴漏在外部让被人随意修改属性值)

/**
 *  占位文字Label
 */
@property (weak, nonatomic) UILabel *phLabel;
复制代码

2.2 重写getter方法,采用懒加载,不用关心控件的创建时间,用到时自动加载,同时初始化一些属性

- (UILabel *)phLabel
{
    if (!_phLabel) {
        UILabel *phLabel = [[UILabel alloc] init];
         // 文字自动换行
        phLabel.numberOfLines = 0; 
        phLabel.x = 4;
        phLabel.y = 7;
        [self addSubview:phLabel];
        self.phLabel = phLabel;
    }
    
    return _phLabel;
}
复制代码

2.3 给控件添加监听器,代码同上1.3 2.4 重写dealloc方法,移除监听器,同上1.4 2.5 在- (void)textDidChange方法中控制label的显示或隐藏

- (void)textDidChange
{
    // 有文字就隐藏
    self.phLabel.hidden = self.hasText;
}
复制代码

2.6 在- (void)layoutSubviews方法中动态计算占位文字的label大小

- (void)layoutSubviews
{
    [super layoutSubviews];
    // 确定label的宽度,高度由文字数量自动计算
    CGSize size = CGSizeMake(self.width - 2 * self.phLabel.x, MAXFLOAT);
   // 根据文字的字体属性、文字的数量动态计算label的尺寸
    self.phLabel.size = [self.placeholder boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : self.font} context:nil].size;
}
复制代码
  • 注:label的font要在初始化时确定,不然程序会奔溃

2.7 重写字体、占位文字的setter方法(满足控制器中可能随时修改占位文字的尺寸和大小)

// 占位文字
- (void)setPlaceholder:(NSString *)placeholder
{
    _placeholder = placeholder;
    self.phLabel.text = placeholder;
    
    // 更新文字尺寸
    [self setNeedsLayout];

}
// 字体
- (void)setFont:(UIFont *)font
{
    [super setFont:font];
    self.phLabel.font = font;
    [self setNeedsLayout];
}
复制代码

setNeedsLayout方法会在控件尺寸变化时调用layoutSubviews重新计算子控件的尺寸和大小

这种方式增加的占位文字有一个优势:

占位文字可以跟随光标一起拖拽上下移动(弹簧效果),但利用drawRect:画上去的占位文字是固定的,没有拖拽效果

控制器在拿到这个控件后,设置占位文字后就可以直接用了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值