How do I add a timer using Swift? - swift

I have come across a lot of questions similar to this, but many were for older versions of Xcode, or simply did not work.
I'm using Xcode Version 8.3.2 (8E2002) and Swift coding language. I don't know much about coding, but am young and eager to learn!
I'm creating a clicker game that will give you money per second that you are on the game itself. So if you idle for 2 minutes, it would give you $120 ($1per second #120 sec). In addition to this, you also can earn money from clicking the main object.
Here is my coding so far:
import UIKit
class ViewController: UIViewController {
var score = 0
var add = 1
func addpersec() {
score += 1
}
//func used to add to the score based timer. Aka, adding 1 per second
#IBOutlet weak var scorecount: UILabel!
#IBAction func clicks(_ sender: Any) {
score += 1
scorecount.text = "Honey: \(score)"
}
#IBOutlet weak var Bees: UITableView!
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.
}
}

class ViewController: UIViewController {
var timer: Timer? = nil // Property
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(handleTimer), userInfo: nil, repeats: true)
}
func handleTimer(_ timer: Timer) {
print("Timer ticking!")
}
}
To invalidate the timer, call self.timer?.invalidate()

Your question seems to be related to iOS UI, so I don't know if my answer makes sense.
For general purpose delayed function execution (like Javascript's setTimeout), you can use a DispatchQueue
// have this as a global somewhere
let bg = DispatchQueue(label: "bg", qos: .background, target: nil)
// anywhere else in your code:
// First decide the time to execute your function
let delayInSeconds = 3
let when = DispatchTime.now() + delayInSeconds
// call it
bg.asyncAfter(deadline: when) {
// code to execute later (this is the body of an anonymous function)
}

Related

Nil cannot be assigned to type 'Timer'?

Hi there I am completing some research for Alzheimers Disease - I want to be able to record the time it takes to complete a drawing (it should only take a few seconds for the patient to draw). I want to record both the time spent with apple pencil on tablet and the time spent overall to complete a drawing (time on tablet plus time in between strokes).
I have created this application so far but can't get the timer to work.
I have the drawing/scribble board down pat.
I have tried many different approaches but I am just not experienced enough in code to work out why it is not starting the timer when the apple pencil hits the tablet. The code below is for the ViewController script.
Only one of the timers for = time spent drawing, has been created so far. But I can't even get that to work.
Tried changing the script, tried asking friends. Im quite new to swift so any help is much appreciated.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var canvasView: CanvasView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func clearCanvas(_ sender: Any) {
canvasView.clearCanvas()
timer.invalidate()
seconds = 0
timeDrawing.text = "\(seconds)"
}
#IBOutlet weak var timeDrawing: UILabel!
var seconds = 0
var timer = Timer()
var isTimerRunning = false //This will be used to make sure only one timer is created at a time.
var resumeTapped = false
var touchPoint:CGPoint!
var touches:UITouch!
func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(ViewController.updateTimer)), userInfo: nil, repeats: true)
}
private(set) var from: Date?
#objc func updateTimer() {
let to = Date()
let timeIntervalFrom = (from ?? to).timeIntervalSince1970
let time = to.timeIntervalSince1970 - timeIntervalFrom
timeDrawing.text = "\(round(time))" //This will update the label.
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
guard let touch = touches.first else { return }
let touches = touch.location(in: canvasView) // or maybe ...(in: self)
if touch.type == .pencil {
if !isTimerRunning {
from = Date()
runTimer()
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
if !isTimerRunning {
timer.invalidate()
timer = nil
from = nil
timeDrawing.text = "0"
}
}
}
I was hoping when the apple pencil touched the tablet it would start the timer. And then when the pencil left the tablet it would stop one timer and start one of the timers (yet to be implemented). (i have yet to add another timer for the time in between strokes, any help with that would be appreciated too.)
Only optionals can take nil values, you can make the timer object as optional like this
var timer:Timer?
Yes it makes sense. Edited

Why is my prepareForSegue code activating the wrong button?

