这是我参与更文挑战的第3天,活动详情查看: 更文挑战
为了让用户在输入密码、验证码时辨识度更高,一般都会自定义输入框。WLUnitField 实现了一个类似 UITextField 的输入框。
XIB 的支持
WLUnitField 支持了 XIB 实时渲染,可以在 XIB 上直接设置属性,然后查看效果,大大降低了学习使用的难道。
为了用来处理 IB 中不支持(自定义枚举)的类型,和修饰符不统一的问题(是否为 readonly 属性),作者使用了预处理宏 TARGET_INTERFACE_BUILDER
:
#if TARGET_INTERFACE_BUILDER
// 只在 IB 中执行的代码
#else
// App 运行过程中的代码
#endif
复制代码
部分代码如下:
IB_DESIGNABLE
@interface WLUnitField : UIControl <UITextInput>
#if TARGET_INTERFACE_BUILDER
@property (nonatomic, assign) IBInspectable NSUInteger inputUnitCount;
@property (nonatomic, assign) IBInspectable NSUInteger style;
#else
@property (nonatomic, assign, readonly) NSUInteger inputUnitCount;
@property (nonatomic, assign, readonly) WLUnitFieldStyle style;
#endif
@end
复制代码
其中 style 在代码中使用的是 WLUnitFieldStyle 类型,IB 中只能使用基础类型。
NSNotificationName
以前定义通知时一直都使用的 NSString *
,但是有更合理的类型 NSNotificationName
。
#ifdef NSFoundationVersionNumber_iOS_9_x_Max
NSNotificationName const WLUnitFieldDidBecomeFirstResponderNotification = @"WLUnitFieldDidBecomeFirstResponderNotification";
NSNotificationName const WLUnitFieldDidResignFirstResponderNotification = @"WLUnitFieldDidResignFirstResponderNotification";
#else
NSString *const WLUnitFieldDidBecomeFirstResponderNotification = @"WLUnitFieldDidBecomeFirstResponderNotification";
NSString *const WLUnitFieldDidResignFirstResponderNotification = @"WLUnitFieldDidResignFirstResponderNotification";
#endif
复制代码
其实 NSNotificationName
就是 NSString *
的别名:
typedef NSString *NSNotificationName NS_EXTENSIBLE_STRING_ENUM;
复制代码
NSString 的编码、循环
WLUnitField 的作者在对一个字符串进行循环时,使用了 enumerateSubstringsInRange:options:usingBlock
方法:
[text enumerateSubstringsInRange:NSMakeRange(0, text.length)
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
if (self.characterArray.count < self.inputUnitCount)
[self.characterArray addObject:substring];
else
*stop = YES;
}];
复制代码
通过搜索看到了这篇文章:NSString 与 Unicode。里面详细的介绍了 Unicode 的发展历程,标准等价(canonically equivalent)和 相容等价(compatibility equivalence)等定义,以及 NSString 的编码方式,我们在处理字符串长度、循环字符串时的注意事项和技巧。
以前总是调用
length
和characterAtIndex:
,在处理 emoji 时就会出错。有些代码在写错了几百遍、几千遍之后,才知道什么是正确的方式。
想起之前有个需求:一个汉字为两个字符长度。实现方式如下:
extension String {
/// 获取 GB 编码下的字符长度。一个汉字 = 两个字符长度。
/// - Returns: 字符长度
func GBCharacterCount() -> Int {
let GBEncoding = CFStringConvertEncodingToNSStringEncoding(UInt32(CFStringEncodings.GB_18030_2000.rawValue))
return self.data(using: String.Encoding(rawValue: GBEncoding))?.count ?? 0
}
}
复制代码