When you click a "contact" button in my app, a number of views are animated to slide the initial views out and the contact views in. One of the options at that point is to send and email. When you tap that button, I use MFMailComposeViewController to show the in-app email view.
The Issue:
As the email view animates in, the display reverts back to almost the initial view. The views in which I animated their frame, are reset to their original locations, but the one view I fade out stays faded out.
I am fairly certain I am not doing anything out of the ordinary with the animations and the mail composer code is exactly the same as all the references I have found.
EDIT:
Here is my animation code. I am basically animate a couple views off the screen and a couple others on the screen. Everything is layed out in a single view controller on a storyboard.
[UIView animateWithDuration:0.5f animations:^{
searchView.frame = CGRectMake(0, originalSearchY - searchView.frame.size.height, searchView.frame.size.width, searchView.frame.size.height);
footerView.frame = CGRectMake(0, originalFooterY + footerView.frame.size.height, footerView.frame.size.width, footerView.frame.size.height);
refreshView.alpha = 0;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.5f animations:^{
supportView.frame = CGRectMake(0, 64, supportView.frame.size.width, supportView.frame.size.height);
closeView.frame = CGRectMake(0, originalCloseY - closeView.frame.size.height, closeView.frame.size.width, closeView.frame.size.height);
} completion:^(BOOL finished) {}];
}];
Here is a screen flow of whats happening for clarification...
Tap the contact button, animated to the contact screen
Tap on the email button, email composer is shown
Cancel or send email
As you can see the visibility is reverted back to screen 1, with "update data" view gone (this is the view I fade out rather than animate the frame)
Ok, so I finally figured out the issue. I was doing some initialization in - (void)viewDidLayoutSubviews and that is called as the modal email view appears and as it is dismissed. I ended up adding a boolean "isInitialized" variable and tweaking the view processing based on that.
Related
I have a UIView "MainView" that initially appears as follows:
The gradient bar is part of MainView, the whitespace beneath is part of a Container View subview.
When the search button in top-right is tapped, I animate a searchBar from offscreen to be visible in the view:
I manage to do this by the following code:
CGRect currentViewFrame = self.view.bounds;
currentViewFrame.origin.y += searchViewHeight;
[UIView animateWithDuration:0.4
delay:0.0
usingSpringWithDamping:1.0
initialSpringVelocity:4.0
options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.view.frame = currentViewFrame;
}
completion:^(BOOL finished)
{
}];
Visually, the result of this animation is perfect. The entire view shifts, and the searchBar is now on screen. However, the searchBar does not respond to interaction. Correct me if I'm wrong, but I expect this is because the MainView's frame no longer includes the screen area that the searchBar now occupies, so its effectively a gesture deadzone.
So this makes me think that instead of lazily animating the entire MainView down to accomodate the searchBar, I must instead individually translate all subviews of MainView one at a time. In this simple situation, that would not be a big problem, but I can envision a circumstance with tens of subviews making that completely unrealistic.
What is the best method to accomplish what I am trying to do? Is there a secret to animating entire views/subviews without having gesture deadzones? Thanks in advance!!
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.
This is a strange problem. I have an iPhone view controller for creating a new user with some text fields that require animation to avoid the keyboard when it is shown, i.e. the view animates up and down to keep a minimum clearance between the top of the keyboard and the lower edge of the text field. That worked fine when it was presented modally from the login existing user screen, the initial view controller in the storyboard.
Then I decided to change the app a bit so that the login/create user views would belong to the same view controller. They transition like so:
if(accountCreateView == nil) {
UINib *nib = [UINib nibWithNibName:#"NewAccountView" bundle:nil];
NSArray *nibViews = [nib instantiateWithOwner:self options:nil];
accountCreateView = [nibViews objectAtIndex:0];
}
[UIView transitionFromView:(displayingLoginView ? loginView : accountCreateView)
toView:(displayingLoginView ? accountCreateView : loginView)
duration:0.6
options:(displayingLoginView ?
UIViewAnimationOptionTransitionFlipFromRight :
UIViewAnimationOptionTransitionFlipFromLeft)
completion:^(BOOL finished) {
if (finished) {
displayingLoginView = !displayingLoginView;
}
}
];
This works fine too. However the code that does animation of the views does not. When a text field becomes active the code to check proximity of the text field to the keyboard is called but nothing happens when the animation block executes.
So I checked:
if([UIView areAnimationsEnabled])
NSLog(#"Animations are enabled");
else
NSLog(#"Animations are disabled");
Animations are certainly enabled.
So I moved it back further to viewDidLoad, trying a simple animation to see what works, before anything else:
CGRect viewFrame = loginNameTextField.frame;
viewFrame.origin.y = 400.f;
[UIView animateWithDuration:1.f
delay:0.f
options:UIViewAnimationOptionCurveEaseInOut
animations:^ {
loginNameTextField.frame = viewFrame;
}
completion:NULL];
This works fine! Text field moves slowly down the window.
This does nothing, and that is the confusing part:
CGRect viewFrame = self.view.frame;
viewFrame.origin.y = 400.f;
[UIView animateWithDuration:1.f
delay:0.f
options:UIViewAnimationOptionCurveEaseInOut
animations:^ {
self.view.frame = viewFrame;
}
completion:NULL];
viewFrame when set from self.view.frame has origin 0,20 and size 320,460 as expected.
I tried setting the frame property directly:
CGRect viewFrame = self.view.frame;
viewFrame.origin.y = 400.f;
self.view.frame = viewFrame;
Looks like I can change the frame of subviews of self.view but not of self.view. A fix might be to leave the initial view controller as a blank view in the storyboard and create xibs for both the login view and create user view and load them in viewDidLoad - but why?
edit: Yes, switching the storyboard version of the view controller to a plain empty view and adding the view containing the actual UI from a xib as a subview does make animations work again. Still don't know why though.
If all you need to do is keep your text fields above your keyboard, why not drop the UIViewController and change to a UITableViewController and use static cells? You place one text field per cell (creating enough cells to meet your needs) and style it how you like. When you don't have a first responder and there are few enough cells to fit on the screen, it won't scroll. It there are more than what will fit, then it will scroll to reach the bottom/top cells. Yet no matter how many cells you have, if you select a text field that will be covered up by the keyboard, the device will automatically scroll the cell to a viewable position. It's all built in and I use this feature all the time. It works with text fields and text views. Just remember to comment out the number of sections, number of rows per section, and the cell for index path methods so the static cells will work correctly. It saves a lot of coding.
HTH
Rob
I have a split view controller in my Ipad app, in which the detail view has subviews which are UITableView's.
The functionality I am trying to implement is to get an info view at the same position of a subview when a button(info button) on the subview is pressed. I need to get this info view by animation and that animation is that when the info button is pressed, that subview alone would flip (UIAnimationOptionFlipFromRight) and the info view would show...
This is how try to implement it -
-(void) showInfoView: (id)sender
{
infoView = [[InfoViewController alloc] initWithNibName:#"ViewViewController" bundle:nil];
infoView.view.frame = CGRectMake(250, 300, 200, 200);
[UIView transitionWithView:self.view duration:1
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
[self.view addSubview:infoView.view];
}
completion:nil];
}
When I run the simulator and press the info button on any subview, what happens is that the animation happens perfectly, i.e. the subview flips from the right but the infoView is not getting displayed.
It would be great if someone could tell me where I am going wrong here.
The basic steps of performing an animation to onscreen is as follows:
Create the view.
Move it to the initial (offscreen) position
Add to the view hierarchy
Perform the animation.
It seems like you're skipping step 3 and possibly step 2.
Did you try setting the subview frame to the frame of your superview, i.e
info.View.frame = self.view.frame;