Saving Int to UserDefaults when starting at 0 - ios

I want the code to start a var num = 0. Then after every button hit num increases by 1. The problem that I am running into is that when I switch view controllers and come back to this one it starts at 0 again. Num can only starts at 0 one tie when is loaded for the first time. I want to use userdefults to save the number so no matter what the score will always increase.
import UIKit
class ViewController: UIViewController {
var num = 0
let defaults = UserDefaults.standard
#IBAction func press () {
num += 1
defaults.set(num, forKey: "num")
}
}

You have to initialize num from UserDefaults.
Add this to viewDidLoad:
num = defaults.integer(forKey: "num")
Or simply update your var num = 0 line to:
var num = UserDefaults.standard.integer(forKey: "num")
You can't use your defaults variable in this case.

Call synchronize() immediately after your set data in user defaults, so it save it.
set already set value back to num in View Will Appear as View Will Appear will be called on Pop to this View from other Screen.
See Code
import UIKit
class ViewController: UIViewController {
var num = 0
let defaults = UserDefaults.standard
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
num = defaults.integer(forKey: "num")
print("Value of number from defaults \(num)")
}
#IBAction func press () {
num += 1
defaults.set(num, forKey: "num")
defaults.synchronize()
}
}

Related

UITextView does not update during loop

so I have tried 2 different ways to display the timer count down on the screen.
the code will print to the console but not to the UITextView (in both loop cases) in the repeat the UITextView ends with a 0 and that is the only thing it displays other than original txt "time count".... in the case where the commented loop is implemented the UITextView only displays the 1 (end of count down)... why is it printing to the console though these commands are in the same brackets as UITextView and they repeat
the image is after running code and clicking Soft (this is spin off of app brewery egg timer)
//
// ViewController.swift
// EggTimer
//
// Created by Angela Yu on 08/07/2019.
// Copyright © 2019 The App Brewery. All rights reserved.
//
import UIKit
let softTime = 5
let medTime = 7
let hardTime = 12
class ViewController: UIViewController {
#IBOutlet weak var timerTextView: UITextView!
#IBAction func eggHardnessButton(_ sender: UIButton) {
let hardness = sender.currentTitle
func timeCountDownSoft() {
var time = 3
repeat {
time = time - 1 //repeats
timerTextView.selectAll("") // does not repeat
timerTextView.insertText("") // does not repeat
let timeWord = String(time) // repeats
timerTextView.insertText(timeWord)//does not repeat
print(time) //repeats
sleep(1) //repeats
} while time >= 1
/*for timer in stride(from: 2, to: 0 , by: -1){
let time = String(timer)
timerTextView.selectAll("")
timerTextView.insertText("")
timerTextView.insertText(time)
print(time)
sleep(1)
}*/
}
func timeCountDownMed() {
for timer in stride(from: 420, to: 0 , by: -1) {
print(timer)
sleep(1)
}
}
func timeCountDownHard() {
for timer in stride(from: 720, to: 0 , by: -1) {
print(timer)
sleep(1)
}
}
if hardness == "Soft" {
print(softTime) // does not repeat
timeCountDownSoft() // does not repeat
} else if hardness == "Medium" {
print(medTime)
timeCountDownMed()
} else {
print(hardTime)
timeCountDownHard()
}
}
}
You never (well, almost never) want to use sleep().
The reason your text is not updating is because you are running closed-loops that never allow UIKit to update the view.
What you want to do instead is to use a repeating Timer with a one-second interval. Each time the timer fires, decrement your counter and update the UI. When the counter reaches Zero, stop the timer.
Here's a simple example:
import UIKit
let softTime = 5
let medTime = 7
let hardTime = 12
class ViewController: UIViewController {
#IBOutlet weak var timerTextView: UITextView!
var theTimer: Timer?
#IBAction func eggHardnessButton(_ sender: UIButton) {
let hardness = sender.currentTitle
var numSeconds = 0
// set number of seconds based on button title
switch hardness {
case "Soft":
numSeconds = softTime * 60
case "Medium":
numSeconds = medTime * 60
case "Hard":
numSeconds = hardTime * 60
default:
// some other button called this func, so just return
return()
}
// stop current timer if it's running
if let t = theTimer {
if t.isValid {
t.invalidate()
}
}
// update text field with selected time
let timeWord = String(numSeconds)
self.timerTextView.text = timeWord
// start a timer with 1-second interval
theTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
numSeconds -= 1
let timeWord = String(numSeconds)
self.timerTextView.text = timeWord
// if we've reached Zero seconds remaining
if numSeconds == 0 {
// stop the timer
timer.invalidate()
}
}
}
}
Several points:
When your app changes the UI, those changes do not actually get
rendered to the screen until your code returns and visits the app's
event loop. Thus, with your repeat loop, nothing will show up on in
the UI until the loop completes
Do not use sleep() on the main thread. That blocks everything, and
your app comes to a screeching halt.
If you want to update the UI once a second, set up a repeating timer
that fires once a second and do your UI updates in the timer code.

Continuously append to the end of an array

