NSDate is crashing my app and I have no idea why - uitableview

The basics are are follows:
I need to control 2 times (NSDate) with 1 UIDatePicker. I created a new Single View project in XCode (v 4.4.1) just to start fiddling with the items I'll be needing. I used a storyboard in the creation of the project, easy peasy. In the storyboard I got rid of the view controller and dragged out a navigation controller. Removed the table view that was the root view controller and added a regular view controller as the root view controller. On there I place a button. I dragged out another view controller and connect that button on the root view controller to this new view controller using 'Push'. Very simple. I test it, it works, basic.
I create a class for the new view controller I just added and set the view controller in storyboard to be of that type. In storyboard I then add a UIDatePicker, a UIButton, and a UITableView (please keep in mind this is just me fiddling trying to make things work and trying to learn how to use everything, still quite new to this).
The code for my ViewController class is as follows:
NewViewController.h
#import <UIKit/UIKit.h>
#interface Reminders : UIViewController <UITableViewDataSource, UITableViewDelegate> {
NSInteger selectedRow;
}
#property (nonatomic, retain) NSDate *amTime;
#property (nonatomic, retain) NSDate *pmTime;
#property (nonatomic, retain) IBOutlet UITableView *tbl;
#property (nonatomic, retain) IBOutlet UIDatePicker *picker;
- (IBAction)timeChange:(id)sender;
- (IBAction)pickerChange:(id)sender;
- (NSString *)dateAsString: (NSDate *)date;
#end
NewViewController.m
#import "Reminders.h"
#interface Reminders ()
#end
#implementation Reminders
#synthesize tbl, picker;
#synthesize amTime, pmTime;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
NSLog(#"init");
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(#"did load");
amTime = [NSDate date];
NSLog(#"AM time viewDidLoad: %#", [self dateAsString:amTime]);
pmTime = [NSDate date];
NSLog(#"PM time viewDidLoad: %#", [self dateAsString:pmTime]);
selectedRow = (NSInteger)1;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger) tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
{
return 3;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//NSLog(#"cell for row");
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyIdentifier"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"MyIdentifier"];
}
if (indexPath.row == 0) {
cell.textLabel.text = #"Dose";
cell.detailTextLabel.text = #"120";
}
else if (indexPath.row == 1) {
cell.textLabel.text = #"AM";
//cell.detailTextLabel.text = #"time am";
cell.detailTextLabel.text = [self dateAsString:amTime];
}
else if (indexPath.row == 2) {
cell.textLabel.text = #"PM";
//cell.detailTextLabel.text = #"time pm";
cell.detailTextLabel.text = [self dateAsString:pmTime];
}
if (selectedRow == indexPath.row) {
[tbl selectRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0] animated:NO scrollPosition:0];
}
return cell;
}
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0) {
selectedRow = 0;
}
else if (indexPath.row == 1) {
selectedRow = 1;
[picker setDate:amTime];
}
else if (indexPath.row == 2) {
selectedRow = 2;
[picker setDate:pmTime];
}
}
- (IBAction)timeChange:(id)sender {
if (selectedRow == 1) {
amTime = [NSDate date];
NSLog(#"time change: %#", [self dateAsString:amTime]);
}
else if (selectedRow == 2) {
pmTime = [NSDate date];
NSLog(#"time change: %#", [self dateAsString:pmTime]);
}
else {
}
[tbl reloadData];
}
- (IBAction)pickerChange:(id)sender {
if (selectedRow == 1) {
amTime = [picker date];
NSLog(#"PICKER time change: %#", [self dateAsString:amTime]);
}
else if (selectedRow == 2) {
pmTime = [picker date];
NSLog(#"PICKER time change: %#", [self dateAsString:pmTime]);
}
else {
NSLog(#"picker does NOTHING");
}
[tbl reloadData];
}
- (NSString *)dateAsString:(NSDate *)date {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSString *stringFromDate = [formatter stringFromDate:date];
return stringFromDate;
}
Yes, I totally realize there are actions for both the button and date picker that are altering the times and the reason the button and corresponding action are there is because I started adding things in when I couldn't get it to work the way I thought it should have been working.
Anyway. The times seem to start off fine, but are some point are made invalid objective-c objects from what I can see by inspection. It chokes on the cellForRowAtIndexPath method when it tried to put the 'amTime' into the cell detail text.
Here's the kicker.... I created the same app, using the same steps I mentioned above, same code (copy pasted), on another machine (one that has proper code signing keys), and that version works properly, even if I run that project on my current machine. When I create the project on my current machine, it crashes.
I apologize for the length post but I wanted to try to give as much detail as possible.

It seems you have variables being deallocated too soon. I would save amTime and pmTime in instance variables, and access them in my code through self.amTime and self.pmTime: since they are properties, getters/setters methods will retain/release appropriately.
There is a wonderful three-part tutorial on raywenderlich.com that explain the basics of memory management in iOS development: a must read.
http://www.raywenderlich.com/2657/memory-management-in-objective-c-tutorial

ARD did the trick without much effort on my part but without ARC it was a matter of throwing in some retains on the amTime and pmTime variables.

Related

Set UITableViewCell contents only once? [duplicate]

I'm using https://github.com/mineschan/MZTimerLabel/
and in my Tableview cellForRowAtIndex using the timer like below:
UILabel *lblTimer=(UILabel *)[cell viewWithTag:10];
MZTimerLabel *UpgradeTimer = [[MZTimerLabel alloc] initWithLabel:lblTimer andTimerType:MZTimerLabelTypeTimer];
[UpgradeTimer setCountDownTime:timestamp];
[UpgradeTimer startWithEndingBlock:^(NSTimeInterval timestamp) {
lblTimer.text = #"✔";
}];
But after any table reloading or scrolling, the timer behaves strange and seems it re-generates multiple timers for counting in the same place.
How should I fix this while using this timer?
Appreciate any help,
Elias
I had a look at MZTimerLabel, and it violates MVC badly. It puts something that belongs into the model (the timer that count's down the time) into the view. That is where your problem comes from. Views should be able to be recreated without having side effects on the model.
I would recommend to ditch that class, and create your own. It's actually quite easy to achieve something like this.
Create a new class that saves a title and a endDate
Store instances of that class in the model that backs your table
Create one NSTimer that refreshes the tableView
Set up your cells.
That's basically all the code you need for a basic countdown in a table. Because it does not store any data in the view you can scroll as much as you like:
#interface Timer : NSObject
#property (strong, nonatomic) NSDate *endDate;
#property (strong, nonatomic) NSString *title;
#end
#implementation Timer
#end
#interface MasterViewController () {
NSArray *_objects;
NSTimer *_refreshTimer;
}
#end
#implementation MasterViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *modelStore = [NSMutableArray arrayWithCapacity:30];
for (NSInteger i = 0; i < 30; i++) {
Timer *timer = [[Timer alloc] init];
timer.endDate = [NSDate dateWithTimeIntervalSinceNow:i*30];
timer.title = [NSString stringWithFormat:#"Timer %ld seconds", (long)i*30];
[modelStore addObject:timer];
}
_objects = modelStore;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[_refreshTimer invalidate]; // timer should not exist, but just in case.
_refreshTimer = [NSTimer timerWithTimeInterval:0.5f target:self selector:#selector(refreshView:) userInfo:nil repeats:YES];
// should fire while scrolling, so we need to add the timer manually:
[[NSRunLoop currentRunLoop] addTimer:_refreshTimer forMode:NSRunLoopCommonModes];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[_refreshTimer invalidate];
_refreshTimer = nil;
}
- (void)refreshView:(NSTimer *)timer {
// only refresh visible cells
for (UITableViewCell *cell in [self.tableView visibleCells]) {
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[self configureCell:cell forRowAtIndexPath:indexPath];
}
}
#pragma mark - Table View
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _objects.count;
}
- (void)configureCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
Timer *timer = _objects[indexPath.row];
cell.textLabel.text = timer.title;
NSInteger timeUntilEnd = (NSInteger)[timer.endDate timeIntervalSinceDate:[NSDate date]];
if (timeUntilEnd <= 0) {
cell.detailTextLabel.text = #"Finished";
}
else {
NSInteger seconds = timeUntilEnd % 60;
NSInteger minutes = (timeUntilEnd / 60) % 60;
NSInteger hours = (timeUntilEnd / 3600);
cell.detailTextLabel.text = [NSString stringWithFormat:#"%02ld:%02ld:%02ld", (long)hours, (long)minutes, (long)seconds];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
[self configureCell:cell forRowAtIndexPath:indexPath];
return cell;
}
#end

UITableView not reloading on reloadData

I have 2 views. The first one has the UITableView on it, it loads perfectly, and also if I click my logOut method which is triggered through didSelectRowAtIndexPath it works perfectly.
For the login I have another View, which loads on top as a modal view for Login. Once the login returns success I want it to refresh the menu on UITableView from the first view. Unfortunately this doesn't work.
MenuVC.h
#interface MenuVC : UIViewController<UITableViewDelegate, UITableViewDataSource>
#property (weak, nonatomic) IBOutlet UITableView *mainNavigation;
-(void)logOut;
-(void)refreshMenu;
MenuVC.m
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Aanwezig in viewDidLoad");
NSUserDefaults *userData=[NSUserDefaults standardUserDefaults];
self.mainNavigation.dataSource = self;
self.mainNavigation.delegate = self;
self.mainNavigation.backgroundColor = [UIColor clearColor];
self.menu = [NSMutableArray arrayWithObjects:#"Login",#"Available", #"Downloads", #"FAQ", nil];
if ( [userData objectForKey:#"userId"] != nil ) {
[self.menu removeObject:#"Login"];
[self.menu insertObject:[userData objectForKey:#"email"] atIndex:0];
[self.menu insertObject:#"Logout" atIndex:1];
signedInSuccess = YES;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = #"menuCell";
MenuCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell=[[MenuCell alloc]initWithStyle:
UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
if ( signedInSuccess == YES && indexPath.row == 0 ) {
NSLog(#"Disabled: %#", [self.menu objectAtIndex:indexPath.row]);
cell.userInteractionEnabled = NO;
cell.menuTextLabel.adjustsFontSizeToFitWidth = YES;
} else {
cell.userInteractionEnabled = YES;
cell.menuTextLabel.adjustsFontSizeToFitWidth = NO;
}
cell.menuTextLabel.text = [self.menu objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Logout and refreshMenu both work here
if ( [identifier isEqualToString:#"Logout"] ) {
NSLog("Logout button clicked");
[self logOut];
[self refreshMenu];
}
//Rest of the code
}
-(void)refreshMenu {
[self viewDidLoad];
[self.mainNavigation reloadData];
}
-(void)logOut {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"userId"];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"email"];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"password"];
signedInSuccess = NO;
}
The codes above all work, only here is the problem occurring, when I click loginClicked, and call refreshMenu it IS doing [self viewDidLoad], but NOT [self.mainNavigation reloadData]; from the refreshMenu method.
LoginVC.m
#import "MenuVC.h"
- (IBAction)loginClicked:(id)sender {
{
// If login process is success (which works)
MenuVC *MenuViewController = [[MenuVC alloc] init];
[MenuViewController refreshMenu];
}
First of all:
You don't call yourself viewDidLoad. So remove it in refreshMenu, and if you want other code that you put in viewDidLoad transform it in a method that you'll call in viewDidLoad and that you could also call in refreshMenu
The issue, is that you have an object MenuVC already created. When in LoginVC.m you do MenuVC *MenuViewController = [[MenuVC alloc] init]; it's a whole new object. It isn't even on screen. That not the same object that the previous one. If you doubt it, check their pointers.
If I understood correctly, your MenuVC object is the owner of the LoginVC object.
So, a solution you can use is the delegate pattern.
Here a suggestion you can adapt.
In LoginVC.h
#protocol LoginVCDelegate < NSObject >
-(void)didLoginWithSuccess:(BOOL)success;
#end
#property (nonatomic, week) id < LoginVCDelegate > loginDelegate;
In LoginVC.m, in loginClicked:
if ([_loginDelegate respondsToSelector:#selector(didLoginWithSuccess:)])
[_loginDelegate didLoginWithSuccess:theBooleanYouWant];
In MenuVC.h
#interface MenuVC : UIViewController< UITableViewDelegate, UITableViewDataSource, LoginVCDelegate >
In MenuVC.m
-(void)didLoginWithSuccess:(BOOl)success
{
[self refreshMenu];
//Note that you could check if login succeed or not by checking the success boolean.
}
Also, when LoginVC is created in MenuVC.m:
[loginVC setLoginVCDelegate:self];

Using timer in a tableview re creates the timer after any scroll or table reload

I'm using https://github.com/mineschan/MZTimerLabel/
and in my Tableview cellForRowAtIndex using the timer like below:
UILabel *lblTimer=(UILabel *)[cell viewWithTag:10];
MZTimerLabel *UpgradeTimer = [[MZTimerLabel alloc] initWithLabel:lblTimer andTimerType:MZTimerLabelTypeTimer];
[UpgradeTimer setCountDownTime:timestamp];
[UpgradeTimer startWithEndingBlock:^(NSTimeInterval timestamp) {
lblTimer.text = #"✔";
}];
But after any table reloading or scrolling, the timer behaves strange and seems it re-generates multiple timers for counting in the same place.
How should I fix this while using this timer?
Appreciate any help,
Elias
I had a look at MZTimerLabel, and it violates MVC badly. It puts something that belongs into the model (the timer that count's down the time) into the view. That is where your problem comes from. Views should be able to be recreated without having side effects on the model.
I would recommend to ditch that class, and create your own. It's actually quite easy to achieve something like this.
Create a new class that saves a title and a endDate
Store instances of that class in the model that backs your table
Create one NSTimer that refreshes the tableView
Set up your cells.
That's basically all the code you need for a basic countdown in a table. Because it does not store any data in the view you can scroll as much as you like:
#interface Timer : NSObject
#property (strong, nonatomic) NSDate *endDate;
#property (strong, nonatomic) NSString *title;
#end
#implementation Timer
#end
#interface MasterViewController () {
NSArray *_objects;
NSTimer *_refreshTimer;
}
#end
#implementation MasterViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *modelStore = [NSMutableArray arrayWithCapacity:30];
for (NSInteger i = 0; i < 30; i++) {
Timer *timer = [[Timer alloc] init];
timer.endDate = [NSDate dateWithTimeIntervalSinceNow:i*30];
timer.title = [NSString stringWithFormat:#"Timer %ld seconds", (long)i*30];
[modelStore addObject:timer];
}
_objects = modelStore;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[_refreshTimer invalidate]; // timer should not exist, but just in case.
_refreshTimer = [NSTimer timerWithTimeInterval:0.5f target:self selector:#selector(refreshView:) userInfo:nil repeats:YES];
// should fire while scrolling, so we need to add the timer manually:
[[NSRunLoop currentRunLoop] addTimer:_refreshTimer forMode:NSRunLoopCommonModes];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[_refreshTimer invalidate];
_refreshTimer = nil;
}
- (void)refreshView:(NSTimer *)timer {
// only refresh visible cells
for (UITableViewCell *cell in [self.tableView visibleCells]) {
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[self configureCell:cell forRowAtIndexPath:indexPath];
}
}
#pragma mark - Table View
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _objects.count;
}
- (void)configureCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
Timer *timer = _objects[indexPath.row];
cell.textLabel.text = timer.title;
NSInteger timeUntilEnd = (NSInteger)[timer.endDate timeIntervalSinceDate:[NSDate date]];
if (timeUntilEnd <= 0) {
cell.detailTextLabel.text = #"Finished";
}
else {
NSInteger seconds = timeUntilEnd % 60;
NSInteger minutes = (timeUntilEnd / 60) % 60;
NSInteger hours = (timeUntilEnd / 3600);
cell.detailTextLabel.text = [NSString stringWithFormat:#"%02ld:%02ld:%02ld", (long)hours, (long)minutes, (long)seconds];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
[self configureCell:cell forRowAtIndexPath:indexPath];
return cell;
}
#end

Passing data from dynamic tableview back to static tableview

Please help i have been struggling passing back the data. I have 2 tableViews. 1st tableview=static table=RootVC. 2nd tableview=dynamic table=FirstVC. in RootVC i have a cell with two labels, "repeatLabel" and "repeatDetail" with a disclosure indicator. When i click on the cell it display the next table which is FirstVC, FistVC is populated with weekdays. after selection of my choice, i want the selected days to be passed back into RootVC in "repeatDetail" and when i go back still be able to see previously selected data.
My RootVC looks like this:
#import "RepeatViewController.h"
#interface SettingsViewController : UITableViewController
#property (strong, nonatomic) IBOutlet UILabel *repeatDetail;
#property (strong, nonatomic) IBOutlet UILabel *repeatLabel;
#property (strong,nonatomic) NSString *getRepeatDetail;
#property (nonatomic, strong) NSMutableArray *selectedDaysArray;
#end
in my RootVC.m
#import "SettingsViewController.h"
#interface SettingsViewController ()
#end
#implementation SettingsViewController
#synthesize repeatLabel,repeatDetail;
#synthesize getRepeatLabel;
#synthesize selectedDaysArray;
- (void)viewDidLoad
{
[super viewDidLoad];
repeatLabel.text = #"Repeat";
repeatDetail.text = getRepeatLabel;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
RepeatViewController *destinationController = segue.destinationViewController;
if( [destinationController isKindOfClass:[RepeatViewController class]] )
{
//You can reuse your selectedDays arrays
destinationController.selectedDays = self.selectedDaysArray;
[(RepeatViewController *)destinationController setCompletionBlock:^(NSArray *retDaysArray) // <- make this change
{
// Save your changes
self.selectedDaysArray = [NSMutableArray arrayWithArray: retDaysArray]; // <- make this change
NSLog(#"retDaysArray: %#", self.selectedDaysArray); //<- Add this debug line
}];
}
}
#end
My 1stVC.h
#import "SettingsViewController.h"
typedef void(^WeekdayCompletionBlock)(NSArray *retDaysArray);
#interface RepeatViewController : UITableViewController <UITableViewDataSource,UITableViewDelegate>
#property (nonatomic,strong) NSMutableArray *selectedDays;
#property (nonatomic, copy) NSArray *completionBlock;
#property (copy) WeekdayCompletionBlock returnBlock;
//#property (strong, nonatomic) IBOutlet UIBarButtonItem *saveButton;
-(IBAction)save:(id)sender;
#end
my 1stVC.m
#import "RepeatViewController.h"
#interface RepeatViewController ()
#end
#implementation RepeatViewController
#synthesize selectedDays= _selectedDays;
#synthesize completionBlock;
#synthesize returnBlock;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
completionBlock = [NSArray arrayWithObjects:#"Sunday", #"Monday", #"Tuesday", #"Wednesday", #"Thursday", #"Friday", #"Saturday", nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return 7;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"RepeatCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
}
NSString *day = completionBlock[indexPath.row];
cell.textLabel.text = day;
if ([self.selectedDays containsObject:day])
cell.accessoryType = UITableViewCellAccessoryCheckmark;
else
cell.accessoryType = UITableViewCellAccessoryNone;
//cell.textLabel.text = [completionBlock objectAtIndex:indexPath.row];
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (!self.selectedDays)
self.selectedDays = [[NSMutableArray alloc] init];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark)
{
cell.accessoryType = UITableViewCellAccessoryNone;
//remove data from array
[self.selectedDays removeObject:[completionBlock objectAtIndex:indexPath.row]];
}
else
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
//add data to array
[self.selectedDays addObject:[completionBlock objectAtIndex:indexPath.row]];
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
-(IBAction)save:(id)sender
{
NSUserDefaults *myNewWeekString = [NSUserDefaults standardUserDefaults];
[myNewWeekString setObject:self.selectedDays forKey:#"MY_KEY_FOR_ACCESING_DAYSOFWEEK"];
[myNewWeekString synchronize];
//NSLog(#"The selected day/s is %#",self.selectedDays);
if (self.returnBlock)
{
self.returnBlock(self.selectedDays);
}
[self.navigationController popViewControllerAnimated:YES];
// NSLog(#"The selected day/s is %#",self.selectedDays);
// if (self.returnBlock)
// {
// self.returnBlock([completionBlock objectAtIndex:indexPath.row]);
//}
}
/*
-(void) setReturnBlock:(WeekdayCompletionBlock)returnBlock
{
[self.selectedDays addObject:(self.returnArray);
}
- (NSArray *)setDats
{
return [NSArray arrayWithArray:[self.selectedDays copy]];
}*/
#end
When you work with static cells you have to bind the control you are using directly, there's no need.
So what I can suggest you is the following:
Bind your controls with some specific identifier, like labelFieldRow{rowid} example: labelFieldRow1.
So on prepare for segue, just check what's the selected row and pass the data you want to the destination controller.
Probably not the best, but it should work.
You have to pass data (selected by user before) from your RootVC to FirstVC. To do that in your RootVC add property to keep the selected data;
#property (nonatomic, strong) NSMutableArray *selectedDaysArray;
In prepareForSegue method you have to pass that array to let the table view know what needs to be selected:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *destinationController = segue.destinationViewController;
if( [destinationController isKindOfClass:[RepeatViewController class]] )
{
//You can reuse your selectedDays arrays
((RepeatViewController*)destinationController).selectedDays = self.selectedDaysArray;
[(RepeatViewController *)destinationController setReturnBlock:^(NSArray *retDaysArray) // <- make this change
{
// Save your changes
self.selectedDaysArray = [NSMutableArray arrayWithArray: retDaysArray]; // <- make this change
NSLog(#"DATA: %#", self.selectedDaysArray) //<- Add this debug line
}];
}
}
Remove this line from viewDidLoad you don't want to allocate it every time now you just pass it from rootVC
_selectedDays = [[NSMutableArray alloc] init];
And in cellForRowInIndexPath replace this line:
cell.textLabel.text = [completionBlock objectAtIndex:indexPath.row];
with this code:
NSString *day = completionBlock[indexPath.row];
cell.textLabel.text = day;
if ([self.selectedDays containsObject:day])
cell.accessoryType = UITableViewCellAccessoryCheckmark;
else
cell.accessoryType = UITableViewCellAccessoryNone;
And change didSelectRowAtIndexPath: to
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (!self.selectedDays)
self.selectedDays = [[NSMutableArray alloc] init];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark)
{
cell.accessoryType = UITableViewCellAccessoryNone;
//remove data from array
[self.selectedDays removeObject:[completionBlock objectAtIndex:indexPath.row]];
}
else
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
//add data to array
[self.selectedDays addObject:[completionBlock objectAtIndex:indexPath.row]];
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
Hope this help.

iOS reload UITableView using UISegmentedControl

Hi I'm trying to reload my table view based off of two different arrays. Which array should be loaded is determined by a segment control in the navigation bar. Currently it only will load the first array and nothing happens when the segment control is pressed. Below is my code any help as to why this isn't working is greatly appreciated. I've also checked that my IBAction segmenter is connected in the nib.
MessageViewController.h
#import <UIKit/UIKit.h>
#interface MessageViewController : UIViewController<UITableViewDelegate> {
IBOutlet UISegmentedControl *segmentControl;
IBOutlet UINavigationBar *navBar;
IBOutlet UITableView *tableView;
}
#property (retain, nonatomic) IBOutlet UISegmentedControl *segmentControl;
#property (retain, nonatomic) IBOutlet UITableView *tableView;
#property (nonatomic, retain) NSMutableArray *inbox;
#property (nonatomic, retain) NSMutableArray *sent;
#end
MessageViewController.m
#import "MessageViewController.h"
#interface MessageViewController () <UITableViewDelegate>
#end
#implementation MessageViewController
#synthesize segmentControl;
#synthesize inbox;
#synthesize sent;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.tabBarItem.title = NSLocalizedString(#"Messages", #"Messages");
self.tabBarItem.image = [UIImage imageNamed:#"mail_2_icon&32"];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate = self;
self.inbox = [NSMutableArray arrayWithObjects:#"testing", #"test", #"another", nil];
self.sent = [NSMutableArray arrayWithObjects:#"test", #"another", #"testing", nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if(segmentControl.selectedSegmentIndex == 0){
return [inbox count];
}else if(segmentControl.selectedSegmentIndex == 1){
return [sent count];
}else{
return [inbox count];
}
}
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
if(segmentControl.selectedSegmentIndex == 0){
NSString *cellValue = [inbox objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
}else if(segmentControl.selectedSegmentIndex == 1){
NSString *cellValue = [sent objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
}else{
NSString *cellValue = [inbox objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
}
return cell;
}
-(IBAction)segmenter{
[tableView reloadData];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
}
- (void)dealloc {
[inbox release];
[sent release];
[segmentControl release];
[segmentControl release];
[super dealloc];
}
- (void)viewDidUnload {
[self setSegmentControl:nil];
[segmentControl release];
segmentControl = nil;
[super viewDidUnload];
}
#end
Not sure why it wasn't working in the end all I did was delete the three classes and redo everything with the code above something must have just got borked along the way but it's working now and I'm a happy camper. Didn't need the delegate stuff either since that's all done in the nib so my original code worked fine.
set the delegate and datasource methods and take breakpoint to check the data . your code is right .
tableview.delegate=self;
tableView.datasource=self;
The only problem I see is that you're using a view controller with a table view in it, but you're not setting up the delegate for it in your viewDidLoad, and you're not implementing the delegate for your view controller.
#interface MessageViewController () <UITableViewDelegate, UITableViewDataSource>
#end
In viewDidLoad:
self.tableView.delegate = self;
self.tableView.dataSource = self;
Also make sure you have everything hooked up properly in IB, both your segmented control and your table view.
EDIT: I made my own test as per what you're trying to do, Here's my own implementation file

Resources