Passing data from view controller to another view controller - ios

I have not found an exact solution for my problem. I need to pass data from one view controller to another view controller. The problem is, that after the segue, the passed string data does not appear in the label.
func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject) {
if (segue.identifier == "segue1") {
if let destination = segue.destination as? ResultsViewController {
destination.name = correctslabel.text!
The second controller: there is just variable and "name" and a UIlabel, which does not show the passed data.
import UIKit
class ResultsViewController: UIViewController {
#IBOutlet var namelabel: UILabel!
var name = ""
override func viewDidLoad() {
super.viewDidLoad()
name = namelabel.text!
}
}
I've tried many ways to do it, but none of them worked. Thank you
Finally worked for me to do an override func of it, which meant, that I had to change the sender from AnyObject to Any?.

Haven't you tried this?
// ResultsViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
namelabel.text = name
}

Related

Passing text from a TextLabel in a View, to a Label from another View

I have a TextLabel in the ViewController VC, which will receive the input of the user whenever the user has put text on there. Then, pressing a button, that text that was on the TextLabel, will pass onto a Label at the SecondaryView VC. But the thing is that I have tried multiple ways to set the text from the TextLabel to the Label on the SecondaryView VC.
This is the first way I tried:
This is my ViewController.swift file.
class ViewController: UIViewController {
var text: String = ""
#IBOutlet weak var mainViewTextLabel: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is SecondaryView {
let vc = segue.destination as? SecondaryView
vc?.text = "\(mainViewTextLabel!)"
}
}
}
#IBAction func onButtonTap(_ sender: Any) {
}
}
And this is my SecondaryView.swift file:
import UIKit
class SecondaryView: UIViewController {
var text:String = ""
#IBOutlet weak var secondaryViewLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
secondaryViewLabel?.text = text
}
}
When I run the app and type any text in the TextField and press the Button to go to the SecondaryView VC, there is no text on there.
If someone knowns another way to pass text from a View to another, or a way that the text can appear, I would appreciate it.
You have a couple of issues, I think.
Firstly, you are calling prepare(for:...) within viewDidLoad. This function isn't something you call yourself. It's something that you provide an implementation for and the system calls it just before the segue.
The second is that you are passing a reference to a UITextField rather than the text of that text field.
Maybe something like this would be better:
class ViewController: UIViewController {
var text: String = ""
#IBOutlet weak var mainViewTextLabel: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do anything else you need to do when loading
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is SecondaryView {
let vc = segue.destination as? SecondaryView
vc?.text = mainViewTextLabel.text ?? ""
}
}
}
It looks like you are not passing through the text of the UITextField. Instead you are passing through a reference to the UITextField as a string.
What you want to do is access the .text property:
vc?.text = mainViewTextLabel.text!
The .text property returns a String optional or String? and it is generally bad practice to force unwrap it, since it could crash your application. Instead, you can use a guard/let statement to make sure it is not null. So:
vc?.text = "\(mainViewTextLabel!)"
can be replaced by:
guard let textFromTextField = mainViewTextLabel.text else {
return
}
vc?.text = textFromTextField

Why passing data with delegate fails in Swift 4

