UIKeyboardBoundsUserInfoKey is deprecated, what to use instead?

IosObjective CIphoneUikeyboardIphone Sdk-3.2

Ios Problem Overview


I'm working on an iPad app using 3.2 sdk. I'm dealing with obtaining the keyboard size to prevent my textfields from hidding behind it.

I'm getting a Warning in Xcode -> UIKeyboardBoundsUserInfoKey is deprecated what should I use instead not to get this warning?

Ios Solutions


Solution 1 - Ios

I played with the previously offered solution but still had issues. Here's what I came up with instead:

    - (void)keyboardWillShow:(NSNotification *)aNotification {
	[self moveTextViewForKeyboard:aNotification up:YES];
}

    - (void)keyboardWillHide:(NSNotification *)aNotification {
    	[self moveTextViewForKeyboard:aNotification up:NO];	
    }

- (void) moveTextViewForKeyboard:(NSNotification*)aNotification up: (BOOL) up{
NSDictionary* userInfo = [aNotification userInfo];

// Get animation info from userInfo
NSTimeInterval animationDuration;
UIViewAnimationCurve animationCurve;

CGRect keyboardEndFrame;

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


[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];


// Animate up or down
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:animationCurve];

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

newFrame.origin.y -= keyboardFrame.size.height * (up? 1 : -1);
textView.frame = newFrame;

[UIView commitAnimations];
}

Solution 2 - Ios

From the documentation for UIKeyboardBoundsUserInfoKey:

> The key for an NSValue object containing a CGRect that identifies the bounds rectangle of the keyboard in window coordinates. This value is sufficient for obtaining the size of the keyboard. If you want to get the origin of the keyboard on the screen (before or after animation) use the values obtained from the user info dictionary through the UIKeyboardCenterBeginUserInfoKey or UIKeyboardCenterEndUserInfoKey constants. Use the UIKeyboardFrameBeginUserInfoKey or UIKeyboardFrameEndUserInfoKey key instead.

Apple recommends implementing a convenience routine such as this (which could be implemented as a category addition to UIScreen):

+ (CGRect) convertRect:(CGRect)rect toView:(UIView *)view {
    UIWindow *window = [view isKindOfClass:[UIWindow class]] ? (UIWindow *) view : [view window];
    return [view convertRect:[window convertRect:rect fromWindow:nil] fromView:nil];
}

to recover window-adjusted keyboard frame size properties.

I took a different approach, which involves checking the device orientation:

