Gold challenge


#1

Hi
I was trying to do this challenge but I’m not able to finish it.

I set the date field in BNRItem, removing the property readonly, so I can set it.

I did a new viewcontroller that have the datepicker and its .xib.

I added the button to the detailviewcontroller, I set its ibaction, so when I push the button, the app push the datepicker’s viewcontroller.

The problem is I don’t know how to pick the date from the datepicker and send it to the detailview to set the property date.

Any help?

Thanks


#2

Hi jjalcazar

I sent the BNRItem down the view controller chain to the date view controller (same as it was sent down from the ItemsViewController to the DetailViewController). On the viewWillAppear for the date view controller I then:

[code]- (void)viewWillAppear:(BOOL)animated
{
// Set date picker to selected item date
[super viewWillAppear];

[datePicker setDate:[item dateCreated]];
[datePicker setDatePickerMode:UIDatePickerModeDate];

}[/code]So the dateCreated was initially set correctly on the date picker.

Then on the viewWillDisappear method I saved the dateCreated back to the BNRItem object:

I initially tried to just send down the NSDate from the DetailViewController but couldn’t think of a way of saving the ‘new’ date back to the BNRItem.

I’m not sure if it’s standard practice to send the whole BNRItem to the next view controller even if you don’t need all the data that’s being sent.

Hope this helps anyway.

Mark


#3

This is my solution designed from users’ perspective. Just one thing I’m not sure about that the last sentence “Also, have it display text that warns the user against insurance fraud.”. What exactly would it be displayed?

BNRItem.h

  • Removed “readonly” attribute in the parenthesis for updating the date later by the date picker.

@property (nonatomic, strong) NSDate *dateCreated;

DetailViewController.h

  • Changed *dateLabel from instance variable to property for access from another class (update its label from UIDatePicker instance)
  • Added changeDate method connected to the button

[code]#import <UIKit/UIKit.h>

@class BNRItem;

@interface DetailViewController : UIViewController
{
__weak IBOutlet UITextField *nameField;
__weak IBOutlet UITextField *serialNumberField;
__weak IBOutlet UITextField *valueField;
__weak IBOutlet UIButton *changeDate;
}
@property (nonatomic, strong) BNRItem *item;
@property (nonatomic, weak) IBOutlet UILabel *dateLabel;

  • (IBAction)changeDate:(id)sender;
    @end[/code]

DetailViewController.m

#import "DetailViewController.h"
#import "ChangeDateViewController.h"
#import "BNRItemStore.h"
#import "BNRItem.h"

@implementation DetailViewController
@synthesize item;
@synthesize dateLabel;

- (IBAction)changeDate:(id)sender
{
    ChangeDateViewController *changeDateViewController = [[ChangeDateViewController alloc] init];
    [changeDateViewController setItem:item];
    [[self navigationController] pushViewController:changeDateViewController animated:YES];
}

ChangeDateViewController.h

[code]#import <UIKit/UIKit.h>
@class DetailViewController;
@class BNRItem;

@interface ChangeDateViewController : UIViewController
{
__weak IBOutlet UILabel *currentDate;
__weak IBOutlet UIDatePicker *datePicker;
}
@property (nonatomic, strong) BNRItem *item;
@property (nonatomic, strong) DetailViewController *detail;

  • (IBAction)changeDate:(id)sender;

@end
[/code]

ChangeDateViewController.m

[code]#import “ChangeDateViewController.h”
#import “DetailViewController.h”
#import “BNRItem.h”

@implementation ChangeDateViewController
@synthesize item, detail;

  • (void)viewWillAppear:(BOOL)animated
    {
    [super viewWillAppear];

    // Create a NSDateFormatter that will turn a date into a simple date string
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
    [dateFormatter setTimeStyle:NSDateFormatterNoStyle];

    // Use filtered NSDate object to set dateLabel contents
    [currentDate setText:[dateFormatter stringFromDate:[item dateCreated]]];
    [datePicker setDate:[item dateCreated]];

}

  • (void)viewWillDisappear:(BOOL)animated
    {
    [super viewWillDisappear];

    // “Save” changes to item
    [detail setDateLabel:currentDate];
    [item setDateCreated:[datePicker date]];

}

  • (void)viewDidLoad
    {
    [super viewDidLoad];
    [[self view] setBackgroundColor:[UIColor groupTableViewBackgroundColor]];

}

  • (void)viewDidUnload
    {
    currentDate = nil;
    datePicker = nil;
    [super viewDidUnload];
    }

  • (void)setItem:(BNRItem *)i
    {
    item = i;
    [[self navigationItem] setTitle:@“Set date”];
    }

  • (void)changeDate:(id)sender
    {
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
    [dateFormatter setTimeStyle:NSDateFormatterNoStyle];
    [currentDate setText:[dateFormatter stringFromDate:[datePicker date]]];
    }

@end
[/code]


#4

Only wanted to add that in order for the text about insurance fraud to be displayed when the view appears, use UIAlertView,


#5

Figured I’d post how I tackled the “Insurance Alert” piece of this (using UIAlertView).

First off, I hid the back button so that we could first throw the warning and then deal with the selection made on the UIAlertView (based on the index). In ChangeDateViewController.m, within the viewDidLoad function we add:

Next I created an button with an IBAction of “SaveNewDate”. So in ChangeDateviewController.h we declare:

Then in ChangeDateViewController.m we define the method like so:

[code]- (IBAction)saveNewDate:(id)sender {
UIAlertView *insuranceAlert = [[UIAlertView alloc] initWithTitle:@“Insurance Alert” message:@“It is unlawful to report fraudelent information. Are you sure you want to save this date or would you like to cancel?” delegate:self cancelButtonTitle:@“Cancel” otherButtonTitles:@“I’m sure”, nil];

[insuranceAlert show];    

}[/code]

