Showing a different XIB/NIB in an iOS app - objective-c

I have multiple nib (xib) files and I want the user to see a different one when they tap a button. What I am looking for:
- (IBAction)buttonTap {
//Code for showing a different nib goes here
}
I can't figure out how to do this. I can show a different view within the nib file just fine, but I can't get it to show a different nib. How do I show a different nib when the user taps a button?
Any help is appreciated! Thanks!

The way I handle switching between actual xib's, and I'm sure there are a multitude of ways to accomplish the same thing, is to have my App Delegate act as a routing center between my views.
I subscribe my App Delegate to recieve events from button presses for existing views. When it recieves a request to switch views, such as a button press, I do something like this:
- (void) showLogin
{
LoginViewController *loginViewController = [[LoginViewController alloc]
initWithNibName:#"LoginViewController" bundle:nil];
// Show
self.loginViewController = loginViewController;
[loginViewController release];
self.window.rootViewController = self.loginViewController;
}
I set my rootViewController to the view I am attempting to display. It doesn't release the old controller, but simply replaces the view being displayed. You can place more logic in to determine if it's already displayed, close out other views, etc. In most simplistic terms, this works for me.

Related

View Controller Doesn't respond to Action Methods

I have a simple test app (in OSX) that has a view controller with a button in its view. The button's action method is in the view controller's class, and that IBAction is connected in IB (through File's Owner). When the button is clicked, I get an EXC_BAD_Access error (except occasionally I get -[NSRunLoop buttonClick:] instead). I've read a bunch of posts here on SO having to do with NSViewControllers not being in the responder chain, but also that specifically hooking the action method up in IB should work. The only code I have is this:
In the app delegate:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
TestController *controller = [[TestController alloc] initWithNibName:#"TestController" bundle:nil];
[self.window.contentView addSubview:controller.view];
}
And, in the TestController class, just this:
-(IBAction)buttonClick:(id)sender {
NSLog(#"%#",sender);
}
I have 2 questions. Why is this happening, and where is the correct mvc place to put IBActions for button methods (shouldn't controller classes handle these)?
First off, I believe in the App Delegate there is already a property called viewController already defined for you. You should use this code self.viewController = [[TestController alloc] initWithNibName:#"TestController" bundle:nil]; instead of TestController *controller = [[TestController alloc] initWithNibName:#"TestController" bundle:nil];. If that doesn't fix it, then I'm not sure what's wrong. Your code should return the attributes of the button you clicked. I created my own sample project and tested out your code (although my app delegate looked different than yours) and it worked fine.
Second, you should elaborate on your second question "Why is this happening, and where is the correct mvc place to put IBActions for button methods (shouldn't controller classes handle these)?". It's not very clear what you mean.
Hope this helps a little.
NSViewController will not respond to IBActions on mac os x on iPhone It is the correct place to put your IBAction as the view should only draw its content (data) not change it. But on Mac os x the NSViewController is for setting up the NSView it's no ment to respond to IBActions you have two choices one is to put your IBAction in your NSView or is create a NSWindowController .
On mac osx you have plenty of screen space and you will alway have views within a window you'll use NSViewController to add them to your window and to setup your view but the window is first in your responder chain then your NSWindowController . eg: you may have one window and two view controller showing the same view which may have 5 test fields in it and view controller one loads that with data but you can not edit the data then view controller two loads the same view with the same data but enable editing for the text fields so. but all action methods will go to the WindowController .
I recommend you to check your viewController's identity inspector.
It may indicate wrong custom class.
viewController created basically usually has name ViewController on the section.

Get current view

I have an app which has split view inside a tab bar, and these split views often have navigation hierarchy and then sometimes modal views are presents on top of them, and it all works fine, but...
I am trying to display a passcode lock whenever the app goes into background, so I put
[self.window.rootViewController presentModalViewController:lockView animated:YES];
in my AppDelegate's method
- (void)applicationWillResignActive:(UIApplication *)application
...which works fine unless a modal view is displayed.
the passcode does not display if a modal view is open.
Is there a way to retrieve the currently active view controller so I can present this lock view?
Thanks in advance
Cheerio
Code that worked was as follows:
BOOL hasKids = YES;
UIViewController *topViewController = (UIViewController*)[[(UITabBarController*)self.window.rootViewController viewControllers] objectAtIndex:((UITabBarController*)self.window.rootViewController).selectedIndex];
while (hasKids) {
if (topViewController.presentedViewController) {
hasKids = YES;
topViewController = topViewController.presentedViewController;
} else {
hasKids = NO;
}
}
[topViewController presentModalViewController:lockView animated:YES];`
I think the easiest way is to keep track of which tab is currently active (there are a number of ways to do this, but I'd recommend implementing the UITabBarControllerDelegate and handling its tabBarController:didSelectViewController: method).
Once that's done, you'll probably need to manage a property in each view controller that holds any modal view controllers you present. If, however, you're on iOS 5 or higher, look into the UIViewController property presentedViewController. It appears that this is a new way to do exactly what you want.

Reloading a view

I can't find the correct method on how to reload a modal view controller from within itself. I'm developing a very simple UI based app but I'm not sure how to do this.
I open the modal view with the following:
MainView *mainmview = [[MainView alloc] initWithNibName:#"submod" bundle:nil];
mainmview.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self mainmview animated:YES];
and then can dismiss it depending on iOS version. However this has no effect when used on what i need to be a simple restart button. Any help is appreciated.
You will have to implement your own "refresh" functionality - define a "refresh" method inside the modal view controller's class and do whatever needs to be done inside it - if for instance you have a tableview that requires a reload or to refresh your views depending on new data, etc...

Loading another view from table selection without losing navigation (back)

I have an app that is controled through a UITabBar. In one of the sections within the tab bar, I have a navigation table. It works fine from an example I did from one of the books but I want to be able to go another view controller (aka another xib file) when the user selects a row and I want the user to be able to go back easily. I realize this has to do with pushingViewControllers but I am stuck. Here is where I think the problem is. my code is at the bottom. If you notice, I commented out
// [self presentModalViewController:flowerDetailViewController animated:YES];
While this did take me to the my flowerDetailViewController XIB file, I lost the ability to do navigation (go back). If anyone has any ideas or suggestions, it would be much appreciated.
Thank you
-(void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
FlowerDetailViewController *flowerDetailViewController =
[[FlowerDetailViewController alloc] initWithNibName:
#"FlowerDetailViewController" bundle:nil];
/*flowerDetailViewController.detailURL=
[[NSURL alloc] initWithString:
[[[flowerData objectAtIndex:indexPath.section] objectAtIndex:
indexPath.row] objectForKey:#"url"]];*/
flowerDetailViewController.title=
[[[flowerData objectAtIndex:indexPath.section] objectAtIndex:
indexPath.row] objectForKey:#"name"];
//[self presentModalViewController:flowerDetailViewController animated:YES];
[self.navigationController pushViewController:
flowerDetailViewController animated:YES];
[flowerDetailViewController release];
}
It doesn't make any sense to execute these two methods on the same view consecutively.
[self presentModalViewController:flowerDetailViewController animated:YES];
..
[self.navigationController pushViewController: flowerDetailViewController animated:YES];
because they give a very different result, from user experience point of view.
The first one presents a view modaly. Modaly means, that he application brings up the view to the top (imagine a Z-axis, when you hold the phone, it is a line from the phone to you, on top of it means closer to you), and the user is stuck in that view exclusively because it is on the top, he/she cannot touch anything else from the application unless he resolves the options presented in the view and the view goes away.
The second method is pushing the view onto he stack of views that all belong to the navigation controller. The navigation controller pushes views onto the screen like you would lay a stack of cards onto the table, card1, put onto that card 2, put onto that card 3...and so on card N. But you still have the ability to touch other options that are all around the navigation controller.To get back to the card 1, you need to remove card(views) that are on top of it, for removing on-top views, the navigation controller provides the back button automatically.
Only you cann tell, which of these two is handy in terms of your application UI and design.

Obj-C, Navigation Controller with Tab Controller am I using them incorrectly?

When I first setup my app I had some issues getting a single navigation controller to work.
I have several screens behind each tab item. I think the problem I was getting was that view controllers would show within the wrong tabs, when switching between them. I'm not bothered about keeping the last view controller used open within each tab, in fact I hide the tab bar to stop this anway now.
So at the moment I have navigation controller files for each of my tabs. I have them assigned in IB, in the mainWindow.
And I use them like this...
CategorySelTableViewController *nextController =
[[[CategorySelTableViewController alloc] initWithNibName:
#"CategorySelTableView" bundle:nil] autorelease];
nextController.hidesBottomBarWhenPushed = YES;
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication]
delegate];
[delegate.billsndepsNavController pushViewController:nextController animated:YES];
However, I have some leaks.
I can't release my delegate, it causes an error.
My colleuge suggests that I should just be using self.navigationcontroller.
But this is a big change for me, I'd like to know definetively if I'm doing this wrong before I make the changes ?
When a view controller is pushed into the stack, it has two ways to access the navigation controller:
Using self.navigationController.
Accessing the navigation controller ivar in the delegate:
[UIApplication sharedApplication].delegate.navigationController
Both are equivalent but the first one is shorter, so it tends to be used more. There is no benefit from switching from one to the other. The only reason to do the extra typing is when you are not in a pushed view controller, eg: a view controller used in an independent GUI component, or an object that is not a view controller.
The delegate shouldn't be released because it exists during the whole life of the application.