I'm trying to write simple clicker but I have some problem, on "StartViewController" I have func value() it add per sec 1 + click 1 + last save value and I have shopViewController on the shopViewController I have button when I press it it must give +1(every time) to click but how can I access to current value from func value()? When I try get current value I get nil
func value() -> Float {
let endValue = appPerSec + valueToLoad + click
valueToSave = endValue
valueLable.text = "\(endValue)"
return valueToSave
}
// shopViewController
var StartView:StartViewController!
var currentValue:Float! = 0.0
#IBAction func testAdd(_ sender: Any) {
currentValue = StartView.value // here I get NIL
print(currentValue)
}
i did not UnderStand your question but i can give solution in parts for the terms i can read, or please improve your question so that i can understand exactly what you are looking for.
meanwhile i'll just give some concepts that i think you are looking for, if not useful please edit your question before rating not useful :
to make a timer you can use
#IBOutlet weak var timeInTimer: UILabel!
var timer = Timer()
#IBAction func buttonPressed(_ sender: Any) //to stop the timer
{
timer.invalidate()
}
#IBAction func playButtonPressed(_ sender: Any) //to start the timer
{
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.processTimer), userInfo: nil, repeats: true)
}
To pass data from one view to other view :
let anyObjectName = storyboard?.instantiateViewController(withIdentifier: "IdentifierOfSecondViewController") as! IdentifierOfSecondViewController
anyObjectName.userName = userName //where username is a variable in second view
You can use Delegate and Protocols. Above your shopView add:
protocol ShopProtocol{
func updateCount();
}
In your shopView add:
var delegateShop:ShopProtocol?
And when you change that variable, go to:
delegateShop.updateCount()
In your main view controller, when you present this shopViewController (before presenting it), add:
shopController.delegateShop = self
And change your ViewController definition to
class ...: UIViewController, ..., ShopProtocol{
And, of course, create in that view controller:
func updateCount(){
//do Stuff
}
Related
I have two view controllers (ViewController and ActionViewController) and one manager (Brain), the second view controller is shown when a user tapped on a button by a show segue created in storyboard and to get back to the first I use a self.dismiss in the second view controller.
The user enter a number on ActionViewController that need to be retrieved in ViewController. So I created Brain to use the delegate pattern.
The problem is that the delegate function inside ViewController is never run, I read other SO answers but nothing work. I used print statement to know where the code is not running anymore and the only block not running is the didUpdatePrice inside ViewController
Here is the code
ViewController
class ViewController: UIViewController, BrainDelegate {
var brain = Brain()
#IBOutlet var scoreLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
brain.delegate = self
scoreLabel.layer.cornerRadius = 25
scoreLabel.layer.masksToBounds = true
}
func didUpdateScore(newScore: String) {
print("the new label is \(newScore)")
scoreLabel.text = newScore
}
}
ActionViewController
class ActionViewController: UIViewController {
var brain = Brain()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func addButtonTapped(_ sender: Any) {
brain.newAction(actualScore: 0, newActionValue: 5, isPositive: true)
self.dismiss(animated: true)
}
}
Brain
protocol BrainDelegate {
func didUpdateScore(newScore: String)
}
struct Brain {
var delegate: BrainDelegate?
func newAction(actualScore: Int, newActionValue: Int, isPositive: Bool) {
let newScore: Int
if isPositive {
newScore = actualScore + newActionValue
} else {
newScore = actualScore - newActionValue
}
print("the new score is \(newScore)")
delegate?.didUpdateScore(newScore: String(newScore))
}
}
You dont need an additional Brain class/struct at all, You can achieve it with simple protocol and default extension of protocol.
Step 1: Select your show segue and provide an identifier to that in storyboard as shown below
Step 2: In your ViewController add prepare(for segue method
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "testIdentifier" {
guard let destinationViewController = segue.destination as? ActionViewController else { return }
destinationViewController.delegate = self
}
}
Step 3: In your ActionViewController declare a weak property named delegate
class ActionViewController: UIViewController {
weak var delegate: BrainDelegate? = nil
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func addButtonTapped(_ sender: Any) {
delegate?.newAction(actualScore: 0, newActionValue: 5, isPositive: true)
self.dismiss(animated: true)
}
}
Step 4: Add class clause to your BrainDelegate (Class bound protocol) so that you can hold a weak reference to delegate
protocol BrainDelegate: class {
func didUpdateScore(newScore: String)
func newAction(actualScore: Int, newActionValue: Int, isPositive: Bool)
}
Step 5:
Add a default extension to BrainDelegate and provide default implementation of newAction(actualScore:
extension BrainDelegate {
func newAction(actualScore: Int, newActionValue: Int, isPositive: Bool) {
let newScore: Int
if isPositive {
newScore = actualScore + newActionValue
} else {
newScore = actualScore - newActionValue
}
print("the new score is \(newScore)")
self.didUpdateScore(newScore: String(newScore))
}
}
Step 6: In your ActionViewController simply trigger delegate methods as
#IBAction func addButtonTapped(_ sender: Any) {
delegate?.newAction(actualScore: 0, newActionValue: 5, isPositive: true)
self.dismiss(animated: true)
}
This should do the job
First, on Brain you should use class, not struct. That is because when you use struct, passing the variable to another will make a copy, it will not use the same reference. And class will only copy the reference.
That means that your Brain struct will lose the delegate assigned on .delegate = self
second, you need to use the same instance on the second viewController and the first. like this:
on the first viewController
var brain = Brain()
// this one is the one that you will put your "brain.delegate = self"
on the second viewController, you will need to inject this variable from the first viewController into the second. That is to keep the same instance on both. And this will make the delegate callable.
to do this with storyboard you will do on the first ViewController:
// this function should be called when the next viewController should open.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.destination) {
case let vc as MyViewController:
vc.brain = self.brain
default:
break
}
}
super.prepare(for: segue, sender: sender)
}
inside the second viewController, use:
var brain: Brain?
I have a label (in my secondVC) which is displaying a segued Double from the firstVC. In the secondVCs viewDidLoad I am printing the passedDouble and it is printing the correct amount, so I know my Double is being segued correctly. My UILabel is in the secondVC and only shows an amount if the passedDouble = 0.0.
SecondViewController:
#IBOutlet weak var totalLabel: UILabel!
var passedDouble = 0.0
override func viewDidLoad() {
super.viewDidLoad()
print("testing for a value \(passedDouble)")
totalLabel.text = String("\(passedDouble)")
}
If the passed value is 12.2 for example, it prints this
testing for a value 12.2
But the label completely disappears from view.
If the passed value is 0.0 however, it prints
testing for a value 0.0
and the label shows 0.0
In the storyboard I have left the labels standard text as Label. So, I know the label is connected properly as it's text changes IF the value is nil.
EDIT: Code for the firstVC where I am assigning the value
var totalPrice: Double = Double()
#IBAction func basketAction(_ sender: UIButton) {
for cost in priceDictionaryToBeSent {
totalPrice += Double(cost)!
}
performSegue(withIdentifier: "basket", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "basket" {
if let basketVC = segue.destination as? BasketViewController {
basketVC.passedDouble = totalPrice
//This is sending the correct price
}
}
}
My best guess is that in prepare(for:sender:) the basketVC's view is already loaded, so its viewDidLoad got called before you are setting basketVC.passedDouble = totalPrice.
I would rather use setter to update the label everytime the passedDouble gets updated. Change the BasketViewController code to this:
#IBOutlet weak var totalLabel: UILabel!
var passedDouble = 0.0 {
didSet {
self.totalLabel?.text = "\(passedDouble)"
}
}
override func viewDidLoad() {
super.viewDidLoad()
// no need to set totalLabel.text here
}
Thank you to Milan Nosáľ for saying the view could've already loaded, indeed it had, so I moved the code from viewDidLoad in my BasketViewController to viewDidAppear as seen below:
override func viewDidAppear(_ animated: true) {
super.viewDidAppear(true)
print("testing for total price £\(passedDouble)")
totalLabel.text = "Total price £\(passedDouble)"
}
This question already has answers here:
Sending Variables with a Segue
(3 answers)
Passing data between view controllers
(45 answers)
Closed 4 years ago.
I tried following this guide that used string value but it wasn't that helpful. I'm working on an app that has 8 buttons, each will be assigned with a number representing seconds converted to minutes in CountdownVC. I'm trying to pass the Int value when I tap a button and assign the value to var seconds = 0 in CountdownVC.
When pressing the button in the MainVC:
#IBAction func oneMinuteBtnPressed(_ sender: Any) {
let vc = CountdownVC()
vc.seconds = 60
performSegue(withIdentifier: "toCountdownVC", sender: self)
}
I want to pass the assigned value to the CountdownVC's making it var seconds = 60:
import UIKit
class CountdownVC: UIViewController {
#IBOutlet weak var countdownLbl: UILabel!
#IBOutlet weak var startBtn: UIButton!
#IBOutlet weak var pauseBtn: UIButton!
#IBOutlet weak var resetBtn: UIButton!
// Variables
var seconds = 0 // This variable will hold the starting value of seconds. It could be any amount over 0
var timer = Timer()
var isTimerRunning = false // This will be used to make sure the only one time is created at a time.
var resumeTapped = false
override func viewDidLoad() {
super.viewDidLoad()
pauseBtn.isEnabled = false
}
func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(CountdownVC.updateTimer)), userInfo: nil, repeats: true)
isTimerRunning = true
pauseBtn.isEnabled = true
}
#objc func updateTimer(){
if seconds < 1 {
timer.invalidate()
//TODO: Send alert to indicate "time is up!"
} else {
seconds -= 1 //This will decrement(count down) the seconds.
countdownLbl.text = timeString(time: TimeInterval(seconds)) //This will update the label
}
}
func timeString(time: TimeInterval) -> String {
let hours = Int(time) / 3600
let minutes = Int(time) / 60 % 60
let seconds = Int(time) % 60
return String(format: "%02i:%02i:%02i", hours, minutes, seconds)
}
What I have above doesn't seem to work except for the segue part. Any help would be appreciated.
As Quoc Nguyen pointed it out, you are assigning a value to a controller that will never be used.
When you call performSegue, this will instantiate a different vc from the one you have created.
If you want to pass something to the vc that is created by your storyboard you need to use something like this:
#IBAction func oneMinuteBtnPressed(_ sender: Any) {
performSegue(withIdentifier: "toCountdownVC", sender: self)
}
// This function is called before the segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// get a reference to the your view controller
if let nextViewController = segue.destination as? CountdownVC {
nextViewController.seconds = 60
}
}
I have a watchkit timer,
However, I have no idea how to perform an action like a notification or segue when the timer ends.
Any ideas?
#IBOutlet var sceneTimer: WKInterfaceTimer!
override func awake(withContext context: Any?) {
super.awake(withContext: context)
sceneTimer.setDate(NSDate(timeIntervalSinceNow :21) as Date)
}
override func willActivate() {
super.willActivate()
sceneTimer.start()
}
override func didDeactivate() {
super.didDeactivate()
}
Yes I am new to this whole thing so its not a ground-breaking code, feel free to correct.
Someone could certainly improve upon this I'm sure. But I built a little watchOS App with a Start Button, a timer and a label. Hook up your WKInterfaceTimer, WKInterfaceLabel and Button as appropriate and this code should work. You can also download the project from GitHub.
var theTimer = Timer()
var backgroundTimer = TimeInterval(15)
#IBOutlet var appleTimer: WKInterfaceTimer!
#IBOutlet var label: WKInterfaceLabel!
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
appleTimer.setDate(Date(timeIntervalSinceNow: 15))
label.setText("")
}
#IBAction func startButton() {
let startTime = Date(timeIntervalSinceNow: 15)
appleTimer.setDate(startTime)
appleTimer.start()
// This will call timerCountDown() once per second until conditions are met.
theTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timerCountDown), userInfo: nil, repeats: true)
}
func timerCountDown() {
backgroundTimer -= 1.0
print(backgroundTimer)
if backgroundTimer == 0 {
theTimer.invalidate()
appleTimer.stop()
// You could call an Alert Action here.
label.setText("Timer Done!")
}
}
WKInterfaceTimer is just a label that can be used to display a countdown. It has no associated function that is called by the system once the countdown reaches zero. You need to configure a Timer object with the same target date if you need to know when the countdown reaches 0.
This is my first post so I hope this is a valid question. I've searched the forums for an answer to this with no luck. Below is my code for a stopwatch app. Problem I am having is when the play button is clicked multiple times it is ticking multiple seconds at a time. How do I safely stop this from happening?
ViewController
import UIKit
class ViewController: UIViewController {
// MARK: Properties
#IBOutlet weak var timerLabel: UILabel!
var timer = NSTimer()
var time = 0
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: Functions
func increaseTimer() {
time++
let formattedTime = String(format:"%02d:%02d", (time/60), time%60)
timerLabel.text = "\(formattedTime)"
}
// MARK: Actions
#IBAction func btnPlay(sender: AnyObject) {
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self,
selector: Selector("increaseTimer"), userInfo: nil, repeats: true)
}
#IBAction func btnStop(sender: AnyObject) {
timer.invalidate()
}
#IBAction func btnReset(sender: AnyObject) {
timer.invalidate()
time = 0
timerLabel.text = "00:00"
}
}
EDIT: SOVED http://imgur.com/mqw1Xnp
Make your button into a start/stop button. Use a boolean instance variable to keep track of whether the timer is running or not. If it is, stop it. If it's not, start it.
Alternately, make the code that starts the timer set button.disabled = true