togo

= ひとりごと to go

zumuya の人による机の上系情報サイト

ユーザの操作として UITextView/NSTextView の値を書き換える方法

  • Apple
  • 開発

テキストビューの値を書き換えるとき、Mac なら -[NSTextView setString:]、iOS なら -[UITextView setText:] を呼ぶと思う。

ビューが表示される前に初期値を設定するような用途ではこれだけでいいのだけど、ユーザの操作に対してこれを使うと不十分だったりする。

例えばクリアボタン。Timinder(2014.4.26) でも使ったけど、ユーザがテキストを入力したあと、素早く空に戻したいときにあると便利。
その実装が [textView setText: @""] だったとする。そのあとユーザが取り消したくなって本体を振っても、先ほど空にした操作は Undo Manager に登録されていないので取り消しできない。

自力で Undo Manager に操作を登録したりしてもいいけど、ユーザのキーボード入力と同じような扱いでコードから値を書き換える方法があれば、そちらの方が理想的。
NSTextView(Mac)での方法は知っていたけど、UITextView(iOS)の場合どうすればいいのかは最近までわからなかったのでメモしておく。

AppKit:NSTextView での方法

値の書き換えの前後を -shouldChangeTextInRange:replacementString:-didChangeText で囲むのがポイント。

-(IBAction)clear: (id)sender
{
    NSTextView* textView = ...;
    NSUInteger textLength = textView.string.length;
    if (textLength && [textView shouldChangeTextInRange: NSMakeRange(0, textLength) replacementString: @""]) {
        [textView setString: @""];
        [textView didChangeText];
    }
}

UIKit:UITextView での方法

UITextView が実装している -replaceRange:withText: を呼べばいいんだけど、その引数の range は構造体 NSRange ではなく UITextRange というクラスのインスタンスなので扱いにくい。"Create UITextRange from NSRange - Stack Overflow" を参考に変換。

-(UITextRange*)textRangeWithNSRange: (NSRange)range inTextView: (UITextView*)textView
{
    UITextPosition* beginning = textView.beginningOfDocument;
    UITextPosition* start = [textView positionFromPosition: beginning offset: range.location];
    UITextPosition* end = [textView positionFromPosition: start offset: range.length];
    return [textView textRangeFromPosition: start toPosition: end];
}

-(IBAction)clear: (id)sender
{
    UITextView* textView = ...;
    NSUInteger textLength = textView.textStorage.length;
    if (textLength) {
        [textView replaceRange: [textView textRangeWithNSRange: NSMakeRange(0, textLength) inTextView: textView] withText: @""];
    }
}

Share

リンクも共有もお気軽に。記事を書くモチベーションの向上に役立てます。