I am starting a watch development project. It will basically be starting and stopping several timers on the watch. The initial code works fine on the 42mm Simulator but not on the actual watch. That is, tapping on the Start button begins the WKInterfaceTimer label running. Nothing happens on the physical watch. However, I know the timer is working because if I check Enabled in the InterfaceBuilder the timer starts incrementing as soon as the app launches on the watch. Here is the code for watch app extension.h and .m. The outlet is connected properly in IB. Also, tapping on the Dispatch button enters the Dispatch IBAction and works correctly. I have also tried powering off and powering on the watch. That does not help.
I would really appreciate some help on this.
interface controller.h
/
/
/
/
/
/
#import <WatchKit/WatchKit.h>
#import <Foundation/Foundation.h>
#interface InterfaceController : WKInterfaceController
#property (unsafe_unretained, nonatomic) IBOutlet WKInterfaceTimer *ElapsedTime;
#property (unsafe_unretained, nonatomic) IBOutlet WKInterfaceLabel *dispatchTime;
#property (weak, nonatomic) NSDate *startDate;
#end
/
/
/
interface controller.m
/
/
/
/
#import "InterfaceController.h"
#interface InterfaceController()
#end
#implementation InterfaceController
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
/
}
- (void)willActivate {
/
[super willActivate];
}
- (void)didDeactivate {
/
[super didDeactivate];
}
- (IBAction)Start {
self.startDate = [NSDate date];
[self.ElapsedTime setDate:self.startDate];
[self.ElapsedTime start];
}
- (IBAction)Stop {
[self.ElapsedTime stop];
}
- (IBAction)Dispatch {
NSDate *date =[NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterNoStyle];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
NSString *dateString = [dateFormatter stringFromDate:date];
[self.dispatchTime setText:dateString];
}
#end
Still not sure why the WKInterfaceTimer object doesn't work. Instead, I used a standard NSTimer object and a standard NSDate object. The code works. Note: if you have another page-based storyboard scene that the first scene seques to, you have to add a custom WkInterfaceController object to the next scene and connect your outlets from the interface storyboard to it. Otherwise, the outlets won't connect because the file's owner isn't correct. Also, make sure that you add the custom WkInterfaceController to the watch kit extension target, not the iOS target.
//
// InterfaceController.h
// EMSTimers WatchKit 1 Extension
//
// Created by Nelson Capes on 12/18/15.
// Copyright © 2015 Nelson Capes. All rights reserved.
//
#import <WatchKit/WatchKit.h>
#import <Foundation/Foundation.h>
#interface InterfaceController : WKInterfaceController
#property (weak, nonatomic) IBOutlet WKInterfaceLabel *ElapsedTime;
#property (strong, nonatomic) IBOutlet WKInterfaceLabel *dispatchTime;
#property (strong, nonatomic) NSDate *startDate;
#property (strong, nonatomic) NSDate *dateStarted;
#property (strong, nonatomic) NSTimer *elaspsedTimer;
#end
//
// InterfaceController.m
// EMSTimers WatchKit 1 Extension
//
// Created by Nelson Capes on 12/18/15.
// Copyright © 2015 Nelson Capes. All rights reserved.
//
#import "InterfaceController.h"
#interface InterfaceController()
#end
#implementation InterfaceController
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
// Configure interface objects here.
}
- (IBAction)Start {
NSDate *date =[NSDate date];
self.dateStarted = date;
NSTimeInterval timeInterval = 1.0;
self.elaspsedTimer = [NSTimer scheduledTimerWithTimeInterval:timeInterval target:self selector:#selector(calculateTimer:) userInfo:self.elaspsedTimer repeats:YES];
[self.elaspsedTimer fire];
}
- (IBAction)Stop {
[self.elaspsedTimer invalidate];
}
-(void)calculateTimer:(NSTimer *)theTimer
{
NSTimeInterval interval = [self.dateStarted timeIntervalSinceNow];
interval = (-1 * interval);
int time = round(interval);
div_t h = div(time, 3600); //seconds total, divided by 3600 equals
int hours = h.quot; // hours, divided by 60 equals
div_t m = div(h.rem, 60); // minutes
int minutes = m.quot;
int seconds = m.rem; // and remainder is seconds
NSString *intervalString = [NSString stringWithFormat:#"%02d:%02d:%02d", hours, minutes, seconds];
[self.ElapsedTime setText:intervalString];
}
- (void)willActivate {
// This method is called when watch view controller is about to be visible to user
[super willActivate];
}
- (void)didDeactivate {
// This method is called when watch view controller is no longer visible
[super didDeactivate];
}
#end
Related
My app consists of 3 ViewControllers. ViewController1 consists of a few labels and buttons where the user tries to solve a problem and presses a button. The button takes them to ViewController 2, where they are shown their stats (accuracy, time, etc.). The app then automatically navigates the user back to the game after 2 seconds. This process goes on for 2 minutes. After the 2 minutes is up, I want the app to navigate to a ViewController 3 (Main Menu).
I'm trying to implement a timer that that keeps going even when switching between view controllers. I've implemented (with the help of stackoverflow) a singleton class that has a timer property. However, it's not functioning as required. I've tried printing out values(commented in the code) to see what's going on, but no luck.
Here's my code:
ApplicationManager.m
//
// ApplicationManager.m
#import <Foundation/Foundation.h>
#import "ApplicationManager.h"
#implementation ApplicationManager
static ApplicationManager* appMgr = nil;
+(ApplicationManager*)instance{
#synchronized ([ApplicationManager class]){
if (!appMgr) {
appMgr = [[self alloc]init];
}
return appMgr;
}
return nil;
}
+(id) alloc{
#synchronized([ApplicationManager class]){
NSAssert((appMgr == nil), #"Only one instance of singleton class may be instantiated");
appMgr = [super alloc];
return appMgr;
}
}
+(id) init{
if (!(self == [super init])) {
return nil;
}
return self;
}
#end
ApplicationManager.h
#interface ApplicationManager : NSObject
+(ApplicationManager*) instance;
#property(weak, nonatomic) NSTimer* timer;
#property(weak, nonatomic) NSDate* startDate;
#end
ViewController.m
#import "ViewController.h"
#import "ApplicationManager.h"
NSString* const MAXTRIALTIME = #"00:50.000"; //50 Seconds just for testing purposes
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[[ApplicationManager instance]setstartDate:[NSDate date]];
[self startTimer];
//other functionalities
}
-(void)startTimer{
[[ApplicationManager instance] setTimer:[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTimer) userInfo:nil repeats:YES]];
}
-(void) resetTimer{
[[[ApplicationManager instance] timer]invalidate];
}
-(void)updateTimer{
NSDate *currentTime = [NSDate date];
NSLog(#"Start Date2: %#", [[ApplicationManager instance]startDate]); //PRINTS (null)
NSTimeInterval timerInterval = [currentTime timeIntervalSinceDate:[[ApplicationManager instance]startDate]];
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timerInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"mm:ss.SSS"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
self.finalTime = [dateFormatter stringFromDate:timerDate];
NSLog(#"Time: %#\n",self.finalTime); //PRINTS random numbers
if ([self.finalTime isEqualToString:MAXTRIALTIME]) {
[self resetTimer];
//move to a different view controller
}
}
#end
ViewController.h
extern NSString* const MAXTRIALTIME;
#property (strong, nonatomic) NSString *finalTime;
//other declarations
In your ApplicationController singleton class you are making a call to self in the instance method. Any methods that instantiate a class (i.e. Methods that begin with a +) should never reference self as self will not exist at that point.
You need to do:
if (!appMgr) {
appMgr = [[ApplicationManager alloc] init];
}
This is the reason you are getting NULL when you print the startDate property.
Also, the ApplicationManager singleton is the owner of its timer property so that needs to be (strong). Weak properties are for references to objects that are owned by other classes. Check out ARC stuff.
Lastly the init method should be a - method not a + one and You don't need to override the alloc method as long as you only use the instance method to access your ApplicationManager. If I were you I'd get rid of the alloc and init methods altogether and just use the instance one.
I have an app here that asks for user input for 3 fields. After the 3 fields are entered, they are displayed in a view controller inside of a custom cell. You can add as many of these events as you like. You can then choose to click on an event cell to display the information in a new view controller about that specific event. For some reason, the details for the events I have entered are not showing up at all. I just get a blank controller screen. I can't seem to figure out why, but I thought I had the code correctly implemented in this controller. Does it have to do with my creation of a new Event object "theEvent"?
Here is some of my code (the full project will be linked below):
Full Project: Full Project Link Removed
FinalDetailViewController.h
#import <UIKit/UIKit.h>
#class Event;
#interface FinalDetailViewController : UITableViewController
#property (strong, nonatomic) id detailItem;
#property (strong, nonatomic) Event *event;
#property (weak, nonatomic) IBOutlet UILabel *detailLabel;
#property (weak, nonatomic) IBOutlet UILabel *locationLabel;
#property (weak, nonatomic) IBOutlet UILabel *dateTimeLabel;
#end
FinalDetailViewController.m
#import "FinalDetailViewController.h"
#import "Event.h"
#interface FinalDetailViewController ()
- (void)configureView;
#end
#implementation FinalDetailViewController
#pragma mark - Managing the detail item
- (void)configureView
{
Event *theEvent = self.event;
static NSDateFormatter *formatter = nil;
if (formatter == nil) {
formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"CCCC, MMMM dd, yyyy hh:mm a"];
}
if (theEvent) {
self.detailLabel.text = theEvent.detail;
self.locationLabel.text = theEvent.location;
self.dateTimeLabel.text = [formatter stringFromDate:(NSDate *)theEvent.date];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self configureView];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
It looks like you intended to make the FinalDetailViewController's table be a static table view (since you're making outlets to the labels, which you can only do in a static table). Change the table view to static cells, and it should work.
Okay, I am building an app for iOS and I am having some trouble with getting the current time to display properly within a UILabel.
Here is the code in the ViewController.h file:
#interface ViewController : UIViewController <UIApplicationDelegate>
#property (strong, nonatomic) IBOutlet UILabel *timeLabelStandbyScreen;
-(void)updateLabel;
-(void)updateTime;
#end
#interface updateTime : NSDate
#end
I'm new to Obj-C and I was playing around with it to see if I could fix the problem. Everything is fine in this file it's the .m that has the problems.
ViewController.m:
#interface ViewController ()
#end
#implementation ViewController
-(void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self updateLabel];
}
-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)updateTime
{
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"hh:mm"];
_timeLabelStandbyScreen.text = [dateFormat stringFromDate: [NSDate date]];
[self preformSelector:#selector(updateTime) withObject:self afterDelay:1.0];
}
#end
I'm sure the problem is really simple and I appreciate your help. Thanks!
By commenting out the preformSelector method and adding a NSTimer (like #rocir suggested) it correctly displayed the time.
First off, do you guys know of any good tutorial sites or books to learn how to code for iOS6?
Now to the problem at hand, I have an AppDelegate class and a ViewController class.
I am using the AppDelegate class to find my current location and save it as an NSString.
I have a label in my ViewController class where I want to display my location.
In java I would usually just do something like this
label.text = AppDelegate.myLocationString;
But I am fairly new to objective c and I don't know how to do the equivalent. To be honest I'm not even sure if I am taking the correct approach for this language. Is there another way to approach this problem in objective c?
I've tried searching around for how to do it but I'm not even sure that I'm phrasing my searches correctly so I don't know if I'm searching in the right direction.
Any help that could be offered would be very much appreciated
My code as it is now:
AppDelegate.h
#import <UIKit/UIKit.h>
#import CoreLocation/CoreLocation.h
#class BIDViewController;
#interface BIDAppDelegate : UIResponder <UIApplicationDelegate, CLLocationManagerDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) BIDViewController *viewController;
#property (strong, nonatomic) CLLocationManager *locationManager;
#property (strong, nonatomic) NSString *myPosition;
#end
LocationManager class from AppDelegate.m
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSDate *eventDate = newLocation.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
if (abs(howRecent) < 15) {
//if it is within the last 15 seconds, use it. Else do nothing.
if (newLocation.horizontalAccuracy < 35.0) {
//location seems pretty accurate so we will use it
NSLog(#"latitude: %+.6f, longitude: %+.6f\n", newLocation.coordinate.latitude, newLocation.coordinate.longitude);
NSLog(#"Horizontal accuracy: %f", newLocation.horizontalAccuracy);
int latDegrees = newLocation.coordinate.latitude;
int lonDegrees = newLocation.coordinate.longitude;
double latDecimal = fabs(newLocation.coordinate.latitude - latDegrees);
double lonDecimal = fabs(newLocation.coordinate.longitude - lonDegrees);
int latMinutes = latDecimal * 60;
int lonMinutes = lonDecimal * 60;
double latSeconds = ((latDecimal * 3600) - (latMinutes * 60));
double lonSeconds = ((lonDecimal * 3600) - (lonMinutes * 60));
_myPosition = [NSString stringWithFormat:#"Lat: %d, %d, %1.4f, Lon: %d, %d, %1.4f", latDegrees, latMinutes, latSeconds, lonDegrees, lonMinutes, lonSeconds];
}
}
}
ViewController.h
#import <UIKit/UIKit.h>
#interface BIDViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *positionLabel;
#end
ViewController.m
#import "BIDViewController.h"
#import "BIDAppDelegate.h"
#interface BIDViewController ()
#end
#implementation BIDViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//I have tried setting the label's text here but to no avail so far, I have been
//able to set it as "blah" with the following line:
//
// positionLabel.text = #"blah";
//
//and I have tried things such as the following to populate the label with the
//myPosition variable:
//
// positionLabel.text = _myPosition;
// or
// positionLabel.text = AppDelegate._myPosition;
}
- (void)viewDidUnload
{
[self setPositionLabel:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
To reference the App Delegate from your view controller, first import its definition (which you've ready done):
#import "BIDAppDelegate.h"
and then in code get the delegate:
BIDAppDelegate *appDelegate = (BIDAppDelegate *)[[UIApplication sharedApplication] delegate];
and then get the location from the delegate:
CLLocation *location = appDelegate.locationManager.location;
and then call whatever method you like on the location object:
NSString *descr = location.description;
Or get the pre-calculated position with:
NSString *position = appDelegate.myPosition;
In your ViewController
BIDAppDelegate *appDelegate = (BIDAppDelegate *)[UIApplication sharedApplication].delegate;
[positionLabel setText:appDelegate.myPosition];
How to call a variable from another class?
Assume you are in ClassB
ClassA *aObj=... ; //create an object of ClassA
aObj.propertyName=...;//set the property or ivar
NSString *string=aObj.propertyName;//retrieve it
do you guys know of any good tutorial sites or books to learn how to
code for iOS6?
http://www.lynda.com/Apple-training-tutorials/106-0.html
http://www.raywenderlich.com/tutorials
I'm having a problem getting an NSTimer to work, probably because I don't know how to properly use it (I've tried reading the apple documentation, it didn't help me much). I get a time off of a UIDatePicker and I want the app to call the method when that time is reached. Simple, no? So the code I have is below:
-(IBAction)SetButtonPress:(id)sender{
NSDate *date = [Picker date];
alarmTimer = [[AlarmTimer alloc] init];
[alarmTimer runTimer: Picker.date];
}
A few things now follow standards, but it still doesn't work. It still gives me a time 6 hours ahead, and when it hits that time, it doesn't do anything.
And the code for the RunTimer method of my alarmTimer class is as follows:
-(void)RunTimer: (NSDate *) date
{
NSRunLoop *theLoop = [NSRunLoop currentLoop];
[theLoop addTimer:timer forMode:NSDefaultRunLoopMode];
[timer initWithFireDate:date interval:0 target:self selector:#selector(Play) userInfo: nil repeats:NO];
}
A few things now follow standards, but it still doesn't work. It still gives me a time 6 hours ahead, and when it hits that time, it doesn't do anything.
And just in case it helps, here is the .h and .m files for the view controller:
.h:
//
// Assignment_1ViewController.h
// Assignment 1
//
// Created by Jack Schaible on 11-09-28.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "AlarmTimer.h"
#interface Assignment_1ViewController : UIViewController {
UIButton *SetButton;
UIDatePicker *Picker;
UIButton *CancelButton;
NSString *time;
AlarmTimer *alarmTimer;
}
#property (nonatomic, retain) IBOutlet UIButton *SetButton;
#property (nonatomic, retain) IBOutlet UIDatePicker *Picker;
#property (nonatomic, retain) IBOutlet UIButton *CancelButton;
- (IBAction)SetButtonPress:(id)sender;
- (IBAction)CancelButtonPress:(id)sender;
#end
.m:
//
// Assignment_1ViewController.m
// Assignment 1
//
// Created by Jack Schaible on 11-09-28.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "Assignment_1ViewController.h"
#import "AlarmTimer.h"
#implementation Assignment_1ViewController
#synthesize Picker;
#synthesize CancelButton;
#synthesize SetButton;
- (void)dealloc
{
[SetButton release];
[Picker release];
[CancelButton release];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewDidUnload
{
[self setSetButton:nil];
[self setPicker:nil];
[self setCancelButton:nil];
[super viewDidUnload];
[alarmTimer release];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (IBAction)SetButtonPress:(id)sender {
NSDateFormatter *ndf = [[NSDateFormatter alloc] init];
time = [ndf stringFromDate:self.Picker.date];
alarmTimer = [AlarmTimer alloc];
[alarmTimer init];
NSLog(#"Date is: %#", [ndf dateFromString:time]);
[alarmTimer RunTimer:[ndf dateFromString:time]];
}
- (IBAction)CancelButtonPress:(id)sender {
}
#end
And the code for the AlarmTimer class:
.h:
//
// Timer.h
// Assignment 1
//
// Created by Jack Schaible on 11-09-28.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#interface AlarmTimer : NSObject {
NSString *time;
NSTimer *timer;
AVAudioPlayer *player;
}
-(void) RunTimer: (NSDate *)date;
-(void)Play;
-(void)CancelTimer;
#end
And finally, the .m:
//
// Timer.m
// Assignment 1
//
// Created by Jack Schaible on 11-09-28.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "AlarmTimer.h"
#implementation AlarmTimer
-(id) init
{
if(self == [super init])
{
timer = [NSTimer alloc];
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/Time.mp3", [[NSBundle mainBundle] resourcePath]]];
NSError *error;
player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
}
return self;
}
- (void) RunTimer: (NSDate *)date
{
[timer initWithFireDate:date interval:86400 target:self selector:#selector(Play) userInfo:nil repeats:NO];
NSLog(#"Date is: %#", date);
}
-(void) Play
{
[player play];
UIAlertView *aView = [[UIAlertView alloc] initWithTitle:#"Holy Chimes!" message:#"If you didn't hear that..." delegate:self cancelButtonTitle:#"Okay" otherButtonTitles:nil, nil];
[aView show];
}
-(void)CancelTimer
{
time = nil;
}
#end
Thanks a lot to anyone who can help!
Why do you use NSDateFormatter? try to do it like that:
alarmTimer = [AlarmTimer alloc];
[alarmTimer init];
NSLog(#"Date is: %#", self.Picker.date);
[alarmTimer RunTimer: self.Picker.date];
As a rule, do the +alloc and -init together: Foo *someFoo = [[Foo alloc] init];. Don't separate the calls as you've done with your timer and alarmTimer variables. The reason for this is that sometimes the initializer will return a different pointer than what you get from +alloc, and when that happens you want to be certain to store that new pointer. I don't know if NSTimer ever does that, but if so you'll have all kinds of trouble with your current code.
Don't try to reuse a timer. Once it fires, release it and create a new one when you need to.
What's all that craziness in -SetButtonPress:? You get a date from a date picker, convert it to a string, and then immediately convert that string back to a date... why?
You're using an interval of 24 hours (86400 seconds) for your timer. Are you under the mistaken impression that a timer with a long interval will wake up an app that's in the background? NSTimer relies on the run loop; when the run loop isn't running, the timer isn't working.
It'd be somewhat easier to help you if you'd stick to the usual Objective-C convention and name methods starting with lower-case characters, i.e. -runTimer: instead of -RunTimer:. The same rule applies to instance variables.