How to measure position of UI objects on iPhone simulator screen - iphone

I have calculated positions and offsets on paper. I have written code and debugged that I get expected results. However on iPhone Simulator things are overlapping by about 15 pixels.
To go on with my debugging, I need to know where exactly UI objects are on the screen.
Related to popup search keyboard and resizing a UITableView between static UISearchBar and dynamically added UITabBar (table view is embedded into one of the tabs). No, I really don't want to use any hardcoded values due rotation and different screen sizes.
How can I find out this?
- (void)keyboardWasShown:(NSNotification*)aNotification
{
if (self.keyboardShown)
return;
// Get keyboard dimensions
NSDictionary* info = [aNotification userInfo];
NSValue* aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
CGSize kbSize = [aValue CGRectValue].size;
NSValue* endValue = [info objectForKey:UIKeyboardCenterEndUserInfoKey];
CGPoint endCenter = [endValue CGPointValue];
CGRect frame = myView.frame;
frame.size.height = endCenter.y - kbSize.height/2 - frame.origin.y;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.2];
myView.frame = frame;
[UIView commitAnimations];
self.keyboardShown = YES;
}
...code just in case there're obvious bugs I can't see anymore...

Your best bet is probably UIView's convertPoint:toView:. To find out where a given view is located, use:
CGPoint myPoint = [myView convertPoint:CGPointMake(0, 0) toView:((YourAppDelegate *)[[UIApplication sharedApplication] delegate]).window];

Related

Convert UIKeyboardFrameEndUserInfoKey to View or Window Coordinates

For the constant UIKeyboardFrameEndUserInfoKey, in the Apple docs it says:
These coordinates do not take into account any rotation factors
applied to the window’s contents as a result of interface orientation
changes. Thus, you may need to convert the rectangle to window
coordinates (using the convertRect:fromWindow: method) or to view
coordinates (using the convertRect:fromView: method) before using it.
So if I use [view1 convertRect:rect fromView:view2]
What would I insert for the above parameters to get it to convert the rotation values correctly? ie:
view1 = ?
rect = ? (the keyboard frame I'm assuming)
view2 = ?
Been trying some things and getting some funny stuff.
The first view should be your view. The second view should be nil, meaning window/screen coordinates. Thus:
NSDictionary* d = [notification userInfo];
CGRect r = [d[UIKeyboardFrameEndUserInfoKey] CGRectValue];
r = [myView convertRect:r fromView:nil];
Now you have the rect that the keyboard will occupy, in terms of your view. If your view is the current view controller's view (or a subview thereof), rotation and so forth are now accounted for.
I tried the accepted answer and found that it does not actually provide the CGRect of the keyboard within the view. I found that I have to convert the CGRect from the UIScreen object to the UIWindow object, and from the UIWindow object to the UIView object:
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];
I use the above to resize the root view to not be hidden by the keyboard:
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];
+ (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];
// http://stackoverflow.com/a/16615391/202451
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);
}
Can be used like this
- (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];
}];
}

NSNotificationCenter

Right now I'm trying to write a function to moving up frame when keyboard appears on the screen.
I started to use NSNNotificationCenter. My code is working but not correctly. When keyboard appears my formView is moving up but when I start to edit next textField in formView, formView is moving up again. What's wrong with my code?
Thanks.
- (void)keyboardWillShow:(NSNotification *) aNotification {
NSDictionary *userInfo = [aNotification userInfo];
CGRect frame = self.formView.frame;
frame.origin.y -= 170;
NSValue *animationDurationValue = [userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey];
NSTimeInterval animationDuration;
[animationDurationValue getValue:&animationDuration];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationDuration];
formView.frame = frame;
[UIView commitAnimations];
}
You should add again your 170 pixels (or whatever you calculate as suggested by Mike) to the origin.y of your view when the keyboard is disappearing. When you click on another text field, technically the current keyboard will disappear (your view does not react in any way) and a new keyboard will appear (your keyboardWillShow will be called again and you shift your view again up by 170 px).

UITableView won't scroll after keyboard is hidden

