popover shrinks to upper left corner when keyboard appears on iPad - ios

I'm working on an app that has some forms that are contained within a popover. My problem is that when I click on any text entry field and the keyboard appears, the popover shrinks to the upper left hand corner of the screen (0,0) and you can't see the fields you are entering into. When you click the hide keyboard button, the popover returns to its normal size and position.
Is there any way to prevent popover resizing when the keyboard appears?
Here are screen shots in case my description is inadequate.
edit: Here is the code for how the popover is presented on the screen:
(void)displayPopoverForOrientation:(UIInterfaceOrientation)orientation {
if ([Utilities getAppDelegate].menuPopover) {
CGRect rect = CGRectMake(0, 0, 0, 0);
if (orientation == UIInterfaceOrientationLandscapeLeft ||
orientation == UIInterfaceOrientationLandscapeRight) {
if (self.currentPopover == RESERVATIONS_POPOVER) {
rect = CGRectMake(365, 0, 0, 0);
} else if (self.currentPopover == ACCOUNT_POPOVER) {
rect = CGRectMake(600, 0, 0, 0);
} else if (self.currentPopover == RESORTS_POPOVER) {
rect = CGRectMake(0, 0, 0, 0);
}
}
[[Utilities getAppDelegate].menuPopover presentPopoverFromRect:rect
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionUp
animated:YES];
}
}
Inside the popover is a separate view controller with the layout setup in a xib.
Feel free to ask any questions, I'm fairly new to iOS and Objective C, but I'm not new to coding so I'll do my best to clarify.
edit 2:
I've found that this only occurs in iOS 5. In older versions of iOS, the popover merely collapses vertically until there is enough room for the keyboard. Any ideas about about why this behavior has changed in iOS 5?

It's not possible to prevent the popover from changing shape when the keyboard appears. According to Apple's UIPopoverController docs:
The size you specify is just the preferred size for the popover’s view. The actual size may be altered to ensure that the popover fits on the screen and does not collide with the keyboard.
IOS will try to move it out of the way, but it may not be possible, depending on where you are presenting it from. For best results, present it from a bar button or from inside a scroll view (UITableView is ok), and design your UI so the popover is not too large (if the popover covers most of the screen, perhaps you should consider a segue to a modal view or similar). I found I needed to draw some diagrams so I could understand the layout better before I could get a solution that worked in all cases.

Actually it is very possible!... This is to do with the auto-Resizing that is ticked by default in IB:
I posted this on an another forum with similar question,.. The solution for me was to:
1) Go into the designer by
2) Opening the XIB ViewController that is causing the problem (i.e. the PopOver one).
3) Click to select it's VIEW.
4) Uncheck "AutoResizeSubviews"
5) When loading the PopOver in code, make sure you do:
6) Your_Popup_Window.popoverContentSize = Your_ViewController.view.bounds.size;
I hope this helps.
Kind Regards
Heider Sati

Related

iOS UITextField Outside of SuperView not Responding to Touches

