一句代码实现银行卡手机号输入时格式化

源码地址:https://github.com/csfuwwc/NSNumberFormatterTest

2020.05.20更新

新增支持0开头银行卡号

2018.11.30更新

现在好像已经有最多23位银行卡号了?使用的同学记得核准业务涉及银行卡号长度哈

2018.09.05更新

在将string转为number时使用NSDecimalNumber类型,可有效避免精度损失情况

2018.01.18更新

NSString+More 新增<已有数据展示时格式化>

//转换为手机号格式  xxx xxxx xxxx
- (NSString *)phoneFormatter;

//转换为银行卡格式 xxxx xxxx xxxx xxxx xxx
- (NSString *)banCardFormatter;

位数不够时也显示上述格式,
如1391111111 显示为 139 1111 111
如622848040256489001显示为6228 4804 0256 4890 01

UITextField+More 新增<输入数据时格式化显示>

//格式-手机号/银行卡/金额等等
@property (assign, nonatomic)UIInputTextFieldStyle style;

//最大输入位数
@property (assign, nonatomic)NSInteger maxInputLength;

//最终字符
@property (assign, nonatomic)NSString * resultString;

style:控制格式,目前支持手机号/银行卡 后期将扩展支持小数位数自定义(如小数点后指定位数,有其他需求的童鞋可以留言~~~)

maxInputLength:控制位数,当超过指定位数时不可再输入

resultString:最终字符,主要针对手机号/银行卡格式输入控制时获取实际字符

原文

场景一、手机号/银行卡 格式化<展示>

核心代码

- (NSString *)PhoneFormatter:(NSString*)str
{
     NSDecimalNumber * number = [[NSDecimalNumber alloc] initWithString:str]

     NSNumberFormatter * formatter = [NSNumberFormatter new];

     //设置分隔符
     [formatter setGroupingSeparator:@" "];

     //设置分割格式
     formatter.positiveFormat = @"###,###0";

    //如果string以0开头
    if ([self hasPrefix:@"0"] && [number stringValue].length != self.length)
   {
           NSString *prefixStr = [self substringToIndex:[self length]-[number stringValue].length];
    
    
           NSMutableString *tempStr = [prefixStr mutableCopy];
                    
          for (NSInteger i = 4; i <= prefixStr.length; i += 4)
          {
                 [tempStr insertString:@" " atIndex:(i+(i-1)/4)];
          }
                    
          formatter.positivePrefix = tempStr;
   }

     NSString * string = [formatter stringFromNumber:number];

     NSLog(@"%@",string);
}

输入手机号 13911112222 输出结果如下:

139 1111 2222

同理,输入一个银行卡号 6228480402564890018 输出结果如下:

622 8480 4025 6489 0018

适用场景:已有数据,格式化展示。

场景二、手机号/银行卡 格式化<输入时>

如果继续使用上面方法,输入过程中数字跳动,逼死强迫症。有兴趣的同学可以试试。

核心代码

NSDecimalNumber * number = [[NSDecimalNumber alloc] initWithString:string];

NSNumberFormatter * formatter = [NSNumberFormatter new];
    
//设置分隔符
[formatter setGroupingSeparator:@" "];
//设置使用组分割
formatter.usesGroupingSeparator = YES;
    
// 数字分割的尺寸<从右向左> 
formatter.groupingSize = ([string length]-3)%4>0?([string length]-3)%4:4;
    
//除了groupingSize决定的尺寸外,其他数字位分割的尺寸
formatter.secondaryGroupingSize = [string length]>7?4:3;
 
//如果string以0开头
if ([string hasPrefix:@"0"] && [number stringValue].length != string.length)

{
NSString *prefixStr = [string substringToIndex:[string length]-[number stringValue].length];
NSMutableString *tempStr = [prefixStr mutableCopy];

       for (NSInteger i = 4; i <= prefixStr.length; i += 4)
       {
               [tempStr insertString:@" " atIndex:(i+(i-1)/4)];
        }
        formatter.positivePrefix = tempStr;
}           
//获取格式化后的字符
tf.text  = [formatter stringFromNumber:number];
formatter.gif

适用场景:输入字符同时要求格式。

封装集成

在项目中,通过封装TextField+More分类,使用时,只需要一句代码即可实现:

self.inputTextField.style = UIInputTextFieldStyle_Phone;

下面看下TextField+More的实现

<UITextField+More.h>

//  UITextField+More.h
//  NSNumberFormatterTest
//
//  Created by 李彦鹏 on 2017/12/7.
//  Copyright © 2017年 csfuwwc. All rights reserved.
//

#import <UIKit/UIKit.h>

typedef void(^EditBlock)(UITextField * tf);

typedef enum : NSUInteger {

     UIInputTextFieldStyle_None = 0,//无格式限制

     UIInputTextFieldStyle_Phone = 1, //手机号

     UIInputTextFieldStyle_BankCard = 2, //银行卡

} UIInputTextFieldStyle;

@interface UITextField (More)<UITextFieldDelegate>


//扩展输入框style属性,使用时直接赋值style即可实现格式化功能
@property (assign, nonatomic)UIInputTextFieldStyle style;


//最大输入位数
@property (assign, nonatomic)NSInteger maxInputLength;

//最终字符
@property (assign, nonatomic)NSString * resultString;