I have a UITableView with custom cells. Based on an answer to this question I am resizing the view when to accommodate the keyboard and resizing when the keyboard is dismissed. After dismissing the keyboard the table view no longer scrolls.
These are the methods called when showing and hiding the keyboard:
-(void)keyboardWillShow:(NSNotification *)note
{
NSDictionary* userInfo = [note userInfo];
NSValue* keyboardFrameValue = [userInfo objectForKey:#"UIKeyboardBoundsUserInfoKey"];
if (!keyboardFrameValue) {
keyboardFrameValue = [userInfo objectForKey:#"UIKeyboardFrameEndUserInfoKey"];
}
// Reduce the tableView height by the part of the keyboard that actually covers the tableView
CGRect windowRect = [[UIApplication sharedApplication] keyWindow].bounds;
CGRect viewRectAbsolute = [myTableView convertRect:myTableView.bounds toView:[[UIApplication sharedApplication] keyWindow]];
CGRect frame = myTableView.frame;
frame.size.height -= [keyboardFrameValue CGRectValue].size.height - CGRectGetMaxY(windowRect) + CGRectGetMaxY(viewRectAbsolute);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
myTableView.frame = frame;
[UIView commitAnimations];
UITableViewCell *textFieldCell = (id)((UITextField *)self.textFieldBeingEdited).superview.superview;
NSIndexPath *textFieldIndexPath = [myTableView indexPathForCell:textFieldCell];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[myTableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
-(void)keyboardWillHide:(NSNotification *)note
{
CGRect keyboardRect = [[[note userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
NSTimeInterval animationDuration = [[[note userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
CGRect frame = self.myTableView.frame;
frame.size.height += keyboardRect.size.height + 49;
[UIView beginAnimations:#"ResizeForKeyboard" context:nil];
[UIView setAnimationDuration:animationDuration];
self.myTableView.frame = frame;
[UIView commitAnimations];
myTableView.scrollEnabled = YES;
}
Any ideas what I am missing?
I've used the same question you link to to solve the same problem. It's been working great for me although I don't remember how much of the original code I ended up using.
For your problem, (though I imagine you've tried this) the first thing that comes to mind is to look in your code to see if your doing
self.tableView.scrollEnabled = NO;
and if so you should verify that you have a corresponding statement somewhere sets it back to YES; in fact you might set scrollEnabled to YES in keyboardWillHide just to test if that helps.
The problematic line is:
frame.size.height += keyboardRect.size.height + 49;
it should be:
frame.size.height += keyboardRect.size.height - self.navigationController.toolbar.frame.size.height;

Handling keyboard show/hide & orientation changes

I use the following method to resize the view after keyboard show/hide:
- (void)moveViewForKeyboard:(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 = self.view.frame;
CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil];
newFrame.size.height += (up? -1 : 1) * keyboardFrame.size.height;
self.view.frame = newFrame;
keyboardUp = up;
[UIView commitAnimations];
}
This works very good, until the screen orientation changes. What happens then is that the method resizes the view correctly, but after this method returns - something else resizes the view again to the maximum height of the screen.
Any ideas?
First you'll need to figure out what is resizing the view again. I would recommend putting a breakpoint into the above method so when it gets called that 2nd, unwanted time, you can look at the method call stack and see what is causing the 2nd call. Then you'll be able to stop it from happening, or change your code so that it doesn't cause a problem.

UITextView not resizing along woth keyboard showing

I have a text view (nib-file) with a UITextView maximised the entire view.
When ever i push the viewcontroller on to the navigationcontroller i load the keyboard in the text view's viewDidLoad method.
My problem is that when i write text that has more lines than there is space for, the text is behind the keyboard. I followed the sampe from Apple here.
If i log the "newTextViewFrame" it's NULL. but if i debug the thing it has a fine value.
Even if i hardcode it, the UITextView doesent change size.
Here is my code:
- (void)keyboardWillShow:(NSNotification *)notification {
/*
Reduce the size of the text view so that it's not obscured by the keyboard.
Animate the resize so that it's in sync with the appearance of the keyboard.
*/
NSDictionary *userInfo = [notification userInfo];
// Get the origin of the keyboard when it's displayed.
NSValue* aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
// Get the top of the keyboard as the y coordinate of its origin in self's view's coordinate system. The bottom of the text view's frame should align with the top of the keyboard's final position.
CGRect keyboardRect = [aValue CGRectValue];
keyboardRect = [self.view convertRect:keyboardRect fromView:nil];
CGFloat keyboardTop = keyboardRect.origin.y;
CGRect newTextViewFrame = self.view.bounds;
newTextViewFrame.size.height = keyboardTop; - (self.view.bounds.origin.y);
// Get the duration of the animation.
NSValue *animationDurationValue = [userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey];
NSTimeInterval animationDuration;
[animationDurationValue getValue:&animationDuration];
// Animate the resize of the text view's frame in sync with the keyboard's appearance.
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:animationDuration];
createListingTextView.frame = newTextViewFrame;
[UIView commitAnimations];
NSLog(#"Textview resized: %#", newTextViewFrame);
}
My task i simple: Implement a View with a UITextView and the keyboard open and scroll the UITextView when ever the text exceeds.
Please advice...
There is a semi-colon after keyboardTop on the line
newTextViewFrame.size.height = keyboardTop; - (self.view.bounds.origin.y);
This would certainly prevent newTextViewFrame from getting the adjusted height.