performSegueWithIdentifier - Cannot convert value of type 'AnyObject' to argument type 'AnyObject?' - ios

I am trying to pass some string data to a viewcontroller using performSegueWithIdentifier, but I get this error Cannot convert value of type 'AnyObject?'. Type(Aka'Optional<AnyObject>.Type) to expected argument type 'AnyObject?'
Even if I use sender:self, it still does not work.
In the storyboard, the segue is made by dragging a segue from 1st to 2nd view controller.
#IBAction func resetPassword(sender: AnyObject) {
FIRAuth.auth()?.sendPasswordResetWithEmail(emailTextField.text!, completion: { (error) in
var customError = error?.localizedDescription
if error == nil {
let noError = "Click on the link received in the email"
self.emailTextField.text = ""
self.emailTextField.attributedPlaceholder = NSAttributedString(string: noError, attributes:[NSForegroundColorAttributeName: UIColor.blueColor()])
self.customErroSent = noError
performSegueWithIdentifier("fromSeventhToFifth", sender: AnyObject?)
//self.resetButtonOutlet.hidden = true
// self.emailTextField.hidden = true
} else {
self.emailTextField.text = ""
self.emailTextField.attributedPlaceholder = NSAttributedString(string:customError!, attributes:[NSForegroundColorAttributeName: UIColor.redColor()])
}
})
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "fromSeventhToFifth" {
if let destViewController = segue.destinationViewController as? FifthViewController {
destViewController.label.text = customErroSent
}
}
}
}

The sender parameter is of type AnyObject? - so you can supply any object reference or nil, but you can't put AnyObject? since that is a type, not an object.
The error you are getting when you make this change, Implicit use of 'self' in closure, refers to the invocation of the function performSegueWithIdentifier, not the sender argument.
Since you are calling the function from within a closure, Swift needs to ensure that the closure captures self i.e. prevents it from being deallocated while the closure still exists.
Outside the closure this capture isn't necessary as if the object that self refers to has been deallocated the code can't be executing (The code is part of self).
To capture self, simply refer to it inside the closure:
self.performSegueWithIdentifier("fromSeventhToFifth", sender: self)
or
self.performSegueWithIdentifier("fromSeventhToFifth", sender: nil)

AnyObject? is a optional type. You should set it nil or any instance of Class. For example:
performSegueWithIdentifier("fromSeventhToFifth", sender: nil)
performSegueWithIdentifier("fromSeventhToFifth", sender: slef)

Swift 4.0, in TableView Project Template.
To declare:
// MARK: - Segues
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "fromSeventhToFifth" {
if let indexPath = tableView.indexPathForSelectedRow
{
}
}
}
To call:
performSegue(withIdentifier: "fromSeventhToFifth", sender: self)

Related

How to access variables when preparing for segue?

