Reputation: 11
I'm trying to do a small iOS app to visually prove the big number's law.
How it is supposed to work?
A function finds a random number (based on an interval given by the user, using the UIStepper
) and check if it is equal to 1, for a number of times also given by the user (using the UISlider
). If it is equal to 1, it then increments a variable. At the end, it gives the percentage of 1 after the whole loop is processed.
What's the problem then?
First of all, the UIProgressBar isn't moving through the process, and goes all the way up to 100% at the end. The main label is supposed to show the value in real time if the Switch is on, but it doesn't (the switch state does nothing). And - last but not least - if you select the maximum value of the UISlider
, after the process is finished, you can't start again.
You can check its
User Interface
to get a better idea of it.
This is the code:
class ViewController: UIViewController {
//Outlets :
@IBOutlet weak var refreshButton: UIBarButtonItem!
@IBOutlet weak var mainButton: UIBarButtonItem!
@IBOutlet weak var processingSwitch: UISwitch!
@IBOutlet weak var probStepper: UIStepper!
@IBOutlet weak var probSlider: UISlider!
@IBOutlet weak var progressBar: UIProgressView!
@IBOutlet weak var mainLabel: UILabel!
@IBOutlet weak var labelMax: UILabel!
@IBOutlet weak var labelPossibilties: UILabel!
//Actions :
@IBAction func refreshValues(_ sender: UIBarButtonItem) {
probSlider.setValue(1, animated: true)
probStepper.value = 2
labelMax.text = String(probSlider.value)
labelPossibilties.text = String(probStepper.value)
}
@IBAction func probaSlider(_ sender: UISlider) {labelMax.text = String(Double(Int(probSlider.value)))}
@IBAction func probaStepper(_ sender: UIStepper) {labelPossibilties.text = String(UInt32(probStepper.value))}
@IBAction func mainButton(_ sender: UIBarButtonItem) {bigNumbersLaw(processingSwitch.isOn, UInt32(probStepper.value), Double(Int(probSlider.value)))}
//Variables :
var repeatNumber:Double = 0
var randomNumber:UInt32 = 0
var probability:Double = 0
var timesFound:Double = 0
//Functions :
func bigNumbersLaw(_ liveShow: Bool, _ poss: UInt32, _ max: Double) {
while repeatNumber < max {
repeatNumber += 1
randomNumber = arc4random_uniform(poss)
progressBar.progress = Float(repeatNumber/max)
if randomNumber == 1 {
timesFound += 1
}
probability = (timesFound/repeatNumber)
if liveShow {mainLabel.text = String(probability)}
}
mainLabel.text = String(probability)
}
override func viewDidLoad() {
super.viewDidLoad()
probStepper.maximumValue = 1000000
probStepper.minimumValue = 2
probSlider.maximumValue = 1000000
probSlider.minimumValue = 1
}
}
Thank you some much to anyone that can help me, and thanks for reading.
Upvotes: 1
Views: 784
Reputation: 8006
Your problem lies with the fact, that there is no layout/drawing pass during the execution of your bigNumbersLaw
method. Since this method is called synchronously, there are no UI updates until it executes itself, and when the next frame is being drawn it uses the latest values set.
What you need to do, is to "break down" this execution into steps, and update the UI between them.
//Functions :
func bigNumbersLaw(_ liveShow: Bool, _ poss: UInt32, _ max: Double) { // #3
if repeatNumber < max { // #1
repeatNumber += 1
randomNumber = arc4random_uniform(poss)
progressBar.progress = Float(repeatNumber/max)
if randomNumber == 1 {
timesFound += 1
}
probability = (timesFound/repeatNumber)
mainLabel.text = String(probability)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .milliseconds(1)) {
bigNumbersLaw(liveShow, poss, max) // #2
}
}
}
Bear in mind - I haven't tested this code, but it should work. Two main things happened here :
if
check.1
milisecond in the future - this will allow the system to update the UI.I've removed the liveShow
variable from the code, since I couldn't find an elegant solution to fit it - this is left as an exercise for the reader.
Another note - you should probably block the UI while this is being executed.
Upvotes: 1
Reputation: 2609
Have you tried putting your changes of the GUI on the main thread?
DispatchQueue.main.async() {
// your UI update code
}
Changes of the GUI are should always be called on the main thread (GCD). This would apply in all your IBActions and when you want the slider to animate/move.
A very good read about it Grand Central Dispatch - Joyce Matos on Medium
Upvotes: 0