Detect the viewcontroller - iphone

i have a application which is handling lots of functions and also having lots of navigations.
I am using lots of BOOLs inside my application. I know its not efficient. So I want to remve these BOOLs to create more efficient and clean application.
This is my question. For a Example lets say I have 3 UIViewControllers.
Test1 , Test2, Test3
I need to navigate to Test3 from both Test1 and Test2 view Controllers . Also if I navigate to Test3 View Controller from Test1, I need to execute one method and, if I navigate to Test3 from Test2, I need to Execute another method.
Currently what i am doing is, I am Using Globals.h and Globals.m classes to over come this problem. I create a BOOL in Globals and enable that BOOL value in Test1 and, I checked that BOOL value inside Test3 and execute the method i want.
This is just an example. I got lots of view controllers and lots of behaviors for the application. So I create lots of BOOLs inside Globals and used them in different Classes. So this is a pain to handle lots of bools in one application and it not good too. So can any one please help me how to overcome this problem.
Thanks in advance :)

you can also check class with NSObject method isKindOfClass.
You can also find example given in that method explaination.
Here you can compare the object is which kind of class, and accordingly you can perform your operation.
For that you can pass your self reference to Controller3 every time and you can store it with id type.
Hope this will help you in your code.

Like #mrunal said you can use isKindOfClass. just figured I through in some code.
// self is Test3
if ([self.presentingViewController isKindOfClass:[Test1ViewController class]]) {
// Run your method for Test1 - Test 3 here.
}
If you're pushing or presenting a modal you'll need to grab the actual viewController since presentingViewController will be a UINavigationController. This is how I do it.
// self is Test3
if ([self.presentingViewController.childViewControllers.lastObject isKindOfClass:[Test1ViewController class]]) {
// Run your method for Test1 - Test 3 here.
}

Try using the viewControllers property of the navigation controller.
UINavigationController reference
What I mean is, when a view controller loads that you need a specific action done based on where it came from grab the view controllers array and look at the object at location n - 2 (where n is the number of elements in the array). Then test the type of class of that object using isKindOfClass method and perform the appropriate action.
Rough Example:
-(void)viewDidLoad {
[super viewDidLoad];
NSArray *viewControllers = [[self navigationController] viewControllers];
int parentIndex = [viewControllers count] - 2;
UIViewController *parentVc = [viewControllers objectAtIndex:parentIndex];
if ([parentVc isKindOfClass:ClassA.class]) {
//action
}
else if ([parentVc isKindOfClass:ClassB.class]) {
//different action
}
else ... etc
}

Related

How to get NSString variable value from NSObject to ViewController

