In my iOS app I have a custom UITableViewCell that is part of a TableView in my FirstViewController. In the FirstViewController I have an array of objects that corresponds to populating the rows and then of course the objects in that array which have properties.
I have a timer in each cell and I need to get the length of the timer from a property of the corresponding object. I've tried importing FirstViewController.h but I get the error "property not found on object of type" no matter what.
I already have the object created and configured in the cellForRowAtIndexPath method of the FirstViewController but I was hoping to use it in a method in TableViewCell.m.
Is there a way I can use the array or the object in the TableViewCell? Or should the method be implemented in the FirstViewController?
EDIT #1:
I moved cell.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:cell selector:#selector(startTimer) userInfo:nil repeats:YES]; into the cellForRow... method of the FirstViewController. Now the timer runs with the desired time but it's no longer started by a button. It immediately runs when the TableView is loaded and restarts every time the TableView is reloaded.
Here is my ATCTableViewCell.h file: (Updated with Edit #2)
#import <UIKit/UIKit.h>
#import "ATCFirstViewController.h"
#interface ATCTableViewCell : UITableViewCell
- (void)startTimer;
// Contents moved to cellForRow... -> - (IBAction)playTimer:(id)sender;
#property (weak, nonatomic) IBOutlet UILabel *titleLabel;
#property (weak, nonatomic) IBOutlet UILabel *timeLabel;
#property (weak, nonatomic) IBOutlet UIButton *playButton;
#property (weak, nonatomic) IBOutlet UIButton *pauseButton;
#property NSTimer *timer;
#property int secondsCount; // initially the length in seconds and then decreased
#end
EDIT #2:
The timer is a decrementing timer that changes a label in the startTimer method which is in ATCTableViewCell.m. The label is a property of the cell at the moment along with another label and the buttons. I'll work on moving things to the object class instead of having them as properties the cell.
Here is the startTimer method:
- (void)startTimer {
self.secondsCount--;
int hours = self.secondsCount / 3600;
int minutes = (self.secondsCount / 60) - (hours * 60);
int seconds = self.secondsCount - (hours * 3600) - (minutes * 60);
NSString *timerOutput = [NSString stringWithFormat:#"%02d:%02d:%02d", hours, minutes, seconds];
self.timeLabel.text = timerOutput;
if (self.secondsCount == 0) {
[self.timer invalidate];
self.timer = nil;
}
}
Here is my ATCObject.h file:
#import <Foundation/Foundation.h>
#interface ATCObject : NSObject
#property NSString *title;
#property int lengthInSeconds;
#property int initialHours;
#property int initialMinutes;
#end
Thanks
I am updating this answer based on all of the previous discussion.
#interface ATCObject : NSObject
#property (nonatomic, strong) NSString *title;
#property (nonatomic, assign) NSInteger lengthInSeconds;
#property (nonatomic, assign) NSInteger initialHours;
#property (nonatomic, assign) NSInteger initialMinutes;
//Move the NSTimer and the -playTimer method to this class
#property (nonatomic,strong) NSTimer *timer;
-(void)startTimer;
-(void)timerTick;
-(void)pauseTimer;
#end
In your tableView controller class:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Identifier";
ATCTableViewCell *cell = (ATCTableViewCell *)[tableView dequeueReusableCellForIdentifer:Identifier];
if (!cell) {
cell = [[ATCTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault identifier:Identifier];
[cell.playButton addTarget:self action:#selector(playTouched:) forControlEvents:UIControlEventTouchUpInside];
[cell.pauseButton addTarget:self action:#selector(pauseTouched:) forControlEvents:UIControlEventTouchUpInside];
}
ATCObject *objectForRow = [myDataArray objectAtIndex:indexPath.row];
cell.timeLabel.text = objectForRow.title;
return cell;
}
//Also in your controller class
-(void)playTouched:(id)sender {
//you may need to change self.tableView to however you reference the table in your class
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
if (indexPath != nil) {
ATCObject *currentObject = [myDataArray objectAtIndex:indexPath.row];
[currentObject startTimer];
}
}
//ATCObject.m
-(void)startTimer {
if (!self.timer) {
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(timerTick:)
userInfo:nil
repeats:YES];
}
}
-(void)timerTick {
//Already assuming lengthInSeconds is set
if (lengthInSeconds > 0) {
lengthInSeconds--;
//Do your math to get total hours and minutes
NSInteger totalHours = //math stuff convert lengthInSeconds
NSInteger totalMinutes = //more math stuff etc
NSString *newTitle = [NSString stringWithFormat:#"%i:%i",totalHours,totalMinutes];
self.title = newTitle;
} else {
self.title = #"00:00";
[self.timer invalidate];
self.timer = nil;
}
}
Related
I want a ranking in my app where you can see who has the fastest lap.
I try to convert the time into milliseconds, but it didn't work well.
This is my code at the moment.
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize watch,start,reset;
- (void)viewDidLoad {
[super viewDidLoad];
running = NO;
count = 0;
watch.text = #"00:00.00";
start.layer.cornerRadius = 45;
reset.layer.cornerRadius = 45;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (IBAction)startpressed:(id)sender {
if (running == NO) {
running = YES;
[start setTitle:#"STOPP" forState:UIControlStateNormal];
NSDate *watch = [NSDate dateWithTimeIntervalSince1970:(1273636800 / 1000.0)];
if (myTimer == nil) {
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.0055
target:self
selector:#selector(updateTimer)
userInfo: nil
repeats:YES];
}
} else {
running = NO;
[myTimer invalidate];
myTimer = nil;
[start setTitle:#"START" forState:UIControlStateNormal];
}
}
- (IBAction)resetpressed:(id)sender {
running =NO;
[myTimer invalidate];
myTimer =nil;
[start setTitle:#"START" forState:UIControlStateNormal];
count = 0;
watch.text = #"00:00.00";
}
- (void)updateTimer {
count++;
int min = floor(count/100/60);
int sec = floor(count/100);
int mSec = count % 100;
if (sec >= 60) {
sec = sec % 60;
}
watch.text = [NSString stringWithFormat:#"%02d:%02d.%02d", min,sec,mSec];
}
#end
this is my ViewController.h code:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
{
NSTimer *myTimer;
BOOL running;
int count;
double methodStart;
}
#property (weak, nonatomic) IBOutlet UILabel *watch;
#property (weak, nonatomic) IBOutlet UIButton *start;
#property (weak, nonatomic) IBOutlet UIButton *reset;
#property (weak, nonatomic) IBOutlet UILabel *eins;
#property (weak, nonatomic) IBOutlet UILabel *zwei;
#property (weak, nonatomic) IBOutlet UILabel *drei;
- (IBAction)startpressed:(id)sender;
- (IBAction)resetpressed:(id)sender;
- (void) updateTimer;
#end
Create array for holding lap history in class interface/extension and initialise it.
self.lapHistory = [#[] mutableCopy];
Capture and sort every lap time when lap is reset/stopped
- (IBAction)resetpressed:(id)sender {
[self.lapHistory addObject:#(count)];
self.lapHistory = [self.lapHistory sortedArrayUsingSelector: #selector(compare:)];
}
Display sorted ranking from lapHistory
-(void)displayRanking{
NSMutableString *rankingResult = [[NSMutableString alloc] init];
for (NSNumber *lap in self.lapHistory) {
[rankingResult appendString:[NSString stringWithFormat:#"%ld\n",[lap integerValue]]];
}
NSLog(#"Ranking result is %#", rankingResult);
}
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
I am new to iPhone App development, below is ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self updateMyView];
}
- (IBAction)clickButtonResult:(id)sender
{
enteredText = [textField text]; // Or textField.text
NSLog(#"Number 1 : %i", number_1);
NSLog(#"Number 2 : %i", number_2);
NSLog(#"Entered Text is %#", enteredText);
int NUM_RESULT = number_1 + number_2;
verify_result = [NSString stringWithFormat:#"%i", NUM_RESULT];
NSLog(#"Verify Result : %#", verify_result);
NSString *final_result = [NSString stringWithFormat:#"%d", [enteredText isEqualToString:verify_result]];
int final_int_result = [final_result integerValue];
if (final_int_result) {
//result_label.text = #"Correct";
NSLog(#"Correct");
[self updateMyView];
} else {
//result_label.text = #"Wrong";
NSLog(#"Wrong");
}
}
- (int)getRandomNumberBetween:(int)min maxNumber:(int)max
{
return min + arc4random() % (max - min + 1);
}
- (void) updateMyView
{
number_1 = [self getRandomNumberBetween:10 maxNumber:99];
number_2 = [self getRandomNumberBetween:10 maxNumber:99];
num_1.text = [NSString stringWithFormat:#"%i", number_1];
num_2.text = [NSString stringWithFormat:#"%i", number_2];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
and ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
{
IBOutlet UILabel *num_1;
IBOutlet UILabel *num_2;
IBOutlet UILabel *result_label;
IBOutlet UITextField *textField;
int number_1;
int number_2;
NSString *verify_result;
NSString *enteredText;
BOOL display_result;
}
- (IBAction)clickButtonResult:(id)sender;
#end
After entering the correct result the UIView should be updated with updateMyView function but it is not happening.
Can anyone help here??
First of all, start using Properties.
ViewController.h
#interface ViewController : UIViewController
#property (nonatomic, weak) IBOutlet UILabel *num_1;
#property (nonatomic, weak) IBOutlet UILabel *num_2;
#property (nonatomic, weak) IBOutlet UILabel *resultLabel;
#property (nonatomic, weak) IBOutlet UITextField *textField;
#property (nonatomic) NSInteger number_1;
#property (nonatomic) NSInteger number_2;
#property (nonatomic, strong) NSString *verifyResult;
#property (nonatomic, strong) NSString *enteredText;
#property (nonatomic) BOOL displayResult;
- (IBAction)clickButtonResult:(id)sender;
#end
In ViewController.m code use self.{name of property}, for example self.textField for the textField property.
Now, go to Interface builder and connect the IBOutlet properties to the right objects. (click with right button on File's Owner)
Try changing num_1.text in viewDidLoad to make sure you have access to that label from your UIViewController.
So in viewDidLoad, just put something like
num1.text = #"Updated from viewDidLoad"
Make sure that you have connected num_1 and num_2 with the UILabels properly. It seems that you have not connected these.
Just move [self updateMyView]; outside the if statements in clickButtonResult: function
then it will update the view when click the button.
if (final_int_result) {
//result_label.text = #"Correct";
NSLog(#"Correct");
} else {
//result_label.text = #"Wrong";
NSLog(#"Wrong");
}
[self updateMyView];
I have an iOS app that makes a request to a web-service which returns JSON formatted data. There is a predefined class in my iOS app that inherits and implements the JSONModel Framework, to which this returned data is bound to as an NSMutableArray containing these objects. The TableView's data is generated from these objects.
My conundrum is that in my custom UITableViewCell I allow the user to change some of the data presented, and I need the ability to save that back to the classes which can be serialized and sent via POST back to the web-service.
Custom Cell .h:
#interface EnclosureDetailCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UILabel *enclosureNumber;
#property (weak, nonatomic) IBOutlet UITextField *QTY;
#property (weak, nonatomic) IBOutlet UIStepper *stepper;
#property (weak, nonatomic) IBOutlet DeSelectableSegmentControl *enclosureStatus;
- (IBAction)valueChanged:(UIStepper *)sender;
- (IBAction)changedTextValue:(id)sender;
#end
Custom Cell .m:
#implementation EnclosureDetailCell
- (IBAction)changedTextValue:(id)sender
{
self.stepper.value = self.QTY.text.intValue;
}
- (IBAction)valueChanged:(UIStepper *)sender
{
int stepperValue = sender.value;
self.QTY.text = [NSString stringWithFormat:#"%i", stepperValue];
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
Model Class (.h):
#protocol Enclosure #end
#interface Enclosure : JSONModel
#property (nonatomic, strong) NSString *EnclosureNumber;
#property (nonatomic, strong) NSString *InventoryID;
#property (nonatomic, strong) NSString *UseInventoryID;
#property (nonatomic) int CensusQTY;
#property (nonatomic) BOOL Verified;
#property (nonatomic) BOOL MissingEnclosure;
#property (nonatomic) BOOL RetireEnclosure;
#end
TableViewController (partial)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
EnclosureDetailCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
ProtocolEnclosure *loc = (ProtocolEnclosure *)_objects[indexPath.section];
Enclosure *enc = (Enclosure *) loc.Enclosures[indexPath.row];
cell.enclosureNumber.text = enc.EnclosureNumber;
cell.QTY.text =[NSString stringWithFormat:#"%i", enc.CensusQTY];
cell.stepper.value = enc.CensusQTY;
if (enc.Verified)
{
cell.QTY.enabled = false;
cell.stepper.enabled = false;
cell.enclosureStatus.selectedSegmentIndex = Verified;
}
else if (enc.MissingEnclosure)
cell.enclosureStatus.selectedSegmentIndex = MissingEnclosure;
else if (enc.RetireEnclosure)
cell.enclosureStatus.selectedSegmentIndex = RetireEnclosure;
else
cell.enclosureStatus.selectedSegmentIndex = None;
return cell;
}
enum{
Verified = 0,
MissingEnclosure = 1,
RetireEnclosure = 2,
None = -1
};
So in my UITableViewCell I have a text field that corresponds to CensusQTY and a SegmentControl who's selection corresponds to Verified/MissingEnclosure/RetireEnclosure.
How can I go about saving the data the user has changed via the UI back into the model class?
I obviously can't iterate over each of the UITableView rows - because of dequeue, I will only get the ones that are currently on screen.
Any thoughts on how this could be accomplished?
Thanks!
There are probably many ways to do that, the cleanest way that comes to mind would be to create a delegate for your custom cell. (That you could declare in .h). Your cell should add a index property to keep track of what instance of Enclosure it's referring to.
#class EnclosureDetailCell;
#protocol EnclosureCellDelegate
#required
- (void) qtyDidUpdate:(EnclosureDetailCell*)cell;
- (void) stepperDidUpdate:(EnclosureDetailCell*)cell;
#end
#interface EnclosureDetailCell : UITableViewCell
#property (nonatomic,assign) NSInteger index;
#property (nonatomic,weak) id<EnclosureCellDelegate> delegate;
....
In your .m you would have to call your delegate
#implementation EnclosureDetailCell
- (IBAction)changedTextValue:(id)sender
{
self.stepper.value = self.QTY.text.intValue;
[self.delegate qtyDidUpdate:self];
}
- (IBAction)valueChanged:(UIStepper *)sender
{
int stepperValue = sender.value;
self.QTY.text = [NSString stringWithFormat:#"%i", stepperValue];
[self.delegate stepperDidUpdate:self];
}
You'd have to implement those methods in your TableViewController. It would look like :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
EnclosureDetailCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.index = indexPath.row;
cell.delegate = self;
ProtocolEnclosure *loc = (ProtocolEnclosure *)_objects[indexPath.section];
Enclosure *enc = (Enclosure *) loc.Enclosures[indexPath.row];
....
}
- (void) qtyDidUpdate:(EnclosureDetailCell*)cell{
Enclosure *enc = (Enclosure *)loc.Enclosures[cell.index];
//Here you can update directly the items of your array
}
- (void) stepperDidUpdate:(EnclosureDetailCell*)cell{
Enclosure *enc = (Enclosure *)loc.Enclosures[cell.index];
//Here you can update directly the items of your array
}
That way you will be able to keep your whole array updated, and be able to send your new data to your web service whenever you like.
Probably the easiest way would be to have the cell own a weak reference to the Model object which you update in the IBAction methods of the cell.
#interface EnclosureDetailCell : UITableViewCell
...
#property(nonatomic, weak) Enclosure *enclosure;
...
#end
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
Enclosure *enc = (Enclosure *) loc.Enclosures[indexPath.row];
cell.enclosure = enc;
...
}
#implementation EnclosureDetailCell
- (IBAction)changedTextValue:(id)sender
{
self.stepper.value = self.QTY.text.intValue;
//you have access to self.enclosure, do what you want
}
- (IBAction)valueChanged:(UIStepper *)sender
{
int stepperValue = sender.value;
self.QTY.text = [NSString stringWithFormat:#"%i", stepperValue];
//you have access to self.enclosure, do what you want
}