简易总结封装一个带有占位文字的UITextField/TextView

占位文字

  • 1、曾经有个这么一个项目需求: 使用textField时,占位文字默认是黑色的,我们的需求是当开始编辑时,让占位文字和光标变成红色(或其他颜色)
    • 思路: textField和button类似,内部都拥有子控件,在OC机制中,所有控件内部都是以懒加载的形式添加的.我们可以拿到textField中的子控件label,通过监听textField的状态,设置内部子控件label的样式.

  • 2、当系统自带的控件满足不了项目要求时
    • 对于 UITextField :

    1> 有占位文字 2> 最多只能输入一行文字

    • 对于UITextView

    1> 没有占位文字 2> 能输入任意行文字,并且超出显示范围的可以滚动(UITextView继承自UIScrollView)

    • 而我们需要的结果 :

1> 有占位文字 2> 能输入任意行文字
那么我们可以用脚想一想便可选择自定义控件继承于UITextField /UITextView,实现添加其占位文字的功能。


  • 3、以后开发中可能经常会使用到这种技术,所以我现在就总结一下如何弄占位文字以及修改占位文字的颜色,方便以后开发(只提供了一种思路,其他方法思路自己可以总结)。
    • 注意 : 以下方法我们最好是将它封装到一个分类中,提高代码的复用和封装性.

以下是封装的代码:

一、UITextField

1.第一种方法:UITextField利用RunTime来设置占位文字的颜色

主要思路: 方法二的主要思路是利用KVC思想,拿到TextFiled内部中的子控件,在使用KVC之前,用runtime变出TextFiled中所有子控件,找到placeholderLabel即可.

########################### .h文件 ###########################
//  UITextField+LYMPlaceholderColor.m
//  Created by ming on 14/12/20.
//  Copyright © 2014年 ming. All rights reserved.

#import <UIKit/UIKit.h>
@interface UITextField (LYMPlaceholderColor)
/** 占位颜色 */
@property (nonatomic,strong) UIColor *placeholderColor;
/** 名字 */
@property (nonatomic,copy) NSString *name;
@end

########################### .m文件 ###########################
//  UITextField+LYMPlaceholderColor.m
//  Created by ming on 14/12/20.
//  Copyright © 2014年 ming. All rights reserved.
/**
 *  利用RunTime来设置占位文字的颜色
 */


#import "UITextField+LYMPlaceholderColor.h"
// 导入头文件,导入下面其中一个即可
#import <objc/runtime.h>
//#import <objc/message.h>

// OC最喜欢懒加载,用的的时候才会去加载
// 需要给系统UITextField添加属性,只能使用runtime

static NSString *const LYMPlaceholderLabelKey = @"placeholderLabel";
static NSString *const placeholderColorName = @"placeholderColor";
@implementation UITextField (LYMPlaceholderColor)

#pragma mark - 利用RunTime动态增加属性和交换方法 =
/// 实现交换方法 (mg_setPlaceholder:和setPlaceholder:的互换)
+ (void)load{
    Method mg_setPlaceholder = class_getInstanceMethod(self, @selector(mg_setPlaceholder:));
    
    Method setPlaceholder = class_getInstanceMethod(self, @selector(setPlaceholder:));
    
    method_exchangeImplementations(setPlaceholder, mg_setPlaceholder);
}

