前言
這是在我項目開發(fā)中遇到的問題,主要是給創(chuàng)建的作品保存名字,這樣我們就會涉及到名稱字?jǐn)?shù)和特殊字符的限制,目前涉及到用戶輸入的大部分解決方案就是UITextField和UITextView,兩者的原理差不多,那么下面就針對UITextField進(jìn)行簡單的分享.
補(bǔ)充:目前為止XCode版本更新后(目前為8.1)對開發(fā)者賬戶進(jìn)行了相關(guān)的限制,至于何種限制,目前影響最大的就是從網(wǎng)上下載下來的項目不能用了,原因是證書等問題,所以給大家一個建議,小程序可以用模擬器的用模擬器,用到真機(jī)的盡可能的選擇將你開發(fā)組的賬號,這個一般公司都會配備.這里簡單說一下,詳細(xì)的可以參考我的另外一邊文章:iOS坑--XCode8 的一些問題,希望對大家有所幫助.
正文
1.準(zhǔn)備工作
1).創(chuàng)建屬性
由于項目中的UITextField為StoryBoard控件,創(chuàng)建省略
/// 記錄顯示剩余個數(shù)
@property (weak, nonatomic) IBOutlet UILabel *totalCharacterLabel;
/// 輸入UITextField
@property (weak, nonatomic) IBOutlet UITextField *nameField;
2).設(shè)置最大限制數(shù)
static NSInteger CharacterCount = 8;
3).設(shè)置代理
self.nameField.delegate = self;
4).遵循代理
@interface ViewController () <UITextFieldDelegate>
2.原理
首先我們需要區(qū)分的是UITextField的輸入分為兩個階段,即為輸入階段和確定輸入階段,輸入階段這里主要針對的是中文的輸入,這里不需要做嚴(yán)格的判斷要求,再就是確定輸入階段,也就是UITextField的輸出顯示階段,這里需要排除空格,emoji等特殊字符和字?jǐn)?shù)的限制(截取有效字符).
3.觀察者
1).添加觀察者
[self.nameField addTarget:self action:@selector(textFieldChanged:)forControlEvents:UIControlEventEditingChanged];
2).執(zhí)行觀察者對應(yīng)的方法
在方法中,特地針對目前iOS支持的第三方輸入法做出的解決方案
- (void)textFieldChanged:(UITextField *)textField {
NSString *toBeString = textField.text;
if (![self isInputRuleAndBlank:toBeString]) {
textField.text = [self disable_emoji:toBeString];
return;
}
NSString *lang = [[textField textInputMode] primaryLanguage]; // 獲取當(dāng)前鍵盤輸入模式
//簡體中文輸入,第三方輸入法(搜狗)所有模式下都會顯示“zh-Hans”
if([lang isEqualToString:@"zh-Hans"]) {
UITextRange *selectedRange = [textField markedTextRange];
//獲取高亮部分
UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0];
//沒有高亮選擇的字,則對已輸入的文字進(jìn)行字?jǐn)?shù)統(tǒng)計和限制
if(!position) {
NSString *getStr = [self getSubString:toBeString];
if(getStr && getStr.length > 0) {
textField.text = getStr;
}
}
} else{
NSString *getStr = [self getSubString:toBeString];
if(getStr && getStr.length > 0) {
textField.text= getStr;
}
}
}
4.執(zhí)行代理
1). 在UITextField的代理中,主要是對確定輸入階段的判斷,確定沒有空格和空
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if ([self isInputRuleNotBlank:string] || [string isEqualToString:@""]) {//當(dāng)輸入符合規(guī)則和退格鍵時允許改變輸入框
return YES;
} else {
NSLog(@"超出字?jǐn)?shù)限制");
return NO;
}
}
2). return收回鍵盤
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[self.view endEditing:YES];
return YES;
}
5.輸入內(nèi)容判斷(正則判斷)
1).字母、數(shù)字、中文正則判斷(不包括空格)
- (BOOL)isInputRuleNotBlank:(NSString *)str {
NSString *pattern = @"^[a-zA-Z\u4E00-\u9FA5\\d]*$";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", pattern];
BOOL isMatch = [pred evaluateWithObject:str];
return isMatch;
}
2).字母、數(shù)字、中文正則判斷(包括空格)(在系統(tǒng)輸入法中文輸入時會出現(xiàn)拼音之間有空格,需要忽略,當(dāng)按return鍵時會自動用字母替換,按空格輸入響應(yīng)漢字)
注意: 因為考慮到輸入習(xí)慣,許多人習(xí)慣使用九宮格,這里在正常選擇全鍵盤輸入錯誤的時候,進(jìn)行九宮格判斷,九宮格對應(yīng)的是下面????????的字符.目前項目已經(jīng)在github上進(jìn)行更新.
- (BOOL)isInputRuleNotBlank:(NSString *)str {
NSString *pattern = @"^[a-zA-Z\u4E00-\u9FA5\\d]*$";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", pattern];
BOOL isMatch = [pred evaluateWithObject:str];
// 這里是后期補(bǔ)充的內(nèi)容:九宮格判斷
if (!isMatch) {
NSString *other = @"????????";
unsigned long len=str.length;
for(int i=0;i<len;i++)
{
unichar a=[str characterAtIndex:i];
if(!((isalpha(a))
||(isalnum(a))
||((a=='_') || (a == '-'))
||((a >= 0x4e00 && a <= 0x9fa6))
||([other rangeOfString:str].location != NSNotFound)
))
return NO;
}
return YES;
}
return isMatch;
}
3).過濾字符串中的emoji
- (NSString *)disable_emoji:(NSString *)text {
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[^\\u0020-\\u007E\\u00A0-\\u00BE\\u2E80-\\uA4CF\\uF900-\\uFAFF\\uFE30-\\uFE4F\\uFF00-\\uFFEF\\u0080-\\u009F\\u2000-\\u201f\r\n]"options:NSRegularExpressionCaseInsensitive error:nil];
NSString *modifiedString = [regex stringByReplacingMatchesInString:text
options:0
range:NSMakeRange(0, [text length])
withTemplate:@""];
return modifiedString;
}
6.截取字符串(重點(diǎn))
有趣的是,根據(jù)不同的需求做出了兩種不同的方案,就是中文按照原理是占用兩個字節(jié)來算,但是有很多需求只是簡單地計算個數(shù),即為表面上看到的個數(shù),所以從用戶角度來講,個數(shù)更符合用戶審美.但是從一個程序員角度而言,兩個限制一個中文,沒錯,程序員,就是這么矯情,反正,下面做出了兩種不同的解決方案,根據(jù)你的需求而定吧
-(NSString *)getSubString:(NSString*)string
{
/// 第一種:兩個字節(jié)一個中文
// NSStringEncoding encoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
// NSData* data = [string dataUsingEncoding:encoding];
// NSInteger length = [data length];
// if (length > CharacterCount) {
// NSData *data1 = [data subdataWithRange:NSMakeRange(0, CharacterCount)];
// NSString *content = [[NSString alloc] initWithData:data1 encoding:encoding];//注意:當(dāng)截取CharacterCount長度字符時把中文字符截斷返回的content會是nil
// if (!content || content.length == 0) {
// data1 = [data subdataWithRange:NSMakeRange(0, CharacterCount - 1)];
// content = [[NSString alloc] initWithData:data1 encoding:encoding];
// }
// return content;
// }
// return nil;
/// 第二種:按照個數(shù)進(jìn)行判斷
if (string.length > CharacterCount) {
NSLog(@"超出字?jǐn)?shù)上限");
_totalCharacterLabel.text = @"0";
return [string substringToIndex:CharacterCount];
}else {
_totalCharacterLabel.text = [NSString stringWithFormat:@"%ld",(long)(CharacterCount - string.length)];
}
return nil;
}
其他
點(diǎn)擊view的其他區(qū)域收回鍵盤
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
}
寫在最后
- 項目中遇到的問題,給予的解決方案,可能不是做完美的,但是根據(jù)需求已經(jīng)達(dá)到要求,所以,如有不做指出,歡迎大家相互交流
- 此次解決方案中,用到的正則表達(dá)式?jīng)]有過多性的進(jìn)行表述,因為本人對這方面還未曾涉獵,今后會做以補(bǔ)充
- 按照慣例,奉上Demo地址:
zhangfurun的Github--UITextFieldDemo-限制字?jǐn)?shù)