最项目有一个提现的功能,像支付宝、微信输入支付密码样式的文本框,这样的轮子已经很多了,用起来都不太适合自己,所以决定自己去重复造个,大多数的实现方式都用一个View 又放上一个UITextField,只是这个TextField 看不到,不太喜欢这种实现方式那有没有更好的方式呢?那就本文的重点想说的UIKeyInput, 这个protocol 很简单只有三个方法
@protocol UIKeyInput <UITextInputTraits>
- (BOOL)hasText;// A Boolean value that indicates whether the text-entry objects has any text. (required)YES
//if the backing store has textual content, NO otherwise. 官方文档的解释我也翻译不好
- (void)insertText:(NSString *)text;// 插入文本
- (void)deleteBackward; // 键盘上的退格事件
@end
实现了这个协议我们就可以实现一个简单文本输入视图了,先看看实现的最终效果:
代码很简单实现的这个三个协议,就可以接受键盘的一些输入了,还有一个协议我们也需要看下就是我们在使用textField 经常用的一些东西,那就是 :UITextInputTraits 看起很陌生,我们点进去看看源码:
@protocol UITextInputTraits <NSObject>
@optional
@property(nonatomic) UITextAutocapitalizationType autocapitalizationType; // default is UITextAutocapitalizationTypeSentences
@property(nonatomic) UITextAutocorrectionType autocorrectionType; // default is UITextAutocorrectionTypeDefault
@property(nonatomic) UITextSpellCheckingType spellCheckingType NS_AVAILABLE_IOS(5_0); // default is UITextSpellCheckingTypeDefault;
@property(nonatomic) UIKeyboardType keyboardType; // default is UIKeyboardTypeDefault
@property(nonatomic) UIKeyboardAppearance keyboardAppearance; // default is UIKeyboardAppearanceDefault
@property(nonatomic) UIReturnKeyType returnKeyType; // default is UIReturnKeyDefault (See note under UIReturnKeyType enum)
@property(nonatomic) BOOL enablesReturnKeyAutomatically; // default is NO (when YES, will automatically disable return key when text widget has zero-length contents, and will automatically enable when text widget has non-zero-length contents)
@property(nonatomic,getter=isSecureTextEntry) BOOL secureTextEntry; // default is NO
@end
这不是我们常用的UITextField一些属性吗,我们可以实现这个协议来实现一些自定义的东西。下面我们来实现支付密码样式的文本框代码很简单就不多说了直接看:
#import <UIKit/UIKit.h>
@interface SBPasswordTextField : UIView <UIKeyInput,UITextInputTraits>
@property (nonatomic, copy, readonly)NSString *text;
@property (nonatomic, strong) UIColor *septaLineColor; // defaut black
@property (nonatomic, assign) CGFloat septaLineWidth; //default 1.0px
@property (nonatomic, strong) UIColor *dotFillColor; //default black
@property (nonatomic, assign) CGFloat dotRadius; //default 5.0
@property (nonatomic, strong) UIColor *textColor; //default black
@property (nonatomic, strong) UIFont *font; //default 14;
@property (nonatomic, assign) NSUInteger passwordLength; //default 6;
#pragma mark- UITextInputTraits
@property(nonatomic) UIKeyboardType keyboardType;
@property(nonatomic) UIReturnKeyType returnKeyType;
@property(nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry;
m.文件
#import "SBPasswordTextField.h"
@interface SBPasswordTextField ()
@property (nonatomic, strong)NSMutableString * innerText;
@property (nonatomic, strong)NSMutableArray *dotsLayers;
@property (nonatomic, strong)NSMutableArray *subTextLayers;
@property (nonatomic, assign)BOOL didLayoutSubview;
@end
@implementation SBPasswordTextField
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self setup];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
- (void)setup {
_innerText = [NSMutableString new];
_dotsLayers = [NSMutableArray arrayWithCapacity:6];
_subTextLayers = [NSMutableArray arrayWithCapacity:6];
_dotRadius = 5.f;
_dotFillColor = [UIColor blackColor];
_font = [UIFont systemFontOfSize:14];
_textColor = [UIColor blackColor];
_septaLineColor = [UIColor darkGrayColor];
_septaLineWidth = 1.f;
_keyboardType = UIKeyboardTypeNumberPad;
_returnKeyType = UIReturnKeyDone;
_secureTextEntry = YES;
_passwordLength = 6;
_secureTextEntry = YES;
self.backgroundColor = [UIColor whiteColor];
}
- (void)layoutSubviews {
if (_didLayoutSubview) {
return;
}
[super layoutSubviews];
CGSize size = self.bounds.size;
CGFloat gridWith = size.width*1.0/_passwordLength;
CAShapeLayer *gridLayer = [CAShapeLayer layer];
gridLayer.frame = self.bounds;
gridLayer.strokeColor = _septaLineColor.CGColor;
gridLayer.lineWidth = _septaLineWidth;
UIBezierPath *gridPath = [UIBezierPath bezierPath];
[gridPath moveToPoint:CGPointMake(0, _septaLineWidth/2.0)];
[gridPath addLineToPoint:CGPointMake(size.width, _septaLineWidth/2.0)];
[gridPath moveToPoint:CGPointMake(size.width, size.height-_septaLineWidth/2.0)];
[gridPath addLineToPoint:CGPointMake(0, size.height-_septaLineWidth/2.0)];
[gridPath moveToPoint:CGPointMake(_septaLineWidth/2.0, _septaLineWidth)];
[gridPath addLineToPoint:CGPointMake(_septaLineWidth/2.0, size.height-_septaLineWidth)];
for (int i = 1; i<= _passwordLength; ++i) {
[gridPath moveToPoint:CGPointMake(gridWith*i-_septaLineWidth/2.0, _septaLineWidth/2)];
[gridPath addLineToPoint:CGPointMake(gridWith*i-_septaLineWidth/2.0, size.height-_septaLineWidth/2)];
}
gridLayer.path = gridPath.CGPath;
[self.layer addSublayer:gridLayer];
_didLayoutSubview = YES;
}
/**
* 生成小黑点
*
* @param index 第几个
*
* @return 当前小黑点
*/
- (CAShapeLayer *)makeBlackDotLayerAtIndex:(NSUInteger)index {
CAShapeLayer *layer = [CAShapeLayer layer];
layer.fillColor = _dotFillColor.CGColor;
CGSize size = self.bounds.size;
CGFloat gridWith = size.width*1.0/_passwordLength;
layer.path = [self circlePathWithCenter:CGPointMake(gridWith*0+gridWith/2.0, size.height/2.0)].CGPath;
return layer;
}
/**
* 生成text 文本 注意 这种方式不适成大量文本
*
* @param index 第几个
*
* @return 当前文本
*/
- (CATextLayer *)makeTextLayerAtIndex:(NSUInteger)index {
CATextLayer *textLayer = [CATextLayer layer];
textLayer.alignmentMode = kCAAlignmentCenter;
textLayer.wrapped = YES;
textLayer.string = [_innerText substringWithRange:NSMakeRange(index, 1)];
textLayer.contentsScale = [UIScreen mainScreen].scale;
CFStringRef fontName = (__bridge CFStringRef)_font.fontName;
CGFontRef fontRef = CGFontCreateWithFontName(fontName);
textLayer.font = fontRef;
textLayer.fontSize = _font.pointSize;
textLayer.foregroundColor = _textColor.CGColor;
CGFontRelease(fontRef);
return textLayer;
}
/**
* 计算实心小圆点
*
* @param center 圆心
*
* @return 圆的路径
*/
- (UIBezierPath *)circlePathWithCenter:(CGPoint)center{
return [UIBezierPath bezierPathWithArcCenter:center radius:_dotRadius startAngle:0 endAngle:2*M_PI clockwise:YES];
}
- (NSString *)text {
return [_innerText copy];
}
#pragma mark -
#pragma mark Respond to touch and become first responder.
- (BOOL)canBecomeFirstResponder {
return YES;
}
#pragma mark -
#pragma mark UIKeyInput Protocol Methods
- (BOOL)hasText {
return (self.innerText.length >0);
}
- (void)insertText:(NSString *)theText {
if (_innerText.length == _passwordLength) {
return;
}
[self.innerText appendString:theText];
[self addBlackDotOrTextAtIndex:_innerText.length-1];
}
/**
* 添加小圆点或文本到相应的框内
*
* @param index 位置
*/
- (void)addBlackDotOrTextAtIndex:(NSUInteger)index {
if (_secureTextEntry) {
CAShapeLayer *layer = [self makeBlackDotLayerAtIndex:index];
CGSize size = self.bounds.size;
CGFloat gridWith = size.width*1.0/_passwordLength;
layer.frame = CGRectMake(gridWith*index, 0, gridWith, size.height);
[self.layer addSublayer:layer];
[_dotsLayers addObject:layer];
} else {
CATextLayer *layer = [self makeTextLayerAtIndex:index];
CGSize size = self.bounds.size;
CGFloat gridWith = size.width*1.0/_passwordLength;
layer.frame = CGRectMake(gridWith*index, (size.height-_font.pointSize-2)/2.0, gridWith,_font.pointSize+2);
[self.layer addSublayer:layer];
[_subTextLayers addObject:layer];
}
}
/**
* 删除文本
*/
- (void)deleteText {
if (_secureTextEntry) {
CAShapeLayer *layer = [_dotsLayers lastObject];
[layer removeFromSuperlayer];
[_dotsLayers removeLastObject];
}else{
CATextLayer *layer = [_subTextLayers lastObject];
[layer removeFromSuperlayer];
[_subTextLayers removeLastObject];
}
}
代码很简单自己又定义了一些属性可以方便的实现自定义的一些效果:比如边框颜色 ,边框粗细,黑色圆点的大小,颜色,明文状态的文字大小,颜色等。也支持xib 拖个View 只需改成相关的类就可以了。项目还很粗糙(上传到了GitHub了地址:
https://github.com/lsb332/SBPasswordTextField)
只是提供一种思路而已,附两张实现图: