关于设置TextFiled中占位文字的颜色的几个方法

欢迎"@WilliamAlex大叔"一起讨论iOS技术

项目需求: 使用textField时,占位文字默认是黑色的,我们的需求是当开始编辑时,让占位文字和光标变成红色(或其他颜色)

思路: textField和button类似,内部都拥有子控件,在OC机制中,所有控件内部都是以懒加载的形式添加的.我们可以拿到textField中的子控件label,通过监听textField的状态,设置内部子控件label的样式.

  • 方法: 有四中方法:

  • 1, 代理. 2, 通知. 3, target. 4, 是否是第一相应者.

  • 方法一: 使用target方法监听富文本字符串的改变.

// 1, 首先在xib中描述登录界面

// 2, 新建一个继承于UITextField的类,将xib中的所有TextFiled都绑定这个类.
// 方法1: 通过富文本字符串来设置样式

- (void)awakeFromNib
{

    // 1, 设置光标的颜色
    self.tintColor = [UIColor whiteColor];

    // 2, 监听textField的开始编辑状态
    [self addTarget:self action:@selector(textFieldBeginEditing) forControlEvents:UIControlEventEditingDidBegin];

    // 3, 监听textField的结束编辑
    [self addTarget:self action:@selector(textFieldEndEditing) forControlEvents:UIControlEventEditingDidEnd];

    // 4, 描述占位文字属性
    NSMutableDictionary *attr = [NSMutableDictionary dictionary];
    attr[NSForegroundColorAttributeName] = [UIColor darkGrayColor];

    // 5, 富文本属性
    NSAttributedString *attribute = [[NSAttributedString alloc] initWithString:self.placeholder attributes:attr];

    self.attributedPlaceholder = attribute;

}

  • 注意: 当不知道设置某个控件的什么属性时,先去头文件中找,有没有和占位文字相关的属性或者方法.如果实在找不到可以使用runtime,遍历内部的子控件,拿到它对应的控件设置属性值即可.
#pragma mark - 事件监听方法

// 监听开始编辑
- (void)textFieldBeginEditing {

    // 1, 描述占位文字属性
    NSMutableDictionary *attr = [NSMutableDictionary dictionary];
    attr[NSForegroundColorAttributeName] = [UIColor whiteColor];

    // 2, 富文本属性
    NSAttributedString *attribute = [[NSAttributedString alloc] initWithString:self.placeholder attributes:attr];

    self.attributedPlaceholder = attribute;

}

// 监听结束编辑
- (void)textFieldEndEditing {

    // 4, 描述占位文字属性
    NSMutableDictionary *attr = [NSMutableDictionary dictionary];
    attr[NSForegroundColorAttributeName] = [UIColor darkGrayColor];

    // 5, 富文本属性
    NSAttributedString *attribute = [[NSAttributedString alloc] initWithString:self.placeholder attributes:attr];

    self.attributedPlaceholder = attribute;
}

方法二 :

  • 不使用富文本字符串的形式修改占位文字的字体颜色.因为重复代码太多.前面讲过最好是直接给对象控件设置颜色.TextFiled和Button一样,内部之所以能够显示文字,是因为其内部包含有label.所以只要我们,拿到这个占位用的label,就可以直接设置颜色.
  • 主要思路: 方法二的主要思路是利用KVC思想,拿到TextFiled内部中的子控件,在使用KVC之前,用runtime变出TextFiled中所有子控件,找到placeholderLabel即可.
- (void)awakeFromNib
{
    UITextField *textFiled;
    // 1.设置光标
    self.tintColor = [UIColor whiteColor];

    // 监听开始编辑
    [self addTarget:self action:@selector(textBegin) forControlEvents:UIControlEventEditingDidBegin];

    // 监听结束编辑
    [self addTarget:self action:@selector(textEnd) forControlEvents:UIControlEventEditingDidEnd];

    // 根据属性名获取这个属性的值
    UILabel *placeholderLabel = [self valueForKeyPath:@"placeholderLabel"];

    placeholderLabel.textColor = [UIColor lightGrayColor];

}

// 文本框开始编辑
- (void)textBegin
{
    // 修改占位文字样式
    // 根据属性名获取这个属性的值
    UILabel *placeholderLabel = [self valueForKeyPath:@"placeholderLabel"];

    placeholderLabel.textColor = [UIColor whiteColor];
}

