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.
Related
To begin with, in general, I want to build the functionality of the program on a timer, which will alert you about the specified breaks, etc. The program is for concentration.
When saving all the variables that we set at the very beginning, when you press the button, the timer should start. It must perform a specific cycle (period) that we set earlier.
But I have something wrong with the implementation of exactly the same cycle. It seems that all variables are saved, but why the label does not change them ...
Ideally, you should first run Work -> Short break -> Work -> Short Break -> Work -> Short Break -> Work -> Short Break -> Long Break and then repeat depending on how many Cycles are installed. But for some reason, I have Work -> Short break -> Short break ...
From you I just want to hear the opinion of what my mistake may be and how to solve it?
For my "code" do not pay attention and do not scold, I know myself. Now I just want to learn how to write and understand what I am writing. The code will of course be better over time.
My app looks like this:
import UIKit
class ViewController: UIViewController {
var time: Int = 0
var timer = Timer()
var min: Int = 0
var sec: Int = 0
#IBOutlet weak var shortBreakLabel: UITextField!
#IBOutlet weak var longBreakLabel: UITextField!
#IBOutlet weak var workLabel: UITextField!
#IBOutlet weak var cyclesLabel: UITextField!
#IBOutlet weak var goButton: UIButton!
#IBOutlet weak var minutesLabel: UILabel!
#IBOutlet weak var secondsLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
shortBreakLabel.text = String(5)
longBreakLabel.text = String(15)
workLabel.text = String(25)
cyclesLabel.text = String(16)
saveTimer()
goButton.layer.cornerRadius = 10
}
//GoButton pressed
#IBAction func goButtonAction(_ sender: UIButton) {
timerFunc()
}
func timerFunc() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerDidEndend), userInfo: nil, repeats: true)
}
#objc private func timerDidEndend() {
if (time > 0) {
time -= 1
updateUI()
} else {
timer.invalidate()
}
changeTimeToShortBreak()
changeTimeToWork()
changeTimeToShortBreak()
}
private func updateUI() {
min = (time/60) % 60
sec = time % 60
minutesLabel.text = String(min)
secondsLabel.text = String(sec)
}
func changeTimeToShortBreak() {
if time == 0 {
timer.invalidate()
minutesLabel.text = shortBreakLabel.text
time = Int(minutesLabel.text!)! * 60
timerFunc()
}
}
func changeTimeToWork() {
if time == 0 {
timer.invalidate()
minutesLabel.text = workLabel.text
time = Int(minutesLabel.text!)! * 60
timerFunc()
}
}
func saveTimer() {
minutesLabel.text = workLabel.text
time = Int(minutesLabel.text!)! * 60
}
//Hide keyboard function
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
saveTimer()
self.view.endEditing(true)
}
}
You really have 2 or 3 different things going on here:
Allowing the user to set time intervals
Displaying the time since the last/to the next interval
Keeping track of the time to figure out what state you're currently in or will be in next
I would break this into 2 classes - a state class and a UI class. The state class would just have an array of time intervals and an indicator of which one you're on. Something like this:
var timeIntervals = [ kDefaultWorkInterval, kDefaultShortBreakInterval, kDefaultWorkInterval, kDefaultShortBreakInterval, kDefaultWorkInterval, kDefaultShortBreakInterval, kDefaultWorkInterval, kDefaultShortBreakInterval, kDefaultLongBreakInterval ]
var currentInterval = 0
var timer = Timer()
Rather than setting the timer to fire every second, you simply set it to the time in the timeIntervals [ currentInterval ] element. When it fires, increment currentInterval, get the time interval for that interval, and set the timer to fire in that many seconds.
Next, in your UI class, don't poll for changes to the text fields. Set the UI class (your ViewController) to receive notifications from the text fields when they have been edited. That way you only need to update the state object's time intervals when the user has actually changed them. This is much more efficient than changing them once per second. When that happens call a setter on the state class to update the time intervals.
I would keep the 1 second timer in the ViewController and simply use it for updating the countdown and nothing else.
I have the following code in Swift trying to get a simple random number generator as a simulator for a game.
var randomNumber = 0
override func viewDidLoad() {
super.viewDidLoad()
randomNumber = Int(arc4random_uniform(74) + 1)
label.text = "\(randomNumber)"
}
I'm new to programming Swift but I know to use timer() and import Foundation to use the timer function but I'm not sure how to implement and make it so a new number appears in the label every 10 seconds. Thanks for any help.
Use a Timer with an interval of 10 seconds to pull a new number from an array of numbers. Remove the number from the array so that you don't call the same number twice. When the stop button is pressed, or you are out of numbers call invalidate on the timer to stop it.
class BingoCaller: UIViewController {
#IBOutlet weak var label: UILabel!
var numbers = Array(1...75)
let letters = ["B", "I", "N", "G", "O"]
var timer: Timer?
override func viewDidLoad() {
timer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { timer in
let index = Int(arc4random_uniform(UInt32(self.numbers.count)))
let number = self.numbers.remove(at: index)
self.label.text = "\(self.letters[(number - 1) / 15])-\(number)"
if self.numbers.isEmpty {
timer.invalidate()
}
}
}
#IBAction func stop(_ button: UIButton) {
timer?.invalidate()
}
}
Suggestions for next steps:
Add the numbers that have been pulled to a second array. Use that array to populate a tableView so that Gran is able to review the numbers when someone calls "Bingo!".
Use AVSpeechSynthesizer to have the iPhone actually speak the numbers.
Add a reset button to start a new game. Initialize the numbers to Array(1...75), the calledNumbers to [] and start again. It's a good idea to move the Timer loop to its own function so that it can be called from a start button.
You can define a helper array, that would let you check if that number was already returned:
var array = [Int]()
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(ViewController.timerFunction), userInfo: nil, repeats: true)
}
func timerFunction(){
var n = arc4random_uniform(75) + 1
while array.contains(Int(n)){
n = arc4random_uniform(75) + 1
}
array.append(Int(n))
label.text = String(n)
if array.count == 75{
timer?.invalidate()
}
}
This way you are sure that the timer is invalidated when all the numbers have already been used and also avoid index-removal errors.
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)
}
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")
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/