Releasing a view controller after a CATransition: Am I doing this right? - iphone

My application is loading a first view (used to login into a Web service). When the login is successful, it performs a CATransition (basic kCATransitionFromRight) to show a second view and hides the first view. I've set the delegate of the transition to self so I can use -(void)animationDidStop:(CATransition *)theAnimation finished:(BOOL)flag.
When that method is called (right after the transition is over) I want to release the first view since I won't need it anymore. However, when I call [firstView release] (in animationDidStop:) the retain count doesn't seem to change. I used [loginView retainCount] to check this and since I know it's not always reliable I was wondering: am I doing this right?
Thank you.

taken from the book "Cocoa Touch for iPhone OS 3" is a similar approach.
They set up an animation remove the old subview, add the new one and then commit the animation.

Jilouc in his comment is right, forget to check "retaincount"...
if you want to be sure that your object view firstView just add a
NSLog(#"i'm removing myFirstView");
in its
-(void)dealloc{
}
method...
if you get that NSLog in debugger console window then be sure you had it removed/released in the right way...
btw... the right way could be something like this:
in animationDidStop:
if (firstView!=nil){
[firstView.view removeFromSuperview];
[firstView release];
firstView=nil;
}

Related

simple uiview change not working

I am trying to just do a simple view change for proof of concept.
here is the code
- (void)swipedScreen
{
if (self.secondView.superview == nil) {
[myView removeFromSuperview];
[self.view insertSubview:secondView atIndex:0];
}
}
when I swipe the screen what happens is the view area just goes black... and becomes unresponsive.
I started with a navigatoin app, replaced the tableview with just a standard uiviewcontroller class.. that worked fine..Then i added a secondView (xib only) and changed its class to match the viewcontroller of the first view.
The reason I am finding this difficult is because i am trying to animate the views inside the navigation controller and not push a whole view onto the stack which I am used to doing.
I'll bet that blank unresponsive view is, in reality, your secondView object. I always test by setting [secondView setBackgroundColor:[UIColor greenColor]] and checking if the massive green rectangle actually shows up.
EDIT: having looked at your code, there are multiple problems that arose:
You never actually +alloc or -init anything.
You never actually touch those nibs or make a reference to them in code
You declare two UIView's as IBOutlets and Strong (two exact opposites, as IBOutlets are __weak, __unsafe_unretained, or assign), yet do not link them to anything.
I've taken the liberty of revising it (sans nibs). Take a look.
Did you init the secondView? if init,you can try to set frame for the secondView
Your inserting the view at the bottom of the stack,
[self.view insertSubview:secondView atIndex:0];
Try using addSubview instead. Also you need to set the views frame somewhere.

When trying to view table view after viewing another, app crashes

i have a tough one for you today. I have two tableViews in my app the first is on the first page. There are two other pages the user drills down to get to the second table view. After i leave the first table view, i can press the back buttons to get back perfectly until i reach the second table view. As soon as i drill down to the second table view and then try to return to the first via pressing the back buttons. As soon as i get to the last back button to return to the first table view, the app crashes. The code for the back buttons is simply:
- (IBAction)goBack:(id)sender {
[self dismissModalViewControllerAnimated:YES];
}
Any Help Would be greatly appreciated!! Thanks everyone!! :D
Whenever I create a modal view controller from a XIB, the automatic #property generator duplicates things in the Dealloc method, thus throwing an EXC_BAD_ACCESS when the view is dismissed. Make sure you aren't releasing something twice.
Sounds like your are releasing something too early. Open you app in instruments (command + i) and run a zombie test.
As soon as you see zombie has been messaged expand the right panel and have a look at the user code (your code) blocks. Indicated by the back person icon.
Double click that and it will indicate what it was trying to access that had already been released.
Are your tableviews being displayed in a modal window? If not, why are you calling [self dismissModalViewControllerAnimated:YES]? Shouldn't you be calling [[self navigationController] popViewControllerAnimated:YES]?
If you're using a UINavigationController, the back button functionality should be provided automatically.
if you are using [[self navigationController] popViewControllerAnimated:YES] to
then for back you write as mentioned below:
(IBAction)goBack:(id)sender {
// Tell the controller to go back
[self.navigationController popViewControllerAnimated:YES];
}
if you are using [self presentmodalviewcontroller: animated:]
then only [self dismissModalViewControllerAnimated:YES] will work
you try this [[self navigationController] popViewControllerAnimated:YES]

Getting "Using two-stage rotation animation" warning with UIImagePickerController

I wrote simple code to test UIImagePickerController:
#implementation ProfileEditViewController
- (void)viewDidLoad {
[super viewDidLoad];
photoTaker_ = [[UIImagePickerController alloc] init];
photoTaker_.delegate = self;
photoTaker_.sourceType = UIImagePickerControllerSourceTypeCamera;
photoTaker_.showsCameraControls = NO;
}
- (void)viewDidAppear: (BOOL)animated {
[self presentModalViewController: photoTaker_ animated: NO];
}
#end
And I'm getting strange warnings like the following:
2010-05-20 17:53:13.838 TestProj[2814:307] Using two-stage rotation animation. To use the smoother single-stage animation, this application must remove two-stage method implementations.
2010-05-20 17:53:13.849 TestProj[2814:307] Using two-stage rotation animation is not supported when rotating more than one view controller or view controllers not the window delegate
Got any idea what this is about? Thanks a lot in advance!
This message will appear if you are presenting the UIImagePickerController within another UIViewController. Because it isn't pushed like a UINavigationController stack, there is confusion at the UIWindow level. I don't know if the warning is a problem, but to eliminate the warning you can do the following:
// self = a UIViewController
//
- (void) showCamera
{
cameraView = [[UIImagePickerController alloc] init];
[[[UIApplication sharedApplication] keyWindow] setRootViewController:cameraView];
[self presentModalViewController:cameraView animated:NO];
}
- (void) removeCamera
{
[[[UIApplication sharedApplication] keyWindow] setRootViewController:self];
[self dismissModalViewControllerAnimated:NO];
[cameraView release];
}
Perhaps you are adding the root UIViewController's view as a subview of the window instead of assigning the view controller to the window's rootController property?
IT ALL FALLS BACK ON THE UI
This warning can be implemented for several different objects: Pickers, keyboard, etc.
I have found that it is related to the UI taking two steps to complete a transition or other animation. Or for any instance where the UI is trying to finish one thing and is being asked to execute another before it has finished. (therefore it covers a wide range of possible triggers)
I have seen the warning appear on 4.0 & 4.2. In my case I was working with rotating the device and catching whether the keyboard was still up-(i.e. text field was still first responder). If so, the keyboard needed to stay up for between the views, but this presented other complications with other views.
Therefore, I implemented a BOOL tracker to keep track if keybaordIsPresent, and if so I was {textfield resignFirstResponder]; when detecting the orientation change and the reset the textfield to becomeFristResponder after the transition that was wrapped in an Animation Block. My BOOL tracker worked better, I still use the NSNotifications for the Keyboard, but there were overlaps of notifications during rotations because the keyboard was being dismissed without requesting such. The BOOL is set to NO on Load, and when the [textfield resignFirstResponder]; is implemented. *not when "-(void)keyboardWillhide is trigger by the NSNotifications, which gives me both working triggers that never conflict. The BOOL is set to YES, only when the user touches textfield which automatically triggers becomeFirstResponder.
I removed the warning by taking the [textfild resignFirstResponder]; out of the
-(void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
//if (keyboardIsPresent == YES) {[self.entryTextField resignFirstResponder];}
}
and placing it back at the top of the code for the:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
if (keyboardIsPresent == YES) {
[self.entryTextField resignFirstResponder];
}
//Determine Which Orientation is Present:
if((fromInterfaceOrientation == UIInterfaceOrientationPortrait) || (fromInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)){
//LANDSCAPE VIEW:
[self configureLandscapeView];
}else {
//PORTRAIT VIEW:
[self configurePortraitView];
}
}
**Even though I had no code inside the -(void)willAnimatFirstHalfOfRotationToInterface:, the warning was still popping up. I think the warning was still popping up because the compiler still has to attempt the method while it is trying to execute the first animation and therefore gets the double animation call or overlap of resources. It doesn't know that there is no executable code with the method until after it runs through it. And by that time it already set aside resource in preparation for handling possible actions within the method.
**To ellimiate the warning I had to remove or nil out the code for the willAnimateFirstHalfOfRotation, so that the compiler does not have to even check to see if there is a possible 2nd animation or action that may need to be executed at the same time.
/*-(void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
//if (keyboardIsPresent == YES) {[self.entryTextField resignFirstResponder];}}*/
After the transition is completed, within the original animation block I check to see if the "keyboardIsPresent" was YES prior to the rotation, and if so, I resign the First Responder once again. I use setAnimationDuration:0.3which comes out pretty clean and not jumpy.
Well, you are presenting UIImagePickerController modally inside viewDidAppear of ProfileEditViewController.
Think about this. That means when ProfileEditViewController view appears, the UIImagePickerController appears, say later you dismiss UIImagePickerController and it goes back to ProfileEditViewController, then viewDidAppear is called again and UIImagePickerController appears, say later you dismiss UIImagePickerController and it goes back to ProfileEditViewController, then viewDidAppear is called again and.... you get the idea.
That warning is rather cryptic though, not sure if that is what it's trying to tell you. I would suggest making a button somewhere on the ProfileEditViewController that calls presentModalViewController and also make sure you have a way to dismiss the UIImagePickerController (I've never used it not sure if it has one automatically).
You may be trying to present two modal view controllers at the same time, and they are fighting for animation resources.
1) There is rarely any UI reason to do this. You could instead just go directly to the second view controller (the image picker); and, after dismissing it, then present the first view or view controller.
2) If you do want two stacked view controllers or a view controller on top of a view, then set a timer in viewDidAppear to present the second view controller after the first one has finished it's animation. (You could display a dummy png image of a blank picker in the first one to prevent too much display flashing until the second view controller goes live.)
EDIT - Added a random code example:
I might try substituting this as an experiment:
- (void)foo {
[self presentModalViewController: photoTaker_ animated: NO];
}
- (void)viewDidAppear: (BOOL)animated {
NSTimer *bar = [ NSTimer scheduledTimerWithTimeInterval: (2.0f)
target: self
selector: #selector(foo)
userInfo: nil
repeats:NO ];
}
A shorter time delay may work as well.
I just had the same problem. In my case was a silly mistake that I'm putting here just in case anyone else falls into that same issue.
In my tabbed app I remove one of the original ViewControllers and added a new one with Storyboard to create a "Settings" section.
This new VC had to be a table view VC and even I designed, compiled and run it without a problem, when I changed the orientation of the app I kept getting this “Using two-stage rotation animation” error.
My problem was that I forgot to change in the original .h file interface "UIViewController" for "UITableViewController".
Once this was done I changed on the Storyboard identity badge the class from the general value to my SettingsViewController and that was the end of it.
I hope it can help someone else. It took me a while to get to bottom of this.
Cheers,
I think the warning here is about Core Animation performance. As a test, I loaded the image picker without any action sheet or other animations going on and the warnings are still there. I think these are warnings coming from the image picker class itself and not from any misuse of the API.

