web-dev-qa-db-ja.com

キーボードが表示されたときにiOSでUITextViewのサイズを変更するにはどうすればよいですか?

UITextView(iPhoneの場合)のタブにUITabBarControllerが挿入されています。

  1. UITextViewをたくさんの行で埋めます。
  2. テキストを編集するためのキーボードを表示します。

どうしたの?キーボードはカーソルでUITextViewの半分を非表示にします。結果としてテキストを編集できません。

すべてのAppleモバイルデバイス(画面解像度が異なる)の問題を解決する方法は?助けてくれてありがとう!

16
Dmitry

何年も経った今でも、問題は現実のものです。 Appleは間違いなくこれらすべてを処理する必要がありますが、そうではありません。これは、公式のAppleの ドキュメント とバグ修正に基づく新しいソリューションです。iOS8をサポートします。 iOS 9、inputAccessoryViewであり、新しいバージョンのiOSと新しいデバイスに対応しています。

/* Apple's solution to resize keyboard but with accessory view support */

- (void)keyboardDidShow:(NSNotification*)aNotification {
    NSDictionary* info = [aNotification userInfo];
    CGRect keyboardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    double keyboardHeight = [[UIScreen mainScreen] bounds].size.height - keyboardFrame.Origin.y;

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardHeight, 0.0);
    editor.contentInset = contentInsets;
    editor.scrollIndicatorInsets = contentInsets;
}

- (void)keyboardWillHide:(NSNotification*)aNotification {
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    editor.contentInset = contentInsets;
    editor.scrollIndicatorInsets = contentInsets;

    // button to hide the keyboard
    buttonDone.enabled = false;
}

/* Fix issues with size classes and accessory view */

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
    // fix incorrect size of the inputAccessoryView when size class changed
    // willTransitionToTraitCollection and traitCollectionDidChange can't help us
    if (editor && editor.inputAccessoryView && !editor.inputAccessoryView.hidden) {
        [editor resignFirstResponder];
    }
}

/* Hide accessory view if a hardware keyboard is present */

#define gThresholdForHardwareKeyboardToolbar 160.f // it's minimum height of the software keyboard on iPhone 4 in landscape mode

- (bool)isExternalKeyboard:(NSNotification*)aNotification {
    NSDictionary* info = [aNotification userInfo];
    CGRect keyboardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    double keyboardHeight = [[UIScreen mainScreen] bounds].size.height - keyboardFrame.Origin.y;

    return keyboardHeight < gThresholdForHardwareKeyboardToolbar;
}

- (void)keyboardWillShow:(NSNotification*)aNotification {
    if ([self isExternalKeyboard:aNotification]) {
        // hardware keyboard is present
        if (editor && editor.inputAccessoryView) {
            editor.inputAccessoryView.hidden = true;
        }
    } else {
        // only on-screen keyboard
        if (editor && editor.inputAccessoryView) {
            editor.inputAccessoryView.hidden = false;
        }
    }

    // button to hide the keyboard
    buttonDone.enabled = true;
}
1
Dmitry

次のコードで最良の結果が得られました。また、背景色をUIViewに設定し、UITextViewbefore他のトップ画面コントロール(UITabBarなど)を配置することを忘れないでください。

最終的にテキストを編集することは、今でも完璧ではありません。あなたは改善しようとするかもしれません。

FirstViewController.h:

@interface FirstViewController : UIViewController {
    IBOutlet UIBarButtonItem *buttonDone;
    IBOutlet UITextView *textView;
    UITabBarController* tabBarController; // set from superview in AppDelegate (MainWindow.xib)
}

@property (nonatomic, retain) UITabBarController* tabBarController;

FirstViewController.m:

@synthesize tabBarController;

- (void)viewDidAppear:(BOOL)animated
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShown:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)moveTextViewForKeyboard:(NSNotification*)aNotification up:(BOOL)up {
    NSDictionary* userInfo = [aNotification userInfo];
    NSTimeInterval animationDuration;
    UIViewAnimationCurve animationCurve;
    CGRect keyboardEndFrame;

    [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
    [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
    [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];

    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:animationDuration];
    [UIView setAnimationCurve:animationCurve];

    CGRect newFrame = textView.frame;
    CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil];
    keyboardFrame.size.height -= tabBarController.tabBar.frame.size.height;
    newFrame.size.height -= keyboardFrame.size.height * (up?1:-1);
    textView.frame = newFrame;

    [UIView commitAnimations];   
}