This is my protocol
protocol PassDataDelegate {
func passData(data: String)
}
My first controller
class FirstViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
var delegate: PassDataDelegate?
override func viewDidLoad() {
super.viewDidLoad()
delegate = SecondViewController()
}
#IBAction func sendDataButtonTapped(_ sender: Any) {
delegate?.passData(data: textField.text!)
performSegue(withIdentifier: "Go", sender: nil)
}
}
And second, final one
class SecondViewController: UIViewController, PassDataDelegate {
#IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func passData(data: String) {
print("This came from first: \(data). Will change UI.")
myLabel.text = data
}
}
App is crashing on label changing part. It says nil while unwrapping optional. What is wrong here?
SecondViewController() is not the controller designed in the storyboard. It's a brand new instance without connected outlets (which is the reason of the crash). You need the real reference to the SecondViewController instance.
Assuming the SecondViewController instance is the destination view controller of the segue you don't need protocol / delegate, pass the data through the segue
class FirstViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
#IBAction func sendDataButtonTapped(_ sender: Any) {
performSegue(withIdentifier: "Go", sender: nil)
}
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Go" {
let secondController = segue.destination as! SecondViewController
controller.passedData = textField.text!
}
}
}
class SecondViewController: UIViewController, PassDataDelegate {
#IBOutlet weak var myLabel: UILabel!
var passedData = ""
override func viewDidLoad() {
super.viewDidLoad()
print("This came from first: \(passedData). Will change UI.")
myLabel.text = passedData
}
}
There are several fundamental issues with your code.
I think there might also be some misapprehensions on your side regarding delegation and UIStoryboardSegue mechanism. You should probably read up on that here (Delegation) and here (Segues).
That being said, let me post a solution to your problem with inline comments explaining what's going on.
// Has to be marked as a class protocol (`: class`) so that
// `weak var delegate: PassDataDelegate?` can be weak.
protocol PassDataDelegate: class {
func passData(data: String)
}
class FirstViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
// Important!
// Make this a `weak` var. In your case, you would fortunately not create a retain cycle
// but there is a big threat of creating those when using delegation patterns with non-weak delegates.
//
// In your case, if you don't make this `weak`, `SecondViewController` would never be deallocated unless you
// cleared this var manually (setting it to `nil`).
//
// Also note that, if you're using `PassDataDelegate` solely for forwarding some data to the next view controller,
// you can dismiss this var entirely. There is no need to have a reference to the second view controller hanging around.
// In fact, as mentioned above, it can be dangerous to do so.
// Additionally, you don't need to make the protocol `: class` then.
private weak var delegate: PassDataDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// It doesn't make any sense to create a `SecondViewController` here.
// The segue mechanism will create a new instance of `SecondViewController`.
// delegate = SecondViewController()
}
#IBAction func sendDataButtonTapped(_ sender: Any) {
// `delegate?` is `nil` here.
// delegate?.passData(data: textField.text!)
performSegue(withIdentifier: "Go", sender: nil)
}
// This is the proper 'hook' for you to forward data or do anything with a destination
// view controller presented using `UIStoryboardSegue`.
// This method will be called by the system following your call to `performSegue`.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
// `UITextField.text` can be `nil`, so safeguard for that here.
// If the destination implements our protocol, we can forward data to it.
if let text = textField.text, let delegate = segue.destination as? PassDataDelegate {
// This is optional. You can hang on to the destination view controller here, but
// review the comments above to reason about whether this makes sense or not.
self.delegate = delegate
// We can safely forward the data (text) here.
delegate.passData(data: text)
}
}
}
SecondViewController can stay as is.
Update
Regarding Delegation
The delegation pattern usually describes a back pointer which talks back to an initiating instance. E.g. using UITableViewDataSource, a UITableView talks back to a thing implementing this protocol to get information about its data and so on.You are essentially doing the opposite here by forwarding data to SecondViewController. As mentioned in the comments, this code even breaks, because the implementation of passData in SecondViewController is using outlets not yet initialised.
Now you can do one of three things here:
1
Keep the pattern you are using right now (which is not delegation to be precise) and change SecondViewController to make things work
class SecondViewController: UIViewController, PassDataDelegate {
#IBOutlet weak var myLabel: UILabel!
private var data: String?
override func viewDidLoad() {
super.viewDidLoad()
// It is safe to access `myLabel` in `viewDidLoad`. Outlets have been connected.
if let data = data {
myLabel.text = data
}
}
func passData(data: String) {
self.data = data
// Only access `myLabel` if the view is loaded.
if isViewLoaded {
print("This came from first: \(data). Will change UI.")
myLabel.text = data
}
}
}
This approach is very cumbersome actually, because you need to manoeuvre around the fact that passData may be called at any time. So you don't know if your outlets have been initialised yet, which leads to bloated and repetitive code. Bad.
2
Strip protocols entirely and use a more straightforward approach
class FirstViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
// This is the proper 'hook' for you to forward data or do anything with a destination
// view controller presented using `UIStoryboardSegue`.
// This method will be called by the system following your call to `performSegue`.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
// `UITextField.text` can be `nil`, so safeguard for that here.
// If the destination is a `SecondViewController`, we know that is has `public var data: String` and we can forward data to it.
if let text = textField.text, let destination = segue.destination as? SecondViewController {
// We can safely forward the data (text) here.
destination.data = text
}
}
}
class SecondViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
// Deliberatly marking this a `public` to make clear that
// you're intented to set this from the 'outside'.
public var data: String? {
didSet {
if isViewLoaded {
myLabel.text = data
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// It is safe to access `myLabel` in `viewDidLoad`. Outlets have been connected.
if let data = data {
myLabel.text = data
}
}
}
Again, there are things we don't like about his approach:
Still repeating code and having to check for isViewLoaded
You specifically wanted to use protocols, we don't do that here
We could work around the repetitive code issue by providing the data in an init of SecondViewController. However, since you're using segues, the storyboard will instantiate the destination view controller for you and you cannot gain control over that. Now you could strip using segues, but this quickly moves far away from your original question and is a totally different code only approach. So this is no good either.
3
Use protocols but apply the delegation pattern correctly.
protocol DataProvider: class {
func provideData() -> String?
}
protocol DataHandler: class {
var providerDelegate: DataProvider? { get set }
}
class FirstViewController: UIViewController, DataProvider {
#IBOutlet weak var textField: UITextField!
func provideData() -> String? {
return textField.text
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
// If the destination is a `DataHandler`, we can set yourselves as its provider.
if let destination = segue.destination as? DataHandler {
destination.providerDelegate = self
}
}
}
class SecondViewController: UIViewController, DataHandler {
#IBOutlet weak var myLabel: UILabel!
weak var providerDelegate: DataProvider?
override func viewDidLoad() {
super.viewDidLoad()
if let data = providerDelegate?.provideData() {
// Safe to access `myLabel`, because we are in `viewDidLoad`.
myLabel.text = data
}
}
}
This approach is the most generic. Both parties don't care what exactly the handler and provider are. Note that in a classical delegation pattern, you would probably not have the DataHandler protocol and check for a concrete type (here SecondViewController) in prepareForSegue. However, this approach is more flexible while still having the delegation weaved into it. This approach is also the most robust from the SecondViewController point of view. Instead of having to handle passData at any moment, it can decide itself when to ask its delegate (DataProvider) for the data. This way, SecondViewController can reason about when all of its outlets, etc. are initialised and it is safe to process the data.

How to read data set in first VC in other VC?

I am a looking for a way to read the data set in the first view controller in another view controller
In my fist VC i have
class ScannerViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, AVCaptureMetadataOutputObjectsDelegate {
var scannedVisitors = [Visitor]()
.....
....
override func viewDidLoad() {
super.viewDidLoad()
// test data, will be provided server side
var visitor = Visitor(visitorName: "John", visitorCompany: "google", visitorPlace: "San", visitorPhone: "94888484", visitorEmail: "john#google.com")
scannedVisitors += [visitor]
visitor = Visitor(visitorName: "Matt", visitorCompany: "apple", visitorPlace: "Tokyo", visitorPhone: "94888484", visitorEmail: "matt#apple.com")
scannedVisitors += [visitor]
Now First view in Storyboard shows the scannedVisitors
But in an other viewController/view i like to list all scannedVisitors in a table view again, for this i have
class ListVisitorsVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var scannedVisitorsArr = [Visitor]()
override func viewDidLoad() {
super.viewDidLoad()
// first try this, later use NSUserDefaults or Core Data
let firstVC = ScannerViewController()
scannedVisitorsArr = firstVC.scannedVisitors
print("Visitors \(scannedVisitorsArr)")
}
But the scannnedVisitorsArr is empty???
1/ How can i fix the empty scannedVisitorsArr? Should i have a seperate populate scannedVisitors function in my first view controller??
2/ Is there a size restriction when using NSUserDefaults??
[EDIT]
Testing with adding a function to first VC
func readAndPopulateData(){
var visitor = Visitor(visitorName: "John", visitorCompany: "google", visitorPlace: "San", visitorPhone: "94888484", visitorEmail: "john#google.com")
scannedVisitors += [visitor]
visitor = Visitor(visitorName: "Matt", visitorCompany: "apple", visitorPlace: "Tokyo", visitorPhone: "94888484", visitorEmail: "matt#apple.com")
scannedVisitors += [visitor]
}
and
override func viewDidLoad() {
super.viewDidLoad()
self.readAndPopulateData()
Now in my other VC
let firstVC = ScannerViewController()
firstVC.readAndPopulateData()
scannedVisitorsArr = firstVC.scannedVisitors
But this just returns
Visitors [folder_name.Visitor, folder_name.Visitor]
and not the data set?
[EDIT]
Reading up on data container singleton as it looks like it's usable for this. But i read different pos/neg stories about using singletons? Any tips on the subject data container singleton are welcome.
You can use prepareForSegue Method
example
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "ToListVisitorsVC") {
//Checking identifier is crucial as there might be multiple
// segues attached to same view
var detailVC = segue!.destinationViewController as ListVisitorsVC;
detailVC.visitors = self.scannedVisitors
}
}

Making a randomizer picking words from text field (Swift)

I'm trying to make app that takes info from the two text fields and randomly selects one of the sentences and places it in a label on another view controller. I'm a student in the Mobile Apps 1 class so I'm new to this. If you could explain it as much as possible it will be greatly appreciated. Happy new year!
My code:
class twoIdeasViewController: UIViewController {
#IBOutlet weak var twoIdeaContinueButton: UIButton!
#IBOutlet weak var twoIdea2TextField: UITextField!
#IBOutlet weak var twoIdea1TextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Enter Ideas"
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
let twoIdea1:String = twoIdea1TextField.text!
let twoIdea2:String = twoIdea2TextField.text!
return true
}
func prepareForSegue(segue: UIStoryboardSegue, Object: AnyObject?){
let twoIdeaFinal = segue.destinationViewController as! twoFinalViewController
twoIdeaFinal.twoIdea = //the variable that will contain the randomizer
}
}
Make use of arc4random_uniform() to generate a random number that controls which of the two text fields you wish to extract and send text from. Also, you seem to need to fix up your prepateForSegue method: you need to match the segue identifier with the identifier of your 2nd view controller (set in attributes inspector while selecting this other view controller in your storyboard).
#IBOutlet weak var twoIdea2TextField: UITextField!
#IBOutlet weak var twoIdea1TextField: UITextField!
// ...
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
/* Get the new view controller using segue.destinationViewController.
Pass the randomly chosen text view text to the UILabel of the
new view controller. */
/* Here: you need to match with the identifier of your
VC 'twoFinalViewController' (set in attributes inspector) */
if segue.identifier == "twoFinalVC" {
let viewController = segue.destinationViewController as! ViewController
let random = arc4random_uniform(2)
viewController.twoFinalLabel.text = (random == 0) ? (twoIdea1TextField.text ?? "") : (twoIdea2TextField.text ?? "")
}
}
For a detailed description covering segue communication between two view controllers (UITableViewController and UIViewController), see the following thread
Global variable and optional binding in Swift
You can use something like that
func getRandomString() -> String
{
let randomNumber = arc4random_uniform(2) + 1
switch randomNumber
{
case 1:
return twoIdea1TextField.text!
case 2:
return twoIdea2TextField.text!
default:
return ""
}
}
I have no time, but I think that with an enum is simpler than what I did.

