iOS之UITextView实现placeHolder占位文字的N种方法

前言

iOS开发中,UITextField和UITextView是最常用的文本接受类和文本展示类的控件。UITextField和UITextView都输入文本,也都可以监听文本的改变。不同的是,UITextField继承自UIControl这个抽象类。UITextView继承自UIScrollView这个实体类。这就导致了UITextView可以多行展示内容,并且还可以像UIScrollView一样滚动。而UITextField只能单独的展示一行内容。从这个角度,UITextView在功能上是优于UITextField的。
但是,众所周知,UITextField中有一个placeholder属性,可以设置UITextField的占位文字,起到提示用户输入相关信息的作用。可是,UITextView就没那么幸运了,apple没有给UITextView提供一个类似于placeholder这样的属性来供开发者使用。而开发中,我们经常会遇到既要占位文字,又要可以多行展示并且可以滚动的控件,单纯的UITextField或者UITextView都不能满足这种产品上的需求。比如,现在市面上的app大多都有一个用户反馈的入口,如下图(一)所示。下面我就把自己能够想到的方法汇总一下,让更多的开发者知道,原来有这么多方法可以实现UITextView的占位文字。

图(一)

方法一

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

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

// 创建textView
UITextView *textView =[[UITextViewalloc]initWithFrame:CGRectMake(,,SCREEN.width-,)];
textView.backgroundColor= [UIColor whiteColor];
textView.text = @"我是placeholder";
textView.textColor = [UIColor grayColor];
textView.delegate = self;
[self.view addSubview:textView]; #pragma mark - UITextViewDelegate
- (void)textViewDidEndEditing:(UITextView *)textView
{
if(textView.text.length < ){
textView.text = @"我是placeholder";
textView.textColor = [UIColor grayColor];
}
}
- (void)textViewDidBeginEditing:(UITextView *)textView
{
if([textView.text isEqualToString:@"我是placeholder"]){
textView.text=@"";
textView.textColor=[UIColor blackColor];
}
}

方法二

1.创建textView
2.给textView添加一个UILabel子控件,作为placeholder
3.在文本改变的代理方法里面显示/隐藏UILabel

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

#import "WSViewController.h"

@interface WSViewController () <UITextViewDelegate>

@property(nonatomic, weak)UITextView *textView;

@property(nonatomic, weak)UILabel *placeHolder;

@end

@implementation WSViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view. [self setupTextView]; } // 添加textView
- (void)setupTextView
{
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(, , SCREEN_WIDTH - * , )];
textView.frame = CGRectMake(, , SCREEN_WIDTH - * , ); [self.view addSubview:textView];
self.textView = textView; textView.contentInset = UIEdgeInsetsMake(-, , , ); textView.delegate = self;
[self setupPlaceHolder]; //在弹出的键盘上面加一个view来放置退出键盘的Done按钮
UIToolbar * topView = [[UIToolbar alloc] initWithFrame:CGRectMake(, , , )];
[topView setBarStyle:UIBarStyleDefault];
UIBarButtonItem * btnSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
UIBarButtonItem * doneButton = [[UIBarButtonItem alloc] initWithTitle:@"完成" style:UIBarButtonItemStyleDone target:self action:@selector(dismissKeyBoard)];
NSArray * buttonsArray = [NSArray arrayWithObjects:btnSpace, doneButton, nil]; [topView setItems:buttonsArray];
[textView setInputAccessoryView:topView]; } // 给textView添加一个UILabel子控件
- (void)setupPlaceHolder
{
UILabel *placeHolder = [[UILabel alloc] initWithFrame:CGRectMake(, -, SCREEN_WIDTH - * , )];
self.placeHolder = placeHolder; placeHolder.text = @"我是placeholder";
placeHolder.textColor = [UIColor lightGrayColor];
placeHolder.numberOfLines = ;
placeHolder.contentMode = UIViewContentModeTop;
[self.textView addSubview:placeHolder];
} #pragma mark - UITextViewDelegate
- (void)textViewDidChange:(UITextView *)textView
{
if (!textView.text.length) {
self.placeHolder.alpha = ;
} else {
self.placeHolder.alpha = ;
}
} //关闭键盘
-(void) dismissKeyBoard{
[self.textView resignFirstResponder];
} @end

