Timer counts but doesn´t desplay - swift

I just started to learn how to code and speak English^^
When I go from secondViewController to ViewController, a timer starts. A label shows the timer.
My problem is, when I go from secondViewController to ViewController for the second time, the label doesn´t show the timer. But the timer continue to count (I printed it out). Why the label doesn´t shoews the timer ?
I would be very grateful for your help
ViewController
var habit = 0
var statusFirst = false
class ViewController: UIViewController {
var firstTitel = ""
var secondTitels = ""
var timerTime = 0
var timer = Timer()
#IBOutlet weak var titelLabel: UILabel!
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var secondTitel: UILabel!
#IBOutlet weak var secondDate: UILabel!
#objc func processTimer() {
timerTime += 1
dateLabel.text = String(timerTime)
print(timerTime)
}
override func viewDidLoad() {
super.viewDidLoad()
// Hide Label - START
print(habit)
if habit == 0 {
titelLabel.isHidden = true
secondTitel.isHidden = true
dateLabel.isHidden = true
secondDate.isHidden = true
timer.invalidate()
}else if habit == 1 {
titelLabel.isHidden = false
secondTitel.isHidden = true
dateLabel.isHidden = false
secondDate.isHidden = true
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.processTimer), userInfo: nil, repeats: true)
}else if habit == 2 {
titelLabel.isHidden = false
secondTitel.isHidden = false
dateLabel.isHidden = false
secondDate.isHidden = false
}
// Hide Label - END
let titelObject = UserDefaults.standard.object(forKey: "firstTitel")
if let titel = titelObject as? String {
titelLabel.text = titel
}
let titellObject = UserDefaults.standard.object(forKey: "secondTitel")
if let titell = titellObject as? String {
secondTitel.text = titell
}
// 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.
}
}
SecondViewController
class SecondViewController: UIViewController {
var titelSecondViewController = ""
var dateSecondViewController = 0
#IBOutlet weak var titelField: UITextField!
#IBOutlet weak var dateField: UITextField!
#IBOutlet weak var okButton: UIButton!
#IBAction func okButtonAction(_ sender: Any) {
titelSecondViewController = titelField.text!
if let datefield = dateField.text {
if datefield == "" {
dateSecondViewController = 0
}else{
dateSecondViewController = Int(datefield)!
}
}else{
print("lol")
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
// Segue - Start
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toViewController" {
let viewController = segue.destination as! ViewController
habit += 1
print(habit)
if habit == 1 {
statusFirst = true
UserDefaults.standard.set(titelSecondViewController, forKey: "firstTitel")
}else if habit == 2{
UserDefaults.standard.set(titelSecondViewController, forKey: "secondTitel")
}else{
return
}
}
}
// Segue - End
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

In first case habit = 1 so the timer is called
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.processTimer), userInfo: nil, repeats: true)
since the timer has a strong reference and you don't invalidate() it inside viewDidDisappear , it keeps the VC in memory after you dismiss it
//
In second time habit = 2 and you don't init the time inside it's if statement then the label of the second instance ( the presented one ) doesn't update , while the hidden dismissed one do

Related

Force unwrapping nil optional for UIImageView when transitioning to view controller

