UIView Animation doesn't fire when called from different view controller? - iphone

I'm calling a method from another view controller by using this:
InitialViewController *secondController = [[InitialViewController alloc] init];
[secondController forecast];
Here's the method in the InitialViewController:
-(void)forecast{
[UIView beginAnimations:#"Forecast" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:1.0f];
self.customPager.frame = CGRectMake(0,5, 320, 510);
self.view1.frame = CGRectMake(-320,5, 320, 510);
radarView.frame = CGRectMake(0,560, 320, 510);
[UIView commitAnimations];
NSLog(#"Method Passed");
}
In my console, I get the NSLog "Method Passed", but the UIView animation does not occur at all. Any ideas?

It seems to me that you are not displaying in any way the view associated to secondController. I.e., after doing:
InitialViewController *secondController = [[InitialViewController alloc] init];
I would expect you do something like:
[self.view addSubview:secondController.view];
This would trigger loadView/viewDidLoad before you call forecast. Furthermore, I would give a chance to the UI to be show your view before animating it; thus, I would call forecast either like this:
[self performSelector:#selector(forecast) withObject:nil afterDelay:0.0];
or from viewDidAppear.
EDIT:
According to your comment,
your InitialViewController is already displayed on screen. In this case, what you should do is getting a reference to it and sending it the forecast message.
What you are doing now is instantiating a new InitialViewController (and then sending the forecast message to it) that has no relation with the one already displayed.

Even though you instantiate secondController, its view is never displayed on screen, or even loaded, so any animations that you apply to it have no effect.

If your second controller is already on the screen, then your alloc init is creating another one, not getting the one already present (the log works because you are creating an instance of InitialViewController, so its code will run, but another instance's view is the one you're seeing on screen). You need to get a reference to the one on screen. I can't say how you should do that without knowing how you got your 2 controllers on the screen in the first place.

Related

UIView transitions

I am using ViewControllers for my different views, this is currently how I am switching from page to page:
- (IBAction)switchToSecondPage:(id)sender
{
SecondPage * secondPage = [[SecondPage alloc] initWithNibName:nil bundle:nil];
[self presentViewController: secondPage animated:YES completion:NULL];
}
This is the typical new view coming up from the bottom of the screen. I can use this to change the transition style, but I only get a couple choices to chose from, and none of them are any good.
secondPage.modalTransitionStyle = UIModalTransitionStylePartialCurl;
I am looking for transitions that are similar to the original one you get (page slides up from bottom). So I want 'slides down from top' and 'slides from left/right'. Is there any way I can do this. I see all sorts of applications doing this but I haven't been successful in finding anything.
One way to do this is,
[self.view addSubview:secondPage.view];
secondPage.view.frame = frameOutSideCurrentView;//Frame is set as outside the screen
[UIView animateWithDuration:1.0f
animations:^{
//actual frame needed with an animation.
secondPage.view.frame = originalFrame;
}
completion:^(BOOL finished) {
//if you need to anything specific once the animation is completed.
}];
Another way to do is by using this. TransitionController is custom implementation which helps to do this. In case you are facing issue with above code, use the category methods in this implementation. There are methods such as,
- (id)initWithViewController:(UIViewController *)viewController;
- (void)transitionToViewController:(UIViewController *)viewController
withOptions:(UIViewAnimationOptions)options;
which can be used to achieve this.

UIView transition causing crash

- (IBAction)StartGame:(id)sender
{
MainGameDisplay *secondPage = [[MainGameDisplay alloc] initWithNibName:nil bundle:nil];
[self.view addSubview:secondPage.view];
secondPage.view.frame = CGRectMake(568, 0, 568, 320);//(N = horizontal, N = vertical)
[UIView animateWithDuration:1.0f
animations:^{
//actual frame needed with an animation.
secondPage.view.frame = CGRectMake(0, 0, 568, 320);
}
completion:^(BOOL finished) {
//ENTER HERE ANYTHING TO RUN AFTER ANIMATION IS COMPLETED:
[self presentViewController: secondPage animated:NO completion:NULL];
//This will make the next page load correctly after the transition, otherwise you cannot. interact with anything.
}];
}
This works perfectly for entering the view. Without [self presentViewController: secondPage animated:NO completion:NULL];, you cannot interact with the new page, any button you click causes it to crash.
If I return back to the first page, doing it normally, it's fine, but if I click StartGame again, the program changes views and then simply crashes. The problem is with the line [self.view addSubview:secondPage.view];, if I comment this out, the program doesn't crash but when it changes views it doesn't transition and instantly appears.
The transition is also pretty laggy/glitchy in landscape mode, perfect in portrait though.
Here you are Adding secondPage on currentView as aSubview. and Again your presenting it.
I think it causing some memory related thing may be some other thing(it would be much clear if you put some crash log here in your question). But i'll suggest you no need to present secondPage Again .Here is your Code i made Single Line Change here .
- (IBAction)StartGame:(id)sender
{
MainGameDisplay *secondPage = [[MainGameDisplay alloc] initWithNibName:nil bundle:nil];
[self.view addSubview:secondPage.view];
secondPage.view.frame = CGRectMake(568, 0, 568, 320);//(N = horizontal, N = vertical)
[UIView animateWithDuration:1.0f
animations:^{
//actual frame needed with an animation.
secondPage.view.frame = CGRectMake(0, 0, 568, 320);
}
completion:^(BOOL finished) {
//ENTER HERE ANYTHING TO RUN AFTER ANIMATION IS COMPLETED:
You should comment below sinlge because you have already added secondPage as a Subview on currentView.so further no need to present it again.
// [self presentViewController: secondPage animated:NO completion:NULL];
//This will make the next page load correctly after the transition, otherwise you cannot. interact with anything.
}];
}
Edit: For Landscape Mode you should change coordinate while you are setting the frame secondPage view . i think here you are creating this view for 4 inch Devices.
here you can do this by setting some checks as this
if(checkIfDeviceIsInlandscae)
{
secondPage.view.frame = CGRectMake(568, 0, 568, 320);
}
else
{
secondPage.view.frame = CGRectMake(320, 0, 320, 568);
}
Inside the animation block change the frame according to the orientation, as i did above tow line.
One problem is by calling both presentViewController: and addSubview: you are adding the view twice. I suspect you find it necessary to call presentViewController: because it invokes viewDidAppear: and you're using that to hook up the UI in secondPage. I'd put a logging statement in MainGameDisplay's viewDidAppear: method and see when it is getting called.
One fix may be to just replace [self presentViewController: secondPage animated:NO completion:NULL] with [secondPage viewDidAppear:NO].
Your problem is one of memory allocation/deallocation. You're not retaining secondPage anywhere, and as such it will be deallocated after the StartGame method logic is completed. This deallocation will in turn deallocate MainGameDisplay's ivars/properties too, including MainGameDisplay.view. This will all happen before you start to interact with the displayed view, so the moment you try to change values or interact with the no-longer-existent view, you're going to crash with an EXC_BAD_ACCESS error.
If you truly want to manage MainGameDisplay.view outside of the view controller, you'll need to retain secondPage in an ivar or member variable in your parent class:
ParentClass.m:
#interface ParentClass ()
{
MainGameDisplay *_secondPage;
}
#end
...
- (IBAction)StartGame:(id)sender
{
_secondPage = [[MainGameDisplay alloc] initWithNibName:nil bundle:nil];
[self.view addSubview:_secondPage.view];
...
}
However, this is not generally a recommended design pattern. It's the responsibility of a view controller to manage its view, particularly layout changes that might be made, animations in how it's displayed, etc. You're violating the encapsulation responsibilities of MainGameDisplay. You should consider doing whatever view management you need to do inside of MainGameDisplay.
I would recommend reading through the View Controller Programming Guide for iOS. It will explain the fundamentals around what you're trying to do, and best practices for doing it.

Animating UIView while pushing a UINavigationController

following situation.
i have an app that uses a UINavigationController for navigating.
For a push of a special Navigation Controller i want a custom Animation, a zoom out.
What i have is looking good so far, the only problem is, that the "old" Viewcontroller disappears before the animation starts, so the new viewcontroller zooms out of "nothing" instead of viewing the old viewcontroller in the background.
For better viewing i created a simple sample app: download
Does anybody know, how to animate the new viewcontroller (image3) that the old view controller(image1) stays in the background while animating (image 2)?
/* THIS CANNOT BE CHANGED */
AnimatedViewController *animatedViewController = [[AnimatedViewController alloc] initWithNibName:#"AnimatedViewController" bundle:nil];
/* THIS CAN BE CHANGED */
animatedViewController.view.transform = CGAffineTransformMakeScale(0.01, 0.01);
[UIView beginAnimations:#"animationExpand" context:NULL];
[UIView setAnimationDuration:0.6f];
animatedViewController.view.transform=CGAffineTransformMakeScale(1, 1);
[UIView setAnimationDelegate:self];
[UIView commitAnimations];
/* THIS CANNOT BE CHANGED */
[self.navigationController pushViewController:animatedViewController animated:NO];
Additional information: my app isn't that simple. i use three20 framework for my app but the pushing and creating of the view controllers is simply what three20 does. i only can hook into the part between (THIS CAN BE CHANGED in my code). i cannot change the code before and after this (except with a lot of researching).
I whipped this up very quickly. The idea is to call pushViewController after the scale animation is completed.
-(IBAction)animateButtonClicked:(id)sender {
animatedViewController = [[AnimatedViewController alloc] initWithNibName:#"AnimatedViewController" bundle:nil];
animatedViewController.view.transform = CGAffineTransformMakeScale(0.01, 0.01);
[UIView animateWithDuration:0.6
animations:^{
[self.view addSubview:animatedViewController.view];
animatedViewController.view.transform=CGAffineTransformMakeScale(1, 1);
}
completion:^(BOOL finished){
[animatedViewController.view removeFromSuperview];
[self.navigationController pushViewController:animatedViewController animated:NO];
}];
}
Ok, one way to do this (a bit ugly) is to do the animation and upon the completion of the animation, do the pushViewController. When you do the animation, you need to animate up what the about-to-be-presented view controller's screen would look like. Since the animation ends with the new view, when the new view comes to the screen, nothing should change because its the same as the just animated view.

UIView Animations stop working after dismiss Modal View

I just upgraded my iPhone 4 from iOS 4.2.1 to 4.3.2, and to XCode 4.0.2, and I am encountering some bizarre issues with uiview animations. When I first launch my app, code like this executes perfectly:
[UIView beginAnimations:#"fadeAlphaIn" context:nil];
[UIView setAnimationDuration:0.5f];
viewClue.alpha = 1.0f;
[UIView commitAnimations];
But then, after dismissing a presenting and then dismissing a modal view by the standard method:
[self presentModalViewController:more animated:YES];
and
[self dismissModalViewControllerAnimated:YES];
the first animation no longer works. Instead of fading in, for example, the viewClue view simply jumps from alpha = 0 to alpha = 1. Similarly, other animations altering other views' frame property just force the frame to jump from the initial to final value without animation. These animations worked fine before the modal view was presented and dismissed.
I understand that others have experienced animation issues with the upgrade to iOS 4.3.2, but the way the modal view disrupts animation seems very odd. Has anyone else experienced this problem? Any ideas as to a solution? I'm thinking of just adding the modal view as a subview and animation it as it hides and appears, but using the standard modal view method would be much preferred.
Thanks for your help,
James
EDIT: Some more code showing how the app's map is animated
-(void) viewMapfunc
{
AudioServicesPlaySystemSound(soundID);
if(mapvisible){
[UIView animateWithDuration:0.5
delay:0.1
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
map.frame = CGRectMake(0, 350, 320, 27);
mapscroll.frame = CGRectMake(0, 27, 320, 0);
}
completion:nil];
mapvisible = NO;
viewMapLabel.text = #"View Map";
}else {
[UIView animateWithDuration:0.5
delay:0.1
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
map.frame = CGRectMake(0, 50, 320, 300);
mapscroll.frame = CGRectMake(0, 27, 320, 300);
}
completion:nil];
mapvisible = YES;
viewMapLabel.text = #"Hide Map";
}
}
Try to check two things:
Do you commit all started animations? I got all kinds of strange effects after not committing one of them.
Do any animations take place in the same time? Especially with the same view.
Whether any animations take place right after changing properties. Something like:
-
view.alpha = 1;
[UIView beginAnimations:…];
view.alpha = 0;
[UIView commitAnimations:…];
In this example, view will not change it's alpha value from 1 to 0. It will change it instantly. To start an animation you have to extract animations block to another method and call it with performSelectorInMainThread:withObject:afterDelay:. Delay can be even 0.
I solved it by restarting my animation in my UIView subclass:
override func willMove(toWindow newWindow: UIWindow?) {
if newWindow != nil {
spinner.startSpinning() // Restart any animation here
}
}
In the end, I just removed all modal views and implemented them in other ways. For some reason, using modal views messed up animations. Makes no sense, but removing them fixed the problem. If anyone can enlighten me as to why this is going on, it might be nice for memory concerns...
I had the same issue. The root of my trouble was that my animation was being triggered by a notification, and I was adding an observer on each viewWillAppear, but forgot to remove in viewDidDisappear (remember that iOS 6 no longer calls viewDidUnload reliably).
Essentially, I was calling my animation function twice in quick succession, which was causing the visible irregularity. Hopefully this helps someone out down the line!
I've managed to solve this same issue in my own application.
I noticed while debugging that my UIImageViews which I was animating had different memory addresses before and after I pushed my modal view controller(s). At no other time did these UIImageViews switch their memory addresses.
I thought this might have been the root of the issue and it seems I was right.
My client's code had been allocating/initializing my View Controller's UIImageViews in
-viewDidAppear instead of in -viewDidLoad. Thus, every time I launched and dismissed a modal view controller my UIImageViews I was animating would get reinitialized.
Check for yourself if your map object's memory address is changing before and after you launch your modals, and if it is be sure to move your initialization logic to a more proper section of your code.
Hope this helps you!
Dexter
I was using UIView animateWithDuration: and I solved it by not using the completion block. This is code from a subclassed UIView. In the view controller's viewWillAppear: I set self.shouldAnimate to YES, and in the view controller's viewWillDisappear: I set self.shouldAnimate to NO.
-(void)continueRotate {
if (self.shouldAnimate) {
[self rotateRadarView:self.radarInner];
}
}
-(void)rotateRadarView:(UIView *)view {
[UIView animateWithDuration:0.6 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(continueRotate)];
[view setTransform:CGAffineTransformRotate(view.transform, M_PI_2)];
}completion:nil];
}

iphone xcode; releasing a view instance crashing the table with datasource

My code crashes with a bad access, when i have a view with tables. I have figured what the problem is, but looking for the right solution.
In View 1 - i have a button that creates and instance of view 2 and then releases it, something like this:
settingsScreen *settings = [[settingsScreen alloc] initWithNibName:#"settingsScreen" bundle:nil];
CGRect theFrame = settings.view.frame;
//theFrame.origin = CGPointMake(self.view.frame.size.width, 0);
theFrame.origin = CGPointMake(0,-(self.view.frame.size.height));
settings.view.frame = theFrame;
theFrame.origin = CGPointMake(0,0);
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.8f];
settings.view.frame = theFrame;
[UIView commitAnimations];
[self.view addSubview:settings.view];
[settings release];
In view 2 there is table, where i am setting the datasource and delegate to self on viewDidLoad, but this crashes the app with exec_bad_access
If i remove [settings release] in View 1, everything is fine.
If i do not release the view, is'nt it wrong to leave it in the memory?
how can i get over this situation?
Thanks
you should use either pushViewController or presentModalViewController instead of adding a view of one Viewcontroller in another Viewcontroller and when you want to come back you can use popViewController or dismissModalViewController, this helps in memory management as well. Also use [NSBundle mainBundle] for bundle parameter in initWithNibName which finds proper xib in the bundle.
I am sure somehow your settings instance is being released somewhere in your code again and it makes your app unstable.
Sanniv's answer is correct. You should use push-pop concept of navigation controller or present the controller in a modal way.
(Visit: i) http://forums.macrumors.com/showthread.php?t=472236 & ii) http://www.iphonedevsdk.com/forum/iphone-sdk-development/3643-pushviewcontroller-versus-presentmodalviewcontroller.html for more info).
Now the error you're getting is because of your "settings" object is being used in animation which you're releasing before your animation gets finished. Thus, instead of releasing the object immediately, release in another method which would be called exactly after animation stops. Like following way:
settingsScreen *settings = [[settingsScreen alloc] initWithNibName:#"settingsScreen" bundle:nil];
CGRect theFrame = settings.view.frame;
//theFrame.origin = CGPointMake(self.view.frame.size.width, 0);
theFrame.origin = CGPointMake(0,-(self.view.frame.size.height));
settings.view.frame = theFrame;
theFrame.origin = CGPointMake(0,0);
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.8f];
[UIView setAnimationDidStopSelector:#selector(releaseObjects)];
settings.view.frame = theFrame;
[self.view addSubview:settings.view];
[UIView commitAnimations];
In the "releaseObjects" objects method, you should release your settings object. And in a case, if your settings object is a local, do it autorelease like this way -
settingsScreen *settings = [[[settingsScreen alloc] initWithNibName:#"settingsScreen" bundle:nil] autorelease];