Looking for some clarification and just to double check if I am on the right path. I have a category list where multiple buttons will send a specific string through a segue. I have the IBOutlets for each button but want to make sure when that specific button is touched that specific string is sent. I am just unsure if the way I am setting up the segue is correct so that each button is specific to the set strings. So far, the current segue works for "attractionsButton" but when I tap other buttons it passes the same data. I know it's not set, but want to make sure that when another button is tapped its not sending the wrong string.
#IBOutlet weak var attractionsButton: UIButton!
#IBOutlet weak var eatingButton: UIButton!
#IBOutlet weak var financialButton: UIButton!
#IBOutlet weak var lodgingButton: UIButton!
#IBOutlet weak var medicalButton: UIButton!
#IBOutlet weak var publicButton: UIButton!
#IBOutlet weak var servicesButton: UIButton!
#IBOutlet weak var storesButton: UIButton!
#IBOutlet weak var transportationButton: UIButton!
let attractions = "Attractions & Entertainment"
let eating = "Eating & Drinking"
var financial = "Financial Institution"
var lodging = "Lodging Establishment"
var medical = "Medical & Health"
var publicService = "Public Services & Buildings"
var services = "Service"
var stores = "Stores & Shopping"
var transportation = "Transportation"
#IBAction func attractionsButton(_ sender: Any) {
performSegue(withIdentifier: "category", sender: self)
}
#IBAction func eatingButton(_ sender: Any) {
performSegue(withIdentifier: "category", sender: self)
}
#IBAction func financialButton(_ sender: Any) {
performSegue(withIdentifier: "category", sender: self)
}
#IBAction func lodgingButton(_ sender: Any) {
performSegue(withIdentifier: "category", sender: self)
}
#IBAction func medicalButton(_ sender: Any) {
performSegue(withIdentifier: "category", sender: self)
}
#IBAction func publicButton(_ sender: Any) {
performSegue(withIdentifier: "category", sender: self)
}
#IBAction func serviceButton(_ sender: Any) {
performSegue(withIdentifier: "category", sender: self)
}
#IBAction func storesButton(_ sender: Any) {
performSegue(withIdentifier: "category", sender: self)
}
#IBAction func transportationButton(_ sender: Any) {
performSegue(withIdentifier: "category", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if segue.identifier == "category" {
if let button1 = attractionsButton {
let user = attractions
let controller = segue.destination as? CategoryListedViewController
controller?.categoryList = user
}
}
}
change self in your action to sender, you can use this one action for all the buttons
#IBAction func transportationButton(_ sender: Any) {
performSegue(withIdentifier: "category", sender: sender)
}
use this code in your prepare for segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if segue.identifier == "category" {
let senderButton = sender as! UIButton
switch senderButton{
case attractionsButton:
let user = attractions
let controller = segue.destination as? CategoryListedViewController
controller?.categoryList = user
case eatingButton:
//editing button scenario
print("editing button scenario")
default:
//default code
print("default scenario")
}
}
}
All of your buttons can connect to just this one #IBAction:
#IBAction func allButtons (_ sender: Any) {
performSegue(withIdentifier: "category", sender: sender)
}
Of course, if that is all the buttons are doing, you can skip using the #IBAction entirely and just wire the segues from the buttons directly. If you do that when creating the first button in the Storyboard, you can copy that button and all copies will be wired to the same segue.
Then in prepare(for:sender:), compare the sender to your #IBOutlets to set your string:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let button = sender as? UIButton,
let controller = segue.destination as? CategoryListedViewController,
segue.identifier == "category" {
let str: String
switch button {
case attractionsButton: str = attractions
case eatingButton: str = eating
case financialButton: str = financial
default: str = ""
}
controller.categoryList = str
}
}
If I were you I would use buttons tags and a good old enum to work this out:
First, in Interface Builder, you set the tag property of each of your buttons (and you don't even need to have them anymore as #IBOutlet)
1 for attractionsButton, 2 for eatingButton, etc.
Then, you create an enum, with raw value as Int with matching values :
enum Category : Int, CustomStringConvertible {
case attractions = 1
case eating = 2
case financial = 3
case lodging = 4
case medical = 5
case publicService = 6
case services = 7
case stores = 8
case transportation = 9
var description : String {
switch self {
case .attractions: return "Attractions & Entertainment"
case .eating: return "Eating & Drinking"
case .financial: return "Financial Institution"
case .lodging: return "Lodging Establishment"
case .medical: return "Medical & Health"
case .publicService: return "Public Services & Buildings"
case .services: return "Service"
case .stores: return "Stores & Shopping"
case .transportation: return "Transportation"
}
}
}
After that, you link all your buttons to only one #IBAction like this one:
#IBAction func onButtonTap(_ sender: UIButton) {
performSegue(withIdentifier: "category", sender: Category(rawValue: sender.tag))
}
This way, according to the tag of the button, you'll create the enum you need.
As an end, in your segue preparation, you can set things like that:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "category" {
guard
let controller = segue.destination as? CategoryListedViewController,
let category = sender as? Category
else { return }
controller.categoryList = category.description
}
}
This way, things are much more concise and you can attache more behaviors to your enum Category, use it in switches, and so on, instead of relying on Strings or multiplying copy&paste code.
Related
What the app should do:
1) The user types a word into a textField and taps the corresponding button
2) The app should segue to another scene containing two labels. One should display the word typed by the user, the other identifies the button tapped. These pieces of data should be passed via properties on the receiving ViewController.
What it actually does:
1) The segue is immediately invoked, apparently bypassing prepare(segue: sender:)
2) Both labels are blank
3) Breakpoints and print() indicate that prepare(segue: sender:) is never called
What I've checked/tried:
1) The buttons have the correct tags in storyboard, according to my print() statements
2) I've substituted if else for switch
3) I'm pretty sure I've read every associated question and answer on SO
4) Switched sender in prepare(segue: sender:) from Any? to UIButton and back again
The code
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var tf2: UITextField!
#IBOutlet weak var tf3: UITextField!
#IBOutlet weak var tf4: UITextField!
#IBAction func sendButton(_ sender: UIButton) {
print("sender.tag is \(sender.tag)")
self.performSegue(withIdentifier: "mySegue", sender: sender)
print("sender.tag is \(sender.tag)")
}
#IBAction func button2(_ sender: UIButton) {
print("sender.tag is \(sender.tag)")
self.performSegue(withIdentifier: "mySegue", sender: sender)
print("sender.tag is \(sender.tag)")
}
#IBAction func button3(_ sender: UIButton) {
print("sender.tag is \(sender.tag)")
self.performSegue(withIdentifier: "mySegue", sender: sender)
print("sender.tag is \(sender.tag)")
}
#IBAction func button4(_ sender: UIButton) {
print("sender.tag is \(sender.tag)")
self.performSegue(withIdentifier: "mySegue", sender: sender)
print("sender.tag is \(sender.tag)")
}
func prepare(for segue: UIStoryboardSegue, sender: UIButton) {
print("Inside prepare for segue")
print("sender.tag is \(sender.tag)")
if segue.identifier == "mySegue" {
let vc = segue.destination as! SecondViewController
print("sender.tag is \(sender.tag)")
switch sender.tag {
case 101:
if (textField.text?.count)! >= 1 {
vc.staticText = "Button 101"
vc.textProp = self.textField.text!
print("sender.tag is \(sender.tag)")
}
case 102:
if (tf2.text?.count)! >= 1 {
vc.staticText = "Button 102"
vc.textProp = self.tf2.text!
print("sender.tag is \(sender.tag)")
}
case 103:
if (tf3.text?.count)! >= 1 {
vc.staticText = "Button 103"
vc.textProp = self.tf3.text!
print("sender.tag is \(sender.tag)")
}
case 104:
if (tf4.text?.count)! >= 1 {
vc.staticText = "Button 104"
vc.textProp = self.tf4.text!
print("sender.tag is \(sender.tag)")
}
default:
print("Something went wrong")
}
// print("In FirstVC, vc.textProp = \(vc.textProp)")
}else{
print("No text in textField...")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I admit upfront that I'm pretty good at overlooking the obvious, but I did put in a lot of time before bugging you guys. Any help or direction will be very much appreciated...
Thanks!
prepare(for segue is not called because the signature is wrong. You must not change the type of the sender parameter.
Cast sender to the expected type in an extra line for example
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "mySegue",
let button = sender as? UIButton {
...
and replace all subsequent occurrences of sender with button
I'm very new to coding and I'm having trouble passing data to a second view controller. I want to change a label in the second view controller depending on which button is pressed in the first (if you press art the label says "art museum", if you press rollercoaster the label says "theme park")
I have tried putting the override function into the brackets for the button but I got the error "override can only be specified on class members"
firstacCode
class firstacCode: UIViewController {
#IBAction func art(_ sender: Any) {
performSegue(withIdentifier: "artSegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destVC : acCode = segue.destination as! acCode
destVC.descriptionLabel = "art museum"
}
acCode
class acCode: UIViewController {
var descriptionLabel :String = "";
#IBOutlet weak var descriptionText: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
descriptionText.text = descriptionLabel
// Do any additional setup after loading the view.
}
This worked for me for one button but I'm not sure how to add another button into the equation. Any help would be greatly appreciated.
First of all connect your buttons to the art function, from the attribute inspector give tags to the buttons.
Now your code will be look like this :
#IBAction func art(_ sender: UIButton) {
if sender.tag == 0{
performSegue(withIdentifier: "mySegue", sender: "art museum")
}
if sender.tag == 1{
performSegue(withIdentifier: "mySegue", sender: "Theme Park")
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc : acCode = segue.destination as! acCode
vc.destVc = sender as! String
}
First off, welcome to Stackoverflow. You're already doing the passing of data to the other controller correctly. So your only problem now I guess is how to handle multiple buttons in your IBAction function.
Tip:
You can actually make this #IBAction func art(_ sender: Any) as #IBAction func art(_ sender: UIButton). Instead of Any, turn it into UIButton to avoid casting.
Assuming you connected multiple buttons into that art IBAction function, you can catch that specific button object through your sender and pass that sender to the sender parameter of your prepareForSegue, or in your case, I just realized that you might have different function for your two buttons, just pass the string to that sender parameter.
So your code would be like so:
#IBAction func art(_ sender: Any) {
performSegue(withIdentifier: "artSegue", sender: "art museum")
}
#IBAction func rollercoaster(_ sender: Any) {
performSegue(withIdentifier: "artSegue", sender: "theme park")
}
and in your prepareForSegue, cast the sender to String.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destVC : acCode = segue.destination as! acCode
destVC.descriptionLabel = sender as! String
}
#IBAction func art(_ sender: Any) {
sender.tag = 1
performSegue(withIdentifier: "artSegue", sender: self)
}
#IBAction func art2(_ sender: Any) {
sender.tag = 2
performSegue(withIdentifier: "art2Segue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(sender.tag == 1){
let destVC : acCode = segue.destination as! acCode
destVC.descriptionLabel = "art museum"
}
if(sender.tag == 2){
let destVC : acCode = segue.destination as! acCode
destVC.descriptionLabel = "art2 museum"
}
}
My code takes text from a textfield and segues to a separate view controller. The problem is that it can only store 1 segue entry. The new one replace the old every time the button is pressed. How on vc2 can I store every new entry to the segue.
VC
import UIKit
class ViewController: UIViewController {
#IBOutlet var txt: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func pressButton(_ sender: Any) {
if txt.text != "" {
performSegue(withIdentifier: "segue", sender: self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondController = segue.destination as! twoViewController
secondController.myString = txt.text!
}
}
twoVC
import UIKit
class twoViewController: UIViewController {
#IBOutlet var labelSmtih: UILabel!
var myString = String()
override func viewDidLoad() {
super.viewDidLoad()
labelSmtih.text = myString
}
}
If the second view controller is a UISplitViewController for example you should be able to append the text of the button to the label, by doing something in the lines of:
#IBAction func pressButton(_ sender: Any) {
if txt.text != "" {
performSegue(withIdentifier: "segue", sender: self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondController = segue.destination as! twoViewController
secondController.myString += txt.text!
}
}
If you really have a separated view controller it will probably lose the data once you go back to the first VC. In that case, I suggest you save the current text of the label on viewWillDisappear of the 2nd VC and then retrieve this on the prepareforsegue function on the first VC. More info here: Swift: How to store user preferences?
So how i transfer my ViewController label: totalPar to ThirdViewController label: totalPar5. And I want to transfer data that I have added there with my minus and plus buttons
Here is simulator pic that show what I want to transfer
My ThirdViewController is almost same as ViewController except prepare segue
Here is my ViewController
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var totalPar: UILabel!
#IBOutlet weak var plusminusPar: UILabel!
#IBOutlet weak var currentPar: UILabel!
var totalPAR = Int()
var plusminusPAR = 0
var currentPAR = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func minusButton(_ sender: Any) {
totalPAR = Int(totalPar.text!)!
self.totalPar.text = String(totalPAR - 1)
plusminusPAR = Int(plusminusPar.text!)!
self.plusminusPar.text = String(plusminusPAR - 1)
currentPAR = Int(currentPar.text!)!
self.currentPar.text = String(currentPAR - 1)
}
#IBAction func plusButton(_ sender: Any) {
totalPAR = Int(totalPar.text!)!
self.totalPar.text = String(totalPAR + 1)
plusminusPAR = Int(plusminusPar.text!)!
self.plusminusPar.text = String(plusminusPAR + 1)
currentPAR = Int(currentPar.text!)!
self.currentPar.text = String(currentPAR + 1)
}
#IBAction func Seuraava(_ sender: Any) {
self.performSegue(withIdentifier: "next1", sender: Any?.self)
}
#IBAction func seiraava2(_ sender: Any) {
self.performSegue(withIdentifier: "seuraava2", sender: Any?.self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let thirdviewcontroller = segue.destination as! ThirdViewController
thirdviewcontroller.totalPAR5 = self.totalPAR
}
}
From the accepted answer and its comments you need to start improving your code and learn some concepts of data source in general.
For your case stop constantly converting string to integers to string. Choose one or the other:
class ViewController: UIViewController {
var totalPAR: Int = 0
var plusminusPAR: Int = 0
var currentPAR: Int = 0
Do natural operations on actions and separate methods:
#IBAction func minusButton(_ sender: Any) {
addToAll(valueToAdd: -1)
refreshLabels()
}
private func addToAll(valueToAdd: Int) {
totalPAR += valueToAdd
plusminusPAR += valueToAdd
currentPAR += valueToAdd
}
private func refreshLabels() {
self.totalPar.text = String(totalPAR)
self.plusminusPar.text = String(plusminusPAR)
self.currentPar.text = String(currentPAR)
}
Then you will have no issues calling
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let thirdviewcontroller = segue.destination as? ThirdViewController {
thirdviewcontroller.totalPAR = self.totalPAR
thirdviewcontroller.plusminusPAR = self.plusminusPAR
thirdviewcontroller.currentPAR = self.currentPAR
}
}
Now if in both view controllers you call refreshLabels on viewDidLoad the values are successfully transferred.
Simply make the variable in ThirdViewController and assign the value in prepare for segua and setup value to the label in viewDidLoad:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let thirdviewcontroller = segue.destination as! ThirdViewController
thirdviewcontroller.totalPAR5 = self.totalPAR
}
class ThirdViewController: UIViewController {
var totalPAR5 = 0
override func viewDidLoad() {
super.viewDidLoad()
self.totalPar.text = "\(totalPAR5+1)" //You can calculate here like +1 or -1
// Do any additional setup after loading the view.
}
}
I have created a Calculator using Swift in Xcode. I would like to add another functionality to it, I already have a history label that displays the previous calculations. I would like to pass the data that displays in that history label into a TableViewController and for each calculation my history label shows to have it add each data into my TableViewController. I know I should be able to use perform segue, but I'm not exactly sure. Here is my code so far:
ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var displayLabel: UILabel!
#IBOutlet weak var HistoryLabel: UILabel!
var userIsTypingNumbers = false
#IBAction private func NumbersEntered(_ sender: UIButton) {
//know what number is being pressed
let digit = sender.currentTitle!
//if user is typing a number, do this.
if userIsTypingNumbers{
//specify what number is being pressed
let textCurrentDisplay = displayLabel.text!
//append the another number onto the previous number.
displayLabel.text = textCurrentDisplay + digit
} else {
displayLabel.text = digit
}
userIsTypingNumbers = true
}
var displayValue: Double {
get {
return Double(displayLabel.text!)!
}
set {
displayLabel.text = String(newValue)
}
}
private var calculations = PerformCalculations()
#IBAction func Enter(_ sender: UIButton) {
//perform the calculations
if userIsTypingNumbers{
calculations.setOperand(operand: displayValue)
userIsTypingNumbers = false
}
if let mathematicalSymbol = sender.currentTitle {
calculations.performOperation(symbol: mathematicalSymbol)
}
HistoryLabel.text = HistoryLabel.text! + String(sender.currentTitle!);displayValue = calculations.result
HistoryLabel.text = HistoryLabel.text! + String(displayValue)
}
#IBAction func Clear(_ sender: UIButton) {
//clear display to 0.
displayLabel.text = "0"
HistoryLabel.text = ""
}
#IBAction func Delete(_ sender: UIButton) {
//deleting last typed number, if user messed up.
let name: String = self.displayLabel.text!
//count number of characters.
let stringLength = name.characters.count
let substringIndex = stringLength - 1
displayLabel.text = (name as NSString).substring(to: substringIndex)
}
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.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "History"){
if let destinationVC = segue.destination as? HistoryClassViewController{
destinationVC
}
}
}
}
I have started attempting a prepare for segue function, but not sure how to make it work. Any help would be much appreciated. Hopefully this makes sense. Here is a picture of how my Calculator is laid out in Xcode:
Create in your HistoryClassViewController field like
var someData: Any!
Then:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "History" {
if let destinationVC = segue.destination as! HistoryClassViewController {
destinationVC.someData = something
}
}
}
Then to perform segue in place you wants to type this:
self.performSegue(withIdentifier: "History", sender: self)
Hope it helps
Here is thing Take one String Variable as global Variable in HistoryClassViewController class
example:
class HistoryClassViewController: UITableViewController{
var dataString:String!
//............Continue code
//............
Now Write this Method in ViewController class
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "History"){
if let destinationVC = segue.destination as? HistoryClassViewController{
destinationVC.dataString = displayLabel.text!
}
}
}
Finally access dataString value in TableViewController.