标题显得很屌丝,搞那么长、长就长一点吧,这样可能方便搜索。
一、先看一张图
神气的 iOS 分类
是的、就这三个分类:NSString+Regex,UITextField+HG,UITextView+HG。接下来就对他们做一个简单的介绍。
二、如何获取
在之前、已经放到 pod 上。所以获取方式有两种:
1、直接到这里Categorys去查看。
2、通过 pod 方式拉取:
pod 'HGCategorys'
温馨提示:一般 Podfile 应该如何去创建?很多同行是这样的命令:vim Podfile,其实最专业&方便的创建姿势是这样的:pod init 。
看了代码之后会发现实现是很简单的,不仅代码简单、功能也很简单。存在、就是真理。再简单、也一起看一看有何用途吧。
三、有何功能
1、NSString+Regex
先看分类名 Regex,正则表达式的意思。那么这部分的功能就很明确了,主要就是一个字符串的匹配。在开发的过程中对 正则表达式 的使用几乎是不难的,但是如何将不同的功能整合在一起,这恐怕是一个问题。接下来看一看这里面到底是如何实现的(只看.h 文件中的内容)。
1.1 枚举定义
具体定义如下:
// 基本匹配 : 数字, 汉字, 字符, 空格, 下划线, 点, @ typedef NS_OPTIONS(NSUInteger, HGRegexType) { HGRegexTypeNone = 0 << 0, // 未知 HGRegexTypeDigital = 1 << 0, // 数字 HGRegexTypeChinese = 1 << 1, // 汉字 HGRegexTypeCharacter = 1 << 2, // 字符 HGRegexTypeSpace = 1 << 3, // 空格 HGRegexTypeUnderline = 1 << 4, // 下划线 HGRegexTypeDot = 1 << 5, // 点 HGRegexTypeAT = 1 << 6, // @ };
关于这个枚举的定义的一大亮点就是 NS_OPTIONS,千万不要看成 NS_ENUM,这两种枚举在用法上是有很大区别的。相比较而言 NS_OPTIONS 的功能比 NS_ENUM 还要强大。这里并不说我现在使用了 NS_OPTIONS 就吹嘘它强大,而显得自己有多牛逼。不是这样的、而是想说,对于OC 中的枚举一定要弄清楚这两种的不同。在适当的时机使用适当的实现方式,才能让自己的代码更加合理、更加牛气。
其实苹果给我们提供了很多这样的枚举,比如 UIViewAutoresizing、NSKeyValueObservingOptions 等等。
看到这里,应该能猜测到 HGRegexType 的用途是什么了。我不说、你不说,心里知道就可以了。[偷笑 5 秒钟、思考一小时]
1.2 提供的方法
在上代码之前,先自我批评一下:所有的方法注释,没有对参数做介绍。这是在写代码过程中的大忌,在 .m 文件中可以不用、但是在 .h 文件中不做注释,这肯定是不对的。由于代码是3个月前上传的,也不想去更新了。
直接看代码:
/** * 通过 pattern 进行匹配 */ - (BOOL)hg_regexMatchWithPattern:(NSString*)pattern; /** * 基本匹配: 是否支持空字符串返回为YES的情况 */ - (BOOL)hg_regexMatchWithType:(HGRegexType)rType returnWhenEmpty:(BOOL)empty;
看过代码的你应该知道,实际提供的方法不止以上的两个。但是没有关系,只要明白了这两个方法,其它方法看到就秒懂了。接下来细说一下这两个方法。
/** * 通过 pattern 进行匹配 */ - (BOOL)hg_regexMatchWithPattern:(NSString*)pattern;
当你看到这个方法,根据你多年的开发经验,再加上我那么屌丝的注释,你可能会这样的去用:
// 匹配的条件 NSString* pattern = @"."; // 即将匹配的字符串 NSString* strTEXT = @"CoderHG."; // 匹配结果 BOOL result = [hg_regexMatchWithPattern:pattern]; // 打印 if (result) { NSLog(@"有点"); } else { NSLog(@"没点"); }
毫无保留的认为 strTEXT 中有一个点(.),所以结果 result 的值是 YES,打印的是 有点。其实不是这样,这里的结果是 NO。我的这个方法的意思是要 完全匹配 才算 YES,因为我之前设计这个匹配功能的初衷是:一个字符串中只能只包含 pattern 中的字符,如果多了,就算失败。
所以、以上如果想要让 strTEXT 完全匹配成功,只能是将 pattern 改成这样 ^[a-zA-Z.]+$,意思是 strTEXT 中只能出现字符与点(.)的情况下才能成功,多了,都是失败。显然 strTEXT 中除了点(.)还有其它,所以为 NO。
这样的功能,一般用于什么样的地方呢?比较常用的地方就是,密码验证。往往会有这样的要求:有且只能用字符、数字与下划线。所以就可以弄弄成这样的:
// 匹配的条件 NSString* pattern = @"^[//da-zA-Z_]+$"; // 即将匹配的字符串 NSString* strTEXT = @"CoderHG123"; // 匹配结果 BOOL result = [strTEXT hg_regexMatchWithPattern:pattern]; // 打印 if (result) { NSLog(@"%@ 中只包含数字,字符与下划线", strTEXT); } else { NSLog(@"%@ 中除了包含数字,字符与下划线,还有其它字符", strTEXT); }
如果 strTEXT 中还包含了 数字,字符与下划线 之外的字符,那么就返回 NO。
那么问题又来了,每次都要去写 pattern 这样的正则表达式,还麻烦啊。是的,的确是很麻烦的。接下来看另外一个方法。
/** * 基本匹配: 是否支持空字符串返回为YES的情况 */ - (BOOL)hg_regexMatchWithType:(HGRegexType)rType returnWhenEmpty:(BOOL)empty;
这个方法中,终于使用上那个枚举(HGRegexType)了,看了上的所有解释,应该都知道这个 rType 如何传值了。但是后面的 empty 是怎么回事么?主要是为了处理给空字符串(@"")而准备的。如果是空字符串(@"")的话,是算匹配成功还是匹配失败呢。主要还是要看心情,不对、主要还是要看需求。 empty 传什么,在如果是空字符串(@"")的时候就返回什么。
那么我们就可以来用一下了,如果一个需求是这样的:一个字符串只能包含字母、数字与下划线,并且如果当前字符串为空(@"")的时候,返回为 YES。那么就可以这样弄了:
// 即将匹配的字符串 NSString* strTEXT = @"CoderHG123"; // 匹配的条件 数字 | 字母 | 下划线 HGRegexType rType = HGRegexTypeDigital | HGRegexTypeCharacter | HGRegexTypeUnderline; // 匹配结果 BOOL result = [strTEXT hg_regexMatchWithType:rType returnWhenEmpty:YES]; // 打印 if (result) { NSLog(@"%@ 中只包含数字,字符与下划线", strTEXT); } else { NSLog(@"%@ 中除了包含数字,字符与下划线,还有其它字符", strTEXT); }
这样看起来,就清晰了很多。到现在为止,NSString+Regex 部分就介绍结束了,希望能对你有所帮助。
欲知更多精彩,请看下面分解。
2、UITextField+HG
这部分不做介绍,功能与 UITextView+HG 类似,直接看 UITextView+HG 即可。
3、UITextView+HG
其实对于文本框的输入,是有很多的话要说的,但是接下来要介绍的仅仅是其中的冰山一角。当遇到输入文本时,还需要有文字限制的时候,应该如何去处理。比如现在的微信(现在的版本号是6.6.5)的个性签名只能输入30个字符,多一个就不行。像我的个性签名是这样的:
Later equals never.
一看我的个性签名,我感觉确实很有个性的。微信只让最多输入30个字符,我就不多也不少的弄30个。其中 升值 的意思是: 努力提升自己的价值。
但是如果我现在想将 感谢自己! 换成感谢所有人,那么是修改不成功的,因为微信根本就输入不进去了。这说明什么呢??[郁闷3秒钟、思考几十年]
不能感谢所有人、只能感谢自己!
现在总结一下微信个性签名的文字限制的一个特点:在输入还处于高亮的时候,就已经开始计算字符。导致在最后无法输入想要输入的结果。
如果说一定要感谢所有人, 应该这么处理呢?目的就是要打破上面的那一个特点:在输入的过程中,高亮的部分不参与字符计算。于是 UITextView+HG 就诞生了。
关于这个 分类,实现是很简单的。如果要完全的实现 在输入的过程中,高亮的部分不参与字符计算。还需要其它的处理。现在先来看一下这个分类都做了什么处理。主要就一个属性与一个方法:
/** 是否高亮 */ @property (nonatomic, readonly) BOOL hg_isHighLighted; /** 输入无效,将已经数据的打回原形 @param curContent 希望当前的显示内容 */ - (void)hg_invalidTextFieldCurContent:(NSString*)curContent;
hg_isHighLighted 诸葛属性主要是用来判断当前的输入框是否在处于高亮的状态。
重点是下面的方法:
注释中的 输入无效,将已经数据的打回原形,当输入超出限制的时候,将高亮的部分去掉去掉,回到高亮之前的状态。这个方法中不仅处理了在文本的最后输入的情况,主要是处理了在文本的中间部分输入的情况下。光标不会跑到最后,而是在高亮的上一个位置。听起来很高大上,同时也很模糊。可以到 HGCategorysDemo 目录中找到具体的项目代码,这个代码最好是:pod update 一下。
具体的功能是在 SetupSignatureCell 中实现的,发现这里面的代码不少,主要是一些代理与数字的控制。与之相关的代码,在这里:
#pragma mark - #pragma mark - UITextViewDelegate - (void)textViewDidChange:(UITextView *)textView { // 必须要在这里弄. self.placeholderLabel.hidden = (textView.text.length > 0);; // 如果在变化中是高亮部分在变,就不要计算字符了 if (textView.hg_isHighLighted) { return ; } // 主要是处理当输入超出限制时的优化 if (textView.text.length > self.maxCount) { [textView hg_invalidTextFieldCurContent:_signatureTEXT]; } // 代理 if (self.delegate && [self.delegate respondsToSelector:@selector(setupSignatureCell:didChangedValue:)]) { [self.delegate setupSignatureCell:self didChangedValue:textView.text]; } // 保留 // 不能这么调用 // self.signatureTEXT = textView.text; // 正确的姿势是这样的 _signatureTEXT = textView.text; _countLabel.text = [NSString stringWithFormat:@"%zd/%zd", self.signatureTEXT.length, (self.maxCount - self.signatureTEXT.length)]; }
是的,没有错,仅仅是在一个地方用到。其中,值得注意的是 signatureTEXT 这个属性是必不可少的。如果没有,那么光标的问题很难处理。现在感兴趣的话,就可以慢慢的看这个SetupSignatureCell 中实现。到此为止,UITextView+HG 的介绍以及一个个性签名的实现基本上结束了。
4、说点题外话
对于 UITextField ,大家尽量不要去使用通知来控制各种状态,最好是使用代理与事件。别忘了UITextField是继承于 UIControl 的。在 UITextView 的delegate 中有 textViewDidChange: 这样的代理方法,但是在 UITextField 中却是没有的,我们可以通过UIControlEventEditingChanged来添加事件。如:
[textField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged]; // 文本变化 - (void)textFieldDidChange:(UITextField *)textField { // TODO: }
四、说在最后的话
本来打算上午就弄完的,没想到搞到了现在。感谢阅读的你、也感谢自己!
以上的功能,不值一提,都是冰山一角。不积跬步无以至千里,我们一步一步的来。
如果由于刚刚时间仓促,忘记了下拉代码,那么可以直接点击这里点击这里点击这里。关于个性签名的代码,记得到 HGCategorysDemo 目录的查看。
要是有什么不对的、或者需要补充的,感谢评论讨论!
我的更多文章,可以直接看这里NewStart NewStart NewStart
谢谢大家!
你说得很对,但是没有意义。-- 来自 《职来职往》
作者:CoderHG
链接:https://www.jianshu.com/p/d3a3242f50e6