my app consists of a table view controller and a view controller. when i press a cell in the table view, the toolbar at that view slips with animation down outside the screen and when i'm in the view controller and press back, the toolbar bar slips upwards to it's original position. My problem is, i figured out a bug that when i'm in the view controller and press the home button to exit the app and then come back. the app resumes where i left but when i go back to the table view, the toolbar shifts upwards beyond it's original position. the sliding of the toolbar works fine when i'm in the app before exiting. so there's like something is being called to reset the toolbar to it's origin and thus adding the additional y-axis point to shift more upwards. does anybody know what are those methods?
Code:
i have this in the viewWillAppear method of the view controller:
[UIView animateWithDuration:0.7 animations:^{
self.navigationController.toolbar.center = CGPointMake(self.navigationController.toolbar.center.x, self.navigationController.toolbar.center.y + self.navigationController.toolbar.frame.size.height);
} completion:^(BOOL finished){
self.navigationController.toolbar.hidden = YES;
}];
and in the same view when it needs to disappear i added this in the viewWillDisappear:
[[self.navigationController toolbar] setHidden:NO];
[UIView animateWithDuration:1 animations:^{
self.navigationController.toolbar.center = CGPointMake(self.navigationController.toolbar.center.x, self.navigationController.toolbar.center.y - self.navigationController.toolbar.frame.size.height);
} completion:^(BOOL finished){
}];
i tried this as another way to animate the hiding of the toolbar but there is no animation:
- (void) viewWillAppear:(BOOL)animated
{
[self.picker setHidden:YES];
[self.navigationController setToolbarHidden:YES animated:YES];
}
- (void) viewWillDisappear:(BOOL)animated
{
[self.navigationController setToolbarHidden:NO animated:YES];
[self.course setValue:self.nameTextField.text forKey:#"courseName"];
[self.course setValue:[NSNumber numberWithInt:[self.creditsTextfield.text integerValue]] forKey:#"courseCredits"];
[self.course setValue:[NSNumber numberWithInt:[self.chaptersTextfield.text integerValue]] forKey:#"courseChapters"];
[self.course setValue:self.gradeTextfield.text forKey:#"courseGrade"];
}
For one thing, you should use frame instead of center, but replace the viewWillDisappear animation line with this:
self.navigationController.toolbar.frame = CGRectMake(0,0,self.navigationController.toolbar.frame.size.width, self.navigationController.toolbar.frame.size.height);
and tell me what happens...
That should fix your problem.
Since you are using the toolbar belonging to a UINavigationController (and not a standalone UIToolbar instantiated and added to a UIView by your own controller), it's better to use the methods that UINavigationController exposes, because you don't know how it manages and move its UIToolbar.
Try to put just this in viewWillAppear:, instead of the entire animation block
[self.navigationController setToolbarHidden:NO animated:YES];
and this in viewWillDisappear:
[self.navigationController setToolbarHidden:YES animated:YES];
i fixed it! i aded the following lines of code in viewWillDisappear:
self.navigationController.toolbar.center = CGPointMake(self.navigationController.toolbar.center.x, 458);
self.navigationController.toolbar.center = CGPointMake(self.navigationController.toolbar.center.x, self.navigationController.toolbar.center.y + self.navigationController.toolbar.frame.size.height);
since the problem seems that when the app goes background then foreground the toolbar resets to it's original position and thus after navigating back to the table view the toolbar is shifted beyond it's original position. therefore the first line i added resets the toolbar to it's original position while still hidden then shift it down. after that the block of animation is done. i did this so that the animation works on the following cases:
1. the user enters the detail view from the table view then goes back to the tableview without exiting the app.
2. the user enters the detail view from the tableview then exits the app and then resume the app and goes back to the table view.
Related
I am working on an iOS app. The root view controller contains a UINavigationController which wraps up the main contents of the app, and a footerViewController (audio player) that will compress the main content when it animates up into view.
I’m using auto layout to show and hide this footer like so:
_footerVisibleConstraints = [… #“V:|[navControllerView][footerView(==90)]|" …];
_footerHiddenConstraints = [… #“V:|[navControllerView][footerView(==0)]|" …];
Generally this works well. But I’m struggling with one issue. I have a situation where I need to push a new view on the UINavigationController stack and animate my footer into view the same time:
[self.view layoutIfNeeded];
[UIView animateWithDuration:1.5f animations:^{
[[self view] removeConstraints:_footerHiddenConstraints];
[[self view] addConstraints:_footVisibleConstraints];
[[self view] layoutIfNeeded];
}];
[navigationController pushViewController:newViewController animated:YES];
The problem in this situation is that newViewController is animating in snapped to it's final (compressed) state, and not beginning from the full starting height of the view. So there is a gap at the bottom while the footer animates in.
I’ve slowed down the animation and posted a video here to demonstrate what I am describing.
Also, notice how when I pop back to the root view controller the content in the UINavigationController isn’t compressed either.
So, can someone explain to me what’s going on here? Is there a way to accomplish what I am after?
Just add a variable to the .h of your VC to stipulate whether the footer needs to open or not. Then add the footer animation to the didAppear method with a check on the variable. This will result in performing the actions in the order you want them to happen.
If you want both animations to happen at the same time you will need to subclass a segue and add a custom animation.
I have a navigation controller, its root view controller is of type CollectionViewControllerA. Upon selecting an item, I have a fade out and expanding animation followed by a call to push a second view controller of type CollectionVewControllerB onto the stack:
CollectionViewControllerB *b = ... // construction of the view controller to be pushed
[UIView animateWithDuration:.3
animations:^{
self.collectionView.transform = CGAffineTransformMakeScale(1.5, 1.5);
self.collectionView.alpha = 0;
}
completion:^(BOOL s){
[self.navigationController pushViewController:b animated:NO];
}];
I pop the view controller in a similar way
[UIView animateWithDuration:.3
animations:^{
self.collectionView.transform = CGAffineTransformMakeScale(.3, .3);
self.collectionView.alpha = 0;
}
completion:^(BOOL s){
[self.navigationController popViewControllerAnimated:NO];
}];
The problem here is that the app crashes when popping the view controller. Reason:
*** -[CollectionViewControllerB scrollViewDidScroll:]: message sent to deallocated instance
I understand that the problem is because the popped view controller is destroyed, but why does scrollViewDidScroll: get called in the first place? Nothing changes the contentOffset of the collectionView in code, and there is no user interaction either. Unless changing the transform property also triggers the method to get called?
CollectionViewControllerB implements scrollViewDidScroll: because I need to disable vertical scrolling.
Meanwhile I have a very very messy hack to prevent the crash, that is before the animation, I add
self.collectionView.delegate = nil;
This stops the method from getting called. But there has to be a better way.
Can anyone shed some light on why scrollViewDidScroll: is called and how it can be stopped?
It seems like the only way to solve the problem is what I have already done... setting the delegate to nil before the animation.
self.collectionView.delegate = nil;
Hopefully this helps someone else in the future.
Set self.automaticallyAdjustsScrollViewInsets = NO; inside your view controller.
I had a problem similar to this, and found that on navigating away from the page, the contentOffset was changing by 20 every time.
I found that setting this property inside my view controller stopped this changing, and therefor scrollViewDidScroll was no longer being called. Turns out the view controller automatically adjusts content insets for changes in status bar, nav bar etc.. even when you navigate away.
I think this is a better solution and proper explanation as to why the scrolling method was being called.
You can try this in your controller .
[self setEdgesForExtendedLayout:UIRectEdgeNone];
Hope this would help you.
Here's the situation:
I am making an app for iPad w/ iOS 6 using Autolayout along with UINavigationController. What I am trying to do is:
Segue from one view controller to the next with a standard push segue.
When I arrive at the new view controller, hide the nav bar with animation.
As the nav bar hides, I want my view to not shift at all. In fact, I want my view to effectively be drawn underneath the nav bar from the beginning, so I'm left with no shifting or movement of content and no black bars. For reference, this is what happens in the Amazon Kindle app when you go into a book.
With my current code, the contents of my view shift up to fill in the void left by the UINavigationBar.
I've tried force-setting the frame of my UIViewController's view and my UINavigationController's view to the entire iPad screen in the viewWillAppear method of my viewcontroller but no dice. I've experimented w/ Constraints in Autolayout but that also didn't get me to where I wanted to go.
Any help you can give would be great!
Try following before animating the navigation bar:
self.navigationController.navigationBar.alpha = 0.99f;
I didn't try this but this should work.
Looks like you need to add custom navigation bar in your new view and animate it to disappear.
I think, hiding original Navigation bar of Navigation Controller without shifting the view is not possible.
Rather add UINavigationBar to xib file, bind it to IBOutlet uiNavigationBar and try following code
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES animated:NO];
}
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
CGRect f = self.uiNavigationBar.frame;
f.origin = CGPointMake(f.origin.x, f.origin.y - 44);
self.uiNavigationBar.frame = f;
} completion:^(BOOL finished) {
NSLog(#"done");
}];
}
I am using the code snippet from Tito to add a custom button to my tab bar:
https://github.com/tciuro/CustomTabBar
(Subclassing UITabbarController and adding a custom button using
// .. created a UIButton *button
[self.view addSubview:button];
)
This works great with my storyboard-based app except for the case of a subview within a navigation controller with the option "Hides bottom bar on push" enabled.
This hides the tab bar as promised, but not the custom button.
Seems like the button should be added as a subview to the tab bar itself?
I tried this ugly code which did not even make the button show up:
for(UIView *view in self.view.subviews)
{
if([view isKindOfClass:[UITabBar class]])
{
[view addSubview:button];
break;
}
}
Any ideas?
UPDATE:
My solution:
In my ApplicationDelegate i define the following methods, which i call whenever needed in the viewWillAppear or viewWillDisappear methods:
-(void)hideCenterButton:(BOOL)animated
{
if(animated){
[UIView animateWithDuration:0.3
delay:0.0f
options:UIViewAnimationCurveLinear
animations:^{
CGRect frame = self.centerButton.frame;
frame.origin.x = -100;
self.centerButton.frame = frame;
}
completion:^(BOOL finished){
}];
}
}
-(void)showCenterButton:(BOOL)animated
{
if(animated){
[UIView animateWithDuration:0.35
delay:0.0f
options:UIViewAnimationCurveLinear
animations:^{
CGRect frame = self.centerButton.frame;
frame.origin.x = (self.view.superview.frame.size.width / 2) - (self.centerButton.frame.size.width / 2);
self.centerButton.frame = frame;
}
completion:^(BOOL finished){
}];
}
}
I had to set the animation's duration to 0.35s to get a smooth effect in harmony with the tab bar.
Why don't you make button your tabbar's part.
tabBarController.tabBar.addSubView(yourButton)
everything would be solve. cheers!
One easy way to handle this would be to create an instance of the button in .h of your file.
UIButton *customTabButton;
When calling the hides bottom bar on push set the button property to hidden and reset it again in the other views if the bottom bar is visible.
shareFbButton.hidden=YES;
You can check this is the viewDidLoad of all the files and put this line of code if needed to make sure you are displaying the button and hiding the button on all the pages you need.
if(self.tabBarController.tabBar.isHidden){
// set or reset the custom button visibility here
}
This is one way.
I think there are 2 ways you can got with this.
1) try to get the button into a view that is above the old top view controller and the tab bar BUT below the new top view controller that is pushed.
2) animate away the button when the new view controller is pushed.
The first will require mucking with the iOS proprietary view hierarchy which is undocumented, unsupported and could change anytime.
The second will be a matter of making the animation appear smooth enough for your user not to notice. It's not entirely a matter of behaving perfect, just appearing appropriately.
I would personally recommend an animation of the the button disappearing (animate it's alpha to 0) and reappearing based on if your view controller that goes over the tab bar is appearing or disappearing.
The animation for a navigation is (I believe) 0.3 seconds. If the button is in the middle of the tab bar, you'll likely want it invisible as the animating in view controller reaches it (if not sooner) so something between 0.1 and 0.15 seconds could be used to animate it out.
Now this does not make the button behave exactly the same as the tab bar, but with the quickness of the transition being so short, it will be unnoticeable really to the user.
Now just to provide a question for you to ask yourself. Why do you need to push a view controller that overlaps the tab bar? Why is that more desirable/necessary than presenting a modal view controller? If you can strongly argue for it, keep at it and good luck, if it's not necessary however, you may be able to achieve the experience you want with a modal view controller.
Check this one to put a button on the UITabBar. See if it works after with hidesBottoBarWhenPushed.
Edit: just to be clearer on what I am looking to do. I have 1 view with button on it. When you press the button a new view transisions in with a flip. On the new view their are two switched that I set through code after the user has clicked the button. What I want to happen is that the switches are in their final spots, before the new view flips in. What happens is the new view flips in, and then the UIswitches flicker into place.
This is the code I have. I have tried setting the uiswitch on viewwillappear and viewdidload, but each time it comes over with the default setting from IB and switched after the UIview transition is complete. I want the switch to be set already, so that once the view "flip" is done, there is no more movement on the screen. Thanks.
//set the uiswitch before the transision
[self.settingsPage.myUISwitchThing setOn:NO animated:NO];
//transition by flip
[UIView transitionFromView:self.view toView:self.settingsPage.view duration:1.0 options:UIViewAnimationOptionTransitionFlipFromRight completion:^(BOOL done ) {
[self.view removeFromSuperview];
Looks like the issue was that I was trying to animate the switch movement when I was setting the switch value in the viewdidload. setting animated:NO has resolved the issue in the iPhone simulator. hopefully it will fix it on the device when I can test it at home later.