I'm preparing for a Segue (just learning at the moment!) and want to pass an array, which is created in a function, through a segue to arrive at the new view controller.
It's working fine if I just put a text string in there, but when I try change to an array it is blank - I think because it can't access the array because it's outside the scope - but I'm stuck on how to do it
Here's the initial VC code :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToWorkout" {
if let destVC = segue.destination as? WorkoutViewController {
destVC.workoutName = (testArray)
}
}
//Get the new view controller using segue.destinationViewController.
//Pass the selected object to the new view controller.
}
func testfunction() {
let testArray = ["blah","blah","ploop"]
}
and the 'receiving VC code'
class WorkoutViewController: UIViewController {
var workoutName = [String]()
override func viewDidLoad() {
super.viewDidLoad()
print(workoutName)
// Do any additional setup after loading the view.
}
I'm nearly there with it but think I must be missing something basic. How do you do this passing arrays/other variables created in functions?
If the function had more than 1 variable/array, would that change the approach? I.e. one function might produce the exercises in the workout AND the number of reps for example
You can send it in sender
self.performSegue(withIdentifier: "goToWorkout ", sender: testArray)
and in
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToWorkout" {
if let destVC = segue.destination as? WorkoutViewController {
destVC.workoutName = sender as! [String]
}
}
Yes, both occurrences of testArray are not in the same scope and the compiler complains.
Declare the function this way
func testfunction() -> [String] {
let testArray = ["blah","blah","ploop"]
return testArray
}
and assign the array by calling the function
destVC.workoutName = testfunction()
Your issue is caused by testArray being a local variable defined inside the testfunction function making it only accessible from inside the function. If you want to make a variable accessible from everywhere inside the class, you'll have to make it an instance property.
class InitialVC: UIViewController {
let testArray = ["blah","blah","ploop"]
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToWorkout" {
if let destVC = segue.destination as? WorkoutViewController {
destVC.workoutName = testArray
}
}
//Get the new view controller using segue.destinationViewController.
//Pass the selected object to the new view controller.
}
}
When saying:
destVC.workoutName = (testArray)
in the prepare(for segue: UIStoryboardSegue, sender: Any?) method, it definitely doesn't refers to testArray variable in testfunction(), since it is a local variable. If you are not getting a compile time error at the above line, then probably your declaring testArray somewhere in the view controller or maybe globally in the app.
So what you could do is to declare testfunction() as testfunction() -> [String]:
func testfunction() -> [String] {
return["blah","blah","ploop"]
}
thus:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToWorkout" {
if let destVC = segue.destination as? WorkoutViewController {
destVC.workoutName = testfunction()
}
}
}
func testfunction() -> [String] {
return ["blah","blah","ploop"]
}
I guess this is what you want. Functions / methods can return values. It can be achieved by the -> syntax.
Now you can use like:
destVC.workoutName = testfunction()
Few notes
Functions, methods and variables should be named through camel case notation. So, instead of testfunction you should write testFunction (maybe choose a better name also).
Do not forget to read Apple documentation on the subject: Functions.

swift - return and pass multiple arrays and an Int via Segue

I'm nearly there with a small basic program I'm writing (still learning) and I've hit a roadblock.
I can now pass 1 array between 2 view controllers and successfully print it when I hit a button in the 2nd one.
However, what I really want to do is pass 2 arrays and an Integer, created from a function on the first VC and have them accessible via the 2nd VC.
Code for 1st VC is here :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToWorkout" {
if let destVC = segue.destination as? WorkoutViewController {
destVC.bothArrays = self.testFunction()
}
}
//Get the new view controller using segue.destinationViewController.
//Pass the selected object to the new view controller.
}
func testFunction() -> [String] {
let randomArray1 = ["blah","blah","ploop"]
let randomArray2 = ["alan", "john"]
let randomInt = 5
return BOTH ARRAYS AND INT TO SEND TO THE NEXT VIEW CONTROLLER?
}
#IBAction func goPressed(_ sender: UIButton) {
performSegue(withIdentifier: "goToNextVC", sender: self)
}
and 2nd VC here :
class WorkoutViewController: UIViewController {
var randomArray1 = [String]()
var randomArray2 = [String]()
var randomInt = 0
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func nowButtonPressed(_ sender: UIButton) {
print(randomArray1)
print(randomArray2)
print(randomInt)
}
}
I can get it working with just one array but I need more than one array and a value to be passed! I've tried playing around with it (e.g. trying '-> [String], [String], Int) but no luck
Any help much appreciated!
You can simply use a tuple to include several variables of different types in a single variable. You should pass the tuple including all 3 variables in your performSegue function as the sender argument, then assign them to the relevant instance properties in the prepare(for:) method.
If you want to keep the function for generating the variables, you should change the return type of the function to a tuple that can fit the 3 variables.
func testFunction() -> (arr1:[String],arr2:[String],randInt:Int) {
let randomArray1 = ["blah","blah","ploop"]
let randomArray2 = ["alan", "john"]
let randomInt = 5
return (randomArray1, randomArray2, randomInt)
}
Then assign the return value of testFunction to the sender input argument of performSegue:
performSegue(withIdentifier: "goToNextVC", sender: testFunction())
Assign the variables:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToWorkout" {
if let destVC = segue.destination as? WorkoutViewController, let variablesToBePassed = sender as? (arr1:[String],arr2:[String],randInt:Int) {
destVC.randomArray1 = variablesToBePassed.arr1
destVC.randomArray2 = variablesToBePassed.arr2
destVC.randomInt = variablesToBePassed.randInt
}
}
}
As others have suggested, you can refactor your function to return a tuple, and then use that to pass to your other view controller:
//This is the tuple data type we use to pass 2 string arrays and an Int
typealias parameterTuple = ([String], [String], Int)
func testFunction() -> parameterTuple {
let randomArray1 = ["blah","blah","ploop"]
let randomArray2 = ["alan", "john"]
let randomInt = 5
return (randomArray1, randomArray2, randomInt)
}
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToWorkout" {
if let destVC = segue.destination as? WorkoutViewController {
//refactor WorkoutViewController to have a parameters property
//of type parameterTuple, split out the tuple and pass each part to
//a different property in your WorkoutViewController
destVC.parameters = testFunction()
}
}
}

