How to create flash LED in SOS on swift 2? - ios

I tried write small program base on torch/flash in iPhones. Now i want add SOS signal but i have no idea how should do this. In this code when i start program will switch on and off my LED every 0.2 sec. But i don't know how to do this in SOS signal. And when user click SOS ON and click SOS OFF led should be off immediately. Do i need running some Thread ? or on NSTimer ?
class Sos {
var timer1 = NSTimer()
var timer2 = NSTimer()
var volume: Float = 0.1
let flashLight = FlashLight()
func start() {
self.timer1 = NSTimer.scheduledTimerWithTimeInterval(0.2,
target: self,
selector: Selector("switchON"),
userInfo: nil,
repeats: true)
self.timer2 = NSTimer.scheduledTimerWithTimeInterval(0.4,
target: self,
selector: Selector("switchOFF"),
userInfo: nil,
repeats: true)
}
func stop() {
timer1.invalidate()
timer2.invalidate()
flashLight.switchOFF()
}
#objc func switchON() {
flashLight.switchON(self.volume)
}
#objc func switchOFF() {
flashLight.switchOFF()
}
deinit {
self.timer1.invalidate()
self.timer2.invalidate()
}
}

Many ways to achieve it. Create sequence of time intervals and alternate between on/off for example. Comments in code.
///
/// SOS sequence: ...---...
///
/// . short
/// - long
///
class SOS {
/// Short signal duration (LED on)
private static let shortInterval = 0.2
/// Long signal duration (LED on)
private static let longInterval = 0.4
/// Pause between signals (LED off)
private static let pauseInterval = 0.2
/// Pause between the whole SOS sequences (LED off)
private static let sequencePauseInterval = 2.0
/**
When the SOS sequence is started flashlight is on. Thus
the first time interval is for the short signal. Then pause,
then short, ...
See `timerTick()`, it alternates flashlight status (on/off) based
on the current index in this sequence.
*/
private let sequenceIntervals = [
shortInterval, pauseInterval, shortInterval, pauseInterval, shortInterval, pauseInterval,
longInterval, pauseInterval, longInterval, pauseInterval, longInterval, pauseInterval,
shortInterval, pauseInterval, shortInterval, pauseInterval, shortInterval, sequencePauseInterval
]
/// Current index in the SOS `sequence`
private var index: Int = 0
/// Non repeatable timer, because time interval varies
private weak var timer: NSTimer?
/**
Put your `Flashlight()` calls inside this function.
- parameter on: pass `true` to turn it on or `false` to turn it off
*/
private func turnFlashlight(on on: Bool) {
// if on == true -> turn it on
// if on == false -> turn it off
print(on ? "ON" : "OFF")
}
private func scheduleTimer() {
timer = NSTimer.scheduledTimerWithTimeInterval(sequenceIntervals[index],
target: self, selector: "timerTick",
userInfo: nil, repeats: false)
}
#objc private func timerTick() {
// Increase sequence index, at the end?
if ++index == sequenceIntervals.count {
// Start from the beginning
index = 0
}
// Alternate flashlight status based on current index
// index % 2 == 0 -> is index even number? 0, 2, 4, 6, ...
turnFlashlight(on: index % 2 == 0)
scheduleTimer()
}
func start() {
index = 0
turnFlashlight(on: true)
scheduleTimer()
}
func stop() {
timer?.invalidate()
turnFlashlight(on: false)
}
deinit {
timer?.invalidate()
}
}

Related

Wait 50 run loop ticks in iOS