I'm running into an error when transitioning to view controllers by overriding the built-in prepare() function in Swift. I have a UIImageView for backgrounds on my screens. Here is the code for two of the view controllers in question.
import UIKit
import FirebaseAuth
class HomeVC: UIViewController {
#IBOutlet weak var signOutButton: UIButton!
#IBOutlet weak var backgroundImageView: UIImageView!
#IBOutlet weak var friendsNavButton: UIButton!
#IBOutlet weak var homeNavButton: UIButton!
#IBOutlet weak var profileNavButton: UIButton!
#IBOutlet weak var bumpButton: UIButton!
#IBOutlet weak var welcomeLabel: UILabel!
#IBOutlet weak var doNotDisturbLabel: UILabel!
#IBOutlet weak var doNotDisturbButton: UIButton!
var userName = ""
var dndIsOn: Bool = false
#IBAction func dndToggled(_ sender: Any) {
dndIsOn = !dndIsOn
User.current.available = !dndIsOn
FirestoreService.db.collection(Constants.Firestore.Collections.users).document(User.current.uid).updateData([Constants.Firestore.Keys.available : !dndIsOn])
if dndIsOn {
print("DND is on!")
setupDNDUI()
} else if !dndIsOn {
print("DND is off!")
setupActiveUI()
}
}
#IBAction func signOutTapped(_ sender: Any) {
let firAuth = Auth.auth()
do {
try firAuth.signOut()
} catch let signOutError as NSError {
print ("Error signing out: %#", signOutError)
}
print("Successfully signed out")
}
#IBAction func bumpTapped(_ sender: Any) {
self.performSegue(withIdentifier: Constants.Segues.toCall, sender: self)
}
#IBAction func friendsNavTapped(_ sender: Any) {
self.performSegue(withIdentifier: Constants.Segues.toFriends, sender: self)
}
#IBAction func profileNavTapped(_ sender: Any) {
let nav = self.navigationController //grab an instance of the current navigationController
DispatchQueue.main.async { //make sure all UI updates are on the main thread.
nav?.view.layer.add(CATransition().segueFromLeft(), forKey: nil)
nav?.pushViewController(ProfileVC(), animated: false)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(true, animated: true)
self.backgroundImageView.contentMode = UIView.ContentMode.scaleAspectFill
doNotDisturbLabel.isHidden = true
if !userName.isEmpty {
welcomeLabel.text = "Welcome Back, " + userName + "!"
} else {
welcomeLabel.text = ""
}
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .darkContent
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let friendsVC = segue.destination as? FriendsVC else {
return
}
FirestoreService.db.collection(Constants.Firestore.Collections.users).document(User.current.uid).getDocument { (snapshot, err) in
if let err = err {
print(err.localizedDescription)
} else {
let data = snapshot!.data()!
let requests = data[Constants.Firestore.Keys.requests] as? [String]
if let requests = requests {
friendsVC.requests = requests
}
}
}
}
class FriendsVC: UIViewController {
//var friends: [Friend] = User.current.friends
var friends: [User] = []
var requests: [String]?
#IBOutlet weak var requestsNumberLabel: UILabel!
#IBOutlet weak var backgroundImageView: UIImageView!
#IBOutlet weak var friendRequestsButton: UIButton!
#IBOutlet weak var homeNavButton: UIButton!
#IBOutlet weak var friendsTitle: UILabel!
#IBOutlet weak var friendTableView: UITableView!
#IBOutlet weak var addFriendButton: UIButton!
#IBOutlet weak var tableViewTopConstraint: NSLayoutConstraint!
#IBAction func friendRequestsTapped(_ sender: Any) {
self.performSegue(withIdentifier: Constants.Segues.toRequests, sender: self)
}
#IBAction func homeNavTapped(_ sender: Any) {
let nav = self.navigationController //grab an instance of the current navigationController
DispatchQueue.main.async { //make sure all UI updates are on the main thread.
nav?.view.layer.add(CATransition().segueFromLeft(), forKey: nil)
nav?.pushViewController(HomeVC(), animated: false)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(true, animated: true)
backgroundImageView.contentMode = UIView.ContentMode.scaleAspectFill
friendTableView.backgroundView?.backgroundColor = .white
friendsTitle.isHidden = false
UserService.getUserArray(uids: User.current.friendUids, completion: { (users) in
guard let users = users else {
print("User has no friends")
return
}
self.friends = users
self.friendTableView.reloadData()
})
guard let requests = self.requests else {
friendRequestsButton.isHidden = true
requestsNumberLabel.isHidden = true
self.tableViewTopConstraint.constant = 0
return
}
requestsNumberLabel.text = requests.count.description
// Do any additional setup after loading the view.
friendTableView.delegate = self
friendTableView.dataSource = self
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let homeVC = segue.destination as? HomeVC {
homeVC.userName = User.current.firstName
} else if let requestsVC = segue.destination as? RequestsVC {
UserService.getUserArray(uids: self.requests!) { (requesters) in
if let requesters = requesters {
requestsVC.requesters = requesters
}
}
}
}
}
When my app loads into the home screen, there is no problem, and when a button is tapped to transition to FriendsVC, there is no problem. However, when I try to initiate the transition from HomeVC to ProfileVC or from FriendVC to HomeVC, I get the error: "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value" at the self.backgroundImageView.contentMode = UIView.ContentMode.scaleAspectFill lines in my viewDidLoad methods. These segues have something in common in that these are the ones where I override the prepare() function, but I'm not sure what I'm doing wrong

Swift: Why can I pass info from one IBAction to another, but not to a function

