I'm building an application that inserts records into a database, and it pulls items from that database from lookup tables so it has the proper items in predefined fields. Obviously a picker is the best route for this method, but I'm running into a problem. There are 2 different pages with about 15 different pickers between the two of them. Now what I need to do is load the picker as it's popped up when the user selects the field.
All the online tutorials and examples I've found are using either the GUI editor to link a picker with a data source, or using one single data source for a picker in a separate file(which I'm not doing).
How do I go about loading an NSMutableArray into a picker when it's called?
Thanks!
Have a data source and delegate class that implements all the required data source/delegate methods, that is defined as conforming to UIPickerViewDataSource and UIPickerViewDelegate and that has #property (retain, nonatomic) NSMutableArray *dataArray; defined in the header. Make the data source/delegate class's methods (such as - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component) load the strings etc from dataArray. Then when creating/changing the picker, do the following:
MyPickerSource *source = [[MyPickerSource alloc] init];
source.dataArray = [NSMutableArray arrayWithArray:<PreloadedArray>];
[picker setDelegate:source];
[picker setDataSource:source];
[picker reloadAllComponents]; // If re-using an old picker
[source release];
[self addSubview:picker]; // If creating a new picker
Hope this is all clear!
You want to use a UIPickerViewDataSource and UIPickerViewDelegate, and implement the necessary functions, namely numberOfRowsInComponent: and titleForRow:, through these methods you can draw the data from your NSMutableArray.
The important thing to consider is for you to call reloadAllComponents on the pickerview when you need it to update. You can also use selectRow: inComponent: animated:NO to keep the picker scrolled to the appropriate selection when you update the components in the list.
Related
I have 3 view controllers.
Navigation Controller - N
Table View Controller - T
Custom View Controller - C
Their layout is like so:
N -(root view controller)-> T -(bar button item Add - Triggered Segue)-> C
Just to clarify, T has an "Add" button which has a triggered segue action to present C modally.
C and T both have custom classes. C is a simple UIViewController subclass while T is a UITableViewController subclass.
In T I declare an NSMutableArray and a UITableView like so:
#interface T: UITableViewController {
NSMutableArray *myTableData;
IBOutlet UITableView *myTableView;
}
In C I have two elements within:
A UITextField
A UIButton called "send"
I connect the text field to an IBOutlet like so in C's header, as well as create an action for the button:
#interface C: UITableViewController {
IBOutlet UITextField *itemToAdd;
}
-(IBAction)addItem:(id)sender;
So I have the data array myTableData which populates T's table declared and initialized in T (I will add a myTableData = [[NSMutableArray alloc] init] in the viewDidLoad method of T.
When the "add" button in the navigation bar of T is clicked it will obviously bring up C and take in some text from the text field.
Then finally when the send button is pressed I want to add that text from the text field into the myTableData array, dismiss C (which I plan on doing with [self dismissViewControllerAnimated:YES completion:nil], and update the table view myTableView to reflect the changes that I have made.
Does anyone have any idea on how I could accomplish this? Mainly I need help somehow accessing myTableData (that lives in T) FROM C. Then I need to get T to recognize that some change has been made and to redraw its cells (probably by instigating cellForRowAtIndexPath). I am aware of how to do this all hardcoded Apple's docs, but I find that method isn't viable if you want to visually represent everything in the storyboard and they do not explain what everything means exactly (like itemInputController for instance).
If my approach is totally incorrect and I don't need all these classes or methods or am doing it all wrong, please feel free to give me a similar approach using the storyboard.
Create an NSMutableArray and store it in your NSUserDefaults. Here's how:
In your C - UIViewController, on the send button IBAction :
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *arr = [defaults objectForKey:#"data"];
[arr addObject:WHATEVER_YOU_WANT_TO_ADD];
[defaults setObject:arr forKey:#"data"];
[defaults synchronise];
You can use this same method in T!
okay below is a standard example of creating a datepicker
- (void)viewDidLoad {
CGRect pickerFrame = CGRectMake(0,250,100,100);
UIDatePicker *myPicker = [[UIDatePicker alloc] initWithFrame:pickerFrame];
[myPicker addTarget:self action:#selector(pickerChanged:) forControlEvents:UIControlEventValueChanged];
[self.view addSubview:myPicker];
[myPicker release];
}
- (void)pickerChanged:(id)sender
{
NSLog(#"value: %#",[sender date]);
}
this is all good and well. I'm a little used to creating elements in IB so when I create an object programatically I'm not sure how to access the data.
What I mean is.. should I assign myPicker to a class property and then access it as _myPicker?
Or lets say I want to access the date inside of the pickerChanged method without calling another method. Should I assign an NSDate property and re-assign it every time the picker is changed?
I ran into some memory issues when I was trying to do it that way. I had another method grabbing _theDate, and it probably tried to access it at the same time pickerChanged was modifying it?
Anyway, what I'm getting at is "whats the proper workflow when creating things like action sheets, and pickers programmatically". When these things are changed, how should the resulting data be saved so the rest of the class can access it?
Bonus question:
Is there a difference between this?
for(UILabel *myLabel in view.subviews){
NSLog(myLabel.text);
}
and this? Do I need to check the class all the time if i know my view only contains a certain kind of object?
for((id) myLabel in view.subviews){
if([myLabel isKindOfClass:[UILabel class]){
UILabel *theLabel = myLabel;
NSLog(myLabel.text);
}
}
Generally, you will just define properties if you'll need to access them more than once. You can do this in the .m file's interface:
#interface MyObject()
#property (weak, nonatomic) UIDatePicker *myPicker;
#end
You will then be able to access it by either _myPicker or self.myPicker.
You shouldn't need another NSDate property in your class because you can access the set date at any time:
_myPicker.date
For your last question: the latter of the two is merely extra sanity checks. While you're writing your own code, and you should know what subviews you're adding in, it can't hurt to double check the type of the subviews incase anything should go wrong and you try to access selectors that don't exist. This is a larger programming question though and not necessarily objective-c or iOS specific.
The documented approach is to intercept the UIControlEventValueChanged event, as per your example.
You would then typically copy the [sender date] value to a property in your pickerChanged: method.
If the user hits a save button, then the object that presented the view containing the picker should be able to retrieve the selected date via the property.
It's not considered good practice to use isKindOfClass:. You should structure your code such that you always know what class you're dealing with.
Also, you should really switch to ARC so you don't need to worry about calling release
You need to declare a UIDatePicker property to hold one instance of your child controller
This is what you need to add in your .h file:
#property (strong, nonatomic) UIDatePicker *myPicker;
And then in your .m file you need to add a data source method for this date picker. something like what rdelmar has instructed above:
self.myPicker = [[UIDatePicker alloc] init];
I have a UIViewController class and a UITableViewController class. Within the UIViewController class I have an NSMutableArray.
I now have the issue of how to load data into my table view, a separate class, I must access the NSMutableArray I used to populate the previous UIViewController class.
I tried using a delegate to access the array in the UIViewControllerClass however the array had "0 objects" and was NULL
I would appreciate some guidance in the right direction here.
You could have one view controller hold a reference to the other view controller and query the public NSMutableArray on it for data. Aaron suggested this and it might be your best solution.
Or.. you have multiple view controllers trying to access the same set of data. Potentially you have other classes which will want to access this data also. You might want to consider pulling the data out of the view controller and storying it in a neutral location. You could store it in the AppDelegate and then reference the app delegates from any place you need it.
id<UIApplicationDelegate> appDelegate = [UIApplication sharedApplication].delegate;
NSMutableArray *myData = appDelegate.data;
You could also consider pulling all the logic of your data and the data itself into a separate class and use a Singleton It would allow you to access/manipulate the data fairly easy from anywhere.
The last 2 methods would insulate data from user interface controller objects and prevent the need from potentially unrelated objects needing to hold references to one another. Used properly it will reduce code complexity and mage future changes easier to manage.
Create an NSMutableArray property on your UITableViewController class like so:
#interface CustomTableViewController : UITableViewController
#property (strong, nonatomic) NSMutableArray *dataFromOtherClass;
#end
And then when you transition, perhaps like this, you can set the dataFromOtherClass property:
CustomTableViewController *controller = [[CustomTableViewController alloc] initWithNibName:#"CustomTableViewController" bundle:nil];
controller.dataFromOtherClass = myNSMutableArrayData; // <-- Set data like this
[self.navigationController controller animated:YES];
// Or ...
[self presentViewController:controller animated:YES];
// Etc...
I have a UIViewController in my iOS application that displays a table that is derived from an NSMutableArray. The cells in this table each refer to a unique UIViewController that is called when the user makes a selection. What I am trying to do in my "didSelectRowAtIndexPath:" method is to dynamically create the UIViewController via an NSMutableDictionary that contains keys that match the values in the NSMutableArray that the table is built from, as well as values that contain the corresponding Class names for the respective UIViewController that needs to be called. Because the list is rather long, I figure I need to do this using a for loop, but I am a bit confused as to how to do it. My NSMutableDictionary looks like this:
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:#"aViewController" forKey:#"SelectionA"];
[dict setObject:#"bViewController" forKey:#"SelectionB"];
[dict setObject:#"cViewController" forKey:#"SelectionC"];
and my NSMutableArray that is the basis for my TableView looks like this:
NSMutableArray *myArray = [[NSMutableArray alloc] initWithObjects:#"SelectionA", #"SelectionB", #"SelectionC",...,nil];
How would I obtain a reference to the value inside the cell, and then construct a for loop that would dynamically create the correct viewController that corresponds to the selection made by the user from the tableView, and then take the user to that viewController via the navigationController?
Thanks in advance to all who reply.
Its not a good idea to create many ViewControllers, you should create one ViewController,
and pass the value of they tableCell to it. In other words, you change the data modell of the ViewController, by selecting the cell. But you will present the Same ViewController.
Only in the case that your cells coreesponmd to different types (e.g one cell a road mao, another a text value) , you have to call different ViewControllers.
If you realy need different view contollers, then get the type you want to dispaly from the cell data
in didSelectCellRowAtIndexPath
myAppDelegate *appDelegate =
[[UIApplication sharedApplication] delegate];
UIViewController *viewControllerToDisplay;
switch (selection.type) {
case MapType:
viewControllerToDisplay = appDelegate.mapViewController;
case Picture:
viewControllerToDisplay = appDelegate.pictureViewController;
}
now push viewControllerToDisplay to navigaton Controller.
If you really need a view controller for each cell, there's no need to use a dictionary to look them up. Since selection will be by index path, an array is a better choice.
Create a custom object that has two properties: the name you want to display in the cell and a pointer to the view controller you want to push when it's selected. Load myArray with these objects instead of strings. When you are populating a cell, select the object that matches the row and use its name. When a cell is tapped, select the object that matches the row and push its controller.
(But, as others have said, if you can use the same controller and only change the data, that's the way to go!)
Here is the solution,
If you know the name of class then store all the classes name in array with dictionary having key ClassName and Xib. I prefer plist to store names but you can use other way also.
And at didselect of table or picker place the code like this,
Class classobject = NSClassFromString([[ClassArray objectAtIndex:row]valueForKey:#"ClassName"]);
id object = [[classobject alloc] initWithNibName:[[ClassArray objectAtIndex:row]valueForKey:#"Xib"] bundle:nil];
[self.navigationController pushViewController:object animated:YES];
First line will convert your string to class.
Now as we have a benefit that id can hold any object so creat the object using id. And finally you have a custom class object you can do whatever you want to do with it,here for just a demo I did navigation.
I am trying to create the UIPickerview as described in the iPhone development book by Dave Mark. I have a NSArray which is declared as a property in the h file which will store the data for the UIPickerview. So here is what I have:
in the .h file:
#interface RootViewController : UIViewController {
NSArray *dateForPicker;
}
#property (nonatomic, retain) NSArray *dateforPicker;
#end
In the .m file viewDidLoad method (I do have #synthesize for thedateForPicker property at the beginning of the .m file):
NSArray *tempArray = [[NSArray alloc] initWithObjects:#"1",#"2",#"3",#"4",#"5", nil];
self.dateforPicker = tempArray;
[tempArray release];
When the UIPickerview comes up, it comes up with "?" in all the rows. So when I used a breakpoint to inspect the values of tempArray and dateForPicker in the viewDidLoad method, I find that the tempArray is fine but the dateForPicker never gets the values from the tempArray. Xcode says "Invalid Summary" for the dateForPicker array and has "out of scope" as the values for the five rows. What is going on? As described by the book, this should work.
Here is the code for the UIPickerView:
#pragma mark -
#pragma mark picker data source methods
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return [dateforPicker count];
}
#pragma mark picker delegate methods
-(NSString *)pickView:(UIPickerView *)pickerView titleForRow:(NSInteger)row
forComponent:(NSInteger)component{
return [dateforPicker objectAtIndex:row];
}
#end
Some problems with your code. I'm not clear if you've typed this into the question manually or copied and pasted from your actual code:
You are setting self.dateforPicker and not self.dateForPicker, there is a difference in capitalisation between your ivar and your property. In iOS the compiler will have synthesized a dateforPicker ivar when you declared your property, which was set in your viewDidLoad, but in your other methods you may be referring to the dateForPicker ivar, which is never touched.
Your RootViewController does not declare that it implements the UIPickerViewDataSource or UIPickerViewDelegate protocols
Your declaration of the titleForRow method is wrong - yours begins with pickView rather than pickerView so will not get called.
If you have the correct number of rows in your component (you said multiple question marks, how many?), so it looks like the data source is wired up properly, but you also need to connect the delegate, as this is what actually supplies the values for each row. The datasource, confusingly, only supplies the number of components and the number of rows per component.