iOSでカーソル位置を追跡したい

最近ではSwiftもVersion3になりましたね。
でも、あえてObjective-Cで。(Swift書けない)

## カーソル位置を取得したい
UITextField(または、UITextView)で文字を打っているときのカーソルの位置を取得したいということがありました。
<img width="432" alt="スクリーンショット 2017-02-07 23.45.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/46278/6a162800-0525-41aa-d595-28b4692bd015.png">
この青い線の場所です。

以下のようにすると取得できます。

```objc:sample.m
UITextRange *textRange = textField.selectedTextRange;
UITextPosition *startPosition = textRange.start;
```

こんな感じでUITextPositionというクラスの値が取得できます。
UITextPosition…!?

UITextPositionとか扱いづらそうなので、int(NSInteger)に変換します。

```objc:sample.m
UITextRange *textRange = textField.selectedTextRange;
UITextPosition *beginning = textField.beginningOfDocument;
UITextPosition *startPosition = textRange.start;
NSInteger pos = [textField offsetFromPosition:beginning toPosition:startPosition];
```

これで、カーソルの位置が数値として得られます。

## じゃあ、今度は追跡したい
次は、カーソルの位置が変わるたびに値を取得したいと思いました。
KVOを使います。
参考:[[Objective-C] KeyValueObserve（キー値監視）メモ - Qiita](http://qiita.com/edo_m18/items/d78a2d4a1ca7dcaec69a)


```objc:sample.m
- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:YES];
    [textField addObserver:self forKeyPath:@"selectedTextRange" options:NSKeyValueObservingOptionNew context:nil];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
    UITextRange *textRange = textField.selectedTextRange;
    UITextPosition *beginning = textField.beginningOfDocument;
    UITextPosition *startPosition = textRange.start;
    NSInteger pos = [textField offsetFromPosition:beginning toPosition:startPosition];
}
```

これで、位置が変わるたびに取得できる！

と思ったのですが、文字を打った際には呼び出されませんでした…

## UITextFieldのtextが書き換えられた時にメソッドを呼ぶ
UITextFieldのデリゲートメソッドを使います。

```objc:sample.m
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
    NSInteger pos = range.location+string.length;
    return YES;
}
```

これでいい感じです。（めんどくさくなったので、詳しい解説はしません。)

## 以上！
これで、カーソル位置を追跡。NSInteger型の値で取得できます。

```objc:sample.m
- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:YES];
    [textField addObserver:self forKeyPath:@"selectedTextRange" options:NSKeyValueObservingOptionNew context:nil];
}

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
    pos = range.location+string.length;
    return YES;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
    // ほんとだったらkeyPathできちんとチェックしてください…
    UITextRange *textRange = _textField.selectedTextRange;
    UITextPosition *beginning = _textField.beginningOfDocument;
    UITextPosition *startPosition = textRange.start;
    pos = [textField offsetFromPosition:beginning toPosition:startPosition];
}
```

より良い方法があれば教えてください…

## 参考URL

[[Objective-C] KeyValueObserve（キー値監視）メモ - Qiita](http://qiita.com/edo_m18/items/d78a2d4a1ca7dcaec69a)
[the moon at dawn: UITextFieldのキャレットを操作(UITextRangeとUITextPosition)](http://glayash.blogspot.jp/2012/08/uitextfielduitextrageuitextposition.html)
[ios - UITextPosition to NSRange - Stack Overflow](http://stackoverflow.com/questions/11732150/uitextposition-to-nsrange)