I am trying to set up an object to control all of my data so it can set things up in the background to it appears my tableviews load faster than they do now etc.
This is what I am trying to achieve.
I am setting a variable in the NSObject from the secondVC when the tableviewcell is selected like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//Access selected cells content (cell.textLabel.text)
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
//Parent view logic (sends info back to the correct cell in parent view)
if (parentViewSelectedIndexPath.section == 0)
{
if (parentViewSelectedIndexPath.row == 0)
{
//Predicates restrict the values that will be returned from the query
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K like %#",#"MANUFACTURER",cell.textLabel.text];
NSArray *filterArray = [myDataArray filteredArrayUsingPredicate:predicate];
//[[self delegate] setManufactureSearchFields:filterArray withIndexPath:indexPath]; //This is where I pass the value back to the mainview
//Using Object
VehicleControllerNSObject *vehicleControllerNSObject = [[VehicleControllerNSObject alloc] init];
[vehicleControllerNSObject setFirstCell:filterArray];
}
//etc
At the end there you can see the method that is getting set up in the VechicleControllerNSObject which looks like this.
-(void)setFirstCell:(NSArray *)array{
manufactureSearchObjectStringFVC = [[array valueForKey:#"MANUFACTURER"] objectAtIndex:0];
NSLog(#"%#", manufactureSearchObjectStringFVC); // this prints the correct value to the console
}
As you can see this prints the correct output fine.
however I have no idea how to call manufactureSearchObjectStringFVC and pass the value it holds into the uitableviewcell that I would like to pass it in on my firstviewcontroller.
This is what I have for testing atm.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
VehicleControllerNSObject *vehicleControllerNSObject = [[VehicleControllerNSObject alloc] init];
manufactureSearchObjectString = vehicleControllerNSObject.manufactureSearchObjectStringFVC;
NSLog(#"%#", vehicleControllerNSObject.manufactureSearchObjectStringFVC);
}
That nslog prints null..
I have three questions
1, how do I get the correct value into the first valuecontroller.
2, should I be using viewDidAppear like this?.. I think not.. how can I do this better
3, Do you think this is a good way of doing this type of thing, as in the future i would like to use the NSObjectClass to parse info, cache etc all behind the senses leaving the views to just display when the data is ready hopefully helping performance..
Any help would be hugely appreciated as I really want to learn this stuff as i know its important for me to know.
Your question is so beautifully and clearly formatted and diagrammed that it seems a shame to ask you to do a search. But here it is:
Search for Sharing Data between View Controllers
You'll find many good discussions about sharing data between view controllers.
Briefly, though, I can tell you why your code isn't working. In your tableView:didSelectRowAtIndexPath: method, you are creating (alloc/init) a new instance of your VehicleControllerNSObject class each time. Then back in your first view controller on viewDidAppear:, again you are creating (alloc/init) a whole new instance each time.
So you have multiple objects coming and going and they have nothing to do with each other. It's a bit like giving some important information to one person at a bus station and then later randomly picking some other person out and trying to retrieve that same information from her.
So one quick idea would be to create just once instance of your VehicleControllerNSObject (just an aside, that's a bit of a strange name for a class since generally all objective-c objects are descendants of NSObject anyway. I'm just going to call that VehicleController for now)
So let's say you wanted a 'sharedInstance' of VehicleController. You could add a class method to VehicleController to give you a way to easily get that one sharedInstance:
+(VehicleController*)sharedInstance {
static VehicleController *sharedInstance_ = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance_ = [[VehicleController alloc] init];
});
return sharedInstance_;
}
So to get that instance in methods in other classes you can just do something like :
VehicleController *sharedController = [VehicleController sharedInstance];
sharedController.someProperty = someValue;
// and then back in your first view controller, similarly:
VehicleController *sharedController = [VehicleController sharedInstance];
id someValue = sharedController.someProperty;
Again, check the search, many people have had good discussions on this. This is just one approach. I hope it at least makes sense why your code wasn't working.
Hope that helps.
To answer question 3. No.
I think that the best way to do something like this would be to use Core Data and it's NSManagedObject.
A combination of UITableViewController and NSFetchedResultsController that is feed from a Core Data sqlite backing store, if well set would feed and keep your UITableView updated.
It would be to long to describe all in here. So I will stop there.
If you don't want to go with that there is always the possibility to use a shared pointers to a mutable object or to use a singleton object to communicate information between UIViewController.

How to save nsdictionary of a subview to a mainview based off tableviewcell selection

I am currently parsing some xml that looks like this
<Rows>
<Row MANUFACTURERID="76" MANUFACTURERNAME="Fondont" ISMANU="F" ISAUTO="F"/>
<Row MANUFACTURERID="18" MANUFACTURERNAME="Anti" ISMANU="T" ISAUTO="T"/>
</Rows>
I parse it so that there is an array of dictionaries (each dictionary has the four values of the Row in it).
I then pass ManufacturerName to my startSortingTheArray method like this
if (dataSetToParse == #"ICMfg") // ICMfg is a string passed to this view from the parent view cell selection enabling me to pass different data sets to this view
{
//Filter results (ISAUTO = T)
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K like %#",#"ISAUTO",#"T"];
NSArray *filteredArray = [myDataArray filteredArrayUsingPredicate:predicate];
//Passes Manufacturer strigs over to startSortingtheArray method
[self startSortingTheArray:[filteredArray valueForKey:#"MANUFACTURER"]];
}
So from here all of the ManufacturerNames are sent to my method as an array of strings. I then use this array to set up all of my sections / index-scroller. The method below shows how I am doing this.
//method to sort array and split for use with uitableview Index
- (IBAction)startSortingTheArray:(NSArray *)arrayData
{
//If you need to sort incoming array alphabetically use this line of code
//TODO: Check values coming in for capital letters and spaces etc
sortedArray = [arrayData sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
//If you want the standard array use this code
//sortedArray = arrayData;
self.letterDictionary = [NSMutableDictionary dictionary];
sectionLetterArray = [[NSMutableArray alloc] init];
//Index scrolling Iterate over values for future use
for (NSString *value in sortedArray)
{
// Get the first letter and its associated array from the dictionary.
// If the dictionary does not exist create one and associate it with the letter.
NSString *firstLetter = [[value substringWithRange:NSMakeRange(0, 1)] uppercaseString]; //uppercaseString puts lowercase values with uppercase
NSMutableArray *arrayForLetter = [letterDictionary objectForKey:firstLetter];
if (arrayForLetter == nil)
{
arrayForLetter = [NSMutableArray array];
[letterDictionary setObject:arrayForLetter forKey:firstLetter];
[sectionLetterArray addObject:firstLetter]; // This will be used to set index scroller and section titles
}
// Add the value to the array for this letter
[arrayForLetter addObject:value];
}
//Reload data in table
[self.tableView reloadData];
}
from here I do several things to do with setting up the tableview after [self.tableView reloadData]; is called, The main thing being is that I set the cell up with the string values of the array.
//Display cells with data
NSArray *keys = [self.letterDictionary objectForKey:[self.sectionLetterArray objectAtIndex:indexPath.section]];
NSString *key = [keys objectAtIndex:indexPath.row];
cell.textLabel.text = key;
when the cell is then selected the string value inside the cell is then sent back to the main view and used later as a search parameter... The thing being is that I am setting up several parameters that will be used as one search string.
Looking back at the XML I parsed
<Rows>
<Row MANUFACTURERID="76" MANUFACTURERNAME="Fondont" ISMANU="F" ISAUTO="F"/>
<Row MANUFACTURERID="18" MANUFACTURERNAME="Anti" ISMANU="T" ISAUTO="T"/>
</Rows>
These are the values of columns inside an SQl table that has a keyvalue MANUFACTURERID that is also found in other tables that I parse. I would like to use these key values to restrict/refine other queries but I just cannot figure out how to pass them to my parentview where I set up all of the search parameters, that is my question how can I save the dictionary of values that is related to the users tableview selection from the subview. So that I can then pass one or some of those values back to the subview of a different dataset to restrict the information that is displayed dependent on the users previous selections.
This has taken me about an hour to type up. Hopefully it makes sense, I am still fairly new to iOS development and Objective C, and this concept is really pushing my capabilities and before I move on and end up hasing some crap together that I will have to fix later on I am hoping that one or some of you will be able to lend your experience in this type of this to me so I can get this right first time :)
If you need me to clarify anything or provide you more information that will help you help me just let me know.
Thanks in advance!
The common pattern for passing information backwards in your view controller hierarchy is to use delegation. You can achieve this in your scenario by implementing the following:
1) Define a protocol in the SearchParametersViewController, which represents your the parent view controller you mentioned.
#protocol SearchParametersViewControllerDelegate <NSObject>
#optional
- (void)searchOptionsSelected:(NSArray *)selectedSearchOptions;
#end
2) Conform to that protocol in your SearchOptionsSelectionViewController, which represents the table view controller that has a list of selections to choose from. Make sure to import or forward-declare the class the protocol is defined in (e.g. SearchParametersViewController) .
#import "SearchParametersViewController.h"
#interface SearchOptionsSelectionViewController <SearchParametersViewControllerDelegate>
3) Define a delegate property in your SearchOptionsSelectionViewController (assumes you are using ARC on iOS 5.0, 4.x use unsafe_unretained instead of weak. Use assign if the project is using manual memory management). This delegate object will contain a reference to your parent view controller (e.g. SearchParametersViewController). You do not want this property to be retained as to avoid retain cycles/circular references where one object references another, which in turn has a reference back to the first and neither object is ever deallocated.
#property (nonatomic, weak) id<SearchParametersViewControllerDelegate> delegate;
4) When instantiating the SearchOptionsSelectionViewController instance inside your parent view controller (SearchParametersViewController), set the delegate property to the parent view controller instance as represented by the self keyword. This ensures you can send the message (and corresponding data) backward in your view controller hierarchy, yet the object relationships remain loosely coupled. This delegate protocol could be conformed to in any other view controller, there are no tight relationships in the selection view controller back to the parent view controller, the only thing linking them is the flexible delegate protocol adoption by the selection view controller.
SearchOptionsSelectionViewController *selectionViewController = [[SearchOptionsSelectionViewController alloc] init];
selectionViewController.delegate = self;
5) Finally, in your SearchOptionsSelectionViewController table view's -tableView:didSelectRowAtIndexPath: delegate method, pass the data corresponding to the selected row back to your parent view controller (SearchParametersViewController) via the delegate method you defined in the SearchParametersViewControllerDelegate protocol. You must use the -respondsToSelector: method to ensure that the delegate object actually implements the -searchOptionsSelected: delegate method. To force this implementation, change #optional to #required above the method prototype in the protocol definition in step #1. self.someDataArray represents a the data source you are using with the selection table view controller. The specifics of the delegate protocol method and data object(s) sent back to the parent view controller can be changed, the important thing here is the delegation pattern and not having any tightly coupled relationships between the instances of either class, but especially backwards in the view controller hierarchy.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([self.delegate respondsToSelector:#selector(searchOptionsSelected:)])
{
NSArray *selectedObjs = [NSArray arrayWithObject:[self.someDataArray objectAtIndex:indexPath.row]];
[self.delegate searchOptionsSelected:selectedObjs]
}
}
6) Implement the delegate method inside SearchOptionsSelectionViewController.m
- (void)searchOptionsSelected:(NSArray *)selectedSearchOptions
{
// do what you need to with selectedSearchOptions array
}
Further reading:
Cocoa Fundamentals Guide - Delegates and Data Sources
Cocoa Core Competencies - Protocol
You could use the application delegate to achieve your goals here.
I'm going to assume your app has a structure a bit like this. Please excuse the crudity of this model.
Application delegate (A) --> Search Options View (B) --> Table where you do selections (C)
|
|
--> Some other view where you need the selection (D)
Your problem is that you need information to flow from C to D.
Your application delegate has the merit of being universally accessible via [[UIApplication sharedApplication] delegate]. So you can get a pointer to it from anywhere. From C, you can send your selection information back to A. A can either send this on automatically to D, or D can request it from A whenever it wants it.
A couple of points:
I won't expand any further on my answer at the moment because it's beer o' clock here now, plus I might have misunderstood your requirement. If you do need anything else, I will be up at baby o' clock in the morning UK time so there might be some delay.
Some people frown on using the application delegate as a "data dump" in the way I have suggested. Some of those people would rather set up a whole singleton class and treat that as a data dump instead. It seems to be one of those neverending arguments so I try not to get involved.
You have a few options, one is to use user defaults. It might be the easiest.
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/Reference/Reference.html
Another is to post a notification with the information.
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsnotificationcenter_Class/Reference/Reference.html