同样地思路,我们也可以把作为占位文字的UILabel用UITextField或者UITextView来替换,同样可以实现带placeholder的textView,在次就不在详述。

方法三

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

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

#import <UIKit/UIKit.h>

@interface WSPlaceholderTextView : UITextView
/** 占位文字 */
@property (nonatomic, copy) NSString *placeholder;
/** 占位文字颜色 */
@property (nonatomic, strong) UIColor *placeholderColor;
@end #import "WSPlaceholderTextView.h" @implementation WSPlaceholderTextView - (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
// 设置默认字体
self.font = [UIFont systemFontOfSize:]; // 设置默认颜色
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 = ;
rect.origin.y = ;
rect.size.width -= * 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];
}
@end

方法四

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

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

#import <UIKit/UIKit.h>

@interface WSTextView : UITextView
/** 占位文字 */
@property (nonatomic,copy) NSString *placeholder;
/** 占位文字颜色 */
@property (nonatomic,strong) UIColor *placeholderColor;
@end #import "WSTextView.h" @implementation WSTextView
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.font = [UIFont systemFontOfSize:];
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(, , 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];
}
@end // 应用的时候需要配合UITextView的文本改变的代理方法 #import "ViewController.h"
#import "WSTextView.h" @interface ViewController ()<UITextViewDelegate> // @property(nonatomic,weak) WSTextView *textView; @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
WSTextView *textView = [[WSTextView alloc] initWithFrame:CGRectMake(, , self.view.frame.size.width, )];
textView.placeholder = @"ws";
textView.delegate = self;
[self.view addSubview:textView];
// textView.text = @"试试会不会调用文本改变的代理方法"; // 不会调用文本改变的代理方法
textView.attributedText = [[NSAttributedString alloc] initWithString:@"富文本"]; // self.textView = textView;
} #pragma mark - UITextViewDelegate
- (void)textViewDidChange:(WSTextView *)textView // 此处取巧,把代理方法参数类型直接改成自定义的WSTextView类型,为了可以使用自定义的placeholder属性,省去了通过给控制器WSTextView类型属性这样一步。
{
if (textView.hasText) { // textView.text.length
textView.placeholder = @""; } else {
textView.placeholder = @"ws"; }
}
@end

方法五

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

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

#import "ViewController.h"
#import <objc/runtime.h>
#import <objc/message.h> @interface ViewController () @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];   // 通过运行时,发现UITextView有一个叫做“_placeHolderLabel”的私有变量
unsigned int count = ;
Ivar *ivars = class_copyIvarList([UITextView class], &count); for (int i = ; i < count; i++) {
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
NSString *objcName = [NSString stringWithUTF8String:name];
NSLog(@"%d : %@",i,objcName);
} [self setupTextView]; }
- (void)setupTextView
{
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(, , [UIScreen mainScreen].bounds.size.width, ];
[textView setBackgroundColor:[UIColor greenColor]];
[self.view addSubview:textView]; // _placeholderLabel
UILabel *placeHolderLabel = [[UILabel alloc] init];
placeHolderLabel.text = @"请输入内容";
placeHolderLabel.numberOfLines = ;
placeHolderLabel.textColor = [UIColor lightGrayColor];
[placeHolderLabel sizeToFit];
[textView addSubview:placeHolderLabel]; // same font
textView.font = [UIFont systemFontOfSize:.f];
placeHolderLabel.font = [UIFont systemFontOfSize:.f]; [textView setValue:placeHolderLabel forKey:@"_placeholderLabel"];
} @end

第五个注意:要保持textView.text 和 placeHolderLabel 的font一致 (防止偏移量不对)

 