I'm learning how to program and am playing with a Swift project in Xcode. The main storyboard has two view controllers. The first view controller is simply called ViewController and the second view controller is called HelpScreenViewController.
In ViewController I have a "help" button that switches the user to HelpScreenViewController. This button uses a segue called "goToHelpScreenSegue".
In HelpScreenViewController I have three buttons:
"Close" button to dismisses the view (no segue used)
"Send Feedback" button to generate a new email in the Mail app (no segue used)
"Reset Game" button to call a function that is coded within the first ViewController. This third button uses a segue called "resetGameSegue".
What I'm trying to do is...
...Get the "Reset Game" button on the HelpScreenViewController to reset the game by calling a function that's coded within the first view controller.*
To try and get this to work the way I want, I've used the following code:
WITHIN first main ViewController
import UIKit
import iAd
import AdSupport
import AVFoundation //audio
import GameplayKit
class ViewController: UIViewController, ADBannerViewDelegate, MyResetGameProtocol {
#IBOutlet weak var Banner: ADBannerView!
#IBOutlet weak var buttonA: UIButton!
#IBOutlet weak var buttonB: UIButton!
#IBOutlet weak var buttonC: UIButton!
#IBOutlet weak var buttonD: UIButton!
#IBOutlet weak var labelQuestion: UILabel!
#IBOutlet weak var labelScore: UILabel!
#IBOutlet weak var labelTotalQuestionsAsked: UILabel!
#IBOutlet weak var labelFeedback: UILabel!
#IBOutlet weak var buttonNext: UIButton!
var score :Int! = 0
var totalquestionsasked :Int! = 0
var allEntries : NSArray!
var shuffledQuestions: [AnyObject]!
var nextQuestion = -1
var currentCorrectAnswerIndex : Int = 0
var audioPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.Banner?.delegate = self
self.Banner?.hidden = true
LoadAllQuestionsAndAnswers()
if #available(iOS 9.0, *) {
shuffledQuestions = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(allEntries as [AnyObject])
nextQuestion++
LoadQuestion(nextQuestion)
// Fallback on earlier versions
}else{
let randomNumber = Int(arc4random_uniform(UInt32(allEntries.count)))
LoadQuestionPreiOS9(randomNumber)
}
LoadScore()
AdjustInterface()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let viewController = segue.destinationViewController as! HelpScreenViewController
viewController.controller = self
}
func ResetGame() {
PlaySoundReset()
score = 0
totalquestionsasked = 0
SaveScore()
LoadScore()
}
func PlaySoundReset()
{
let alertSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("pcbeep", ofType: "wav")!)
do {
audioPlayer = try AVAudioPlayer(contentsOfURL: alertSound)
} catch {
}
audioPlayer.prepareToPlay()
audioPlayer.play()
}
func SaveScore()
{
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setInteger(score, forKey: "Score")
defaults.setInteger(totalquestionsasked, forKey: "Out of")
}
func LoadScore()
{
let defaults = NSUserDefaults.standardUserDefaults()
score = defaults.integerForKey("Score")
totalquestionsasked = defaults.integerForKey("Out of")
labelScore.text = "Score: \(score)"
labelTotalQuestionsAsked.text = "out of \(totalquestionsasked)"
}
and so on....
WITHIN the second HelpScreenViewController
import UIKit
protocol MyResetGameProtocol {
func ResetGame()
}
class HelpScreenViewController: UIViewController, MyResetGameProtocol {
var controller: MyResetGameProtocol? // reference to the delegate alias First Controller
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
#IBAction func SendFeedback(sender: AnyObject) {
UIApplication.sharedApplication().openURL(NSURL(string: "mailto:feedback#felice.ws?")!)
}
#IBAction func DismissView(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil) }
#IBAction func buttonResetGame(sender: AnyObject) {
controller.ResetGame()
}
}
Now, at the moment with the above code what happens is that if the user taps the "help" button in the first main ViewController (i.e. goToHelpScreenSegue), not only does it take the user to the help screen, but it also calls the function I want activated when the user taps on the "Reset Game" button instead. That is, at the moment, it's the "help" button that resets the game before taking the user to the help screen.
Now, within the help screen, the first two buttons work normally (but they're not using segues). Tapping on the third button (the Reset Game one) simply returns the user back to the main screen. It doesn't call the function, doesn't reset the game.
I've lost count of the times I've changed the code around to try and get it to work right, but I've obviously missed something really obvious.
In particular, I've tried using the following code instead within the main ViewController:
override func prepareForSegue(segue: UIStoryboardSegue?, sender: AnyObject?) {
if segue?.identifier == "resetGameSegue" {
let viewController = segue!.destinationViewController as! HelpScreenViewController
viewController.controller = self
}
However, this results in nothing happening. What I mean is that the button on the main screen works properly (taking the user to the help screen and not incorrectly calling the resetGame function). And, within the help screen the first two buttons work as expected, but the "Reset Game" button just returns the user to the first screen but without calling the ResetGame function.
I also tried removing the IBActions from both my code and the connections inspector for the "Reset Game" button, but that made no difference either.
Any assistance would be most appreciated as I'm just not getting it! :(
I'm agree with MikeG, that you should probably learn about how delegates should be implemented. But the thing you're doing wrong inside this code is that you're not actually calling ResetGame() function on your delegate. Try to implement your #IBAction function in this way:
#IBAction func buttonResetGame(sender: AnyObject) {
controller?.ResetGame()
}
And yeah, if I understand your logic correctly your HelpScreenViewController should not implement MyResetGameProtocol cause your ViewController is the one who's implementing it.

UISlider, save status

My question is about the UISlider. I managed to implement everything but i don't know how i can save its status.
Ive looked everywhere but al the posts are in older versions of swift/xcode. So the question is how do i save its status so that when i go to another view and then come back the status is still the same.
Thanks very much!
import UIKit
class SettingsViewController: UIViewController {
var sequeInt = 0
let savedWordLength = NSUserDefaults.standardUserDefaults()
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet weak var wordLength: UISlider!
#IBOutlet weak var wordLengthValue: UILabel!
var selectedValue: Int = 5
#IBAction func valueChanged(sender: UISlider) {
selectedValue = Int(sender.value)
savedWordLength.setInteger(selectedValue, forKey: "myInt")
let ourInt = savedWordLength.integerForKey("myInt")
sequeInt = ourInt
print (sequeInt)
wordLengthValue.text = String(ourInt)
}
UISlider value property is a Float so you can use NSUserDefault's method setFloat to save its value and retrieve it next time your view appears using NSUserDefaults method floatForKey.
to save it:
NSUserDefaults.standardUserDefaults().setFloat(sender.value, forKey: "wordLength")
load it:
override func viewWillAppear(animated: Bool) {
wordLength.setValue(NSUserDefaults.standardUserDefaults().floatForKey("wordLength"), animated: false)
}
Follow these steps.
In your view will appear.
fontSlider.setValue(UserDefaults.standard.float(forKey: "slider_value"), animated: false)
Take another outlet from storyBoard as "editingDidEnd".
In that function:
UserDefaults.standard.set(fontSlider.value, forKey: "slider_value")
And finally in your ValueChanged Outlet.
UserDefaults.standard.set(fontSlider.value, forKey: "slider_value")

Looping Label Change Background task in Swift

I was looking for a way in Swift to loop a changing label that cycles through an array of Strings. Most ways I've tried have stopped all other tasks while the loop was running.
You're view controller could look something like this:
class ViewController: UIViewController {
#IBOutlet weak var cycleLabel: UILabel!
var strings: [String]!
var timer: NSTimer!
var index: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
self.strings = ["Lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit", "Vestibulum", "erat", "lacus", "congue"]
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.cycleLabel.text = self.strings[self.index]
}
#IBAction func beginCyclingTapped(sender: UIButton) {
let interval = 1.0
if self.timer.valid {
self.timer.invalidate()
}
self.timer = NSTimer.scheduledTimerWithTimeInterval(interval, target: self, selector: "updateLabel", userInfo: nil, repeats: true)
}
func updateLabel() {
self.index += 1
self.cycleLabel.text = self.strings[self.index % self.strings.count]
}
}
This code will update the label text to the next string in the strings property every one second. If you'd like a different interval, change the interval constant in the beginCyclingTapped(:) method. The label will start restart from the beginning of the strings array after it reaches the last element in that array. The if statement in beginCyclingTapped(:) ensures that multiple timers are not scheduled to update that label, which would result in the label getting updated more frequently than desired. Also, make sure you hook up the IBOutlet to a UILabel on your Storyboard.

