UIPopover and UITableView data exchange - iphone

I have a UITableView in a UINavigationController. On the navigation bar I have a button called add. When this button is pressed it presents a UIPopoverController, where user can input data to be added as a new row/cell in the UITableView. My issue is how can I add a new cell to the UITableView from the UIPopover? Do I pass in the array data to the UIPopOver root controller?

There are two solutions to this that I'm aware of. One would be to send a notification from the popover to the root controller and apply the necessary code to update the tableView in the handleNotification method.
The other, one that I personally use, is to set up a delegate protocol for the popover. You'll have to set it up something like this:
#protocol PopoverDelegate
- (void)addNewCell; // you can add any information you need to pass onto this if necessary such as addNewCellWithName:(NSString *)name, etc.
#end
#interface MyPopoverViewController..... {
id <PopoverDelegate> delegate;
// the rest of your interface code;
}
#property (nonatomic, retain) id delegate;
// any other methods or properties;
#end
Then in your root view controller header file, you need to add the delegate
#interface RootViewController .... <PopoverDelegate> {
Then in your root view controller implementation file, assign the popover delegate when you instantiate it. For example:
MyPopoverViewController *vc = [[MyViewController alloc] init];
vc.delegate = self; // this is where you set your protocol delegate
myPopover = [[UIPopoverController alloc] initWithContentViewController:vc];
myPopover.delegate = self;
[vc release];
Finally, you'll add your protocol method somewhere in the code
- (void)addNewCell {
// do what you want with the tableView from here
}
Sorry that's a bit long. I just wanted to make sure I was thorough. Hope it helps

Related

How to trigger an action from one view controller to another in iOS