Each time I press a button I am trying to append that new number to the end of my array. I have tried a ton of different things, but I can't quite get it.
I have tried the append, insert, creating an empty array and so much more.
import UIKit
import Foundation
var num1 = 0
class ViewController: UIViewController {
#IBAction func button(_ sender: UIButton) {
var digitCounts = Array(repeating: 0, count: 10)
var number_list = [1,2,3]
if num1 >= 0 {
digitCounts.insert(3, at: num1)
print(digitCounts)
number_list.append(num1)
num1 += 1
print(num1)
print(number_list)
}
}
the result would look like
starting array [1,2,3] after button pressed first time [1,2,3,4] after
button pressed second time [1,2,3,4,5] after button pressed third time
[1,2,3,4,5,6]
Ok now that you've shown the full code I can see the problem. You are creating new arrays every-time you click the button, but are storing num1 outside of this function.
Try this:
import UIKit
import Foundation
class ViewController: UIViewController {
var num1 = 0
var digitCounts = Array(repeating: 0, count: 10)
var number_list = [1,2,3]
#IBAction func button(_ sender: UIButton) {
if num1 >= 0 {
digitCounts.insert(3, at: num1)
print(digitCounts)
number_list.append(num1)
num1 += 1
print(num1)
print(number_list)
}
}
By storing the arrays as properties of the class, instead of inside the function, they will persist through multiple clicks
you have to make you array global and make changes as follow
import UIKit
import Foundation
var num1 = 0
class ViewController: UIViewController {
var number_list = [1,2,3]
#IBAction func button(_ sender: UIButton) {
var number = number_list.last ?? 0
number += 1
number_list.append(number)
print(number_list)
}
}
If you're not multithreading, a simple solution is to remember the last number instead of having to check the last element in the array:
class ViewController: UIViewController {
var number_list = [1, 2, 3]
var num1 = 3
#IBAction func button(_ sender: UIButton) {
number += 1
number_list.append(number)
print(number_list)
}
}
First of all you should declare var number_list as a ViewController's property:
class ViewController: UIViewController {
var number_list = [1,2,3]
}
Otherwise var number_list with it's default value will be created each time when button(_ sender:) is calling.
And use the following handler for your button:
#IBAction func button(_ sender: UIButton) {
number_list += number_list.last.flatMap { [$0+1] } ?? []
print(number_list)
}
All code in this post was tested in Xcode 10.2.1. I used Swift 5.

How to terminate (not segue) just one view in Swift

I am wondering how to make just 1 view terminate in iOS. I am making an app such that in the viewDidLoad() it generates 52 random strings and appends them to an array. I have it so that when you shake the device, it will perform a segue.
The problem with this is that it keeps the items in the array that I mentioned earlier. This means that when it does the user goes into that view again, it will still have the 52 random strings, plus when it goes through the viewDidLoad function, it will append 52 more strings, so if the user doesn't terminate the whole entire app, but just segues out of the view and goes back in, there will be 104 strings and not 52.
I don't want the above to happen in my app, so I am wondering how you could just terminate this one view while also performing a segue so that this problem won't happen.
Here is some code from my app:
These are the arrays I had:
var theCardsTopPlayerHas = [""]
var theCardsBottomPlayerHas = [""]
var theCardsThatWork = ["2_of_clubs", "2_of_diamonds", "2_of_hearts", "2_of_spades", "3_of_clubs", "3_of_diamonds", "3_of_hearts", "3_of_spades", "4_of_clubs", "4_of_diamonds", "4_of_hearts", "4_of_spades", "5_of_clubs", "5_of_diamonds", "5_of_hearts", "5_of_spades", "6_of_clubs", "6_of_diamonds", "6_of_hearts", "6_of_spades", "7_of_clubs", "7_of_diamonds", "7_of_hearts", "7_of_spades", "8_of_clubs", "8_of_diamonds", "8_of_hearts", "8_of_spades", "9_of_clubs", "9_of_diamonds", "9_of_hearts", "9_of_spades", "10_of_clubs", "10_of_diamonds", "10_of_hearts", "10_of_spades", "jack_of_clubs2", "jack_of_diamonds2", "jack_of_hearts2", "jack_of_spades2", "queen_of_clubs2", "queen_of_diamonds2", "queen_of_hearts2", "queen_of_spades2", "king_of_clubs2", "king_of_diamonds2", "king_of_hearts2", "king_of_spades2","ace_of_clubs", "ace_of_diamonds", "ace_of_hearts", "ace_of_spades"]
Here is what the viewDidLoad method has:
struct shuffledVar {
static var shuffled = [String]();
}
override func viewDidLoad() {
super.viewDidLoad()
upperLabelNumber.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
//MARK: - Shuffling Cards
for _ in 1...52 {
let shuffledCardList = Int(arc4random_uniform(UInt32(theCardsThatWork.count)))
let the_card = theCardsThatWork[shuffledCardList]
print("\(the_card)")
shuffledVar.shuffled.append(the_card)
theCardsThatWork.remove(at: shuffledCardList)
print("Finished Card")
}
theCardsTopPlayerHas = Array(shuffledVar.shuffled[0...25])
theCardsBottomPlayerHas = Array(shuffledVar.shuffled[26...51])
print("")
print(shuffledVar.shuffled)
print("")
print(theCardsTopPlayerHas)
print("")
print(theCardsBottomPlayerHas)
}
This is the code that segues the view:
override func motionBegan(_ motion: UIEventSubtype, with event: UIEvent?) {
performSegue(withIdentifier: "friendToDashboard", sender: self)
}
Thank you!
The problem is that you are using a static var to store your array of strings.
To solve this problem there are several solutions depending on what you need. Here there are a couple of solutions:
Solution 1
If you really need to keep the shuffled array as a Singleto, then you can empty the shuffled array in the viewDidLoad method:
override func viewDidLoad() {
super.viewDidLoad()
shuffledVar.shuffled = []
upperLabelNumber.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
...
}
Solution 2
If you do not need to keep the shuffled array as a Singleton, then you can modify it as follows:
struct shuffledVar {
var shuffled = [String]();
}
var shuffled = shuffledVar()
override func viewDidLoad() {
super.viewDidLoad()
upperLabelNumber.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
//MARK: - Shuffling Cards
for _ in 1...52 {
let shuffledCardList = Int(arc4random_uniform(UInt32(theCardsThatWork.count)))
let the_card = theCardsThatWork[shuffledCardList]
print("\(the_card)")
shuffled.shuffled.append(the_card)
theCardsThatWork.remove(at: shuffledCardList)
print("Finished Card")
}
theCardsTopPlayerHas = Array(shuffled.shuffled[0...25])
theCardsBottomPlayerHas = Array(shuffled.shuffled[26...51])
print("")
print(shuffled.shuffled)
print("")
print(theCardsTopPlayerHas)
print("")
print(theCardsBottomPlayerHas)
}
You are using static var shuffled so you are using static variable. Do you really need a static variable? If not instead of:
struct shuffledVar {
static var shuffled = [String]();
}
use:
var shuffled:[String] = []
and then in code use:
shuffled.append(the_card)
But if you need this variable to be static (for example you are using it in some other view controller) just remove all elements before adding a new one:
//MARK: - Shuffling Cards
shuffledVar.shuffled.removeAll()
You can read more about static vars https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html and more about arrays: https://developer.apple.com/documentation/swift/array

