Why I cannot set the labels with the data passed from one UIViewController to another (printing the data in console works) in Swift? - ios

In my app I have an embedded UIViewController inside a container. In my story board I added a touch event to this viewcontroller and I called the segue: fullRequestSegue
Then in the code of that UIViewController I wrote:
class RequestDetails: UIViewController, ShowRequestDetailsFromMap {
var fullRequestDetails: FullRequestFromMap?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "fullRequestSegue"){
fullRequestDetails?.changeEventDescr(self.descr, username: "adam", number: "12", created_at: self.created_at, photo: self.photo)
fullRequestDetails = segue.destinationViewController as? FullRequestFromMap
fullRequestDetails!.showRequestDetails = self
}
}
}
Then in my class FullRequestFromMap I have:
protocol ShowRequestDetailsFromMap {
func changeEventDescr(text:String)
}
class FullRequestFromMap: UIViewController{
#IBOutlet weak var userNumber: UILabel!
var showRequestDetails:ShowRequestDetailsFromMap?
override func viewDidLoad() {
super.viewDidLoad()
}
func changeEventDescr(description: String, username: String, number: String, created_at: NSDate, photo: String) {
print(username)
print(description)
print(number)
print(created_at)
print(photo) //that works fine, I see all valid data in the console
userNumber.text = number //doesn't work, I see empty label instead of filled with passed data, the same problem is with other labels
}
What is the problem here?

The problem is when the method changeEventDescr is called the userNumber label is not initialized. You are trying to assign to a nil object.
Create a string variable in your FullRequestFromMap class and store text in it and in your viewDidLoad method you have to assign the text to userNumber label.
class FullRequestFromMap: UIViewController {
#IBOutlet weak var userNumber: UILabel!
var showRequestDetails:ShowRequestDetailsFromMap?
var userNumberLabelText:String = "Default Value"
override func viewDidLoad() {
super.viewDidLoad()
userNumber.text = userNumberLabelText
}
func changeEventDescr(description: String, username: String, number: String, created_at: NSDate, photo: String) {
print(username)
print(description)
print(number)
print(created_at)
print(photo)
userNumberLabelText = number // Here you set the class variable, not the label it self
}
}
class RequestDetails: UIViewController {
......
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "fullRequestSegue") {
fullRequestDetails = segue.destinationViewController as? FullRequestFromMap
// Option 1: You can directly assign it
fullRequestDetails?.userNumberLabelText = "12"
// Option 2: You can call your method
fullRequestDetails?.changeEventDescr(self.descr, username: "adam", number: "12", created_at: self.created_at, photo: self.photo)
}
}
}

Related

The data I entered in the TextField does not transfer to another label