I am trying to get the values for splitValue and tipPercent into the getSettings() at the bottom. Why can I get the values for both of those in the IBAction calculatePressed, but when I try to get the values into the function the value is nil. I am sooo confused. Thank you for the help!
#IBOutlet weak var billTextField: UITextField!
#IBOutlet weak var zeroPctButton: UIButton!
#IBOutlet weak var tenPctButton: UIButton!
#IBOutlet weak var twentyPctButton: UIButton!
#IBOutlet weak var splitNumberLabel: UILabel!
var tipChosen = ""
var totalPerPerson = ""
var tipPercent = ""
var splitValue = ""
#IBAction func tipChanged(_ sender: UIButton) {
tipPercent = sender.currentTitle!
if sender.isSelected == true {
return
}
zeroPctButton.isSelected = false
tenPctButton.isSelected = false
twentyPctButton.isSelected = false
sender.isSelected = true
if sender.currentTitle == "0%" {
tipChosen = "0.00"
} else if sender.currentTitle == "10%" {
tipChosen = "0.10"
} else if sender.currentTitle == "20%" {
tipChosen = "0.20"
}
billTextField.endEditing(true)
}
#IBAction func stepperValueChanged(_ sender: UIStepper) {
splitValue = String(Int(sender.value))
splitNumberLabel.text = String(Int(sender.value))
}
#IBAction func calculatePressed(_ sender: UIButton) {
let bill = Float(billTextField.text!)!
let tip = Float(tipChosen)!
let tax = bill * tip
let splitNumber = Float(splitNumberLabel.text!)
let total = (bill + tax) / Float(splitNumber!)
totalPerPerson = "$\(String(format: "%.2f", total))"
performSegue(withIdentifier: "goToTotal", sender: self)
}
func getSettings() -> String {
return "Split between \(splitValue) people, with a \(tipPercent) tip."
}
Ok, sorry it took me a bit, but I finally think I understand what I did.
class CalculatorViewController: UIViewController {
var tip = 0.0
var finalBill = ""
var split = 2
#IBOutlet weak var billTextField: UITextField!
#IBOutlet weak var zeroPctButton: UIButton!
#IBOutlet weak var tenPctButton: UIButton!
#IBOutlet weak var twentyPctButton: UIButton!
#IBOutlet weak var splitNumberLabel: UILabel!
#IBAction func tipChanged(_ sender: UIButton) {
if sender.isSelected == false {
sender.isSelected = false
} else if sender.isSelected == true {
sender.isSelected = true
}
zeroPctButton.isSelected = false
tenPctButton.isSelected = false
twentyPctButton.isSelected = false
sender.isSelected = true
billTextField.endEditing(true)
}
#IBAction func stepperValueChanged(_ sender: UIStepper) {
splitNumberLabel.text = Int(sender.value).description
}
#IBAction func calculatePressed(_ sender: UIButton) {
if zeroPctButton.isSelected == true {
tip = 0.0
} else if tenPctButton.isSelected == true {
tip = 0.1
} else if twentyPctButton.isSelected == true {
tip = 0.2
}
print(tip)
let bill = Double(billTextField.text!)
split = Int(Double(splitNumberLabel.text!)!)
if billTextField.text != "" {
let billWithTip = (bill! * tip) + bill!
let billWithTipSplit = billWithTip / Double(split)
finalBill = String(format: "%.2f", billWithTipSplit)
print(billWithTip)
}
self.performSegue(withIdentifier: "getResults", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "getResults" {
let destinationVC = segue.destination as! ResultsViewController
destinationVC.finalBill = finalBill
destinationVC.split = split
destinationVC.tip = tip
}
}
}
class ResultsViewController: UIViewController {
#IBOutlet weak var totalLabel: UILabel!
#IBOutlet weak var settingsLabel: UILabel!
var tip = 0.0
var split = 2
var finalBill = ""
override func viewDidLoad() {
super.viewDidLoad()
totalLabel.text = "$\(finalBill)"
settingsLabel.text = "Split between \(Int(split)) people, with a \(Int(tip * 100))% tip"
}
#IBAction func recalculatePressed(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
}
}
I did what you suggested with the string and some minor calculations on the second view controller, changed the values of a few declared properties and got rid of the getSettings(). I was under the impression that I couldn't pass data without a return value from a function. Thank you for the help!

xcode 10.1: Timer stops at 1 not 0

