I am using a UIPageViewController. When the in-call status bar is toggled while in this view, it adjusts fine. If the in-call status bar is open before the UIPageViewController is loaded, the page gets pushed down by 20pt and a black bar is shown in that space.
Here's a screenshot of what this looks like:
Other posts recommend using setting automaticallyAdjustsScrollViewInsets on the page controller to NO. I've already tried that and it does not work. I've also tried self.edgesForExtendedLayout = UIRectEdgeNone;
Setting the origin of the UIPageViewController frame to 0 works, but feels wrong:
CGRect frame = self.pageController.view.frame;
frame.origin.y = 0;
self.pageController.view.frame = frame;
I ended up resolving this by ensuring the right constraints were on my view controller's main view. Here is an example where I am adding a view controller's view as a subview to a root view controller and then using Masonry (https://github.com/SnapKit/Masonry) to establish constraints that will snap all the child view controller's main view's edges to the root view controller (which itself already has autoresizing masks in place from Interface Builder). This resolved my issue of the status bar leaving that black space because it was pushing this view down (rather than re-sizing):
[self.view addSubview:viewController.view];
[viewController.view mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
In your case, and I don't have your full code so I don't know for sure but something like:
[self.pageController.view mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.pageController.view.superview);
}];
.. ought to do the trick.
Related
I have a scenario where I needed to add an animation inside the Navigation Bar, but the simulator doesn't animate. I add a UIView, add a label/text field and create outlets for the constraints and update them with "UIView animateWithDuration:" Instead, the simulator just updates the constraints and displays the view accordingly, but without any animation. I have tried it by modifying both constraints as well as frames (separately).
[self.view layoutIfNeeded];
[UIView animateWithDuration: 3
delay: 0.0
options: UIViewAnimationOptionCurveEaseInOut
animations: ^{
self.constraint.constant += 60;
NSLog(#"inside animation");
[self.view layoutIfNeeded];
}
completion:nil];
This is shown when I increment the width constraint by "60" in viewDidLoad: . And it comes out non-animated.
When I configure the constraints (increment width by "60") through a button click, even a static updation doesn't happen.
The same code works perfectly outside the navigation bar inside the UIView, though.
Appreciate any help...
If your navigation bar contains some animation, then you could try out drawing custom navigation bar. Here's what you've to do :
In your view controller in viewDidLoad() method add the following code
self.navigationController.navigationBarHidden = YES;
After that using a UIView create a custom navigation bar with frame adjusted to be visible and top.
After that add components on the view and start working with animations.
You can achieve that by taking custom UIView and create width NSLayoutConstraint outlet to it and in code you can handle the constant property of this variable by calling UpdateConstraintsIfNeeded or UpdateViewConstraints method before calling animation code
Ok, I've solved this at last. Phew...
What I did was...
Added a view in place of barButtonItem (let's call this "superView") and over it another view (and let's call this "subView") so that I get the following configuration.
Constraints were set between subView and objects on top of it. Now, I created outlets (superView and subView) for superView and subView, made them transparent (optional, of course).
Useful note: Set the superView's width to the default bar button item's width and the subView's to the screen width. Disable "Clip Subviews" on superView but enable on the subView.
Added animations as needed where I had wanted, but layoutIfNeeded on the "superView", not the parent view.
[self.superView layoutIfNeeded];
Good luck.
I have a UINavigationController with a UIViewController set as it's rootController, it contains a background on its UIView using an image set just under the navBar. I then push onto the navigation controller a new UIViewController and when the back button is pushed, the previous controller looks different. Using the visual debugger I can see that the self.view has moved entirely down below the navBar where previously it was at the top. I have no idea and been racking my brains as to why this might be happening
-(void)pushIPhoneMessagingContactsController:(MessageContactsViewController *)contactsController{
self.selectorView.hidden = YES;
[self.navigationController pushViewController:contactsController animated:YES];
}
On the RootViewController (iPhoneMessagingNotificationsController)
-(void)viewWillAppear:(BOOL)animated{
self.selectorView.hidden = NO;
[[[self navigationItem] leftBarButtonItem] setTintColor:[UIColor blackColor]];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];
if ([_displayType intValue] == MESSAGES_SHOWING) {
[self.notificationsViewController.view removeFromSuperview];
[self.contentView addSubview:_messagesViewController.view];
} else {
[self.messagesViewController.view removeFromSuperview];
[self.contentView addSubview:_notificationsViewController.view];
}
}
It seems the offending line was in the viewWillAppear method of the pushed UIViewController
self.navigationController.navigationBar.translucent = YES;
Somewhere else this navigationBar gets set as translucent:
[self.navigationController.navigationBar setBackgroundImage:[UIImage new]
forBarMetrics:UIBarMetricsDefault];
self.navigationController.navigationBar.shadowImage = [UIImage new];
self.navigationController.navigationBar.translucent = YES;
and to make it solid colour again:
self.navigationController.navigationBar.shadowImage = nil;
self.navigationController.navigationBar.translucent = NO;
but this code seems to mess with the layout so perhaps there is another way to change the opacity of the navBar and statusBar without affecting the layout?
What you're currently trying to do is hide or show a selectorView which really only should appear for one specific view controller.
Here's an encapsulated way to solve this that makes your selectorView a part of the root view controller, removing the connection from other view controllers. They no longer have to know about it or hide it.
Add your selectorView to your rootViewController's navigation bar titleView. (You can do this in code, or drop it in Storyboard and add an IBOutlet for it.)
self.navigationItem.titleView = selectorView;
Now when you push another view controller, its title will replace your rootViewController's selectorView title (view). Your other view controllers don't need to know anything about that view.
This is a good design approach in general. Anytime you have a control that should only appear on one view controller's navigation bar, you want to make it a part of that view controller's navigationItem (titleView, or left/right bar button items.) iOS will display the control when it presents that view controller, and hide the control when that view controller is no longer the top view controller in the navigation controller stack.
As for the 64-pixel height issue, it's likely related to some complexity in the rootViewController hierarchy that shouldn't be there.
In iOS 7/8, a view's content, by default, appears under a translucent navigation bar. Apple freely managed this for you, by insetting the first view of the view hierarchy.
From your code, it appears that you're trying to "hide" or "show" the (un)selected viewController's view.
Each view controller should have a view it controls. A view controller shouldn't be trying to control other view controller's views, or adding other view controller's views to its own view hierarchy.
Here's Apple's recommended way to approach this. Use a containerView in your rootViewController. The whole purpose of a container view is to encapsulate a view controller within a view. As your selectorView changes which view to show, you have your container view transition from one view controller to the other. (If you're not familiar with how to do that, check out this answer.)
Pin the containerView to the rootViewController's view, so Auto Layout can size it for you.
Your view hierarchy now looks like view -> containerView, instead of view -> hidden view of unselected view controller, shown view of selected view controller. Apple can adjust the first view's inset, and nothing gets incorrectly offset (by the height of the navigation control).
Update:
This question talks about scrollViewInsets and how they can be set on a view-controller-by-view-controller basis. If you do have a view controller, and you don't want its content to appear under a bar, uncheck that box.
But the best way to handle this is to "standardize" your UI, so it isn't varying from view to view. Either make the bar always be translucent, or not always be translucent. This makes transitions less "jarring" for the users.
I have a simple UIScrollView
UIScrollView *mainScrollView = [[UIScrollView alloc] initWithFrame:CGRectZero];
[self.view addSubview:mainScrollView];
[mainScrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
When the view is loaded and shown, part of UIScrollView appears under the navigation bar. But after rotation the view is suddenly considers navigation bar and pins the view to its bottom.
How can I make a view consider navigation bar from the beginning?
To start UIScrollView at bottom of UINavigationBar then i would suggest you to set Adjust Scroll View Insets property and setting Extend Edges property as well. do the needful changes of below property as per your requirement.
Ok, I solved this issue.
Problem has been solved by moving creation of scrollView to viewDidLoad method. Somehow, if scrollView is created outside the "load" methods, we can face such kind of problems.
I was just wondering if there is a simple way to specify whether a UINavigationBar should overlay its content when shown. I currently have a UINavigationController that contains a custom UIViewController with a UIScrollView, which contains a UIPageViewController (I wanted a zooming/scrollable UIPageViewController).
When I call:
[self setNavigationBarHidden:NO animated:YES];
From within my UINavigationController, the UINavigationBar animates in, but pushes the custom container with all its content down, instead of overlaying it.
The bar is set to translucent and I've tried all the settings I can think of. I changed the extendEdges settings in the child view controllers and that resized the content when the navigation bar came in, instead of pushing it down. But I still can't work out how to get it to overlay instead.
Many thanks.
Ok, apologies. This just reveals how little I know about iOS programming...
I noticed that the container's view origin y value was -44. So adding the following within my container class:
- (void) viewDidLayoutSubviews
{
CGRect boundsRect = self.view.bounds;
boundsRect.origin = CGPointMake(0, 0);
self.view.bounds = boundsRect;
}
Results in the view staying at the top of the screen, so the UINavigationBar overlays it nicely when it appears.
EDIT: Actually the proper way appears to be just calling:
self.automaticallyAdjustsScrollViewInsets = NO;
I implemented a custom UIViewController Transition in my App, which replaces the navigation controllers built in push animation.
Everything works so far, except the toplayoutguide in the newly pushed view controller is 0 although the new view controller inherited the navigation bar from the old view controller.
It should be 64.0 (Statusbar height + Navigation bar height), where it is 0.0 now.
So all objects, which are attached to the top layout guide in the storyboard now appear 64 points too high (below the translucent bar).
When I disable the custom View Transition the top layout guide will have the expected value.
I tried to call layoutSubviews and updateConstraints "all over the place". In the view controller as well as in the navigationcontroller.
As I understand the navigationcontroller (parentviewcontroller) should update the toplayoutguide of the new view controller, but apparently I am missing something in my custom transitioning code, which triggers the update to the correct value for the toplayoutguide.
Here's my custom transition code which is an object set as delegate of the navigationcontroller:
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return 0.7;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIView *animationContainerView = [transitionContext containerView];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *toView = [toVC view];
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIView *fromView = [fromVC view];
CGRect endFrame = [transitionContext finalFrameForViewController:toVC];
CGRect startFrame;
startFrame = CGRectOffset(endFrame, 0, endFrame.size.height);
if (self.operation == UINavigationControllerOperationPop) {
[animationContainerView insertSubview:toView belowSubview:fromView];
[toView setFrame:endFrame];
}
else{
[toView setFrame:startFrame];
[animationContainerView insertSubview:toView aboveSubview:fromView];
}
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 usingSpringWithDamping:0.7 initialSpringVelocity:0.8 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
if (self.operation == UINavigationControllerOperationPop) {
[fromView setFrame:startFrame];
[fromView layoutIfNeeded];
}
else{
[toView setFrame:endFrame];
[toView layoutIfNeeded];
}
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}
Nothing really fancy happens there. Just the view sliding from the bottom up with some built in dynamics.
The problem is, that the objects attached to the top layout guide now are under the navigation bar, as the top layout guide length == 0.
I can't figure out what I need to do, so that the view controller's toplayoutguide is set to the correct value.
The push navigation is performed "plain vanilla" with a push storyboard segue. All I do, before calling performSegueWithIdentifier is to set the navigationcontrollers delegate.
Here's the code:
self.navigationController.delegate = [[My_CustomNavigationTransitionDelegate alloc] init];
[self performSegueWithIdentifier:#"infosSegue" sender:nil];
What do I miss?
I was having an issue where the bottomLayoutGuide property would set itself to zero length and then would cause my buttons above the tab bar to fall below to tab bar with the autolayout.
Have you looked at doing this
[self.navigationController.view setNeedsLayout]
I put it into my viewwillappear and I stopped getting a zero length on the bottomLayoutGuide property. Maybe that would help you out with your topLayoutGuide property too.
I was able to work around this, with the following view hierarchy:
UIView
UIScrollView
<content, constrained to UIScrollView>
Constrain the UIScrollView to match the UIView's top, leading, trailing, and bottom edges. Interface Builder might want you to use the topLayoutGuide and bottomLayoutGuide for the UIScrollView, or it might not. Maybe it's dependent on the version of Xcode, but some of our View Controllers used the superview, others used the layout guides.
For the views where Interface Builder didn't want to constraint the scroll view relative to its superview, I opened the storyboard in a text editor and adjusted the constraints on the scroll view by hand.
Finally, on the View Controller, make sure that extend edges under top bar is YES, and so is Adjust Scroll View Insets.
Basically, I'm avoiding using the topLayoutGuide, and instead relying on the scroll view insets, which does work.
Where I didn't have a UIScrollView in the hierarchy, like you, NOT extending edges under the top bar worked for me.
I ran into the exact same problem. My custom navigation controller's container view didn't have constraints. The minute I added vertical spacing constraints from the container view to its superview's layout guides (albeit the two were identical in size), and set the top/bottom/status bar appearance on the container view everything was ok and the layout guides of the pushed controllers were in the correct position. Hope that helps.
Update: From the official documentation on topLayoutGuide
A view controller within a container view controller does not set this property's value. Instead, the container view controller constrains the value to indicate:
The bottom of the navigation bar, if a navigation bar is visible
The bottom of the status bar, if only a status bar is visible
The top edge of the view controller’s view, if neither a status bar nor navigation bar is visible
So the container view needs to implement correct constraints and hide/show bars and such for the effects to work. AFAIK there is no API to do this in custom container view controllers.
I found a way. First uncheck "Extend Edges" property of controller after that navigation bar will get in dark color. Add a view to controller and set top and bottom LayoutConstraint -100. Then make view's clipsubview property no (for navigaionbar transculent effect). My english is bad sorry for that. :)