Hello guys can you help me, I have an app that has two ViewController and in the first VC I have four empty TextField and at the second VC I have four empty Labels that should receive new information and show I the label but my code doesn't work so could you help with this problem, I think something not right with my personalData
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var name: UITextField!
#IBOutlet weak var age: UITextField!
#IBOutlet weak var city: UITextField!
#IBOutlet weak var mail: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard))
view.addGestureRecognizer(tap)
}
#objc func edit() {
print("Edit is done")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier == "personalData" else { return }
guard let destination = segue.destination as? SecondViewController else { return }
destination.personalData = name.text ?? ""
destination.personalData = age.text ?? ""
destination.personalData = city.text ?? ""
destination.personalData = mail.text ?? ""
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
}
//////////////////////////////////////
import UIKit
class SecondViewController: UIViewController {
struct User{
}
var personalData = ""
override func viewDidLoad() {
super.viewDidLoad()
firstProfileLabel.text = personalData
secondProfileLabel.text = personalData
thirdProfileLabel.text = personalData
lastProfileLabel.text = personalData
print("SecondVC", #function)
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit,
target: self,
action: #selector(edit))
}
#objc func edit() {
print("Edit is done")
}
#IBOutlet weak var firstProfileLabel: UILabel!
#IBOutlet weak var secondProfileLabel: UILabel!
#IBOutlet weak var thirdProfileLabel: UILabel!
#IBOutlet weak var lastProfileLabel: UILabel!
}
My mentor said that "The problem is with the variable personalData. The variable is of the stripe type and can store only one value.
If you want to pass values through a variable and not directly, you can create a structure, e.g. User with variables Name, Age, City, etc., and make personalData a User type and empty array."
But I don't understand how exactly I should write it in code.
Start simple. Give your second view controller separate properties for each value you want to pass:
class SecondViewController: UIViewController {
var name: String
var age: String
var city: String
var mail: String
}
Then in your first view controller's perpare(for:) method, set each of those variables separately:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier == "personalData" else { return }
guard let destination = segue.destination as? SecondViewController else { return }
destination.name = name.text ?? ""
destination.age = age.text ?? ""
destination.city = city.text ?? ""
destination.mail = mail.text ?? ""
}
And rewrite your second view controller's viewDidLoad method to install each property into the correct field.
Once you've got that working, you can figure out how to instead pass all the string values in a single structure.
Hint:
Create a struct called something like UserInfo:
struct UserInfo {
let name: String
let age: String
let city: String
let mail: String
}
And then give your second view controller a property of type UserInfo, and set that in prepare(for:)

Best way to consolidate and store user input data in each viewController

class NameViewController: UIViewController {
var name: String?
override func viewDidLoad() {
super.viewDidLoad()
}
}
class AgeViewController: UIViewController {
var age: Int?
override func viewDidLoad() {
super.viewDidLoad()
}
}
class SaveViewController: UIViewController {
#IBAction func sendData(_ sender: Any) {
let entity = Person(name: name, age: age) // 'name'is NameViewController's name Value.
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
struct Person {
let name: String
let age: Int
}
NameViewController -> AgeViewController -> SaveViewController
I am implementing a screen with the above function as a simple example.
What's the best way to combine all the data to create an entity from the last screen?
not using Userdefaults and singleton..

How can I pass dictionary from one view controller class to another?SWIFT

I am trying to make a list of users and their passwords in one view controller, save that information in a dictionary, and send that dictionary to another view controller which asks the user to input their username/password combination to authorize the log in. (the key is the username and the value is the password). Is there a way I can send the dictionary from SecondVC to the FirstVC?
First View Controller
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var Username: UITextField!
#IBOutlet weak var Verification: UILabel!
#IBOutlet weak var Password: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
Username.delegate = self
Password.delegate = self
}
var usersDict = [String : String]()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let des = segue.destination as? AccountViewController {
des.usersDict = usersDict
}
}
#IBAction func Authorization(_ sender: Any) {
for ( key , value ) in usersDict{
let v = key.count
var start = 0
if start <= v{
if Username.text == key{
if Password.text == value{
Verification.text = "Looks Good"
}
}
else{
start += 1
}
}
else{
Verification.text = "Yikes"
}
}
}
}
Second View Controller
class AccountViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var CreateUsername: UITextField!
#IBOutlet weak var CreatePassword: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
CreateUsername.delegate = self
CreatePassword.delegate = self
// Do any additional setup after loading the view.
}
var usersDict = [ String : String ]()
#IBAction func MakeANewAccount(_ sender: Any) {
usersDict[CreateUsername.text!] = CreatePassword.text!
}
}
I have made there dictionary, but it will only send in the beginning and won't update after creating the original account. (dictionary it is sending is empty)
With a segue add this method inside ViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let des = segue.destination as? AccountViewController {
des.usersDict = yourDicHere
}
}
Here's a general pattern for making a controller work with data from some object it creates, in this case a second controller.
Try applying it to your situation and let me know if you run into problems.
protocol Processor {
func process(_ dict: [String : String])
}
class FirstController: UIViewController, Processor {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let controller = segue.destination as? SecondController {
controller.delegate = self
} else {
print("Unexpected view controller \(segue.destination)")
}
}
func process(_ dict: [String : String]) {
}
}
class SecondController: UIViewController {
var delegate: Processor?
func someWork() {
if let processor = delegate {
processor.process(["Name" : "Pwd"])
} else {
print("Delegate not assigned")
}
}
}

