UITextField:键盘出现时移动视图

Com*_*Vie 53 iphone cocoa-touch objective-c uitextfield

我目前正在开发一个带有单个视图的iPhone应用程序,它有多个UITextField用于输入.键盘显示时,它覆盖底部文本字段.所以我添加了相应的textFieldDidBeginEditing:方法,将视图向上移动,效果很好:

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    if ( ( textField != inputAmount ) && ( textField != inputAge ) ) {
        NSTimeInterval animationDuration = 0.300000011920929;
        CGRect frame = self.view.frame;
        frame.origin.y -= kOFFSET_FOR_KEYBOARD;
        frame.size.height += kOFFSET_FOR_KEYBOARD;
        [UIView beginAnimations:@"ResizeForKeyboard" context:nil];
        [UIView setAnimationDuration:animationDuration];
        self.view.frame = frame;
        [UIView commitAnimations];      
    }
}
Run Code Online (Sandbox Code Playgroud)

如果消息源是键盘显示时可见的文本字段之一,则此方法检查,如果不是,则向上移动视图.

我还添加了textFieldDidEndEnditing:方法,它再次向下移动视图(并根据更改的输入更新一些模型对象):

- (void)textFieldDidEndEditing:(UITextField *)textField {
    if ( ( textField != inputMenge ) && ( textField != inputAlter ) ) {
        NSTimeInterval animationDuration = 0.300000011920929;
        CGRect frame = self.view.frame;
        frame.origin.y += kOFFSET_FOR_KEYBOARD;
        frame.size.height -= kOFFSET_FOR_KEYBOARD;
        [UIView beginAnimations:@"ResizeForKeyboard" context:nil];
        [UIView setAnimationDuration:animationDuration];
        self.view.frame = frame;
        [UIView commitAnimations];      
    }
    // Additional Code
}
Run Code Online (Sandbox Code Playgroud)

但是,这个解决方案有一个简单的缺陷:当我完成编辑其中一个"隐藏"文本字段并触摸另一个文本字段时,键盘消失,视图向下移动,视图再次向上移动,键盘重新出现.

是否有可能防止键盘在两个编辑("隐藏"文本字段)之间消失和重新出现 - 这样当所选文本字段从键盘隐藏的文本字段变为不隐藏的字段时,视图才会移动)?

Vla*_*rov 47

此解决方案基于ComSubVie的解决方案.

好处:

  • 它支持设备旋转 - 适用于所有方向;
  • 它不会对动画持续时间和曲线的值进行硬编码,而是从键盘通知中读取它们;
  • 它利用UIKeyboardWillShowNotification而不是UIKeyboardDidShowNotification同步键盘动画和自定义动作;
  • 它不使用已弃用的UIKeyboardBoundsUserInfoKey;
  • 由于按下国际键,它处理键盘调整大小;
  • 通过取消注册键盘事件来修复内存泄漏;
  • 所有键盘处理代码都封装在一个单独的类中 - KBKeyboardHandler;
  • 灵活性 - KBKeyboardHandler可以轻松扩展/修改类以更好地满足特定需求;

限制:

  • 适用于iOS 4及更高版本,需要进行少量修改才能支持旧版本;
  • 它适用于单个应用程序UIWindow.如果使用多个UIWindows,则可能需要修改retrieveFrameFromNotification:方法.

用法:

在项目中包含KBKeyboardHandler.h,KBKeyboardHandler.m和KBKeyboardHandlerDelegate.h.KBKeyboardHandlerDelegate在视图控制器中实现协议 - 它由单个方法组成,当键盘显示,隐藏或其大小发生变化时,将调用该方法.实例化KBKeyboardHandler并设置其委托(通常是self).见MyViewController下面的示例.

KBKeyboardHandler.h:

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@protocol KBKeyboardHandlerDelegate;

@interface KBKeyboardHandler : NSObject

- (id)init;

// Put 'weak' instead of 'assign' if you use ARC
@property(nonatomic, assign) id<KBKeyboardHandlerDelegate> delegate; 
@property(nonatomic) CGRect frame;

@end
Run Code Online (Sandbox Code Playgroud)

KBKeyboardHandler.m:

#import "KBKeyboardHandler.h"
#import "KBKeyboardHandlerDelegate.h"

@implementation KBKeyboardHandler

- (id)init
{
    self = [super init];
    if (self)
    {
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(keyboardWillShow:)
                                                     name:UIKeyboardWillShowNotification
                                                   object:nil];

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

    return self;
}

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

@synthesize delegate;
@synthesize frame;

- (void)keyboardWillShow:(NSNotification *)notification
{
    CGRect oldFrame = self.frame;    
    [self retrieveFrameFromNotification:notification];

    if (oldFrame.size.height != self.frame.size.height)
    {
        CGSize delta = CGSizeMake(self.frame.size.width - oldFrame.size.width,
                                  self.frame.size.height - oldFrame.size.height);
        if (self.delegate)
            [self notifySizeChanged:delta notification:notification];
    }
}

- (void)keyboardWillHide:(NSNotification *)notification
{
    if (self.frame.size.height > 0.0)
    {
        [self retrieveFrameFromNotification:notification];
        CGSize delta = CGSizeMake(-self.frame.size.width, -self.frame.size.height);

        if (self.delegate)
            [self notifySizeChanged:delta notification:notification];
    }

    self.frame = CGRectZero;
}

- (void)retrieveFrameFromNotification:(NSNotification *)notification
{
    CGRect keyboardRect;
    [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardRect];
    self.frame = [[UIApplication sharedApplication].keyWindow.rootViewController.view convertRect:keyboardRect fromView:nil];
}