I'm building an iOS 8 app that makes use of the new hidesBarsOnSwipe property on UINavgitationController to hide the nav bar while scrolling. At the same time that the nav bar hides, I'm also programmatically hiding the tab bar. On top of the tab bar, there is a text field which lets users comment on a post (much like Facebook). When the tab bar is hidden (by moving it downward and off the screen), the text field is moved down as well, so that it now sits at the bottom of the screen and so that there's no gap between the bottom of the screen and the text field.
So, things look great. But, turns out that the text field doesn't respond to touch events when it moves to the bottom of the screen. I did some digging and it appears that the reason is because the text field is outside of its superview (the view controller's view), and so touch events will not be sent to the text field.
So I think I've figured out why the issue is occurring, but I haven't yet figured out how to fix it. I've tried messing with hitTest:withEvent: and pointInside:withEvent: but didn't have any luck. Anyone have any solutions?
EDIT: Here is some code to make the question clearer (hopefully). When the nav controller's barHideOnSwipeGestureRecognizer is called, I am running the following code:
- (void)barHideSwipeGestureActivated:(UIPanGestureRecognizer*)gesture
{
[self animateTabBarUpOrDown:self.navigationController.navigationBar.frame.origin.y >= 0 completion:nil];
}
The method above is the following:
- (void)animateTabBarUpOrDown:(BOOL)up completion:(void (^)(void))completionBlock
{
if(!self.animatingTabBar && self.tabbarIsUp != up)
{
self.animatingTabBar = YES;
//to animate the tabbar up, reset the comments bottom constraint to 0 and set the tab bar frame to it's original place
//to animate the tabbar down, move its frame down by its height. set comments bottom constraint to the negative value of that height.
[UIView animateWithDuration:kTabBarAnimationDuration animations:^{
UITabBar *tabBar = self.tabBarController.tabBar;
if(up)
{
tabBar.frame = CGRectMake(tabBar.frame.origin.x, tabBar.frame.origin.y - tabBar.frame.size.height, tabBar.frame.size.width, tabBar.frame.size.height);
self.addCommentViewToBottomConstraint.constant = 0.0f;
}
else
{
tabBar.frame = CGRectMake(tabBar.frame.origin.x, tabBar.frame.origin.y + tabBar.frame.size.height, tabBar.frame.size.width, tabBar.frame.size.height);
self.addCommentViewToBottomConstraint.constant = -tabBar.frame.size.height;
}
} completion:^(BOOL finished) {
self.tabbarIsUp = up;
self.animatingTabBar = NO;
if(completionBlock)
{
completionBlock();
}
}];
}
}
Ok, finally found a solution for this one. I haphazardly messed around with changing the bounds of my view controller's view, but that was too hacky and ultimately didn't accomplish what I wanted it to.
What I ended up doing was changing my view controller's edgesForExtendedLayout property to be equal to UIRectEdgeAll which basically says that the view should take up the entire screen, and extend above top bars / below bottom bars.
I had to hack around a little bit with changing auto layout constraints on my text field so that it appeared in the right place at the right time, but overall, the solution was changing edgesForExtendedLayout to be UIRectEdgeAll - this makes the view take up the entire screen, so the text field is now still in the super view even when it animates downward, thus, allowing it to still receive touch events.

Finding The Center of a View

I have an iphone app with 2 ViewControllers . Both screens(viewcontrollers) show a loading screen. I create the loading screen programmatically:
UIView *loadingScreen = [[UIView alloc] initWithFrame:CGRectMake(100,200,144,144)];
loadingScreen.center = CGPointMake(self.view.frame.size.width / 2.0, self.view.frame.size.height / 2.0);
//{.. Other customizations to loading screen
// ..}
[self.view addSubview:loadingScreen];
For some reason, the second viewcontroller's loadingScreen is significantly lower and it isn't centered on the screen. The first viewcontroller works perfectly and is dead center like I want.
The second viewcontroller is a UITableView and it shows the uinavigationbar, whereas the first viewcontroller doesn't show the uinavigationbar. Also, I use storyboard for my app.
I've outputted to the NSLog self.view.frame.size.height and loadingScreen.center in both instances and THEY HAVE THE SAME COORDINATES! So, not sure why it is showing up lower. Any ideas why the second loadingScreen is lower and how to fix? Thanks!
You mention that one screen displays a UINavigationBar while the other does not. When you display a navigation bar, it offsets the rest of your view - in this case by shifting it down.
There are two quick fixes. You can either adjust your center point up by the size of the UINavigationBar (65 pts - unless it's a custom UINavigationBar and you've changed its size) or you can set the "Adjust Scroll View Insets" value to false in the attributes inspector.
The latter is probably the easiest and comes most recommended. Note though, that the top of your UITableView will now be underneath the UINavigationBar.
My final note would be that if you wanted to do it programmatically than in your UITableView's delegate you can call
- (BOOL)automaticallyAdjustsScrollViewInsets
{
return NO;
}

iOS Sizing UITableView During Orientation Change