Fatal error when trying to pass data to another view controller

In order to practice my networking, I built an app with a text field where you can input something. I use the wikipedia API to fetch the definition of that term / name/ expression. My goal is to then display that definition into another view controller.
A button performs the segue to the new view controller, where a label displays that definition.
The get request works, but when tapping the button, I get a fatalError : "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value".
I would like to add that the error is displayed in the "prepare for segue" function.
Here is the code for my first view controller
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UIViewController {
#IBOutlet weak var textEntryLabel: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
//MARK: - Relevant variables
let wikipediaURl = "https://en.wikipedia.org/w/api.php"
var termDefinitionInfo: String = ""
let segueName: String = "toDefinition"
#IBAction func buttonToDefinition(_ sender: UIButton) {
// on fait la requete ici
httpCall(termDefinition: textEntryLabel.text ?? "nothing to pass")
performSegue(withIdentifier: segueName , sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == segueName {
let secondVC = segue.destination as! DefinitionViewController
secondVC.definitionLabel.text = termDefinitionInfo
}
}
//MARK: - NETWORKING
func httpCall(termDefinition: String) {
let parameters : [String:String] = [
"format" : "json",
"action" : "query",
"prop" : "extracts",
"exintro" : "",
"explaintext" : "",
"titles" : termDefinition,
"indexpageids" : "",
"redirects" : "1",
]
//
request(wikipediaURl, method: .get, parameters: parameters).responseJSON { (response) in
if response.result.isSuccess {
//1. on affiche le tableau json initial
let definitionJSON: JSON = JSON(response.result.value)
print(definitionJSON)
// deux valeurs : pageID et definition
let pageId = definitionJSON["query"]["pageids"][0].stringValue
let pageDefinition = definitionJSON["query"]["pages"][pageId]["extract"].stringValue
self.termDefinitionInfo = pageDefinition
print(self.termDefinitionInfo)
} else {
print("Error! Could not fetch data!")
}
}
}
}
Here is the code for the second view controller
import SwiftyJSON
import Alamofire
class DefinitionViewController: UIViewController {
#IBOutlet weak var definitionLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}```
Tip: Try to avoid force down casting
In your case you are trying to assign a value to an IBOutlet when it's not wired to its parent view controller. You better do this:
class DefinitionViewController: UIViewController {
#IBOutlet weak var definitionLabel: UILabel!
var labelValue: String?
override func viewDidLoad() {
super.viewDidLoad()
definitionLabel.text = labelValue
}
}
And in your first view:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == segueName {
if let secondVC = segue.destination as? DefinitionViewController {
secondVC.labelValue = termDefinitionInfo
}
}
}

Delegate is still nil

