How to check if rootViewController is of a specific class? - iphone

I would like to know if the rootViewController in a navigation controller stack is of specific class. How to do such a thing ?
Thx for helping,
Stephane

Here you go buddy
id rootController = [self.navigationController rootViewController];
if([rootController isKindOfClass:[YourDesiredController class]]){
//do something
}

The navigation controllers root view controller is,
id rootVC = [[navigationController viewControllers] objectAtIndex:0];
And check the class of rootVC like this,
if ([rootVC isKindOfClass:[YourClass class]]) {

You can check the class with:
if ([rootViewController isKindOfClass:[YourClass class]]){
}else if ([rootViewController isKindOfClass:[AnotherClass class]]){
}else{
}

Perhaps you can use the method isKindOfClass
[rootViewController isKindOfClass: [RootViewController class]];

How to check your current rootViewController and use it in an if statement :
// Get your current rootViewController
NSLog(#"My current rootViewController is: %#", [[[UIApplication sharedApplication].delegate.window.rootViewController class]);
// Use in an if statement
UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
if ([rootViewController isKindOfClass:[MyViewController class]])
{
NSLog(#"Your rootViewController is MyViewController!!");
}

Related

Object is nil when called from another class

I want to change properties of another object, when a method is called in another class.
The code to change the properties of this object sits in a method of the first class, and works when calling it from it's own class, but when called from the other class the object in the method returns nil.
Here is the code:
ViewController.h
#interface ViewController : UIViewController {
UIView *menuView; //the object
}
#property (nonatomic, retain) IBOutlet UIView *menuView;
-(void)closeMenu; //the method
#end
ViewController.m
#implementation ViewController
#synthesize menuView;
-(void)closeMenu{
[menuView setFrame:CGRectMake(menuView.frame.origin.x, -menuView.frame.size.height, menuView.frame.size.width, menuView.frame.size.height)];
NSLog(#"%f", menuView.frame.size.height); //returns height when method is called from it's own class. But returns 0 (nil) when called from the other class.
}
SDNestedTableViewController.h (nothing too important, but might help?)
#interface SDMenuViewController : SDNestedTableViewController{
}
SDNestedTableViewController.m
#import "SDMenuViewController.h"
#import "ViewController.h"
- (void) item:(SDGroupCell *)item subItemDidChange:(SDSelectableCell *)subItem
{
ViewController *firstViewController = [[[ViewController alloc] init] autorelease];
SelectableCellState state = subItem.selectableCellState;
NSIndexPath *indexPath = [item.subTable indexPathForCell:subItem];
switch (state) {
case Checked:
NSLog(#"Changed Sub Item at indexPath:%# to state \"Checked\"", indexPath);
[firstViewController closeMenu]; //called from other class
break;
case Unchecked:
NSLog(#"Changed Sub Item at indexPath:%# to state \"Unchecked\"", indexPath);
break;
default:
break;
}
}
What you posted looks like:
-(void)closeMenu{
// menuView is never initialized, == nil
[nil setFrame:CGRectMake(0, -0, 0, 0)];
NSLog(#"%f", 0); //returns height when method is called from it's own class. But returns 0 (nil) when called from the other class.
}
So you are doing NSLog(#"%f", 0);.
If you do load the view by accessing the view property, the menuView will be initialized by IB rules.
For the details of viewController view loading/unloading see the reference docs.
I think this may help you.
At Your AppDelegate class, you have to declare an object of ViewController class. Make it as a property of the YourAppDelegate class. like below. (This would import ViewController class and creates a shared object of YourAppDelegate class so that you can access the members of YourAppDelegate class globally by simply importing the YourAppDelegate.h).
#import "ViewController.h"
#define UIAppDelegate ((YourAppDelegate *)[UIApplication sharedApplication].delegate)
#interface YourAppDelegate : NSObject <UIApplicationDelegate>
{
ViewController *objViewController;
}
#property (nonatomic, retain) ViewController *objViewController;
#end
And synthesize the property at YourAppDelegate.m file.
#implementation YourAppDelegate
#synthesize objViewController;
#end
Then the tricky part is, you have to backup the object of ViewController class in the YourAppDelegate class at the time you are loading the ViewController class.
For that first import the YourAppDelegate.h in the ViewController.h class and at the ViewController.m implement viewWillAppear: delegate as follows.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
UIAppDelegate.objViewController = self;
}
Then at SDNestedTableViewController.m,
#import "SDMenuViewController.h"
#import "ViewController.h"
- (void) item:(SDGroupCell *)item subItemDidChange:(SDSelectableCell *)subItem
{
ViewController *firstViewController = (ViewController *)UIAppDelegate.objViewController;
if(firstViewController && [firstViewController isKindOfClass:[ViewController class]])
{
SelectableCellState state = subItem.selectableCellState;
NSIndexPath *indexPath = [item.subTable indexPathForCell:subItem];
switch (state) {
case Checked:
NSLog(#"Changed Sub Item at indexPath:%# to state \"Checked\"", indexPath);
[firstViewController closeMenu]; //called from other class
break;
case Unchecked:
NSLog(#"Changed Sub Item at indexPath:%# to state \"Unchecked\"", indexPath);
break;
default:
break;
}
}
}
Try this way. I am not saying this as the right way but, this should works. Glad if this helps you.
EDIT 2:
Well, you shipped your code over to me, so now I can no longer say that I don't have enough information to solve your problem.
Let's see.
Now I see that your ViewController is the rootViewController of your app, like so:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.viewController = [[[ViewController alloc] initWithNibName:#"ViewController" bundle:nil] autorelease];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
Good, now how does the ViewController relate to your SDNestedTableViewController?
You have this in your ViewController's viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
SDMenuViewController *mvc = [[[SDMenuViewController alloc] initWithNibName:#"SDNestedTableView" bundle:nil] autorelease];
[self addChildViewController:mvc];
[mvc didMoveToParentViewController:self];
[menuView addSubview:mvc.view];
// Some other stuff with gesture recognizers I'm omitting...
[self openMenu];
}
Alright, so it looks like SDMenuViewController is the child of ViewController. Now, you have a method in SDMenuViewController called item:subItemDidChange:
- (void) item:(SDGroupCell *)item subItemDidChange:(SDSelectableCell *)subItem
{
ViewController *firstViewController = [[[ViewController alloc] initWithNibName:#"ViewController" bundle:nil] autorelease];
SelectableCellState state = subItem.selectableCellState;
NSIndexPath *indexPath = [item.subTable indexPathForCell:subItem];
switch (state) {
case Checked:
NSLog(#"Changed Sub Item at indexPath:%# to state \"Checked\"", indexPath);
//close the menuView
[firstViewController closeMenu];
break;
case Unchecked:
NSLog(#"Changed Sub Item at indexPath:%# to state \"Unchecked\"", indexPath);
break;
default:
break;
}
}
So, you want the reference back to the existing ViewController object, right? Because right there you're making another one. So, you can do this:
ViewController *firstViewController = self.parentViewController;
That gets you a reference to SDMenuViewController's parent, which is the instance of ViewController. This property is set when you do your addChildViewController: call.
Okay, this is confusing though:
In your post, you say that your item:subItemDidChange: method is in SDNestedTableViewController, but in the code you sent me it's in the SDMenuViewController.
In the SDNestedTableViewController, I found this method:
- (void) mainItem:(SDGroupCell *)item subItemDidChange: (SDSelectableCell *)subItem forTap:(BOOL)tapped
{
if(delegate != nil && [delegate respondsToSelector:#selector(item:subItemDidChange:)] )
{
[delegate performSelector:#selector(item:subItemDidChange:) withObject:item withObject:subItem];
}
}
So it looks like you're not using the same code as in the original post, but close enough, whatever.
Now, if you want to get a reference to the ViewController instance from anywhere in the app, not just your SDMenuViewController (which happens to be the child of the ViewController instance) you should use #Mathew Varghese's answer.
Here's a restatement of this method:
Add the line + (AppDelegate *)instance; to your AppDelegate.h file.
Add the following method to your AppDelegate.m file.
Like so:
+ (AppDelegate *)instance
{
AppDelegate *dg = [UIApplication sharedApplication].delegate;
return dg;
}
Then, in whatever object you want that reference, you #import AppDelegate.h and say ViewController *vc = AppDelegate.instance.firstViewController;
Anyway, it's just another way of saying what Mathew mentioned earlier.
the problem is:
- (void) item:(SDGroupCell *)item subItemDidChange:(SDSelectableCell *)subItem {
ViewController *firstViewController = [[[ViewController alloc] init] autorelease];
...
[firstViewController closeMenu];
}
When you call closeMenu from there, it is never initialized, because not enough time has passed to initialize view of view controller, viewDidLoad method of your firstViewController is not called at this point either. menuView is not created from nib either, so this is the reason why it is nil.
Maybe for some reason there might be a delay long enough so menuView is created, but this is not how you should do things in iOS.
So, if you don't want to show your menuView, just add some boolean value to your firstViewController and instead of closeMenu do:
firstViewController.shouldCloseMenu = YES;
Then in your ViewController in viewDidLoad method do something like:
if (self.shouldCloseMenu ) {
[self closeMenu];
}
Maybe this is not the best way to do it, but now you have an idea how it suppose to work.
I believe your problem is the related to the way you have initialized the viewController.
Instead of
ViewController *firstViewController = [[[ViewController alloc] init] autorelease];
use
ViewController *firstViewController = [[[ViewController alloc] initWithNibName:#"yourNibName" bundle:nil] autorelease];
I'm assuming you have a nib because you are using an IBOutlet. But I believe the IBOutlet is never setup because you have not loaded the nib file.
Also double check your IBOutlet connection with interface builder and use "self.menuView"
I would suggest you to solve this problem in the following steps.
Do not use any instance or variable of firstViewController in the SDMenuViewController.
In the case check block, post a message to the NSNotificationCenter
In the ViewController register the message with the same message Id, use the closeMenu method as its handler.
For me, use the message center to dispatch the handling can decouple the relationship between controllers. This is a better way that you would concern less about the lifecycle of the controller within another one.
Hope it would be helpful.
There is a difference between alloc-init'ing a ViewController and alloc-init'ing that view controller's properties.
Regarding your second example (calling from another class). Your current code indicates that you alloc-init firstViewController, but then don't do anything with it. Assuming you have not overriden your ViewController's init method, its properties and iVars should be nil (or undefined at worst). You need to alloc-init your firstViewController.menuView first. I.e:
firstViewController.menuView = [[UIView alloc] initWithFrame]; // Don't do this.
The problem with this approach is that you're setting up firstViewController's properties form another class, and that's generally fairly average design practice. This required setup would usually happen in viewDidLoad but because you haven't done anything with firstViewController yet, it never gets called.
In contrast, when you call closeMenu from its own View Controller, the odds are you are actually doing something with the view and viewDidLoad (or wherever menuView = [[UIView alloc] init];is found) is called first, thus initialising your menuView object.
You need to ensure that your menuView object is initialised first before you try and do anything with it, just initialising the View Controller that contains it is not enough.
#import "SDMenuViewController.h"
#import "ViewController.h"
- (void) item:(SDGroupCell *)item subItemDidChange:(SDSelectableCell *)subItem
{
// why are we allocating this object here, if it is only required in case Checked :
ViewController *firstViewController = [[[ViewController alloc] init] autorelease];
SelectableCellState state = subItem.selectableCellState;
NSIndexPath *indexPath = [item.subTable indexPathForCell:subItem];
switch (state) {
case Checked:
NSLog(#"Changed Sub Item at indexPath:%# to state \"Checked\"", indexPath);
[firstViewController closeMenu]; //called from other class
break;
case Unchecked:
NSLog(#"Changed Sub Item at indexPath:%# to state \"Unchecked\"", indexPath);
break;
default:
break;
}
}
Change it to
#import "SDMenuViewController.h"
#import "ViewController.h"
- (void) item:(SDGroupCell *)item subItemDidChange:(SDSelectableCell *)subItem
{
// why are we allocating this object here, if it is only required in case Checked :
SelectableCellState state = subItem.selectableCellState;
NSIndexPath *indexPath = [item.subTable indexPathForCell:subItem];
switch (state) {
case Checked:
NSLog(#"Changed Sub Item at indexPath:%# to state \"Checked\"", indexPath);
// here no need to put object in autorelease mode.
ViewController *firstViewController = [[ViewController alloc] init];
[firstViewController closeMenu]; //called from other class
[firstViewController release];
break;
case Unchecked:
NSLog(#"Changed Sub Item at indexPath:%# to state \"Unchecked\"", indexPath);
break;
default:
break;
}
}
try to remove UIView *menuView; //the object from the interface file
#interface ViewController : UIViewController {
// try to remove this line
UIView *menuView; //the object
}
and update this method
-(void)closeMenu{
[self.menuView setFrame:CGRectMake(self.menuView.frame.origin.x, -self.menuView.frame.size.height, self.menuView.frame.size.width, self.menuView.frame.size.height)];
NSLog(#"%f", self.menuView.frame.size.height);
}
Everything is correct, change the -(void)closeMenu method like...
-(void)closeMenu
{
menuView=[[UIView alloc]initWithFrame:CGRectMake(50.0,50.0,200.0,200.0)]
NSLog(#"%f", menuView.frame.size.height); //returns height when method is called from it's own class. But returns 0 (nil) when called from the other class.
}
Try this and let me know.
I suggest you use this:
if(menuView) {
[menuView setFrame:CGRectMake(menuView.frame.origin.x, -menuView.frame.size.height, menuView.frame.size.width, menuView.frame.size.height)];
} else {
NSLog(#"menuView is nil");
}

Get the Identifier of the current tab - iphone

Probably a simple question, but I'm having a hellofa time finding a solution to it.
I need to find the identifier of the current tab in a tabbarcontroller and use it in a conditional to run a method.
how do I find this?
if (self.tabbarcontroller.identifier == #"My identifier") {
// do some method
} else {
// do the default method
}
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
UITabBarController *tabBarController = (UITabBarController*) window.rootViewController;
UIViewController *selectedVC = tabBarController.selectedViewController;
if ([selectedVC.identifier isEqualToString:#"anIdentifier"])
{
// Do something
} else {
// Do something else
}
You can set the identifier of a ViewController in your storyboard
Check out the following code. Also make sure that the delegate for the UITabBar is correct pointing to the view controller, in this case FirstViewController.
**FirstViewController.h****
#interface FirstViewController : UIViewController<UITabBarDelegate>
**FirstViewController.m:**
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
NSLog(#"%#",[item tag]);
}

How to access the views of the MoreViewController and change their titles?

on language switch within my App, I need to access the views of the MoreViewController in a TabBar and change their titles.
Could anyone pls tell me how to do that?
Your help is much appreciated.
Cols
Here are some snippets that might work for you. Note that all of the below is subject to break on each and every new iOS release at it is not meant to be done.
Customizing the More view title as itself as well as the title while it is being edited by the user.
- (void)customizeTitleViewWithNavigationItem:(UINavigationItem *)navigationItem
{
VASSERT(navigationItem != nil, #"invalid navigationItem supplied", navigationItem);
UILabel *titleView = [[UILabel alloc] initWithFrame:CGRectZero];
titleView.backgroundColor = [UIColor clearColor];
titleView.font = [UIFont boldSystemFontOfSize:20.0f];
titleView.text = navigationItem.title;
[titleView sizeToFit];
navigationItem.titleView = titleView;
[titleView release];
}
This needs to be implemented within the UINavigationController's delegate;
- (void)navigationController:(UINavigationController *)navigationController
willShowViewController:(UIViewController *)viewController
animated:(BOOL)animated
{
if (navigationController == tabBarController_.moreNavigationController)
{
if ([viewController isKindOfClass:NSClassFromString(#"UIMoreListController")])
{
[self customizeTitleViewWithNavigationItem:viewController.navigationItem];
}
else
{
NSLog(#"viewController (%#) does not seem to be a UIMoreListController", viewController);
}
}
else
{
NSLog(#"navigationController (%#) does not seem to be the moreNavigationController", navigationController);
}
}
This needs to be implemented within the UITabBarController's delegate;
- (void)tabBarController:(UITabBarController *)controller willBeginCustomizingViewControllers:(NSArray *)viewControllers
{
//get the second view of the upcoming tabbar-controller
UIView *editView = [controller.view.subviews objectAtIndex:1];
//did we get what we expected, which is a UITabBarCustomizeView?
if (editView != nil && [editView isKindOfClass:NSClassFromString(#"UITabBarCustomizeView")])
{ //yes->get the navigation-view
UIView *navigationView = [editView.subviews objectAtIndex:0];
//is that a navigationBar?
if (navigationView != nil && [navigationView isKindOfClass:[UINavigationBar class]])
{ //yes->...
UINavigationBar *navigationBar = (UINavigationBar *)navigationView;
[self customizeTitleViewWithNavigationItem:navigationBar.topItem];
}
else
{
NSLog(#"the navigationView (%#) does not seem to be a navigationBar", navigationView);
}
}
else
{
NSLog(#"the editView (%#) does not seem to be a UITabBarCustomizeView", editView);
}
}
I was able to change the title of one of the tabBar items for my 6th view controller (the last one in the more list) with the following code:
NSArray *vcs = [(UITabBarController *)self.window.rootViewController viewControllers];
[[vcs.lastObject tabBarItem] setTitle:#"New Title"];
Is this what you want to do?
After Edit: To change these titles after they are set up the first time, you need to re-set the viewControllers property of the tabBar controller. In this code example, I connected a button in my 6th view controller to an action method that changes the titles of 3 of my controllers, 2 in the more list and one in the regular list.
-(IBAction)changeNames:(id)sender {
UITabBarController *tbc = (UITabBarController *)[[UIApplication sharedApplication] delegate].window.rootViewController;
NSArray *vcs = tbc.viewControllers;
[[vcs.lastObject tabBarItem] setTitle:#"New Title"];
[[[vcs objectAtIndex:4] tabBarItem] setTitle:#"New VC"];
[[[vcs objectAtIndex:3] tabBarItem] setTitle:#"New VC2"];
tbc.viewControllers = tbc.viewControllers;
}

Calling a viewcontroller another class from connectiondidfinish delegate NSURLConnection

Trying to launch a viewcontroller in connectiondidfinish delegate method of NSUrlConection
//Sprequest.m inherited from nsobject
- (void)connectionDidFinishLoading:(NSURLConnection *)conn {
NSLog(#"connectionDidFinishLoading ");
if(nStatus == 401)
{
NSLog(#"called maincontroller to launch dvrview");
MainController *mainview =[[MainController alloc] init];
[mainview reponseFromServer];
}
}
//maincontroller.m from viewcontroller
-(void)reponseFromServer
{
NSLog(#"response from server - main controller ");
dvrView *dvrObj = [[dvrView alloc]initWithNibName:#"dvrView" bundle:nil];
[self.navigationController pushViewController:dvrObj animated:YES];
}
this dvr view doesnt get loaded
Sprequest.m is inherited from NSObject , its not a viewController subclass so you cant use
[self.navigationController pushViewController:dvrObj animated:YES];
inside Sprequest.m
You can get the navigationController object from the appdelegate like this
((AppDelegate *)[UIApplication sharedApplication].delegate).navigationController
then use
[((AppDelegate *)[UIApplication sharedApplication].delegate).navigationController pushViewController:dvrObj animated:YES];

IPhone Custom UIButton Cannot DismissModalViewController

I have a custom UIButton class in my IPhone navigation application. When the user clicks the button I present a modal view which displays a tableview list of records that the user can select from.
On click of the button I create my viewController and show it using the following code:
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
dropDownPickerViewController = [[DropDownListingViewController alloc] initWithNibName:#"DropDownListingView" bundle:[NSBundle mainBundle]];
.....
.....
[[(AppDelegate_iPhone *)[[UIApplication sharedApplication] delegate] navigationController] presentModalViewController:dropDownPickerViewController animated:NO];
[dropDownPickerViewController release];
[super touchesEnded:touches withEvent:event];
}
As you can see the button could be on any viewController so I grab the navigationController from the app delegate. Than in my DropDownPickerViewController code i have the following when a user selects a record:
[[(AppDelegate_iPhone *)[[UIApplication sharedApplication] delegate] navigationController] dismissModalViewControllerAnimated:NO];
But nothing occurs. It seems to freeze and hang and not allow any interaction with the form. Any ideas on why my ModalViewController wouldnt be dismissing? Im thinking it is the way I use the app delegate to present the viewController. Is there a way to go self.ParentViewController in a UIButton class so I can get the ViewController of the view that the button is in (if this is the problem)?
Any ideas would be greatly appreciated.
Thanks
I did recall that I did have to do something similar once, and by looking at this post:
Get to UIViewController from UIView?
I was able to create some categories:
//
// UIKitCategories.h
//
#import <Foundation/Foundation.h>
#interface UIView (FindUIViewController)
- (UIViewController *) firstAvailableUIViewController;
- (id) traverseResponderChainForUIViewController;
#end
#interface UIView (FindUINavigationController)
- (UINavigationController *) firstAvailableUINavigationController;
- (id) traverseResponderChainForUINavigationController;
#end
//
// UIKitCategories.m
//
#import "UIKitCategories.h"
#implementation UIView (FindUIViewController)
- (UIViewController *) firstAvailableUIViewController {
// convenience function for casting and to "mask" the recursive function
return (UIViewController *)[self traverseResponderChainForUIViewController];
}
- (id) traverseResponderChainForUIViewController {
id nextResponder = [self nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]]) {
return nextResponder;
} else if ([nextResponder isKindOfClass:[UIView class]]) {
return [nextResponder traverseResponderChainForUIViewController];
} else {
return nil;
}
}
#end
#implementation UIView (FindUINavigationController)
- (UINavigationController *) firstAvailableUINavigationController {
// convenience function for casting and to "mask" the recursive function
return (UINavigationController *)[self traverseResponderChainForUINavigationController];
}
- (id) traverseResponderChainForUINavigationController {
id nextResponder = [self nextResponder];
if ([nextResponder isKindOfClass:[UINavigationController class]]) {
return nextResponder;
} else {
if ([nextResponder isKindOfClass:[UIViewController class]]) {
//NSLog(#" this is a UIViewController ");
return [nextResponder traverseResponderChainForUINavigationController];
} else {
if ([nextResponder isKindOfClass:[UIView class]]) {
return [nextResponder traverseResponderChainForUINavigationController];
} else {
return nil;
}
}
}
}
#end
you can then call them like so:
UINavigationController *myNavController = [self firstAvailableUINavigationController];
UIViewController *myViewController = [self firstAvailableUIViewController];
maybe they will help you, I hope so.
In your DropDownPickerViewController, Instead of this:
[[(AppDelegate_iPhone *)[[UIApplication sharedApplication] delegate] navigationController] dismissModalViewControllerAnimated:NO];
Try
[self dismissModalViewControllerAnimated:NO];
I would call a method on the button's "parent" viewController and do it from there.