Then we take advantage of the UIAlertView delegate protocol and employ the “ClickedButtonAtIndex” method. Still in the implementation file we add:

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { if (buttonIndex == 1) { [detailView setDateLabel:currentDate]; [item setDateCreated:[datePicker date]]; [[self navigationController] popViewControllerAnimated:YES]; } else { [[self navigationController] popViewControllerAnimated:YES]; return; } }

If the user chooses to cancel, we pop back to our detail view controller without saving, if they are sure they want to proceed in spite of the warning, we save their new date from the datePicker and pop back to the detail view.

This means we get rid of the viewWillDissapear method. Perhaps this is bad form, so if anyone has a better spot to put this code, feel free to offer that up. I put the popViewControllerAnimated command in our UIAlertView delegate callback because it allowed us to save or not save the info depending on the user’s click choice.


#6

I did almost the same thing except:

  1. I didn’t set the time format for the date picker; there’s no need for it.
  • (void)viewWillAppear:(BOOL)animated
    {
    [super viewWillAppear];
    [datePicker setDatePickerMode:UIDatePickerModeDate];
    [datePicker setDate:[item dateCreated] animated:YES];
    }
  1. I didn’t use a UIAlert to display the warning. I added a static text in the XIB file.

Parsimony rules :slight_smile:


#7

[quote=“tomauhk”]


[/code][/quote]

tomauhk’s solution seems to have the same issue as mine. Namely, the UIDatePicker is truncated at the bottom. I suspect this caused by the reduced view frame height, due to the insertion of the NavigationBar. The xib interface uses a 320 point height for the view, but 44 points get used for the NavigationBar. What is the best way to deal with this and have the UIDatePicker located at the bottom? If I simply move the UIDatePicker up by 44 points in the xib, then I’m depending on the height always being 44 points. This seems vulnerable to changes in how the NavigationBar is sized/laid out in future releases.

Ideas?


#8

How did you get the background of the two views to be the same as the background of the ItemsViewController?


#9

[quote=“BananaSkin”]Hi jjalcazar

I sent the BNRItem down the view controller chain to the date view controller (same as it was sent down from the ItemsViewController to the DetailViewController). On the viewWillAppear for the date view controller I then:

[code]- (void)viewWillAppear:(BOOL)animated
{
// Set date picker to selected item date
[super viewWillAppear];

[datePicker setDate:[item dateCreated]];
[datePicker setDatePickerMode:UIDatePickerModeDate];

}[/code]So the dateCreated was initially set correctly on the date picker.

Then on the viewWillDisappear method I saved the dateCreated back to the BNRItem object:

I initially tried to just send down the NSDate from the DetailViewController but couldn’t think of a way of saving the ‘new’ date back to the BNRItem.

I’m not sure if it’s standard practice to send the whole BNRItem to the next view controller even if you don’t need all the data that’s being sent.

Hope this helps anyway.

Mark[/quote]

I struggled with the logic (not the coding so much) of how to determine if the user changed the dateCreated. I added ‘Done’ and ‘Cancel’ buttons to the navigation bar. The ‘Done’ button is on the right, and the cancel button is on the left (replacing the standard button, with the record name). My code updates the BNRtem that is part of this module if Done is pressed, but does not if Cancel is pressed. My challenge is, and I think this is true of all of the code I’ve seen, “How do you determine what the result was from the DateChange view?”. I’d like to be able to get a return result from the code in DateViewController.m which is called by DetailViewController.m. is that possible?


#10

Here’s my solution to updating the data in the DateChange solution:
0. Add a done and cancel button to the DateChange controller

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    
    if (self)
    {
[b]        // Custom initialization
        UIBarButtonItem * bbR = [[UIBarButtonItem alloc] initWithTitle:@"Done"
                                                                 style:UIBarButtonItemStyleDone target:self action:@selector(saveData)];
        
        [[self navigationItem] setRightBarButtonItem:bbR];
        
        UIBarButtonItem * bbL = [[UIBarButtonItem alloc] initWithTitle:@"Cancel"
                                                                 style:UIBarButtonItemStylePlain target:self action:@selector(popView)];
[/b]        
        [[self navigationItem] setLeftBarButtonItem:bbL];
        
        datePicker.date = item.dateCreated;
   
    }   
}
  1. within the DateChange controller, use ‘Done’ button to update the data in the item object in memory. Cancel does not update the record.
-(void) popView
{
    
    [[self navigationController] popViewControllerAnimated:YES];
  
}

-(void)saveData
{
 
    item.dateCreated = datePicker.date;
    
    [self popView];
}
  1. add a method in BNRItemStore to replace the record at the current index
-(void) replaceItemAtIndex: (BNRItem *)newItem atIndex:(int) index;
{
    
    [allItems removeObjectAtIndex:index];
    
    [allItems insertObject:newItem atIndex:index];
    
}
  1. use the method above within ItemsViewController code
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSArray * items  = [[BNRItemStore sharedStore]allItems];
    BNRItem *selectedItem = [items objectAtIndex:[indexPath row]];
    
    DetailViewController * dtvc = [[DetailViewController alloc]init];
    [dtvc setItem:selectedItem];
    
    [[self navigationController] pushViewController:dtvc animated:YES];
 
  [b]  [[BNRItemStore sharedStore]replaceItemAtIndex:selectedItem atIndex:[indexPath row]];[/b]
}

The downside of this solution is that an update occurs to the datastore even if no change is made in the edit. It doesn’t matter for this solution because the datastore is in memory, but if the datastore is a database somewhere in the ‘ether’, then it could slow things down.