How to print a string one letter at a time into a textview SWIFT

How do I print my variable eg. var Output:String = "Test" so that It prints into the textview one letter at a time? Like it's being typed out.
Thanks in advance!
You can use a timer with a random interval as follow:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var myTypeWriter: UITextField!
let myText = Array("Hello World !!!")
var myCounter = 0
var timer:NSTimer?
func fireTimer(){
timer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: "typeLetter", userInfo: nil, repeats: true)
}
func typeLetter(){
if myCounter < myText.count {
myTypeWriter.text = myTypeWriter.text + String(myText[myCounter])
let randomInterval = Double((arc4random_uniform(8)+1))/20
timer?.invalidate()
timer = NSTimer.scheduledTimerWithTimeInterval(randomInterval, target: self, selector: "typeLetter", userInfo: nil, repeats: false)
} else {
timer?.invalidate()
}
myCounter++
}
override func viewDidLoad() {
super.viewDidLoad()
fireTimer()
// 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.
}
}
This should loop trough every character inside the provided string and print it out with delay
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
for char in "test" {
dispatch_async(dispatch_get_main_queue()) { textView.text += String(char) }
usleep(1000)
}
}
I wrote a blog post recently where I created a similar but different effect. In my example I did it using a UILabel and NSAttributedStrings.
In mine I used a fade in animation but you needn't do that if you don't want.
Given that you just want it letter by letter it will make it a lot less complex than mine.
Anyway, it should give you an idea of how I would approach it. Also, unless the use is actually typing into the UITextView then don't use one. Use a UILabel instead.
http://www.oliverfoggin.com/birdman-and-text-animations/