Yep, I have seen a few questions similar to mine but for some reason, I couldn't duplicate it! If I use == or <= with If both are giving me stop at 1. The only < gave me the 0 stops which are good, but very rare I got -1. Any help, please?
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var startButton: UIButton!
var timer = Timer()
var timeLeft = 10
override func viewDidLoad() {
timeLabel.text = String(10)
super.viewDidLoad()
}
#IBAction func pressTimer(_ sender: Any) {
startButton.isEnabled = false
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(startTime), userInfo: nil, repeats: true)
}
#objc func startTime() {
timeLeft -= 1
timeLabel.text = "\(timeLeft)"
if timeLeft < 0 {
startButton.isEnabled = false
self.timer.invalidate()
}
}
}
My suggestion is to declare the timer as optional and check for zero before decrementing the counter.
And set the counter to 10 in the pressTimer method
class ViewController: UIViewController {
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var startButton: UIButton!
var timer : Timer?
var timeLeft = 0
#IBAction func pressTimer(_ sender: Any) {
startButton.isEnabled = false
timeLeft = 10
timeLabel.text = String(timeLeft)
if timer == nil {
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(startTime), userInfo: nil, repeats: true)
}
}
#objc func startTime()
{
if timeLeft == 0 {
startButton.isEnabled = false // shouldn't it be true??
timer?.invalidate()
timer = nil
} else {
timeLeft -= 1
timeLabel.text = String(timeLeft)
}
}
}

Show ViewController programmatically (not working)