How to recreate UIViewController stack?

I'm writing a 'load' feature for my current iPhone app.
Practically what I want to do is get the user back where s/he left off. To do so I want to recreate all the UIViewControllers appeared before.
My problem is the UIViewControllers won't appear if I simply call [[self navigationController] pushViewController:newController animated:YES];.
The same code works fine when invoked as an event handler, like after touching a button. But if I do same without an even (for ex. in viewDidLoad) the new view controller's loadView method won't get called.
My actual code (with some pseudo elements):
- (void)viewDidLoad {
[super viewDidLoad];
if (loading)
[self onSomeEvent];
}
- (void)onSomeEvent {
UIViewController *newController = //init....;
[[self navigationController] pushViewController:newController animated:YES];
[newController release];
}
I guess viewDidLoad is not the right place to do such a call, but then what is?
I'm not sure that spreading your "load" feature all accross your controllers is the best way to achieve it.
I would rather put it in the init of your application, in the applicationDidFinishLauching part. Then you have a centralized place where you restore the previous state (easier to handle IMHO).
Let's suppose you want to implement some more advanced restore feature like:
displaying a splash UIView with an activity indicator to indicate the user that you're restoring previous state
restore the stack of your controllers in the navigation controller
remove the splash
it's easier to have all this code in the applicationDidFinishLauching : it's all managed at one point.
Morever, when restoring the old state to your navigation controller, you can avoid using the transitions and use animated:NO instead of YES when pushing your controllers. It will be easier to handle from your perspective and the restore time may be decreased if you remove time needed to achieve transitions.