Class properties not setting in swift

I am storing values in SimpleClass from the ViewController and i am getting in Secondview class. But i am unable to get the values from the SimpleClass in SecondClass. Help me
Thanks
Demo Project
The reason it doesn't work is because in SecondClass you are creating a new instance of SimpleClass.
What you really want to do is to pass the SimpleClass instance from ViewController to SecondClass.
One way to do this is using the prepareForSegue method in 'ViewController`
on ViewController.swift:
import UIKit
class ViewController: UIViewController {
var simObject:SimpleClass?
override func viewDidLoad() {
super.viewDidLoad()
self.title = "First Class";
simObject = SimpleClass()
simObject!.userName = "damu";
simObject!.userPassword = "123";
simObject!.address = "344r";
print(simObject!.address)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// pass the simObject to secondClass
if let secondClass = segue.destinationViewController as? SecondClass {
secondClass.simpleClass = simObject
}
}
}
On SeconClass.swift
import UIKit
class SecondClass: UIViewController {
var simpleClass:SimpleClass?
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Second Class";
if let passedSimpleClass = self.simpleClass {
println(passedSimpleClass.address)
}
}
}
If you want the values from the ViewController to your SecondView you have to implement
override func prepareForSegue(segue: UIStoryboardSegue?, sender:
AnyObject?)
You're getting the right value for temp.username which is nil displayed in log when you go to the Secondview. This is because you're creating a new instance of SimpleClass who's variables are initialised to nil.

Resources