UIPopoverController with UINavigationController border sizing - ios

I am embedding a UINavigationController within a UIPopoverController. It "works", but the top border of the popover expands to the size of the navigation controller bar (behind it), casting the border's shadow onto the top of the main view (read: the top border of the popover is 44 points high). When I instantiate the popover with the class itself...not within the UINavigationController, it all works fine (but, of course I don't have access to the navigational controller).
Where am I going wrong?
CGPoint buttonPoint = [self.mapView convertPoint:sender.center fromView:self.guideButtonScroll];
GuideViewController *guideViewController = [[GuideViewController alloc] initWithNibName:#"GuideView" bundle:nil];
UINavigationController *guideNavigationController = [[UINavigationController alloc] initWithRootViewController:guideViewController];
self.buttonbarPopoverController = [[UIPopoverController alloc] initWithContentViewController:guideNavigationController];
self.buttonbarPopoverController.delegate = self;
self.buttonbarPopoverController.popoverContentSize = CGSizeMake(320, 504);
[self.buttonbarPopoverController presentPopoverFromRect:CGRectMake(buttonPoint.x - 30, buttonPoint.y, 10, 10) inView:self.mapView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

What you are seeing is not the top border of the popover expanded. The popover has normal borders. What you are seeing is the UINavigationBar at the top of the navigation controller's view. The UINavigationBar casts a shadow in iOS 6.
The UINavigationBar is automatically given a special color / style that matches that of the popover borders. Of course you can change that if you don't like it. You are also free to hide the navigation controller's navigation bar if you don't want to see it.

Went with subclassing the UIPopoverController, but that didn't get the gloss look of the popover. Simply just put the UINavigationController inside a UIViewController...gets the custom navigation bar and the gloss look of the popover. Here's what I ended up with:
UIViewController *guideviewViewController = [[UIViewController alloc] init];
guideviewViewController.view.frame = CGRectMake(0, 0, 320, 508);
GuideViewController *guideViewController = [[GuideViewController alloc] initWithNibName:#"GuideView" bundle:nil];
self.guideNavigationController = [[UINavigationController alloc] initWithRootViewController:guideViewController];
self.guideNavigationController.view.frame = CGRectMake(0, 0, 320, 508);
[guideviewViewController.view addSubview:self.guideNavigationController.view];
CGPoint buttonPoint = [self.mapView convertPoint:sender.center fromView:self.guideButtonScroll];
self.guidePopoverController = [[UIPopoverController alloc] initWithContentViewController:guideviewViewController];
self.guidePopoverController.delegate = self;
self.guidePopoverController.popoverContentSize = CGSizeMake(320, 508);
[self.guidePopoverController presentPopoverFromRect:CGRectMake(buttonPoint.x - 30, buttonPoint.y, 10, 10) inView:self.mapView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

Related

Custom view controller presentation causes the navigation bar to bounce

I'm presenting a view controller using the transitioning delegate modally from my root view controller.
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
UIViewController *rootVC = [window rootViewController];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:authVC];
navController.modalPresentationStyle = UIModalPresentationCustom;
navController.navigationBar.translucent = NO;
navController.transitioningDelegate = self;
[rootVC presentViewController:navController animated:YES completion:nil];
My transitioning delegate adds the view as follows, where authorizationVC is the login view pictured in the screenshots.
UIView *containerView = [transitionContext containerView];
[containerView addSubview:blurredView];
[containerView insertSubview:_authorizationVC.view aboveSubview:blurredView];
_authorizationVC.view.frame = CGRectMake(10, 30, 300, 450);
At first, the view animates in, and the navigation bar is full height, what I believe to be 64 pixels (44 for the nav bar and 20 for the status bar).
As soon as my animation completes, the nav bar shrinks to 44 pixels. The transition is jarring. The content inside my view controller is unaffected.
How do I avoid this jittering navigation bar? The second image is what I'd like to achieve.
Set all the properties of the view before adding it to its superview.
UIView *containerView = [transitionContext containerView];
_authorizationVC.view.frame = CGRectMake(10, 30, 300, 450); /// Change the frame FIRST!
[containerView addSubview:blurredView];
[containerView insertSubview:_authorizationVC.view aboveSubview:blurredView];
Voila! The nav bar acts as expected.
I would avoid using a UINavigationController if you don't really need it.
The UINavigationController takes into account the topLayoutGuide in the way it sizes the navigation bar. If you just want the coloured bar and close button, I'd simplify it and use your own views for that.
If you must use a UINavigationController you could try playing with the status bar's appearance and see how it affects the navigation controller's presentation.

Why isn't the rootViewController of my modally presented (form sheet) navController aware of it's smaller size when presented modally?

I am working on an iPad app with a few different modal views, and this code is pretty common:
UIViewController *v1 = [[UIViewController alloc] init];
UINavigationController *nav1 = [[UINavigationController alloc] initWithRootViewController:v1];
nav1.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:nav1 animated:YES completion:nil];
It could be that I am doing this wrong, but this is how I am presenting a navController-nested vc modally.
The problem is that within the v1 class, any reference to self.frame/bounds results in full screen dimensions:768x1024. Even though the navController clearly isn't being displayed with that size.
What should I be doing to make it so that the v1 vc knows how big it actually is? So that if I wanted to add, say, a tableView, it would know how big it should be?
Thanks!
EDIT:
I have tried a few more things, and still don't have a solution to this problem. I have made a simple sample project to illustrate the problem I am having. I just have one view and this is the core of the code:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Frame: %#", NSStringFromCGRect(self.view.frame));
NSLog(#"Bounds: %#", NSStringFromCGRect(self.view.bounds));
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(self.view.frame.size.width - 400, 0, 400, 400);
button.backgroundColor = [UIColor redColor];
[button addTarget:self action:#selector(presentModal) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
- (void)presentModal {
SSViewController *view = [[SSViewController alloc] init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:view];
nav.modalPresentationStyle = UIModalPresentationFormSheet;
[self.navigationController presentViewController:nav animated:YES completion:nil];
}
When this view loads, I have a big red button that is up against the top right corner of my view. When I press the button, it loads the same VC in a modal view embedded in a navController. The button shows up nearly off screen because the frame hasn't changed. It still shows as full screen. Here is a link to the project.
Not sure why you're having the issue you're having. I'm using the following code:
- (void)presentNewView {
NewViewController *newVC = [[NewViewController alloc] initWithNibName:nil bundle:nil];
newVC.view.backgroundColor = [UIColor redColor];
UINavigationController *newNC = [[UINavigationController alloc] initWithRootViewController:newVC];
newNC.modalPresentationStyle = UIModalPresentationFormSheet;
[self.navigationController presentViewController:newNC animated:YES completion:NULL];
}
.. it results in the following in the simulator:
.. and when I print out the first ViewController's frame and bounds (I thought it might be an issue with the two) I get the following:
frame height: 1024.000000
frame width: 768.000000
bounds height: 1024.000000
bounds width: 768.000000
.. and when I print out the presented ViewController's frame/bounds I get the following:
frame height: 620.000000
frame width: 540.000000
bounds height: 620.000000
bounds width: 540.000000
How are you determining the size of the frame exactly? Any reference within the v1 class that was presented modally SHOULD know its actual size, like I showed above.
EDIT
The major difference I found with my code and yours, is that in my code I created a subclass of my view controller "NewViewController" and was printing out the frame from within that class. The class itself seems to be aware of its correct bounds, but the class the presented it seems not to be. This is demonstrated by printing the view property from the ViewController class that presented it:
NewViewController's View From Presenting Class: frame = (0 0; 768 1024)
..compared to printing out the self.view from within the ViewDidAppear method of NewViewController itself:
NewViewController's View Did Appear: frame = (0 0; 540 576)
Moral of the story, if you are going to be presenting a UIViewController in the way you've shown, you're likely going to want to subclass UIViewController anyway so you can customize it however you want, so within that file if you reference self.view or self.bounds you will be getting the ACTUAL view/bounds.
EDIT #2
Based on the project you provided, the reason why you are having that issue is because you are printing out the frame/bounds of the view in viewDidLoad as opposed to viewDid/viewWillAppear. Adding those NSLog statements to VWA or VDA provides you the correct frame, so as I said in my initial edit, you should be fine accessing the view of the modal correctly at that point.
It's a new feature of iOS7. If you embed a UIViewController in navigation bar, it won't get smaller, because by default navigation bar is translucent.
You will see it if you change the background color of a view controller, that the top part of it is actually behind the navigation bar.
To lay out the v1 view controller underneath the navigation bar, you can use following code:
if ([v1 respondsToSelector:#selector(edgesForExtendedLayout)]) {
v1.edgesForExtendedLayout = UIRectEdgeNone;
}
It will behave just as in iOS6.
when presenting view controllers modally from a child view controller (one that has lass than the full screen and is a child of another view controller..) it is important to do this so that the modal controller knows the size of the canvas its appearing in
childViewController.definesPresentationContext = YES;
modalViewControllerWhichIsAboutToBePushed.modalPresentationStyle = UIModalPresentationCurrentContext

Solve the viewController sizing mystery! Why do elements (UIImageView and Nav bar) change size when switching viewControllers?

So this is the code for my mapView:
self.mapView = [[MKMapView alloc] init];
self.mapView.frame = CGRectMake(0, 70 , self.view.bounds.size.width,
self.view.bounds.size.height);
When I push to this view controller from one viewcontroller it looks just fine. The navigation bar and map view are all in the correct spot; however, when I tried to create a button that go directly to the map Viewcontroller from ANOTHER viewController everything changed.
The map view has shrunk and the navigation bar is missing now..? Here's the new button from the other ViewController:
UIImage* image4 = [UIImage imageNamed:#"Near_me_carousel.png"];
_mapButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 18, 26, 26)];
[_mapButton setBackgroundImage:image4 forState:UIControlStateNormal];
[_mapButton addTarget:self action:#selector(MapButton)
forControlEvents:UIControlEventTouchUpInside];
[_mapButton setShowsTouchWhenHighlighted:YES];
[self.view addSubview:_mapButton];
Here's the method:
-(void) MapButton {
MapViewController *mapView = [[MapViewController alloc] init];
[self.navigationController presentViewController:mapView animated:YES completion:NULL];
}
I'm so confused as to why this is happening! Any ideas?
I don't know if this is really your problem, but here's a shot:
Inside your button method, you are calling your MapViewController modally. If the viewController itself does not have a UINavigationBar, of course it will not be displayed.
When you have a push transition, through a UINavigationController, a navigationBar is automatically added above of your view.
And here's the tricky part: when presented as a push transition, the 0 y value of the view's frame is the point just below the navigationBar. I. e., the navigationBar does not belong to your view.
And when presented through a modal transition (presentViewController:), the 0 y value is the top/left point of the window, even if you add a NavigationBar yourself.
Ilustrating:
The origin of it will be here if presented as a modal:
And here if presented if a push in a navigation stack:
Conclusion:
So, in your case, the y-value 70 in this code
self.mapView.frame = CGRectMake(0, 70 , self.view.bounds.size.width,
self.view.bounds.size.height);
will be different according to the transition style. In a modal, it will look like it's displaced 44 points to the top (size of the navigation bar)

UINavigationController's nav bar seems invisible, but title and buttons are still visible

I've run into a weird situation. This is all done in code - no Interface Builder.
I'm creating a UIViewController and adding some content to it:
UIViewController* popoverViewController = [[[UIViewController alloc] init] autorelease];
UIView* popoverContentView = [[[UIView alloc] init] autorelease];
popoverContentView.backgroundColor = [UIColor blackColor];
// Add some stuff to popoverContentView
popoverViewController.view = popoverContentView;
I then create a UINavigationController, set its root view controller to the UIViewController from above, and add a title and a button to the navigationItem:
UINavigationController* popoverNav = [[[UINavigationController alloc] initWithRootViewController:popoverViewController] autorelease];
popoverViewController.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(dismissPopover)] autorelease];
[popoverViewController.navigationItem setTitle:#"MY TITLE"];
Then I set up a UIPopoverController with the UINavigationController in it and present it:
self.popoverController = [[[UIPopoverController alloc] initWithContentViewController:popoverNav] autorelease];
self.popoverController.delegate = self;
[self.popoverController setPopoverContentSize:CGSizeMake(320, 216) animated:NO];
[self.popoverController presentPopoverFromRect:self.cell.frame inView:self.popoverParentView permittedArrowDirections:UIPopoverArrowDirectionUp | UIPopoverArrowDirectionDown animated:YES];
The problem is that everything appears correctly with one exception: the navigation bar is invisible, but the title text and the bar button still show up and work correctly. I've tried messing with the bar's hidden and tintColor properties and changed the size of the popover, but nothing changes.
I'm sure I'm missing something obvious, but I can't see it.
I have other examples of similar things in my project's codebase, but those appear correctly. Any ideas as to why this is happening or how I could fix it?
EDIT
Not sure if the following will help, but I'm hoping it will provide some clues as to what's really going on here to someone who's seen something like this before.
I pushed a new (blank) view controller onto popoverNav just to see what would happen. It pushes and animates perfectly. Everything looks right except that the nav bar is still transparent and the bar button items are pushed to the top of the view.
Perhaps not the main cause of the problem, but it looks like the UIDatePicker is clipped by the CGSize you have setup. As far as I can remember, the default height for the picker is 216 (which you have) but you haven't taken into consideration the required height for the UINavigationBar.
I'm not sure why the background of the view looks to be some kind of textured grey unless this is set in your UIViewController but it doesn't appear that way from the code you pasted. This certainly isn't a default texture for iOS though.
Perhaps try and make your CGSize a little larger to accommodate the contents of the view as it looks as though the "Done" button is hugging the top right corner where there should be a fair few points worth of spacing around this by default.
Also, is there a reason why you're creating a new view and assigning it as the view property of your view controller?
UIView* popoverContentView = [[[UIView alloc] init] autorelease];
popoverContentView.backgroundColor = [UIColor blackColor];
// Add some stuff to popoverContentView
popoverViewController.view = popoverContentView;
Why not add the content as subviews of the view instead?

How to addSubview with a position?

I have something like this:
myViewController = [[MyViewController alloc] initWithNibName:#"MyView" bundle:nil];
[mainCanvas addSubview: myViewController.view];
self.view = mainCanvas;
It will be added at the position (0, 0), but I want to add it at (0, 100) or somewhere else. How can I do so?
Something like this:
myViewController = [[MyViewController alloc] initWithNibName:#"MyView" bundle:nil];
myViewController.view.frame = CGRectMake(0, 100, myViewController.view.frame.size.width, myViewController.view.frame.size.height);
[mainCanvas addSubview: myViewController.view];
self.view = mainCanvas;
In addition to setting the frame property, you can also set the center property of a view.
Set the frame property on the sub view.
This is the best way I've found to add a subView, like a loading screen or something that you want to show whether you are in a UIView, UIScrollView, or UITableView.
myViewController = [[MyViewController alloc] initWithNibName:#"MyView" bundle:nil];
myViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
[self.view addSubview:myViewController.view];
self.view.bounds = myViewController.view.bounds;
This will make the subView appear in full screen no matter where you are in the self.view by adding the subView to where you are currently located in your self.view instead of positioning it in the top left corner, only showing fully if you are at the very top of your view.

Resources