I would like to wait for the run loop to run and the screen to be rendered 50 times before performing an operation.
Is it necessary to use CAMediaTiming and a counter for that? Is there a way to hook into the NSRunLoop directly? Can I achieve this using 50 nested DispatchQueue.async calls like so?
import Dispatch
func wait(ticks: UInt, queue: DispatchQueue = DispatchQueue.main, _ handler: #escaping () -> Void) {
var ticks = ticks
func predicate() {
queue.async {
ticks -= 1
if ticks < 1 {
handler()
return
}
queue.async(execute: predicate)
}
}
predicate()
}
EDIT: if anyone is wondering, the snippet does work and it performs very well when we're talking about apps run loop.
You can use CADisplayLink, which is called every screen update.
let displayLink = CADisplayLink(target: self, selector: #selector(drawFunction(displayLink:)))
displayLink.add(to: .main, forMode: .commonModes)
In drawFunction (or predicate, etc.) we can subtract from ticks. When they reach 0, we've hit the 50th frame and invalidate displayLink.
var ticks = 50
#objc private func drawFunction(displayLink: CADisplayLink) {
doSomething()
ticks -= 1
if ticks == 0 {
displayLink.invalidate()
displayLink = nil
return
}
}
CADisplayLink can also provide the amount of time between frames. A similar discussion can be found here. If you're concerned about absolute accuracy here, you could calculate the time between frames. From the docs:
The duration property provides the amount of time between frames at the maximumFramesPerSecond. To calculate the actual frame duration, use targetTimestamp - timestamp. You can use this value in your application to calculate the frame rate of the display, the approximate time that the next frame will be displayed, and to adjust the drawing behavior so that the next frame is prepared in time to be displayed.
I think better solution to use CADisplayLink instead of Dispatch. Here is an example:
class Waiter {
let handler: () -> Void
let ticksLimit: UInt
var ticks: UInt = 0
var displayLink: CADisplayLink?
init(ticks: UInt, handler: #escaping () -> Void) {
self.handler = handler
self.ticksLimit = ticks
}
func wait() {
createDisplayLink()
}
private func createDisplayLink() {
displayLink = CADisplayLink(target: self,
selector: #selector(step))
displayLink?.add(to: .current,
forMode: RunLoop.Mode.default)
}
#objc private func step(displaylink: CADisplayLink) {
print(ticks)
if ticks >= ticksLimit {
displayLink?.invalidate()
handler()
}
ticks += 1
}
}
Here is an example of usage:
let waiter = Waiter(ticks: 50) {
print("Handled")
}
waiter.wait()

while loop not executing with timer

I'm pretty new to Swift, been practicing for about a week now. I've declared a timer, I call the function of the timer with viewDidLoad(), and the timers #selector points to goldPerSec, that function is a simple while loop, yet it's not executing every second as it should.
Here's my code:
var timer = Timer()
override func viewDidLoad() {
super.viewDidLoad()
counter()
}
func counter() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.goldPerSec), userInfo: nil, repeats: true)
}
#objc func goldPerSec() {
while (totalOwned >= 1) {
Gold += (minerOwned * 1)
goldLabel.text = "\(Gold)"
}
}
Your while loop in goldPerSec runs forever preventing any other code from running on the main queue, including the timer.
Change the while loop to an if statement.
#objc func goldPerSec() {
if totalOwned >= 1 {
Gold += minerOwned * 1
goldLabel.text = "\(Gold)"
}
}
This will now allow goldPerSec to be called every second from the timer and also allow the rest of your user interface to work.
As a side note, variable names should start with lowercase letters so Gold should be named gold.

I'm trying to use NSTimer in a tableview with mutliple timers

