身份证组成
根据《中华人民共和国国家标准GB 11643-1999》中有关公民身份号码的规定,公民身份号码是特征组合码,由十七位数字本体码和一位数字校验码组成。排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。顺序码的奇数分给男性,偶数分给女性。校验码是根据前面十七位数字码,按照ISO 7064:1983.MOD 11-2校验码计算出来的检验码。
- 前1、2位数字表示:所在省(直辖市、自治区)的代码;
- 第3、4位数字表示:所在地级市(自治州)的代码;
- 第5、6位数字表示:所在区(县、自治县、县级市)的代码;
- 第7—14位数字表示:出生年、月、日;
- 第15、16位数字表示:所在地的派出所的代码;
- 第17位数字表示性别:奇数表示男性,偶数表示女性;
- 第18位数字是校检码:也有的说是个人信息码,不是随计算机的随机产生,它是 用来检验身份证的正确性。校检码可以是0—9的数字,有时也用x表示。作为尾号的校验码,是由号码编制单位按统一的公式计算出来的,如果某人的尾号是0-9,都不会出现X,但如果尾号是10,那么就得用X来代替,因为如果用10做尾号,那么此人的身份证就变成了19位。X是罗马数字的10,用X来代替10,可以保证公民的身份证符合国家标准。
需求
很多情况下,应用都需要用户输入身份证号。身份证号的组成很简单,15位身份证全部由数字组成,18位身份证由数字加X
字母组合而成,所以这里只需要关心18位身份证。在用户输入身份证号时,为了有更好的体验,我们都会限制UITextField
的keyboardType
。但是有的身份证号中有X
,在现有keyboardType
中没有一个是完全满足需求的。在这种情况下,我们只能自定义一个身份证键盘,一个只包含数字和一个X
字母的键盘。
实现方式
自定义方式有两种
- 自己完全重新绘制一个键盘
View
,0~9十个数字按钮,一个X
按钮,一个删除按钮,最后直接将自定义的控件赋值给UITextField
的inputView
。 -
UITextField
的keyboardType
设置为UIKeyboardTypeNumberPad
,然后再此基础上上添加一个X
按钮,并监听X
按钮的点击。
代码
方式一:完全自定义
优点:完全自定义,自己控制起来比较方便,而且不会被监听到,比较安全;不同UITextField
之间切换也比较自然;适用所有设备和系统版本
缺点:要完全定义,所有的控制逻辑要自己写,不过也不是很复杂
IDKeyboardView.h
#import
@protocol IDKeyboardViewDelegate;
@interface IDKeyboardView : UIView
@property (nonatomic, weak) id delegate;
@end
//代理
@protocol IDKeyboardViewDelegate
@optional
//点击0~9和X时调用
- (void)didKeyboardItemSelected:(NSString *)item;
//点击删除时调用
- (void)didKeyboardDeletedClicked;
@end
IDKeyboardView.m
#import "IDKeyboardView.h"
@implementation IDKeyboardView
{
NSMutableArray *_buttons;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.backgroundColor = [UIColor blackColor];
NSArray *items = @[@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"X",@"0",@"删除"];
_buttons = [NSMutableArray array];
for (NSInteger i = 0; i < items.count; i++)
{
UIButton *button = [[UIButton alloc]init];
button.tag = i;
[button setTitle:items[i] forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
if (i < 11) button.titleLabel.font = [UIFont systemFontOfSize:27];
if (i == 9 || i == 11)
{
//生成纯色的背景图片imageWithColor和colorWithHexString都是YYKit中的方法
[button setBackgroundImage:[UIImage imageWithColor:[UIColor colorWithHexString:@"#D1D5DA"]] forState:UIControlStateNormal];
[button setBackgroundImage:[UIImage imageWithColor:[UIColor whiteColor]] forState:UIControlStateHighlighted];
}else
{
[button setBackgroundImage:[UIImage imageWithColor:[UIColor whiteColor]] forState:UIControlStateNormal];
[button setBackgroundImage:[UIImage imageWithColor:[UIColor colorWithHexString:@"#D1D5DA"]] forState:UIControlStateHighlighted];
}
[button addTarget:self action:@selector(didItemClicked:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:button];
[_buttons addObject:button];
}
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat width = (self.frame.size.width - 1) / 3;
CGFloat height = (self.frame.size.height - 1.5) / 4;
for (NSInteger i = 0; i < _buttons.count; i++)
{
UIButton *button = _buttons[i];
button.frame = CGRectMake((width + 0.5) * (i % 3), (height + 0.5) * (i / 3), width, height);
}
}
- (void)didItemClicked:(UIButton *)sender
{
if (_delegate)
{
if (sender.tag == 11 && [_delegate respondsToSelector:@selector(didKeyboardDeletedClicked)])
[_delegate didKeyboardDeletedClicked];
else if([_delegate respondsToSelector:@selector(didKeyboardItemSelected:)])
[_delegate didKeyboardItemSelected:[sender titleForState:UIControlStateNormal]];
}
}
@end
ViewController
#define SCREEN_SIZE [UIApplication sharedApplication].keyWindow.frame.size
CGFloat width = SCREEN_SIZE.width;
CGFloat height = SCREEN_SIZE.width / 320 * 216;
if (height > 300) height = 300;//计算一个合适的高度
/*
* 初始化IDKeyboardView
* 设置代理
* 将keyboardView赋值给UITextField的inputView
*/
IDKeyboardView *keyboardView = [[IDKeyboardView alloc]initWithFrame:CGRectMake(0, 0, width, height)];
keyboardView.delegate = self;
idCardField.inputView = keyboardView;
/*
* 实现相应代理
*/
#pragma mark - IDKeyboardViewDelegate
- (void)didKeyboardDeletedClicked
{
[idCardField deleteBackward];
}
- (void)didKeyboardItemSelected:(NSString *)item
{
if ([item isEqualToString:@"X"] && [idCardField.text containsString:@"X"]) return;
[idCardField replaceRange:idCardField.selectedTextRange withText:item];
}
方式二:设置keyboardType
为UIKeyboardTypeNumberPad
,添加X
按钮
优点:不需要完全自定义,只需要添加一个按钮就可以;只需要控制好X
按钮的显示和隐藏就可以了
缺点:代码相对来说有点多o(╯□╰)o,而且不适合iPad
#import
@interface KeyboardVC ()
@property (nonatomic, strong) UIButton *xButton;
@property (nonatomic, strong) UITextField *idField;
@end
@implementation KeyboardVC
- (void)viewDidLoad
{
[super viewDidLoad];
//初始化UITextField
//为UITextField设置代理
//添加UITextField到self.view上
//self.view可能会添加多个UITextField
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
//需要监听键盘
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (UIButton *)xButton
{
if (!_xButton)
{
_xButton = [[UIButton alloc]init];
_xButton.alpha = 0;//alpha设置为0是为了动画效果
_xButton.titleLabel.font = [UIFont systemFontOfSize:27];
[_xButton setTitle:@"X" forState:(UIControlStateNormal)];
[_xButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[_xButton setBackgroundImage:[UIImage imageWithColor:[UIColor whiteColor]] forState:UIControlStateHighlighted];
[_xButton addTarget:self action:@selector(didXClicked:) forControlEvents:UIControlEventTouchUpInside];
}
return _xButton;
}
//点击X按钮
- (void)didXClicked:(UIButton *)sender
{
[_idField replaceRange:_idField.selectedTextRange withText:@"X"];
}
//获取系统版本号,方法来YYKit
- (CGFloat)_systemVersion
{
static CGFloat v;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
v = [UIDevice currentDevice].systemVersion.floatValue;
});
return v;
}
//获取当前的FirstResponder View,方法来自TPKeyboardAvoiding
- (UIView*)findFirstResponderView:(UIView *)view
{
for (UIView *childView in view.subviews)
{
if ([childView respondsToSelector:@selector(isFirstResponder)] && [childView isFirstResponder] ) return childView;
UIView *result = [self findFirstResponderView];
if ( result ) return result;
}
return nil;
}
//获取KeyboardView,方法来YYKit
- (UIView *)_getKeyboardViewFromWindow:(UIWindow *)window
{
/*
iOS 6/7:
UITextEffectsWindow
UIPeripheralHostView << keyboard
iOS 8:
UITextEffectsWindow
UIInputSetContainerView
UIInputSetHostView << keyboard
iOS 9:
UIRemoteKeyboardWindow
UIInputSetContainerView
UIInputSetHostView << keyboard
*/
if (!window) return nil;
// Get the window
NSString *windowName = NSStringFromClass(window.class);
if ([self _systemVersion] < 9)
{ // UITextEffectsWindow
if (windowName.length != 19) return nil;
if (![windowName hasPrefix:@"UI"]) return nil;
if (![windowName hasSuffix:@"TextEffectsWindow"]) return nil;
} else
{ // UIRemoteKeyboardWindow
if (windowName.length != 22) return nil;
if (![windowName hasPrefix:@"UI"]) return nil;
if (![windowName hasSuffix:@"RemoteKeyboardWindow"]) return nil;
}
// Get the view
if ([self _systemVersion] < 8)
{
// UIPeripheralHostView
for (UIView *view in window.subviews)
{
NSString *viewName = NSStringFromClass(view.class);
if (viewName.length != 20) continue;
if (![viewName hasPrefix:@"UI"]) continue;
if (![viewName hasSuffix:@"PeripheralHostView"]) continue;
return view;
}
} else
{ // UIInputSetContainerView
for (UIView *view in window.subviews)
{
NSString *viewName = NSStringFromClass(view.class);
if (viewName.length != 23) continue;
if (![viewName hasPrefix:@"UI"]) continue;
if (![viewName hasSuffix:@"InputSetContainerView"]) continue;
// UIInputSetHostView
for (UIView *subView in view.subviews)
{
NSString *subViewName = NSStringFromClass(subView.class);
if (subViewName.length != 18) continue;
if (![subViewName hasPrefix:@"UI"]) continue;
if (![subViewName hasSuffix:@"InputSetHostView"]) continue;
return subView;
}
}
}
return nil;
}
//获取keyboardView,方法来YYKit
- (UIView *)keyboardView
{
UIApplication *app = [UIApplication sharedApplication];
if (!app) return nil;
UIWindow *window = nil;
UIView *view = nil;
for (window in app.windows)
{
view = [self _getKeyboardViewFromWindow:window];
if (view) return view;
}
window = app.keyWindow;
view = [self _getKeyboardViewFromWindow:window];
if (view) return view;
return nil;
}
//获取KeyboardView所在的Window,方法来YYKit
- (UIWindow *)keyboardWindow
{
UIApplication *app = [UIApplication sharedApplication];
if (!app) return nil;
UIWindow *window = nil;
for (window in app.windows)
{
if ([self _getKeyboardViewFromWindow:window]) return window;
}
window = app.keyWindow;
if ([self _getKeyboardViewFromWindow:window]) return window;
NSMutableArray *kbWindows = nil;
for (window in app.windows)
{
NSString *windowName = NSStringFromClass(window.class);
if ([self _systemVersion] < 9)
{ // UITextEffectsWindow
if (windowName.length == 19 &&
[windowName hasPrefix:@"UI"] &&
[windowName hasSuffix:@"TextEffectsWindow"])
{
if (!kbWindows) kbWindows = [NSMutableArray new];
[kbWindows addObject:window];
}
} else
{ // UIRemoteKeyboardWindow
if (windowName.length == 22 &&
[windowName hasPrefix:@"UI"] &&
[windowName hasSuffix:@"RemoteKeyboardWindow"])
{
if (!kbWindows) kbWindows = [NSMutableArray new];
[kbWindows addObject:window];
}
}
}
if (kbWindows.count == 1) return kbWindows.firstObject;
return nil;
}
//显示
- (void)showX
{ //键盘显示的时候,keyboardWindow才会获取到
UIWindow *keyboardWindow = [self keyboardWindow];
if (!keyboardWindow) return;
UIView *keyboardView = [self keyboardView];
if (!keyboardView) return;
CGSize keyboardSize = keyboardView.frame.size;
CGFloat width = (keyboardSize.width - 7) / 3;
CGFloat height = (keyboardSize.height - 2) / 4;
CGFloat y = keyboardWindow.frame.size.height - height;
_xButton.frame = CGRectMake(0, y, width, height);
//显示动画
[UIView animateWithDuration:0.3 animations:^{
_xButton.alpha = 1.0f;
}];
}
- (void)hideX
{
UIWindow *keyboardWindow = [self keyboardWindow];
if (!keyboardWindow) return;
//获取当前的FirstResponder
UIView *view = [self findFirstResponderView:self.view];
if (view)
{ //如果FirstResponder不为空,则说明只是切换UITextField
[UIView animateWithDuration:0.3 animations:^{
_xButton.alpha = 0.0f;
}];
}else
{ //如果FirstResponder为空,则说明键盘消失
[UIView animateWithDuration:0.3 animations:^{
_xButton.alpha = 0.0f;
} completion:^(BOOL finished) {
if (finished) [_xButton removeFromSuperview];
}];
}
}
#pragma mark - UITextFieldDelegate
- (void)textFieldDidBeginEditing:(UITextField *)textField
{ //如果有多个UITextField,需要判断一下textField是否是输入身份证号的UITextField
//UITextField进入编辑状态和keyboardWillShow的通知几乎是同时的,我们需要在键盘完全显示出来的时候再显示X按钮
[self performSelector:@selector(showX) withObject:nil afterDelay:0.3];
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
[self performSelector:@selector(hideX) withObject:nil afterDelay:0];
return YES;
}
/*
* iOS7下,不管怎么切换UITextField都只调用一次
* iOS8及以上,因为第三方键盘的问题,有可能会被调用多次
*/
#pragma mark - Notification
- (void)keyboardWillShow:(NSNotification *)notification
{
UIWindow *keyboardWindow = [self keyboardWindow];
if (!keyboardWindow) return;
if (_xButton.superview) return;//如果X按钮已经被添加
_xButton.alpha = 0;
[keyboardWindow addSubview:_xButton];//只能把按钮添加到keyboardWindow上,添加到keyboardView上是无法点击的
}
@end