iOS - UITextView实现placeHolder占位文字的更多相关文章

  1. iOS UITextView placeHolder占位文字的N种方法实现方法

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

  2. ios中怎么样调节占位文字与字体大小在同一高度

    在设置好字体以后,在占位文字中设置leading这个字体属性,用leading来乘以一个比例(CGFloat)来调节位置.

  3. iOS开发中设置UITextField的占位文字的颜色,和光标的颜色

    在iOS开发中,对于很多初学者而言,很有可能碰到需要修改UITextField的占位文字的颜色,以及当UITextField成为第一响应者后光标的颜色,那么下面小编就介绍一下修改占位文字和光标的颜色. ...

  4. UITextView设置占位文字

    这里只介绍一种,这种方便扩展,可以占位文字颜色. 我们继承一个UITextView: #import <UIKit/UIKit.h> @interface MyTextView : UIT ...

  5. AJ学IOS 之微博项目实战(11)发送微博自定义TextView实现带占位文字

    AJ分享,必须精品 一:效果 二:代码: 由于系统自带的UITextField:和UITextView:不能满足我们的需求,所以我们需要自己设计一个. UITextField: 1.文字永远是一行,不 ...

  6. 简易封装一个带有占位文字的TextView

    在实际iOS应用开发中我们经常会用到类似于下图所示的界面,即带有占位文字的文本框:

  7. UITextView实现placeHolder方法汇总

    UITextField中有一个placeholder属性,可以设置UITextField的占位文字,起到提示用户的作用.可是UITextView就没那么幸运了,apple没有给UITextView提供 ...

  8. iOS中用UILabel实现UITextView的占位文字

    @interface BSPublishTextView : UITextView /** 对外属性占位字符 placeholder */ @property (nonatomic, copy) NS ...

  9. iOS开发小技巧--自定义带有占位文字的TextView(两种方式)

    自定义控件注意或框架注意:自己暴露在外面的属性,一定要重写setter,保证外界与内部的交互性 一.方案一:通过drawRect:方法将文字画到textView中,监听文字改变用的是通知中心(代理也可 ...

随机推荐

  1. python3 解决zip解压中文乱码问题,亲测可用, ZipFile

    中文乱码是个很头疼的问题,找了好久都没用找到解决办法 后来也忘了在哪儿找到的解决办法, 很久以前了,但不可行, 解决了思路 在源码里面想要修改内容 if flags & 0x800: # UT ...

  2. SpringBoot系列四:SpringBoot开发(改变环境属性、读取资源文件、Bean 配置、模版渲染、profile 配置)

    声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.概念 SpringBoot 开发深入 2.具体内容 在之前已经基本上了解了整个 SpringBoot 运行机制,但是也需要清 ...

  3. SpringMVC系列(十)<mvc:default-servlet-handler/>(处理静态资源)和<mvc:annotation-driven />

    一.<mvc:default-servlet-handler/>处理静态资源 若将 DispatcherServlet 请求映射配置为 /,则 Spring MVC 将捕获WEB 容器的所 ...

  4. unity提高----------射线使用【unity3d 怎样获得当前鼠标点击的对象】

    unity3d 怎样获得当前鼠标点击的对象 最佳答案   var ray = Camera.main.ScreenPointToRay (Input.mousePosition);var hit : ...

  5. MVC2 ,MVC3 ,MVC4,MVC5的区别,EF的各个版本的区别;LocalDB是个啥

    2010年發行ASP.NET MVC 2.0版,2011年發行ASP.NET MVC 3.0版,2012年發行ASP.NET MVC 4.0版 MVC3 需要.net framework 4.0 版本 ...

  6. [lua, mysql] 将多条记录数据组合成一条sql插入语句(for mysql)

    -- 演示将多条记录数据组合成一条sql插入语句(for mysql) function getTpl0(tname) -- 获取表各个字段 local t = { tpl_pack = {" ...

  7. Java 注解 (Annotation)

    Java 注解用于为 Java 代码提供元数据.作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的.Java 注解是从 Java5 开始添加到 Java 的. 注解语法 ...

  8. MYSQL之You can't specify target table for update in FROM clause解决办法

    mysql中You can't specify target table for update in FROM clause错误的意思是说,不能先select出同一表中的某些值,再update这个表( ...

  9. LNMP架构介绍 MySQL安装 PHP安装 Nginx介绍

  10. eclipse常用插件介绍

    1. 测试覆盖率工具:EclEmma https://www.cnblogs.com/Ming8006/p/5811425.html 2. 单元测试系列:如何使用JUnit+JaCoCo+EclEmm ...