Getting the selectedIndex of the currently selected tab from a viewcontroller - iphone

I currently have an iPhone application with a tabbar and multiple viewcontrollers. All the views are designed in Interface Builder. I'd like to be able to get the currently selected index of the tabbar from the viewcontroller, but for some reason this property returns (null).
I called the following in the viewDidLoad function of my viewcontroller:
self.tabBarController.selectedIndex
What would be the correct way to do this?
Updated with the code of the AppDelegate class.
MyAppDelegate.h
#import <UIKit/UIKit.h>
#interface MyAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> {
UIWindow *window;
UITabBarController *tabBarController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
#end
MyAppDelegate.m:
#import "MyAppDelegate.h"
#implementation MyAppDelegate
#synthesize window, tabBarController;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[window addSubview:tabBarController.view];
}
- (void)dealloc {
[tabBarController release];
[window release];
[super dealloc];
}
#end

You should have a pointer to your tabbar in your appDelegate class. Your view has no tabbar, so you recieve nil from [self.tabBarController selectedIndex].

I think I've got it. Using the following returns the correct index:
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog(#"%d", appDelegate.tabBarController.selectedIndex);
The reason the application was crashing was the fact that I used %# instead of %d in the NSLog part. I could have sworn I tried %d before, strange...
The index is now returned, but only once. After you tap the tab section an index number is returned, but when you tap another section again no number is printed. Probably because the view has already been loaded once. Is there any way to work around this?

Related

Null value when accessing variable in other classes (Combined Navigation & Tab Controller)

I have a navigation controller residing inside a tab bar controller and whenever I try to access a class from a class within the navigation controller all my values return (null).
This is how I'm trying to do it.
AppDelegate.h
#interface AppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate> {
NSString *searchQueryA;
}
#property (strong, nonatomic) NSString *searchQueryA;
ThirdViewController.h
#import "MasterViewController.h"
#import "AppDelegate.h"
#class MasterViewController;
#interface ThirdViewController : UIViewController {
code
}
#property (strong, retain) MasterViewController *masterViewController;
ThirdViewController.m
- (IBAction)showDetail:(id)sender {
AppDelegate *appDelegate = [[AppDelegate alloc] init];
appDelegate.searchQueryA = _searchField.text;
masterViewController = [[MasterViewController alloc] initWithNibName:#"MasterViewController" bundle:nil];
[self.navigationController pushViewController:masterViewController animated:YES];
}
MasterViewController.h
#import "AppDelegate.h"
#interface MasterViewController : UITableViewController
{
NSString *searchQueryM;
}
#property (nonatomic, strong) NSString *searchQueryM;
MasterViewController.m
AppDelegate *appDelegate = [[AppDelegate alloc] init];
searchQueryM = appDelegate.searchQueryA;
NSLog(#"%#", searchQueryM);
And in the log I can see that searchQueryM is (null). If I try to access the variable in AppDelegate from another class, that isn't involved with navigation controller, then it shows perfectly fine. What am I missing?
If you need to see more code I'd be happy to provide it.
EDIT:
For legibility I'll post code changes here:
I have the delegate in my AppDelegate.h
As Leonardo pointed out I only alloc'd and init'd my AppDelegate. I changed that snippet to this:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
searchQueryM = appDelegate.searchQueryA;
but still no go as searchQueryM still is (null).
This is what I do with searchQueryM
MasterViewController.h
#interface MasterViewController : UITableViewController
{
NSString *searchQueryM;
}
#property (nonatomic, strong) NSString *searchQueryM;
MasterViewController.m
#synthesize searchQueryM;
I'm fairly new to Objective-C (as well as OO-programming) and should probably read a book on it, but it seems to me like there isn't a lot more to it than that. Do correct me if I'm wrong.
EDIT 2
ThirdViewController.h
#interface ThirdViewController : UIViewController {
UITextField *_searchField;
}
#property (nonatomic, strong) IBOutlet UITextField *searchField;
ThirdViewController.m
#synthesize searchField = _searchField;
...
- (IBAction)showDetail:(id)sender {
_code_
NSLog(#"%#", searchField.text);
_code_
If i type in "asd" in the searchField textfield and output it with the log I get "asd".
}
Why are you alloc init your AppDelegate ?
The AppDelegate should be accessed with:
[[UIApplication sharedApplication] delegate]
We should see how you normally initialize searchQueryM, you are getting null, probably because the AppDelegate get only allocated and init, but the logic that initialize its properties never gets called.

how does this code work, re setting the UITableViewController's data list?

I have this code working, however I don't quite understand how it is managing to set the data source for the UITableViewController? Would this have to be occurring via Interface Builder settings somehow?
That is if you see the line "tableData = [[NSMutableArray alloc] initWithObjects: etc", and the fact that I don't see where my "tableData" instance variable here is actually assigned to be the data for the UITableView....
#interface RootViewController : UITableViewController <NewItemControllerDelegate> {
NSMutableArray *tableData;
}
#end
#implementation RootViewController
- (void)viewDidLoad {
[super viewDidLoad];
tableData = [[NSMutableArray alloc] initWithObjects:#"My Standard View", #"A Different View", nil]; // <== HOW IS THIS MANAGING TO SET THE VIEW WITH THE DATA
}
and for reference
#interface myProjectAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UINavigationController *navigationController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
#end
#implementation myProjectAppDelegate
#synthesize window;
#synthesize navigationController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Add the navigation controller's view to the window and display.
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
return YES;
}
PS (edit) So what I can't quite understand is the linkage between my "NSMutableArray *tableData" variable I declared in the RootViewController header file, and the actual UITableViewController's datasource so to speak? Is there a default "tableData" in a UITableViewController perhaps that is what I'm really setting or something, so I'm not really allocating new NSMutableArray to my variable I created but another one? (hope this makes sense)>
By default, UITableViewController sets itself as the delegate and datasource of its table view. Since your class is a subclass of UITableViewController, it does the same. Of course, this assumes that you have implemented all the UITableViewDataSource methods to actually use the tableData array (which you aren't showing us here).

How do I display core data on second view controller?

I am working on my first core data iPhone application. I am using a navigation controller, and the root view controller displays 4 rows. Clicking the first row takes me to a second table view controller. However, when I click the back button, repeat the row tap, click the back button again, and tap the row a third time, I get an error. I have been researching this for a week with no success.
I can reproduce the error easily:
Create a new Navigation-based Application, use Core Data for storage, call it MyTest which creates MyTestAppDelegate and RootViewController.
Add new UIViewController subclass, with UITableViewController and xib, call it ListViewController.
Copy code from RootViewController.h and .m to ListViewController.h and .m., changing the file names appropriately. To simplify the code, I removed the trailing “_” from all variables.
In RootViewController, I added #import ListViewController.h, set up an array to display 4 rows and navigate to ListViewController when clicking the first row.
In ListViewController.m, I added #import MyTestAppDelegate.h” and the following code:
- (void)viewDidLoad {
[super viewDidLoad];
if (managedObjectContext == nil) {
managedObjectContext = [(MyTestAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
..
}
The sequence that causes the error is tap row, return, tap row, return, tap row -> error. managedObjectContext is synthesized for the third time. I appreciate your patience and your help, as this makes no sense to me.
ADDENDUM: I may have a partial solution. http://www.iphonedevsdk.com/forum/iphone-sdk-development/41688-accessing-app-delegates-managed-object-context.html
If I do not release the managedObjectContext in the .m file, the error goes away. Is that ok or will that cause me issues?
- (void)dealloc {
[fetchedResultsController release];
// [managedObjectContext release];
[super dealloc];
}
ADDENDUM 2: See solution below. Sorry for the formatting issues - this was my first post.
I think I have the answer.
In the default Core Data Navigation Controller template, the AppDelegate does the following:
- (void)awakeFromNib {
RootViewController *rootViewController = (RootViewController *)[navigationController topViewController];
rootViewController.managedObjectContext = self.managedObjectContext;
}
and the RootViewController has the following code:
#interface PractitionerAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UINavigationController *navigationController;
#private
NSManagedObjectContext *managedObjectContext_;
NSManagedObjectModel *managedObjectModel_;
NSPersistentStoreCoordinator *persistentStoreCoordinator_;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
#property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
#property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (NSString *)applicationDocumentsDirectory;
#end
plus
- (void)dealloc {
[managedObjectContext_ release];
[managedObjectModel_ release];
[persistentStoreCoordinator_ release];
[navigationController release];
[window release];
[super dealloc];
}
In other words, when the managedObjectContext is set by code, either as above or in the tableView: didSelectRowAtIndexPath, then it needs to be deallocated.
On the other hand, if the managedObjectContext is not passed to the View Controller directly, and the following code is used to set the managedObjectContext...
if (managedObjectContext_ == nil) {
managedObjectContext_ = [(PractitionerAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
... then the managedObjectContext should not be released.
A much shorter answer is here. http://stackoverflow.com/questions/4028797/why-dont-i-have-to-release-managedobjectcontext-in-the-2nd-tableviewcontroller Apparently, even though the MOC is allocated in the View Controller, that has no effect since the MOC is owned by the AppDelegate.

EXC_BAD_ACCESS in tableview application

This is my first iPhone application and it's based on a top-level tableview. Selections of rows either go to another tableview or to a view. The application runs OK on the simulator but when ported to my iPhone it fails with a EXC_BAD_ACCESS error. This happens while my splash screen is being displayed. NSLog indicates that the program processes in appDelegate.m:
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
but then it just fails. The code never seems to reach the viewDidLoad in my RootViewController.
I'm sure that I've got the RootViewController and appDelegates mixed up somehow but cannot figure out exactly what's wrong. I've attached the beginning code of my RootViewController, appDelegate - any help appreciated.
RootViewController.h code....
#interface RootViewController : UITableViewController {
TyresViewController *tyresController;
EngineSpecViewController *engineSpecController;
CarbonTaxBandViewController *carbonTaxBandController;
TyreSpecificationsViewController *tyreSpecificationsController;
FuelConsumptionandEmissionsViewController *fuelConsumptionandEmissionsController;
CompanyCarTaxBandViewController *companyCarTaxBandController;
CarbonCalculatorViewController *carbonCalculatorController;
ReminderViewController *reminderController;
//NSString *selectedSpecification;
NSArray *listOfItems;
}
RootViewController.m code ......
#import "RootViewController.h"
#implementation RootViewController
#synthesize listOfItems;
//#synthesize selectedSpecification;
#synthesize carbonTaxBandController;
#synthesize engineSpecController;
#synthesize tyreSpecificationsController;
#synthesize tyresController;
#synthesize fuelConsumptionandEmissionsController;
#synthesize companyCarTaxBandController;
#synthesize carbonCalculatorController;
#synthesize reminderController;
appDelegate.h code.....
#interface MyCar3AppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UINavigationController *navigationController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
#end
appDelegate.m code .....
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after app launch
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
}
Just a thought, in your main Info.plist file there should be an entry for Main nib file base name. This refers to the nib that will be loaded when your app starts. The simulator is not case sensitive, but the device is. Check the case of the value of your main nib.

Calling a function from another UIViewController

I'm a beginner programmmer, this is for xcode - iPhone. although i made alot of my iPhone app but i seem to lack some understanding of how a simple communication might work.
Specially when I've got 2 ViewControllers.
And I wana call one function of a ViewController from another ViewController. Both are under a tabbarController. What I want to achieve is When I'm in ViewA, after tapping on a tableCell, I Should Invoke a method of ViewB and the NavigationBar of ViewB pushes to viewDetail.
The Following is the code i'm using
in ViewControllerA.h (where I'm calling a method)
#class ViewControllerB;
#interface SmartDDxViewController : UIViewController {
IBOutlet UITableView *tableView;
ViewControllerB *xViewController;
}
#property (nonatomic, retain) UITableView *tableView;
#property (nonatomic, retain) ViewControllerB *xViewController;
And this is what I use to invoke it..ViewControllerA.m
ViewControllerB *ddViewController = [[ViewControllerB alloc] init];
self.xViewController = ddViewController;
[xViewController InitialiseDetailWithId:2 title:#"HEYA"];
Heres the InitialiseDetailWithId code: in ViewControllerB.m
-(void)InitialiseDetailWithId:(NSInteger)pkey title:(NSString *)tt{
NSLog(#"InitialiseDetailC=========================================");
AppDelegate *appDelegate = (Smart_DifferentialsAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate GetConditionDetailsWithId:pkey];
DDisViewController *viewController= [[DDisViewController alloc] initWithNibName:#"DetailView" bundle:nil];
viewController.title = tt;
[self.NavBar pushViewController:viewController animated:YES];
//[tt release];
[viewController release];
viewController = nil;
[self say:#"HEYA"]; //this is ALERTVIEW box that displays HEYA
}
I'm getting all information fine, and the alertview does get displayed. But when I chose that View in TabBar, its not pushed.
Do not use direct access between view controllers, instead use the delegate pattern. Define your controller like this:
#protocol ViewControllerAInitDelegate;
#interface ViewControllerA : UIViewController {
IBOutlet UITableView *tableView;
id<ViewControllerAInitDelegate> initDelegate;
}
#property (nonatomic, retain) UITableView *tableView;
#property (nonatomic, assign) ViewControllerAInitDelegate *initDelegate;
#end
#protocol ViewControllerInitDelegate
-(void)initializeDetailWithId:(NSInteger)pkey title:(NSString)tt;
#end
So in
Now let your application delegate conform to the ViewControllerInitDelegate protocol. It should look something like this:
#interface AppDelegate : NSObject <UIApplicationDelegate, ViewControllerInitDelegate> {
IBOutlet UITabBarControler* tabBarController;
IBOutlet ViewControllerA* controllerA;
IBOutlet ViewControllerB* controllerB;
}
#end;
The AppDelegate should know about both ViewControllerA, and ViewControllerB, but neither of the view controller should know about each other. This way it will be much easier to debug and extend your app.
//current view controller index
int currentVCIndex = [self.navigationController.viewControllers indexOfObject:self.navigationController.topViewController];
//previous view controller (index -1)
AccountViewController *account = (AccountViewController *)[self.navigationController.viewControllers objectAtIndex:currentVCIndex - 1];
(access to anything you want)
account.property = object;
[account doSmthng];
In each of your view controllers, you might want to add a instance variable/property to keep track of the other view controller.
you might have for example:
#interface ThisViewController : UIViewController {
SomeViewController *sViewController;
// other instance variables
}
#property (nonatomic, retain) SomeViewController *sViewController;
This not only makes it easier to call methods from the other view controller and access its public properties, but it also allows you an easier way of flipping between the two (with or without animation).