using a swift loop that waits on functions to complete - swift

I'm new and trying to learn and code my first app. A simple app to get started...So I'm trying to make a loop that will perform a series of functions completely for a certain number of times (rounds). I've figured out the body of code and I'm trying to implement DispatchGroup() to wait on the functions to perform:
#IBAction func buttonPush(_ sender: Any) {
let group = DispatchGroup()
while rounds >= 0 {
group.enter()
DispatchQueue.main.async {
if self.isGameStarted == 0 {
self.setTimer()
self.resetVariables()
self.runClock()
self.roundLabel.text = String(self.self.rounds)
group.leave()
} else if self.isGameStarted >= 1{
self.self.isGameStarted = 0
self.timer.invalidate()
group.leave()
}
}
group.notify(queue: .main) {
self.rounds -= 1
}
}
}
Here was the original body pre DispatchGroup(). The issue was that the while loop was completing before the functions could run, I think (best guess). I'm using the latest version of swift as of 11/19/17.
#IBAction func buttonPush(_ sender: Any) {
while rounds >= 0 {
if isGameStarted == 0 {
setTimer()
resetVariables()
runClock()
roundLabel.text = String(rounds)
} else if isGameStarted >= 1{
isGameStarted = 0
timer.invalidate()
rounds -= 1
}
}
}
12/13/17 edit. This works for each button press and while loop commented out. However, with while loop uncommented it goes into infinite loop...
#IBAction func buttonPush(_ sender: Any) {
//while rounds >= 0 {
if isGameStarted == 0 {
setTimer()
resetVariables()
runClock()
roundLabel.text = String(rounds)
DispatchQueue.main.asyncAfter(deadline: .now() + Double(timeRoundTot){
self.rounds -= 1
} else if isGameStarted >= 1{
isGameStarted = 0
timer.invalidate()
}
}
//}
}

Related

In Swift, I got EXC_BAD_ACCESS (code=1, address=0x1fedad19a120) while executing Peterson's Algorithms

circumstance
I executed below code for checking Peterson's Algorithms in sample app not playgrounds.
If code will correctly be executed, I will get sum as 0
But instead, I got error message 😭
⬇️ error message
🤔 Question
I can't understand why this message popped up.
Could you help me teach some reason about why this happen?
⬇️ Code
class ViewController: UIViewController {
var group = DispatchGroup()
var sum = 0
var flag = [false, false,false , false ]
var turn = 0
override func viewDidLoad() {
super.viewDidLoad()
var queue = DispatchQueue(label: "producer")
var queue2 = DispatchQueue(label: "consumer")
queue.async(group: group) {
self.producer()
}
queue2.async(group: group) {
self.consumer()
}
group.notify(queue: .main) {
print(self.sum)
}
}
func producer() {
for i in 0..<10000 {
flag[0] = true
turn = 1
while flag[1] && turn == 1 { }
sum += 1
flag[0] = false
}
}
func consumer() {
for i in 0..<10000 {
flag[1] = true
turn = 0
while flag[0] && turn == 0 { }
sum -= 1
flag[1] = false
}
}
}

Swift var score does not increase

var players : [Player]?
var currentplayerIndex = 0
var currentQuestion : QuestionObject!
var questions = Questions()
var score = 0
var currentQuestionPos = 0
func updateUi() {
if let score = players?[currentplayerIndex].score {
playerPoints.text = "Points: \(score)"
}
}
func loadnextQuestion () {
if(currentQuestionPos < questions.questions.count) {
currentQuestionPos += 1
if currentplayerIndex < players!.count - 1 {
currentplayerIndex += 1
} else {
currentplayerIndex = 0
}
playerTurn.text = (players?[currentplayerIndex].name)
currentQuestion = questions.questions[currentQuestionPos]
questionText.text = currentQuestion.question
}
}
#IBAction func submitButtonPressed(_ sender: UIButton) {
let i = pickerView.selectedRow(inComponent: 0)
if(currentQuestion.answer == i) {
players?[currentplayerIndex].score += 1
loadnextQuestion()
updateUi()
} else {
updateUi()
loadnextQuestion()
}
}
}
My score displays only 0 all the time.
Does not increase when the answer is right.
All the added players get 1 question each but the sore is still 0 for all the players.
Your code doesn't work because you are doing this series of things when the answer is correct:
players?[currentplayerIndex].score += 1
loadnextQuestion()
updateUi()
The player's score is indeed incremented. But notice that the next thing you do is loadnextQuestion(), which increments currentplayerIndex. This causes updateUi to update the label's text to the score of the next player, not of the player that just answered the question correctly.
One way to fix this is to swap the last two lines:
players?[currentplayerIndex].score += 1
updateUi()
loadnextQuestion()
Notice that this is the same order as in the else branch, which you wrote correctly.
This causes the label to always display the score of the last player, which might not be desirable. Assuming there's not like 100 players or something crazy like that, I think it would be a much better UX if you display all the player's scores:
func updateUi() {
if let scores = players?.map({ "\($0.score)" }) {
playerPoints.text = "Points: \(scores.joined(separator: ", "))"
}
}
You can replace the button action work
#IBAction func submitButtonPressed(_ sender: UIButton) {
let i = pickerView.selectedRow(inComponent: 0)
if(currentQuestion.answer == i) {
players?[currentplayerIndex].score += 1
updateUi()
loadnextQuestion()
} else {
updateUi()
loadnextQuestion()
}
}

Dispatchqueue and SVProgressHUD in swift