prepareForSegue called before performSegue

I am trying to perform a segue that passes a number of variables to the next view including one variable, currentID, which is retrieved from a parse database. performSegue should not be called until after currentID has been set to the currentID downloaded from the database. However, when I run the code, currentID ends up being an empty string when it is passed to the next view.
Here is my code called by the Button:
#IBAction func submitButtonPressed(_ sender: Any) {
let point = PFGeoPoint(latitude:0.0, longitude:0.0)
let testObject = PFObject(className: "Person")
testObject["inputAmount"] = inputAmount
testObject["outputAmount"] = outputAmount
testObject["inputCurrency"] = inputCurrency
testObject["outputCurrency"] = outputCurrency
testObject["location"] = point
testObject.saveInBackground { (success, error) -> Void in
// added test for success 11th July 2016
if success {
print("Object has been saved.")
self.currentID = String(describing: testObject.objectId!)
if(self.currentID != ""){
self.performSegue(withIdentifier: "mainToListSegue", sender: self)
}
} else {
if error != nil {
print (error)
} else {
print ("Error")
}
}
}
}
And here is the prepareForSegue method:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let listViewController = (segue.destination as! UINavigationController).viewControllers[0] as! ListViewController
listViewController.inputCurrency = inputCurrency
listViewController.outputCurrency = outputCurrency
listViewController.inputAmount = inputAmount
listViewController.outputAmount = outputAmount
listViewController.currentID = currentID
listViewController.cellContent = cellContent
}
To achieve your needs, you MUST connect your segue between viewcontrollers, and not from UIButton to viewcontroller.
Every time you need to prepare your segue before calling it, this is the procedure:
Then, name it and use delegate method
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "mySegue" {
}
}
For navigating from one controller to another, connect your segue from view controller instead of from the button and it will work.

Swift sending Multiple Objects to View Controller

I am trying to send multiple objects from my initial view controller to my Username VC. Here is the segue code from my controllers: The issue comes when I add in the code to send the second object, termreport. If I delete the termsM and the assignment, it send the students as usually, but I also need to send the termReport object. How would I fix this?
ViewControler:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let students = sender as AnyObject as? [Student]
else { return }
guard let termsM = sender as AnyObject as? [TermReport] //How would I send both objects?
else { return }
if let secondVC = segue.destination as? UsernameVC {
secondVC.students = students
secondVC.userWebView = webView
secondVC.terms = termsM // not sending
}
let gradeResponse = try Parser(innerHTML)
self.performSegue(withIdentifier: "ShowStudents", sender: gradeResponse.students)
self.performSegue(withIdentifier: "ShowStudents", sender: gradeResponse.termReports) //how would I send both variables?
UsernameVC:
var terms: [TermReport]!
override func viewDidLoad() {
print("TERM \(terms[0].grades[3])")//returns found nil optional ERROR
}
You have to include all of the variables you want to send to another ViewController using a segue into a single object (which can be a collection as well). You either create a custom class/struct that has properties with type [Student] and [TermReport] or put these into a native collection (Tuple or Dictionary).
Create custom struct:
struct TermData {
var students = [Student]()
var termReports = [TermReport]()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let segueData = sender as? TermData
else { return }
if let secondVC = segue.destination as? UsernameVC {
secondVC.students = segueData.students
secondVC.userWebView = webView
secondVC.terms = segueData.termReports
}
}
let gradeResponse = try Parser(innerHTML)
let termData = TermData(students: gradeResponse.students, termReports: gradeResponse.termReports)
self.performSegue(withIdentifier: "ShowStudents", sender: termData)

