I have a simple UIViewController with a simple view. As a result of a user interaction, a new UINavigationViewController is instantiated and its view is being added as a subview to the view of the UIViewController. This takes place as part of an aninmation transition (flip).
This works quite well and the first view is flipped over in favor of the second view. But when the animation comes to an end (the UINavigationViewController's view fills the whole screen) the navigation bar items jump, i.e. the title jumps about 5-10 pixel from right to left, the buttons' jump depending on which side (left / right) they are positioned. During the animation you can see that the buttons are misplaced and that the jumping movement is kind of a repositioning.
Could anyone tell me the reason for this and give me some advice how to avoid this?
This is a little late, but there's no accepted answer and I've encountered this issue even fairly recently (albeit with an older app running on iOS 8).
If you encounter this issue and also see a warning along the lines of the following, it may be that you haven't properly set your root view controller in the app delegate:
Application windows are expected to have a root view controller at the
end of application launch
Modifying the app delegate as follows recently remedied the issue for me:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Other logic goes here
// ...
self.window.rootViewController = myRootViewController; // This was missing
[self.window makeKeyAndVisible];
return YES;
}
(I previously had some nearly-equivalent code that was setting up the view so everything displayed correctly on launch, but was not specifically setting the window's root view controller.)
I know it's annoying, but I have no idea why it happens, but in my experience, it happens in one of two cases:
Translucent navigationBar: maybe it had something to do with the fact that a translucent navigationBar sometimes sets a view's wantsFullScreen property (the view will then extend below the navigationBar).
During transitions: as you have described.
To avoid it, maybe a nice animation to fade it on an off screen before and after animation so as not to give the illusion of low quality.
OR
In viewWillAppear, assign the pixel value it's jumping to the navigationBar's origin.y. It's sad that it happens, but sometimes it just can't be fixed any other way.
This can be caused by partially-corrupt PNGs used for navigation bar buttons when the UIImages are resizableWithEdgeInsets:. If you're using custom button item images, try exporting them again using techniques known to produce reliable images (See blog posts by Marc Edwards at Bjango for a good start).
I recently had a problem that sounds identical to what you were experiencing. I found that using the [UIView performWithoutAnimation:^{}] block inside of transitionWithView fixed it.
During custom segue transition, view's navigation bar items are misplaced.
Related
I have an app which has a UITabBarController, and when different tabs are pressed, I override viewWillAppear, get a reference to the tabBarController and then resize its frame. The reason for doing so is that some tabs require the full height of the screen, whilst other tabs have a background in the top 120pixels.
The way I am referencing the tabBarcontroller is by getting the application delegate and using its reference to the tabBarController to move its position:
MyAppDelegate *del= [UIApplication sharedApplication].delegate;
del.tabBarController.view.frame = CGRectMake(0,120,320,360);
This method works fine when switching between tabs, but the first time the application loads up it does things a bit weirdly, and I cant understand why. My first tab actually has a NavigationBar, and then contains a view which has a pickerView. The first time my application loads, the NavigationBar appears about 10px too low, however the pickerView still appears in the right position. I was hoping I could get around this by treating things differently the first time viewWillAppear is called, however if I decrease the y position to compensate for the navigation bar, it shifts everything together including the datepicker within the view.
I would like to understand why the application behaves differently when first loaded and was hoping someone could either explain this to me and with any ideas on how to solve the issue or please point me in the right direction. I suspect this has something to do with the status bar on top since that is about the same size as the offset I am seeing..
Any help would be appreciated!
thanks
OK I've figured it out. If anyone is interested, it appears that on application startup, the UINavigationBar's frame has a y position of 10 pixels. All I had to do was set the UINavigationBar's frame vertical position back to 0 pixels and all works! Anyone needing to do this, here's the code:
self.navigationController.navigationBar.frame = CGRectMake(0,0,320,45)
I call the above line in the viewWillAppear method and all works as expected.
I begin with a view-based app template. In the automatically created viewcontroller's xib (call it VC1), I add a button, and define in its interface/implementation:
- (IBAction)showVC2;
- (IBAction)showVC2 {
[self presentModalViewController:[[VC2 alloc] init] animated:NO];
}
I then create VC2. In both implementations, I allow only landscape orientations. In both xibs, I set the views to landscape. In the info.plist file, I specify starting orientation as landscape right.
When I run the project in the simulator and press the button in VC1, VC2 is displayed, but a 20 pixel gap shows both on the lower edge and on the right edge. Notably, rotation causes the view to be placed correctly on screen after rotation completes.
This issue is similar to others:
1 2 3, though the solutions identified do not seem to work in the present case. The correct/expected behavior occurs if the modal view is presented with animation (I do not want the transition animated, however). This issue has persisted since last summer (iOS 3.1.3?). It continues with 4.3. The issue does not occur in portrait orientation.
Can anyone provide a solution or explanation for why such a simple modal viewcontroller presentation does not give the expected result?
EDIT: Xcode project
I just tried building what you suggested and have no problems with a gap. I don't know if there is a way for you to post all of your code or your project. If you can do that I will try and help.
In the iPad's Photos app, when you tap an album the stack of pictures expands to fill the screen - you're in the same view, it's just rearranged the grid a little. But at the top, a left-arrow-style Back button appears, as if pushViewController had been used - except it fades in neatly, rather than sliding in. When you tap that, it fades out again, rather than sliding out.
Is there a way to replicate this behaviour? I've tried a few options so far, and might just be missing something. What I've tried:
Setting self.navigationItem's leftBarButtonItem works, but gives me a square button rather than an angled Back-style one - there are a few hacks online to make this work, such as using pictures for the button, but I'd rather only use them if there's definitely no "official" way to do this.
Setting self.navigationItem.backBarButtonItem - this is generally used to customise the back button when a view controller is pushed, so it has no effect.
[self.navigationController.navigationBar setItems::] - this works, although it gives me the sliding animation rather than fading. As a result, I use animated:NO to make it just appear. Downside: when tapping Back, you do get the sliding out animation, which looks weird because the rest of the UI stays still.
Has anyone managed to replicate this effect?
Thanks in advance!
Your first approach is probably the best.
It doesn't have to be super-hacky, you can use a normal UIButton and customize it to look like a back-button using backgroundImageForState: and titleForState: (etc.), then set the UIButton object as the customView of your UIBarButtonItem.
Many apps these days customize the look & feel of the buttons anyway, so using a custom background image is quite normal. If you use resizableImageWithCapInsets: (or stretchableImageWithLeftCapWidth:topCapHeight: if you need to support iOS earlier than 5.0) then the button can still stretch to fit whatever text goes inside, and as it's a normal UIButton the text is localizable, etc. I don't consider this approach to be hacky, it is a perfectly sensible way to get around the limited functionality of UIBarButtonItem objects.
Better late than never.
To show a back button without pushing a view controller, use pushNavigationItem:animated: and popNavigationItemAnimated: on UINavigationBar. These result in the standard slide animation and creation of a back button. However, there is no way to ensure your content animation runs for the same time as the bar animation other than making an educated guess at the duration.
Since iOSĀ 7 there is a better API for achieving this effect, where you still push and pop view controllers but you provide a custom transition animation through navigationController:animationControllerForOperation:fromViewController:toViewController: from UINavigationControllerDelegate. This allows the animations between the bar and content to be perfectly coordinated.
Finally, if your content before and after is managed by UICollectionViewController, you can use useLayoutToLayoutNavigationTransitions, which is designed for use-cases like Photos.
I have an iPhone app that displays a modal view controller. The modal view controller shows two instances of a custom subclass of UITextView called RoundedTextView, an MKMapView, and a UIToolbar. I construct the viewController only once, and reset its data and present it each time the user summons it.
When showing this view controller with presentModalViewController, I noticed that the animation to show the view was choppy on the 3G. So, to speed it up, I set the alpha of the MKMapView and the two RoundedTextView objects to 0 on viewWillDisappear and back to 1 on viewDidAppear. This made it nice and fast. I also presume that I could remove the views from the superview to speed it up as well.
Does anyone else jump through these kind of hoops on the iPhone. Is there something else I should be doing to avoid this hack?
It's not a hack to simplify drawing during animation in order to make the animation more smooth. It is indeed a very valid technique.
You may be able to achieve similar performance improvements by setting all UI elements to Opaque, a technique also used to fix table view cell performance issues. You just have to make sure background colors match.
The main problem I had was I subclassed UIButton to make gradient buttons and I had the boundary mask enabled. This made the performance terrible. I removed that option and made my buttons square and it's blazin now.
I would like to implement the effect of a view sliding in, much like the animation of a view being brought in by presentModalViewController, but the view only slides in to cover half the screen.
I approached it this way:
1) Right before thew view should appear and slide in, addSubview the child view. Position it in such a way it's out of view in the beginning.
2) Call a method on the view controller of the subview to perform the Core Animation code to bring it in.
Doing the above didn't seem to work (nothing happens - the view just appears at its starting location). Adding the view ahead of time in viewDidLoad won't work either. Ideally, I would like the code to have the same requirement as presentModalViewController - which requires you to instantiate the view controller at the point you need to bring the view in and animate it.
Would appreciate if you can provide pointer or code on animating the view in/out as well.
I would check out the sample project ViewTransitions on the Apple dev site. It is a great resource for understanding the simple things you need to do when setting up transitions. If you wanted to only slide in half-way you could try setting the frame of the "sliding view" to only half the screen... just check out the code and give it a try.
I usually animate modal screen in the parent viewController instead of modal viewController itself.
Maybe you should post the code you use in step number 2.
I did something similar on one of my apps, where the user slides a view from one edge of the screen to the other as if placeing a cover over the screen.
If what you are trying to do is an automatic animation (without user interaction) I believe that you need to: set to the sliding view a frame representing the final position. Do this within an animation block. Play with the duration, animation curve, etc parameters.