【iOS】UITextField でのアルファベット大文字制限、長さ制限

#UITextField でのアルファベット大文字制限

iOSアプリでの入力ボックスとして使われるUITextFieldで、
アルファベット大文字のみの入力に制限したい！
Android の場合は [【Android】EditText でのアルファベット大文字制限、長さ制限](https://qiita.com/a__i__r/items/8a7efaeeeae9b8c56f81) を参照してください。

# 環境
ホスト: MaxOS Mojave
Xcode: 10
Objective-C

# 入力時キーボードの設定
StoryBoard で対象の UITextField を選択して Attributes Inspector を開いて編集します。
<img width="369" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/108113/2748e8b5-a2e7-ab68-4754-c89d468e7367.png">

# 設定方法
1. 対象の UITextField を Outlet で関連づけておく
2. ヘッダファイルにて UITextFieldDelegate を実装設定
1. Main.StoryBoard を開いて 対象の UITextField を選択
1. 右ペインの Attributes Inspector を選択
1. Capitalization に All Characters, KeyboardType に ASCII Capable を選択

```objc:MyView.h
@interface MyView : UIViewController<UITextFieldDelegate, ...>

@property (weak, nonatomic) IBOutlet UITextField *targetField;

@end
```

# 関数用意
```objc:Constant.m
NSString* const ALPHABET_UPPER = @"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
NSString* const ALPHASPACE_ONLY = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ";

+(NSString*) getAlphabetUpperCharacters {
    return ALPHABET_UPPER;
}
+(NSString*) getAlphaSpaceCharacters {
    return ALPHASPACE_ONLY;
}

// アルファベット＋スペース以外があるかチェック
+(BOOL) containsAlphabetWithSpace:(NSString*) string {
    
    NSCharacterSet *alphaSet = [NSCharacterSet characterSetWithCharactersInString:[Constant getAlphaSpaceCharacters]];
    NSCharacterSet *invSet = [alphaSet invertedSet];
    NSRange range = [string rangeOfCharacterFromSet:invSet];
    if (range.location != NSNotFound) {
        NSLog(@"Except alphabet and space:%@", string);
        return false;
    }
    return true;
}

// アルファベット大文字以外があるかチェック
+(BOOL) containsAlphabetUpper:(NSString*) string {
    NSCharacterSet *alSet = [NSCharacterSet characterSetWithCharactersInString:[Constant getAlphabetUpperCharacters]];
    NSCharacterSet *invSet = [alSet invertedSet];
    NSRange range = [string rangeOfCharacterFromSet:invSet];
    if (range.location != NSNotFound) {
        NSLog(@"Except upper alphabet:%@", string);
        return false;
    }
    return true;
}
```

# Delegate実装
1. shouldChangeCharactersInRange 関数を実装

```objc:MyView.m
- (BOOL)textField:(UITextField *) textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {

    BOOL replaceUpper = false;
    NSUInteger maxLength = 0;
    
    // 位置情報
    UITextPosition* beginning = textField.beginningOfDocument;
    UITextPosition* start = [textField positionFromPosition:beginning offset:range.location];
    UITextPosition* end = [textField positionFromPosition:start offset:range.length];
    UITextRange* textRange = [textField textRangeFromPosition:start toPosition:end];
    
    if(textField == _targetField) {
        maxLength = 5;
        // check alphabet only
        if (![Constant containsAlphabetWithSpace:string]) {
            return false;
        }
        if (![Constant containsAlphabetUpper:string]) {
            replaceUpper = true;
        }
    }
    
    NSUInteger oldLength = [textField.text length];
    NSUInteger replacementLength = [string length];
    NSUInteger rangeLength = range.length;
    
    NSUInteger newLength = oldLength - rangeLength + replacementLength;
    if (newLength > maxLength) {
        return false;
    }
    
    if ([string rangeOfString: @"\n"].location != NSNotFound) {
        return false;
    }
    
    // テキスト置換は他の制限判定後
    if (replaceUpper) {
        [textField replaceRange:textRange withText:[string uppercaseString]];
        return false;
    }
    return true;
}
```

# 実機テスト
キーボードにて数字入力ができない事、長さの制限がされていること、
小文字を入力した場合に大文字に自動変換されることを確認します。


以上、お疲れ様でした！