将UIKeyboardFrameEndUserInfoKey转换为视图或窗口坐标

28

对于常量UIKeyboardFrameEndUserInfoKey,在苹果文档中说:

这些坐标不考虑应用于窗口内容的任何旋转因素,这是由于界面方向更改而导致的。因此,在使用之前,您可能需要将矩形转换为窗口坐标(使用convertRect:fromWindow:方法)或视图坐标(使用convertRect:fromView:方法)。

因此,如果我使用[view1 convertRect:rect fromView:view2]

为了正确转换旋转值,我应该在上面的参数中插入什么? 即:

view1 =? rect =?(我假设是键盘框架) view2 =?

一直在尝试一些东西并且得到了一些有趣的结果。


希望下面的答案有所帮助。我之前也使用过 convertRect,但是以下代码更加简洁,我个人认为。 - memmons
显然,我不同意@Answerbot关于什么是“更干净”的观点 :) 顺便说一句,我展示的代码来自我的书,你可能会想在这个问题上进行咨询:http://www.apeth.com/iOSBook/ch23.html#_summoning_and_dismissing_the_keyboard - matt
@matt 很不错的东西。我曾经读过你的一本书,真的很喜欢它。我停止使用 convertRect 的原因是因为它只在一个视图控制器内部的 myView(表示最顶层的视图)中非常有效。然而,如果您在一个 UITextField 子类中监听通知,则使用 self 进行转换并不特别有帮助。 - memmons
3个回答

69

第一个视图应该是您的视图。第二个视图应该为nil,表示窗口/屏幕坐标。因此:

NSDictionary* d = [notification userInfo];
CGRect r = [d[UIKeyboardFrameEndUserInfoKey] CGRectValue];
r = [myView convertRect:r fromView:nil];

现在你有了键盘在视图中占据的矩形区域。如果你的视图是当前视图控制器的视图(或其子视图),则旋转等已经考虑在内。


4
+1,但我认为只有当myView代表视图控制器的根视图时,这才有用。但如果不是这种情况,而是像{100,100,200,200}这样的子视图,会怎样呢? - memmons
1
请注意,您还需要将矩形转换为窗口坐标系!@david-m-syzdek的答案更加完整。 - Diogo T
1
@DiogoTridapalli 只是为了明确,在iOS 8中,无需通过窗口坐标系转换来处理横向方向的情况,因为整个应用程序都会旋转,键盘也随之旋转。 - matt
@matt 我还没有在iOS 8上进行测试,但是为了支持旧版本的iOS,您需要转换矩形。 - Diogo T
1
绝妙的技巧。令人沮丧的是,我在App Store上有一个应用程序,它没有使用ConvertRect函数。在iOS 8出现之前,一切都很正常。现在,随着iOS 8的到来,我的横向iPad应用程序突然报告键盘高度为1024像素,并破坏了我的屏幕。因此,即使这个技巧已经有一年半的历史了,现在比以往任何时候都更加重要!! - Mike Gledhill
似乎如果您想要键盘的高度,最好不要使用框架的高度,而是屏幕高度减去框架原点的y坐标。即使键盘正在消失,它也具有非零高度,但其框架的原点将是屏幕的底部边缘。 - BallpointBen

23

我尝试了被接受的回答,发现它实际上并没有提供键盘在视图中的CGRect。 我发现我必须将CGRect从UIScreen对象转换为UIWindow对象,然后从UIWindow对象转换为UIView对象:


我尝试了被接受的回答,但发现它实际上没有提供键盘在视图中的CGRect。我发现需要将CGRect从UIScreen对象转换为UIWindow对象,再从UIWindow对象转换为UIView对象:
NSValue * keyboardEndFrame;
CGRect    screenRect;
CGRect    windowRect;
CGRect    viewRect;

// determine's keyboard height
screenRect    = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
windowRect    = [self.view.window convertRect:screenRect fromWindow:nil];
viewRect      = [self.view        convertRect:windowRect fromView:nil];

我使用上述代码来调整根视图的大小,以避免被键盘遮挡:

NSTimeInterval  duration;
CGRect          frame;

// determine length of animation
duration  = [[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];

// resize the view
frame              = self.view.frame;
frame.size.height -= viewRect.size.height;

// animate view resize with the keyboard movement
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:duration];
self.view.frame = frame;
[UIView commitAnimations];

我也遇到了这种情况。所以,从现在开始我会这样做。 - ma11hew28
我正在使用这种方法,但在横向方向上获取的视图矩形的 origin 值明显不正确(负的 y)。在您的解决方案中,您仅使用了 height。您的 origin 是否包含正确的 y 值? - burax
@ma11hew28 我猜测你指的是iPad Pro的缩放问题?哦,等等,抱歉,那是个时代错误。 - Dan Rosenstark
@DanRosenstark,抱歉,我不记得了。现在,我可能会用不同的方法来做事情。例如,我从UIResponder.keyboardWillShowNotification获取keyboardHeightEnd,代码如下:let rect = (notification.userInfo![UIResponder.keyboardFrameEndUserInfoKey]! as! NSValue).cgRectValue; let keyboardHeightEnd = view.convert(rect, from: nil).size.height - ma11hew28
@ma11hew28 看起来像是一种稍微不同的语言 ;) 但没错,一切都好。我只是想知道是否对所有设备(包括长宽比奇怪的11英寸iPad Pro)都正确地选择了缩放因子。 - Dan Rosenstark
@DanRosenstark,是的,我现在使用Swift。我不知道。我没有测试过,但我想象我上一条评论中的代码(特别是UIView实例方法convert(_:from:))对于所有设备/比例都能正常工作。如果它不能正常工作,我觉得那将是一个UIKit的错误。 - ma11hew28

1
+ (void)parseKeyboardNotification:(NSNotification *)notification
                 inRelationToView:(UIView *)view
                             info:(void(^)(NSTimeInterval keyboardAnimationDuration, CGRect keyboardFrameInView, UIViewAnimationOptions keyboardAnimationOptions))callback
{
    NSParameterAssert(notification != nil);
    NSParameterAssert(view != nil);

    NSDictionary *userInfo = [notification userInfo];

    UIViewAnimationCurve animationCurve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
    UIViewAnimationOptions animationOption = animationCurve << 16; // https://devforums.apple.com/message/878410#878410
    NSTimeInterval animationDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];

    // https://dev59.com/zGUp5IYBdhLWcg3wRF2b#16615391
    CGRect screenRect    = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    CGRect windowRect    = [view.window convertRect:screenRect fromWindow:nil];
    CGRect viewRect      = [view        convertRect:windowRect fromView:nil];

    callback(animationDuration, viewRect, animationOption);
}

可以这样使用。
- (void)keyboardWillShowOrHide:(NSNotification *)notification
{    
    [AGKeyboardInfo parseKeyboardNotification:notification inRelationToView:self.view info:^(NSTimeInterval keyboardAnimationDuration, CGRect keyboardFrameInView, UIViewAnimationOptions keyboardAnimationOptions) {

        [UIView animateWithDuration:keyboardAnimationDuration delay:0 options:keyboardAnimationOptions animations:^{

             // do any modifications to your views

        } completion:nil];
    }];
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接