I have been designing an app that analyzes lines of text, and I want to use SVProgressHUD to show progress of it.
This is my code:
let total = text.count
for line in text{
count = count + 1
DispatchQueue.global(pos: .background).async{
//Analyzing line
DispatchQueue.main.async{
SVProgressHUD.showProgress(count/total)
}
}
}
The analyzing works, and the HUD shows properly, when the count reaches total, the process gets stuck, and SVProgressHUD stops at max status, and the program stops. What is the issue with the program?
Am I using Dispatchqueue wrong?
Do I have to call something other to free the background process or something?
I have tried exchanging //Analyzing line and SVProgressHUD.show..., but it still doesn't work. I initially used SVProgress within the loop without the dispatchqueue, but then the progress hud moves only after the analyzation(full loop) has been completed, which is a problem.
Any help would be appreciated.
Thank you.
Try using this code. It does not use loop but implements recursive calls to a function for processing your string data.
func processLines() {
SVProgressHUD.showProgress(0)
// sample data
var text = Array("abcdefghijklmnop")
var lines : [Line] = []
let count : [Int] = Array(0..<text.count)
count.forEach({ pos in lines.append(Line(char: text[pos])) })
var currentIndex : Int = 0
func processLine(at index: Int) {
DispatchQueue.global(qos: .background).async{
//Analyzing line
let line = lines[index]
print("Processing Line CHAR: \(line.char)")
DispatchQueue.main.async{
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
guard currentIndex < lines.count-1 else {
SVProgressHUD.showProgress(1)
return
}
currentIndex += 1
startLineProces(at: currentIndex)
}
}
}
}
func startLineProces(at index: Int) {
processLine(at: index)
SVProgressHUD.showProgress(Float(index) / Float(lines.count))
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
startLineProces(at: currentIndex)
}
}
struct Line {
var char: Character
init(char: Character) {
self.char = char
}
}
Use the below string extension to analyse your string. It has a completion block which will return progress as well as status of completion.
extension String {
func analyseString(completion: #escaping (Bool, Float) -> ()) {
let totalCountOfString = self.count
for (index, _) in self.enumerated() {
if index == totalCountOfString - 1 {
completion(true, Float(index)/Float(totalCountOfString))
} else {
completion(false, Float(index)/Float(totalCountOfString))
}
}
}
}
You can call the above method to show your progress as below (maybe on a button click). self.yourString is your input string that you need to analyse.
#IBAction func clicked(_ sender: UIButton) {
DispatchQueue.main.async {
self.yourString.analyseString { (isCompleted, progress) in
if isCompleted {
SVProgressHUD.dismiss()
print("Ending")
} else {
SVProgressHUD.showProgress(progress, status: "Analysing (\(progress)%)")
}
}
}
}

Using UIPanGestureRecognizer to change images in UIImageView Swift 4

I have loaded 31 images into a UIImageView and would like to use the UIPanGestureRecognizer to have the user simply pan up and down to change the images. The only example I could find is in Obj-C and can seem to figure out how to make it work. So far I have try to used the following code:
#IBAction func panPerformed(_ sender: UIPanGestureRecognizer) {
if sender.state == .began || sender.state == .changed {
let translation = sender.translation(in: self.view).y
//print (translation)
if translation > 0 { //swipe up
} else { //swipe down
var pos = Int(abs(translation / panThreshold))
if pos < chartImageArray.count {
print (pos)
chartView.image = chartImageArray[pos]
}
}
This does scroll through, but when you release it obviously go back to 0. Is there some better way to achieve this functionality?
I was try to figure out a way to track the transition number and somehow increase or decrease an index number that would correlate to the image array.
Thanks
Craig
EDIT: Here is the code I used to get the functionally
#objc func panPerformed(_ sender: UIPanGestureRecognizer) {
if sender.state == .began || sender.state == .changed {
let velocity = sender.velocity(in: self.view).y
var count = 0
if velocity > 0 {
count -= 1
} else {
count += 1
}
movePictures(count)
} else if sender.state == .ended { //release swipe
}
}
func movePictures(_ ind:Int) {
// print (index)
if index >= 0 {
index = index + ind
} else {
index = arrayCount * panThreshold
}
if index <= arrayCount * panThreshold {
index = index + ind
} else {
index = 0
}
}

go back and forth a string-array with two buttons

I want to show the first item on viewdidload() and then have the user go to the next item in the array and have the option to go back and disable the back button if it’s the first index and disable the button if its the last index. Here is my code
#IBAction func back(_ sender: Any) {
c -= 1
if c == thkrArray.count{
nextButton.isEnabled = false
}
if c == 0{
backButton.isEnabled = false
}
let thkr: String = thkrArray[c]
Text1.text = thkr
}
#IBAction func A2(_ sender: Any) {
c += 1
if c == thkrArray.count{
nextButton.isEnabled = false
}
if c == 0 {
backButton.isEnabled = false
}
if c > 0{
backButton.isEnabled = false
}
let thkr: String = thkrArray[c]
Text1.text = thkr
}
The easiest way to achieve this is to add an observer to c:
var c: Int = 0 {
didSet {
Text1.text = thkrArray[c]
backButton.isEnabled = c > 0
nextButton.isEnabled = c < thkrArray.count - 1
}
}
#IBAction func back(_ sender: Any) {
c -= 1
}
#IBAction func A2(_ sender: Any) { // isn't this `next`?
c += 1
}
override func viewDidLoad() {
super.viewDidLoad()
c = 0
}