I got a main VC called "ViewController.swift", which is bond to my ViewController Scene in Main.storyboard.
Then I want another screen, which shows when a condition in my ViewController.swift is fulfilled.
Therefore I got a Gameover.storyboard, which has a VC Scene with Storyboard ID "overID" as well as a "GameoverViewController.swift" to handle the GameOver Scene.
Here is my code in ViewController.swift, which should open Gameover.storyboards VC Scene when the condition you see below is fulfilled:
import UIKit
class ViewController: UIViewController {
var wantedLiter = 10.00
var timer : Timer?
var actualLiter = 0.00
var timerReset = true
var preisProLiter = 1.26
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
let storyBoard: UIStoryboard = UIStoryboard(name: "Gameover", bundle: nil)
let gameoverViewController = storyBoard.instantiateViewController(withIdentifier: "overID") as! GameoverViewController
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#IBOutlet weak var preisliterLabel: UILabel!
#IBOutlet weak var euroLabel: UILabel!
#IBOutlet weak var numberLabel: UILabel!
#IBOutlet weak var numberButton: UIButton!
#IBOutlet weak var confirmButton: UIButton!
#IBAction func confirm(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
numberLabel.text = String(round(1000*actualLiter)/1000) //initial value
preisliterLabel.text = String(preisProLiter)
// Do any additional setup after loading the view.
}
#IBAction func holdingTheButton(_ sender: Any) {
print("I am holding")
timerReset = false // reset to false since you are holding the button
guard timer == nil else { return }
timer = Timer.scheduledTimer(timeInterval: 0.005, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
#IBAction func buttonReleased(_ sender: Any) {
print("button released")
if (wantedLiter == actualLiter){
print("WIN!")
actualLiter = 0;
}
//startTime = 0.00
timer?.invalidate()
timer = nil
// timerReset = true // reset to true since you released.
}
#objc func updateTime(){
//update label every second
print("updating label ")
if (wantedLiter <= actualLiter){
print("Ooops")
actualLiter = 0;
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
self.present(gameoverViewController, animated: true, completion: nil)
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}
actualLiter += 0.01
numberLabel.text = String(round(1000*actualLiter)/1000)
euroLabel.text = String((round(100*actualLiter*preisProLiter)/100))
}
}
And now got you some Screenshots, which might help.
If you need more Screens, please ask.
Right now, I got following errors:
1) ViewController.swift in the upper part marked with "!!!!..."
Cannot use instance member 'storyBoard' within property initializer; property initializers run before 'self' is available
2) ViewController.swift
Cannot convert value of type 'gameoverViewController.Type' to expected argument type 'UIViewController'
3) ViewController.swift upper Part:
Use of undeclared type 'GameoverViewController'
This is how I changed the code:
import UIKit
class ViewController: UIViewController {
var wantedLiter = 10.00
var timer : Timer?
var actualLiter = 0.00
var timerReset = true
var preisProLiter = 1.26
let storyBoard: UIStoryboard = UIStoryboard(name: "Gameover", bundle: nil)
var gameoverViewController: GameoverViewController!
#IBOutlet weak var preisliterLabel: UILabel!
#IBOutlet weak var euroLabel: UILabel!
#IBOutlet weak var numberLabel: UILabel!
#IBOutlet weak var numberButton: UIButton!
#IBOutlet weak var confirmButton: UIButton!
#IBAction func confirm(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
gameoverViewController = storyBoard.instantiateViewController(withIdentifier: "overID") as? GameoverViewController
numberLabel.text = String(round(1000*actualLiter)/1000) //initial value
preisliterLabel.text = String(preisProLiter)
// Do any additional setup after loading the view.
}
#IBAction func holdingTheButton(_ sender: Any) {
print("I am holding")
timerReset = false // reset to false since you are holding the button
guard timer == nil else { return }
timer = Timer.scheduledTimer(timeInterval: 0.005, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
#IBAction func buttonReleased(_ sender: Any) {
print("button released")
if (wantedLiter == actualLiter){
print("WIN!")
actualLiter = 0;
}
//startTime = 0.00
timer?.invalidate()
timer = nil
// timerReset = true // reset to true since you released.
}
#objc func updateTime(){
//update label every second
print("updating label ")
if (wantedLiter <= actualLiter){
print("Ooops")
actualLiter = 0;
self.present(gameoverViewController as! UIViewController, animated: true, completion: nil)
}
actualLiter += 0.01
numberLabel.text = String(round(1000*actualLiter)/1000)
euroLabel.text = String((round(100*actualLiter*preisProLiter)/100))
}
}
(not working)
And I changed the GameoverViewController type to UIViewController.
But where was my Typo?? (G instead of g)?
Move the initializer for gameoverViewController into viewDidLoad or the like and make gameoverViewController an optional or implicitly unwrapped optional (that's the ! after a type)
Should be fixed by 1.
Please show the source for GameoverViewController if this is not fixed by 1. too.

Run Counter while button press

I am very new to swift and I want to make a button work like this:
Press the Button and hold. While the Button is pressed the label value goes up like every second +1 until the button is released.
This is what I get so far:
class ViewController: UIViewController {
var counter = 0;
override func viewDidLoad() {
super.viewDidLoad();
}
#IBOutlet weak var label: UILabel!
#IBAction func btn(_ sender: Any) {
if (sender as AnyObject).state != .ended{
counter+=1;
// wait 100ms
self.label.text = String (counter);
}
}
}
This is how I linked it:
You can achieve this using the UIButton actions Touch Down and Touch Up Inside
class ViewController: UIViewController {
var timer : Timer?
var startTime = 0
var timerReset = true // I don't know what logic you want. This basically has been added so the number is not set to 0 immediately when you release the button. You can also add another button to reset startTime variable and the label
#IBOutlet weak var numberLabel: UILabel!
#IBOutlet weak var numberButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
numberLabel.text = String(startTime) //initial value
// Do any additional setup after loading the view.
}
#IBAction func holdingTheButton(_ sender: Any) {
print("I am holding")
timerReset = false // reset to false since you are holding the button
guard timer == nil else { return }
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
#IBAction func buttonReleased(_ sender: Any) {
print("button released")
startTime = 0
timer?.invalidate()
timer = nil
timerReset = true // reset to true since you released.
}
#objc func updateTime(){
//update label every second
print("updating label ")
if timerReset {
startTime = 0
}
startTime += 1
numberLabel.text = String(startTime)
}
}
IMPORTANT: Make sure you are connecting in the right way. Touch Down has to be used if you want to call the function while you hold the button:
In your console, you should see this happening if you release the button after 10 SECONDS:
If you want to have a button to reset, you can just add the it and then connect it to the following function (but also make sure you remove the bool timeReset and the if statement inside updateTime:
#IBAction func resetTimer(_ sender: Any) {
startTime = 0
numberLabel.text = String(startTime)
}
You can achieve it using two sent event touch of UIButton and a Timer.
var counter = 0
var timer: Timer?
#IBAction func buttonTouchUpInside(_ sender: Any) {
timer?.invalidate()
print(counter)
}
#IBAction func buttonTouchDown(_ sender: Any) {
timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(increaseCount), userInfo: nil, repeats: true)
}
#objc func increaseCount() {
counter += 1
print(counter)
}