I have an UIPageViewController with a number in the center of each VC in it.
I want that when I swipe from view to view, the number will begin at 0 and count up until it gets to the correct number (or if the number is negative - count down) like in this gif:
https://d13yacurqjgara.cloudfront.net/users/345970/screenshots/2126044/shot.gif
How can I do that?
Thank you!
You can use NSTimer to achieve this.
Here is example project I created for you.
Create layout like this:
Then in your ViewController do like so:
import UIKit
class ViewController: UIViewController {
#IBOutlet var countingLabel: UILabel!
var number = 0
var destinationNumber = 30
var timer: NSTimer!
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.
}
#IBAction func startButtonTapped(sender: AnyObject) {
timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: "countUp", userInfo: nil, repeats: true)
}
func countUp() {
if number < destinationNumber {
number += 1
countingLabel.text = "\(number)"
} else {
timer.invalidate()
}
}
}
It will work.
Do not overcomplicate with timers and invalidations, etc.
extension UILabel {
func countAnimation(upto: Double) {
let from: Double = text?.replace(string: ",", replacement: ".").components(separatedBy: CharacterSet.init(charactersIn: "-0123456789.").inverted).first.flatMap { Double($0) } ?? 0.0
let steps: Int = 20
let duration = 0.350
let delay = duration / Double(steps)
let diff = upto - from
for i in 0...steps {
DispatchQueue.main.asyncAfter(deadline: .now() + delay * Double(i)) {
self.text = "\(from + diff * (Double(i) / Double(delay)))"
}
}
}
}
Related
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
In my app project I am displaying 107 of my images at random, I have set up a for loop that puts all of my images into an array. I then take that array and choose a random Index. That index correlates to a picture and then that picture appears on the screen when the user swipes to the left. My question is that can I make it so my code will not repeat the same index in the array until all of them (or the amount until closing the app) have been chosen at random. Here is my code
class ViewController: UIViewController {
var pictureArray: [String] = []
#IBOutlet weak var quoteImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
for i in 0...107{
pictureArray.append("quote\(i).jpg")
print(pictureArray)
}
// Do any additional setup after loading the view, typically from a nib.
let swipeRecognizer = UISwipeGestureRecognizer(target: self, action: #selector (changeQuotes))
swipeRecognizer.direction = UISwipeGestureRecognizerDirection.left
self.view.addGestureRecognizer(swipeRecognizer)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override var prefersStatusBarHidden: Bool{
return true
}
#objc func changeQuotes(){
let numberOfImages: UInt32 = 107
let randomIndex = Int(arc4random_uniform(UInt32(pictureArray.count)))
let imageName = "\(pictureArray[randomIndex])"
print(imageName)
quoteImage.image = UIImage(named: imageName)
}
}
Thanks for any help!
Flush your array randomly, then just get the "sorted" image one by one.
extension Sequence {
func randomSorted() -> [Element] {
var result = Array(self)
guard result.count > 1 else { return result }
for (i, k) in zip(result.indices, stride(from: result.count, to: 1, by: -1)) {
let newIndex = Int(arc4random_uniform(UInt32(k)))
if newIndex == i { continue }
result.swapAt(i, newIndex)
}
return result
}
}
You can generate unique random number by managing 2 arrays one contains images that are not yet displayed and one that are already displayed
Make following changes in your code
var pictureArray: [String] = []
var selectedPictureArray: [String] = []
static let TOTAL_IMAGES = 108
#IBOutlet weak var quoteImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
self.fillPictureArray()
let swipeRecognizer = UISwipeGestureRecognizer(target: self, action: #selector (changeQuotes))
swipeRecognizer.direction = UISwipeGestureRecognizerDirection.left
self.view.addGestureRecognizer(swipeRecognizer)
}
func fillPictureArray() {
self.selectedPictureArray.removeAll()
for i in 0..<ViewController.TOTAL_IMAGES {
pictureArray.append("quote\(i).jpg")
}
print(pictureArray)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override var prefersStatusBarHidden: Bool{
return true
}
func changeQuotes(){
if self.pictureArray.count == 0 {
self.fillPictureArray()
}
var randomIndex = 0
while true {
randomIndex = Int(arc4random_uniform(UInt32(ViewController.TOTAL_IMAGES)))
if self.selectedPictureArray.contains("quote\(randomIndex).jpg") {
continue
}else {
break
}
}
let imageName = "quote\(randomIndex).jpg"
self.selectedPictureArray.append(imageName)
print(imageName)
quoteImage.image = UIImage(named: imageName)
let index = self.pictureArray.index(of: imageName)
self.pictureArray.remove(at: index!)
}
Happy coding :)
I have created a simple timer in swift 3.
For some reason though, even though it works the label display too many numbers :S it displays a crazy amount of 9's. Is this to do with rounding?
Thanks, code below :)
var swiftTimer : Timer?
var swiftCounter: Double = 0.00
#IBOutlet weak var displayTimeLBL: UILabel!
#IBAction func start(_ sender: Any) {
swiftTimer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(ViewController.updateCounter), userInfo: nil, repeats: true)
}
#IBAction func stop(_ sender: Any) {
if swiftTimer != nil{
swiftTimer?.invalidate()
swiftCounter = 0.00
updateLabel()
swiftTimer = nil
}
}
func updateLabel(){
displayTimeLBL.text = String(swiftCounter)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func updateCounter(){
swiftCounter+=0.01
round(swiftCounter)
updateLabel()
}
enter image description here
Simplest Solution:
func updateCounter(){
swiftCounter+=0.01
round(swiftCounter)
swiftCounter = Double(round(100*swiftCounter)/100)
updateLabel()
}
If you want two fractions then you can use swiftCounter.round(2) instead of round(swiftCounter)
to update the text use:
displayTimeLBL.text = "\(swiftCounter.round(2))"
Here is the Double extension used:
extension Double {
func round(_ places: Int) -> Double {
let divisor = pow(10.0, Double(places))
return (self * divisor).rounded() / divisor
}
}
I'm trying to figure out how to set the accessibility Increment/ Decrement of an UISlider with stepping. It keeps saying that the value is 0%, which it shouldn't. I also wonder if this is the reason why the accessibilityValue is not working..
ViewController
import UIKit
class RateAppViewController: UIViewController {
//MARK: IBOutlets
#IBOutlet weak var ratingSlider: UISlider!
let steps: Float = 0.2
override func viewDidLoad() {
super.viewDidLoad()
// UpdateUI
updateUI()
// InitalizeAccessibility
initalizeAccessibility()
}
private func updateUI() {
ratingSlider.value = 0.2
ratingSlider.minimumValue = 0.2
ratingSlider.maximumValue = 1.0
}
private func initalizeAccessibility() {
ratingSlider.isAccessibilityElement = true
ratingSlider.accessibilityTraits = UIAccessibilityTraitAdjustable
ratingSlider.accessibilityLabel = NSLocalizedString("Rating", comment: "RatingView Label")
ratingSlider.accessibilityIncrement()
ratingSlider.accessibilityDecrement()
}
override func accessibilityIncrement() {
ratingSlider.value += steps
setRating(ratingSlider.value)
}
override func accessibilityDecrement() {
ratingSlider.value -= steps
setRating(ratingSlider.value)
}
private func setRating(value: Float) {
let stars: Int = Int(value * 5)
print(stars)
if value == 0.2 {
ratingSlider.accessibilityValue = NSLocalizedString("1 star", comment: "One Star RatingSlider")
} else {
ratingSlider.accessibilityValue = NSLocalizedString("\(stars) Stars", comment: "Multiple Stars RatingSlider")
}
}
#IBAction func ratingValueChanged(sender: UISlider) {
let roundSteps = round(sender.value / steps) * steps
sender.value = roundSteps
}
}
I believe you need to subclass UISlider and implement the methods in that class.
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.