I've been working on a small app that contains only two elements, a UILabel and a UIPickerView.
The UILabel is updated by an NSTimer to count down from 60 to 0.
The UIPickerView only contains an array of data, don't interact directly with either UILabel with NSTimer.
My problem is that when I play with the UIPickerView and I scroll up or down, the counter "animation" stops, continues to make its action until you select an option from the UIPickerView.
I could not fix this, I do not know if it's a bug with the iPhone SDK or is there any property as becomeFirstResponder that can help me.
I'm using iOS SDK 6.1.
This is my code:
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UIPickerViewDataSource,UIPickerViewDelegate>{
IBOutlet UIPickerView *uno;
NSMutableArray *numeros;
NSTimer *timer;
IBOutlet UILabel *contador;
int num;
}
#property (nonatomic, retain) IBOutlet UIPickerView *uno;
#property (nonatomic, retain) NSMutableArray *numeros;
#property (nonatomic, retain) IBOutlet UILabel *contador;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize uno, numeros, contador;
- (void)viewDidLoad
{
[super viewDidLoad];
numeros = [[NSMutableArray alloc] init];
[numeros addObject:#"0"];
[numeros addObject:#"1"];
[numeros addObject:#"2"];
[numeros addObject:#"3"];
[numeros addObject:#"4"];
[numeros addObject:#"5"];
[numeros addObject:#"6"];
[numeros addObject:#"7"];
[numeros addObject:#"8"];
[numeros addObject:#"9"];
uno.delegate = self;
uno.dataSource = self;
num = 60;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateCounter:) userInfo:nil repeats:YES];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)thePickerView {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)thePickerView numberOfRowsInComponent:(NSInteger)component {
return [numeros count];
}
- (NSString *)pickerView:(UIPickerView *)thePickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return [numeros objectAtIndex:row];
}
- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
NSLog(#"Hi");
}
- (void)updateCounter:(NSTimer *)theTimer {
num = num - 1;
[contador setText:[NSString stringWithFormat:#"%d",num]];
}
#end
The app looks like this: http://imgur.com/ljB93tN
add NSRunLoop after
timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateCounter:) userInfo:nil repeats:YES];
NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer:timer forMode: NSDefaultRunLoopMode];
Related
I want to set multiple running timer value in tableview so user easily checked how many time is remaining and stop any specific timer.
Multiple Timer Start and updated value display in NSLog perfectly.
User set NSTimer for particular recipe.
Now suppose user set timer for recipe1 in recipedetail and now back from that recipedetail.
Then again set timer for recipe2 in recipedetail and back from that recipedetail.
and i want to All timer value display in UITableView. And tableview placed on recipedetail screen.
So my point is TimerUpdated value display in NSLog but not display in tableview because every Recipedetails screen generate new object of UITableView So value is not updated perfactly
-(IBAction)okBtn:(id)sender
{
[self countdownTimer];
}
-(void)countdownTimer {
[[NSUserDefaults standardUserDefaults]setObject:recipeboxId forKey:[NSString stringWithFormat:#"timer_%#",recipeboxId]];
[[NSUserDefaults standardUserDefaults]synchronize];
// set timer
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
(theAppDelegate).timer = [NSTimer scheduledTimerWithTimeInterval:0.6f
target:self
selector:#selector(updateCounter:)
userInfo:recipeboxId
repeats:YES];
[(theAppDelegate).timer fire];
// [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];//Timer run in background
[self CreateLocalNotification];
}
-(void)calculateTimeFromPicker
{
NSString *hoursStr = [NSString stringWithFormat:#"%#",[hoursArray objectAtIndex:[self.picker_timer selectedRowInComponent:0]]];
NSString *minsStr = [NSString stringWithFormat:#"%#",[minsArray objectAtIndex:[self.picker_timer selectedRowInComponent:1]]];
NSString *secsStr = [NSString stringWithFormat:#"%#",[secsArray objectAtIndex:[self.picker_timer selectedRowInComponent:2]]];
int hoursInt = [hoursStr intValue];
int minsInt = [minsStr intValue];
int secsInt = [secsStr intValue];
interval = secsInt + (minsInt*60) + (hoursInt*3600);
secondsLeft=interval;
}
- (void)updateCounter:(NSTimer *)theTimer {
if(secondsLeft > 0 )
{
secondsLeft -- ;
hours = secondsLeft / 3600;
minutes = (secondsLeft % 3600) / 60;
seconds = (secondsLeft %3600) % 60;
[[NSUserDefaults standardUserDefaults]setInteger:secondsLeft forKey:[NSString stringWithFormat:#"secondsLeft_%#",recipeboxId]];
[[NSUserDefaults standardUserDefaults]synchronize];
NSLog(#"timer :%#",[NSString stringWithFormat:#"%02d:%02d:%02d", hours, minutes, seconds]);
NSString *timer_updated_value=[NSString stringWithFormat:#"%02d:%02d:%02d", hours, minutes, seconds];
[[NSUserDefaults standardUserDefaults]setObject:timer_updated_value forKey:[NSString stringWithFormat:#"updated_timer_%#",[(theAppDelegate).timer userInfo]]];
[[NSUserDefaults standardUserDefaults]synchronize];
recipeArr = [[(theAppDelegate).Timer_recipeIdArr reverseObjectEnumerator] allObjects];
for (int section = 0; section < [recipeArr count]; section++)
for (int section = 0; section < [recipeArr count]; section++)
{
for (int row = 0; row < (int)[self.timerWindowTbl numberOfRowsInSection:section]; row++)
{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
UITableViewCell *cell = [self.timerWindowTbl cellForRowAtIndexPath:indexPath];
for(UILabel *lbl in [cell.contentView subviews])
{
if([lbl isKindOfClass:[UILabel class]])
{
if(lbl.tag == 1)
{
NSString *timer_desc= [[NSUserDefaults standardUserDefaults]objectForKey:[NSString stringWithFormat:#"timerDescString_%#",recipeArr[indexPath.row]]];
NSString *recipe_title=[[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithFormat:#"timerRecipeTitle_%#",recipeArr[indexPath.row]]];
NSString *updated_timer=[[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithFormat:#"updated_timer_%#",recipeArr[indexPath.row]]];
NSString *desc=[NSString stringWithFormat:#"%#\n'%#\' %# %#.",recipe_title,timer_desc,NSLocalizedString(#"is done in",nil),updated_timer];
lbl.text=updated_timer;
[lbl setNeedsDisplay];
NSLog(#"*******************************lbl.text:%#",lbl.text);
lbl.numberOfLines=0;
[lbl sizeToFit];
for(UIButton *btn in [cell.contentView subviews])
{
if([btn isKindOfClass:[UIButton class]])
{
if(btn.tag==2)
{
btn.titleLabel.text=[NSString stringWithFormat:#"%#",recipeArr[indexPath.row]];
}
}
}
break;
}
}
}
}
}
}
else
{
secondsLeft = hours = minutes = seconds = 0;
[self TimerInvalidate];
NSLog(#"Time out :");
}
}
i think u need to refractor your code, u need reload the tableview cell each time when recipe timer stared for particular recipe
this is sample demo u can try with separate project,
first create a Recipe class subclass of NSObject like below,
in Recipe .h file
#import <Foundation/Foundation.h>
#class Recipe;
#protocol RecipeDelegate <NSObject> //we need to notify which recipe time changing
- (void)timerChangedInRecipe:(Recipe *)recipe;
#end
#interface Recipe : NSObject
#property (nonatomic, assign) NSInteger recipeboxId;
#property (nonatomic, strong) NSString *recipeName;
#property (nonatomic, strong) NSString *countTimer;
#property (nonatomic, assign) NSTimeInterval secondsLeft;
#property (nonatomic, assign) NSTimeInterval interval;
#property (nonatomic, assign) int hours;
#property (nonatomic, assign) int minutes;
#property (nonatomic, assign) int seconds;
#property (nonatomic, strong) NSTimer *timer;
#property (nonatomic, assign) id<RecipeDelegate>delegate;
- (id)initWithRecipieId:(NSInteger)recipeId recipeName:(NSString *)name;
- (void )currentTimerString;
- (void)startTimer; //use to start the timer
- (void)stopTimer; //stop the timer
#end
in Recipe.m file
#import "Recipe.h"
#implementation Recipe
- (id)init
{
self = [super init];
if(self)
{
}
return self;
}
- (id)initWithRecipieId:(NSInteger)recipeId recipeName:(NSString *)name
{
self = [super init];
if(self)
{
self.recipeboxId = recipeId;
self.recipeName = name;
}
return self;
}
- (void)currentTimerString //this is similar to your method updateCounter:
{
self.secondsLeft -- ;
if(self.secondsLeft > 0 )
{
_hours = (int)self.secondsLeft / 3600;
_minutes = ((int)self.secondsLeft % 3600) / 60;
_seconds = ((int)self.secondsLeft %3600) % 60;
self.countTimer = [NSString stringWithFormat:#"%02d:%02d:%02d", self.hours, self.minutes, self.seconds];
if([self.delegate respondsToSelector:#selector(timerChangedInRecipe:)])
[self.delegate timerChangedInRecipe:self]; //notifying the tableview to reload the particular cell
}
else
{
_hours = _minutes = _seconds = 0;
[self stopTimer]; //timer finished stop the timer
}
}
- (void)startTimer
{
if(_timer == nil)
_timer = [NSTimer scheduledTimerWithTimeInterval:0.6f target:self selector:#selector(currentTimerString) userInfo:nil repeats:YES];
else
[_timer fire];
}
- (void)stopTimer
{
if(_timer)
[self.timer invalidate];
self.timer = nil;
}
#end
we are created the recipe object, this will handle the timer and notification to tableview, starting the timer and stoping the timer you can add some other functionalities if u want to.. :)
in ViewController.h file
#import <UIKit/UIKit.h>
#import "Recipe.h" //import the recipe class
#interface ViewController : UIViewController <UITableViewDataSource,UITableViewDelegate,RecipeDelegate>
#property (weak, nonatomic) IBOutlet UITableView *timerWindowTbl;
#property (nonatomic, strong) NSMutableArray *recipeArr; //array to hold the recipes
#end
in ViewController.m file
#import "ViewController.h"
#import "RecipeDetailViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_recipeArr = [[NSMutableArray alloc] init];
[self populateRecipe]; //i am simply creating the recipes hear for testing
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)populateRecipe
{
for(NSInteger k = 0 ; k < 10 ; k++)
{
Recipe *recipe = [[Recipe alloc] initWithRecipieId:k recipe eName:[NSString stringWithFormat:#"recipe_%ld",(long)k]];
[self.recipeArr addObject:recipe];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//just configure your tableview
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.recipeArr.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"RECIPEE_CELL"];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"RECIPEE_CELL"];
}
Recipe *recipe = self.recipeArr[indexPath.row];
recipe.delegate = self;
cell.textLabel.text = recipe.recipeName;
cell.detailTextLabel.text = recipe.countTimer;
return cell;
}
//hear go to recipe detail controller and set timer
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Recipe *selRecipe = self.recipeArr[indexPath.row];
RecipeDetailViewController *recipeDetailController = [[RecipeDetailViewController alloc] initWithNibName:#"RecipeDetailViewController" bundle:nil];
recipeDetailController.selectedRecipe = selRecipe;
[self.navigationController pushViewController:recipeDetailController animated:YES];
}
//this is the delegate method from recipe class
//this method is responsible for reloading the particular cell
- (void)timerChangedInRecipe:(Recipe *)recipe
{
NSInteger index = recipe.recipeboxId;
NSIndexPath *rowPath = [NSIndexPath indexPathForRow:index inSection:0];
[self.timerWindowTbl reloadRowsAtIndexPaths:#[rowPath] withRowAnimation:UITableViewRowAnimationNone];
}
#end
and in detail controller same as your recipe detail controller RecipeDetailViewController.h file
#import <UIKit/UIKit.h>
#import "Recipe.h"
#interface RecipeDetailViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIDatePicker *timePicker;
#property (weak, nonatomic) IBOutlet UIButton *okButton;
#property (weak, nonatomic) IBOutlet UILabel *timerLabel;
#property (nonatomic, strong) Recipe *selectedRecipe;
- (IBAction)okBtn:(id)sender;
#end
and in RecipeDetailViewController .m file,
#import "RecipeDetailViewController.h"
#interface RecipeDetailViewController ()
#end
#implementation RecipeDetailViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[_timePicker addTarget:self action:#selector(timerChanged:) forControlEvents:UIControlEventValueChanged]; //i am setting a sample picker
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)timerChanged:(UIDatePicker *)datePickerView
{
NSTimeInterval duration = datePickerView.countDownDuration;
int hours = (int)(duration/3600.0f);
int minutes = ((int)duration - (hours * 3600))/60;
int seconds = minutes/60;
_selectedRecipe.interval = seconds + (minutes*60) + (hours*3600);
_selectedRecipe.secondsLeft =_selectedRecipe.interval;
_timerLabel.text = [NSString stringWithFormat:#"%02d:%02d:%02d", hours, minutes, seconds];
_selectedRecipe.interval = duration;
}
- (IBAction)okBtn:(id)sender //hear start the timer particular recipe
{
[_selectedRecipe startTimer];
}
#end
Edit in the source code u are shared
the problem in the cell type i think, if you want u can subclass the tableview cell and place the timer labels in the required position,
so in your code, ViewController.m file replace below method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"RECIPEE_CELL"];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"RECIPEE_CELL"];
}
Recipe *recipe = self.recipeArr[indexPath.row];
recipe.delegate = self;
cell.textLabel.text = recipe.recipeName;
cell.detailTextLabel.text = recipe.countTimer;
NSLog(#"timer in cell :%#",cell.detailTextLabel.text);
return cell;
}
and in the RecipeDetailViewController.m file replace,
//no need to wait for value change
- (IBAction)okBtn:(id)sender //hear start the timer particular recipe
{
NSTimeInterval duration = _timePicker.countDownDuration;
_selectedRecipe.interval = duration;
[_selectedRecipe startTimer];
}
and also change the timePicker mode to Count Down Timer in attribute inspector
u can download the edited code hear
Put UI related changes in main queue for better understanding show given below code. You can also put other UI related changes in Main_queue
dispatch_async(dispatch_get_main_queue(), ^{
lbl.text=updated_timer;
});
I have a UIPickerView with an array in one view controller and an NSTimer in another, i am trying to:
1) link the picker array to NSTimer so a user selects a time on the picker for image to self destruct
2) show time remaining in UILabel so seconds the user selects going down to 0. (ex. select 2 from picker label shows 2 then 1 then 0 and image destucts)
one vc.h
#property (strong, nonatomic) IBOutlet UIPickerView *timePicker;
#property (nonatomic, strong) NSArray *pickerData;
one vc.m
- (void)viewDidLoad {
[super viewDidLoad];
self.pickerData = #[#1,#2,#3,#4,#5,#6,#7];
self.timePicker.dataSource = self;
self.timePicker.delegate = self;
}
- (void) viewWillAppear:(BOOL)animated {
[self.textField setText:self.userText];
}
-(long)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
-(long)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return self.pickerData.count;
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
[self.pickerData objectAtIndex:[self.timePicker selectedRowInComponent:0]];
}
two vc.h
#property (weak, nonatomic) IBOutlet UILabel *timeLabel;
#property (nonatomic, assign) int seconds;
two vc.m
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[NSTimer scheduledTimerWithTimeInterval:7 target:self selector:#selector(timeout)
userInfo:nil repeats:NO];
[NSTimer scheduledTimerWithTimeInterval:self.seconds target:self selector:#selector(setTimeToLabel) userInfo:nil repeats:NO];
}
-(void)timeout
{
// pops to root view
}
- (void)setTimeToLabel
{
self.seconds = self.seconds - 1;
self.timeLabel.text = [NSString stringWithFormat:#"%d", self.seconds];
}
the NSTimer does not respond to the self.seconds and does not pick up the user input from the UIPicker.
any ideas as to how I can link the picker to the timer so that the timer responds to the seconds the user selects in the picker and display the remaining seconds in the label??
my prepareforsegue method below:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.destinationViewController isKindOfClass:[oneViewController class]]) {
oneViewController * ovc = (oneViewController*)segue.destinationViewController;
ovc.array = //whatever;
}
if ([segue.destinationViewController isEqual:#"secondVC"]) {
NSInteger row = [self.timePicker selectedRowInComponent:0];
twoViewController *tvc = [segue destinationViewController];
tvc.seconds = [[self.pickerData objectAtIndex:row] intValue];
}
}
what can be the issue with it, the onviewcontroller method works, whatever i pass it works, but for the tvc.seconds nothing is passed to it.
You are creating a new instance of the controller rather than the actual one that would be used. You should be using the prepareForSegue method to do this, and get a reference to the destinationViewController, like so:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqual: #"nextScreen"]) {
NSInteger row = [self.timePicker selectedRowInComponent:0];
SecondViewController *tvc = [segue destinationViewController];
tvc.seconds = [[self.pickerData objectAtIndex:row] intValue];
}
}
// First View Controller
//.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UITextField *textField;
#property (weak, nonatomic) IBOutlet UIPickerView *timePicker;
#property (nonatomic, strong) NSArray *pickerData;
#property (weak, nonatomic) IBOutlet UIButton *next;
#end
//.m
#import "ViewController.h"
#import "SecondViewController.h"
#interface ViewController () <UIPickerViewDataSource, UIPickerViewDelegate>
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.pickerData = #[#1,#2,#3,#4,#5,#6,#7];
self.timePicker.dataSource = self;
self.timePicker.delegate = self;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return self.pickerData.count;
}
- (NSString*)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return [NSString stringWithFormat: #"%#", self.pickerData[row]];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
[self.pickerData objectAtIndex:[self.timePicker selectedRowInComponent:0]];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqual: #"nextScreen"]) {
NSInteger row = [self.timePicker selectedRowInComponent:0];
SecondViewController *tvc = [segue destinationViewController];
tvc.seconds = [[self.pickerData objectAtIndex:row] intValue];
}
}
#end
// SecondViewController
//.h
#import "ViewController.h"
#interface SecondViewController : ViewController
#property (nonatomic) int seconds;
#property (weak, nonatomic) IBOutlet UILabel *timeLabel;
#end
//.m
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[NSTimer scheduledTimerWithTimeInterval:7 target:self selector:#selector(timeout)
userInfo:nil repeats:NO];
[NSTimer scheduledTimerWithTimeInterval:self.seconds target:self selector:#selector(setTimeToLabel) userInfo:nil repeats:NO];
}
-(void)timeout
{
// pops to root view
}
- (void)setTimeToLabel
{
self.seconds = self.seconds - 1;
self.timeLabel.text = [NSString stringWithFormat:#"%d", self.seconds];
}
#end
So I've followed everything this post Default Picker Value iphone says, but I can't seem to get my picker to work.
Here's my .m snippet:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.arrPercent = #[#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17",#"18",#"19",#"20",#"21",#"22",#"23",#"24",#"25",#"26",#"27",#"28",#"29",#"30",#"31",#"32",#"33",#"34",#"35",#"36",#"37",#"38",#"39",#"40",#"41",#"42",#"43",#"44",#"45",#"46",#"47",#"48",#"49",#"50"];
self.arrPeople = #[#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17",#"18",#"19",#"20"];
self.percent = [NSNumber numberWithInt:15];
self.people = [NSNumber numberWithInt:5];
self.strSubTotal = #"";
self.myPicker.dataSource = self;
self.myPicker.delegate = self;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
//[self.myPicker selectRow:self.percent.integerValue inComponent:0 animated:YES];
[self.myPicker selectRow:5 inComponent:0 animated:YES];
[self.myPicker selectRow:5 inComponent:1 animated:YES];
[self.myPicker reloadAllComponents]; // tried commenting out this line with no luck
}
Here's my .h snippet:
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#interface ViewController : UIViewController <UIPickerViewDataSource,UIPickerViewDelegate>
#property (nonatomic,strong) NSArray *arrPercent;
#property (nonatomic,strong) NSArray *arrPeople;
#property (strong, nonatomic) IBOutlet UIPickerView *myPicker;
#property (strong, nonatomic) NSString *strSubTotal;
#property (strong, nonatomic) NSNumber *percent;
#property (strong, nonatomic) NSNumber *people;
#end
Here's the delegate stuff, I think...
// returns the number of 'columns' to display.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 2;
}
// returns the # of rows in each component..
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent: (NSInteger)component
{
if(component== 0)
{
return [self.arrPercent count];
}
else
{
return [self.arrPeople count];
}
}
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
if(component == 0)
{
return [NSString stringWithFormat:#"%#%#", [self.arrPercent objectAtIndex:row], #"%"];
}
else
{
return [self.arrPeople objectAtIndex:row];
}
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
if(component == 0)
{
self.percent = [self.arrPercent objectAtIndex:row];
}
else
{
self.people = [self.arrPeople objectAtIndex:row];
}
[self updateSubTotal:-3];
}
When I start the emulator, the picker doesn't animate to the position I tell it to. Am I doing anything wrong? Please help!
As it turns out, the error was that for some reason, the connection between the picker in the UI was disconnected with the code. I had to hold the Control key while dragging the picker from the UI to the .h file.
I saw this because I noticed a little "empty button icon" next to the method declaration in the .h file. Once Xcode understood that the picker is controlled by the piece of code, everything works as expected.
Thank you so much!
I get the exc_bad_access error when running my project and trying to change the picker.
The error is occurring on
- (NSString*)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component
Below is my code. From reading the other SO articles I realize I am probably not retaining my variable. I'm new and learning and appreciate the help.
#import <UIKit/UIKit.h>
#import "RootViewController.h"
#class RootViewController;
#interface AddConditionViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate> {
IBOutlet UITextField *txtConditionDetail;
IBOutlet UITextField *txtConditionArea;
IBOutlet UIPickerView *conditionNamesPicker;
NSMutableArray *names;
NSMutableArray *conditionDefs;
RootViewController *rvc;
NSString *conditionName;
}
#property (retain, nonatomic) IBOutlet UIPickerView *conditionNamesPicker;
#property (nonatomic,assign) RootViewController *rvc;
#property (nonatomic, retain) NSString *conditionName;
#property (nonatomic,assign) NSMutableArray *names;
#property (nonatomic,assign) NSMutableArray *conditionDefs;
#end
#import "AddConditionViewController.h"
#import "ConditionsAppDelegate.h"
#import "Condition.h"
#import "ConditionDef.h"
#import "Formula.h"
#implementation AddConditionViewController
#synthesize rvc, conditionNamesPicker, names, conditionDefs, conditionName;
/*
// Implement loadView to create a view hierarchy programmatically.
- (void)loadView {
}
*/
// Implement viewDidLoad to do additional setup after loading the view.
- (void)viewDidLoad {
[super viewDidLoad];
self.title = #"Add Condition";
self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
target:self action:#selector(cancel_Clicked:)] autorelease];
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemSave
target:self action:#selector(save_Clicked:)] autorelease];
self.view.backgroundColor = [UIColor groupTableViewBackgroundColor];
ConditionsAppDelegate *appDelegate = (ConditionsAppDelegate *)[[UIApplication sharedApplication] delegate];
conditionDefs = appDelegate.getConditionDefs;
self.names = [NSMutableArray arrayWithCapacity:[conditionDefs count]];
for (ConditionDef *def in conditionDefs) {
NSString *condition_name = def.condition_name;
if (!condition_name) {
condition_name = #"<Unknown Account>";
}
[names addObject:condition_name];
}
self.conditionNamesPicker.dataSource = self;
self.conditionNamesPicker.delegate = self;
NSLog(#"LINE 48");
}
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
//Set the textboxes to empty string.
txtConditionArea.text = #"";
txtConditionDetail.text = #"";
//Make the Category name textfield to be the first responder.
[txtConditionArea becomeFirstResponder];
NSLog(#"LINE 63");
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
// The number of columns of data
- (int)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
// The number of rows of data
- (int)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
NSLog(#" LINE 87 - COUNT OF CONDITION DEFS TO SHOW = %i", names.count);
[conditionNamesPicker setDataSource:self];
return [names count];
}
// The data to return for the row and component (column) that's being passed in
- (NSString*)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component
{
// NSLog(#"LINE 94 - here is the bug: conditionDefs[row] %#", names[row]);
return names[row];
}
// Catpure the picker view selection
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
// This method is triggered whenever the user makes a change to the picker selection.
// The parameter named row and component represents what was selected.
conditionName = names[row];
}
- (void) save_Clicked:(id)sender {
ConditionsAppDelegate *appDelegate = (ConditionsAppDelegate *)[[UIApplication sharedApplication] delegate];
//Create a Condition Object.
Condition *c = [[Condition alloc] init];
NSInteger newId = c.getNextConditionId;
Condition *cond = [[Condition alloc] initWithPrimaryKey:newId];
cond.condition_area = txtConditionArea.text;
cond.condition_detail = txtConditionDetail.text;
cond.condition_name = conditionName;
//Add the object
// [appDelegate addCondition:cond];
[appDelegate populateFromDatabase];
// ADD TO THE ARRAY:
// [cvc.categories addObject:cond];
// [cvc.Conditions addObject:cond];
rvc.Conditions = [appDelegate activeConditions];
// UPDATE THE TABLEVIEW
[rvc.tableView reloadData];
// release
[cond release];
[c release];
//Dismiss the controller.
[self.navigationController dismissViewControllerAnimated:YES completion: nil];
}
- (void) cancel_Clicked:(id)sender {
//Dismiss the controller.
[self.navigationController dismissViewControllerAnimated:YES completion: nil];
}
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {
NSLog(#"LINE 159");
[theTextField resignFirstResponder];
return YES;
}
- (void)dealloc {
[txtConditionArea release];
[txtConditionDetail release];
[conditionNamesPicker release];
[super dealloc];
}
#end
Please change the property assign toretain for names property. And other array or those object inheriting fromNSObject. It is an object and you are keeping it as assign property. Use assign only for primitive data type. Try this and let me know.
You are setting the data source of PickerView in one the DataSource methods. This is not valid. Remove the below line
[conditionNamesPicker setDataSource:self];
from function:
numberOfRowsInComponent:
You are setting it again.
I have a UIPickerView that is being automatically loaded in loaded when a user touches the UITextField, at the end of selection the user can close the UIPicker by touching the "close" that I have added in the inputAccessoryView = toolbar;. My problem is that I need to set the value that is chosen in the UIPicker to be the value of self.textOutlet.text... I attempt to do so here but with no luck :
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return [tempList objectAtIndex:row];
selectedText = _textOutlet.text;
Can someone please give me some ideas on how to fix this problem? Here is my header code:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate>
#property (strong, nonatomic) NSString* selectedText; //the UITextField text
#property NSArray * tempList;
#end
And here is my implementation code:
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UILabel *myLabel;
#property (weak, nonatomic) IBOutlet UIPickerView *thePicker;
#property (weak, nonatomic) IBOutlet UITextField *textOutlet;
#end
#implementation T3ViewController
#synthesize tempList, selectedText;
// 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 [tempList count];
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return [tempList objectAtIndex:row];
selectedText = _textOutlet.text;
}
- (void)pickerView:(UIPickerView *)pickerView
didSelectRow:(NSInteger)row
inComponent:(NSInteger)component {
CGFloat chosenValue;
switch (row) {
case 0:
chosenValue = 0.0000233;
break;
case 1:
chosenValue = 0.0001273;
break;
case 2:
chosenValue = 0.0012333;
break;
case 3:
chosenValue = 0.0032204;
break;
case 4:
chosenValue = 0.0234179;
break;
case 5:
chosenValue = 0.0002369;
break;
case 6:
chosenValue = 0.0004442;
break;
default:
chosenValue = 0;
break;
}
NSNumberFormatter *formatter = [[NSNumberFormatter alloc]init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
formatter.minimumFractionDigits=7;
self.myLabel.text = [formatter stringFromNumber:#(chosenValue)];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
tempList = [[NSArray alloc] initWithObjects:#"3222° F",#"22150° F",#"1260° F",#"12170° F",#"83210° F",#"84415° F",#"10120° F", nil];
// hides the picker upon initiation of the view
self.thePicker.hidden = YES;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)doneClicked:(id) sender
{
[_textOutlet resignFirstResponder]; //hides the pickerView
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
if (textField == self.textOutlet) {
self.textOutlet.inputView = _thePicker;
self.thePicker.hidden = NO;
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* doneButton = [[UIBarButtonItem alloc] initWithTitle:#"Done"
style:UIBarButtonItemStyleDone target:self
action:#selector(doneClicked:)];
[toolbar setItems:[NSArray arrayWithObjects:flexibleSpaceLeft, doneButton, nil]];
self.textOutlet.inputAccessoryView = toolbar;
}
}
#end
In your first snippet of code, you are setting selectedText after the return statement. This line of code will never get executed.
You need to return AFTER setting the selectedText
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
selectedText = _textOutlet.text;
return [tempList objectAtIndex:row];