segue and transfer data from viewcontroller to viewcontroller to another viewcontroller - ios

sorry for confusing title, but here is my problem. i do a segue from "superVC" (collectionviewcell) to "childVC" and its working well. then i want to segue again from that "childVC" to the "secondChildVC" (which is all the data is from superVC). is it possible? cause i always get a nil value when performing that segue.
its something like this : SuperVC -> ChildVC -> secondChildVC
here is the superVC segue:
var destination = [DestinationData]()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "DestinationDetailVC"{
let detailVC = segue.destination as? DestinationDetailVC
if let data = sender as? DestinationData{
detailVC?.destinationDetail = data
}
}
}
here is 2nd vc segue
var destinationDetail : DestinationData?
#IBAction func goToMapOnPressed(_ sender: Any) {
let detail = destinationDetail
self.performSegue(withIdentifier: "DestinationMapVC", sender: detail )
print(detail)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "DestinationMapVC"{
let detailVC = segue.destination as? DestinationMapVC
if let data = sender as? DestinationData{
detailVC?.destinationMapDetail = data
}
}
}
Thanks

I think you are sending array of DestinationData to first segue, but inside the first segue you have put condition, so that if the data would be of DestinationData kind class, then the data will be sent to nextVC, But in actual you are sending array of DestinationData object so the if condition fails and hence the data are not passed to childVC
That's why you might be getting nil in the secondChildVC as there is no data in destinationDetail of childVC.
Hence you can modify the condition to check for array as the destination holds array type of data. or else remove the condition from the prepareSegue method.
Code :
//Write this code in to get that clicked object then pass that object in sender
let destTemp = sender[your selected index]
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "DestinationDetailVC"{
let detailVC = segue.destination as? DestinationDetailVC
if let data = sender as? DestinationData{
detailVC?.destinationDetail = data
}
}
}

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.

Getting error while trasferring a file through prepare for segue method use of undeclared type of destination viewcontroller in swift 3

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "stopRecording" {
let playsoundVC = segue.destination as! RecordsoundViewCAONTROLLER
let recordAudioURL = sender as! URL
playsoundVC.recordAudioURL = recordAudioURL
}
}
I am transferring audio file from one viewcontroller to other but I am getting an error i.e : Use of undeclared type 'RecordsoundViewCAONTROLLER'

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)

Can't seem to get a certain value to pass to another controller