Share a bool variable / NSNUmber between two view controllers

I have two view controllers and I want to share a bool variable between them.
So I create a bool variable with a #propery (nonatomic, assign) on both sides and on the one side I wrote
newVC.myBool1 = self.myBool2;
On the other view controller I can read the value of the passed bool variable, but I need to change it at the second view controller so I can read the value at the first view controller.
So I know, this is not possible, because `bool* it is a primitive type.
So I used NSNumber, but this also does not work. On the first view controller I set on viewDidLoad
self.myBool1 = [NSNumber numberWithBool:NO];
On the second view controller:
self.myBool2 = [NSNumber numberWithBool:YES];
But on the first view controller the value is 0 - NO... So it seems that creating the new NSNumber is not shared to the first view controller.
What can I do to solve this problem?
Regards Tim
You have lots of choices, but which you should use depends on whether both viewControllers need notification of when the value changes.
If you don't need notification, the easiest choice is to use a global BOOL variable, although purists will scoff at the suggestion. But really it's two lines of code and you're done. Another option would be to store the value in NSUserDefaults.
If you need change notification in each viewController, perhaps the cleanest design is to write a "set" method in one viewController that sets the value in both itself and the other viewController. Something like:
-(void) setMyBool:(BOOL)newValue
{
myBool = newValue;
otherViewController.myBool = newValue;
}
If you want to change the value from either viewController, it gets a little trickier because you have to have each viewController keep a reference to the other and make sure not to recurse when setting the value. Something like:
-(void) setMyBool:(BOOL)newValue
{
if ( self.busyFlag == YES )
return;
self.busyFlag = YES;
myBool = newValue;
otherViewController.myBool = newValue;
self.busyFlag = NO;
}
Yet another option would be to use NSNotifications to change the value and have each viewController class listen for the change notification. And TheEye's suggestion of writing a wrapper class and keeping a reference to an instance of that class in both viewControllers would work too.
If you don't need change notifications, though, I would just create a global BOOL variable and get on with the rest of the application because it's so easy, reliable and hard to mess up.
An NSNumber object is immutable, so you can't use it like that. If you write [NSNumber initWithxxx], in fact you create a new object.
If you want to share a number or boolean between several classes, you should create your own wrapper class with setters and getters for the bool value (or subclass NSNumber). This class you can share between classes.

ViewController Hierarchy getting 'lost'

I've got a view hierarchy which is setup (programmatically) as follows:
Window.root = TabBarController-->UINavigationControllers-->UIViewControllers
I presume that's rather standard. Here's my problem:
I'm on Tab A. I want to navigate to Tab B, and call a method on the visibleViewController on Tab B.
// View Changes OK
[AppDelegate.tabBarController setSelectedIndex:tabB];
// nav = 0x387ABF i.e. Valid Address
UINavigationController *nav = (UINavigationController*)[AppDelegate.tabBarController selectedViewController];
// The problem:
nav.viewControllers; // this is nil
nav.topViewController; // as is this
nav.visibleViewContorller; // this too.
Even if I put the calls to nav.viewControllers in a separate method which is called from the Main Thread, I still get 0x0/nil.
What am I doing wrong?
A follow-up question is:
How can I pass information from one ViewController to another when changing tabs? (If I can't call methods on VC's from tabA to tabB)
I have a feeling it is related to my question here.
You should store the information in a common place, either a singleton or as you are a beginner just make a class and pass it down.
sharedDataObject = [[MySharedDataObject alloc] init];
firstViewController.myDataObject = sharedDataObject;
secondViewController.myDataObject = sharedDataObject;

how to send string value with popToViewController

I am using navigationcontroller. i have (Root,A,B,C,D) class. i want to sand a string test value Class D to Class A via popToViewController.
Please give me suggestion.
Thanks
UINavigationController maintain the list of all pushed controller in viewControllers and the root controller always reside at 0.
MyAController *myController = (MyAController *)[self.navigationController.viewControllers objectAtIndex:0];
myController.myText = #"My String" ;
[self.navigationController popToViewController:myController animated:YES];
You might want to rethink your design, but since you haven't given enough information for me to suggest how, you could just try this:
A *aController = (A *)[myNavController rootViewController];
[aController setMyString:#"your string here"];
[myNavController popToRootViewControllerAnimated:YES];
Make that string as property of class a then you need to access that object of the view A from navigation stack in class D.
And then access that property for using.
If Class A is rootView then jtbandes answers helps you otherwise pick it up from stack code some thing like this
if([self.navigationController.viewControllers count]>3)
A *aController=(A *)[self.navigationController.viewControllers objectAtIndex: [self.navigationController.viewControllers count]-4];
if your navigation is A->B->c->D
then you can access c by [self.navigationController.viewControllers count]-2 similarly B by -3 A by -4.
There are several approaches to achieve this.
Using Notifications.
Using Delegates.
Using Outlets/Properties.
You could also create a ControllerA instance var in your ControllerB and then pass the ControllerA (self) to ControllerB(ex: by a method) before call pushViewController from A to B. You can do the same from B to C...etc; i think the best solution is that of Jhaliya in case you want to work with navigationControllers.