Displaying a UIViewController view - ios

I'm displaying a UIViewController as one of the pages in a UIPageViewController.
This viewController (VC1) has a size of (320, 568). What I then do is display another VC using this method:
- (void)displayContentController:(UIViewController *)content {
[self addChildViewController:content];
content.view.frame = self.view.frame;
[self.view addSubview:content.view];
[content didMoveToParentViewController:self];
}
Called this way:
NewViewController *newVC = [self.storyboard instantiateViewControllerWithIdentifier:#"NewViewController"];
[self displayContentController:newVC];
The reason I'm doing this instead of pushing it or presenting it modally, is because I need it to overlap the other UIViewController(VC1), blurring it while sliding with the UIPageViewController like VC1 normally would.
viewDidLoad in newVC is reporting a self.view.frame of (320, 568) at first, but then a little later it suddenly reports a self.view.frame of (320, 571) - rather than a height of 568.
I'm using autolayout.
Why is this and how can I resolve it?

In viewDidLoad() method frames don't give you actual size of controller views. To get correct sizes try to check it in viewdidlayoutsubviews() method of ViewController

Related

Custom presented UIViewController changing to fullscreen

I have a view controller presented using UIModalPresentationCustom presentation style. I use a custom UIViewControllerTransitioningDelegate to present the view controller as a sidebar (so it slides in from the edge of the screen and does not occupy the full screen).
However when I then present another view controller from this one using UIModalPresentationFullScreen — and then dismiss the full screen view controller, my underlying custom presented controller is suddenly resized to occupy the full screen. Does anyone know why this is the case?
Edit: this is essentially my animateTransition method for presenting the sidebar — I've stripped out most of the code to make it readable. Basically it gets the container from the transitionContext, adds and animates the destination view controller's view to the container.
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIView *container = transitionContext.containerView;
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *fromView = fromVC.view;
UIView *toView = toVC.view;
if( toVC.isBeingPresented )
{
[container addSubview:toView];
//... Animate some new frame for toView
//Call [transitionContext completeTransition:YES] on animation completion
}
else
{
//... Animate fromView out
//On completion remove fromView from superview
//Call [transitionContext completeTransition:YES] on animation completion
}
}
Edit 2: Doing a little more research, I notice that the frame of my custom presented view controller's view is being set when the view controller above it in the modal stack is dismissed. The following stack trace leads to the frame being set as full screen:
0 -[MyCustomPresentedViewControllerView setFrame:]
1 -[UIView(MPAdditions) setFrameOrigin:]
2 -[UIViewControllerAccessibility(SafeCategory) dismissViewControllerWithTransition:completion:]
3 -[UIViewController dismissViewControllerAnimated:completion:]
Changing the presentation style by assigning:
viewController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
results in the modal presentation controller occupying a fraction of the screen, the same fraction as the original presented view controller of the UIPresentationController.
However, using viewController.modalPresentationStyle = UIModalPresentationOverFullScreen
is better for this case, since his modal controller is full screen.
I am experiencing the same issue. I've tried debugging it using symbolic breakpoints and there seem to be some internal call on some kind of layout manager that does this.
While I wasn't able to "solve" this (it seems to me like a bug in the SDK), I was able to come up with a workaround that fixes this. Basically, you have to set the correct dimensions of the presented view at two opportune times. Like this:
In the view controller that was presented using your custom UIPresentationController, add this:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
dispatch_async(dispatch_get_main_queue(), ^{
self.view.frame = [self.presentationController frameOfPresentedViewInContainerView];
});
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.view.frame = [self.presentationController frameOfPresentedViewInContainerView];
}
And if you are wondering: yes, you do need it to do in two places. Because weirdly enough, when I did it only in the viewDidAppear async block, it got broken (resized to fullscreen) once the animation finished.
Try to use:
viewController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
It helped me in iOS 8.1

How to access properties from view after it loaded