When I start a timer, however, it seems to repeatedly run, but it won't decrease the counter. I'm trying to pass the count variable through the counter into the selector, but it seems that the counter resets each time, instead of continuely decreasing. I'm new to programming, so while I'm hoping it's something silly, I might have everything wrong organizationally... my code is:
func timerDidEnd(timer: NSTimer) {
var timeCount = timer.userInfo as! Double
timeCount -= timeInterval
if timeCount <= 0 { //test for target time reached.
print("Timer = 0")
timer.invalidate()
} else { //update the time on the clock if not reached
print("Timer Count: \(timeCount)")
}
extension ViewController: TimerTableViewCellDelegate {
func startTimer(indexPath: NSIndexPath!) {
print("timer \(indexPath.row) button started")
var currentTimer = baseArray[indexPath.row]
currentTimer.timeCount = Double((currentTimer.duration[0] * 60) + (currentTimer.duration[1]) + currentTimer.duration[2])
currentTimer.timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval, target: self, selector: "timerDidEnd:", userInfo: currentTimer.timeCount, repeats: true)
}
func stopTimer(indexPath: NSIndexPath!) {
let currentTimer = baseArray[indexPath.row]
print("Stop Timer")
currentTimer.timer.invalidate()
}
I figured it out- I updated my timer struct to include the proper time count, and this worked while passing in the indexPath as userInfo

Change Label Text at a Time Interval based on Character Count of a String within String Array

I'm trying to have a character count of a String element in an Array (buttonTappedOutput) determine the time before updating a UILabel (outputText.text). Immediately after the label is updated, I'd like the timer to start again based on the next String in the array, updating again and again for all String elements (in order from element 0 to X).
Why it's not working:
Right now, my for/in loop is doing it's thing, the timer is overwritten for each loop, and finally, the final set timer (based on length of the last String in the array) invokes updateOutputText and updates the output text to the last String in the array. I've also tried calling my updateOutputText function within the for/in loop and including a delay in my updateOutputText function, but have the same result. I basically need to "pause" the execution of the for/in after each loop until updateOutputText does it's thing, but is that really the best way to attack this issue? A pause?
Also, am I passing the local variable 'output' the right way (currentOutput = output)? Is there another/better way to access it outside of the for/in?
Here's my setup with the NSTimer:
#IBAction func eastPressed(sender: AnyObject) {
for output in trigger.currentDecisionPoint.buttonTappedOutput {
currentOutput = output
timer = NSTimer.scheduledTimerWithTimeInterval(Double(output.characters.count / 5), target: self, selector: "updateOutputText", userInfo: nil, repeats: false)
}
}
func updateOutputText() {
outputText.text = currentOutput
return
}
With a delay in the updateOutputText function:
#IBAction func eastPressed(sender: AnyObject) {
for output in trigger.currentDecisionPoint.buttonTappedOutput {
currentOutput = output
updateOutputText()
//timer = NSTimer.scheduledTimerWithTimeInterval(Double(output.characters.count), target: self, selector: "updateOutputText", userInfo: nil, repeats: false)
}
}
func updateOutputText() {
delay(Double(currentOutput.characters.count)) { () -> () in
self.outputText.text = currentOutput
}
return
}
func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
The way I would approach it is something like that (didn't test it though):
var index = 0
#IBAction func eastPressed(sender: AnyObject) {
firstOutput = trigger.currentDecisionPoint.buttonTappedOutput[index]
timer = NSTimer.scheduledTimerWithTimeInterval(Double(firstOutput.count / 5), target: self, selector: "updateOutputText", userInfo: nil, repeats: false)
}
func updateOutputText() {
outputText.text = trigger.currentDecisionPoint.buttonTappedOutput[index]
if index < trigger.currentDecisionPoint.buttonTappedOutput.count - 1 {
nextOutput = trigger.currentDecisionPoint.buttonTappedOutput[index + 1]
timer = NSTimer.scheduledTimerWithTimeInterval(Double(nextOutput.count / 5), target: self, selector: "updateOutputText", userInfo: nil, repeats: false)
index++
} else {
index = 0
}
}

make my enemy spawn a little faster when reach a certain score

func updateTimer() {
if Score < 10 {
runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.runBlock(addEnemy), SKAction.waitForDuration(1.0)])))
}else if Score == 10 {
runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.runBlock(addEnemy), SKAction.waitForDuration(0.5)])))
}else if Score == 20 {
runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.runBlock(addEnemy), SKAction.waitForDuration(0.1)])))
}
}
i was trying to make the spawn faster when a player reach a certain score but this code has its error can anyone help me with this please
thank you very much to whoever answers my question
i wanted to make it like this one
func updateTimer() {
if score < 3 {
timerDots = NSTimer.scheduledTimerWithTimeInterval(1.5, target: self, selector: "fireDot", userInfo: nil, repeats: true)
// Dots get generated a bit faster than before after score reaches 3
} else if score == 3 {
timerDots?.invalidate()
timerDots = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "fireDot", userInfo: nil, repeats: true)
// Dots get generated even faster than before after score reaches 12
} else if score == 12 {
timerDots?.invalidate()
timerDots = NSTimer.scheduledTimerWithTimeInterval(0.9, target: self, selector: "fireDot", userInfo: nil, repeats: true)
}
}
It's simpler to do this using an instance variable and the SKScene update: method:
private var score: Int = 0
private var priorEnemyAddTime: CFTimeInterval = 0
private var nextEnemyAddTime: CFTimeInterval {
var waitTime = score < 10 ? 1.0
: score < 20 ? 0.5
: 0.1
return priorEnemyAddTime + waitTime
}
override func update(currentTime: CFTimeInterval) {
if nextEnemyAddTime <= currentTime {
priorEnemyAddTime = currentTime
addEnemy()
}
}
On every frame, you check whether it's been long enough since the last time you added an enemy. If so, you save the current time as the time of last add, and add an enemy.

Resources