CGRect _keyboardEndFrame;
[[notification.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] getValue:&_keyboardEndFrame];
CGFloat _keyboardHeight = ([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortrait || [[UIDevice currentDevice] orientation] == UIDeviceOrientationPortraitUpsideDown) ? _keyboardEndFrame.size.height : _keyboardEndFrame.size.width;

Solution 3 - Ios

You simply use this code:

//NSVale *aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
//instead of Upper line we can use either next line or nextest line.
//NSValue *aValue = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
NSValue *aValue = [info objectForKey:UIKeyboardFrameBeginUserInfoKey];

Solution 4 - Ios

The following code fixes an issue in Jay's answer, which assumes that UIKeyboardWillShowNotification will not fire again when the keyboard is already present.

When typing with the Japanese/Chinese keyboard, iOS fires an extra UIKeyboardWillShowNotification with the new keyboard frame even though the keyboard is already present, leading to the height of the self.textView being reduced a second time in the original code.

This reduces self.textView to almost nothing. It then becomes impossible to recover from this problem since we will only expect a single UIKeyboardWillHideNotification the next time the keyboard is dismissed.

Instead of subtracting/adding height to self.textView depending on whether the keyboard is shown/hidden as in the original code, the following code just calculates the maximum possible height for self.textView after subtracting the height of the keyboard on screen.

This assumes that self.textView is suppose to fill the entire view of the view controller, and there's no other subview that needs to be visible.

- (void)resizeTextViewWithKeyboardNotification:(NSNotification*)notif {
	
    NSDictionary* userInfo = [notif userInfo];
    NSTimeInterval animationDuration;
    UIViewAnimationCurve animationCurve;
    CGRect keyboardFrameInWindowsCoordinates;
	
	[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
	[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
	[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrameInWindowsCoordinates];
	
	[self resizeTextViewToAccommodateKeyboardFrame:keyboardFrameInWindowsCoordinates
							 withAnimationDuration:animationDuration
									animationCurve:animationCurve];
	
}

- (void)resizeTextViewToAccommodateKeyboardFrame:(CGRect)keyboardFrameInWindowsCoordinates
						   withAnimationDuration:(NSTimeInterval)duration
								  animationCurve:(UIViewAnimationCurve)curve
{
	
	CGRect fullFrame = self.view.frame;
	
	CGRect keyboardFrameInViewCoordinates =
	[self.view convertRect:keyboardFrameInWindowsCoordinates fromView:nil];
	
	// Frame of the keyboard that intersects with the view. When keyboard is
	// dismissed, the keyboard frame still has width/height, although the origin
	// keeps the keyboard out of the screen.
	CGRect keyboardFrameVisibleOnScreen =
	CGRectIntersection(fullFrame, keyboardFrameInViewCoordinates);
	
	// Max frame availble for text view. Assign it to the full frame first
	CGRect newTextViewFrame = fullFrame;
	
	// Deduct the the height of any keyboard that's visible on screen from
	// the height of the text view
	newTextViewFrame.size.height -= keyboardFrameVisibleOnScreen.size.height;
	
	if (duration)
	{
		[UIView beginAnimations:nil context:nil];
		[UIView setAnimationDuration:duration];
		[UIView setAnimationCurve:curve];
	}
	
	// Adjust the size of the text view to the new one
    self.textView.frame = newTextViewFrame;
	
	if (duration)
	{
		[UIView commitAnimations];
	}
	
}

Also, don't forget to register the keyboard notifications in viewDidLoad:

- (void)viewDidLoad
{
    [super viewDidLoad];
	NSNotificationCenter* notifCenter = [NSNotificationCenter defaultCenter];
	
	[notifCenter addObserver:self selector:@selector(resizeTextViewWithKeyboardNotification:) name:UIKeyboardWillShowNotification object:nil];
    [notifCenter addObserver:self selector:@selector(resizeTextViewWithKeyboardNotification:) name:UIKeyboardWillHideNotification object:nil];
}

About splitting the resizing code into two parts

The reason why the textView resizing code is split into two parts (resizeTextViewWithKeyboardNotification: and resizeViewToAccommodateKeyboardFrame:withAnimationDuration:animationCurve:) is to fix another issue when the keyboard persists through a push from one view controller to another (see How do I detect the iOS keyboard when it stays up between controllers?).

Since the keyboard is already present before the view controller is pushed, there's no additional keyboard notifications being generated by iOS, and thus no way to resize the textView based on those keyboard notifications.

The above code (as well as the original code) that resizes self.textView will thus only work when the keyboard is shown after the view has been loaded.

My solution is to create a singleton that stores the last keyboard coordinates, and on - viewDidAppear: of the viewController, call:

- (void)viewDidAppear:(BOOL)animated
{
	[super viewDidAppear:animated];
	
	// Resize the view if there's any keyboard presence before this
	// Only call in viewDidAppear as we are unable to convertRect properly
	// before view is shown
	[self resizeViewToAccommodateKeyboardFrame:[[UASKeyboard sharedKeyboard] keyboardFrame]
						 withAnimationDuration:0
								animationCurve:0];
}

UASKeyboard is my singleton here. Ideally we should call this in - viewWillAppear:, however in my experience (at least on iOS 6), the convertRect:fromView: method that we need to use in resizeViewToAccommodateKeyboardFrame:withAnimationDuration:animationCurve: does not properly convert the keyboard frame to the view coordinates before the view is fully visible.

Solution 5 - Ios

Just use the UIKeyboardFrameBeginUserInfoKey or UIKeyboardFrameEndUserInfoKey key instead of UIKeyboardBoundsUserInfoKey

Solution 6 - Ios

@Jason, you code if fine except for one point.

At the moment you are not actually animating anything and the view will simply `pop' to its new size.height.

You have to specify a state from which to animate. An animation is a sort of (from state)->(to state) thing.

Luckily there is a very convenient method to specify the current state of the view as the (from state).

[UIView setAnimationBeginsFromCurrentState:YES];

If you add that line right after beginAnimations:context: your code works perfectly.

Solution 7 - Ios

- (CGSize)keyboardSize:(NSNotification *)aNotification {
    NSDictionary *info = [aNotification userInfo];
    NSValue *beginValue = [info objectForKey:UIKeyboardFrameBeginUserInfoKey];

    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];

    CGSize keyboardSize;
    if ([UIKeyboardDidShowNotification isEqualToString:[aNotification name]]) {
        _screenOrientation = orientation;
        if (UIDeviceOrientationIsPortrait(orientation)) {
            keyboardSize = [beginValue CGRectValue].size;
        } else {
            keyboardSize.height = [beginValue CGRectValue].size.width;
            keyboardSize.width = [beginValue CGRectValue].size.height;
        }
    } else if ([UIKeyboardDidHideNotification isEqualToString:[aNotification name]]) {
        // We didn't rotate
        if (_screenOrientation == orientation) {
            if (UIDeviceOrientationIsPortrait(orientation)) {
                keyboardSize = [beginValue CGRectValue].size;
            } else {
                keyboardSize.height = [beginValue CGRectValue].size.width;
                keyboardSize.width = [beginValue CGRectValue].size.height;
            }
        // We rotated
        } else if (UIDeviceOrientationIsPortrait(orientation)) {
            keyboardSize.height = [beginValue CGRectValue].size.width;
            keyboardSize.width = [beginValue CGRectValue].size.height;
        } else {
            keyboardSize = [beginValue CGRectValue].size;
        }
    }
      

    return keyboardSize;
}

Solution 8 - Ios

Solution 9 - Ios

Its worked like this

This is the constraint of the save button bottom

@IBOutlet weak var saveBtnBottom: NSLayoutConstraint!
@IBOutlet weak var nameText: UITextField!

Inside viewDidLoad

NotificationCenter.default.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyBoardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
nameText.delegate = self

This is the functions we need

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    nameText.resignFirstResponder()
    return true
}


@objc func keyBoardWillShow(notification: Notification){
    if let userInfo = notification.userInfo as? Dictionary<String, AnyObject>{
        let frame = userInfo[UIResponder.keyboardFrameEndUserInfoKey]
        let keyBoardRect = frame?.cgRectValue
        if let keyBoardHeight = keyBoardRect?.height {
            self.saveBtnBottom.constant = keyBoardHeight 
            
            UIView.animate(withDuration: 0.5, animations: {
                self.view.layoutIfNeeded()
            })
        }
    }
}

@objc func keyBoardWillHide(notification: Notification){
    self.saveBtnBottom.constant = 30.0
    UIView.animate(withDuration: 0.5, animations: {
        self.view.layoutIfNeeded()
    })
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMikewareView Question on Stackoverflow
Solution 1 - IosJayView Answer on Stackoverflow
Solution 2 - IosAlex ReynoldsView Answer on Stackoverflow
Solution 3 - IosiOS_UserView Answer on Stackoverflow
Solution 4 - IosjunjieView Answer on Stackoverflow
Solution 5 - IosAnand MishraView Answer on Stackoverflow
Solution 6 - IosThomasView Answer on Stackoverflow
Solution 7 - IosCameron Lowell PalmerView Answer on Stackoverflow
Solution 8 - IoskarimView Answer on Stackoverflow
Solution 9 - IosMohammed AbunadaView Answer on Stackoverflow