I am trying to save the selection in a UIPickerView as a string, but I seem to be losing the value after I dismiss the view. How can I retain this value?
.m file
/*********************************/
#pragma mark -
#pragma mark PickerView DataSource
//Below are the functions for the UIPicker:
- (NSInteger)numberOfComponentsInPickerView:
(UIPickerView *)pickerView
{
return 1;
}
//count of categories array denotes how many elements the picker will have
- (NSInteger)pickerView:(UIPickerView *)pickerView
numberOfRowsInComponent:(NSInteger)component
{
return arrCategorySelection.count;
}
//return the corresponding caregory
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return arrCategorySelection[row];
}
/*********************************/
#pragma mark -
#pragma mark PickerView Delegate
//Delegate method for the category picker
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
//trying to save the value as a string
selectedCategory = [arrCategorySelection objectAtIndex:row];
}
.h
#property (nonatomic, retain) NSString *selectedCategory;
A property is tied directly to the controller that it is declared in. To use the property, you need to access it before the controller is dismissed, normally with a delegate call. Apple has lots of example code on how to do this. Below is a summary.
Let's say MasterView is presenting PickerView. Normally, you want to do this with a segue.
[self performSegueWithIdentifier:#"Present PickerView" sender:nil];
In PickerView.h you declare a protocol before the #interface portion:
#protocol PickerViewDelegate <NSObject>
-(void)itemSelected:(NSString*)item;
#end
Then in the #interface portion, you declare a delegate property
#property(weak)id<PickerViewDelegate> delegate;
In MasterView, you need to populate this delegate in your prepareForSegue method
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"Present PickerView"]) {
PickerView *controller = segue.destinationViewController;
controller.delegate = self;
}
}
You also need to implement the delegate method (remember, it is best practice for the MasterView to both present and dismiss PickerView)
-(void)itemSelected:(NSString*)item {
// do something with item
[self dismissViewControllerAnimated:YES completion:nil];
// depending on the presentation, you may need this instead
// [self.navigationController popViewControllerAnimated:YES];
}
Now in PickerView.m, establish some way for the user to say he is done with his selection, say a tap of the save button on the upper right that calls this method:
-(IBAction)saveButtonTapped:(id)sender {
if (self.delegate && [self.delegate respondsToSelector:#selector(itemSelected:)]) {
[self.delegate itemSelected:self.selectedCategory];
}
Related
As beginner in iOS Developing I struggle with specific implementation ofUIPickerView.
I already know how to createUIPickerView with specific data.
My goal is a bit complicated though - I would like this PickerView to print me selected value. For example - like in simple currency calculator - I pick EUR currency and I have value "4.30" printed out.
FYI - I need to pass this value to another ViewController just to be multiplied.
Any help will be appreciated!
Here's sample code - for now I am still trying to understand what should I do here
#interface SecondViewController ()
{
NSArray *currency;
}
#property NSArray *currency;
#end
#implementation SecondViewController
#synthesize currencyValue;
#synthesize currency;
- (IBAction)Back:(id)sender {
[self dismissViewControllerAnimated:NO completion:NULL];
}
- (NSInteger) numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
- (NSInteger) pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return [self.currency count];
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:
(NSInteger)component {
return self.currency[row];
}
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
[currencyValue setText:[NSString stringWithFormat:#"Exchange rate of currency : %#",[currency objectAtIndex:row]]];
}
-(IBAction)currencyValue:(id)sender {
}
- (void)viewDidLoad {
[super viewDidLoad];
self.currency = #[#"EUR", #"PLN", #"CFH", #"DKK", #"SEK", #"NOK"];
In UIPickerViewDelegate you can set the rows' titles with pickerView:titleForRow:forComponent: as described in the documentation.
The UIPickerView's selectedRowInComponent: will return you the current selected row: use the previous data source with this index to retrieve the current selected title.
Okay, I need a picker so that a user can select from a list of predefined options. Can someone give me the easy, simplified version of how to populate a picker view from an array of NSStrings? Then, how do I read that value from the picker? I'm noticing that things like nameOfPicker.value and nameOfPicker.text do not work here.
Thank you!
I have already read the following documents and I don't really understand what they are getting at.
https://developer.apple.com/library/ios/documentation/userexperience/conceptual/UIKitUICatalog/UIPickerView.html
https://developer.apple.com/library/ios/documentation/General/Conceptual/CocoaEncyclopedia/DelegatesandDataSources/DelegatesandDataSources.html#//apple_ref/doc/uid/TP40010810-CH11
It is quite similar to how you populate data in a UITableView, by setting datasource and delegate.
1. First step is to set the delegate of the picker view. In .m file you set your datasource and delegate
#interface YourViewController () <UIPickerViewDataSource, UIPickerViewDelegate>
{
}
#property (strong, nonatomic) UIPickerView *yourPickerView;
2. Assign the datasource and delegate
self.yourPickerView.delegate = self;
self.yourPickerView.datasource = self;
3. Implement datasource and delegate
// returns the number of 'columns' to display.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
// returns the # of rows in each component..
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return self.yourArrayofStrings.count;
}
//The title that should be shown in picker view
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
NSString *yourTitle = [self.yourArrayofStrings objectAtIndex:row]
return yourTitle;
}
//This is called when Picker view value is selected
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
NSLog #(#"Selected row = %#",[self.yourArrayofStrings objectAtIndex:row]);
}
I'm a newbie in IOS,
I've been strugling with passing data from child to parent tableview. I have defined the parent table view as static, four cells are connected to other table views. These tableviews has data i would like when selected to be passed to my static cell accordingly. I read a lot of solutions about passing data, using delegates and segue but none of them seem to be working for me. i.e. a repeat cell in static table view has two labels and the UILabel Repeat, I don't want that to change and repeatDetail this is the one that when a disclosure indicator is triggered and a new tableview is presented with the data to choose to be able when i click back button to have the seleted data in my repeatDetail Label. My static table is embeded in Navigation controller using storyboard. I would like when data is selected in FirstChildViewController to modify selected data i.e. Monday to Mon in RootViewController. However in my code after selecing data in child checkmark is there but as soon
as I move back to RootVC nothing is showing,and when i go back to Child no selction is howing either.
1. Save the selected Data in Child, only change when there is new selection
2. Use short week names when sending to RootVC
3. repeatDetail to have the selected data
Without writting too much let me show what i have done.
in RootViewController.h // RootViewController is static
#import "FirstChildViewController"
#interface RootViewController: UITableViewController <repeatProtocol> //RootViewController COnfirms to the delegate
#property repeat, repeatDetail;
#end
next on my RootViewController.m
#implementaion RootViewController
#sysnthesis repeat,repeatDetail;
- (void) viewDidload
{
repeat.text = #"Repeat"
repeatDetail= //not show how call this label from 1stViewController
}
-(void) selectedValue:(NSString *)string //protocol method
{
FirstChildViewController *RVC =[[FirstChildViewController alloc] init];
RVC.delegate =self;
[self selectedValue:string]; //This part confuses me, i know i have to implement the delegate method but not sure if i implement it correctly.
}
-(void) didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
in FirstChildViewController.h
#class FirstChildViewController
#protocol repeatProtocol <NSObject>
#required
-(void) selectedValue:(NSString *)string;
#end
#interface FirstChildViewController: UITableViewController
{
NSArray *tableData;
id <repeatProtocol > repeatDelegate;
NSString *selectedDay;
}
#property (retain) id <repeatProtocol> repeatDelegate;
in FirstChildViewController.m
#synthesize tableData;
#synthesize repeatDelegate;
- (void) viewDidLoad
{
[super viewDidLoad]
tableData= [NSArray alloc] initWithArrays:#"Sunday",#"Monday",#"Tuesday",#"Wednesday",#"Thursday",#"Friday",#"Saturday";
}
- (int)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (int)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 7;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"RepeatCell"];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"RepeatCell"];
}
cell.textLabel.text = [tableData objectAtIndex:indexPath.row]//strings from an array here;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = {tableView cellForRowAtIndexPath:indexPath];
cell.accessaryType = UITableViewCellAccessaryCheckMark;
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if([self.delegate respondsToSelector:#selector(selectedValue:)])
{
[self.myDelegate selectedValue:selectedDay];
NSLog(#"string passed");
}
[self dismissViewControllerAnimated:YES completion:nil];
NSLog(#"FirstChildViewController dismissed");
}
#end
It's a little hard to sort out what's happening from your description; so I'll restate what I think the issue is.
You have a UITableView that displays something like settings that you which to modify in a series of distal view controllers. But you're unsure of what mechanism to use when returning that data to the static table view. Basically, you want to capture that data when the distal controller finishes. I won't deal with how you're displaying it in the root view controller, because it's unclear from your code sample.
Nonetheless, I'd favor not using a formal delegate protocol at all. It's just one datum going back - so a protocol seems like a wasted formality. I'd use a completion block.
So your FirstViewController interface could look like
typedef void(^WeekdayCompletionBlock)(NSString *dayName);
#interface FirstViewController : UIViewController
#property (nonatomic, strong) WeekdayCompletionBlock completionBlock;
#end
When you instantiate your FirstViewController, just provide it with a completion block. Since I think you are using Storyboards, you'd do this in prepareForSegue: method of your RootViewController.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
UIViewController *destinationController = segue.destinationViewController;
if( [destinationController isKindOfClass:[FirstViewController class]] ) {
[(FirstViewController *)destinationController setCompletionBlock:^(NSString *returnString){
// do something here with your string
// maybe you must reload your table
// it depends on where your returning data needs to display
}];
}
}
Finally, you need to execute that block when the user passes control back to the RootViewController. For example, is there a Save button or the like? There you would just execute the completion block, e.g. self.completionBlock(myNewDayOfWeekString)
Alternatively, you can create a global NSString in rootVC.h:
NSString *returnString;
Include rootVC.h in firstVC.h if you haven't already done that. This allows returnString to be accessible from firstVC.m:
#import "rootVC.h"
You can assign returnString in firstVC.m:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
...
returnString = <selected value>;
}
And show the returnString in rootVC.m below as NSBum pointed out:
-(void) viewDidload
{
repeat.text = #"Repeat";
repeatDetail.text = returnString;
}
I have a pickerview that loads from a core data fetch on the view controller did load method. Everything works great, however I'm finding that I'm passing null values quite often, and that I'm having to spin the picker back and forth and click several times to get the value to hold.
I also have a problem if the user doesn't manually pick something, I need it to pass the value that the picker is on. I have a datepicker on the same viewcontroller and it passes whatever date it is sitting on without any problems. I'm probably overlooking something..any help would be appreciated
# pragma mark PickerView Section
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1; // returns the number of columns to display.
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return [profiles count]; // returns the number of rows
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
// Display the profiles we've fetched on the picker
Profiles *prof = [profiles objectAtIndex:row];
return prof.profilename;
}
//If the user chooses from the pickerview
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
selectedProfile = [[profiles objectAtIndex:row]valueForKey:#"profilename"];
}
# pragma mark Segue Section
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"resultsSegue"]) {
ResultsVC *result = segue.destinationViewController;
result.profileName = [NSString stringWithFormat:#"%#",selectedProfile];
NSDate *selectedDate = [datePicker date];
result.trialDate = selectedDate;
}
}
You need to initialize selectedProfile to the 1st value in the picker. Do this in viewDidLoad. This way if the user never explicitly picks a value, the selectedProfile will be set to the 1st value by default.
I need the last selected row in my pickerview to be totally recalled when the view appears! I have this code in my viewDidAppear it animates to the last selected row but it doesn't really call the delegate and the NSLog in my rows won't print without touching my picker and reselect them.
How do I do that?
- (void)viewDidAppear:(BOOL)animated {
NSUserDefaults *pickerViewSelectionDefaults = [NSUserDefaults standardUserDefaults];
[tasbeehPicker selectRow:[pickerViewSelectionDefaults integerForKey:#"picker"]
inComponent:0 animated:YES];
[pickerViewSelectionDefaults synchronize];
[UIPicker reloadAllComponents];
NSLog(#"Last selcted row was %d ",[[NSUserDefaults standardUserDefaults] integerForKey:#"picker"]);
}
The delegate is not supposed to be called when you update the picker through code. The delegates are only called when the user interacts with the picker. If you need the same code called in both cases then do something like this:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[myPicker selectRow:4 inComponent:0 animated:animated];
[self handlePickerSelection:myPicker];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
[self handledPickerSelection:pickerView];
}
- (void)handlePickerSelection:(UIPickerView *)pickerView {
// process selection
}
You can get selected row from below method,
Add this code in viewDidAppear,
int index = [pickerViewObject selectedRowInComponent:0];
[pickerViewObject selectRow:index inComponent:0 animated:YES];
selectedRowInComponent will give you selected row number, and then you can use selectRow:Row_number inComponent:Component_number animated:YES to select any row, picker delegate method will not get called.
If you want to save selected row in NSUserDefaults than you can do in in viewWillDisappear,
int index = [pickerViewObject selectedRowInComponent:0];
now save this index value in NSUserDefaults.