占位文字
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添加属性,只能使用runtimestatic 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 - 2self.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; }}