I have a root view controller which contains an outlet for my login view controller. the root view should control the flow to the next view, yet my login view has the button to continue. how would I set the button's touch up inside to the IBAction in my root controller?
One method I have though of was keep a pointer to the root class where I create the login class (new code is commented out):
// RootViewController.m
- (void)viewDidLoad {
LoginViewController *loginController =
[[LoginViewController alloc]initWithNibName:#"LoginView" bundle:nil];
self.loginViewController = loginController;
//loginViewController.parent = self;
[self.view insertSubview:loginController.view atIndex:0];
[loginController release];
[super viewDidLoad];
}
- (IBAction)loginPressed: (id)sender
{
self.loginViewController.loginButton.enabled = NO; //yea... doesnt work
}
So in IB I add a UIViewController to the Nib and give it parent as an outlet, and then assign button's touch up inside event to loginPressed which is defined in the root controller (parent)... this didnt work so well explicitly refering to the controls from self.loginViewController in the RootViewController.
is there a correct way to do this.
-frustrated c++ / c# / java coder
Have you considered presenting the loginView modally? Then you can pop the view and be back at the rootViewController and move on from there.
Create the button pressed method outlet in the same class as that XIB and hook it up. I assume your login view is created by the root controller... create an assign property for the root view on the login view, and then assign the root controller to that when creating the login view. Then, in the login view's method for "button pressed" you can reference the root view controller. Spelled out:
in LoginController's .h file:
#property (nonatomic, assign) RootViewController* rvc;
in LoginController's .m file:
#synthesize rvc;
in Root View Controller's "make the login controller code", after you've initialized it but before you've presented it:
[loginController setRvc:self];
in your LoginController's button touched method:
[[self rvc] whateverMethodThatDoesLoginStuff];
This way you have your path between when the user presses the button and your "do-login" code.
You can have a loginViewController as an IBOutlet, connect its action 'touch up inside' to -loginPressed: of file owner.
Or you can alloc and init your controller in -viewDidLoad programmatically all by yourself, and set the action programmatically as well.
But what you did seems to mix the two ways.
Related
Bit confused with this one so bear with me...
I have a Navigation-based project which is working fine. I'm trying to create my first custom UIView to make a couple of buttons which I will use in multiple places. One of the buttons needs to push a viewcontroller into the navigation when it's clicked but I'm not sure how to do this.
When I had the button set up within a view controller I was using:
LocationViewController *controller = [[LocationViewController alloc] initWithNibName:#"LocationViewController" bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
but the self.navigation controller won't work now, will it? How do I access the navigation controller of the viewcontroller that this uiview will be added to?
Hope at least some of that makes sense, as I said it's my first go at subclassing the uiview and adding it to multiple pages so I'm a bit lost.
EDIT TO ADD - I have the button click events inside the custom UIView, so that is where I'm trying to change the viewcontroller from. Should I instead wire up the events in whichever viewcontroller I add the view to?
Usually your appDelegate has a UINavigationController property. You can access it in your custom view like this:
UINavigationController *navController = (MyAppDelegate *)[[[UIApplication sharedApplication]
delegate] navigationController];
But more effective way is to make delegate method for your custom view and handle button action in your viewController.
MyCustomView.h
#protocol MyCustomViewDelegate
#interface MyCustomView : UIView {
id<MyCustomViewDelegate> cvDelegate; }
#property(nonatomic, assign) id<MyCustomViewDelegate> cvDelegate;
#protocol MyCustomViewDelegate #optional
-(void)didClickInCustomView:(MyCustomViewDelegate*)view withData:(NSObject*)data;
#end
MyCustomView.m
- (void)myButtonClick:(id)sender
{
[self.cvDelegate didClickInCustomView:self withData:someData];
}
So now you can handle this event in any place where is your custom
view.
Add the button from the interface builder or from the view controller's viewDidLoad using code:
CGRect frame = CGRectMake(0, 0, 24, 24);
UIButton *button = [[UIButton alloc] initWithFrame:frame];
[button addTarget:self action:#selector(handleMyButton:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
Then implement -(void)handleMyButton:(id)sender {}; in your view controller. Or you could instead write -(IBAction)handleMyButton:(id)sender {}; and link method and button using the interface builder.
Then inside the method just paste the block of code you posted above. If you started with the Xcode navigation controller template project it should work.
I think it's cleaner to hide the designated initializer initWithNibName: because it is an implementation detail.
When you say you are subclassing the UIView I don't know exactly what you mean. If you want to add another view controller with a custom view just use the UIViewController template and customize the XIB file, no need to subclass an UIView unless you are really modifying its behaviour, which I guess you are not. The view is a view, and the controller stuff like handling buttons should be in the controller.
The actual controller need to be in the navigation controller stack to be able to push another controller.
Or you can make a new navigation controller instance and push your LocationViewController.
I created a view in IB with a navbar and a table. On the navbar I put two buttons, cancel and done. I use this view like a modal view with:
[self presentModalViewController:controller animated:YES];
My problem is when I use:
[self.navigationItem.rightBarButtonItem setEnabled:YES];
to enable the right button. It doesn't work.
Have I to set a delegate? what code passages I have to do? It works if I create an IBOutlet for the right button and I use [doneButton setEnabled:YES], but I think this isn't the proper way.
In order to place a navigation bar on your modal view controller in interface builder (and set up bar button items that call actions in your detail view controller), you need to go through a level of indirection (your navigation bar will be in one .xib, and the details of your detail view will be in a different xib):
create a xib file containing a navigation controller object, and set its root view controller to be your detail view controller that you want to display modally with a navigation bar.
add bar button items to the detail controller's navigation bar and hook them up to IBActions in your detail view controller object.
your detail view controller will need to be in a separate .xib file
create a "loader" object that just exists to hold the navigation controller iboutlet, and set it to be the File's Owner object of that xib:
#interface Loader : NSObject
#property (nonatomic, retain) IBOutlet UINavigationController *navVC;
#end
#implementation Loader
#synthesize navVC;
- (void) dealloc
{
[navVC release];
[super dealloc];
}
#end
Your xib file containing the navigation controller will look like this:
Make sure the navigation controller object is conntected to the "Loader" object's navVC outlet, and make sure the bar button items are connected to your detail view controller's desired IBActions.
Then you present this whole thing using this code:
Loader *loader = [[[Loader alloc] init] autorelease];
[[NSBundle mainBundle] loadNibNamed:#"ModalVC" owner:loader options:nil];
[self presentModalViewController:loader.navVC animated:YES];
Delegate has nothing to do with your issue.
You probably did put navigation bar into your view directly. Thus things like self.navigationItem doesn't work. You have two choices ...
Connect your buttons to outlets in your code and access them directly.
Or remove navigation bar from your view and present your view controller in this way ...
MyViewController *vc = [[MyViewController alloc] initWith...];
UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:vc];
[vc release];
[self presentModalViewController:navCon animated:YES];
[navCon release];
... and now you can access left/right buttons via navigationItem.
I have a UIViewController within a UINavigationController, and I'm pushing a second UIViewController onto the navigation stack from a XIB file. This XIB also includes a UINavigationItem for the second view controller's title and button, but these are ignored when loading the XIB. Any ideas?
Here is my code (photoViewController is the second viewController)
- (void) displayPhotoWithId:(int)photoId {
if (_photoViewController == nil) {
self.photoViewController = [[[PhotoViewController alloc] initWithNibName:#"PhotoView" bundle:[NSBundle mainBundle]] autorelease];
}
_photoViewController.photoId = photoId;
[self.navigationController pushViewController:_photoViewController animated:YES];
}
You can wire this up from interface builder. Add something like this to your view controller:
IBOutlet UINavigationItem* navigationItem;
Then wire it up to the view controller outlets (from File Owner, presumably, to the UINavigationItem in your XIB). I have done this and it works fine. I suppose UINavigationController looks for the 'navigationItem' automagically.
Navigation item is ignored because it has nothing to do with this controller, it's just happened to be in this controller's XIB. Controller's navigation item is created on instantiation. You have to setup title and navigation buttons in code (in viewDidLoad for example) because controller doesn't have navigationItem outlet. UINavigationItem in XIB works only inside UINavigationController's root controller.
I need to pop up a quick dialog for the user to select one option in a UITableView from a list of roughly 2-5 items. Dialog will be modal and only take up about 1/2 of screen. I go back and forth between how to handle this. Should I subclass UIView and make it a UITableViewDelegate & DataSource?
I'd also prefer to lay out this view in IB. So to display I'd do something like this from my view controller (assume I have a property in my view controller for DialogView *myDialog;)
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"DialogView" owner:myDialog options:nil];
myDialog = [nibViews objectAtIndex:0];
[self.view addSubview:myDialog];
problem is i'm trying to pass owner:myDialog which is nil as it hasn't been instantiated...i could pass owner:self but that would make my view controller the File's Owner and that's not how that dialog view is wired in IB.
So that leads me to think this dialog wants to be another full blown UIViewController... But, from all I've read you should only have ONE UIViewController per screen so this confuses me because I could benefit from viewDidLoad, etc. that come along with view controllers...
Can someone please straighten this out for me?
There is no such thing as a view controller being on the screen; its view is on the screen. With that said, you can present as many views as you want on the screen at once.
I would create a new view and view controller. You would not make a UIView be a UITableViewDelegate, you make a UIViewController be a UITableViewDelegate. But instead of doing that manually, instead make your new view controller a subclass of UITableViewController, if you're using iPhone OS 3.x+. You can then present this view controller modally.
You probably want to give the user a chance to cancel out of the selection. A good way to do that is to wrap your new dialog view controller in a UINavigationController and then put a "Cancel" button in the nav bar. Then use the delegate pattern to inform the parent view controller that the user has made their choice so you can pop the stack.
Here's what the code will look like inside your parent view controller, when you want to present this option dialog:
- (void)showOptionView
{
OptionViewController* optionViewController = [[OptionViewController alloc] initWithNibName:#"OptionView" bundle:nil];
optionViewController.delegate = self;
UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:optionViewController];
[self.navigationController presentModalViewController:navController animated:YES];
[navController release];
[optionViewController release];
}
Your OptionViewController .h will look like this:
#protocol OptionViewControllerDelegate;
#interface OptionViewController : UITableViewController
{
id<OptionViewControllerDelegate> delegate;
}
#property (nonatomic, assign) id<OptionViewControllerDelegate> delegate;
#end
#protocol OptionViewControllerDelegate <NSObject>
- (void)OptionViewController:(OptionViewController*)OptionViewController didFinishWithSelection:(NSString*)selection;
// or maybe
- (void)OptionViewController:(OptionViewController*)OptionViewController didFinishWithSelection:(NSUInteger)selection;
// etc.
#end
Your OptionViewController.m will have something like this:
- (void)madeSelection:(NSUInteger)selection
{
[delegate OptionViewController:self didFinishWithSelection:selection];
}
Which has a matching method back in your original view controller like:
- (void)OptionViewController:(OptionViewController*)OptionViewController didFinishWithSelection:(NSUInteger)selection
{
// Do something with selection here
[self.navigationController dismissModalViewControllerAnimated:YES];
}
There are plenty of examples throughout Apple's sample source code that follow this general pattern.
I have a project in which the root viewcontroller call multi viewcontrollers. There is button on a sub viewcontroller's view, when I press the button, I hope it notify the root view controller to load another sub viewcontroller.
//the function in this viewcontroller
-(IBAction)submitButtonPressed:(id)sender;
{
[self.parentViewController notifyLoadAnotherViewContrller ] ;
}
//the function in root viewcontroller
-(void) notifyLoadAnotherViewContrller
{
Submit *tController = [[AnotherViewController alloc] initWithNibName: #"AnotherViewController" bundle:nil];
self.vanotherViewController = tController;
[tController release];
[self.view insertSubview:tController.view atIndex:10];
}
but this does not works
I set the breakpoint in function -(void) notifyLoadAnotherViewContrller
it does not work I checked the function name, no problem.
What is reason I am doing wrong?
Welcome any comment
Best Regards
interdev
So some standard debugging... Set a breakpoint in submitButtonPressed: to make sure your IBAction is properly connected. Examine parentViewController to make sure it's what you expect.
Since the method is not getting called, then your button is either not connected in Interface Builder or your parentViewController is nil, which will silently ignore the method call.