/**
 *  输入框事件触发回调
 *
 *  @param event 事件
 *  @param block 回调
 */
 
- (void)handleControlEvent:(UIControlEvents)event withBlock:(EditBlock)block;


 
<UITextField+More.m>

//
//  UITextField+More.m
//  NSNumberFormatterTest
//
//  Created by 李彦鹏 on 2017/12/7.
//  Copyright © 2017年 csfuwwc. All rights reserved.
//
  
#import "UITextField+More.h"
#import <objc/runtime.h>

@implementation UITextField (More)

//设置最大输入位数
#pragma mark - UITextFieldInputLength

static const char * TextFieldMaxInputLengthKey =    "TextFieldMaxInputLengthKey";

- (void)setMaxInputLength:(NSInteger)maxInputLength
{
      objc_setAssociatedObject(self, TextFieldMaxInputLengthKey, @(maxInputLength), OBJC_ASSOCIATION_ASSIGN);

      self.delegate = self;
}

- (NSInteger)maxInputLength
{
     return [objc_getAssociatedObject(self, TextFieldMaxInputLengthKey) integerValue];
}

 #pragma mark - UIControlEventBlock  输入字符Block回调实现

static const char * TextFieldMoreKey = "TextFieldMoreKey";

-(void)handleControlEvent:(UIControlEvents)event withBlock:(EditBlock)block
{
    objc_setAssociatedObject(self, TextFieldMoreKey, block, OBJC_ASSOCIATION_COPY_NONATOMIC);
    [self addTarget:self action:@selector(editEvent:) forControlEvents:event];
}

-(void)editEvent:(UITextField *)textField
{
    EditBlock block = objc_getAssociatedObject(self, TextFieldMoreKey);

    if (block)
    {
        block(textField);
    }
}



#pragma mark - UITextFieldInputSytle 输入字符格式化核心部分

static const char * TextFieldStyleKey = "TextFieldStyleKey";

- (void)setStyle:(UIInputTextFieldStyle)style
{
     objc_setAssociatedObject(self, TextFieldStyleKey, @(style), OBJC_ASSOCIATION_ASSIGN);

      switch (style)
      {
            case UIInputTextFieldStyle_Phone:
            {
                  //手机号码最多11位
                  self.maxInputLength = 11;
        
                 //检测输入-格式化
                 [self formatterInputString];
            }
             break;
            case UIInputTextFieldStyle_BankCard:
            {
                  //银行卡号最多19位 
                  self.maxInputLength = 19;
        
                 //检测输入-格式化
                 [self formatterInputString];
            }
              break;
        
           default:
            {
                //默认不限制位数
                self.maxInputLength = MAXFLOAT;
            }
               break;
      }
}

- (UIInputTextFieldStyle)style
{
     return [objc_getAssociatedObject(self, TextFieldStyleKey) integerValue];
}


//格式化输入字符
- (void)formatterInputString
{
    [self handleControlEvent:UIControlEventEditingChanged withBlock:^(UITextField *tf) {
   
    //去空
    NSString * string = [tf.text stringByReplacingOccurrencesOfString:@" " withString:@""];
    
    if ([string length]==0)
    {
        return;
    }
    
    NSDecimalNumber * number = [[NSDecimalNumber alloc] initWithString:string];

    //获取格式化后的字符
    tf.text  = [[self formatterWithString:string] stringFromNumber:number];
    
    
    }];
}
- (NSNumberFormatter *)formatterWithString:(NSString *)string
{
    NSNumberFormatter * formatter = [NSNumberFormatter new];

    //设置分隔符
    [formatter setGroupingSeparator:@" "];
    //设置使用组分割
    formatter.usesGroupingSeparator = YES;

    switch (self.style)
    {
        case UIInputTextFieldStyle_Phone:
        {
            //设置首个组长度<从右向左>
            formatter.groupingSize = ([string length]-3)%4>0?([string length]-3)%4:4;
            //设置第二个组长度
            formatter.secondaryGroupingSize = [string length]>7?4:3;
        }
            break;
        case UIInputTextFieldStyle_BankCard:
        {
        
            //设置首个组长度<从右向左>
            formatter.groupingSize = ([string length]-4)%4>0?([string length]-4)%4:4;
            //设置第二个组长度
            formatter.secondaryGroupingSize = 4;
        }
            break;
        
        default:
            break;
    }

    return formatter;
}




#pragma mark - UITextFieldDelegate

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    if (![string isEqualToString:@""])
    {
    
        NSString * currentString = self.style!=UIInputTextFieldStyle_None?[textField.text stringByReplacingOccurrencesOfString:@" " withString:@""]:textField.text;

        if ([currentString length]>= self.maxInputLength)
        {
            return NO;
        }
    }

    return YES;
}

//最终字符
- (NSString *)resultString
{
    if (self.style != UIInputTextFieldStyle_None)
    {
        return [self.text stringByReplacingOccurrencesOfString:@" " withString:@""];
    }

    return self.text;
}

@end

源码地址:https://github.com/csfuwwc/NSNumberFormatterTest

结束语

更多格式
http://unicode.org/reports/tr35/tr35-6.html#Number_Format_Patterns

PS:NSNumberFormatter远比想象的强大哟!

谢谢大家。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350