Search code examples
uipickerview

Display data from 2 arrays in one UIPickerView component


I'm writing a program that implements a UIPickerView with a single component to select and display data from two arrays (time-of-day and month). The first array contains 3 rows and the second contains 12. The program begins by displaying the array with three items. When I select the 'Month' button to display the second array, only the first three months are displayed instead of all 12.

If I select the 'Time-of-Day' button, the program crashes, indicating:

"*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** `-[__NSArrayI objectAtIndex:]: index 3 beyond bounds [0 .. 2]'"`.

I'm not sure why it would be read beyond the array size.

Could anybody help me understand what I need to do to:

  1. Correct my code to allow the picker view to display all 12 months when the 'Month' button is selected
  2. Prevent the program from crashing when 'Time-Of-Day' button is selected, after Month has been displayed.

Following is the code ...

Header

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate>

@property (strong, nonatomic) IBOutlet UITextField *todTxtField;
@property (strong, nonatomic) IBOutlet UITextField *monthTxtField;

@property (strong, nonatomic) NSArray *todArray;
@property (strong, nonatomic) NSArray *monthArray;

@property (strong, nonatomic) IBOutlet UIPickerView *thePickerView;

- (IBAction)monthBtnAction:(id)sender;
- (IBAction)todBtnAction:(id)sender;

@end

Implementation

#import "ViewController.h"

@interface ViewController () 
@end

@implementation ViewController

@synthesize todArray, monthArray;
@synthesize thePickerView;
@synthesize monthTxtField, todTxtField;

int buttonTouched = 0;

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.todArray = [[NSArray alloc] initWithObjects:@"AM", @"Mid-Day", @"PM", nil];
    self.monthArray =[[NSArray alloc] initWithObjects: @"January", @"February", @"March", @"April",  @"May",  @"June", @"July", @"August", @"September", @"October", @"November", @"December", nil];
}

- (void) viewDidAppear:(BOOL)animated
{
    //set default rows of UITextFields to an initial state
    monthTxtField.text = @"January";
    todTxtField.text = @"Mid-Day";
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark Button Actions

- (IBAction)todBtnAction:(id)sender
{
    buttonTouched = 0;
    [thePickerView reloadAllComponents];
}

- (IBAction)monthBtnAction:(id)sender
{
    buttonTouched = 1;
    [thePickerView reloadAllComponents];
}

#pragma mark Picker

// returns the number of components to be used
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
    return 1;
}


// returns the # of rows in the components
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:NSInteger)component
{
    switch (buttonTouched)
    {
        case 0:
            return [todArray count];
            break;
        default:
            return [monthArray count];
            break;
    }
}

//Display the row number from the array
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
    switch (buttonTouched)
    {
        case 0:
            return [todArray objectAtIndex:row];
            break;
        default:
            return [monthArray objectAtIndex:row];
            break;
     }
}

//Display the row content from the array
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
    switch (buttonTouched)
    {
        case 0:
           self.todTxtField.text = [todArray objectAtIndex:row];
           break;
        default:
           self.monthTxtField.text = [monthArray objectAtIndex:row];
           break;
     }
}

@end

Solution

  • If the currently selected row in component 0 is greater than 2 when you flip component 0 to represent an array with 3 members rather than an array with 12 members it'll crash for sure. So before you flip from the month array to the am/pm one you need to call

    [picker selectRow:0 inComponent:0 animated:YES]
    

    Otherwise the picker will be trying to select row 11 for December for example after it flips to having only three, range exception every time...

    Edit.. SUMMARY.. the UIPickerView has a variable you have not considered, the currently selected row in each component. Reloading the data does not alter this variable, you need to babysit it a little..