I am new both to coding and to Xcode and am working on my first iOS app - so please forgive me if the answer to my question is obvious to experienced developers.
My app works but the design looks very "old fashioned" due to the buttons I have on each View Controller. I would prefer to use a more modern navigation system using Navigation Bars with Navigation buttons at the top of each screen. However, when I use this method of navigation the global variables entered on my first View Controller cannot be accessed on my final View Controller. I have double checked my Swift code files for each View Controller which, as explained, works fine if I have UIButtons instead of the Navbar.
The only difference I can see is that the Xcode 11 Navbar system uses the "Show" (Push) method of segues between View Controllers whereas my storyboard buttons use modal presentation.
I would be grateful if someone can steer me in the right direction - thanks!
The code showing the global variables from the first view controller is:
\\
import UIKit
var name = ""
var lastname = ""
var address = ""
var city = ""
var zip = ""
var email = ""
var phone = ""
var dateofbirth = ""
class ViewController1: UIViewController {
#IBOutlet weak var outlet: UITextField!
#IBOutlet weak var outlet2: UITextField!
#IBOutlet weak var outlet6: UITextField!
#IBOutlet weak var outlet3: UITextField!
#IBOutlet weak var outlet4: UITextField!
#IBOutlet weak var outlet5: UITextField!
#IBOutlet weak var outlet8: UITextField!
#IBOutlet weak var outlet7: UITextField!
#IBAction func submit(_ sender: Any) {
// Code for First Name
if (outlet.text != "")
{
name = outlet.text!
}
// Code for Last Name
if (outlet2.text != "")
{
lastname = outlet2.text!
}
// Code for Address
if (outlet3.text != "")
{
address = outlet3.text!
}
// Code for city
if (outlet4.text != "")
{
city = outlet4.text!
}
// Code for Zip
if (outlet5.text != "")
{
zip = outlet5.text!
}
// Code for Email
if (outlet6.text != "")
{
email = outlet6.text!
}
// Code for Phone
if (outlet7.text != "")
{
phone = outlet7.text!
}
// Code for Date of Birth
if (outlet8.text != "")
{
dateofbirth = outlet8.text!
}
// Dismissal of Keyboard
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
super.touchesBegan(touches, with: event)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
\\\\
The code for the final View Controller is:
\\\\
import UIKit
class ReportViewController: UIViewController {
//Client ID Label Outlets
#IBOutlet weak var Label: UILabel!
#IBOutlet weak var Label2: UILabel!
#IBOutlet weak var Label3: UILabel!
#IBOutlet weak var Label4: UILabel!
#IBOutlet weak var Label5: UILabel!
#IBOutlet weak var Label6: UILabel!
#IBOutlet weak var Label7: UILabel!
#IBOutlet weak var Label8: UILabel!
// Return function
#IBAction func unwindToReportVC (_sender:UIStoryboardSegue){
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated:Bool){
// Client ID Labels text using declared global variables
Label.text = name
Label2.text = lastname
Label3.text = address
Label4.text = city
Label5.text = zip
Label6.text = email
Label7.text = phone
Label8.text = dateofbirth
}
}
Related
I'm completely new to programming. Trying to learn Swift. I've created the UI for my app. A simple data entry app for weight lifting PB's. However when I close the app my data doesn't update to new stored values. How do assign a variable string to each UITextfield entry, which when I close the app it will display its last stored value?
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var benchPressPB: UITextField!
#IBOutlet weak var squatPB: UITextField!
#IBOutlet weak var deadliftPB: UITextField!
#IBOutlet weak var ohpPB: UITextField!
#IBOutlet weak var rackPullPB: UITextField!
#IBOutlet weak var legPressPB: UITextField!
#IBOutlet weak var pullUpsPB: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.benchPressPB.delegate = self
self.squatPB.delegate = self
self.deadliftPB.delegate = self
self.ohpPB.delegate = self
self.rackPullPB.delegate = self
self.legPressPB.delegate = self
self.pullUpsPB.delegate = self
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
P.S this may completely wrong and long already, but currently its achieving what I want it do, just not saving new inputted data. If there's any shorter way to get the keyboard to hide on return, let me know!
Your current code doesn't do anything with the values a user enters into your text fields.
You should
Set up a model object to hold the values that the user enters.
In your textFieldShouldReturn, collect the user input and save it
into your model.
Decide on how you want to persist your app's state so it restores
when the app is launched. At it's simplest, this could be saving each
string to a different key/value pair in UserDefaults, or grouped
together in a dictionary or an array.
The code might look something like this: (not tested. Not even compiled. It will need cleanup before you can use it:
#IBOutlet weak var benchPressPB: UITextField!
#IBOutlet weak var squatPB: UITextField!
#IBOutlet weak var deadliftPB: UITextField!
#IBOutlet weak var ohpPB: UITextField!
#IBOutlet weak var rackPullPB: UITextField!
#IBOutlet weak var legPressPB: UITextField!
#IBOutlet weak var pullUpsPB: UITextField!
var textFields = [UITextField]
var textFieldKeys = [
"benchPressPB",
"squatPB",
"deadliftPB",
"ohpPB",
"rackPullPB",
"legPressPB",
"pullUpsPB"
]
var textFieldStrings = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Note that you can hook up the delegates for your
// text fields in your Storyboard.
self.benchPressPB.delegate = self
self.squatPB.delegate = self
self.deadliftPB.delegate = self
self.ohpPB.delegate = self
self.rackPullPB.delegate = self
self.legPressPB.delegate = self
self.pullUpsPB.delegate = self
textFields = [benchPressPB, squatPB, deadliftPB, ohpPB, rackPullPB, legPressPB, pullUpsPB]
// Read values from UserDefaults into the text fields.
for (index, key) in textFieldKeys.enumerated() {
let aValue = UserDefaults.standard.string(forKey: key)
textFields[index].text = aValue
textFieldStrings.append(aValue ?? "")
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
let newText = textField.text
if let index = textFields.firstIndex(of: textField) {
textFieldStrings[index] = newText
UserDefaults.standard.set(newText, forKey: textFieldKeys[index])
}
return true
}
You can subclass UITextField and add a target for editing changed. Every time your text changes you can simply save its new value into user defaults. To make sure you use a unique key for each field you can override the accessibilityIdentifier and implement didSet to load the old values when you set its identifier:
import UIKit
class PersistentTextField: UITextField, UITextFieldDelegate {
override var accessibilityIdentifier: String? {
didSet {
text = UserDefaults.standard.string(forKey: accessibilityIdentifier ?? "")
}
}
override func didMoveToSuperview() {
addTarget(self, action: #selector(editingChanged), for: .editingChanged)
autocapitalizationType = .none
autocorrectionType = .no
delegate = self
}
#objc func editingChanged(_ textField: UITextField) {
UserDefaults.standard.set(text ?? "", forKey: accessibilityIdentifier ?? "")
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
resignFirstResponder()
return true
}
}
Then in your view controller just make sure to set their id when your view loads:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var benchPressPB: PersistentTextField!
#IBOutlet weak var squatPB: PersistentTextField!
#IBOutlet weak var deadliftPB: PersistentTextField!
#IBOutlet weak var ohpPB: PersistentTextField!
#IBOutlet weak var rackPullPB: PersistentTextField!
#IBOutlet weak var legPressPB: PersistentTextField!
#IBOutlet weak var pullUpsPB: PersistentTextField!
override func viewDidLoad() {
super.viewDidLoad()
benchPressPB.accessibilityIdentifier = "bench press"
squatPB.accessibilityIdentifier = "squat"
deadliftPB.accessibilityIdentifier = "dead lift"
ohpPB.accessibilityIdentifier = "ohp"
rackPullPB.accessibilityIdentifier = "rack pull"
legPressPB.accessibilityIdentifier = "leg press"
pullUpsPB.accessibilityIdentifier = "pull ups"
}
}
Hi I am very new to Swift. I have watched several different videos and tutorials on youtube and I am unable to identify what I am doing wrong.
I am trying to take the data from a check-in view and segue it to the summary view. The code runs, and I am able to move through the app, but the data does not move. Below is the code for the two different swift files. Any insight would be greatly appreciated.
Check-In:
import Foundation
import UIKit
class VieNewCheckIN : UIViewController {
#IBOutlet var SWR: UILabel!
#IBOutlet var ModelNumber: UITextField!
#IBOutlet var SerialNumber: UITextField!
#IBOutlet var Notes: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let viewnewsummary = segue.destination as? ViewNewSummary else { return }
viewnewsummary.NotesValue = Notes.text
viewnewsummary.SerialNumberValue = SerialNumber.text
viewnewsummary.ModelNumberValue = ModelNumber.text
}
}
Summary:
import Foundation
import UIKit
class ViewNewSummary : UIViewController {
#IBOutlet var FirstNameLabel: UILabel!
var FirstNameValue : String!
#IBOutlet var LastNameLabel: UILabel!
var LastNameValue : String!
#IBOutlet var IDLabel: UILabel!
var BaylorIDValue : String!
#IBOutlet var EmailLabel: UILabel!
var BaylorEmailValue : String!
#IBOutlet var PhoneNumberLabel: UILabel!
var PhoneNumberValue : String!
#IBOutlet var ModelNumberLabel: UILabel!
var ModelNumberValue : String!
#IBOutlet var SerialNumberLabel: UILabel!
var SerialNumberValue : String!
#IBOutlet var NotesLabel: UILabel!
var NotesValue : String!
override func viewDidLoad() {
super.viewDidLoad()
FirstNameLabel.text = FirstNameValue
LastNameLabel.text = LastNameValue
BaylorIDLabel.text = BaylorIDValue
BaylorEmailLabel.text = BaylorEmailValue
PhoneNumberLabel.text = PhoneNumberValue
ModelNumberLabel.text = ModelNumberValue
SerialNumberLabel.text = SerialNumberValue
NotesLabel.text = NotesValue
}
}
Verify prepare(for:sender:) is called, e.g. using breakpoints or print/caveman debugging;
verify ViewNewSummary.viewDidLoad() is called, too;
verify prepare is called before viewDidLoad.
My suspicion is that viewDidLoad is called before prepare(for:sender:) is called; that'd make sense since you need an initialized view controller object to prepare the segue with.
So you will want to add viewWillAppear(_:) as a quick fix:
extension ViewNewSummary {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
FirstNameLabel.text = FirstNameValue
LastNameLabel.text = LastNameValue
BaylorIDLabel.text = BaylorIDValue
BaylorEmailLabel.text = BaylorEmailValue
PhoneNumberLabel.text = PhoneNumberValue
ModelNumberLabel.text = ModelNumberValue
SerialNumberLabel.text = SerialNumberValue
NotesLabel.text = NotesValue
}
}
If that doesn't scale well for all cases, you may want to look into property observers, like
var PhoneNumberValue : String! {
didSet {
guard isViewLoaded else { return }
PhoneNumberLabel.text = PhoneNumberValue
}
}
So I'm trying to program an "Mad Lib" themed app for a class I'm in but I keep getting a
EXC_BAD_INSTRUCTION
error when I hit the submit button. The other students in the class couldn't figure it out either. Please help me!
Here is my firstViewController.swift:
import UIKit
class ViewController: UIViewController
{
#IBOutlet weak var firstTextField: UITextField! //adjective
#IBOutlet weak var secondTextField: UITextField! //male name
#IBOutlet weak var thirdTextField: UITextField! //verb
#IBOutlet weak var fourthTextField: UITextField! //female name
#IBOutlet weak var fifthTextField: UITextField! //adjective
#IBOutlet weak var sixthTextField: UITextField! //athlete
#IBOutlet weak var seventhTextField: UITextField! //food
#IBOutlet weak var eighthTextField: UITextField! //restaurant name
var userInfo = myCustomClass()
override func viewDidLoad()
{
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
userInfo.adjectiveOne = firstTextField.text!
userInfo.maleName = secondTextField.text!
userInfo.verb = thirdTextField.text!
userInfo.femaleName = fourthTextField.text!
userInfo.adjectiveTwo = fifthTextField.text!
userInfo.athlete = sixthTextField.text!
userInfo.food = seventhTextField.text!
userInfo.restaurantName = eighthTextField.text!
let nextView = segue.destination as! secondViewController
nextView.passedObject = userInfo
}
}
Here is my secondViewController.swift:
import UIKit
class secondViewController: UIViewController
{
var passedObject = myCustomClass()
#IBOutlet weak var myFinishedProduct: UILabel!
override func viewDidLoad()
{
super.viewDidLoad()
myFinishedProduct.text = "There was once a \(passedObject.adjectiveOne) man /n \(passedObject.maleName). One day while he was \(passedObject.verb) he saw /n \(passedObject.femaleName), a rather \(passedObject.adjectiveTwo) woman. /n She was also a \(passedObject.athlete), and a very good /n one too. The two went to lunch together at \(passedObject.restaurantName) /n and ate some \(passedObject.food). After /n that they never crossed paths again."
}
}
Finally here is my NSOBject called "myCustomClass.swift":
import UIKit
class myCustomClass: NSObject
{
var adjectiveOne = ""
var maleName = ""
var verb = ""
var femaleName = ""
var adjectiveTwo = ""
var athlete = ""
var food = ""
var restaurantName = ""
}
Basically, `whatever the user enters into the eight text fields will be stored in myCustomClass when the submit button is pressed. From there, in the secondViewController it will put the eight inputs into the story and display it on a label.
Any help is appreciated, thank you!
Edit: The "Submit Button" is connected to the secondViewController on my storybook with the purpose of "show".
First View Controller
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var txtmobile: UITextField!
#IBOutlet weak var txtlname: UITextField!
#IBOutlet weak var txtfname: UITextField!
var ArrayStudent:[PassData] = []
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.
}
#IBAction func btnclick(_ sender: UIButton)
{
let objdata = PassData(fname: txtfname.text!, lname: txtlname.text!, mobile: txtmobile.text!)
ArrayStudent.append(objdata)
passdata()
}
func passdata()
{
let objstory = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
objstory.Arradata1 = ArrayStudent
_ = self.navigationController?.pushViewController(objstory, animated: true)
}
}
Second View Controller
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var lblmobile: UILabel!
#IBOutlet weak var lbllastname: UILabel!
#IBOutlet weak var lblname: UILabel!
var Arradata1:[PassData ] = []
override func viewDidLoad() {
super.viewDidLoad()
lblname.text = Arradata1.first?.StrFirstName
lbllastname.text = Arradata1.first?.StrLastName
lblmobile.text = Arradata1.first?.StrMobile
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
NSObject Class
import UIKit
class PassData: NSObject
{
var StrFirstName:String!
var StrLastName:String!
var StrMobile:String!
init(fname:String , lname:String , mobile:String)
{
StrMobile = mobile
StrFirstName = fname
StrLastName = lname
}
}
activite1Label as the tag 1
class StatsViewController: UIViewController {
#IBOutlet weak var activite1Label: UILabel!
#IBOutlet weak var activite2Label: UILabel!
#IBOutlet weak var activite3Label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
activite1Label.text = activite[0]
activite2Label.text = activite[1]
activite3Label.text = activite[2]
miseAjourTotal()
}
func miseAjourTotal() {
let leLabel = view.viewWithTag(1) as! UILabel
print("leLabel: \(leLabel.text)")
}
}
Nothing in your code tells that the label has the tag 1. You should go to your storyboard and check if the label does have tag 1 or set the tag programmatically
I have made a small program which I use to count the money in the safe at work, but after updating my iPhone, it didn't work anymore.
After a lot of reading and so on I fixed everything, but I haven't found a new way to resign the keyboard.
Before the update I used this:
#IBAction func resignKeyboard(_sender: AnyObject) {
_sender.resignFirstResponder()
}
This doesn't work anymore so I would love a new way of doing it. I have searched quite a bit for a solution, but I haven't understood any of the solutions I have found. So please simplify your answers as a lot as possible.
This is everything from the "ViewController.Swift" from my program:
//
// ViewController.swift
// Pengeskabstæller
//
// Created by Alex on 09/07/2016.
// Copyright © 2016 Alex. All rights reserved.
//
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var In50B: UITextField!
#IBOutlet weak var In50L: UITextField!
#IBOutlet weak var In20B: UITextField!
#IBOutlet weak var In20L: UITextField!
#IBOutlet weak var In10B: UITextField!
#IBOutlet weak var In10L: UITextField!
#IBOutlet weak var In5B: UITextField!
#IBOutlet weak var In5L: UITextField!
#IBOutlet weak var In2B: UITextField!
#IBOutlet weak var In2L: UITextField!
#IBOutlet weak var In1B: UITextField!
#IBOutlet weak var In1L: UITextField!
#IBOutlet weak var In05B: UITextField!
#IBOutlet weak var In05L: UITextField!
#IBOutlet weak var Ialt: UILabel!
#IBAction func resignKeyboard(_sender: AnyObject) {
_sender.resignFirstResponder()
}
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.
}
#IBAction func Knap(sender: AnyObject) {
//Hvad knappen gør
//Ganger input ved værdi af bundt eller løse
var x50B: Int = Int(In50B.text!)!
x50B = x50B*5000
var x50L:Int = Int(In50L.text!)!
x50L = x50L*500
var x20B:Int = Int(In20B.text!)!
x20B = x20B*4000
var x20L:Int = Int(In20L.text!)!
x20L = x20L*400
var x10B:Int = Int(In10B.text!)!
x10B = x10B*2000
var x10L:Int = Int(In10L.text!)!
x10L = x10L*200
var x5B:Int = Int(In5B.text!)!
x5B = x5B*1000
var x5L:Int = Int(In5L.text!)!
x5L = x5L*200
var x2B:Int = Int(In2B.text!)!
x2B = x2B*500
var x2L:Int = Int(In2L.text!)!
x2L = x2L*50
var x1B:Int = Int(In1B.text!)!
x1B = x1B*500
var x1L:Int = Int(In1L.text!)!
x1L = x1L*50
var x05B:Int = Int(In05B.text!)!
x05B = x05B*200
var x05L:Int = Int(In05L.text!)!
x05L = x05L*20
//Lægger det hele sammen
let penge1 = (x50B + x50L + x20B)
let penge2 = (x20L + x10B + x10L)
let penge3 = (x5B + x5L + x2B + x2L)
let penge4 = (x1B + x1L + x05B + x05L)
let penge99 = String(penge1+penge2+penge3+penge4)
//Printer ialt
Ialt.text = penge99
}
}
I think this is a better way to do it, instead of adding an outlet.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
}
Hope it helps!
Assuming all textFields are subviews of view, you can use endEditing method like this:
#IBAction func resignKeyboard() {
view.endEditing(true)
}
Try to change the sender to UITextField instead of AnyObject and then you can do sender.resignFirstResponder()