// 文本框结束编辑
- (void)textEnd
{
    // 根据属性名获取这个属性的值
    UILabel *placeholderLabel = [self valueForKeyPath:@"placeholderLabel"];

    placeholderLabel.textColor = [UIColor lightGrayColor];
}
  • 注意 : 这样设置相对上一中方法来说就相对比较简洁一点.但是,我们最好是将设置封装到一个分类中,提高代码的复用.

封装代码

- (void)awakeFromNib
{
    // 1.设置光标
    self.tintColor = [UIColor whiteColor];

    // 监听开始编辑
    [self addTarget:self action:@selector(textBegin) forControlEvents:UIControlEventEditingDidBegin];

    // 监听结束编辑
    [self addTarget:self action:@selector(textEnd) forControlEvents:UIControlEventEditingDidEnd];

    // 根据属性名获取这个属性的值
    self.placeholderColor = [UIColor lightGrayColor];
}

// 文本框开始编辑
- (void)textBegin
{
    // 修改占位文字样式
    self.placeholderColor = [UIColor whiteColor];
}

// 文本框结束编辑
- (void)textEnd
{
    self.placeholderColor = [UIColor lightGrayColor];
}

  • 定义一个分类,将设置占位文字颜色的具体实现封装到分类中.
.h文件
// 需要将属性定义在.h文件中,方便外界调用
// 设置占位文字颜色
@property UIColor *placeholderColor;
#import "UITextField+Placeholder.h"
#import 
// runtime:主要目的操作系统的类

// OC系统自带控件中,所有的子控件都是懒加载
@implementation UITextField (Placeholder)

+ (void)load
{
    Method setPlaceholderMethod = class_getInstanceMethod(self, @selector(setPlaceholder:));
     Method wg_setPlaceholderMethod = class_getInstanceMethod(self, @selector(wg_setPlaceholder:));

    // 交互方法
    method_exchangeImplementations(setPlaceholderMethod, wg_setPlaceholderMethod);

}

