Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I am looking for a way to write a for loop that iterates over an NSDate. Each loop should increment the NSDate by 10 seconds.
I want to have 2 timestamps. Say timestamp A is Midnight Monday and timestamp B is Midnight Tuesday.
What I then want is some code to say for A to B incrementing at 10 second intervals between the two points in time, use the timestamp at current position, and the timestamp at the last position, so I can run a query based on the intervals.
Would someone be so good as to show me how I would do this?
Many thanks
A for loop needs three parts, an initialisation, a compare, and an increment. It could look like this:
for (NSDate *date = startDate; // initialisation
[date compare:endDate] == NSOrderedAscending; // compare
date = [date dateByAddingTimeInterval:10]) // increment
{
// do something with date here, eg:
NSDate *rangeStart = date;
NSDate *rangeEnd = [date dateByAddingTimeInterval:10];
[runQuery begin:rangeStart end:rangeEnd];
}
You might prefer to refactor to use a while loop so that the dateByAddingTimeInterval doesn't need to be repeated.
This is the same structure as a normal for loop:
for (int i = 0; // initialisation
i < 10; // compare
i++) // increment
{
// do something with i here
}
It sounds like you may want to look into NSTimer's timerWithTimeInterval. The interface looks like this:
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats
and then you can start one ten seconds later using:
initWithFireDate:interval:target:selector:userInfo:repeats:
You can sign up for a call that is sent every seconds as shown below:
// SomeClass.m
#import "SomeClass.h"
#interface ()
#property (nonatomic, strong) NSTimer timer1;
#property (nonatomic, strong) NSTimer timer2;
#end
#implementation SomeClass
- (id)init
{
self = [super init];
if (self) {
NSDate* date = [[NSDate date] dateByAddingTimeInterval:10];
self.timer1 = [NSTimer timerWithTimeInterval:10 target:self selector:#selector(timerFireMethod:) userInfo:nil repeats:YES];
self.timer2 = [[NSTimer alloc] initWithFireDate:date interval:10 target:self selector:#selector(timerFireMethod:) userInfo:nil repeats:YES];
}
return self;
}
- (void)timerFireMethod:(NSTimer *)timer
{
if (timer == self.timer1) {
NSLog(#"timer1 fired");
}
}
#end
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
There are 10 pages that I want to turn at different time intervals. I need to create 10 timers for flip? Or can I make an array intervals and to insert them to the timer? Сan I do it in a simpler way?
There are a lot of ways to do this. Here's one:
This is possibly a good solution if your intervals are irregular and far apart. If they are more evenly spaced (e.g. first is two seconds, next is four seconds) then you could simplify this design. But, I'm assuming that you just want to be able to pass in an array of intervals and then let the code take care of it all.
For this implementation, we're going to need an instance variable to hold an array of timers. Use a property if you prefer them to instance variables.
#implementation ViewController {
NSArray *_multipleTimerInstances;
}
Then some code to handle the timings.
- (void)multipleTimers:(NSArray *)durations {
// Get rid of any timers that have already been created.
[self mulitpleTimerStopAll];
NSMutableArray *newTimers = [NSMutableArray new];
// Create the new timers.
[durations enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// Each element of the durations array specifies the flip interval in seconds.
double duration = [(NSNumber *)obj doubleValue];
// Schedule it.
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:duration
target:self
selector:#selector(multipleTimersFlip:)
userInfo:[NSNumber numberWithInteger:idx]
repeats:YES];
// Keep a reference to the new timer so we can invalidate it later, if we want.
[newTimers addObject:timer];
}];
// We now have a new list of timers.
_multipleTimerInstances = [newTimers copy];
}
- (void)multipleTimersFlip:(NSTimer *)timer {
NSInteger index = [timer.userInfo integerValue];
// The integer index corresponds to the index of the timer in the array passed
// to the multipleTimers method.
NSLog(#"Flip page for timer with index: %ld", (long)index);
}
- (void)mulitpleTimerStopAll {
// Get rid of any timers that have already been created.
for (NSTimer *timer in _multipleTimerInstances) [timer invalidate];
// And nil out the array, they're gone.
_multipleTimerInstances = nil;
}
Then you can invoke the timers like this:
[self multipleTimers:#[ #5.0, #10.0, #7.5 ]];
That's going to call back the multipleTimersFlip method every 5, 7.5, and 10 seconds.
Remember to call multipleTimersStopAll to stop all this going on when you don't need it.
I am creating a simple drum machine. This function controls the time between each sample that is played (thus controlling the tempo of the drum machine). I need to control the tempo with a slider, so I'm hoping to be able to control the 'time duration until next step' value with this if possible. However, when I have tried to do this, it tells me "time is part of NSDate"
-(void)run
{
#autoreleasepool
{
// get current time
NSDate* time = [NSDate date];
// keeping going around the while loop if the sequencer is running
while (self.running)
{
// sleep until the next step is due
[NSThread sleepUntilDate:time];
// update step
int step = self.step + 1;
// wrap around if we reached NUMSTEPS
if (step >= NUMSTEPS)
step = 0;
// store
self.step = step;
// time duration until next step
time = [time dateByAddingTimeInterval:0.5];
}
// exit thread
[NSThread exit];
}
}
This tells me NSTimeInterval is an incompatable type
// time duration until next step
time = [time dateByAddingTimeInterval: self.tempoControls];
Here is where the slider is declared
.m
- (IBAction)sliderMoved:(UISlider *)sender
{
AppDelegate* app = [[UIApplication sharedApplication] delegate];
if (sender == self.tempoSlider)
{
PAEControl* tempoControl = app.tempoControls[app.editIndex];
tempoControl.value = self.tempoSlider.value;
}
}
.h
#interface DetailController : UIViewController
#property (weak, nonatomic) IBOutlet UISlider *tempoSlider;
- (IBAction)sliderMoved:(UISlider *)sender;
Any help would me much appriciated, thanks in advance.
It looks like self.tempoControls is an array of PAEControl objects. The method named dateByAddingTimeInterval: needs an argument of type NSTimeInterval (aka double). It looks like you're trying to pass in this array instead.
Try changing this line -
time = [time dateByAddingTimeInterval: self.tempoControls];
To maybe this -
PAEControl* tempoControl = self.tempoControls[self.editIndex];
time = [time dateByAddingTimeInterval: (NSTimeInterval)tempoControl.value];
On another note, if this is all running on the main thread, be aware that you are blocking it and the UI will become very unresponsive.
This question already has answers here:
Non repeating random numbers in Objective-C
(6 answers)
Closed 7 years ago.
I'm pretty new to coding, I've been looking for similar questions but none fits my needs. I'm working in a dice rolling app, and I need a a random number generator to "roll" the dice. arc4random seems perfect, but the problems that I can't have the same face occurring twice in a row. I have a method firing when I press the button with a timer
- (IBAction)dieRoll:(id)sender {
self.currentFace = 1;
_timer = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:#selector(roll) userInfo:nil repeats:YES];;
}
but I have to implement the 'roll' method where I get a random number different from the one already selected (the property self.currentFace).
any clue?
this is how your implementation could look like:
#interface ViewController ()
#property (assign, nonatomic) NSInteger currentFace;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.currentFace = -1;
}
- (IBAction)diceRoll:(id)sender {
NSInteger newFace = -1;
do {
newFace = arc4random_uniform(6) + 1;
} while (newFace == self.currentFace);
self.currentFace = newFace;
}
#end
A simple solution would be to just remember the last rolled number and roll the dice until you get a different one. Pretty simple and you can keep arc4random.
An example:
- (NSUInteger)rollDiceWithLastFaceNumber:(NSUInteger)lastFaceNumber
{
NSUInteger currentFaceNumber;
do {
currentFaceNumber = (arc4random_uniform(6) + 1);
} while (currentFaceNumber == lastFaceNumber);
return currentFaceNumber;
}
and how to use it:
[self rollDiceWithLastFaceNumber:3];
This solution avoids an unknown number of iterations until you get your result.
- (void)roll {
NSUInteger next = [self nextFaceWithPreviousFace:self.currentFace];
NSLog(#"%lu", next);
self.currentFace = next;
}
- (NSUInteger)nextFaceWithPreviousFace:(NSUInteger)previous {
NSMutableArray *candidates = [NSMutableArray arrayWithObjects:#1, #2, #3, #4, #5, #6, nil];
[candidates removeObject:#(previous)];
NSUInteger index = arc4random_uniform((unsigned)candidates.count);
return [candidates[index] unsignedIntegerValue];
}
In my iOS app I have to create a Digital timer (Not the system Time) in the HH:MM:SS format which should start from 00:00:00 on tap of a button, can I make use of any standard library to do so? Or I should write my own logic?
So one thing you can do is to create a timer and remember time in which you created it.
#IBAction func buttonTapped() {
// Store date / time in which you tapped the button
self.initialDate = NSDate()
// Create timer that fires every second starting now (scheduled), and repeats
self.timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("timerTick"), userInfo: nil, repeats: true)
}
Then, when you have initial stuff, you can do timerTick method. In here, you get current date, do difference between your stored one and current and present it:
func timerTick() {
// Get calendar and components of the dates in interval <initialDate, currentDate>
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.CalendarUnitSecond | .CalendarUnitMinute | .CalendarUnitHour, fromDate: self.initialDate, toDate: NSDate(), options: NSCalendarOptions.allZeros)
// In this point you have minutes, seconds and hours, you can just present it
// "%02d:%02d:%02d" in format means "number, always at least 2 numbers, fill with zeroes if needed")
self.label.text = String(format: "%02d:%02d:%02d", components.hour, components.minute, components.second)
}
If you want to stop the timer, you can do it by calling self.timer.invalidate()
Hope it helps!
The iOS Foundation framework includes the NSDateFormatter class (and the NSDate data type) which does just this.
In your .m file add these properties:
#import "MyVC.h"
#interface MyVC()
#property (strong, nonatomic) NSTimer* timer; // our timer
#property (nonatomic) NSInteger secondsPassed; // how many seconds have been passed since the start of the timer
#end
In viewDidLoad or in the IBAction method of your UIButton:
- (void)viewDidLoad {
[super viewDidLoad];
self.myLabel.text = #"00:00:00"; // start text
// invoke updateTimer every second
self.timer = [NSTimer scheduledTimerWithTimeInterval: 1.0
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats: YES];
}
This method will be called every second to update the UILabel
-(void) updateTimer{
NSInteger hours, minutes, seconds;
// increase the passed seconds
self.secondsPassed++;
// calculate the hours, minutes, seconds from the total number of seconds
hours = self.secondsPassed / 3600;
minutes = ( self.secondsPassed % 3600) / 60;
seconds = ( self.secondsPassed %3600) % 60;
// update the label with the time
self.myLabel.text = [NSString stringWithFormat:#"%02zd:%02zd:%02zd", hours, minutes, seconds];
}
alternatively you can use MZTimerLabel
I'm starting out my iOS experience with a project that incorporates a large clock as a portion of the user interface. When I try to incorporate a user-adjustable toggle to switch from 12-hr to 24-hr time format, I get issues. After the initial user change of the on-screen toggle, the displayed time "flickers" between the 2 formats.
My approach:
viewDidLoad sets the attribute used for 12-hr time format and calls the startTimer method
startTimer method: NSDateFormatter is set. NSTimer is initiated with scheduled interval, passing the DateFormatter to the upDateTime method
updateTimer method checks current time and sets the on-screen
If the user, clicks the on-screen 12-hr/24-hr segmented control, the IBAction invalidates the timer and passes the desired time format attribute to the startTimer method
Testing/Observations:
Code below shows an approach where I modified the time format by setting the Locale and passing it to the DateFormatter. I thought this might be an issue conflicting with the device Locale. I tried using a custom Date Formatter (example #"hh:mm a") instead and had the same flickering issue.
NSTimer scheduled interval shows as 1 sec. I have this for testing, but saw same behavior at 0.1 sec.
NSLog calls within the updateTime method show that the DateFormatter object id and the displayed time are changing between successive loops, even though the user did not adjust the toggle switch.
I suspect this may have something to do with how the Timer is initiated and stopped. Perhaps the old "calls" with the previous format is still running in the background? I experimented with moving the [updateTimer invalidate] to a couple of places in the methods without success
Root Question:
Any suggestions or better approach to having an on-screen clock with a user switch for 12-hr vs. 24-hr time format? Any reason why the displayed time format keeps cycling?
Code:
- (void)startTimer:(NSString *)displayedClockMode {
// using locale within formatter overrides device system behavior chosen by user
NSString *localeValue = nil;
NSDateFormatter *timeFormatter = [[NSDateFormatter alloc] init];
if([displayedClockMode isEqual:#"12-hr"]){
//then 12 hr format - based on US locale
localeValue = #"en_US_POSIX";
//[timeFormatter setDateFormat:#"hh:mm a"];
}
else { //assume no other value exists
// 24 hr format - based on GB locale
localeValue = #"en_GB";
//[timeFormatter setDateFormat:#"HH:mm"];
}
NSLocale *clockLocale = [NSLocale localeWithLocaleIdentifier:localeValue];
[timeFormatter setLocale:clockLocale];
[timeFormatter setTimeStyle:NSDateFormatterMediumStyle];
//stop timer before re-starting with new format
[self.updateTimer invalidate];
self.updateTimer = nil;
NSTimer *updateTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTime:) userInfo:timeFormatter repeats:YES];
}
- (void)updateTime:(NSTimer *)updateTimer {
NSDate *currentTime = [NSDate date];
NSLog(#"old display time: %#",self.displayedTime.text);
self.displayedTime.text = [updateTimer.userInfo stringFromDate:currentTime];
NSLog(#"new display time: %#",self.displayedTime.text);
NSLog(#"new timeformatter: %#",updateTimer.userInfo);
}
- (IBAction)displayedTimeMode:(id)sender {
[self.updateTimer invalidate];
self.updateTimer = nil;
NSString *timeFormat = nil;
if(self.displayedTimeToggle.selectedSegmentIndex == 0){
//if 0, then 12 hr format
timeFormat = #"12-hr";
}
else {
// is 1, 24 hr format
timeFormat = #"24-hr";
}
[self startTimer:timeFormat];
}
The problem is that you have two different updateTimers -- self.updateTimer the class variable and updateTimer the local variable. You're invalidating the class variable, but initializing and running multiple local NSTimers with different locales during each call to startTimer. That's why you see this "flickering" -- it's because multiple NSTimers are setting the label using different localeValues.
To fix this, change:
NSTimer *updateTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTime:) userInfo:timeFormatter repeats:YES];
to
self.updateTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTime:) userInfo:timeFormatter repeats:YES];