I had made a Custom UIPickerView with three Components each showing label on Selection Indicator which is working fine in iOS 6.
But I am getting a crash in iOS 7.
Here is my code:
ViewController.h
#define kDaysComponent 0
#define kHoursComponent 1
#define kMinutesComponent 2
#import <UIKit/UIKit.h>
#import "LabeledPickerView.h"
#interface ViewController : UIViewController<UIPickerViewDataSource,UIPickerViewDelegate,UITextFieldDelegate>
{
NSMutableArray *arrDays;
NSMutableArray *arrHours;
NSMutableArray *arrMinutes;
LabeledPickerView *timePicker;
IBOutlet UITextField *txtTimeLimit;
}
#property(nonatomic, retain) NSMutableArray *arrDays;
#property(nonatomic, retain) NSMutableArray *arrHours;
#property(nonatomic, retain) NSMutableArray *arrMinutes;
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize arrDays;
#synthesize arrHours;
#synthesize arrMinutes;
- (void)viewDidLoad
{
self.arrDays = [[NSMutableArray alloc]initWithCapacity:100];
for (NSInteger i = 0; i < 100; i++){
[arrDays addObject:[NSString stringWithFormat:#"%d",i]];
}
//self.arrDays = arrDays;
self.arrHours = [[NSMutableArray alloc]initWithCapacity:24];
for (NSInteger i = 0; i < 24; i++){
[arrHours addObject:[NSString stringWithFormat:#"%d",i]];
}
//self.arrHours = arrHours;
self.arrMinutes = [[NSMutableArray alloc]initWithCapacity:60];
for (NSInteger i = 0; i < 60; i++){
[arrMinutes addObject:[NSString stringWithFormat:#"%d",i]];
}
//self.arrMinutes = arrMinutes;
[self.navigationController setNavigationBarHidden:YES];
[super viewDidLoad];
}
-(IBAction)Click:(id)sender{
timePicker = [[LabeledPickerView alloc] init];
timePicker.showsSelectionIndicator = YES;
timePicker.dataSource = self;
timePicker.delegate = self;
[timePicker addLabel:#"Days" forComponent:kDaysComponent forLongestString:#"Hours"];
[timePicker addLabel:#"Hours" forComponent:kHoursComponent forLongestString:#"Hours"];
[timePicker addLabel:#"Mins" forComponent:kMinutesComponent forLongestString:#"Mins"];
UIToolbar* toolbar = [[UIToolbar alloc] init];
toolbar.barStyle = UIBarStyleBlackTranslucent;
[toolbar sizeToFit];
//to make the done button aligned to the right
UIBarButtonItem *flexibleSpaceLeft = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *flexibleSpaceRight = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem* doneButton = [[UIBarButtonItem alloc] initWithTitle:#"Done" style:UIBarButtonItemStyleDone target:self action:#selector(doneClicked:)];
UIBarButtonItem *unlimitedButton = [[UIBarButtonItem alloc] initWithTitle:#"Unlimited" style:UIBarButtonItemStyleBordered target:self action:#selector(unlimitedClicked:)];
[unlimitedButton setTintColor:[UIColor colorWithRed:100.0f/255.0f green:197.0f/255.0f blue:84.0f/255.0f alpha:1.0f]];
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithTitle:#"Cancel" style:UIBarButtonItemStyleDone target:self action:#selector(cancelClicked:)];
[toolbar setItems:[NSArray arrayWithObjects:cancelButton,flexibleSpaceLeft,unlimitedButton,flexibleSpaceRight,doneButton, nil]];
//custom input view
txtTimeLimit.inputView = timePicker;
txtTimeLimit.inputAccessoryView = toolbar;
}
-(void)doneClicked:(id) sender
{
[txtTimeLimit resignFirstResponder]; //hides the pickerView
NSInteger daysRow = [timePicker selectedRowInComponent: kDaysComponent];
NSInteger hoursRow = [timePicker selectedRowInComponent: kHoursComponent];
NSInteger minutesRow = [timePicker selectedRowInComponent: kMinutesComponent];
NSString *days = [arrDays objectAtIndex:daysRow];
NSString *hours = [arrHours objectAtIndex:hoursRow];
NSString *minutes = [arrMinutes objectAtIndex:minutesRow];
txtTimeLimit.text = [[NSString alloc] initWithFormat:
#"Expires in [%#] Days [%#] Hours [%#] Minutes.",days,hours,minutes];
if ([txtTimeLimit.text isEqualToString:#""])
{
}
else
{
}
}
-(void)cancelClicked:(id) sender
{
[txtTimeLimit resignFirstResponder]; //hides the pickerView
}
-(void)unlimitedClicked:(id) sender
{
[txtTimeLimit resignFirstResponder]; //hides the pickerView
txtTimeLimit.text = #"Select Time Limit";
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark Picker View Delegate
- (NSInteger)numberOfComponentsInPickerView:(LabeledPickerView *)pickerView
{
return 3;
}
- (NSInteger)pickerView:(LabeledPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
if (component == kDaysComponent)
{
return [self.arrDays count];
}
else if (component == kHoursComponent)
{
return [self.arrHours count];
}
else if (component == kMinutesComponent)
{
return [self.arrMinutes count];
}
return 0;
}
- (NSString *)pickerView:(LabeledPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
if (component == kDaysComponent)
{
NSLog(#"%#",[arrDays objectAtIndex:row]);
return [self.arrDays objectAtIndex:row];
}
else if (component == kHoursComponent)
{
NSLog(#"%#",[arrHours objectAtIndex:row]);
return [self.arrHours objectAtIndex:row];
}
else if (component == kMinutesComponent)
{
return [self.arrMinutes objectAtIndex:row];
}
return 0;
}
#pragma mark TEXT FIELD DELEGATE
- (BOOL)textFieldShouldBeginEditing:(UITextField *)aTextField
{
[self Click:aTextField];
return YES;
}
I am showing UIPickerView as an input view for UITextField and each time I am getting this error in iOS 7 only:
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 5 beyond bounds [0 .. 2]'
I don't know what's wrong with it. Can anyone please help me on this?
Your crash is telling you what the problem is:
-[__NSArrayM objectAtIndex:]: index 5 beyond bounds [0 .. 2]'
You are trying to get a value of object at the index 5 when your array only contains 3 objects.
Put a break point in and check the arrays contain what you expect and the component value is what you expect.
Please try to set txtTimeLimit.inputView after delays like one second.
[self performSelector:#selector(setInputView:) withObject:timePicker afterDelay:1];
- (void)setInputView:(LabeledPickerView*)picker {
txtTimeLimit.inputView = picker;
}
I think it's something doing with the animation....
You have to concern in few things,
First is the delegate and datasource method.
If u get the warning on delegate and datasource then correct the code like this
timePicker.dataSource = (id)self;
timePicker.delegate = (id)self;
Then Check the array. Hopefully it will solve your problem.
Related
the basic premise of this code is that it has two fields, a textfield that stores the height of the person in feet, and one that stores the height in inches. Thus, when someone clicks the feet textfield or the inches text field, a pickerview pops up that allows the user to pick the height. However, I'm getting the following error:
[__NSArrayI pickerView:numberOfRowsInComponent:]: unrecognized selector sent to instance
When I run the following code:
#import "GetUserStatistics.h"
#interface GetUserStatistics ()
#end
#implementation GetUserStatistics
#synthesize feetField, inchesField, pickerViewFeet, pickerViewInches, ftPicker, inPicker;
- (void)viewDidLoad {
[super viewDidLoad];
pickerViewFeet = [self createNumberPickerViewWithStartingValue:1 endingValue:8 defaultValue:5];
NSLog(#"%f", pickerViewFeet.frame.size.height);
feetField.inputView = pickerViewFeet;
NSLog(#"%f", feetField.inputView.frame.size.height);
//feetField.inputAccessoryView = [self createToolbar];
pickerViewInches = [self createNumberPickerViewWithStartingValue:0 endingValue:11 defaultValue:8];
NSLog(#"%f",pickerViewInches.frame.size.height);
inchesField.inputView = pickerViewInches;
NSLog(#"%f", inchesField.inputView.frame.size.height);
//NSLog(#"%#", pickerViewFeet.delegate);
//inchesField.inputAccessoryView = [self createToolbar];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL)disablesAutomaticKeyboardDismissal
{
return NO;
}
-(void) inputAccessoryViewDidFinish{
feetField.text = [[NSString alloc]initWithFormat:#"%i", [(UIPickerView *)feetField.inputView selectedRowInComponent:0]];
inchesField.text = [[NSString alloc]initWithFormat:#"%i", [(UIPickerView *)inchesField.inputView selectedRowInComponent:0]];
actualHeight = ([feetField.text intValue])*12 + [inchesField.text intValue];
NSLog(#"actual height:%i", actualHeight);
[feetField endEditing:YES];
[inchesField endEditing:YES];
}
-(UIPickerView *) createNumberPickerViewWithStartingValue: (int) startVal endingValue: (int) endingVal defaultValue: (int) defaultValue{
UIPickerView * tempPicker = [[UIPickerView alloc]initWithFrame:CGRectMake(0, 50, 100, 150)];
ftPicker = [[NumberPickerView alloc]initWithStartingValue:startVal endingVal:endingVal];
tempPicker.delegate = ftPicker;
[tempPicker selectRow:defaultValue inComponent:0 animated:NO];
return tempPicker;
}
-(UIToolbar * ) createToolbar{
UIToolbar *myToolbar = [[UIToolbar alloc] initWithFrame:
CGRectMake(0,0, 320, 44)]; //should code with variables to support view resizing
UIBarButtonItem *doneButton =
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self action:#selector(inputAccessoryViewDidFinish)];
//using default text field delegate method here, here you could call
//myTextField.resignFirstResponder to dismiss the views
[myToolbar setItems:[NSArray arrayWithObject: doneButton] animated:NO];
return myToolbar;
}
-(void) addPickerViewToTextField: (UITextField **) textField pickerViewToAdd : (UIPickerView **) pickerView{
NSLog(#"dading view");
*pickerView = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 50, 100, 150)];
}
#end
NumberPickerView code (implements the UIPickerViewDelegate protocol):
-(NumberPickerView *) initWithStartingValue: (int) startingVal endingVal: (int) endingVal
{
startingValue = startingVal;
endingValue = endingVal;
return self;
}
// 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{
NSLog(#"calling this function");
return endingValue - startingValue + 1;
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
return [[NSString alloc]initWithFormat:#"%i", (int)(row) + startingValue];
}
I suspect the issue is with the returning of the UIPickerView, specifically that the NumberPickerView is being destroyed after the function returns the view and so the pickerViewFeet no longer has a delegate. I'm not sure if this is the problem, and if it is how to fix this, can anyone help?
Thanks!
You call createNumberPickerViewWithStartingValue twice. The 2nd call results in ftPicker being reset to a new instance of NumberPickerView. This means the first instance assigned to the first picker view gets deallocated. And this results in the crash.
You need to reorganize your code so the same instance variable isn't being used to hold the two NumberPickerView instances.
You also need a newer tutorial. You shouldn't be calling #synthesize in most cases. And all of your references to the property instance variables should be changed to references to the actual property instead.
first use self.ftPicker instead of ftpicker and:
-(NumberPickerView *) initWithStartingValue: (int) startingVal endingVal:(int) endingVal {
self = [super init];
startingValue = startingVal;
endingValue = endingVal;
return self;
}
I have a UIPickerView with 2 components. Selecting 1st components populates 2nd component. Looks like this:
Test Case:
Select any option in Component 0 and then pick any episode in Component 1 of picker and click Fitler which hides the picker.
Show the Picker again to select something else. The first component loads properly. The second component is blank like this screenshot:
Here's the code:
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view{
UILabel* tView = (UILabel*)view;
if (!tView){
tView = [[UILabel alloc] init];
tView.textAlignment = NSTextAlignmentCenter;
// Setup label properties - frame, font, colors etc
//...
//adjustsFontSizeToFitWidth property to YES
if(pickerView == self.seasonEpiPickerView && component == 1){
tView.font= [UIFont systemFontOfSize:12];
tView.adjustsFontSizeToFitWidth = YES;
}
}
// Fill the label text here
if (pickerView == self.seasonEpiPickerView)
{
if (component == 0) {
if (row == 0) {
item = #"All";
} else {
//NSLog(#"c1:%ld", (long)row);
item = [NSString stringWithFormat:#"S%#", ((Season *)[self.media.seasonManager.seasonArray objectAtIndex:row-1]).seasonNr];
}
} else if (component == 1){
if(self.media.seasonManager && self.media.seasonManager.seasonArray && [self.media.seasonManager.seasonArray count]>0 ) {
Season* tempSeason = self.media.seasonManager.seasonArray[[self.seasonEpiPickerView selectedRowInComponent:0]-1];
item = [NSString stringWithFormat:#"%#.%# \"%#\"", tempSeason.seasonNr, [NSString stringWithFormat: #"%02d", [((Episode *)[tempSeason.episodeManager.episodeArray objectAtIndex:row]).episodeNr intValue]], ((Episode *)[tempSeason.episodeManager.episodeArray objectAtIndex:row]).title];
NSLog(#"c2:%ld:%#", (long)row, item);
}
}
}
tView.text = item;
return tView;
}
Edit:
Here's how I create the Picker in viewDidLoad
self.seasonPickerViewTextField = [[UITextField alloc] initWithFrame:CGRectZero];
[self.view addSubview:self.seasonPickerViewTextField];
self.seasonEpiPickerView = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
self.seasonEpiPickerView.showsSelectionIndicator = YES;
self.seasonEpiPickerView.dataSource = self;
self.seasonEpiPickerView.delegate = self;
//add the toolbar for the season picker
UIToolbar *toolBar= [[UIToolbar alloc] initWithFrame:CGRectMake(0,0,screenWidth,44)];
[toolBar setBarStyle:UIBarStyleBlack];
UIBarButtonItem *barButtonDone = [[UIBarButtonItem alloc] initWithTitle:#"Filter " style:UIBarButtonItemStylePlain target:self action:#selector(filterByEpisode:)];
UIBarButtonItem *flexible = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
UIBarButtonItem *barCancelButton = [[UIBarButtonItem alloc] initWithTitle:#" Cancel" style:UIBarButtonItemStylePlain target:self action:#selector(cancelPicker:)];
toolBar.items = [NSArray arrayWithObjects: barCancelButton, flexible, barButtonDone, nil];
self.seasonPickerViewTextField.inputAccessoryView = toolBar;
// set change the inputView (default is keyboard) to UIPickerView
self.seasonPickerViewTextField.inputView = self.seasonEpiPickerView;
EDIT 2
doing a delayed reload when the picker view is presented the second time reloads the second component correctly. This seems very hawkish.
- (IBAction)pickEpisodeButton:(id)sender
{
[self.seasonPickerViewTextField becomeFirstResponder];
/* None of the following commented lines of code have any effect*/
//[self.seasonEpiPickerView selectRow:1 inComponent:0 animated:NO];
//[self.seasonEpiPickerView selectRow:selectedSeason inComponent:0 animated:YES];
//[self.seasonEpiPickerView selectRow:selectedEpisode inComponent:1 animated:YES];
//[self.seasonEpiPickerView reloadComponent:1];
//set a 1.5 sec timer to go to the next screen
[NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:#selector(loadSecondComponent) userInfo:nil repeats:NO];
}
-(void)loadSecondComponent {
[self.seasonEpiPickerView reloadComponent:1];
}
I've got a very weird problem with my iOS project.
I have a UIViewController with some labels and buttons and when user enters this view I programmatically build a gameboard (it's the minesweeper game).
What I wanted to do now is add a simple element (in my case a segmented control but i tried also with a button and doesn't work) at the bottom or at the beginning of the board.
When I add something from the Interface Builder I can see that in the storyboard but when I run the app puff, it's disappeared!
View contains only the "old" elements and the new one is not shown.
Yes I made a IBOutlet:
#property (weak, nonatomic) IBOutlet UISegmentedControl *clickTypeSegmentedControl;
And yes I checked if references of segemented control are ok.
I can't really understand what is wrong, if someone can help I'll be very grateful.
** EDIT: I added code of the ViewController, there are some other methods I left out because they only handle the game. I repeat: even if I add a simple button with no actions, it will not be shown.
#import "GameViewController.h"
#import "GameBoardModel.h"
#import "ScoreViewController.h"
#import <AVFoundation/AVFoundation.h>
#define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
#interface GameViewController ()
#property (weak, nonatomic) IBOutlet UILabel *timerLabel;
#property (weak, nonatomic) IBOutlet UILabel *bombsLabel;
#property (weak, nonatomic) IBOutlet UIButton *restartButton;
#property(nonatomic, strong) AVAudioPlayer *soundEffects;
#property (weak, nonatomic) IBOutlet UISegmentedControl *clickTypeSegmentedControl;
#end
#implementation GameViewController
#synthesize difficulty, timer, playerName, scoreToAdd, clickTypeSegmentedControl;
double secondsPassed=-1.0f;
int seconds=0;
int totalRowsCols=0, totalMines=0, heightWidth=0, clicKNumber=0;
static NSString * const IMAGE_NAME_FLAG = #"flag.png";
static NSString * const IMAGE_NAME_BOMB_X = #"bomb.png";
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.hidesBackButton = YES;
UIBarButtonItem *backButton =[[UIBarButtonItem alloc]initWithTitle:#"Menù" style:UIBarButtonItemStyleBordered target:self action:#selector(popAlertAction:)];
self.navigationItem.leftBarButtonItem=backButton;
clickTypeSegmentedControl.selectedSegmentIndex = 0;
rootController = (BombsSeekerViewController *)[self.navigationController.viewControllers objectAtIndex:0];
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
playerName = [defaults objectForKey:#"NomeGiocatore"];
scoreToAdd = [[NSMutableDictionary alloc]init];
[self createGameBoard:difficulty];
[self newGame:nil];
}
-(void) newGame:(UIButton *)sender {
[self.view.subviews makeObjectsPerformSelector: #selector(removeFromSuperview)];
clicKNumber=0;
secondsPassed=0;
self.timerLabel.text = [self labelString:secondsPassed];
self.game = [[Game alloc] initWithWidth:totalRowsCols AndHeight:totalRowsCols AndMineCount:totalMines];
self.buttonArray = [[NSMutableArray alloc] init];
[self.restartButton addTarget:self action:#selector(newGame:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.restartButton];
[self.view addSubview:self.timerLabel];
[self.view addSubview:self.bombsLabel];
for(int i = 0; i < totalRowsCols; i++) {
for (int j = 0; j < totalRowsCols; j++) {
self.button = [[Tile alloc] initWithLocation: CGPointMake(j,i) andHW:heightWidth andBlock:self.game.board[j][i] andTag:(i*totalRowsCols+j)];
[self.buttonArray addObject:self.button];
[self.view addSubview:self.button];
[self.button addTarget:self action:#selector(click:) forControlEvents:UIControlEventTouchUpInside];
}
}
[self.timer invalidate];
}
- (void) createGameBoard:(int)diff{
switch (diff) {
case 1:
totalRowsCols = 6;
totalMines = 2;
heightWidth = 44;
break;
case 2:
totalRowsCols = 8;
totalMines = 16;
heightWidth = 35;
break;
case 3:
totalRowsCols = 10;
totalMines = 25;
heightWidth = 29;
break;
}
self.flagCount = totalMines;
_bombsLabel.text = [NSString stringWithFormat:#"%d",self.flagCount];
}
-(NSString *) labelString: (int) num {
if(num < 10) {
return [NSString stringWithFormat:#"00%i",num];
}
else if(self.timeCount.intValue < 100) {
return [NSString stringWithFormat:#"0%i",num];
}
else if(self.timeCount.intValue < 1000) {
return [NSString stringWithFormat:#"%i",num];
}
else
return #"---";
}
-(void) click:(Tile *)sender {
if(clicKNumber <1){
[self refreshLabel:self.timer];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(refreshLabel:)
userInfo:nil
repeats:YES];
clicKNumber++;
}
if(self.game.gameStatus == STATUS_LOST || self.game.gameStatus == STATUS_WON) return;
if (self.clickTypeSegmentedControl.selectedSegmentIndex == 0 && sender.block.marking == MARKING_BLANK) {
[self.game clickedAtColumn: sender.point.x AndRow: sender.point.y];
for(Tile *b in self.buttonArray) {
if(b.block.marking == MARKING_CLICKED) {
b.enabled = NO;
}
else if(b.block.marking == MARKING_BLANK) {
[b setTitle:#"" forState:UIControlStateNormal];
[b setImage:nil forState:UIControlStateNormal];
}
}
}
else if (self.clickTypeSegmentedControl.selectedSegmentIndex == 1){
if(sender.block.marking == MARKING_BLANK && self.flagCount > 0) {
sender.block.marking = MARKING_FLAGGED;
self.flagCount--;
_bombsLabel.text = [NSString stringWithFormat:#"%d",self.flagCount];
[sender setImage:[UIImage imageNamed:IMAGE_NAME_FLAG] forState:UIControlStateNormal];
}
else if(sender.block.marking == MARKING_FLAGGED) {
sender.block.marking = MARKING_BLANK;
self.flagCount++;
_bombsLabel.text = [NSString stringWithFormat:#"%d",self.flagCount];
[sender setImage:[UIImage imageNamed:#"tile.png"] forState:UIControlStateNormal];
}
}
if(self.game.gameStatus == STATUS_LOST) [self gameLost:sender];
else if(self.game.gameStatus == STATUS_WON) [self gameWon];
}
If this is what I think it is, you should not be doing this:
[self.view.subviews makeObjectsPerformSelector: #selector(removeFromSuperview)];
When the viewDidLoad is called, the newGame method is called, which removes al subviews from Superview. Including the ones added in Storyboard.
Make sure controller in storyboard is linked with the right controller class.
You call this method during -viewDidLoad:
-(void) newGame:(UIButton *)sender {
[self.view.subviews makeObjectsPerformSelector: #selector(removeFromSuperview)];
//...
}
Thus, anything you put in your nib is getting removed after it loads.
I'm using the following code to move to the next field using the UITextField delegate and also I'm adding a toolbar to the keyboard with the previous, next and ok buttons. The code is working fine.
Like you see the keyboard return button logic is pretty generic, using the UITextField tags, and that's good because I'm gonna use the piece of code all around. Now I will need to write the previous and next buttons logic, and I'm lost. Any ideas?
UPDATE (complete code, with some modifications, thanks to #8vius that spent some time with me in the chat to make it work):
//
// SigninViewController.m
//
#import "SigninViewController.h"
#implementation SigninViewController
#synthesize firstResponder = _firstResponder;
#synthesize toolbar;
#synthesize email;
#synthesize password;
- (void)move:(UIBarButtonItem*)sender {
NSInteger tag = self.firstResponder.tag;
if ([sender.title isEqualToString:#"Anterior"]) {
tag -= 1;
} else if ([sender.title isEqualToString:#"Próximo"]) {
tag += 1;
}
UITextField *nextTextField = (UITextField*)[self.view viewWithTag:tag];
if (nextTextField && tag > 0) {
[nextTextField becomeFirstResponder];
} else {
[self.firstResponder resignFirstResponder];
self.firstResponder = nil;
}
}
- (void)ok:(id)sender {
[self.view endEditing:YES];
self.firstResponder = nil;
}
- (void)textFieldDidBeginEditing:(UITextField*)textField {
self.firstResponder = textField;
}
- (BOOL)textFieldShouldReturn:(UITextField*)textField {
NSInteger tag = textField.tag + 1;
UITextField *nextTextField = (UITextField*)[self.view viewWithTag:tag];
if (nextTextField) {
[nextTextField becomeFirstResponder];
} else {
[textField resignFirstResponder];
self.firstResponder = nil;
}
return NO;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.email.delegate = self;
self.password.delegate = self;
if (self.toolbar == nil)
{
self.toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 44)];
UIBarButtonItem* previous = [[UIBarButtonItem alloc] initWithTitle:#"Anterior" style:UIBarButtonItemStyleBordered target:self action:#selector(move:)];
UIBarButtonItem* next = [[UIBarButtonItem alloc] initWithTitle:#"Próximo" style:UIBarButtonItemStyleBordered target:self action:#selector(move:)];
UIBarButtonItem* space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:(UIBarButtonSystemItemFlexibleSpace) target:nil action:nil];
UIBarButtonItem* ok = [[UIBarButtonItem alloc] initWithTitle:#"Ok" style:UIBarButtonItemStyleBordered target:self action:#selector(ok:)];
[self.toolbar setItems:[[NSArray alloc] initWithObjects:previous, next, space, ok, nil]];
[self.toolbar setTranslucent:YES];
[self.toolbar setTintColor:[UIColor blackColor]];
}
for (UIView* view in self.view.subviews) {
if ([view isKindOfClass:[UITextField class]]) {
[(UITextField*)view setInputAccessoryView:toolbar];
}
}
}
- (void)viewDidUnload {
self.email = nil;
self.password = nil;
[super viewDidUnload];
}
#end
It's quite simple, when you load your view you set the tag property on your text fields depending on the order you want them in, then you have to just traverse the tag element on the fields:
- (void)toggleTextfield:(UIBarButtonItem *)sender {
NSInteger nextTag = self.firstResponder.tag;
if ([sender.title isEqualToString:#"Previous"] && nextTag > 1) {
nextTag -= 1
} else if ([sender.title isEqualToString:#"Next"]) {
nextTag += 1;
}
UITextField *nextTextField = (UITextField *)[self.view viewWithTag:nextTag];
if (nextTextField) {
[nextTextField becomeFirstResponder];
}
}
And keep track of who is the first responder:
-(void)textFieldDidBeginEditing:(UITextField *)textField {
self.firstResponder = textField;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
self.firstResponder = nil;
return YES;
}
And when you load your view, you bind the buttons to the toggle action:
UIBarButtonItem *previousButton = [[UIBarButtonItem alloc] initWithTitle:#"Previous"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(toggleTextfield:)];
UIBarButtonItem *nextButton = [[UIBarButtonItem alloc] initWithTitle:#"Next"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(toggleTextfield:)];
In my case, for instance, I set up my text fields inside a table view, so in my cellForRowAtIndexPath method I set the tag property to be the row of the indexPath.
EDIT: You have to set the firstResponder property for it to work.
In your .h file:
#property UIView *firstResponder
In your .m file:
#synthesize firstResponder = _firstResponder;
I started learning iOS programming about two months ago and so far I love it, but I'm kinda struggling a bit with UIPicker(s) and UIDatePickers in the app that I'm making, and I was hoping that someone might point me in the right direction. The app is supposed to mimic already existing PHP web app, that is nothing more than a form with a bunch of dropdowns.
Lets say that i have GetAddressViewController that will have a bunch of input fields. For the sake of simplicity lets say that I will have only 3 input fields for: country, city and street.
The user should tap on "country" input field and the UIPicker shows up much like a keyboard would and after selecting the country, a web app will return a JSON array with all the cities of that country. The same process is repeated when user taps on "city" input field, the UIPicker pops up with a list of returned cities, user selects a street, the UIPicker slides out and web app returns an array of streets etc.
Lets say that in my In my GetAddressViewController.h file i have:
#import <UIKit/UIKit.h>
#interface GetAddressViewController : UIViewController
{
UITextField *countryField;
UITextField *cityField;
UITextField *streetField;
NSArray *countries;
NSArray *cities;
NSArray *streets;
}
#property(nonatomic, strong) IBOutlet UITextField *countryField;
#property(nonatomic, strong) IBOutlet UITextField *cityField;
#property(nonatomic, strong) IBOutlet UITextField *streetField;
#property(nonatomic, strong) IBOutlet NSArray *countries;
#property(nonatomic, strong) IBOutlet NSArray *cities;
#property(nonatomic, strong) IBOutlet NSArray *streets;
#end
In GetAddressViewController.m file i have synthesized properties and in the storyboard i only have 3 input fields that have been connected to appropriate outlets.
Is there some fundamental mistake in my existing code that i should be aware of?
Now, I feel that I have missed something while reading the tutorials regarding picker views, since most of the examples that I've found on StackOverflow don't make much sense to me.
Could someone point me to a basic similar example that could help me. Are UIPickers the way to go or is there a better approach?
I haven't felt this helpless for a while and any help would be greatly appreciated, thanks
EDIT:
Ok I'm making progress I hope that I can help someone else who has the same problem.
To make this work and to connect pickerview to input fileds inputView property you need to do this.
To your .h file you need to add two delegates: UIPickerViewDelegate and UIPickerViewDataSource
#interface ViewController : UIViewController<UIPickerViewDelegate, UIPickerViewDataSource>{ ...
Then in your .m file you'll need to have something like this. I have this code in my wiewDidLoad method.
UIPickerView *cityPicker = [[UIPickerView alloc] initWithFrame:CGRectZero];
cityPicker.delegate = self;
cityPicker.dataSource = self;
[cityPicker setShowsSelectionIndicator:YES];
cityField.inputView = cityPicker;
This simply means that cityPicker pickerview will appear when you tap on the cityField.After that you need to add the following pickerview delegate methods that will fill pickerview with correct data. The data in my case is an array that holds a list of cities.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return cities.count;
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return [cities objectAtIndex:row];
}
- (void) pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
cityField.text = (NSString *)[cities objectAtIndex:row];
}
If you want to have "Done" that will hide the UIPicker you'll need this code:
UIToolbar* mypickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 56)];
mypickerToolbar.barStyle = UIBarStyleBlackOpaque;
[mypickerToolbar sizeToFit];
NSMutableArray *barItems = [[NSMutableArray alloc] init];
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
[barItems addObject:flexSpace];
UIBarButtonItem *doneBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(pickerDoneClicked)];
[barItems addObject:doneBtn];
[mypickerToolbar setItems:barItems animated:YES];
cityField.inputAccessoryView = mypickerToolbar;
And add a new method pickerDoneClicked
-(void)pickerDoneClicked
{
NSLog(#"Done Clicked");
[cityField resignFirstResponder];
}
I hope that this may help someone.
Hi Please follow this whole solution of multiple pickerview or just use -(void)createActionsheet it may solve your problem
define in .h
UIActionSheet *actionSheet;
NSString *pickerType;
BOOL select
define in .m
-(IBAction)countryBtnPressed:(id)sender
{
[self createActionSheet];
pickerType = #"picker";
select = NO;
UIPickerView *chPicker = [[UIPickerView alloc] initWithFrame:CGRectMake(0.0, 44.0, 0.0, 0.0)];
chPicker.dataSource = self;
chPicker.delegate = self;
chPicker.showsSelectionIndicator = YES;
[actionSheet addSubview:chPicker];
Txt.text = [array objectAtIndex:0];
[chPicker release];
}
-(IBAction)stateBtnPressed:(id)sender
{
[self createActionSheet];
pickerType = #"statepicker";
select = NO;
UIPickerView *chPicker = [[UIPickerView alloc] initWithFrame:CGRectMake(0.0, 44.0, 0.0, 0.0)];
chPicker.dataSource = self;
chPicker.delegate = self;
chPicker.showsSelectionIndicator = YES;
[actionSheet addSubview:chPicker];
stateTxt.text = [stateArray objectAtIndex:0];
[chPicker release];
}
/// Create Action Sheet
- (void)createActionSheet {
if (actionSheet == nil) {
// setup actionsheet to contain the UIPicker
actionSheet = [[UIActionSheet alloc] initWithTitle:#"Select"
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
UIToolbar *pickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
pickerToolbar.barStyle = UIBarStyleBlackOpaque;
[pickerToolbar sizeToFit];
NSMutableArray *barItems = [[NSMutableArray alloc] init];
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
[barItems addObject:flexSpace];
[flexSpace release];
UIBarButtonItem *doneBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(pickerDone:)];
[barItems addObject:doneBtn];
[doneBtn release];
[pickerToolbar setItems:barItems animated:YES];
[barItems release];
[actionSheet addSubview:pickerToolbar];
[pickerToolbar release];
[actionSheet showInView:self.view];
[actionSheet setBounds:CGRectMake(0,0,320, 464)];
}
}
#pragma mark UIPickerViewDelegate Methods
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
int count;
if ([pickerType isEqualToString:#"picker"])
count = [array count];
else if([pickerType isEqualToString:#"picker"])
count = [statearray count];
return count;
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
NSString *string;
if ([pickerType isEqualToString:#"picker"])
string = [array objectAtIndex:row];
if ([pickerType isEqualToString:#"statepicker"])
string = [statearray objectAtIndex:row];
return string;
}
// Set the width of the component inside the picker
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component {
return 300;
}
// Item picked
- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
select = YES;
if ([pickerType isEqualToString:#"picker"])
{
Txt.text = [array objectAtIndex:row];
}
if ([pickerType isEqualToString:#"statepicker"])
{
stateTxt.text = [statearray objectAtIndex:row];
}
}
- (void)pickerDone:(id)sender
{
if(select == NO)
{
if ([pickerType isEqualToString:#"picker"])
{
Txt.text = [array objectAtIndex:0];
}
else if ([pickerType isEqualToString:#"statepicker"])
{
stateTxt.text = [statearray objectAtIndex:0];
}
}
[actionSheet dismissWithClickedButtonIndex:0 animated:YES];
[actionSheet release];
actionSheet = nil;
}
}
UIPickers are OK to use, but in this particular case, where the list of entries can be a very long list and also the entries can some times may be more than a simple string, for example, if the country name is "Republic of Zimbabwe, Africa" or some thing like that UIPicker controllers cannot display the data in the best possible way. You can use table views and present them modally. Tableviews give you a much better way to do a selection if the list of entries are more than a hundred or so.