So I'm building a calendar-type app on the new Apple iWatch. This is the initial storyboard layout for my app:
Basically the initial table view will parse the calendar and grab the event name and the date of it. What I want to do is basically, through a push segue, send that data to the second view controller.
I have tried using the method -(NSArray *)contextsForSegueWithIdentifier:(NSString *)segueIdentifier, but the context in the second view controller is showing up as nil.
This is my code:
InterfaceViewController:
#import "InterfaceController.h"
#import <EventKit/EventKit.h>
#import "Calendar.h"
#interface InterfaceController() {
NSArray *events;
NSArray *eventsWithNotes;
}
#end
#implementation InterfaceController
- (void)setupTable
{
EKEventStore *store = [[EKEventStore alloc] init];
// Get the appropriate calendar
NSCalendar *calendar = [NSCalendar currentCalendar];
if ([store respondsToSelector:#selector(requestAccessToEntityType:completion:)])
{
[store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error)
{
if (granted)
{
NSLog(#"User has granted permission!");
// Create the start date components
NSDateComponents *oneDayAgoComponents = [[NSDateComponents alloc] init];
oneDayAgoComponents.day = -1;
NSDate *oneDayAgo = [calendar dateByAddingComponents:oneDayAgoComponents
toDate:[NSDate date]
options:0];
// Create the end date components
NSDateComponents *oneYearFromNowComponents = [[NSDateComponents alloc] init];
oneYearFromNowComponents.year = 1;
NSDate *oneYearFromNow = [calendar dateByAddingComponents:oneYearFromNowComponents
toDate:[NSDate date]
options:0];
// Create the predicate from the event store's instance method
NSPredicate *predicate = [store predicateForEventsWithStartDate:oneDayAgo
endDate:oneYearFromNow
calendars:nil];
// Fetch all events that match the predicate
events = [store eventsMatchingPredicate:predicate];
NSMutableArray *rowTypesList = [NSMutableArray array];
for(int i=0; i < events.count; i++){
[rowTypesList addObject:#"Calendar"];
}
[self.tableView setRowTypes:rowTypesList];
for (NSInteger i = 0; i < self.tableView.numberOfRows; i++)
{
NSObject *row = [self.tableView rowControllerAtIndex:i];
Calendar *calendar = (Calendar *) row;
NSLog(#"notes: %#",[[events objectAtIndex:i] notes]);
NSString* notes = [[events objectAtIndex:i] notes];
[calendar.titleLabel setText:[[events objectAtIndex:i] title]];
}
}
else
{
NSLog(#"User has not granted permission!");
}
}];
}
}
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
// Configure interface objects here.
}
- (void)willActivate {
// This method is called when watch view controller is about to be visible to user
[super willActivate];
[self setupTable];
}
- (void)didDeactivate {
// This method is called when watch view controller is no longer visible
[super didDeactivate];
}
- (NSArray *)contextsForSegueWithIdentifier:(NSString *)segueIdentifier inTable:(WKInterfaceTable *)table rowIndex:(NSInteger)rowIndex {
NSArray *array = nil;
NSString *notes = [[events objectAtIndex:rowIndex] notes];
NSString *title = [[events objectAtIndex:rowIndex] title];
NSString *strippedNumber = [notes stringByReplacingOccurrencesOfString:#"[^0-9]" withString:#"" options:NSRegularExpressionSearch range:NSMakeRange(0, [notes length])];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
NSString *date = [dateFormatter stringFromDate:[[events objectAtIndex:rowIndex] startDate]];
if([segueIdentifier isEqualToString:#"IBM"]) {
array = #[notes, title, strippedNumber, date];
}
return array;
}
#end
DetailIntefaceViewController.h:
#import <WatchKit/WatchKit.h>
#import <Foundation/Foundation.h>
#interface DetailInterfaceController : WKInterfaceController
#property (nonatomic, strong) NSString *currentContext;
#property (weak, nonatomic) IBOutlet WKInterfaceLabel *phoneNumber;
#end
DetailIntefaceViewController.m:
#import "DetailInterfaceController.h"
#interface DetailInterfaceController ()
#end
#implementation DetailInterfaceController
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
NSLog(#"%#",context);
self.currentContext = context;
// Configure interface objects here.
}
- (void)willActivate {
// This method is called when watch view controller is about to be visible to user
[super willActivate];
NSLog(#"%# willActivate",self.currentContext);
[self.phoneNumber setText:self.currentContext];
}
- (void)didDeactivate {
// This method is called when watch view controller is no longer visible
[super didDeactivate];
NSLog(#"%# didDeactivate",self.currentContext);
}
#end
Any help would be appreciated.
You don't need that contextsForSegueWithIdentifier method.
After setting up the table, use this method.
- (void)table:(WKInterfaceTable *)table didSelectRowAtIndex:(NSInteger)rowIndex
{
NSString *notes = [[events objectAtIndex:rowIndex] notes];
NSString *title = [[events objectAtIndex:rowIndex] title];
NSString *strippedNumber = [notes stringByReplacingOccurrencesOfString:#"[^0-9]" withString:#"" options:NSRegularExpressionSearch range:NSMakeRange(0, [notes length])];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
NSString *date = [dateFormatter stringFromDate:[[events objectAtIndex:rowIndex] startDate]];
//You can push controller instead of segue like this and sending the variable data as a dictionary in context,
[self pushControllerWithName:#"NibIdentifier" context:[NSDictionary dictionaryWithObjectsAndKeys:notes,#"notes",title,#"title",strippedNumber,#"strippedNumber",date,#"date",nil]];
}
Replace "NibIdentifier" with your specific identifier from storyboard.
Retrieve the data in another controller from context using this,
- (void)awakeWithContext:(id)context
{
[super awakeWithContext:context];
NSLog(#"%#",[context objectForKey:#"key1"]);
NSLog(#"%#",[context objectForKey:#"key2"]);
NSLog(#"%#",[context objectForKey:#"key3"]);
NSLog(#"%#",[context objectForKey:#"key4"]);
}
Related
I have two view controllers(viewController and popUpViewController). When i open app, viewController will load. It has buttons. When i click on button, image will be opened in popUpViewController. Then i click close button on popUpViewController,i am dismissing popUpViewController. My scenario is, when i click button in viewController i have to take opened time and i click close button on popUpViewController, i have to take end time.Both should be stored in an array. But array is showing as nil. I am using the following code.
viewController.m
viewDidLoad:
fileOpenedValues = #[#"",#"" ,#""];
fileOpenedKeys = #[#"File Name",#"STime",#"ETime"];
fileOpened = [[NSMutableDictionary alloc] initWithObjects:fileOpenedValues forKeys:fileOpenedKeys];
[fileOpenedArr addObject:fileOpened];
-(void) storeArrayFromPopUp :(NSString *)fname second:(NSString *)mname third:(NSString *)lname
{
fileOpenedValues = #[fname ,mname ,lname];
fileOpenedKeys = #[#"File Name",#"STime",#"ETime"];
fileOpened = [[NSMutableDictionary alloc] initWithObjects:fileOpenedValues forKeys:fileOpenedKeys];
[fileOpenedArr addObject:fileOpened];
}
popUpViewController.m
[baseObj storeArrayFromPopUp :openedFileName second:fileOpenStime third:fileOpenEtime];
After calling storeArrayFromPopUp. fileOpenedArr is showing as nil.
Please advice.
#implementation ViewController {
NSString *strStartTime;
NSString *strEndTime;
NSMutableArray *arrTime;
}
- (void)viewDidLoad {
[super viewDidLoad];
arrTime = [[NSMutableArray alloc]init];
strStartTime = #"";
strEndTime = #"";
}
-(void)viewWillAppear:(BOOL)animated {
//check start has value means popupview controller was open.
//viewWillAppear always call when you close the popup.
if (strStartTime.length > 0) {
strEndTime = [self getCurrentTime];
NSDictionary *dic = [[NSDictionary alloc]initWithObjectsAndKeys:strStartTime,#"StartTime",strEndTime,#"EndTime", nil];
[arrTime addObject:dic]; //you can have all the start and endtime details in the arrTime array.
strStartTime = #"";
strEndTime = #"";
}
}
-(IBAction)btnShowPopupClicked:(id)sender {
//Set the start time when popup is going to open.
strStartTime = [self getCurrentTime];
[self performSegueWithIdentifier:#"imagePopup" sender:nil];
}
-(NSString *)getCurrentTime {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeZone:[NSTimeZone localTimeZone]];
NSString *currentTime = [dateFormatter stringFromDate:[NSDate date]];
return currentTime;
}
IOS newb here trying to implement an open source calendar control MADay in my project with the data stored in core data. The calendar control does not provide documentation for accessing core data.
I am guessing that I need to create an NSArray after pulling the events from core data that corresponds to the "Events" array below. Is that all? And if so, how do I do that?
Thanks for any suggestions.
Here is the datasource code from the library:
- (NSDate *)nextDayForDate:(NSDate *)date;
- (NSArray *)eventKitEventsForDate:(NSDate *)date;
- (NSArray *)eventKitEventsToMAEvents:(NSArray *)eventKitEvents;
#property (readonly) EKEventStore *eventStore;
#end
#implementation MAEventKitDataSource
- (NSArray *)dayView:(MADayView *)dayView eventsForDate:(NSDate *)date
{
return [self eventKitEventsToMAEvents:[self eventKitEventsForDate:date]];
}
- (NSArray *)weekView:(MAWeekView *)weekView eventsForDate:(NSDate *)date
{
return [self eventKitEventsToMAEvents:[self eventKitEventsForDate:date]];
}
- (EKEventStore *)eventStore
{
if (!_eventStore) {
_eventStore = [[EKEventStore alloc] init];
}
return _eventStore;
}
- (NSDate *)nextDayForDate:(NSDate *)date
{
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:1];
return [CURRENT_CALENDAR dateByAddingComponents:components toDate:date options:0];
}
- (NSArray *)eventKitEventsForDate:(NSDate *)startDate
{
NSPredicate *predicate = [self.eventStore predicateForEventsWithStartDate:startDate
endDate:[self nextDayForDate:startDate]
calendars:nil];
NSArray *events = [self.eventStore eventsMatchingPredicate:predicate];
return events;
}
- (NSArray *)eventKitEventsToMAEvents:(NSArray *)eventKitEvents
{
NSMutableArray *events = [[NSMutableArray alloc] init];
for (EKEvent *event in eventKitEvents) {
MAEvent *maEvent = [[MAEvent alloc] init];
maEvent.title = event.title;
maEvent.start = event.startDate;
maEvent.end = event.endDate;
maEvent.allDay = event.allDay;
maEvent.backgroundColor = [UIColor colorWithCGColor:event.calendar.CGColor];
maEvent.textColor = [UIColor whiteColor];
[events addObject:maEvent];
}
return events;
}
I haven't worked myself with this framework, but as far as I can see from code and the EventKit documentation, it has an own store so it doesn't require a further core-data handling.
https://developer.apple.com/library/prerelease/ios/documentation/EventKit/Reference/EKEventStoreClassRef/index.html#//apple_ref/occ/instm/EKEventStore/saveCalendar:commit:error:
so: read the documentation to understand ;)
I'm having trouble updating my old code which made synchronous JSOn calls to a new one which makes asynchronous calls using AFNetworking.
In my old code I was grouping cells with a UICollectionReusableView using a date string ("release_date"), all of this was done in the viewDidLoad. Now with AFNetworking I moved everything out of the viewDidLoad, so I'm stuck trying to figure out how to merge my old code with my new one.
This is the new code I have to parse my JSON with AFNetworking:
- (void)viewDidLoad
{
[super viewDidLoad];
self.upcomingReleases = [[NSMutableArray alloc] init];
[self makeReleasesRequests];
[self.collectionView registerClass:[ReleaseCell class] forCellWithReuseIdentifier:#"ReleaseCell"];
}
-(void)makeReleasesRequests //AFNetworking Call
{
NSURL *url = [NSURL URLWithString:#"http://www.soleresource.com/upcoming.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"#");
self.upcomingReleases = [responseObject objectForKey:#"upcoming_releases"];
[self.collectionView reloadData];
} failure:nil];
[operation start];
}
Code I had in my viewDidLoad before I started using AFNetworking to make JSON calls and "group" my cells:
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *upcomingReleaseURL = [NSURL URLWithString:#"http://www.soleresource.com/upcoming.json"];
NSData *jsonData = [NSData dataWithContentsOfURL:upcomingReleaseURL];
NSError *error = nil;
NSDictionary *dataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
NSArray *upcomingReleasesArray = [dataDictionary objectForKey:#"upcoming_releases"];
//This is the dateFormatter we'll need to parse the release dates
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
NSTimeZone *est = [NSTimeZone timeZoneWithAbbreviation:#"EST"];
[dateFormatter setTimeZone:est];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"]]; //A bit of an overkill to avoid bugs on different locales
//Temp array where we'll store the unsorted bucket dates
NSMutableArray *unsortedReleaseWeek = [[NSMutableArray alloc] init];
NSMutableDictionary *tmpDict = [[NSMutableDictionary alloc] init];
for (NSDictionary *upcomingReleaseDictionary in upcomingReleasesArray) {
//We find the release date from the string
NSDate *releaseDate = [dateFormatter dateFromString:[upcomingReleaseDictionary objectForKey:#"release_date"]];
//We create a new date that ignores everything that is not the actual day (ignoring stuff like the time of the day)
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components =
[gregorian components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:releaseDate];
//This will represent our releases "bucket"
NSDate *bucket = [gregorian dateFromComponents:components];
//We get the existing objects in the bucket and update it with the latest addition
NSMutableArray *releasesInBucket = [tmpDict objectForKey:bucket];
if (!releasesInBucket){
releasesInBucket = [NSMutableArray array];
[unsortedReleaseWeek addObject:bucket];
}
UpcomingRelease *upcomingRelease = [UpcomingRelease upcomingReleaseWithName:[upcomingReleaseDictionary objectForKey:#"release_name"]];
upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:#"release_date"];
upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:#"release_date"];
[releasesInBucket addObject:upcomingRelease];
[tmpDict setObject:releasesInBucket forKey:bucket];
}
[unsortedReleaseWeek sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSDate* date1 = obj1;
NSDate* date2 = obj2;
//This will sort the dates in ascending order (earlier dates first)
return [date1 compare:date2];
//Use [date2 compare:date1] if you want an descending order
}];
self.releaseWeekDictionary = [NSDictionary dictionaryWithDictionary:tmpDict];
self.releaseWeek = [NSArray arrayWithArray:unsortedReleaseWeek];
[self.collectionView registerClass:[ReleaseCell class] forCellWithReuseIdentifier:#"ReleaseCell"];
}
CollectionViewCell
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"Cell";
ReleaseCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
// Part of my new code AFNetworking
NSDictionary *upcomingReleaseDictionary = [self.upcomingReleases objectAtIndex:indexPath.row];
//
// I had this in my old code
UpcomingRelease *upcomingRelease = [self.releaseWeekDictionary objectForKey:self.releaseWeek[indexPath.section]][indexPath.row];
//
cell.release_name.text = upcomingRelease.release_name;
return cell;
}
This is the rest:
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return [self.releaseWeek count];
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [[self.releaseWeekDictionary objectForKey:self.releaseWeek[section]] count];
}
-(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
ReleaseWeek *releaseWeek = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:#"releaseWeek" forIndexPath:indexPath];
//We tell the formatter to produce a date in the format "Name-of-the-month day"
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MMMM dd"];
NSTimeZone *est = [NSTimeZone timeZoneWithAbbreviation:#"EST"];
[dateFormatter setTimeZone:est];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"]];
//We read the bucket date and feed it to the date formatter
NSDate *releaseWeekDate = self.releaseWeek[indexPath.section];
releaseWeek.releaseDate.text = [[dateFormatter stringFromDate:releaseWeekDate] uppercaseString];
return releaseWeek;
}
I'm basically trying to figure out how to take the code that grouped my cells be the date string and integrate it with my new code.
Thanks.
Think MVC. Separate your model (the stuff that comes from the network) into a separate class that does the network operations, and grouping things into arrays and dictionaries. Your view controller should simply observe the model. You can use delegation or (my favorite) KVO to know when the model has updated data available. Then you just update your collection view. Your view controller should simply be an interface between the model and the views. If you separate things out this way you will find that it is much more natural, and you aren't fighting against the system.
You are closer than you think.
Simply put everything you used to do in viewDidLoad: (everything between the assignment of jsonData to the registration of your collection view class) into the callback block of your AFNetworking call (where jsonData is now called responseObject).
At the end of the callback block, simply invoke [self.collectionView reloadData], and the your collection view will reload itself (i.e. call numberOfItems and cellForItemAtIndexPath for each item).
In your UICollectionViewDataSource methods that return the number of sections and items, simply return 0 if the properties that hold your model are nil or empty.
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
// Correctly returns 0 if nil or empty.
return [self.releaseWeek count];
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
if(!self.releaseWeekDictionary[section] || !self.releaseWeek[section]) {
return 0;
}else{
return [self.releaseWeekDictionary[self.releaseWeek[section]] count];
}
}
Below should be the gravy code in your completion block.
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"#");
NSArray *upcomingReleasesArray = [dataDictionary objectForKey:#"upcoming_releases"];
//This is the dateFormatter we'll need to parse the release dates
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
NSTimeZone *est = [NSTimeZone timeZoneWithAbbreviation:#"EST"];
[dateFormatter setTimeZone:est];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"]]; //A bit of an overkill to avoid bugs on different locales
//Temp array where we'll store the unsorted bucket dates
NSMutableArray *unsortedReleaseWeek = [[NSMutableArray alloc] init];
NSMutableDictionary *tmpDict = [[NSMutableDictionary alloc] init];
for (NSDictionary *upcomingReleaseDictionary in upcomingReleasesArray) {
//We find the release date from the string
NSDate *releaseDate = [dateFormatter dateFromString:[upcomingReleaseDictionary objectForKey:#"release_date"]];
//We create a new date that ignores everything that is not the actual day (ignoring stuff like the time of the day)
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components =
[gregorian components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:releaseDate];
//This will represent our releases "bucket"
NSDate *bucket = [gregorian dateFromComponents:components];
//We get the existing objects in the bucket and update it with the latest addition
NSMutableArray *releasesInBucket = [tmpDict objectForKey:bucket];
if (!releasesInBucket){
releasesInBucket = [NSMutableArray array];
[unsortedReleaseWeek addObject:bucket];
}
UpcomingRelease *upcomingRelease = [UpcomingRelease upcomingReleaseWithName:[upcomingReleaseDictionary objectForKey:#"release_name"]];
upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:#"release_date"];
upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:#"release_date"];
[releasesInBucket addObject:upcomingRelease];
[tmpDict setObject:releasesInBucket forKey:bucket];
}
[unsortedReleaseWeek sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSDate* date1 = obj1;
NSDate* date2 = obj2;
//This will sort the dates in ascending order (earlier dates first)
return [date1 compare:date2];
//Use [date2 compare:date1] if you want an descending order
}];
self.releaseWeekDictionary = [NSDictionary dictionaryWithDictionary:tmpDict];
self.releaseWeek = [NSArray arrayWithArray:unsortedReleaseWeek];
[self.collectionView reloadData];
} failure:nil];
Edit: In your code, you were no longer assigning anything to upcomingReleases (block of commented code lines 91-102), and you were crashing referencing the index that didn't exist in the array. The fix is easy:
109: self.upcomingReleases = [dataDictionary objectForKey:#"upcoming_releases"];
122: for (NSDictionary *upcomingReleaseDictionary in self.upcomingReleases) {
I'm changing my app so it could parse my JSON through AFNetworking and I'm getting an error when I try to change my date string with dateFormatter. I have a NSObject called "UpcomingRelease", a CollectionViewController called "UpcomingReleasesViewController" and a destinationViewController called "ReleaseViewController"
This my old code:
* UpcomingRelease.h
#interface UpcomingRelease : NSObject
#property (nonatomic, strong) NSString *release_date;
- (NSString *) formattedDate;
#end
* UpcomingRelease.m
- (NSString *) formattedDate {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
NSDate *newDate = [dateFormatter dateFromString:self.release_date];
[dateFormatter setDateFormat:#"MMMM dd"];
return [dateFormatter stringFromDate:newDate];
}
* ReleaseViewController.h (destinationViewController)
#import "UpcomingRelease.h"
#property (strong, nonatomic) NSDictionary *singleRelease;
#property (weak, nonatomic) IBOutlet UILabel *release_date;
* ReleaseViewController.m
#synthesize singleRelease = _singleRelease;
#synthesize release_date = _release_date;
- (void)viewDidLoad
{
[super viewDidLoad];
if([_singleRelease objectForKey:#"release_date"] != NULL)
{
self.release_date.text = [NSString stringWithFormat:#"%#", _singleRelease.formattedDate];
}
else
{
self.release_date.text = [NSString stringWithFormat:#"Releasing Soon"];
}
}
I get an error saying "Property 'formattedDate' not found on object of type 'NSDictionary').
The error already says that NSDictionary doesn't have a property called formattedDate. And right now you are calling _singleRelease.formattedDate and _singleRelease is a NSDictionary.
Change your code to the following:
if([_singleRelease objectForKey:#"release_date"] != NULL)
{
NSString *rd = [_singleRelease objectForKey:#"release_date"]; // I assume that this is a string
UpcomingRelease *upcoming = [[UpcomingRelease alloc] init];
upcoming.release_date = rd;
self.release_date.text = [NSString stringWithFormat:#"%#", upcoming.formattedDate];
}
I are trying to make to a stopwatch app and facing problems getting it to work properly. When I start the stopwatch and stop and start off again, it doesn't continue from where it left off. It carries on running as if it didn't stop. Need some guidance on getting it work properly. I have been trying since morning, the rest of the functions i have made as similar to the apple's stopwatch, only this is bugging me.. Appreciate any help...
The code looks like this:
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController<UITableViewDataSource,UITableViewDelegate>{
UILabel *lbl;
NSTimer *stopTimer;
NSDate *startDate,*currentDate;
BOOL running;
UIButton *bttn;
NSMutableArray *tableItems;
NSString *timeString,*currentString;
UITableView *tableview;
int counter;
}
#property (strong,nonatomic) IBOutlet UILabel *lbl;
#property (strong,nonatomic) IBOutlet UIButton *bttn;
#property (strong,nonatomic) NSMutableArray *tableItems;
#property (strong,nonatomic) NSString *timeString;
#property (strong,nonatomic) IBOutlet UITableView *tableview;
-(IBAction)startPressed:(id)sender;
-(IBAction)resetPressed:(id)sender;
-(void)updateTimer;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize lbl,bttn,tableItems,timeString,tableview;
- (void)viewDidLoad
{
[super viewDidLoad];
lbl.text = #"00.00.00.0";
running = FALSE;
//difference = #"0";
startDate = [NSDate date];
tableItems = [[NSMutableArray alloc] init];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(IBAction)startPressed:(id)sender{
if(!running){
running = TRUE;
[sender setTitle:#"Stop" forState:UIControlStateNormal];
[bttn setTitle:#"Lap" forState:UIControlStateNormal];
if (stopTimer == nil) {
stopTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats:YES];
}
}else{
running = FALSE;
//startDate = currentDate;
//difference = currentString;
[sender setTitle:#"Start" forState:UIControlStateNormal];
[bttn setTitle:#"Restart" forState:UIControlStateNormal];
[stopTimer invalidate];
stopTimer = nil;
}
}
-(void)updateTimer{
currentDate = [NSDate date];
NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:startDate];
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm:ss.S"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
timeString=[dateFormatter stringFromDate:timerDate];
lbl.text = timeString;
}
-(IBAction)resetPressed:(id)sender{
if (!running) {
[stopTimer invalidate];
stopTimer = nil;
tableItems = [[NSMutableArray alloc] init];
startDate = [NSDate date];
lbl.text = #"00.00.00.0";
running = FALSE;
}
else{
[tableItems insertObject:timeString atIndex:0];
[tableview reloadData];
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return tableItems.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//Step 1:Check whether if we can reuse a cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
//Step2: If there are no new cells to reuse,create a new one
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:#"cell"];
}
//Step 3: Set the cell text content
cell.textLabel.text = [tableItems objectAtIndex:indexPath.row];
//Step 4: Return the row
return cell;
}
#end
In your updateTimer method you calculate the time difference between startDate and the current date. You don't take into account that you would have to subtract the time that passed while the stopwatch was stopped.
Edit:
I'd suggest to sum up and save all timeIntervals that pass between start and stop
Add a property NSTimeInterval timePassed to your class and modify your code like this:
- (void)viewDidLoad
{
//...
timePassed = 0;
}
-(IBAction)startPressed:(id)sender{
if(!running){
//...
startDate = [NSDate date];
//...
}else{
NSDate *currentDate = [NSDate date];
NSTimeInterval intervalToAdd = [currentDate timeIntervalSinceDate:startDate];
timePassed += intervalToAdd;
//...
}
-(void)updateTimer{
currentDate = [NSDate date];
NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:startDate];
timeInterval += timePassed; // <<<<<
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm:ss.S"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
timeString=[dateFormatter stringFromDate:timerDate];
lbl.text = timeString;
}
-(IBAction)resetPressed:(id)sender{
if (!running) {
//...
timePassed = 0;
//...
}
//...
}
Quoting some code inside my active project.
NSDate *timeStart = [NSDate dateWithTimeIntervalSince1970:0];
NSDate *timeEnd = [timeStart dateByAddingTimeInterval:_timePassed];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"00:00:00"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"GMT"]];
NSString *strDate = [dateFormatter stringFromDate:timeEnd];
_timeLabel.text = strDate;
This is how I did mine. If you want to use UILabel as a stopwatch, MZTimerLabel is a awesome two line solution for you. Take a look.
Cheers.