Trying to make my iphone app universal. Navigation Controller keeps saying Unbalanced calls - iphone

have been trying couple of hours now to make my iphone app universal. The mission was successful but have a strange problem. The navigation controller keeps pushing things without even pushing anything. The app doesn't crash but it gives me a message in the console
nested push animation can result in corrupted navigation bar
2012-02-06 10:52:07.701 ##$%^^$[54755:207] Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
2012-02-06 10:52:07.704 !##$%^$%#[54755:207] Unbalanced calls to begin/end appearance transitions for <searchEditViewController: 0xc652150>.
and here is how i've set app the whole thing :
![navigation screen shot][1]
here is my code in the AppDelegate_iphone.h
#import <UIKit/UIKit.h>
#import "iPhoneView.h"
#import "AboutUsViewController.h"
#import "FavoritesViewController.h"
#class iPhoneView;
#interface AppDelegate_iPhone : NSObject <UIApplicationDelegate> {
UITabBarController *tabBarController;
UINavigationController *homeNavigationController;
UINavigationController *favouritesNavigationController;
AboutUsViewController *aboutUsViewController;
iPhoneView * search;
FavoritesViewController *favoritesViewController;
UIWindow *window;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet iPhoneView *search;
#property (nonatomic, retain) FavoritesViewController *favoritesViewController;
#end
and here is on my AppDelegate.m file
#import "AppDelegate_iPhone.h"
#import "iPhoneView.h"
#implementation AppDelegate_iPhone
#synthesize window,search,favoritesViewController;
#pragma mark -
#pragma mark Application lifecycle
- (void)applicationDidFinishLaunching:(UIApplication *)application
{ tabBarController = [[UITabBarController alloc] init];
homeNavigationController = [[UINavigationController alloc] init];
search = [[iPhoneView alloc] init];
[homeNavigationController pushViewController:search animated:NO];
favouritesNavigationController = [[UINavigationController alloc] init];
favoritesViewController = [[FavoritesViewController alloc]init];
[favouritesNavigationController pushViewController:favoritesViewController animated:NO];
aboutUsViewController =[[AboutUsViewController alloc] init];
UITabBarItem *item = [[UITabBarItem alloc] initWithTitle:#"επικοινωνία" image:[UIImage imageNamed:#"aboutus"] tag:0];
aboutUsViewController.tabBarItem = item;
[item release];
UITabBarItem *item2 = [[UITabBarItem alloc] initWithTitle:#"αγαπημένα" image:[UIImage imageNamed:#"favorites"] tag:0];
favouritesNavigationController.tabBarItem = item2;
[item2 release];
NSArray *tabBarControllerCollection = [NSArray arrayWithObjects:homeNavigationController,favouritesNavigationController,aboutUsViewController,nil];
[tabBarController setViewControllers:tabBarControllerCollection animated:NO];
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
}
- (void)applicationWillResignActive:(UIApplication *)application {
/*
Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
*/
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
*/
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
/*
Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background.
*/
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
}
- (void)applicationWillTerminate:(UIApplication *)application {
/*
Called when the application is about to terminate.
See also applicationDidEnterBackground:.
*/
}
#pragma mark -
#pragma mark Memory management
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
/*
Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later.
*/
}
- (void)dealloc {
[tabBarController release];
[search release];
[favoritesViewController release];
[favouritesNavigationController release];
[aboutUsViewController release];
[window release];
[super dealloc];
}
#end

OK solved had connected the button 2 times with the same action in IB. Took me 48hours to figure it out!

Related

How to remove UINavigationViewController

I have created one navigationController in name_of_my_appAppDelegate.h...
After the use I want to remove it from the superview...
in my name_of_my_RootViewController I want to call it and remove.
How to call it?
In the NewsPadViewController, how to remove the NavigationController when I finished to use it?
#import <UIKit/UIKit.h>
#class NewsPadViewController;
#interface NewsPadAppDelegate : NSObject <UIApplicationDelegate>{
UIWindow *window;
UINavigationController *navigationController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
#end
and this is the implementation
#import "NewsPadAppDelegate.h"
#import "NewsPadViewController.h"
#implementation NewsPadAppDelegate
#synthesize window;
#synthesize navigationController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[window addSubview:navigationController.view];
[window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
/*
Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
*/
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
*/
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
/*
Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
*/
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
}
- (void)applicationWillTerminate:(UIApplication *)application
{
/*
Called when the application is about to terminate.
Save data if appropriate.
See also applicationDidEnterBackground:.
*/
}
- (void)dealloc
{
[navigationController release];
[window release];
[super dealloc];
}
#end
Check this out: UINavigationController Class Reference.
You probably want something like this:
[name_of_my_RootViewController popToRootViewControllerAnimated:YES];
Or:
[name_of_my_RootViewController.view removeFromSuperview];
Or:
for (UIView *v in self.view.subviews) {
if ([v isEqual:myView]) {
[myView removeFromSuperview];
}
}
Or:
[((NewsPadAppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController.view removeFromSuperview];
To remove the view controller you remove its view, like this:
[name_of_my_RootViewController.view removeFromSuperview];
Sounds like you should be presenting the UINavigationController modally in the first place. Set up a plain UIViewController called rootViewController and make that visible instead of the navigation controller, then call:
[rootViewController presentModalViewController:navigationController animated:YES];
And when you're done with it, hit a button on the navigation controller which calls:
[self dismissModalViewControllerAnimated:YES];
And you'll go back to the plain UIViewController where you can show the rest of your app.

releasing objects?

I'm really new to iPhone programming.
This app is a simple quiz. FirstAppDelegate.m creates an instance of QuizViewController and adds its view to the window.
#import "FirstAppDelegate.h"
#import "ResultViewController.h"
#import "QuizViewController.h"
#implementation FirstAppDelegate
#synthesize window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions (NSDictionary *)launchOptions {
UIViewController *vc = [[QuizViewController alloc] init];
[window addSubview:[vc view]];
[window makeKeyAndVisible];
[vc release];
return YES;
}
- (void)dealloc {
[window release];
[super dealloc];
}
#end
I thought I could release vc like I do hear since window will retain it (?) but it generated an error :
2011-06-28 23:06:34.190 First[14289:207] -[__NSCFType foo:]: unrecognized selector sent to instance 0x4e1fc90
2011-06-28 23:06:34.193 First[14289:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFType foo:]: unrecognized selector sent to instance 0x4e1fc90'
...so I commented it and now it works fine. But where should I release vc? Here's QuizViewController.h:
#import <UIKit/UIKit.h>
#interface QuizViewController : UIViewController {
IBOutlet UILabel *questionLabel;
IBOutlet UIButton *button1;
IBOutlet UIButton *button2;
IBOutlet UIButton *button3;
int currentQuestionIndex;
int corrects;
NSMutableArray *questions;
NSMutableArray *answers;
NSMutableArray *correctAnswers;
}
- (IBAction)foo:(id)sender;
#end
...and QuizViewController.m:
#import "QuizViewController.h"
#implementation QuizViewController
- (id)init {
NSLog(#"QuizViewController init");
[super initWithNibName:#"QuizViewController" bundle:nil];
questions = [[NSMutableArray alloc] init];
answers = [[NSMutableArray alloc] init];
correctAnswers = [[NSMutableArray alloc] init];
[questions addObject:#"Vad betyder det engelska ordet \"though\"?"];
[answers addObject:#"Tuff"];
[answers addObject:#"Dock"];
[answers addObject:#"Tanke"];
[correctAnswers addObject:#"Dock"];
[questions addObject:#"Vad hette frontpersonen i Popbandet Queen?"];
[answers addObject:#"Pierre Bouviere"];
[answers addObject:#"Freddie Mercury"];
[answers addObject:#"Stevie Wonder"];
[correctAnswers addObject:#"Freddie Mercury"];
return self;
}
- (IBAction)foo:(id)sender {
NSLog(#"foo");
}
- (void)loadView {
NSLog(#"QuizViewController loadView");
[questionLabel setText:[questions objectAtIndex:currentQuestionIndex]];
[button1 setTitle:[answers objectAtIndex:currentQuestionIndex] forState:UIControlStateNormal];
[button2 setTitle:[answers objectAtIndex:currentQuestionIndex + 1] forState:UIControlStateNormal];
[button3 setTitle:[answers objectAtIndex:currentQuestionIndex + 2] forState:UIControlStateNormal];
[super loadView];
}
- (void)viewDidLoad {
NSLog(#"QuizViewController viewDidLoad");
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
[super viewDidUnload];
}
- (void)dealloc {
[super dealloc];
}
#end
You should create an instance variable to hold the VC. The reason you are losing it when you release it is that the window is only retaining the view and not the controller.
I thought I could release vc like I do hear since window will retain it...
Notice that you are adding the view associated to the view controller ([vc view]) to your UIWindow. That object will be retained, not your controller.
You can fix this by defining a variable in your FirstAppDelegate to store the controller there and release it in FirstAppDelegate dealloc.
#interface FirstAppDelegate
.....
#property (nonatomic, retain) QuizViewController* controller;
.....
#end
#implementation FirstAppDelegate
#synthesize window;
#synthesize controller;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions (NSDictionary *)launchOptions {
self.controller = [[QuizViewController alloc] init] autorelease];
[window addSubview:[vc view]];
[window makeKeyAndVisible];
return YES;
}
- (void)dealloc {
....
[controller release]; controller = nil;
....
}
The views/windows do retain their child views, the view controllers retain their views, but the views don't retain their controllers. It's a "one-way" relationship, a clear has-a. This also comes in handy to prevent retain cycles.
You probably want to save the controller in an ivar in the class you alloc/init it, and release it in dealloc or when you pull the view from screen.
View controllers often get retained by other view controllers, i.e. when you push them onto a navigation stack, or put them in tabs.
If you don't mind dropping support for iOS 3.0/3.1/3.2, you can use the UIWindow.rootViewController property:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions (NSDictionary *)launchOptions {
UIViewController *vc = [[[QuizViewController alloc] init] autorelease];
window.rootViewController = vc;
[window makeKeyAndVisible];
return YES;
}

Handling an external screen on the iPad

Ok, I think its possible I've misunderstood the correct way to implement an external screen on the iPad and it is causing me a lot of headaches.
Since this is a long post, what I'm trying to do is create and send a view to an external screen over VGA, and remove the screen once I'm done with it. I'm having retain count issues so can't get it to work.
I have a view controller that can be called up onto the iPad at any time. When this view loads (it is a remote, similar to Keynote presentation) I check for an external screen, then create a window and add a view to the extra monitor.
in my ipadViewController.h <-- the view that stays on the iPad
I have
#interface ipadViewController : UIViewController {
PresentationViewController *presentationView;
UIScreen *externalScreen;
UIWindow *externalWindow;
}
#property (nonatomic, retain) UIScreen *externalScreen;
#property (nonatomic, retain) UIWindow *externalWindow;
#property (nonatomic, retain) PresentationViewController *presentationView;
#end
(There is more, but that is the external screen stuff).
in ipadViewController.m:
#synthesize externalScreen;
#synthesize externalWindow;
#synthesize presentationView;
So I try to do a few things when the view loads:
Get the external screen (if possible)
Create apresentationViewController and add it to the extra screen
- (void)viewDidLoad {
[super viewDidLoad];
[self getExternalScreen];
[self createPresentationAndSendToWindow];
}
to get the screen I do the following getExternalScreen::
if ([[UIScreen screens] count] > 1)
{
for (UIScreen *currentScreen in [UIScreen screens])
{
if (currentScreen != [UIScreen mainScreen])
self.externalScreen = [currentScreen autorelease];
}
}
and to send the view to it createPresentationAndSendToWindow::
if (self.presentationPath == nil) return;
PresentationViewController *viewController = [[PresentationViewController alloc] initWithNibName:#"CanvasPresentation" bundle:nil];
self.presentationView = viewController;
[viewController release];
if (self.externalWindow == nil)
{
CGRect externalBounds = [self.externalScreen bounds];
self.externalWindow = [[[UIWindow alloc] initWithFrame:externalBounds] autorelease];
[self.externalWindow addSubview:self.presentationView.view];
self.externalWindow.screen = self.externalScreen;
[self.externalWindow makeKeyAndVisible];
}
in dealloc I try to cleanup with:
[presentationView release];
[externalScreen release];
//[externalWindow release]; <- that would crash
Problem I have is that when I dismiss the remoteViewController (it is modal), after releasing externalScreen has a retain count = 1 and externalWindow has retain count = 2.
The crash caused by externalWindow release disappears if I don't release presentationView (but then I'm leaking presentationView.
Your problem is here:
for (UIScreen *currentScreen in [UIScreen screens])
{
if (currentScreen != [UIScreen mainScreen])
self.externalScreen = [currentScreen autorelease];
}
Remove the autorelease. You shouldn't be releasing something you didn't create or retain.
you are autoreleasing externalView on
self.externalWindow = [[[UIWindow alloc] initWithFrame:externalBounds] autorelease];
and then you assign an autorelease view to it
self.externalScreen = [currentScreen autorelease];
you cannot release an autoreleased view, or it will crash.

How to get started coding In-App Purchase

I am working on in-app purchase. In my application we added the following code in appdelegate:
#import "InappPurchaseAppDelegate.h"
#import "MainController.h"
#import "MKStoreManager.h"
#import "MKStoreObserver.h"
#implementation InappPurchaseAppDelegate
#synthesize window;
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
[MKStoreManager sharedManager];
navigationController = [[UINavigationController alloc] init];
[window addSubview:navigationController.view];
MainController *frontController =[[MainController alloc] init];
[navigationController pushViewController:frontController animated:NO ];
[frontController release]; // Override point for customization after application launch
[window makeKeyAndVisible];
}
and added the following code in our controller:
#import "MainController.h"
#import "MKStoreManager.h"
#import "MKStoreObserver.h"
#import "InappPurchaseAppDelegate.h"
#implementation MainController
-(IBAction)InappPurchase:(id)sender
{
[[MKStoreManager sharedManager] buyFeatureA];
}
I also added storekit framework but when the button is clicked nothing happens.
All you need to know is here: http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Overview%20of%20the%20Store%20Kit%20API/OverviewoftheStoreKitAPI.html#//apple_ref/doc/uid/TP40008267-CH100-SW1
It shouldn't take you more than half a day to implement it (maybe a bit more if the content resides on your servers and is not already in the bundle).
The simplest explaination is that your button is not properly configured to send the action message. To test either set a breakpoint for the method or log it like:
-(IBAction)InappPurchase:(id)sender
{
NSLog(#"Buyid method called");
[[MKStoreManager sharedManager] buyFeatureA];
}
If the NSLog or breakpoint are never hit, you need to check the button in Interface Builder and make such it's action is set to the InappPurchase method.
If the InappPurchase method is being called by the button then the problem is in the MKStoreManger object.

View loaded from nib will not update contents of UILabel while running in a method

I am having an issue with updating the contents of an "myInfoBox" object I created to be displayed while some background processes are done.
In the delegate method I am creating a new viewController:
-(void)loadMainView
{
myFirstViewController = [[MyFirstViewController alloc] initWithNibName:#"MyFirstView" bundle:nil];
navigationController = [[UINavigationController alloc] initWithRootViewController:myFirstViewController];
// myFirstViewController was retained again by the controller, release one
[myFirstViewController release];
navigationController.navigationBar.hidden = YES;
[window addSubview:navigationController.view];
[window makeKeyAndVisible];
// the next method is run after the "viewDidLoad" is finished loading
[myFirstViewController loadAlertViewForNewUser];
}
Following is my implementation of "myFirstViewController", it creates an instance of the "infoBox" class(I will show its code later):
- (void)viewDidLoad {
self.navigationController.navigationBar.frame = CGRectMake(0, 0, 0, 0);
self.navigationController.navigationBar.bounds = CGRectMake(0, 0, 0, 0);
self.myInfoBox = [[InfoBoxController alloc] initWithNibName:#"InfoBox" bundle:[NSBundle mainBundle]];
CGRect infoBoxFrame;
infoBoxFrame = CGRectMake(60, 120, 200, 200);
myInfoBox.view.frame = infoBoxFrame;
myInfoBox.i_statusLabel.text = #"Downloading Account Updates";
myInfoBox.i_titleLabel.text = #"Updating";
// disabled for testing
//myInfoBox.view.hidden = YES;
[self.view addSubview:myInfoBox.view];
[super viewDidLoad];
}
// this method is called after the view has been loaded by the delegate
- (void)loadAlertViewForNewUser
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Welcome!" message:#"Connect to download stuff from your account?"
delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"OK", nil];
alert.tag = 0;
[alert show];
}
// implementation of the alertview delegate
- (void)alertView:(UIAlertView *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (actionSheet.tag == 0)
{
if (buttonIndex == 0)
{ NSLog(#"button 0 was pressed"); }
if (buttonIndex == 1)
{
// this is the button that is pressed
[actionSheet removeFromSuperview];
[actionSheet release];
// tried using this also
//[self performSelectorOnMainThread:#selector(userInitialSetupMainThread) withObject:nil waitUntilDone:NO];
// do stuff and update the infobox about it
[self loadInfoBoxInitialUserSetup];
// tried using this as well
//[self performSelectorInBackground:#selector(loadInfoBoxInitialUserSetup) withObject:nil];
}
return;
}
}
- (void)loadInfoBoxInitialUserSetup
{
[self performSelectorOnMainThread:#selector(userInitialSetupMainThread) withObject:nil waitUntilDone:NO];
}
- (void)userInitialSetupMainThread
{
// fetch JSON data
NSDictionary *responseJSON = [NSDictionary dictionaryWithDictionary:[self getUserstuff]];
self.myInfoBox.i_statusLabel.text = #"Processing Recieved information";
// breakpoint - nothing changes in the view on the simulator
[myInfoBox.view setNeedsLayout];
// breakpoint - nothing changes in the view on the simulator
[myInfoBox.view setNeedsDisplay];
// breakpoint - nothing changes in the view on the simulator
[myInfoBox.parentViewController.view setNeedsLayout];
// breakpoint - nothing changes in the view on the simulator
[myInfoBox.parentViewController.view setNeedsDisplay];
// breakpoint - nothing changes in the view on the simulator
[myInfoBox performSelectorOnMainThread:#selector(updateValuesForTitle:) withObject:#"test" waitUntilDone:YES];
// breakpoint - nothing changes in the view on the simulator
[self.view setNeedsLayout];
// breakpoint - nothing changes in the view on the simulator
[self.view setNeedsDisplay];
// breakpoint - nothing changes in the view on the simulator
self.myInfoBox.i_statusLabel.text = #"Reloading...";
// breakpoint - nothing changes in the view on the simulator
[self readStuffFromDB];
sleep(2);
//disabled view removal for testing..
//[self.myInfoBox.view removeFromSuperview];
// breakpoint - nothing changes in the view on the simulator
}
What happens for me in the testing is that the myInfoBox object is created on screen when the -(void)loadMainView method is complete, then I can see on screen the "myInfoBox" in the background while the alertView in front (for testing...) at this point the screen is responsive and I can select the YES, once I select yes the delegate method is called.
As I commented in the source file, using breakpoints I am monitoring the simulator and following the code, never the less the changed label values are not reflected while I am still in the - (void)userInitialSetupMainThread method, but once it finishes the view updates with the latest set .text value!! grrr..
Also, the source for the myInfoBox class:
#interface InfoBoxController : UIViewController {
IBOutlet UILabel* i_titleLabel;
IBOutlet UILabel* i_statusLabel;
IBOutlet UIImageView* i_loadingImage;
IBOutlet UIImageView* i_background;
IBOutlet UIActivityIndicatorView* i_activityIndicator;
}
#property(nonatomic, retain) IBOutlet UILabel* i_titleLabel;
#property(nonatomic, retain) IBOutlet UILabel* i_statusLabel;
#property(nonatomic, retain) IBOutlet UIImageView* i_loadingImage;
#property(nonatomic, retain) IBOutlet UIImageView* i_background;
#property(nonatomic, retain) IBOutlet UIActivityIndicatorView* i_activityIndicator;
//- (void)updateValuesForTitle:(NSString *)title Label:(NSString *)label;
- (void)updateValuesForTitle:(NSString *)title;
#end
#implementation InfoBoxController
#synthesize i_titleLabel, i_statusLabel, i_loadingImage, i_background;
#synthesize i_activityIndicator;
//-(void)updateValuesForTitle:(NSString *)title Label:(NSString *)label
-(void)updateValuesForTitle:(NSString *)title
{
self.i_titleLabel.text = title;
self.i_statusLabel.text = title;
[self.i_titleLabel setNeedsDisplay];
[self.i_statusLabel setNeedsDisplay];
}
Sorry for the LOONG post :)
PLEASE ASSIST!
At the risk of sounding unhelpful, that's kind of just how it works. If you have long-running code in the main event loop (i.e., you don't explicitly create a thread or similar), the operating system won't be able to update the UI.
To update the UI while your code is running, you either need to run your complex operation in the back ground using thread, NSOperationQueue, etc, or just break it into smaller steps and return control to the main loop occasionally so that the UI can be updated.