Accessing a constant from another view controller

I have this function inside class firstViewController that produces itemID
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
// Check if the metadataObjects array is not nil and it contains at least one object.
if metadataObjects == nil || metadataObjects.count == 0 {
qrCodeFrameView?.frame = CGRect.zero
messageLabel.text = "No QR/barcode is detected"
return
}
//Get metadata object
let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
if supportedCodeTypes.contains(metadataObj.type) {
//if the found metadata is equal to the QR code metadata then update the status label's text and set the the bounds
let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj)
qrCodeFrameView?.frame = barCodeObject!.bounds
if metadataObj.stringValue != nil {
messageLabel.text = metadataObj.stringValue
let itemID = metadataObj.stringValue
print(itemID)
}
}
}
I want to call itemID in another viewController called secondViewController. How do I call itemID from the firstViewController inside the secondViewController?
You just need to pass that on "prepareForSegue".
just make sure you declare itemId as variable on secondviewcontroller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if (segue.identifier == "secondViewControllerSeque") {
let secondViewController = segue.destination as! SecondViewController
secondViewController.itemID = self.itemID
}
}
If your segueing from the FirstViewController to SecondViewController you can pass the data like this.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "SecondViewControllerSegue" {
let secondViewController = segue.destination as? SecondViewController
if let itemID = itemID {
secondViewController.itemID = itemID
}
}
}
But don't forget to create itemID variable in FirstViewController and instead write the code like this
var itemID: String?
if metadataObj.stringValue != nil {
messageLabel.text = metadataObj.stringValue
itemID = metadataObj.stringValue
}
The firstViewController class needs a member variable, itemID. Then the second viewcontroller should be able to reference it.
Right now itemID is a local variable in your captureOutpur function.
This goes away once the function is finished running. Assign it to the member instead.
If you are using a segue, you only need to do this:
FirstViewController (note: use capital letter on name):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowNextView" { // the identifier is what you named it
if let vc = segue.destination as? SecondViewController {
vc.itemID = itemID // itemID is accessible here, probably global
}
}
}
SecondViewController:
var itemID:String
func viewWillAppear() {
// code to use itemID value
}
Just need to follow 3 steps, let's assume you want to pass data from ViewControllerA to ViewControllerB:
1.create a segue between ViewControllerA and ViewControllerB
2.name the segue with a Identifier in the attributes inspector of it
3.override the prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) at ViewControllerA
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "MySegueID" {
if let destination = segue.destinationViewController as? ViewControllerB {
destination.myInformation = self.myInformation
}
}
}
At first declare a variable in FirstViewController.
var itemID: String?
And call your function to assign your value to that variable before navigation to SecondViewController.
if metadataObj.stringValue != nil {
self.itemID = metadataObj.stringValue
}
Now, declare itemIdForSVC public variable in SecondViewController also,
public var itemIdForSVC: String?
If you are using Storyboard then pass parameter like this while navigating,
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if (segue.identifier == "secondViewControllerSeque") {
let secondViewController = segue.destination as! SecondViewController
secondViewController.itemIdForSVC = self.itemID
}
}
Or if you are doing all programmatically then pass like this while navigating,
let secondViewConttoller = SecondViewController()
secondViewController.itemIdForSVC = self.itemID
self.navigationController?.pushViewController(secondViewConttoller, animated: true)

Resources