/// 外界赋值占位颜色的时候就会调用
- (void)setPlaceholderColor:(UIColor *)placeholderColor{
    // 动态增加placeholderColor属性
    objc_setAssociatedObject(self, (__bridge const void *)(placeholderColorName), placeholderColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    // 设置颜色
   UILabel *placeholderLabel = [self valueForKeyPath:LYMPlaceholderLabelKey];
    placeholderLabel.textColor = placeholderColor;
}
- (UIColor *)placeholderColor{
    //    return  _placeholderColor;
    return objc_getAssociatedObject(self,(__bridge const void *)(placeholderColorName));
}

/// 外界赋值占位文字的时候会调用(自定义的方法,用来和系统的方法交换)
- (void)mg_setPlaceholder:(NSString *)placeholder{
    // 1.设置占位文字
    [self mg_setPlaceholder:placeholder];
   
    // 2.设置占位文字颜色
    self.placeholderColor = self.placeholderColor;
}


#pragma mark - 测试RunTime动态增加属性
- (void)setName:(NSString *)name{
    // 动态增加“name”属性
    objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name{
    return objc_getAssociatedObject(self, @"name");
}

@end

总结:

1, 文本框和按钮一样,都可以编辑文字,所以内部是有label的,所以需要拿到文本框中的label(可以在"小面包中检测"),当输入文字后,有个label就会消失,那个就是占位label,所以需要拿到系统内部的私有属性,但是不能直接拿到私有的属性和方法,所以需要用到KVC去取值和赋值.通过"运行时"拿到属性
2, 然后通过KVC取值
容易出错点: 不要用setValu:forKey,程序会崩掉,要用forKeyPath:表示不管你在文件的那一层都能去拿到


2.第二种方法:UITextField 设置占位文字的颜色,让外界直接使用(这种方法有点投机取巧)

--这样设置相对上一中方法来说就相对比较简洁

########################### .h文件 ###########################

#import <UIKit/UIKit.h>

@interface UITextField (placeholderColor)

/** 占位颜色 */
@property (nonatomic,strong) UIColor *placeholderColor;

@end

########################### .m文件 ###########################
/**
 *  设置占位文字的颜色,让外界直接使用
 */

#import "UITextField+placeholderColor.h"

static NSString *const placeholderColorKey = @"placeholderLabel.textColor";

@implementation UITextField (placeholderColor)
// 重写placeholderColor的setter方法
- (void)setPlaceholderColor:(UIColor *)placeholderColor{
    // bool属性,有文字就这设置为YES
    BOOL change = NO;
    // 如果当前placeholder文字为空,那么就随便赋值几个文字,让它不为空
    if (self.placeholder == nil) {
        self.placeholder = @"mingge";
        // 设置 change = YES
        change = YES;
    }
    [self setValue:placeholderColor forKey:placeholderColorKey];
    // 如果change = YES,那么要把placeholder文字再次设为空
    if (change) {
        self.placeholder = nil;
    }
}

// 重写placeholderColor的getter方法
- (UIColor *)placeholderColor{
    return [self valueForKeyPath:placeholderColorKey];
}

@end

二、UITextView

1.第一种方法:UITextView利用

Quartz2D绘图 来设置占位文字以及颜色
// 重绘
[self setNeedsDisplay];

########################### .h文件 ###########################
//  LYMTextView.h
//  Created by ming on  14/12/9.
//  Copyright © 2014年 ming. All rights reserved.

#import <UIKit/UIKit.h>

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

########################### .m文件 ###########################
/**
 *   给UITTextView显示占位文字的功能,以后如需使用,就可以直接拿去用
 */
#import "LYMTextView.h"

@implementation LYMTextView

#pragma mark ========== 通知 =============
- (instancetype)initWithFrame:(CGRect)frame{
    if (self = [super initWithFrame:frame]){
        self.textColor = [UIColor blackColor];
        
        // 设置占位文字的默认字体颜色和字体大小
        self.placeholderColor = [UIColor grayColor];
        self.font = [UIFont systemFontOfSize:15];
        
        // 发布通知(当空间的内容发生改变时)
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChange:) name:UITextViewTextDidChangeNotification object:self];
    }
    return self;
}

- (void)textDidChange:(NSNotification *)note{
    // 重绘
    [self setNeedsDisplay];
}