I have a view that has two tables. In the story board, I have two separate views, one horizontal and the other vertical. When I need to navigate to the view, the code detects the orientation and brings up the appropriate view (and does so on an orientation change.
I have the following code in my method:
-(void)viewDidAppear:(BOOL)animated{
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if(orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight){
if(tableHeight2 > 324){
tableHeight2 =325;
}
table1.frame = CGRectMake(table1.frame.origin.x, table1.frame.origin.y, table1.frame.size.width, tableHeight1);
table2.frame = CGRectMake(table2.frame.origin.x, table1.frame.origin.y + 20 + tableHeight1, table2.frame.size.width, tableHeight2);
}else {
if(tableHeight2 > 500){
tableHeight2 = 500;
}
table1.frame = CGRectMake(table1.frame.origin.x, table1.frame.origin.y, table1.frame.size.width, tableHeight1);
table2.frame = CGRectMake(table2.frame.origin.x, table1.frame.origin.y + 50 + tableHeight1, table2.frame.size.width, tableHeight2);
}
}
This works wonderfully when I press a button to navigate to the view. It adds up all of the cell heights and makes the first table the appropriate height, then moves the second table 50 pixels below the first table. It also makes sure the second table doesn't extend beyond the visible screen area.
When the orientation changes, I the following code is executed:
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration {
InitViewController *ini;
ini = [self.storyboard instantiateViewControllerWithIdentifier:#"Init"];
ini.location = MenuName;
[self presentViewController:ini animated:NO completion:nil];
}
This should do the same thing that pressing a barbuttonitem does: change to InitViewController while sending the StoryboardID to it in the ini.location variable. The code for the navigation buttons is pretty much identical to the code in willAnimateRotationToInterfaceOrientation. InitViewController then determines the orientation and sends the app to the correct storyboard UIView.
It does send it to the right view, I can tell based on the table widths. What it doesn't do is change the height of the first (top) table, table1. The first table retains the size it was given in the storyboard.
If there is area of code you think I need to post to get a better picture, let me know I'll be happy to add it. Any help, insight, or even just trial-and-error suggestions would be appreciated.
*Note: I have tried to change willAnimateRotationToInterfaceOrientation to ViewDidLayoutSubviews, to not effect.
Well, it seems a very small change fixed it. I noticed that the code on the navigation buttons had YES under "animate" for the view change, and the willAnimateRotationToInterfaceOrientation "animated:NO". I changed it to "YES" and that fixed it. Not sure why yet, perhaps it affects how the method displays the view or affects the load order, but there it is.

Black bar flashes at top of UITableView when pushing to view with "Hides Bottom Bar When Pushed" in IB

This is a weird error that may just be an issue in Xcode for all I know. I have a tab bar controller where the first view is a UITableView with (obviously) a number of cells. When you select a cell, I've set up a segue on the MainStoryboard to go to a detail view controller. I want the tab bar to be hidden when I go to the detail view, so I went into the storyboard, chose my detail view, and clicked "Hides Bottom Bar on Push" in the editor screen that starts with "Simulated Metrics."
Everything works just fine, except that when I tap on a cell, a black bar flashes at the top of the UITableView screen, dropping the tableview cells down (as if the cells are falling down below the tab bar at the bottom), just before the screen pushes over to the detail view. The effect isn't harmful at all, but it's very disconcerting, and I'd like to smooth that out.
The only fix I've found is to uncheck the "Hides Bottom Bar when Pushed" option on the storyboard. That indeed does get rid of that black bar flash, but of course the tab bar stays on the screen when I go to the detail view, which is what I don't want.
Any ideas?
Just for completeness' sake, I went ahead and ran
[self.navigationController setToolbarHidden:YES animated: YES];
on the detail view controller's viewWillAppear method (and even tried it with the storyboard option both on and off), but there was no difference. The toolbar did indeed hide just fine, but I still got that black line at the top. So weird.
I know it is too late !!! I ran into same issue. It seems like the Auto resizing mask for the view was incorrect to be exact the UIViewAutoresizingFlexibleTopMargin. I checked this on in the xib file. If you are trying to do it in code make sure this flag -UIViewAutoresizingFlexibleTopMargin - is not included in the autoresizing mask.
Hope this will help some one in the future
I know it is a bit late, but I have same problem and I can't solve it with any of the previous answers. (I suppose this is the reason non was accepted).
The problem is that view size of the SecondViewController is same as view size of a previous ViewController, so too small to fit in a ViewController with Toolbar hidden. Thats why black background of a UITabBarController is visible at the top when transition is happening, and on a viewDidAppear view will stretch on right size.
For me it help to subclass root UITabBarController and set background color to same background color as SecondViewController has.
class RootViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = Style.backgroundColor
}
}
Then you can leave checkbox checked inside storyboard and it will look ok.
P.S.
If you have some views, that is position on the bottom part of the view, you need to set bottom constraints so they are smaller by 49 (because this is the height of the toolbar), and then on viewDidAppear set the right constraint.
For example:
I have view that need to be position 44 px from bottom edge. Before, I have constraint set to 44 and I have some strange behaviour of that view. It was placed to height and then jump on the right place.
I fix this with setting constraint to -5 (44-49), and then in viewDidAppear set the constraint back to 44. Now I have normal behaviour of that view.
Wow I just had the same issue now, very painful, and no info on the net about it.
Anyway, a simple workaround for me was to change the current view's Frame moving the y coordinates up and making the height bigger by the height of the tab bar. This fixed the problem if done straight after pushing the new view onto the navigation controller. Also, there was no need to fix the Frame afterwards (it must be updated when the view is shown again).
MonoTouch code:
UIViewController viewControllerToPush = new MyViewController();
viewControllerToPush.HidesBottomBarWhenPushed = true; // I had this in the MyViewController's constructor, doesn't make any difference
this.NavigationController.PushViewController(viewControllerToPush, true);
float offset = this.TabBarController.TabBar.Frame.Height;
this.View.Frame = new System.Drawing.RectangleF(0, -offset, this.View.Frame.Width, this.View.Frame.Height + offset);
Objective C code (untested, just a translation of the monotouch code):
UIViewController *viewControllerToPush = [MyViewController new];
viewControllerToPush.hidesBottomBarWhenPushed = YES; viewControllerToPush.hidesBottomBarWhenPushed = YES;
float offset = self.tabBarController.tabBar.frame.size.height; float offset = self.tabBarController.tabBar.frame.size.height;
self.view.frame = CGRectMake(0, -offset, self.view.frame.width, self.view.frame.height + offset); self.view.frame = CGRectMake(0, -offset, self.view.frame.size.width, self.view.frame.size.height + offset);
Do this in viewWillAppear of detailViewController, it should work fine
subclass your navigation controller, or just find the navigation bar
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let backdropEffectView = navigationBar.subviews[0].subviews[0].subviews[0] //_UIBackdropEffectView
let visualEffectView: UIVisualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .Light))
visualEffectView.frame = backdropEffectView.frame
backdropEffectView.superview?.insertSubview(visualEffectView, aboveSubview: backdropEffectView)
backdropEffectView.removeFromSuperview()
}