So I have 3 controllers, let's say ChatVC, MenuVC, and InviteVC.
Starting on ChatVC, I open the menu and tap a button that dismisses the menu back to ChatVC, then segues to InviteVC.
Both ChatVC and MenuVC have the value I need to pass to InviteVC (currentRoomID). However it seems to be quite the issue getting that to InviteVC. currentRoomID is initialized on InviteVC as an empty string.
This is the action that is performed when I tap the button in the menu to take me to InviteVC:
#IBAction func invitePressed(_ sender: Any) {
weak var pvc = self.presentingViewController
self.dismiss(animated: true) {
pvc?.performSegue(withIdentifier: "chatInvite", sender: nil)
}
}
I've tried adding this in the dismiss closure, as well as in viewDidLoad of both MenuVC and ChatVC:
let inviteVC = InviteVipViewController()
inviteVC.currentRoomId = self.currentRoomID
I've tried passing it in ChatVCs prepareForSegue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationViewController = segue.destination as? MenuViewController {
destinationViewController.transitioningDelegate = self
destinationViewController.interactor = interactor
destinationViewController.currentRoomID = self.currentRoomID
} else if segue.identifier == "chatInvite" {
let inviteVC = InviteVipViewController()
inviteVC.currentRoomID = self.currentRoomID
}
}
And every time, currentUserID remains an empty string when I get to InviteVC. It didn't seem to be a problem when I was segueing to InviteVC straight from MenuVC, but since I changed it to the current transition (menu drops away back to ChatVC, then segues to InviteVC), it's seeming to be impossible to get that value passed.
This is incredibly frustrating so if anyone can help me try something I haven't tried before it will be very much appreciated!
you have problem in prepareForSegue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationViewController = segue.destination as? MenuViewController {
destinationViewController.transitioningDelegate = self
destinationViewController.interactor = interactor
destinationViewController.currentRoomID = self.currentRoomID
// if identifier equals to chatInvite then you get your InviteViewController from segues Destination not by creating one
} else if segue.identifier == "chatInvite" {
let inviteVC = segue.destination as? InviteVipViewController
inviteVC.currentRoomID = self.currentRoomID
}
}
NOTE: What I can see your trying to segue from a ViewController which you have already dismissed. So I think you have to move that logic to your MainViewController in your case I assume it's MenuViewController
also you should not instantiate your InviteVIPViewController like this
let inviteVC = InviteVipViewController()
inviteVC.currentRoomId = self.currentRoomID
segue will do it for you
You can pass your currentRoomID using UserDefaults as well like
#IBAction func invitePressed(_ sender: Any) {
let userDefaults = UserDefaults.standard
userDefaults.set(currentRoomID, forKey: "roomID")
// call the synchronise so it sync force the user defaults to save
userDefaults.synchronize()
weak var pvc = self.presentingViewController
self.dismiss(animated: true) {
pvc?.performSegue(withIdentifier: "chatInvite", sender: nil)
}
}
Now access your currentRoomID in inviteViewController viewDidLoad()
also I assume the roomID as an int
let userDefaults = UserDefaults.standard
currentRoomID = userDefaults.integer(forKey: "roomID")
// if string then you can use object and cast it to string like this
currentRoomID = userDefaults.object(forKey: "roomID") as! String
This line:
let inviteVC = InviteVipViewController()
creates a completely new instance of the InviteVipViewController, completely unrelated to the instance to which you are about to segue. You should instead use segue.destination (which IS the instance you are about to segue to). The only proviso is that you should test to confirm that the destination is of the correct type (as you do for the segue to MenuViewController):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationViewController = segue.destination as? MenuViewController {
destinationViewController.transitioningDelegate = self
destinationViewController.interactor = interactor
destinationViewController.currentRoomID = self.currentRoomID
} else if segue.identifier == "chatInvite" {
if let inviteVC = segue.destination as? InviteVipViewController {
inviteVC.currentRoomID = self.currentRoomID
}
}
}
I used to use segues and found them unnecessarily complicated. Here's a workaround...
In the part of your code where you import UIKit (at the top), add a variable for whatever you want to pass, in this case, let's say I want to pass a score.
//view controller one
import UIKit
var score = 25
Then go in the view controller where you want to receive the-in this case score, and you can easily access it like it's a variable in your own view controller.
//Viewcontrollertwo
super.viewDidLoad()
score += 2
//score is now = 27, requires no segue

2-Way protocol oriented data passing in swift

I am trying to pass data back and forth between two viewControllers to maintain the data when segueing. I have a mainMenuVC and a SettingsVC. The settings are chosen inside the SettingsVC and then passed to the mainMenuVC to be stored, however, it seems that this does not happen and I am not sure why.
This is the code in mainMenuVC which conforms to the transferSettings protocol.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "optionsSegue" {
let receivingVC:SettingsVC = segue.destination as! SettingsVC
receivingVC.settingsDelegate = self
self.settingsDelegate = receivingVC
if self.settingsDelegate != nil{
if self.currentSettings == nil {
print("currentNil")
self.currentSettings = GameOptions()
}
self.currentSettings.description()
self.settingsDelegate.storeGameSettings(settings: self.currentSettings)
}else{ print("settingsDelegate in MainMenuVC nil")}
}
And this is the code inside `SettingsVC' which also conforms to the protocol above.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "settingsReturn" {
let recievingVC:MainMenuVC = segue.destination as! MainMenuVC
///Set the delegate of MainMenu to SettingsVC
recievingVC.settingsDelegate = self
if self.settingsDelegate != nil {
if self.ScrollView.settingsSelected != nil{
self.ScrollView.saveUserSettings()
print("settingsVC")
self.ScrollView.settingsSelected.description()
self.settingsDelegate.storeGameSettings(settings: self.ScrollView.settingsSelected)
}else{ print("No stored settings in the SettingsVC")}
}else{print("SettingsDelegate in SettingsVC is nil")}
}
}
The way I assumed this would work is that my data would simply pushed back and forth between the two viewControllers, but after examining with breakpoints, I found that after prepareforsegue is finished executing, the properties that store the data in the recievingVC are nil, thereby reseting the process.
Am I missing something here?
Thanks!

Resources