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!
}
Related
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()
}
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.
While coming to a view I call a function to load a timer like so...
var count = 10
func startTimer() {
timer = Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
}
and update function is given as..
#objc func update() {
while (count != 0) {
count -= 1
countdownLabel.text = "\(count)"
}
timer.invalidate()
}
But what happens is when I come to this view, straightaway the number 0 is shown as opposed to ideally displaying all numbers in the sequence 9,8,7,6,5,4,3,2,1,0
What am I doing wrong here..?
Swift 4:
var totalTime = 10
var countdownTimer: Timer!
#IBOutlet weak var timeLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
startTimer()
}
This method call initializes the timer. It specifies the timeInterval (how often the a method will be called) and the selector (the method being called).
The interval is measured seconds so for it to perform like a standard clock we should set this argument to 1.
func startTimer() {
countdownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
// Stops the timer from ever firing again and requests its removal from its run loop.
func endTimer() {
countdownTimer.invalidate()
}
//updateTimer is the name of the method that will be called at each second. This method will update the label
#objc func updateTime() {
timeLabel.text = "\(totalTime)"
if totalTime != 0 {
totalTime -= 1
} else {
endTimer()
}
}
I want to changes background images in a nice way like cross dissolve...
I manage to let the image change within 3 seconds.
Can you help me with a code to let it change in a nice way?
var timer = Timer()
var counter = 0
#IBOutlet weak var imageBG: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
repated()
}
func repated() {
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(ViewController.updatecounter), userInfo: nil, repeats: true)
}
#objc func updatecounter() {
counter += 1
if counter == 14 {
counter = 1
}
imageBG.image = UIImage(named: "\(counter).png")
}
Into your upodatecounter you can use an UIView transition like this:
#objc func updatecounter() {
counter += 1
if counter == 14 {
counter = 1
}
UIView.transition(with: imageBG,
duration:3.0,
options: .transitionCrossDissolve,
animations: { self.imageBG?.image = UIImage(named: "\(counter).png") },
completion: nil)
}
When 10 seconds hit the timer. I would like the background color to be change. Right now my code is not working. Nothing is being changed when the code hits 10 seconds. I also dont think the timer is working.
#IBOutlet var espn: UILabel!
var TIMER = Timer()
var seconds = 0
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if seconds == 10{self.view.backgroundColor = UIColor.red
}
}
#IBAction func startTimer() {
TIMER = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(threeVC.clocker), userInfo: nil, repeats: true)
}
#objc func clocker() {
seconds += 1
espn.text = String( seconds)
}
}
Try this code:
#IBOutlet var espn: UILabel!
var TIMER = Timer()
var seconds = 0
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
#IBAction func startTimer() {
TIMER = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(threeVC.clocker), userInfo: nil, repeats: true)
}
#objc func clocker() {
seconds += 1
espn.text = String( seconds)
if seconds == 10{
self.view.backgroundColor = UIColor.red
print("soda")
}
}
if statement should be written in #objc func clocker() not in viewDidAppear