Objective-C/iPhone: Windows' view not responding to button clicks!

So in my app delegate I add a call add the myViewController.view to the main window:
1. [window addSubview:myViewController.view];
In myViewController I do the following code in the viewDidAppear method:
2. [self presentModalViewController: yourViewController animated: YES];
In my yourViewController class I do the following to try and go back to the main window
3. [self dismissModalViewControllerAnimated:YES];
My main windows view appears with buttons in all, but the buttons won't react to any click or anything. It's like there is something over them that I can't see.
Also, the main windows button works before this process but doesn't after the process.
Any help would be appreciated.
If the dismiss method call is in the modal view controller (not the parent that presents it), then you actually want to call [self.parentController dismissModalViewControllerAnimated:YES];
There are a number of reasons why things might not be responding to your touches. Here are two that have happened to me:
The frame of the view you want to touch is too small. UIViews can draw outside of their frames, so it might look ok, but not respond if the touch is technically outside of the frame -- you also have to check that all the superview's up the hierarchy also have a large enough frame.
If anything in your view is a UIImageView or child thereof, it won't respond to user touches because UIImageView has userInteractionEnabled set to NO by default. You can fix this just by setting myImageView.userInteractionEnabled = YES;
Edit: Oli pointed out in the comments that dismissModalViewControllerAnimated: should work if called on either self.parentController or simply self, since that method is smart enough to call the parent if needed, according to the docs. The docs also make it sound like this might behave differently if you have multiple model views open at once, though, so I would still consider it cleaner code to call the method on self.parentController directly.