- (void)keyboardWillShown:(NSNotification*)aNotification
{
    buttonDone.enabled = true;
    [self moveTextViewForKeyboard:aNotification up:YES]; 
}

- (void)keyboardWillHide:(NSNotification*)aNotification
{
    buttonDone.enabled = false;
    [self moveTextViewForKeyboard:aNotification up:NO]; 
}

P.S.スタックオーバーフローなしでiOS用にコーディングするのは難しいです...

33
Dmitry

自動レイアウトを使用すると、(自動レイアウトを理解していれば)処理がはるかに簡単になります。

影響を受けるビューを識別してサイズを変更する代わりに、ビューのすべてのコンテンツの親フレームを作成するだけです。次に、kbdが表示されたら、フレームのサイズを変更します。制約を適切に設定すると、ビューはすべての子ビューを適切に再配置します。このために読みにくいコードをたくさんいじる必要はありません。

実際、 同様の質問 で私はこれへのリンクを見つけました 優れたチュートリアル このテクニックについて。

9

IOS7とiOS8の両方で、新しいQuickType機能を使用して、テキストビューを正しくスクロールおよびアニメーション化しようとすると、いくつかの問題が発生しました。最初はスクロールビューのインセットをアニメーション化することに集中していましたが、iOS 7と8で動作が異なり、両方で正しく機能させることができませんでした。

次に、フレームに焦点を合わせるだけで物事を単純化できることに気付きました。これは、はるかに単純なコードで機能しました。要約すれば:

  • UIKeyboardDidChangeFrameNotificationに登録します(これにより、QuickTypeが表示/非表示になったときにも通知されます)。
  • テキストビューのフレームを変更するために必要な垂直方向のスペースを計算します。
  • フレームサイズの変更をアニメーション化します。

上記を説明するコードを次に示します。

- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidChangeFrameWithNotification:) name:UIKeyboardDidChangeFrameNotification object:nil];
}

- (void)keyboardDidChangeFrameWithNotification:(NSNotification *)notification {
    CGFloat keyboardVerticalIncrease = [self keyboardVerticalIncreaseForNotification:notification];
    [self animateTextViewFrameForVerticalOffset:keyboardVerticalIncrease];
}

- (CGFloat)keyboardVerticalIncreaseForNotification:(NSNotification *)notification {
    CGFloat keyboardBeginY = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue].Origin.y;
    CGFloat keyboardEndY = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].Origin.y;
    CGFloat keyboardVerticalIncrease = keyboardBeginY - keyboardEndY;
    return keyboardVerticalIncrease;
}

- (void)animateTextViewFrameForVerticalOffset:(CGFloat)offset {
    CGFloat constant = self.bottomConstraint.constant;
    CGFloat newConstant = constant + offset;
    self.bottomConstraint.constant = newConstant;
    [self.view layoutIfNeeded];
    [UIView animateWithDuration:0.5 animations:^{
        [self.view layoutIfNeeded];
    }];
}

アニメーションについての簡単なメモ。 Autolayoutを使用したので、フレームを直接アニメーション化するのではなく、テキストビューのNSAutoLayoutConstraintをアニメーション化することにしました。これを行うには、アニメーションブロック内のおよびの前に[self.view layoutIfNeeded]を呼び出します。これは、コンストレイントをアニメートする正しい方法です。私はこのヒントを見つけました ここ

3
guptron

賛成の回答は、デバイスが縦向きモード(逆さまではない)の場合にのみ機能し、他のモードでは境界が間違っていることに注意してください。境界を使用して修正することでこれを並べ替えることができると思いますが、それを機能させることができなかったため、以下の調整が機能しました。