I am passing user data from a view controller called CreateNewAccount to another view controller called ThanksForJoining. For some reason my delegate is nil. I am using a segue in order to set my vc.delegate to self (= self) and the segue identifier "thanksForJoining" refers to the segue that connects CreateNewAccount to ThanksForJoining on the storyboard. Somehow though, the delegate remains nil.
CreateNewAccount:
import UIKit
protocol UserInfoDelegate {
func sendUserInfo(firstName: String, lastName: String, username: String, password: String)
}
class CreateNewAccount: UIViewController{
#IBOutlet weak var FNInput: UITextField!
#IBOutlet weak var LNInput: UITextField!
#IBOutlet weak var usernameInput: UITextField!
#IBOutlet weak var passwordInput: UITextField!
var infoDelegate: UserInfoDelegate?
#IBAction func sendInfo(_ sender: Any) {
if(infoDelegate != nil){
if(FNInput.text != nil && LNInput.text != nil && usernameInput.text != nil && passwordInput.text != nil){
let firstName = FNInput.text
let lastName = LNInput.text
let username = usernameInput.text
let password = passwordInput.text
infoDelegate?.sendUserInfo(firstName: firstName!, lastName: lastName!, username: username!, password: password!)
}
}
}
}
ThanksforJoining:
import UIKit
class ThanksForJoining: UIViewController, UserInfoDelegate {
#IBOutlet weak var fName: UILabel!
func sendUserInfo(firstName: String, lastName: String, username: String, password: String) {
print(firstName)
fName.text = firstName
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "thanksForJoining") {
let createNewAccount: CreateNewAccount = segue.destination as! CreateNewAccount
createNewAccount.infoDelegate = self
}
}
}
First of all, you need to confirm that:
You connected CreateNewAccount to ThanksForJoining via a segue.
The segue's Identifier is set to thanksForJoining correctly
(Be careful about the letter cases.)
If any of the two is not true, you have lost a little time and I have lost my time to prepare for a big typhoon. Update your question to clarify what's happening and wait for someone to help you...
Assuming two things above, prepare(for:sender:) is called on the source view controller. You need to implement it in your CreateNewAccount class.
CreateNewAccount:
import UIKit
class CreateNewAccount: UIViewController {
#IBOutlet weak var firstNameInput: UITextField!
#IBOutlet weak var lastNameInput: UITextField!
#IBOutlet weak var usernameInput: UITextField!
#IBOutlet weak var passwordInput: UITextField!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "thanksForJoining" {
let destinationVC = segue.destination as! ThanksForJoining
if
let firstName = firstNameInput.text, !firstName.isEmpty,
let lastName = lastNameInput.text, !lastName.isEmpty,
let username = usernameInput.text, !username.isEmpty,
let password = passwordInput.text, !password.isEmpty
{
destinationVC.receiveUserInfo(firstName: firstName, lastName: lastName, username: username, password: password)
}
}
}
}
ThanksForJoining:
import UIKit
class ThanksForJoining: UIViewController {
var firstName: String?
#IBOutlet weak var firstNameLabel: UILabel!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
firstNameLabel.text = firstName
}
func receiveUserInfo(firstName: String, lastName: String, username: String, password: String) {
print(firstName)
self.firstName = firstName
}
}
Seems delegate pattern is sort of too much for your purpose and you just need to define a data passing method in the destination view controller ThanksForJoining.
I assume you have connected your segue from some button of your CreateNewAccount. If the segue is connected from the view controller (not from a button), the code above needs small modification.
But anyway, in your original code, the method prepare(for:sender:) in ThanksForJoining would never be called, so the delegate would never be set. Thus, the delegate remains nil.
First you need a reference to the CreateNewAccount class in the class ThanksForJoining class. Then you need to activate the delegate by setting it equal to self in the ThanksForJoing class in viewDidLoad.
class ThanksForJoining: UIViewController, UserInfoDelegate {
var createNewAccount: CreateNewAccount?
override func viewDidLoad() {
super.viewDidLoad()
createNewAccount?.infoDelegate = self
}
{
Then your delegate methods will work.
The only possible issue is segue identifier mismatch in code and storyboard
In the story board, select the segue between two VCs
And then go the attributes inspector and set the ID "thanksForJoining" in "Identifier" field
Some suggestions
If your intention is to check whether user has entered all the fields before sending the data back, then this code will serve the purpose better
if (infoDelegate != nil) {
if let firstName = FNInput.text, firstName.count>0,
let lastName = LNInput.text, lastName.count>0,
let username = usernameInput.text, username.count>0,
let password = passwordInput.text, password.count>0 {
infoDelegate?.sendUserInfo(firstName: firstName, lastName: lastName, username: username, password: password)
}
}

Resources