Storyboard:
Constraints:
Result:
I'm trying to understand autolayout and how I can use it inside a container.
I got a default ViewController that was made for me when I opened the Storyboard. I put a View Container inside there. And then I added a loose (not connected to anything) ViewController. I want the content inside the new ViewController to be put in the container.
So the added ViewController which will be put inside the Container consists of three labels where I am using autolayout.
Clicking on the black bar for the Container View Controller in the Storyboard I go to Identity Inspector and set the Custom Class to "ContainerViewController". Then I set the Storyboard ID to "ChildController" for the loose View Controller.
Then I override viewDidLoad in ContainerViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
UIViewController *child = [self.storyboard instantiateViewControllerWithIdentifier:#"ChildController"];
[self addChildViewController:child];
[self.view addSubview:child.view];
}
Why won't the constraints for the autolayout work when put in a container? I hoped they would, so I could take this further to a UIPageViewController.
EDIT:
So it seems to be the frame size. I need to do something like this before adding it as sub view:
child.view.frame = CGRectMake(0, 0, 265, 370);
Now I created an outlet for the Container in ViewController.h.
#property (weak, nonatomic) IBOutlet UIView *container;
But.. From ContainerViewController, how can I ask presenting view controller (ViewController) of this property when it's not set yet?
ViewController *parent = (ViewController*)self.presentingViewController;
UIView *container = parent.container;
NSLog(#"ParentView Container Width: %f, Height: %f", container.frame.size.width, container.frame.size.height);
It just gave me zero width and height, because the view hasn't loaded yet. Later on when it loads, I get the actual values..
NSLog(#"View Controller Container. Width: %f, Height: %f", self.container.frame.size.width, self.container.frame.size.height);
The question: How can I access properties of presentingViewController when they are ready/loaded?
when adding a child view controller you should set its view frame like this
UIViewController *child = [self.storyboard instantiateViewControllerWithIdentifier:#"ChildController"];
[self addChildViewController:child];
[self.view addSubview:child.view];
//set the view frame
child.view.frame = self.view.bounds;
So the child view controller will resize its view frame and layout contraints will do their homework ;)
EDIT
put the code in -(void)viewDidLayoutSubviews like this:
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
UIViewController *child = [self.storyboard instantiateViewControllerWithIdentifier:#"ChildController"];
[self addChildViewController:child];
[self.view addSubview:child.view];
//set the view frame
child.view.frame = self.view.bounds;
}
Just try to set frame in viewWillLayoutSubviews for view Controller

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

Storyboard to design off screen subview

I want to use Storyboards to design content for a slider, and it seems like an easy way to design offscreen content is to use a childViewController. So I've done this
myViewController = [[UIStoryboard storyboardWithName:#"ipad" bundle:NULL] instantiateViewControllerWithIdentifier:#"keyPadOffScreen"];
[self addChildViewController:myViewController];
[myViewController didMoveToParentViewController:self];
newView = myViewController.view;
[self.view addSubview:newView];
And that adds the entire view controller over top of my root view. The problem is, I only want one of the subviews to show up, not the whole view. I can handle the animation, as long as I know how to add the root view. I tried this to just add the subview (sliderView is the name of the subview I want) instead of the whole view, but that did nothing
newView = myViewController.sliderView;
[self.view addSubview:newView];
Should I be using a different strategy?
EDIT: this DOES work, but it seems silly - setting the views size to just be the size of the subview.
newView.frame = CGRectMake(newView.frame.origin.x, newView.frame.origin.y, newView.frame.size.width, **myViewController.sliderView.frame.size.height**);
It does seem a bit overkill for just a view. Once you start doing a lot of custom view/animation/transition stuff it's often easier to implement in code, or at least it is for me since I've been doing it that way for a long time.
But maybe you want to stick with Storyboards. I respect that. And if you have a few developers working on this then it's important to keep some uniformity to how you set up your UI.
Instead of keeping it in a separate view controller and adding it when you need it to animate on-screen, simply add it to your existing view controller and either set it to hidden, or set it's alpha to 0.0 in IB. Then your animation can undo that and make it visible.
you can use custom segue here, for instance:
#implementation FRPresentEnteringPopupSegue
- (void)perform
{
FirstVC *toVC = self.destinationViewController;
SecondNavigationController *fromVC = self.sourceViewController;
toVC.view.frame = CGRectMake(0.0, 0.0, 300.0, 135.0);
toVC.view.center = CGPointMake(fromVC.view.bounds.size.width/2, fromVC.view.bounds.size.height + toVC.view.bounds.size.height/2);
[fromVC.view addSubview:toVC.view];
[toVC viewWillAppear:YES];
[UIView animateWithDuration:0.5
delay:0.0
usingSpringWithDamping:0.7
initialSpringVelocity:0.5
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
toVC.view.center = CGPointMake(fromVC.view.bounds.size.width/2, fromVC.view.bounds.size.height/2);
}completion:^(BOOL finished) {
[toVC viewDidAppear:YES];
}];
}
#end
make your UIStoryboardSegue subclass
override - (void)perform method with your custom view appearance code
use segue usual way

How to resize a modalViewController with UIModalPresentationPageSheet

I have a modal view controller that I show with UIModalPresentationPageSheet. The problem is that its default size is too large for my content: so I want to resize its frame to adjust accordingly with my content.
Does anyone know of a way/trick to do this?
Thanks.
Actually you can resize the view controller that gets presented with UIModalPresentationPageSheet. To do it you need to create a custom view controller class and add the following to the class:
<!--The header file-->
#interface MyViewController: ViewController{
//Used to store the bounds of the viewController
CGRect realBounds;
}
<!--In the .m file-->
//viewDidLoad gets called before viewWillAppear, so we make our changes here
-(void)viewDidLoad{
//Here you can modify the new frame as you like. Multiply the
//values or add/subtract to change the size of the viewController.
CGRect newFrame = CGRectMake(self.view.frame.origin.x,
self.view.frame.origin.y,
self.view.frame.size.width,
self.view.frame.size.height);
[self.view setFrame:newFrame];
//Now the bounds have changed so we save them to be used later on
_realBounds = self.view.bounds;
[super viewDidLoad];
}
//viewWillAppear gets called after viewDidLoad so we use the changes
//implemented above here
-(void)viewWillAppear:(BOOL)animated{
//UIModalpresentationPageSheet is the superview and we change
//its bounds here to match the UIViewController view's bounds.
[super viewWillAppear:animated];
self.view.superview.bounds = realBounds;
}
And then you display this view controller with a UIModalPresentationPageSheet. And that's it. This does work for iOS 5.1.1 and iOS 6 as of the date of this post.
You can't resize a UIModalPresentationPageSheet because its size is not modifable.
This works for resizing a view controller that's presented as UIModalPresentationFormSheet. I would give this a try, I'm not sure if it'll work or not:
navController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:navController animated:YES];
//these two lines are if you want to make your view smaller than the standard modal view controller size
navController.view.superview.frame = CGRectMake(0, 0, 200, 200);
navController.view.superview.center = self.view.center;

Resources