- (void)moveTextViewForKeyboard:(NSNotification*)aNotification up:(BOOL)up {


NSDictionary* userInfo = [aNotification userInfo];
NSTimeInterval animationDuration;
UIViewAnimationCurve animationCurve;
CGRect keyboardEndFrame;

[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];


[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:animationCurve];

CGRect newFrame = self.view.frame;

if (keyboardEndFrame.size.height >keyboardEndFrame.size.width)
{   //we must be in landscape
    if (keyboardEndFrame.Origin.x==0)
    {   //upside down so need to flip Origin
        newFrame.Origin = CGPointMake(keyboardEndFrame.size.width, 0);
    }

    newFrame.size.width -= keyboardEndFrame.size.width * (up?1:-1);

} else
{   //in portrait
    if (keyboardEndFrame.Origin.y==0)
    {
        //upside down so need to flip Origin
        newFrame.Origin = CGPointMake(0, keyboardEndFrame.size.height);
    }
    newFrame.size.height -= keyboardEndFrame.size.height * (up?1:-1);

}
self.view.frame = newFrame;

[UIView commitAnimations];



}
2

まず、いくつかのキーボードメソッドをNSNotificationCenterdefaultCenterに追加します

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) 
                                             name:UIKeyboardWillShowNotification object:self.view.window]; 

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) 
                                             name:UIKeyboardWillHideNotification object:self.view.window]; 

次に、サイズを変更できます。

- (void)keyboardWillShow:(NSNotification *)notif
{
[thetextView setFrame:CGRectMake(20, 49, 280, 187)]; //Or where ever you want the view to go


}

- (void)keyboardWillHide:(NSNotification *)notif
{
[thetextView setFrame:CGRectMake(20, 49, 280, 324)]; //return it to its original position

}
1
Matt S.
- (void)registerKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter]
     addObserver:self
     selector:@selector(keyboardWillShow:)
     name:UIKeyboardDidShowNotification
     object:nil];

    [[NSNotificationCenter defaultCenter]
     addObserver:self
     selector:@selector(keyboardWillHide:)
     name:UIKeyboardWillHideNotification
     object:nil];
}

- (void)unregisterKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

-(void) keyboardWillHide:(NSNotification *)note
{
   //adjust frame
}

-(void) keyboardWillShow:(NSNotification *)note
{
   //adjust frame 
}

通知もdeallocで登録解除します

- (void)unregisterKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
1
Aswathy Bose

ここでベストアンサーを試しましたが、問題が見つかりました。同じページに別のテキストフィールドがある場合は、テキストフィールドをクリックして、キーボードを表示します。テキストビューが縮小することに気付くでしょう。ただし、ここでテキストビューをクリックすると、テキストビューのサイズが再び縮小するのに気付くはずです。

この問題に対する私の解決策は、キーボードの状態(表示/非表示)を表すViewControllerのプロパティを維持することです。キーボードが現在表示されている場合は、テキストビューを縮小しないでください。さまざまなテキスト入力にさまざまなサイズのキーボードを使用している場合は、古いキーボードサイズも維持する必要があります。

このソリューションでは、異なる方向も考慮されていないことに注意してください。これは、テキストビューのサイズの計算方法に影響を与える可能性があります。

@implementation MyViewController {
    BOOL keyboardShown;
    NSInteger keyboardHeight;
}

- (void)moveTextViewForKeyboard:(NSNotification*)aNotification up: (BOOL) up{
    NSDictionary* userInfo = [aNotification userInfo];
    NSTimeInterval animationDuration;
    UIViewAnimationCurve animationCurve;
    CGRect keyboardEndFrame;

    [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
    [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
    [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];

    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:animationDuration];
    [UIView setAnimationCurve:animationCurve];

    CGRect newFrame = self.textView.frame;
    CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil];

    NSInteger oldHeight = self->keyboardShown ? self->keyboardHeight : 0;
    NSInteger newHeight = up ? keyboardFrame.size.height : 0;
    NSInteger change = oldHeight - newHeight;

    self->keyboardShown = up;
    self->keyboardHeight = keyboardFrame.size.height;

    newFrame.size.height += change;
    self.textView.frame = newFrame;

    [UIView commitAnimations];
}
0
Harper

フォローアップとして、キーボード通知が発生したときにフレームを更新する手法は、iOS 7では機能しません。別の解決策については、以下を参照してください。

iOS 7でキーボードが表示されているときにUITextViewのサイズを変更する方法

0
ColinE

つまり、キーボード通知を登録し、通知されたらサイズ変更作業を行います。

0
Di Wu