// 设置占位文字颜色
- (void)setPlaceholderColor:(UIColor *)placeholderColor
{
    // 1.保存占位文字颜色到系统的类,关联
    // object:保存到哪个对象中
    // key:属性名
    // value:属性值
    // policy:策略
    objc_setAssociatedObject(self, @"placeholderColor", placeholderColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    UILabel *placeholderLabel = [self valueForKeyPath:@"placeholderLabel"];
    placeholderLabel.textColor = placeholderColor;
}

- (UIColor *)placeholderColor
{
    return objc_getAssociatedObject(self, @"placeholderColor");
}

- (void)wg_setPlaceholder:(NSString *)placeholder
{
    // 设置占位文字
    [self wg_setPlaceholder:placeholder];

    // 设置占位文字颜色
    self.placeholderColor = self.placeholderColor;
}

  • 知识拓展: 在使用代理时,最好不要让自己成为自己的代理,虽然不会报错,但是不符合代理的原理.所以,建议不要使用这种方法,上面所述的方法.主要是想知道有这么个方法和过程.在实际开发中我会运用的方法如下:

使用Target监听TextField内部控件的私有属性.

  • 首先是使用运行时机制获取某个控件内部的私有属性
// 1, 获取私有属性
    // 运行时获取类中的私有属性
    unsigned int count;
    // 运行时打印出文本框中的所有成员属性
    Ivar *ivarList = class_copyIvarList([UITextField class], &count);
    for (int i =0; i< count; i++) {
        Ivar ivar = ivarList[i];

        NSLog(@"%s",ivar_getName(ivar));
    }
    free(ivarList);

  • 其次将从遍历出来的属性中找到想要修改的属性
// 2, 找到对应的私有属性,将它设置为一个宏
static NSString * const WGPlaceholderLabel = @"placeholderLabel.textColor";

  • 然后监听私有属性
    // 设置占位文字原有颜色
    [self setValue:[UIColor grayColor] forKeyPath:WGPlaceholderLabel];

    // 监听文本框开始编辑
    [self addTarget:self action:@selector(editingBegin) forControlEvents:UIControlEventEditingDidBegin];

    // 监听文本框结束编辑
    [self addTarget:self action:@selector(editingEnd) forControlEvents:UIControlEventEditingDidEnd];

  • 最后实现监听方法
#pragma mark - 监听文本框的编辑状态

- (void)editingBegin{

    [self setValue:[UIColor whiteColor] forKeyPath:WGPlaceholderLabel];
}

- (void)editingEnd {

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

代理(不推荐使用)

  • 这里使用代理需要注意一点:它是自己成为了自己的代理,虽然可以实现项目需求,但是,这和"代理"的本质有冲突.
  • 代理本质: 自己不能做的事,让自己的代理去做
/*
 总结: 本章是用代理的方式监听文本框的编辑状态,然后通过拿到UITextField中的私有的_placeholderLabel属性,给私有属性赋值即可,通过运行时获取查看其内部的所有私有属性,然后通过KVC赋值;
 */

#import "WGLoginRegisterTextFiled.h"
#import 

static NSString * const WGPlaceholderLabel = @"_placeholderLabel.textColor";

@interface WGLoginRegisterTextFiled () 
@end
@implementation WGLoginRegisterTextFiled

- (void)awakeFromNib {

    // 设置光标颜色
    self.tintColor = [UIColor cyanColor];

    // 通过运行时获取TextFiled的内部私有属性
    unsigned int count ;
    Ivar *ivarList = class_copyIvarList([UITextField class], &count);
    // 遍历UITextField的所有属性,ivarList(指针)实质是一个数组

    for (int i = 0; i < count; i++) {

        // 获取每一个属性
        Ivar ivar = ivarList[i];

        NSLog(@"%s",ivar_getName(ivar));  // "%s"原因是运行时是一个C语言的语法

    }

    // 销毁运行时创建的ivarList
    free(ivarList);

    // 通过KVC将属性取出来并赋值
    [self setValue:[UIColor grayColor] forKeyPath:WGPlaceholderLabel];

    // 设置代理
    self.delegate = self; // 自己成为自己的代理,在实际开发中不这样写,这和代理的原理意义上违背了

}

#pragma mark - 设置代理的方法

// 开始编辑
- (void)textFieldDidBeginEditing:(UITextField *)textField {

    // KVC
    [self setValue:[UIColor whiteColor] forKeyPath:WGPlaceholderLabel];

}

// 结束编辑
- (void)textFieldDidEndEditing:(UITextField *)textField {

    [self setValue:[UIColor grayColor] forKeyPath:WGPlaceholderLabel];

}

根据某些控件特有的方法或者属性来设置

  • TextFiled有一个特别的属性方法:即是否成为第一响应者和不当第一响应者.根据这样的特性做一些操作.
/*
 分析:UITextField有一个特有的属性,就是响应者属性,通过重写成为第一响应者和辞去第一响应者来给占位文字设置颜色


 */
#import "WGLoginRegisterTextFiled.h"
#import 

static NSString * const WGPlaceholderLabel = @"_placeholderLabel.textColor";

@interface WGLoginRegisterTextFiled () 
@end
@implementation WGLoginRegisterTextFiled

- (void)awakeFromNib {

    // 设置光标颜色
    self.tintColor = [UIColor cyanColor];

    // 通过KVC将属性取出来并赋值
    [self setValue:[UIColor grayColor] forKeyPath:WGPlaceholderLabel];

    // 调用时刻: 当成为第一响应者 /获取焦点 /弹出键盘
    [self becomeFirstResponder];

    // 调用时刻: 当失去第一响应者 /获取焦点 /弹出键盘
    [self resignFirstResponder];

}

#pragma mark - 设置代理的方法


// 调用时刻: 当成为第一响应者 /获取焦点 /弹出键盘
- (BOOL)becomeFirstResponder {

    [self setValue:[UIColor whiteColor] forKeyPath:WGPlaceholderLabel];

    return [super becomeFirstResponder];
}

// 调用时刻: 当失去第一响应者 /获取焦点 /弹出键盘
- (BOOL)resignFirstResponder {

    [self setValue:[UIColor grayColor] forKeyPath:WGPlaceholderLabel];

   return [super resignFirstResponder];

}
@end

  • 总结:其实还有通知方法的,和代理类似,所以就不写了.本章重点.

  • 1, 学会编程思想,不要局限于某一种方式开发

  • 2, 学会利用某些特有的方法或者属性来解决问题

  • 3, 如果有时间可以深入学习运行时机制(特别有意思)

  • 知识拓展: 在使用代理时,最好不要让自己成为自己的代理,虽然不会报错,但是不符合代理的原理.所以,建议不要使用这种方法

  • 注意: 本章使用的target监听textFiled的编辑状态,但是这是不符合代理的原理,在实际开发中"尽量不要自己成为自己的代理".我的建议是使用textFiled的特性,即"第一响应者"来监听textFiled的编辑状态.

你可能感兴趣的:(关于设置TextFiled中占位文字的颜色的几个方法)