lldb error out of nowhere [duplicate]

This question already has answers here:
"Thread 1: stopped at breakpoint" error when initializing an NSURL object
(3 answers)
Closed 6 years ago.
I get an "lldb" error in my Debug Area when I try to run this app but I don't know why, because I didn't change anything. The screenshot up top shows what happens when the app gets automatically closed.
import UIKit
class ViewController: UIViewController {
#IBOutlet var ScoreLabel: UILabel!
var taps = 0{
didSet {
if taps == 330 {
print("You have reaches 5 taps !!")
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
let defaults = NSUserDefaults.standardUserDefaults()
if let storedTaps = defaults.objectForKey("key") as? Int {
self.taps = storedTaps
setLabel(storedTaps)
}
}
#IBAction func ScoreButton(sender: UIButton) {
taps += 1
setLabel(taps)
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setInteger(taps, forKey: "key")
}
func setLabel(taps:Int) {
ScoreLabel.text = "Taps: \(taps)"
}
}
You have set a breakpoint in your code most probably unintentionally. Just hold and drag it out till you see an 'X' to remove that breakpoint or right click on it and delete/disable breakpoint.

Launch saved state at startup of app (swift)

what the app does so far is that users go through an array of strings by clicking a button, when they leave and remove the app from multitasking it saves where they were in the array. But when you startup the app you get stuck at the first String but you have to press the button called showFunFact() to get where you left off. I would like the current one to come at startup
Here's the code:
let TechnologyfactBook = TechFactBook()
var TechfactIndex = 0
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func showFunFact() {
if (UIApplication.sharedApplication().applicationIconBadgeNumber != 0){
UIApplication.sharedApplication().applicationIconBadgeNumber = 0
}
if (TechfactIndex >= TechnologyfactBook.TechfactsArray.count) {
self.TechfactIndex = 0
}
TechfactIndex = NSUserDefaults.standardUserDefaults().integerForKey("ByteLocation")
TechByteLabel.text = TechnologyfactBook.TechfactsArray[TechfactIndex]
TechfactIndex++
NSUserDefaults.standardUserDefaults().setInteger(TechfactIndex, forKey: "ByteLocation")
NSUserDefaults.standardUserDefaults().synchronize()
}
Just add the code that loads the last location into the viewDidLoad. What the code does is first it checks if the user's last location is greater than the starting position, which I assume is 0. Once it checks this, it will assign the text value to the value that the user last had.
override func viewDidLoad() {
super.viewDidLoad()
//Checks if the value that is saved into the app is greater than the
//starting position the app first starts at (assuming index 0?)
if NSUserDefaults.standardUserDefaults().integerForKey("ByteLocation") != 0 {
TechByteLabel.text = TechnologyfactBook.TechfactsArray[TechfactIndex]
} else {
//The code that first displays the values if the user
//just started the app
}
}

Resources