I'm trying to send data from one ViewController to another with delegate, but can't seem to get the right instance
I've tried setting the delegate at different places within the receiving ViewController including ViewDidLoad, but the delegate in the sending ViewController is always nil.
From what I've learned, it's an average problem everybody seems to go through, and I've read quite a number of samples, tried them, but to no avail. I don't know if I'm leaving something out or not. Please shed some light if you will.
Below is what I ended up with.
The sending ViewController:
protocol CreateChatDelegate: class{
func appendChatData(_ sender: CreateChatViewController)
}
class CreateChatViewController: UIViewController {
weak var delegate: CreateChatDelegate!
#IBAction func createChat(_ sender: AnyObject) {
delegate?.appendChatData(self)
if delegate == nil {
print("delegate unsuccessful")
} else {
print("delegate successful")
}
}
The receiving ViewController:
class ViewController: UIViewController{
var createChatViewController: CreateChatViewController!
override func viewDidLoad() {
super.viewDidLoad()
...
}
}
extension ViewController: CreateChatDelegate {
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// get the reference to the ViewController
self.createChatViewController = segue.destination as? CreateChatViewController
// set it as delegate
self.createChatViewController?.delegate = self
print("ViewController: delegate successful")
}
}
func appendChatData(_ sender: CreateChatViewController) {
print("ViewController: CreateChatDelegate called")
}
}
this code outputs "delegate unsuccessful", because delegate is always nil
The method you are using is incorrect. You should use the new one:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
....
}
Notice the override keyword? if you don't see this when you are writing the viewController methods, It means that you are NOT calling the original method and misspelled the function signature.
NOTE: if you are targeting older iOS and using older Xcode, the method name may be different, you should write the name of the method and let the AutoComplete help you with the correct one.
To successsfuly configure segue you need to make sure that
1- Navigation is triggered by
self.performSegue(withIdentifier:"segue",sender:nil)
2- Force-unwrap
self.createChatViewController = segue.destination as! CreateChatViewController
as as? cast may fail silently for some reason
First make sure that prepareForSegue() method is called. Then make sure that it is called for the CreateChatViewController i.e.
if let destination = segue.destination as? CreateChatViewController {
//Do all the stuff there e.g.
destination.delegate = self
}
If your prepareForSegue() method is not called then set the action properly so it will fire the prepareForSegue() method then you will get the delegate value in the CreateChatViewController.
Related
I've set up a simple Swift project to try and wrap my head around delegates & protocols. The goal is to pass data between two classes (SendingClass & ReceivingClass). Two buttons in the SendingClass are linked to the delegate which should trigger the Protocol conforming function in the ReceivingClass to execute. This doesn't work unfortunately, I suspect it has to do with where and how I am declaring the ReceivingClass as the delegate.
Appreciate your insights, i'm just starting out!
I've tried setting the delegate in various locations (presently within viewDidLoad, but cant get it to work).
let vc = SendingClass()
vc.statusDelegate = self
SendingClass.swift
import UIKit
protocol StatusDelegate {
func statusChanged(state: Bool, sender: String)
}
class SendingClass: UIViewController {
var statusDelegate : StatusDelegate?
#IBAction func button1Pressed(_ sender: UIButton) {
statusDelegate?.statusChanged(state: true, sender: "Button 1")
}
#IBAction func button2Pressed(_ sender: UIButton) {
statusDelegate?.statusChanged(state: false, sender: "Button 2")
}
}
ReceivingClass.swift
import Foundation
import UIKit
class ReceivingClass: UIViewController, StatusDelegate {
override func viewDidLoad() {
let vc = SendingClass()
vc.statusDelegate = self
}
func statusChanged(state: Bool, sender: String) {
print("Sender = \(sender) , State = \(state)")
}
}
Expected: the ReceivingClass protocol conforming function (func statusChanged) should execute each time the buttons are pressed within the SendingClass.
Actual: Nothing happens
I am using this..
// create extension in your receiving class
extension ReceivingClass: PopUpVCDelegate {
func statusChanged(state: Bool, sender: String) {
print("Sender = \(sender) , State = \(state)")
}
}
// on sending class, when you present your receiving class on any button click
eg.
let resultController = self.storyboard?.instantiateViewController(withIdentifier: "PopUpVCID") as? PopUpVC
resultController?.delegate = self
self.present(resultController!, animated: true, completion: nil)
//or if not have button add on viewdidload in receiving class
// here is full eg
How to get data from popup view controller to custom table view cell?
For protocol and delegate, you use it when u want to bring a value from 2nd VC (presented by 1st or pushed by 1st VC) to 1st VC, which is the original.
From your code, I dont see you presenting or pushing your 2nd VC. that's why it's not working. Hopefully I answered your doubt.
However if you still want to bring a value over from 1st VC to 2nd VC. In second VC, create a variable to receive it
var ReceivedData = String()
then from your first VC, when u are going to push it,
let vc = SendingClass()
vc.ReceivedData = "Whatever you want it to receive"
If you're using storyboard segues, maybe the view controller is instantiated from there so probably you have to use the prepareForSegue and get the destination view controller (which is already instantiated for you) in the ReceivingClass view controller:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if let destination = segue.destination as? SendingClass {
destination.delegate = self
}
}
Also be careful with delegate patter: the delegate property should be declared as a weak property to avoid retain-cycle
weak var delegate: MyDelegate?
I have two VCs in my project. I have a UIButton that segues to the second VC. I have data being sent to this VC. I want the second VC to be able to add to the array that is sent and then send it back.
In my main VC I have:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let toViewController = segue.destination as! SaveViewController
toViewController.masterView = self
In the second VC I have:
var masterView:UIViewController!
...
override func viewWillDisappear(_ animated: Bool) {
masterView.listArray = listArray
}
What I am getting is
Value of type 'UIViewController' has no member 'listArray.'
The listArray is declared in both VCs. If this is a correct way to go about doing what I am trying to do, I am obviously assuming that I must do some more configuring in the second ViewController in order to make the other VC accessible.
Probably this is not the right way to pass data back the the previous view controller. Although there are other options that you can follow to achieve the desired functionality, I would recommend to follow the Delegation pattern approach.
For your case, you could do it like this -for instance-:
According to "How to Apply Delegation?" section in this answer, the first thing that we should do is to implement the needed protocol:
protocol SaveViewControllerDelegate: class {
// I assumed that 'listArray' is an array of strings, change it to the desired type...
func saveViewControllerWillDisappear(_ listArray: [String], viewController: UIViewController)
}
Thus in SaveViewController, you should create -weak- instance of SaveViewControllerDelegate and call its method at for the desired behavior:
class SaveViewController: UIViewController {
weak var delegate: SaveViewControllerDelegate? = nil
var listArray: [String]!
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// assuming that you already did the required update to 'listArray'
// you would need to pass it here:
delegate?.saveViewControllerWillDisappear(listArray, viewController: self)
}
}
So far we added the necessary code for the SaveViewController, let's jump the the MasterViewController (first view controller):
Next, you would need to conform to SaveViewControllerDelegate, Connecting the delegate object and implement its method (steps from 2 to 4 in the mentioned answer):
class MasterViewController: UIViewController, SaveViewControllerDelegate {
var listArray: [String]!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let toViewController = segue.destination as! SaveViewController
// make sure to add this:
toViewController.delegate = self
toViewController.listArray = self.listArray
}
func saveViewControllerWillDisappear(_ listArray: [String], viewController: UIViewController) {
print("here is my updated array list: \(listArray)")
}
}
At this point, saveViewControllerWillDisappear method should be get called when coming back from SaveViewController, including listArray as a parameter.
Aside note:
The reason of the error that you are facing is that you are declaring masterView as UIViewController, what you should do instead is:
var masterView:MasterViewController!
HOWEVER keep in mind that this approach still -as I mentioned before- inappropriate one.
This happens because UIViewController has no element listView.
Change MasterView type to:
var masterView: FirstViewController!
I have gone through all the other posts about this topic but they don't seem to help me.
I have a UITabBarController that is launching two tabs. I want to pass data collected in tab1 to the UITabBar ViewController. I am trying to use delegete protocol for this but I am having trouble setting the delegate variable in the sending ViewController. The prepare for segue never gets called. I cannot even cycle through the viewcontrollers of the tabs inside the ViewDidLoad() of the Tabbar controller as they are not created yet and so nil.
I have used delegates before and it seems rather straightforward. Does it matter that I am using it in a Tabbar?
When I run the code the viewDidLoad() in TabBarViewController is called but not the preparefor segue.
The IBAction donePressed in the MeViewController is called but the delegate is not called as its not set.
Here is the code --
protocol DetailsDelegate: class {
func myDetailsGathered( myDetails: MyDetails )
}
/// RECEIVING VIEW CONTROLLER
class TabBarViewController: UITabBarController, DetailsDelegate
{
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
print("prepare for segue called\n");
if let destinationViewController = segue.destination as? MeViewController
{
destinationViewController.delegate = self
}
}
override func viewDidLoad()
{
print("ViewDidLoad Called \n")
}
func myDetailsGathered(myDetails: MyDetails)
{
self.myDetails = myDetails
print("My details gathered \n")
}
}
---------------
/// SENDING VIEW CONTROLLER
class MeViewController: UIViewController
{
weak var delegate: DetailsDelegate?
override func viewDidLoad()
{
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
// I have UIButton in the view and this is invoked when its pressed.
#IBAction func donePressed(_ sender: Any)
{
var infoToPass = MyDetails()
print("looks like we are done")
delegate?.myDetailsGathered(infoToPass: myDetails)
}
}
prepareForSegue is called when you perform a segue. Which you don´t do and that´s why it does not get called.
A segue defines a transition between two view controllers in your
app’s storyboard file.
You should use a singleton class to store variables and access them between different controllers. You declare one like this:
class Singleton {
static let sharedInstance = Singleton()
var name = ""
}
Assign to Singleton:
Singleton.sharedInstance.name = "Some name"
To read from it from whatever controller:
let name = Singleton.sharedInstance.name
First of all, why do you want your tabbarController to receive some info/data though?
The prepare for segue never gets called.
prepareForSegue method will be invoked right after the performSegue. So where's your performSegue method? Or are you sure that that kind of segue going to MeViewController is being performed?
One more option you have is to use NotificationCenter.
I've got my EditorViewController that segues modally to my ModalViewController, and in the ModalViewController I have to pass some data back to the EditorViewController after the view is dismissed. I've looked at many tutorials about delegates and protocols, and I believe that's what I have to do to pass this information, but I can't seem to get the code right although I've followed the tutorials exactly. If anyone can see what's going wrong in here I would appreciate it. I'll post the code.
The protocol
protocol passColorBackDelegate {
func colorToChange(_ color: String)
}
The first view Controller
class EditorViewController: UIViewController, passColorBackDelegate {
func colorToChange(_ color: String) {
print("Hello")
}
The second view Controller file (the one with data to pass back to first), also has another class in it, I'm stingy with my files
class subView: UIView {
}
class ModalViewController: UIViewController {
var delegate: passColorBackDelegate?
#IBAction func changeColor(_ sender: UIButton) {
switch sender {
case blueColorButton: colorToChangeTo = "Blue"
case redColorButton: colorToChangeTo = "Red"
case greenColorButton: colorToChangeTo = "Green"
case purpColorButton: colorToChangeTo = "Purple"
default: print("error")
}
print(colorToChangeTo)
delegate?.colorToChange(colorToChangeTo)
self.dismiss(animated: true, completion: nil)
}
As you can see my protocol function doesn't include any of the data I need passed back yet, but the message still isn't printing, meaning the function isn't getting called. If anyone could tell me what I'm doing wrong I would appreciate it. Thanks
You will need to set the delegate before you perform the segue. Since it sounds like you're using Storyboards, this can be done in prepare(for segue):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationVC = segue.destination as? ModalViewController {
destinationVC.delegate = self
}
}
Also, as Paul mentioned in the comments, capitalizing your protocols (PassColorBackProtocol) and classes (SubView) is the conventional style in Swift and helps other people understand your code.
Hello learning swift and am stuck with calling a method through delegate. Checked multiple answers with similar issues and have tried the solutions but have not been able to successfully apply them to my own situation however I am close.
I have a delegator class named ViewController that holds a variable I would like to change. I have another view called MoodScroll which serves as the delegate. Moodscroll has a button being used to change the value for the variable in ViewController.
ViewController :
class ViewController: UIViewController, AVAudioPlayerDelegate, MoodScrollDelegate {
var alarmSoundType: String?
func acceptData(data: String?) {
alarmSoundType = "\(data)"
print(data)
}
}
MoodScroll :
protocol MoodScrollDelegate {
func acceptData(data: String?)
}
import UIKit
class MoodScroll: UIViewController {
#IBAction func WTF(sender: AnyObject) {
self.delegate?.acceptData("hello")
print("function called")
}
}
The IBAction calls fine as it prints "function called" in the console however it doesn't pass the value to ViewController as alarmSoundType remains nil and also the print command is not called in ViewController as well.
It seems you still have some confusion about delegation : if ViewController conforms to MoodScrollDelegate protocol, then your ViewController object will be the delegate, not the MoodScroll object.
Where do you set the delegate property of your MoodScroll object ?
If this object is created programmatically from your ViewController object, you should set it after initialization :
myMoodScrollObject.delegate = self
Is the object is created using Interface Builder, you can either use an outlet variable for delegate, or set it in prepareForSegue:sender of your ViewController class :
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let scroll = segue.destinationViewController as? MoodScroll{
scroll.delegate = self
}
}
One picky note: the way you have described your problem, it's actually ViewController what you should call the delegate of MoodScroll. Most likely you're probably forgetting to set the delegate property of MoodScroll.
I don't know how these two view controllers relate to each other in your code, but very often you would set the delegate property in the prepareForSegue method of ViewController, for example:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SegueToMoodScroll" {
let moodScrollController = segue.destinationViewController as! MoodScroll
moodScrollController.delegate = self
}
}