Is it possible to have an #IB Action function inside of viewDidLoad() ?
The action is a simple one - a Stepper that increases other label.text values accordingly. However, the values that the stepper needs to work with depend on the return content of a url - which are only known after the viewDidLoad() of course.
So I think I can't have the IBaction way up on top before the viewDidLoad(), and the error I get if I try to do my IB action inside of the viewDidLoad() is:
"Only instance methods can be declared ‘IBAction' ”
EDIT
Let me clarify myself, sorry for the confusion. I know I need an outlet to get the UIStepper values from. I have that:
#IBOutlet weak var stepper: UIStepper!
I then have an action also connected to same UIStepper that will increase/decrease value of a label's text (new_total) accordingly:
#IBOutlet weak var new_total: UILabel!
#IBAction func step_up_pass(sender: AnyObject) {
new_total.text = "\(Int(stepper.value))"
}
However, I want to start out with a value (todays_price) I'm getting back from a json request and use that as a starting point, to multiply it using the stepper and put the multiplied value into the label's text.
I have a struct in a separate file that defines my object so:
struct PassengerFromOtherBus {
var fname: String?
var lname: String?
var todays_price: Int?
init(json: NSDictionary) {
self.fname = json["fname"] as? String
self.lname = json["lname"] as? String
self.todays_price = json["todays_price"] as? Int
}
}
So later on in the view controller, inside of the viewDidLoad(), after connecting to the URL and then parsing it using NSJSONSerialization and a bunch of other code here (that I don't need to confuse you with) I finally have my value todays_price. So my question is, how do I get my action to use that value when it's only known inside of my viewDidLoad()? Xcode will not even let me connect the IBAction to anywhere inside the viewDidLoad function!
This is not done with an Action but with an Outlet. Connect the Stepper from IB as an Outlet to your ViewController. Then just set the values of the Stepper in ViewDidLoad.
I would never go directly from a UIStepper.value to UILabel.text.
Use an intermediary variable to store the value.
Do the same for the return from the JSON. By setting a didSet function on those variables you can update the UI when any of the values is updated.
class FirstViewController: UIViewController {
var todays_price: Int = 0 {
didSet { // didSet to trigger UI update
myLabel.text = "\(stepperValue * todays_price)"
}
}
var stepperValue : Int = 1 {
didSet { // didSet to trigger UI update
myLabel.text = "\(stepperValue * todays_price)"
}
}
#IBOutlet weak var myStepper: UIStepper!
#IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
//
let returnValueFromJson = 10
todays_price = returnValueFromJson
}
#IBAction func stepperUpdate(sender: AnyObject) {
stepperValue = Int(myStepper.value)
}
}
Just add a variable to the top of your view controller to hold the value from your json request. Then in viewDidLoad you update that variable, and then you can use it to set your label and inside the IBAction (that doesn't have to be inside viewDidLoad).
So you would do something like this:
class WhateverViewController: UIViewController {
var todays_price: Int!
override func viewDidLoad() {
super.viewDidLoad()
todays_price = // The value you got from json goes here
new_total.text = "\(todays_price)"
}
#IBAction func step_up_pass(sender: AnyObject) {
new_total.text = "\(Int(stepper.value) * todays_price)"
}
}
Related
I have two types of table cells, one that has 6 buttons, and each button has a number value, and then a second cell that has a button to calculate the total sum of the selected numbers, and a label to display this total sum in.
My issue is I cannot get the label to calculate the total. Here is what I have so far
Number Cell:
protocol ToggleNumberCellDelegate: AnyObject {
/// This method detects the selected value of the cell.
func toggleNumberCell(_ toggleNumberCell: ToggleNumberCell, selectedValue: Int)
}
class ToggleNumberCell: UITableViewCell {
static let reuseIdentifier = String(describing: ToggleNumberCell.self)
#IBOutlet private weak var titleLabel: UILabel!
#IBOutlet private weak var zeroButton: UIButton!
#IBOutlet private weak var oneButton: UIButton!
#IBOutlet private weak var twoButton: UIButton!
#IBOutlet private weak var threeButton: UIButton!
#IBOutlet private weak var fourButton: UIButton!
#IBOutlet private weak var fiveButton: UIButton!
#IBOutlet private weak var sixButton: UIButton!
private weak var delegate: ToggleNumberCellDelegate?
private var value: Int?
//...
#IBAction func buttonTapped(_ sender: UIButton) {
switch sender {
case zeroButton:
self.zeroButton.backgroundColor = UIColor.NBABlue
self.zeroButton.tintColor = UIColor.white
self.value = 0
self.delegate?.toggleNumberCell(self, selectedValue: self.value!)
print("The value you tapped is \(value)")
case oneButton:
self.oneButton.backgroundColor = UIColor.NBABlue
self.oneButton.tintColor = UIColor.white
self.value = 1
self.delegate?.toggleNumberCell(self, selectedValue: self.value!)
print("The value you tapped is \(value)")
case twoButton:
self.twoButton.backgroundColor = UIColor.NBABlue
self.twoButton.tintColor = UIColor.white
self.value = 2
self.delegate?.toggleNumberCell(self, selectedValue: self.value!)
print("The value you tapped is \(value)")
The above code just sets up the buttons, and gives each a value by using its delegate.
Label Cell
class CalculateCell: UITableViewCell, ToggleNumberCellDelegate {
var increment = 0
static let reuseIdentifier = String(describing: CalculateCell.self)
#IBOutlet private weak var calculateButton: UIButton!
#IBOutlet private weak var totalLabel: UILabel!
func configure(answer: AnswerModel) {
self.backgroundColor = UIColor.secondarySystemGroupedBackground
self.totalLabel.text = answer.text
self.totalLabel.font = .preferredFont(forTextStyle: .headline)
calculateButton.layer.cornerRadius = 10.0
}
override func prepareForReuse() {
super.prepareForReuse()
self.totalLabel.text = nil
}
#IBAction func calculateTapped(_ sender: UIButton) {
// You need to get the selected value in here somehow.
}
func toggleNumberCell(_ toggleNumberCell: ToggleNumberCell, selectedValue: Int) {
increment = selectedValue
}
}
Here I called the delegate to get the selected value, but I think I did this wrong, can someone tell me how I can calculate to total value?
Here is a screenshot of what I want to achieve.
You have a basic problem with your approach. You should not store data in table view cells. They are views, and should display data, not store data.
You should set up a model object to store the state of your UI. (it could be as simple as an array of integers, one of the value of each ToggleNumberCell in your table view.)
You should have a controller object serve as the delegate for the ToggleNumberCell. When its toggleNumberCell method gets called, it would update the count for that cell's entry in your data model, and then tell the total cell to update itself. Your table view data source would query the model and use the value for each entry in the model to calculate the totals, then install that value in the total cell.
I would recommend you make the UIViewController (than manages the UITableView) the ToggleNumberCellDelegate. Then that view controller can keep track of the total of all of the buttons. The view controller then can provide the total to
the CalculateCell. This separates the logic between the cells. Ideally the controller manages the logic, while the cells are simply views displaying the data.
Also, I would change the toggleNumberCell function to the following:
func toggleNumberCell(_ toggleNumberCell: ToggleNumberCell, selectedValue: Int) {
// Using += will result in adding to the existing value instead of overwriting it
increment += selectedValue
}
I am getting nil value error when I try to change UIlabel from a different swift file other than ViewController.
//ViewController.swift
import UIKit
class MainViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel?
override func viewDidLoad() {
super.viewDidLoad()
myLabel?.text = "foo"
}
func label(stringVal: String? = nil){
myLabel?.text = stringVal!
}
}
//Source.swift
//Do working here and get a string value from struct
// I saved that string in stringValue
let VC = MainViewController()
VC.label(stringVal: stringValue!)
Please let me know what am I doing wrong.
View controller controls are not created in constructor, it is expected to be null. You need to show that view controller before to run label method and avoid the error.
A valid option would be to set an internal variable in the constructor and in viewDidLoad, when the label is already created, you could do
myLabel.text = stringValue // stringValue is comes from constructor.
I am recreating a simple app that is a paramedic case logger, effectively it is a bunch of buttons or textfields that get their .text value time stamped and pasted into a UItextView field to create a chronological list of action to refer back to when a paramedic writes up their patient notes.
I have created a tab bar controlled app using the Xcode template.
I have created the UIbuttons and UItextfields that create and paste the string into a test UItextview on the same field.
I have created a func that creates the time stamp string
I run into trouble getting the same string to show on the UItextview on the 2nd view.
RESEARCH
As I am only new to this I have read lots of posts on here with user defaults, segues, delegates/protocols and cannot seem to effectively implement these in my specific circumstance (still not super sure I understand those enough yet to do it).
I have got the point that I have saved the string into a user.default value and then I can use that value in the 2nd view with a func to insert the text and clear the user.default value but I cannot seem to get the function to run from the first view? (get the encounter nil value error).
I feel like this should be a pretty simple process, so not getting it to function is very frustrating...
QUESTION
So I was hoping someone might be able to demo the simple way to take a UItextField.text input from FirstViewController and then when a UIbutton is pressed on FirstViewController pass the string to SecondViewController and insert it into a UITextView field (I don't want to display the 2nd view).
From this demo I'm hoping I can reverse engineer a solution into my code as a learning opportunity without just having my exact code handed to me on a silver platter.
import UIKit
class FirstViewController: UIViewController, UITextFieldDelegate {
// View OUTLETS
#IBOutlet weak var customNoteField: UITextField!
#IBOutlet weak var reportTextViewFieldTemp: UITextView!
#IBOutlet weak var lastEntryTV: UITextView!
#IBOutlet weak var caseIDTF: UITextField!
// Class VARIABLES
let timeButtonArray = ["blank0", "Call Received1", "Dispatched2", "Enroute3", "On Scene4", "At Patient5", "Depart Scene6", "At Hospital7", "Available8",]
// VIEW DID LOAD STUFF
override func viewDidLoad() {
super.viewDidLoad()
self.hideKeyboardWhenTappedAround()
self.customNoteField.delegate = self
}
//TIMING BUTTON PRESSED
#IBAction func timeButtonPressed(_ sender: UIButton) {
//GATHER DATA
let timingButtonInput = timeButtonArray[sender.tag]
let DTGstamp = dateTimeStamp()
//FORM STRING
let buttonData = ("\(DTGstamp) \(timingButtonInput) \n")
//SEND buttonDATA TO ReportViewController
/*??? The part I need help with*/
}
//CUSTOM ENTRY UITextfield PRESSED
#IBAction func customNoteDone(_ sender: Any) {
//GATHER DATA
let DTGstamp = dateTimeStamp()
let customNote = customNoteField.text
//FORM STRING
let customData = ("\(DTGstamp) \(customNote) \n")
//TEST FOR EMPTY TEXT
if customNote != "" {
//SEND customDATA TO ReportViewController
/*??? The part I need help with*/
}
//FUNCTION TO GENERATE DATE,TIME GROUP AS CONSTANT
func dateTimeStamp() -> String {
var date = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "ddLLLyy HH:mm:ss"
print (dateFormatter.string(from: date))
return (dateFormatter.string(from: date))
}
}
//
// ReportViewController.swift
// EMS Case Logger
//
// Created by Allan Ravenscroft on 3/3/19.
// Copyright © 2019 Allan Ravenscroft. All rights reserved.
//
import UIKit
class ReportViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var reportTextViewField: UITextView!
#IBOutlet weak var reportEmailButton: UIButton!
#IBOutlet weak var reportEditButton: UIButton!
#IBOutlet weak var reportClearButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
hideKeyboardWhenTappedAround()
reportEditButton.layer.cornerRadius = reportEditButton.frame.height / 2
reportEmailButton.layer.cornerRadius = reportEmailButton.frame.height / 2
reportClearButton.layer.cornerRadius = reportClearButton.frame.height / 2
}
//Actions when the clear button is pressed
#IBAction func clearButtonPressed(_ sender: Any) {
reportTextViewField.text = ""
}
//Actions when the edit button is pressed
#IBAction func editButtonPressed(_ sender: Any) {
}
//Actions when the email button is pressed
#IBAction func emailButtonPressed(_ sender: Any) {
showMailComposer(bodytext: reportTextViewField.text)
}
}
I know this is not a demo, but maybe can help you
Make a new NSObject file
inside the class put a new variable
import UIKit
class test: NSObject {
var a:String = ""
}
at your ViewController declare a new var referencing the class
let testVar = test()
in your customNote checker
test.a = customNote
then at your 2nd ViewController, do the same thing
let testVar = test()
testVar.a //this is your customNote String
hope this helps
I'm trying to build a key/values pair, for referencing later.
The array will hold a index reference, and it's values inside. One of the values is a video matching the index, and the other value is the corresponding label name (so I can change the color when video is played).
Here is my code:
class ViewController: UIViewController {
var videos = [5: ["urltovideo1", "locationOne"], 7: ["urltovideo2", "locationTwo"]]
var bg: UILabel!
#IBOutlet weak var locationOne: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
bg = videos[5]![1]
bg.backgroundColor = UIColor.green
}
}
I get an error "Cannot assign value of type 'String' to type 'UILabel!'"
I tried "bg = UILabel(videos[5]![1])" and get an error ""Argument labels '(_:)' do not match any available overloads""
Any ideas on how to fix it or better way to implement this?
Here is a solution for you:
#objc var locationOne: UILabel!
#objc var locationTwo: UILabel!
var videos = [5: ["urltovideo1", "locationOne"], 7: ["urltovideo2", "locationTwo"]]
var bg: UILabel! = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
if let bg = self.value(forKey: videos[5]![1]) as? UILabel {
bg.backgroundColor = UIColor.green
}
}
#objc is required to make self.value(forKey:) working properly - you have to add it for every property you want to access via this method.
In my application I have a textbox that should be filled with a Double and the number should be saved into a variable but there's an error.
I dragged and dropped the textbox into ViewController.swift so it should be linked. I created a #IBOutlet. I called the textbox mmolText and the variable mmol.
I tried something like: var mmol = mmolText.text but it shows an error:
'ViewController.Type' does not have a member named 'mmolText'.
What's the problem? How can I solve it? Besides the type of the content of the textbox is a string but I should convert it into Double.
Here the code of ViewController.swift is:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var mmolText: UITextField!
var mmol = mmolText.text
#IBOutlet weak var mmolLabel: UILabel!
#IBOutlet weak var mgLabel: UILabel!
#IBAction func convertBM(sender: AnyObject) {
}
override func viewDidLoad() {
super.viewDidLoad()
// 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.
}
}
It seems like we probably simply want mmol to exist as a convenient way for getting the text property out of the mmolText textfield, right? So why not use a computed property:
var mmol: String {
get {
return mmolText.text ?? ""
}
set {
mmolText.text = newValue
}
}
The get makes use of the nil coalescing operator. UITextField's text property hasn't been updated with the Objective-C nullability annotations yet, so we need to handle the case of it potentially returning nil.
If we want this to be readonly, we can simply omit the set part.
If we want this as a Double, we can modify the above computed property to look more like this:
var mmol: Double {
get {
return ((mmolText.text ?? "0") as NSString).doubleValue
}
set {
mmolText.text = String("%f", newValue)
}
}
And again, if we want this to be readonly, we can simply omit the set half. And of course, the format string can be played around with to get the string version of the double to show up exactly as you intend when using this set method.
class ViewController: UIViewController {
#IBOutlet weak var mmolText: UITextField!
var mmol: String!
override func viewDidLoad() {
super.viewDidLoad()
mmol = mmolText.text
}
}
This way it works. I can remember something like because at that stage, the properties can exist. Which means, it can be there or it isn't. That's why you can't do it like that.
Don't pin me on this explanation though, I'm not very sure.
mmolText is a property on self. You can't refer to it there because self has not been initialized yet.
You'll have to assign it within awakeFromNib, viewDidLoad, etc.