// 移除监听者
- (void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

#pragma mark ========== 绘制占位文字 ===========
// 绘制占位文字
- (void)drawRect:(CGRect)rect {
    // 如果有文字就不绘制(不执行下面的操作)
    if (self.text.length) return;
    
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
//    if (self.font) dict[NSFontAttributeName] = self.font;
//    if (self.placeholderColor)  dict[NSForegroundColorAttributeName] = self.placeholderColor;
     dict[NSFontAttributeName] = self.font;
     dict[NSForegroundColorAttributeName] = self.placeholderColor;

    
    rect.origin.x = 4;
    rect.origin.y = 8;
    rect.size.width = LYMScreenWidth - 2 * rect.origin.x;
    
    [self.placeholder drawInRect:rect withAttributes:dict];
}

#pragma mark ========== 需要重写的属性 ===========
// 重写占位文字
- (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];
}

// textView的尺寸发生改变
- (void)layoutSubviews{
    [super layoutSubviews];
    // 重绘
    [self setNeedsDisplay];
}

@end

2.第二种方法:UITextView利用

刷新 来设置占位文字以及颜色
// 重新布局
[self setNeedsLayout];

########################### .h文件 ###########################
//  LYMTextViewWithLabel.h
//  Created by ming on 14/12/9.
//  Copyright © 2014年 ming. All rights reserved.

#import <UIKit/UIKit.h>

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

########################### .m文件 ###########################
#import "LYMTextViewWithLabel.h"

@interface LYMTextViewWithLabel ()
/** 占位Label */
@property (nonatomic,weak)  UILabel *placeholderLabel;
@end

@implementation LYMTextViewWithLabel

#pragma mark ========== 通知 =============
- (instancetype)initWithFrame:(CGRect)frame{
    if (self = [super initWithFrame:frame]){
        // 创建一个UILabel
        UILabel *placeholderLabel = [[UILabel alloc] init];
        placeholderLabel.numberOfLines = 0;
        [self addSubview:placeholderLabel];
        self.placeholderLabel = placeholderLabel;
        
        // 设置占位文字的默认字体颜色和字体大小
        self.textColor = [UIColor blackColor];
        self.font = [UIFont systemFontOfSize:15];
        
        // 发布通知(当空间的内容发生改变时)
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChange:) name:UITextViewTextDidChangeNotification object:self];
    }
    return self;
}

// 布局占位文字的位置和尺寸
- (void)layoutSubviews{
    [super layoutSubviews];
    self.placeholderLabel.x = 4;
    self.placeholderLabel.y = 8;
    self.placeholderLabel.width = self.width - 2*self.placeholderLabel.x;
    // 自适应
    [self.placeholderLabel sizeToFit];
}

- (void)textDidChange:(NSNotification *)note{
    // 是否隐藏
    self.placeholderLabel.hidden = self.hasText;
}

// 移除监听者
- (void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}


- (void)setPlaceholderColor:(UIColor *)placeholderColor{
    self.placeholderLabel.textColor = placeholderColor;
}

#pragma mark ========== 重新计算placeholderLabel的尺寸 =============
- (void)setFont:(UIFont *)font{
    [super setFont:font];
    self.placeholderLabel.font = self.font;
    // 重新布局
    [self setNeedsLayout];
}

- (void)setPlaceholder:(NSString *)placeholder{
    self.placeholderLabel.text = [placeholder copy];
    // 重新布局
    [self setNeedsLayout];
}

#pragma mark ========== 隐藏placeholderLabel =============
- (void)setText:(NSString *)text{
    [super setText:text];
    // 根据是否有文字来判断要不要隐藏
    self.placeholderLabel.hidden = self.hasText;
}

- (void)setAttributedText:(NSAttributedString *)attributedText{
    [super setAttributedText:attributedText];
    // 根据是否有文字来判断要不要隐藏
    self.placeholderLabel.hidden = self.hasText;
}

@end

3.runTime

  • 头文件
#import <objc/runtime.h>
#import <objc/message.h>```