centering a uipopover in iPad

I would like to create a new keyboard for the iPad.
I have designed a view that has the keys and all the underlying work to fill in a textbox as the user presses the keys, and passes the value back to the calling routine when the user presses the return button.
This all works ok.
Now - I want to create this view as a popover.
I have the basic operations complete (pops up and is dismissed)
But now I need some fine tuning help.
Here are my questions...
1) How do I create a popover window centered on the screen without a UIPopoverArrowDirectionAny selection?
2) Ensure the popover window is the size that I created it with in the XIB file (currently, it resizes and removes some of the rightmost size of the window)
Thanks
tony
To present it with no arrows pass 0 for "permittedArrowDirections":
[popOverController presentPopoverFromRect:rect inView:view permittedArrowDirections:0 animated:YES];
To center it, pass a 1x1 rect located at the center of your view:
CGRect rect = CGRectMake(viewWidth/2, viewHeight/2, 1, 1);
[popOverController presentPopoverFromRect:rect inView:view permittedArrowDirections:0 animated:YES];
Then you have a centered and arrow-free popover.
The bit about removing the arrow could break at any time. They didn't provide a UIPopoverArrowDirectionNone option for a reason, they may choose to throw an exception in the future when 0 is passed, or default to something. Use it at your own risk.
If you are using a UIPopoverController, it will have an arrow. Here are your choices.
UIPopoverArrowDirectionAny
UIPopoverArrowDirectionUp
UIPopoverArrowDirectionDown
UIPopoverArrowDirectionLeft
UIPopoverArrowDirectionRight
As you can see there is no UIPopoverArrowDirectionNone
As for the XIB size, in your ViewDidLoad Method of the implementation file, you can set the following.
self.contentSizeForViewInPopover = CGSizeMake(320.0, 360.0);
You can set the size any way you like for the popover.
I've just been working this one out but wanted to do as much in the storyboard as possible which changes the approach a bit:
1) Select the popover in the storyboard and un-tick Directions Up/Down/Left/Right to get rid of the arrows.
To center it on screen override prepare for segue and do something like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
UIStoryboardPopoverSegue* popover = (UIStoryboardPopoverSegue*)segue;
CGFloat horizontalInset = (self.view.frame.size.width - popover.popoverController.popoverContentSize.width) / (CGFloat)2.0;
CGFloat verticalInset = (self.view.frame.size.height - popover.popoverController.popoverContentSize.height) / (CGFloat)2.0;
popover.popoverController.popoverLayoutMargins =
UIEdgeInsetsMake(
verticalInset,
horizontalInset,
verticalInset,
horizontalInset);
}
In my case I also found that the popover segue's corner radius didn't match my design so I change the background color to clear to sort that out i.e. add this to the above block:
popover.popoverController.backgroundColor = [UIColor clearColor];
2) In the destination controller for the segue find the attribute 'Popover' and tick 'Use Explicit Size' and put the size of you want it to appear in popover mode.

Resources