- (void)notifySizeChanged:(CGSize)delta notification:(NSNotification *)notification
{
    NSDictionary *info = [notification userInfo];

    UIViewAnimationOptions curve;
    [[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&curve];

    NSTimeInterval duration;
    [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&duration];

    void (^action)(void) = ^{
        [self.delegate keyboardSizeChanged:delta];
    };

    [UIView animateWithDuration:duration
                          delay:0.0
                        options:curve
                     animations:action
                     completion:nil];    
}

@end
Run Code Online (Sandbox Code Playgroud)

KBKeyboardHandlerDelegate.h:

@protocol KBKeyboardHandlerDelegate

- (void)keyboardSizeChanged:(CGSize)delta;

@end
Run Code Online (Sandbox Code Playgroud)

示例MyViewController.h:

@interface MyViewController : UIViewController<KBKeyboardHandlerDelegate>
...
@end
Run Code Online (Sandbox Code Playgroud)

示例MyViewController.m:

@implementation MyViewController
{
    KBKeyboardHandler *keyboard;
}

- (void)dealloc
{
    keyboard.delegate = nil;
    [keyboard release];
    [super dealloc];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    keyboard = [[KBKeyboardHandler alloc] init];
    keyboard.delegate = self;
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    keyboard.delegate = nil;
    [keyboard release];
    keyboard = nil;
}

- (void)keyboardSizeChanged:(CGSize)delta
{
    // Resize / reposition your views here. All actions performed here 
    // will appear animated.
    // delta is the difference between the previous size of the keyboard 
    // and the new one.
    // For instance when the keyboard is shown, 
    // delta may has width=768, height=264,
    // when the keyboard is hidden: width=-768, height=-264.
    // Use keyboard.frame.size to get the real keyboard size.

    // Sample:
    CGRect frame = self.view.frame;
    frame.size.height -= delta.height;
    self.view.frame = frame;
}
Run Code Online (Sandbox Code Playgroud)

更新:修复了iOS 7警告,感谢@weienv.

  • 仍然适用于iOS 7!你得到一个警告,因为[UIView animateWithDuration:...]采用UIViewAnimationOptions而不是UIViewAnimationCurve,因此更改该类型会清除隐式转换.感谢轻量级解决方案. (2认同)

Com*_*Vie 30

我刚刚解决了这个问题.解决方案是a UIKeyboardDidShowNotificationUIKeyboardDidHideNotification观察者与上述textFieldDidBeginEditing:textFieldDidEndEditing:方法的组合.

您需要三个附加变量,一个用于存储当前选定的UITextField(我已命名为activeField),一个用于指示当前视图是否已移动,另一个用于指示是否显示键盘.

这就是两个UITextField委托方法现在的样子:

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    activeField = textField;
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
    activeField = nil;
    // Additional Code
}
Run Code Online (Sandbox Code Playgroud)

加载视图时,将创建以下两个观察者:

- (void)viewDidLoad {
    // Additional Code
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWasShown:)
                                                 name:UIKeyboardDidShowNotification
                                               object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWasHidden:)
                                                 name:UIKeyboardDidHideNotification
                                               object:nil];
}
Run Code Online (Sandbox Code Playgroud)

相应的方法实现如下:

- (void)keyboardWasShown:(NSNotification *)aNotification {
    if ( keyboardShown )
        return;

    if ( ( activeField != inputAmount ) && ( activeField != inputAge ) ) {
        NSDictionary *info = [aNotification userInfo];
        NSValue *aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
        CGSize keyboardSize = [aValue CGRectValue].size;

        NSTimeInterval animationDuration = 0.300000011920929;
        CGRect frame = self.view.frame;
        frame.origin.y -= keyboardSize.height-44;
        frame.size.height += keyboardSize.height-44;
        [UIView beginAnimations:@"ResizeForKeyboard" context:nil];
        [UIView setAnimationDuration:animationDuration];
        self.view.frame = frame;
        [UIView commitAnimations];

        viewMoved = YES;
    }

    keyboardShown = YES;
}

- (void)keyboardWasHidden:(NSNotification *)aNotification {
    if ( viewMoved ) {
        NSDictionary *info = [aNotification userInfo];
        NSValue *aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
        CGSize keyboardSize = [aValue CGRectValue].size;

        NSTimeInterval animationDuration = 0.300000011920929;
        CGRect frame = self.view.frame;
        frame.origin.y += keyboardSize.height-44;
        frame.size.height -= keyboardSize.height-44;
        [UIView beginAnimations:@"ResizeForKeyboard" context:nil];
        [UIView setAnimationDuration:animationDuration];
        self.view.frame = frame;
        [UIView commitAnimations];

        viewMoved = NO;
    }

    keyboardShown = NO;
}
Run Code Online (Sandbox Code Playgroud)

此代码现在按预期工作.键盘仅在按下"完成"按钮时被解除,否则它将保持可见状态并且视图不会移动.

作为一个额外的注释,我认为有可能animationDuration通过询问NSNotification对象来动态获取,因为我已经使用了类似的解决方案,但没有让它工作(它现在做).

  • 只是给那些可能没有注意到这一点的人留言.您需要添加到.h文件:UITextField*activeField; BOOL keyboardShown; BOOL viewMoved; (3认同)
  • 好,虽然不是硬编码`animationDuration`和`animationCurve`值,但你应该使用`UIKeyboardAnimationDurationUserInfoKey`和`UIKeyboardAnimationCurveUserInfoKey` userInfo值. (2认同)