- viewDidLoad

  • (void)viewDidLoad {
    [super 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);
    }
    [self setupTextView];
    }```

  • setupTextView

- (void)setupTextView{
        // 提示文字
        CGFloat margin  = 15;
        
        UILabel *tipLabel = [[UILabel alloc] initWithFrame:CGRectMake(margin, 0, MGSCREEN_width - 2 * margin, 50)];
        tipLabel.text = @"你的批评和建议能帮助我们更好的完善产品,请留下你的宝贵意见!";
        tipLabel.numberOfLines = 2;
        tipLabel.textColor = MGRGBColor(255, 10, 10);
        tipLabel.font = MGFont(16);
        [self.view addSubview:tipLabel];
        // 意见输入框
        CGFloat height  = 200;
#ifndef __IPHONE_4_0
        height = 100;
#endif
        UITextView *iderTextView = [[UITextView alloc] initWithFrame:CGRectMake(margin, CGRectGetMaxY(tipLabel.frame) + margin, MGSCREEN_width - 2 * margin, height)];
        iderTextView.backgroundColor = [UIColor whiteColor];
        iderTextView.scrollEnabled = YES;
        iderTextView.scrollsToTop = YES;
        iderTextView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
//    iderTextView.delegate = self;
        self.iderTextView = iderTextView;
        [self.view addSubview:iderTextView];
    
        // _placeholderLabel
        UILabel *placeHolderLabel = [[UILabel alloc] init];
        placeHolderLabel.text = @"请输入宝贵意见(300字以内)";
        placeHolderLabel.numberOfLines = 0;
        placeHolderLabel.font = [UIFont systemFontOfSize:14];
        placeHolderLabel.textColor = [UIColor lightGrayColor];
        [placeHolderLabel sizeToFit];
        [iderTextView addSubview:placeHolderLabel];
    
        [iderTextView setValue:placeHolderLabel forKey:@"_placeholderLabel"];
        iderTextView.font =  placeHolderLabel.font;
}

补充:一种最简单的实现占位文字的textView(该方法有点投机取巧)

  • viewDidLoad
- (void)viewDidLoad {
    [super viewDidLoad];
    //    意见输入
    myTextView=[[UITextView alloc] initWithFrame:(CGRectMake(10, 33, kScreenWidth-20, 130))];
    myTextView.font=kFont(15);
    myTextView.delegate=self;
    myTextView.backgroundColor=self.view.backgroundColor;
    ViewBorderRadius(myTextView, 0, 0.5f, kGray);
    myTextView.textColor=kGray;
    myTextView.text=@" 请输入详细描述";
    myTextView.tintColor=kWhite;
    [self.view addSubview:myTextView];
    //    提交
    UIButton *feedBtn=[UIButton buttonWithType:(UIButtonTypeCustom)];
    feedBtn.frame=CGRectMake(100, myTextView.tail+40, kScreenWidth-200, 40);
    [feedBtn addTarget:self action:@selector(feedBtnHandled:) forControlEvents:(UIControlEventTouchUpInside)];
    [feedBtn setTitle:@"提交" forState:(UIControlStateNormal)];
    [feedBtn setTitleColor:[UIColor whiteColor] forState:(UIControlStateNormal)];
    feedBtn.titleLabel.font=kFont(18);
    feedBtn.backgroundColor=naBarTiniColor;
    [self.view addSubview:feedBtn];

}```

- UITextViewDelegate

  • (void)textViewDidBeginEditing:(UITextView *)textView{
    if([textView.text isEqualToString:@" 请输入详细描述"]){
    textView.text=@"";
    textView.textColor=kWhite;
    }
    }

  • (void)textViewDidEndEditing:(UITextView *)textView{

    if([textView.text isEmptyString]){

      textView.text=@" 请输入详细描述";
      textView.textColor=kGray;
    

    }
    }

![未输入时](http://upload-images.jianshu.io/upload_images/1429890-73a3f245a9ced10a.PNG)
![UITextView成为第一响应者时](http://upload-images.jianshu.io/upload_images/1429890-10b06997fb7af974.PNG)
***
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容