29
31

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

iOSのメッセージアプリ風のテキスト入力UIの実現方法(iOS7)

Last updated at Posted at 2014-08-01

iOSのメッセージアプリの以下のような挙動を真似したい。

  1. キーボード上部からの下ドラッグに合わせてキーボードが隠れる。
    ドラッグを上に戻すと、キーボードを隠す途中でもキャンセルできる

  2. メッセージ入力ボックスは、入力フォーカスを持たない状態では画面最下部にドックされているが、
    フォーカスを持った状態だと、キーボードの上部に配置され、常に表示される。

1.についてはコンテンツを表示する UIScrollView の keyboardDismissMode プロパティをUIScrollViewKeyboardDismissModeInteractive に設定してやればよい。

2.については、UIKeyboardWillShowNotification 等を通知を捕まえて、メッセージ入力ボックスをキーボード表示アニメーションに合わせて移動させる方法があるが、厳密にキーボードアニメーションに追従させないと、アニメーション中にメッセージボックスとキーボードの間に隙間ができるため見た目が悪い。なにより面倒くさい。。

上記解決するため、以下方法を用いた。(Stackoverflow のこちらを参考にさせていただいた)

  • UIViewControllerの inputAccessoryView で入力ボックス(UITextField, UITextArea 等を含む任意のビュー)を返す

  • UIViewControllerの canBecomeFirstResponder メソッドをオーバーライドする。

「UIViewControllerの」というところがポイントである。標準キーボード上部に任意のビューを配置する方法として UITextField 等の inputAccessoryView をオーバーライドする方法が一般的であるが、今回の場合は UIViewController で行っている。

また、入力ボックスとして返すビューは UIViewController のルート view 階層には含めない
(含めるとキーボード非表示時に画面下部にドックしない)


#import "DebugViewController.h"

@interface DebugViewController () <UITextFieldDelegate>

@property(nonatomic, weak) IBOutlet UIScrollView *scrollView;

// テキスト入力ボックス
@property(nonatomic, weak) IBOutlet UITextField *textField;

// キーボード上部に表示する、textField を含むビュー. UIViewController.view のビュー階層に置かずに、強参照にする.
@property(nonatomic, strong) IBOutlet UIView *textFieldContainer;

@end

@implementation SBDebugViewController {
    BOOL _showInputView;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    _textField.delegate = self;
    _scrollView.contentSize = [UIScreen mainScreen].bounds.size;
    _scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    _showInputView = YES;
    [self reloadInputViews];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    _showInputView = NO;
    [self reloadInputViews];
}

// こちらをオーバーライドすることで下にドックする view が得られる.
- (UIView *)inputAccessoryView
{
    if (_showInputView) {
        return _textFieldContainer;
    }
    else {
        // nil にして [self reloadInputViews]; しないとメモリリーク!
        // inputContainer が外部で強参照されているらしい.
        return nil;
    }
}

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

@end

何故か inputAccessoryView で返したビューは参照が解放されないので
ビューが非表示になるタイミングで reloadInputViews を呼び、nil を返し直して参照を解放している。

29
31
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
31

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?