How would I go about changing the a UILabel property in another view controller?
I have #import "SecondViewController.h" imported in the FirstViewController.m file and then
I have the following in a method in FirstViewController
-(IBAction) someAction {
SecondViewController *objV1 = [[SecondViewController alloc]init];
objV1.secondViewControllerLabel.alpha = 0.2;
NSLog(#"someAction");
}
when someAction is called nothing happens to the UILabel in the SecondViewController.
also, in this example both first and second view controllers are in another view controller called MainViewController. So, they are both onscreen at the same time.
thanks for any help.
From what you tell us, it would seem that you need to set the "embeded view controllers" as childs of the parent View Controller.
[mainViewController addChildViewController:childViewController];
[childViewController.view setFrame:self.view.bounds];
[self.childContainerView addSubview:childViewController.view];
[childViewController didMoveToParentViewController:self];
This is very powerful, because you can forward IBActions from the mainViewController to their child...
[mainViewController childViewControllers]
Returns an array of them, and also take a look at
– shouldAutomaticallyForwardRotationMethods
– shouldAutomaticallyForwardAppearanceMethods
So your child get automatically informed about the rotations of their parent.
To answer your question, you could do something like:
// In Parent View Controller
- (IBAction) anAction:(id) sender
{
for (CustomChildController *child in self.viewControllers) {
[child handleSomeAction];
}
}
Check out what the docs say for more details.
#Goles answer will work, but if you specifically want to trigger the change from FirstViewController.m you need to pass in a reference to SecondViewController somehow.
So you could do it with a custom init that takes a reference to your second viewcontroller as a parameter, or create a property on your FirstViewController that you can set from outside, which would be something like this:
FirstController.h:
#interface
..
#property (strong, nonatomic) UIViewController *second;
...
#end
FirstController.m:
#implementation
#synthesize second
In your parent ViewController you would create both the child view controllers, then:
ViewController1.second = ViewController2;
Then your action method would become:
-(IBAction) someAction {
self second.secondViewControllerLabel.alpha = 0.2;
NSLog(#"someAction");
}
Since in the secondViewController, secondViewControllerLabel has not been created yet, 'objV1.secondViewControllerLabel.alpha' will have no effect. Ideally, you should create a NSNumber property called labelAlpha in the secondViewController, set that property in the firstViewController, and then in the viewDidLoad of the second controller, add this line ::
self.secondViewControllerLabel.alpha = self.labelAlpha;
This will work for you.

Dismissing WEPPopover from its subview

I am presenting a WEPPopoverController in my iPhone Application. In that WEPPopoverController, I have added a UITableViewController. This popover I am using for the purpose of search.
Now when I open this Popover, the screen looks something like this:
Now, I want to close dismiss this Popover from the UITableViewController's didSelectRowAtIndexPath,
How to do this?
Code:
self.searchTableViewController = [[SearchTableViewController alloc] initWithStyle:UITableViewStylePlain]; // UITableViewController
self.seachPopoverController = [[[WEPPopoverClass alloc] initWithContentViewController:self.searchTableViewController] autorelease]; // WepPopoverController
self.seachPopoverController.delegate = self;
[self.seachPopoverController presentPopoverFromRect:[searchB frame] inView:topPanelV permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
Thanks in advance!!!
Add 1 variable in SearchTableViewController as parent like
id parent;
Create property for this as
#property (nonatomic,assign)id parent;
Synthesize it
#synthesize parent;
Now
when you create self.searchTableViewController
assign its parent as
self.searchTableViewController.parent = self;
Now in didSelectRowAtIndex
[parent dismisMyPopoverMethod];
-(void)dismisMyPopoverMethod
{
[self.seachPopoverController dismissPopoverAnimated:YES];
self.seachPopoverController = nil;
}
And thats it. Your are done.
Do it this way: make your view controller a delegate of table view controller for only one method - dismissPopover (for other methods the delegate must be table view controller). So your table view controller will have two delegates. Implement this method in your delegate (view controller). But call it from table view controller. In more details step by step:
Declare a property of your popover in a view controller, where you show this popover.
#property (strong, nonatomic) UIPopoverController *popoverController;
Create a new protocol (let's name it TableInPopoverDelegate), that has this method:
- (void) dismissPopover;
Add this method to the implementation file of your view controller and add this:
- (void) dismissPopover {
[self.popoverController dismissPopoverAnimated:YES];
}
Add a header file of protocol and add a delegate property to your table view controller:
#property (strong, nonatomic) id <TableInPopoverDelegate> popoverDelegate;
Make your view controller a delegate of your table view controller before adding it:
tableViewController.popoverDelegate = self; // Here self is your view controller
Call dismissPopover on the delegate when user selects a row.
[popoverDelegate dismissPopover];

Delegate set in View Controller, not maintaining in subview

I asked this question earlier with way too much code.
The ViewController initializes a UIView chain, Controller>>View>>SubView, in the ViewController. After the SubView is initialized the ViewController is set as its delegate:
aSubView.delegate = self;
NSLog(#"$#",aSubview.delegate), returns the ViewController, so I know it is set.
In the SubView, NSLog(#"$#",self.delegate),returns random crap such a hr.lproj or a file path to the Foundation framework.
It crashes when attempting to implement any of the delegates methods, since the delegate doesn't link to the ViewController but instead randomness.
This is what the SubView.h file looks like:
#import "TestDelegate.h"
#interface TestSubView : UIView {
id<TestDelegate> delegate;
}
#property (assign) id<TestDelegate> delegate;
EDIT: ViewController is initialized in the app delegate as such:
ViewController *controller = [[ViewController alloc] init];
[window addSubview:controller.view];
[controller release];
The only other thing I added to the App Delegate, over the default is an import of the ViewController header
Is it possible the view controller object is being released/dealloced between the two calls to NSLog?

How to access data from one view to another view?

I have an UITabBarController with two tabs:
UINavigationController
OptionsViewController : UIViewController
How can I reach data (ie. UILabel.text) set in OptionsViewController, in a new added modal View which has been invoked from UINavigationController?
Edit1:
Scenario: After launching app I select the second tab bar called "Options" where I fill up a textField. The label is set to value from textField. Next I select first tab bar called "Main" where I have a button. I click the button and new modal View appears. In this new modal View I'd like to show the value from textField
I love MVC, but I'm not an absolute purist to the point of hurting yourself to accomplish a fairly trivial task, so the answers you've gotten here are good and useful. However, by creating an ivar to refer back to a specific type such as a label or other view controller, you are coupling things together that aren't necessary to couple. What you could do instead is make your first tab view controller a delegate of your second tab view controller. So do something like this in your app delegate.
OptionsViewController *optionsViewController = // ... get this from the tab view
FirsTabViewController *firstTabViewController = // ... same here
[optionsViewController setDelegate:firsTabViewController];
Which means that you need an ivar in your OptionsViewController:
#property (assign) id delegate;
Then, when whatever event you want to trigger the change occurs in your options view controller, see if the delegate can respond to a selector you've named. For example:
- (void)someEventHappenedLikeTyping:(id)sender;
{
if ([delegate respondsToSelector:#selector(setOptionsString:)]
[delegate performSelector:#selector(setOptionsString:) withObject:[label text]];
}
Notice you never specified any specific object types. You just check to see if the delegate (which was declared as id) can respond to that selector. If it can, it does what it's told and just is silent otherwise.
For this to work, you need an ivar for the optionsString in your FirstTabViewController and so it would be declared in the header as:
#property (copy) NSString *optionsString;
and then #synthesize it in the .m. This causes -setOptionsString to become a valid selector that will get called in the -someEventHappenedLikeTyping method.
Anyhow, now, if you ever need to to change which view controller references which, you don't have to go into the header and change the type of ivar referenced. You simply need to implement the selector (this is known as an informal protocol, by the way) in the view controller that is a delegate of your options view controller.
Just some food for thought there. Hope that helps. There is further de-coupling that could be done in the code I've added, but again it may be overkill for such a simple task. Let me know if you need clarification or want to understand what I mean by further decoupling.
Best regards,
p.s. Sometimes needing to share data between two tab bar view controllers, means you have a design flaw. If you are wanting to store preferences from your options view, you should just call
[[NSUserDefaults standardUserDefaults] setObject:[label text] forKey:#"option1"];
[[NSUserDefaults standardUserDefaults] synchronize];
Then you can pull from the NSUserDefaults back in your main tab with;
NSString *option1 = [[NSUserDefaults standardUserDefaults] objectForKey:#"option1"];
// Do something with option1
In your OptionsViewController, create a property:
#property (nonatomic, retain) UILabel *mylabel;
then after creating your OptionsViewController, but before displaying it, set the mylabel property. (Or perhaps you just want the text, so you can use an NSString* property.)
Edit:
So you probably want to do something like this:
OptionsViewController *vc = [[OptionsViewController alloc] init];
vc.mylabel = mySomethingLabel;
[self presentModalViewController:vc animated:YES];
So after creating the object, you set the property, and then you display the view controller.
I usually set IBOutlets in each of my viewcontrollers which point to the other controller.
So if I had view controllers A and B. A has an IBOutlet to B and B to A. Then whenever I want to access anything in B from A i just use a dot operator on B.
In your example UINavigationController would #include "OptionsViewController.h" and have an ivar IBOutlet OptionsViewController * ovc (which is set in IB) and then any instance variable from your options view controller can be referenced as ovc.UILabel.text from the navigation controller. This process can be reversed to access values from your navigation controller in your options view controller.
Example Navigation Controller (.h):
#include "OptionsViewController.h"
#interface UINavigationController // (whatever the name of this class is)
{
OptionsViewController * ovc;
}
#property (nonatomic, retain) IBOutlet OptionsViewController * ovc;
#end
Example OptionsViewController.h:
#interface OptionsViewController
{
UILabel * label;
}
#property (nonatomic, retain) IBOutlet UILabel * label;
#end
Then from UINavigationController (.m) you can just write ovc.label.text to access the text.
I have an easy way to access data between views. Let's try. You have two views named view1 and view2, you can define a View *vc1 property in view2.h, set the vc1 point to view1, when pop the view2, like this:
view1.m
//here pop out view1 code
View2 *view2 = [[View2 alloc] initWithNibName:#"View2" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:view2 animated:YES];
view2.vc1 = self; //transfer view1 instance to view2,use vc1 you may handle view1 in view2 directly
[view2 release];
view2.h
#import "view1.h"
#property (nonatomic, retain) IBOutlet View1 *vc1; //here you could name it as view1 as well :)
view2.m
vc1.lblTable.text = #"ok"; //you'll see "ok" in view1
[self.navigationController popViewControllerAnimated:YES]; //navigate to view1
//dont forget release vc1

Pop-up modal with UITableView on iPhone

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.