I want to make a label blink forever. Simplified version of my code:
func blink (label: UILabel) -> Void {
let text = label.text
label.text = ""
sleep(1)
label.text = text
sleep(2)
print("blinking")
blink(label: label)
}
override func viewDidLoad() {
super.viewDidLoad()
label = getLabel()
blink(label: label)
}
The view never loads, because its waiting for the Blink function to finish, which it never does. How can I make this function work, but not wait for it to complete itself (witch it never will) before the rest of my code can run?
This will run a timer, with a 1 second interval, which will repeat. Just invalidate the timer if you don't want to use it anymore. Ideally call it when the controller is deinitialized.
let timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { (timer) in
blink(label: label)
}
Also instead of using sleep... try using UIView like this:
UIView.animate(withDuration: 1, animations: {
//do animtions
}) { (completed) in
// repeat or finish
}
It is handy to have a separate class for this action, to ensure you will not have some concurrency and memory allocation problems
So you can create some class like this one
class Blink {
typealias BlinkBlock = (Bool) -> ()
private var timer: Timer?
private let interval: TimeInterval
private let blinkBlock: BlinkBlock
private var blinkState: Bool = true {
didSet {
DispatchQueue.main.async {
self.blinkBlock(self.blinkState)
}
}
}
init(interval: TimeInterval, blinkBlock: #escaping BlinkBlock) {
self.blinkBlock = blinkBlock
self.interval = interval
}
deinit {
timer?.invalidate()
}
func startBlinking() {
stopBlinking()
timer = Timer.scheduledTimer(timeInterval: interval,
target: self,
selector: #selector(onTimer),
userInfo: nil,
repeats: true)
blinkState = true
}
func stopBlinking() {
timer?.invalidate()
timer = nil
}
#objc
func onTimer() {
blinkState = !blinkState
}
}
and then in your view controller just
var blink: Blink?
override func viewDidLoad() {
let text = label.text
blink = blink(interval: 1) {
label.text = $0 ? text : ""
print("blinking")
}
blink?.startBlinking()
}
This will not block main thread and will safely invalidate timer on deallocation
You should create class inherited form UILabel. After that you create function repeat reply on timer. Please follow the code:
class CustomLabel: UILabel {
var timer: Timer?
var counter = 0
deinit {
timer?.invalidate()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.fireTimer()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.fireTimer()
}
func fireTimer(){
timer = Timer.scheduledTimer(timeInterval: 1.0,
target: self,
selector: #selector(waittingAnimation),
userInfo: nil,
repeats: true)
}
#objc func waittingAnimation(){
self.isHidden = !(self.isHidden)
}
}
in your viewcontroller you call fireTimer() to execute animation
#IBOutlet weak var customLabel: CustomLabel!
override func viewDidLoad() {
super.viewDidLoad()
self.customLabel.text = "ABC"
self.customLabel.fireTimer()
}
Related
I'm having difficulties attempting to link two timers together. I'm trying to have a timer count down from a specified amount and to have a second timer constantly updating a label on a view controller. However, I end up having the timer that updates the label lagging exactly 1 second behind the first timer in the timer class. Here's what I have for the view controller: (note that this is a condensed version of my code)
class HomeViewController: UIViewController {
#IBOutlet var timeLabel: UILabel!
override func viewDidLoad() {
timeLabel.text = account.deedManager.globalTimer.timerStr
Timer.scheduledTimer(timeInterval: 0.05, target: self, selector: #selector(UpdateTime), userInfo: nil, repeats: true)
}
#objc func UpdateTime() {
self.timeLabel.text = account.deedManager.globalTimer.timerStr
}
}
And here is the Timer class:
class TimerModel: NSObject, NSCoding {
var myTimer: Timer? = Timer()
var timerInterval: TimeInterval = 1.0
var timerEnd: TimeInterval = 0.0
var timerCount: TimeInterval = 86400.0 // 24 hours
var timerStr: String = "TIME"
func StartTimer(time: Double) {
timerCount = time
myTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(UpdateTime), userInfo: nil, repeats: true)
}
#objc func UpdateTime() {
self.timerStr = self.TimerDate(time: self.timerCount)
self.timerCount-=1
print(self.timerStr)
}
func TimerDate(time:TimeInterval) -> String {
let hours = Int(time) / 3600
let minutes = Int(time) / 60 % 60
let seconds = Int(time) % 60
return String(format: "%02i:%02i:%02i", hours, minutes, seconds)
}
}
I've tried to make the first timer a 0.05 interval so that it updates more rapidly than the timer class, but it lags behind exactly a second no matter what interval I put it at. I don't want to put the count down timer inside the view controller as I want the timer global for all view controllers. If you have any ideas, let me know.
First you need to switch the order around so that you are setting the text after you decrement the timerCount:
#objc func UpdateTime() {
self.timerCount-=1
self.timerStr = self.TimerDate(time: self.timerCount)
print(self.timerStr)
}
Then, you can delete the 0.05-second timer because as you said, that doesn't seem to work.
Try using the delegate pattern instead.
protocol TimerModelDelegate {
func timerTextDidChange(_ timer: TimerModel, text: String)
}
And then in TimerModel,
weak var delegate: TimerModelDelegate?
var timerStr: String = "TIME" {
didSet {
delegate?.timerTextDidChange(self, text: timerStr)
}
}
In HomeViewController, do this:
class HomeViewController: UIViewController, TimerModelDelegate {
#IBOutlet var timeLabel: UILabel!
override func viewDidLoad() {
timeLabel.text = account.deedManager.globalTimer.timerStr
account.deedManager.globalTimer.delegate = self
account.deedManager.globalTimer.StartTimer(time: 60)
}
// You don't need the UpdateTime method here
func timerTextDidChange(_ timer: TimerModel, text: String) {
timeLabel.text = text
}
}
You have this function:
#objc func UpdateTime() {
self.timerStr = self.TimerDate(time: self.timerCount)
self.timerCount-=1
print(self.timerStr)
}
Replace it with
#objc func UpdateTime() {
self.timerCount-=1
self.timerStr = self.TimerDate(time: self.timerCount)
print(self.timerStr)
}
Explanation:
You first have to change the value, then display it
Notice the first two lines are swapped. This should fix your issue.
I have a label that I would like to populate with a number of dots, each dot appearing in sequence, separated by 0.1 second
func setUpDots(numberOfDots: Int) {
for dots in 1...numberOfDots {
DispatchQueue.global(qos: .userInteractive).async {
DispatchQueue.main.async {
self.setLabelToDots(numberOfDots: dots)
}
usleep(100000) // wait 0.1 sec between showing each dot
}
}
}
func setLabelToDots(numberOfDots: Int) {
let dots = Array( repeating: ".", count: numberOfDots).joined()
myLabel.text = dots
myLabel.setNeedsDisplay()
}
But all the dots appear on the label at once
What should I do to get the right effect of the dots showing up with the specified delay between them?
Thanks for your feedback.
Basically, your for-loop is doing something similar to...
for dots in 1...numberOfDots {
self.setLabelToDots(numberOfDots: dots)
}
This is because each task is been allowed to execute at the same and the delay is having no effect on when the next one will run.
You "could" use a serial queue or you could use dependent operation queue, but a simpler solution might be to just use a Timer
This will allow you to setup a "delay" between ticks and treat the timer as a kind of pseudo loop, for example
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
let numberOfDots = 10
var dots = 0
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
myLabel.text = ""
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
guard timer == nil else {
return
}
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(tick), userInfo: nil, repeats: true)
}
#objc func tick() {
dots += 1
guard dots <= numberOfDots else {
timer?.invalidate()
timer = nil
dots = 0
return
}
numberOfDots(dots)
}
func numberOfDots(_ numberOfDots: Int) {
// You could just use string consternation instead,
// which would probably be quicker
let dots = Array( repeating: ".", count: numberOfDots).joined()
myLabel.text = dots
myLabel.setNeedsDisplay()
}
}
There are plenty of other examples about, but you should also have a look at the documentation for Timer
I am in the process of writing a Simon style memory game, the phase of the game where the program shows the user the current list of stuff to remember seems to run instantly.
The idea is to step through the list (in the code I have placed 1 of each item as debug data) and change the colour on screen for a set period then move to the next.
I thought using for each item in memory array and then call a simple procedure to check which one it is and then change colour for a set period then back to original.
The code I have added here will work if I put breaks in between the test change colour (grey) and the original colour. But for some reason the timer does not seem too work.
Any ideas ?
import UIKit
import Foundation
var gameisrunning = false
var playererror = false
var memoryArray = [Int]()
var currentScore = 0
var timer = NSTimer()
class ViewController: UIViewController {
#IBAction func startGameButton(sender: UIButton) {
if gameisrunning == false {
gameisrunning = true
memoryArray.append(1) //for debug
memoryArray.append(2) //for debug
memoryArray.append(3) //for debug
memoryArray.append(4) //for debug
print(memoryArray) //for debug
gameStart()
} else {
}
}
//these are to be implemented once i get the showing sequence sorted.
#IBAction func redButton(sender: UIButton) {
}
#IBAction func greenButton(sender: UIButton) {
}
#IBAction func yellowButton(sender: UIButton) {
}
#IBAction func blueButton(sender: UIButton) {
}
#IBOutlet weak var redLabel: UILabel!
#IBOutlet weak var greenLabel: UILabel!
#IBOutlet weak var yellowLabel: UILabel!
#IBOutlet weak var blueLabel: UILabel!
#IBOutlet weak var scoreLabel: UILabel!
func addAnotherItemToMemory () {
// adds another item to the memory
memoryArray.append(Int(arc4random_uniform(4)+1))
}
func gameStart () {
// main body of game
showPlayerTheMemory()
}
func showPlayerTheMemory () {
// go through list and highlight the colors one at a time
for eachItem in memoryArray {
self.showColor(eachItem)
}
}
func pauseForAWhile(length: Double) {
timer = NSTimer.scheduledTimerWithTimeInterval(length, target:self, selector: nil , userInfo: nil, repeats: false)
timer.invalidate()
}
func showColor(buttonItem: Int) {
//check to see which color, change to grey (test color) and back to original after a set time.
if buttonItem == 1 {
self.redLabel.backgroundColor = UIColor.grayColor()
pauseForAWhile(2)
self.redLabel.backgroundColor = UIColor.redColor()
print(buttonItem) //for debug
} else if buttonItem == 2 {
self.greenLabel.backgroundColor = UIColor.grayColor()
pauseForAWhile(2)
greenLabel.backgroundColor = UIColor.greenColor()
print(buttonItem) //for debug
} else if buttonItem == 3 {
self.yellowLabel.backgroundColor = UIColor.grayColor()
pauseForAWhile(2)
yellowLabel.backgroundColor = UIColor.yellowColor()
print(buttonItem) //for debug
} else if buttonItem == 4 {
self.blueLabel.backgroundColor = UIColor.grayColor()
pauseForAWhile(2)
blueLabel.backgroundColor = UIColor.blueColor()
print(buttonItem) //for debug
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
New relevant code changed to :
func colorChange (){
self.redLabel.backgroundColor = UIColor.redColor()
self.blueLabel.backgroundColor = UIColor.blueColor()
self.yellowLabel.backgroundColor = UIColor.yellowColor()
self.greenLabel.backgroundColor = UIColor.greenColor()
}
func showColor(buttonItem: Int, length: Double) {
//check to see which color, change to grey (test color) and back to original after a set time.
if buttonItem == 1 {
self.redLabel.backgroundColor = UIColor.grayColor()
timer = NSTimer.scheduledTimerWithTimeInterval(length, target:self, selector: ("colorChange") , userInfo: nil, repeats: false)
print(buttonItem) //for debug
} else if buttonItem == 2 {
self.greenLabel.backgroundColor = UIColor.grayColor()
timer = NSTimer.scheduledTimerWithTimeInterval(length, target:self, selector: ("colorChange") , userInfo: nil, repeats: false)
print(buttonItem) //for debug
} else if buttonItem == 3 {
self.yellowLabel.backgroundColor = UIColor.grayColor()
timer = NSTimer.scheduledTimerWithTimeInterval(length, target:self, selector: ("colorChange") , userInfo: nil, repeats: false)
print(buttonItem) //for debug
} else if buttonItem == 4 {
self.blueLabel.backgroundColor = UIColor.grayColor()
timer = NSTimer.scheduledTimerWithTimeInterval(length, target:self, selector: ("colorChange") , userInfo: nil, repeats: false)
print(buttonItem) //for debug
}
}
I have been scratching head all day trying to solve this issue which is baffling me. I have copied the new latest code in below, please discard code above.
I have four labels coloured red blue green and yellow. The array which has test data of 4 3 2 1 inside needs to step through each item - change the colour of the label for x secs then return it to normal colour. I have tried NSTimer, I have tried the current delay as in the code attached. Am I missing something as to where I place the code - should it be under viewdidload ??? I have tried for loops and the current code example shows switch in case it acted differently - it didnt !!
What happens basically is simultaneously all labels go grey (test colour right now) and then all go original colour after the x sec delay.
I need some help before I go insane. I honestly know it is something basic but I just cannot figure it out.
import UIKit
import Foundation
var gameisrunning = false
var playererror = false
var memoryArray = [Int]()
var currentScore = 0
class ViewController: UIViewController {
#IBAction func startGameButton(sender: UIButton) {
if gameisrunning == false {
gameisrunning = true
memoryArray.append(4) //for debug
memoryArray.append(3) //for debug
memoryArray.append(2) //for debug
memoryArray.append(1) //for debug
print(memoryArray) //for debug
gameStart()
} else {
}
}
//these are to be implemented once i get the showing sequence sorted.
#IBAction func redButton(sender: UIButton) {
}
#IBAction func greenButton(sender: UIButton) {
}
#IBAction func yellowButton(sender: UIButton) {
}
#IBAction func blueButton(sender: UIButton) {
}
#IBOutlet weak var redLabel: UILabel!
#IBOutlet weak var greenLabel: UILabel!
#IBOutlet weak var yellowLabel: UILabel!
#IBOutlet weak var blueLabel: UILabel!
#IBOutlet weak var scoreLabel: UILabel!
func addAnotherItemToMemory () {
// adds another item to the memory
memoryArray.append(Int(arc4random_uniform(4)+1))
}
func gameStart () {
// main body of game
showPlayerTheMemory()
}
func delayProg (){
//attempt 100093287492 to get a delay in program
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2.0 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in
self.blueLabel.backgroundColor = UIColor.blueColor()
self.yellowLabel.backgroundColor = UIColor.yellowColor()
self.greenLabel.backgroundColor = UIColor.greenColor()
self.redLabel.backgroundColor = UIColor.redColor()
}
}
func showPlayerTheMemory () {
// go through list and highlight the colors one at a time
for var i=0; i <= memoryArray.count-1; i++ {
self.showColor(memoryArray[i])
}
}
func showColor(buttonItem: Int) {
//check to see which color, change to grey (test color) and back to original after a set time.
switch (buttonItem) {
case 1:
self.redLabel.backgroundColor = UIColor.grayColor()
delayProg()
print(buttonItem) //for debug
case 2:
self.greenLabel.backgroundColor = UIColor.grayColor()
delayProg()
print(buttonItem) //for debug
case 3:
self.yellowLabel.backgroundColor = UIColor.grayColor()
delayProg()
print(buttonItem) //for debug
case 4:
self.blueLabel.backgroundColor = UIColor.grayColor()
delayProg()
print(buttonItem) //for debug
default:
print("error")
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Here is an example of proper implementation of NSTimer()
var myTimer = NSTimer()
func startTimer() {
myTimer = NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector: "myFunction", userInfo: nil, repeats: true)
}
func myFunction() {
myTimer.invalidate()
//do other stuff
}
//the selector is "myFunction", this will be the name of the function that you wish to call every time the timer reaches its specified intervl
//the interval in this case is 10 seconds. In my experience NSTimer is good down to the second but is not precise enough beyond that
//repeats: true ... this will tell the timer to repeat its action consistently firing the selector each time the given time interval is reached. If repeat is set to false then the timer only fires once
//use myTimer.invalidate to stop the timer and to stop calling the selector.
be sure to invalidate your timer or set repeats: false to make sure it doesn't go forever. Make sure your selector is spelled exactly the same as your function. if your function is func myFunction() then the selector should be "myFunction". Make sure you specify a valid time interval, which is taken as seconds.
I want to add a score to the top of my scene in the game I am working on. The score is going to based on how long you last, and will increase every second. Thanks for the help in advance!
import SpriteKit
class easyScene: SKScene {
let scrollBarEasyBottom = SKSpriteNode(imageNamed: "scrollBarEasyBottom")
let scrollBarEasyTop = SKSpriteNode(imageNamed: "scrollBarEasyTop")
let ball = SKSpriteNode(imageNamed: "ball")
var origSBEBpositionX = CGFloat(0)
var origSBETpositionX = CGFloat(0)
var maxSBEBX = CGFloat(0)
var SBEBSpeed = 5
var maxSBETX = CGFloat(0)
var SBETSpeed = 5
var score = 0
var timer: NSTimer?
var scoreText = SKLabelNode(fontNamed: "Kailasa")
override func didMoveToView(view: SKView) {
println("Easy Scene is the location")
self.backgroundColor = UIColor.blackColor()
self.scrollBarEasyBottom.position = CGPoint(x:0, y:270)
self.addChild(self.scrollBarEasyBottom)
self.scrollBarEasyBottom.yScale = 0.2
self.origSBEBpositionX = self.scrollBarEasyBottom.position.x
// end scrollBarEasyBottom
self.scrollBarEasyTop.position = CGPoint(x:20, y:400)
self.addChild(self.scrollBarEasyTop)
self.scrollBarEasyTop.yScale = 0.2
self.origSBETpositionX = self.scrollBarEasyTop.position.x
// end scrollBarEasyTop
self.ball.position = CGPoint(x:40, y:293)
self.addChild(self.ball)
self.ball.yScale = 0.17
self.ball.xScale = 0.17
// end ball
self.maxSBEBX = self.scrollBarEasyBottom.size.width - self.frame.size.width
self.maxSBEBX *= -1
self.maxSBETX = self.scrollBarEasyTop.size.width - self.frame.size.width
self.maxSBETX *= -1
//
self.scoreText.text = "0"
self.scoreText.fontSize = 60
self.scoreText.position = CGPoint(x: CGRectGetMidX(self.frame), y: 500)
self.scoreText.text = String(self.score)
self.addChild(self.scoreText)
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("scoreIncrease") , userInfo: nil, repeats: true)
func scoreIncrease (){
score++
println(score)
}
}
override func update(currentTime: NSTimeInterval) {
if self.scrollBarEasyBottom.position.x <= maxSBEBX + 1200 {
self.scrollBarEasyBottom.position.x = self.origSBEBpositionX
}
if self.scrollBarEasyTop.position.x <= maxSBETX + 1200 {
self.scrollBarEasyTop.position.x = self.origSBETpositionX
}
scrollBarEasyBottom.position.x -= CGFloat(self.SBEBSpeed)
scrollBarEasyTop.position.x -= CGFloat(self.SBETSpeed)
// moving bars
var degreeRotation = CDouble(self.SBEBSpeed) * M_PI / 180
self.ball.zRotation -= CGFloat(degreeRotation)
//rotate ball
}
}
After running this code, I always get an
unrecognized selector sent to instance error
You can use one like this:
var timer = NSTimer()
override func viewDidLoad() {
scheduledTimerWithTimeInterval()
}
func scheduledTimerWithTimeInterval(){
// Scheduling timer to Call the function "updateCounting" with the interval of 1 seconds
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateCounting"), userInfo: nil, repeats: true)
}
func updateCounting(){
NSLog("counting..")
}
Swift 3:
var timer = Timer()
override func viewDidLoad() { // Use for the app's interface
scheduledTimerWithTimeInterval()
}
override func didMove(to view: SKView) { // As part of a game
scheduledTimerWithTimeInterval()
}
func scheduledTimerWithTimeInterval(){
// Scheduling timer to Call the function "updateCounting" with the interval of 1 seconds
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateCounting), userInfo: nil, repeats: true)
}
#objc func updateCounting(){
NSLog("counting..")
}
Swift 5:
Note: this solution is compatible with iOS 10.0+.
// If needing to check for iOS compatibility use
// if #available(iOS 10.0, *) {code}
var timer = Timer()
override func viewDidLoad() {
self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in
updateCounting()
})
}
func updateCounting(){
print("counting...")
}
You can then invalidate (stop) the timer using:
timer.invalidate()
There is something called NSTimer in swift which could solve your problem. I have given an example like how you can use it. Just customise it for your purpose.
var timer = NSTimer.scheduledTimerWithTimeInterval(1.0,
target: self,
selector: Selector("yourMethodToCall"),
userInfo: nil,
repeats: true)
Add this line to the place where you need to call your function repeatedly.
The 1.0 refers to 1 second.
Change the selector to call yourMethodName
repeats is set to true to call that function every second.
Try this out and let me know if your are stuck somewhere. Thanks.
Swift 3
find this solution it worked for me
weak var timer: Timer?
var timerDispatchSourceTimer : DispatchSourceTimer?
func startTimer() {
if #available(iOS 10.0, *) {
timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { [weak self] _ in
// do something here
}
} else {
// Fallback on earlier versions
timerDispatchSourceTimer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main)
timerDispatchSourceTimer?.scheduleRepeating(deadline: .now(), interval: .seconds(60))
timerDispatchSourceTimer?.setEventHandler{
// do something here
}
timerDispatchSourceTimer?.resume()
}
}
func stopTimer() {
timer?.invalidate()
//timerDispatchSourceTimer?.suspend() // if you want to suspend timer
timerDispatchSourceTimer?.cancel()
}
// if appropriate, make sure to stop your timer in `deinit`
deinit {
stopTimer()
}
I prefer
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (timer) in
// Do what you need to do repeatedly
}
}
To stop it:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if timer != nil {
timer?.invalidate()
timer = nil
}
}
Xcode 10.2 Swift 5:
override func viewDidLoad() {
super.viewDidLoad()
// ...
Timer.scheduledTimer(timeInterval: 8.0, target: self, selector: Selector(("your #obcj func name")), userInfo: nil, repeats: true)
}
//Anywhere in the same view controller to stop the loop:
Timer.cancelPreviousPerformRequests(withTarget: your #obcj func name())
I don't think you need NSTimer for this.
Since you are using SpriteKit, I am going to suggest simplest solution in my opinion:
Declare a variable var prevScoreCalcTime:TimeInterval = 0
Inside of update func in your GameScene set it up like below:
override func update(_ currentTime: TimeInterval) {
if currentTime - prevScoreCalcTime > 1 {
prevScoreCalcTime = currentTime
// Any function you put here will execute every second
}
}
Good luck!
// For running a piece of code every second
///Runs every second, to cancel use: timer.invalidate()
#discardableResult public static func runThisEvery(
seconds: TimeInterval,
startAfterSeconds: TimeInterval,
handler: #escaping (CFRunLoopTimer?) -> Void) -> Timer {
let fireDate = startAfterSeconds + CFAbsoluteTimeGetCurrent()
let timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, fireDate, seconds, 0, 0, handler)
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, CFRunLoopMode.commonModes)
return timer!
}
override func viewDidLoad() {
super.viewDidLoad()
fadeBackground()
NSTimer.scheduledTimerWithTimeInterval(self.fadeTime, target: self, selector: Selector("fadeBackground"), userInfo: nil, repeats: true)
}
func fadeBackground(){
var spiralView: UIImageView?
var subviews = self.view.subviews
for v in subviews{
if v.isKindOfClass(UIImageView){
spiralView = v as? UIImageView
}
}
UIView.animateWithDuration(self.fadeTime, delay: 0, options: UIViewAnimationOptions.AllowUserInteraction, animations: { [unowned self] () -> Void in
var randomIndex = Int(arc4random_uniform(UInt32(CONSTANTS.MainColorScheme.count)))
var randomIndex2 = Int(arc4random_uniform(UInt32(CONSTANTS.MainColorScheme.count)))
self.view.backgroundColor = CONSTANTS.MainColorScheme[randomIndex]
if spiralView != nil {
spiralView!.tintColor = CONSTANTS.MainColorScheme[randomIndex2]
}
}) { (stuff Bool) -> Void in
}
}
As you can see, I already have [unowned self] in the closure. I'm not sure why this view controller is still not calling deinit(). I guess there's still a memory leak somewhere?
Do I need to stop the timer on viewDidDisappear? How do I do that?
Solved my question.
I had to invalidate my timer on viewWillDisappear.
var fader: NSTimer?
override func viewWillDisappear(animated: Bool) {
println("lobby viewWillDisappear")
super.viewWillDisappear(animated)
self.fader!.invalidate()
self.fader = nil
}