This question already has answers here:
How do you share data between view controllers and other objects in Swift?
(9 answers)
Closed 6 years ago.
For example I've got 2 controllers that already in memory (Class 1, Class 2). How can I access Class 1 data from Class 2?
class Class_1: UIViewController {
var number:UInt8 = 1
override func viewDidAppear(animated: Bool) {
number = 8
}
}
How do I access number variable in Class 2 and print it value? The point is to not make new instance, the point is to get pointer for Class 1 in memory and get access to it's data.
First of all change the name of your ViewController from Class_1 to Class1ViewController and Class_2 to Class2ViewController
You need to set the variable of Class2ViewController while intializing it in Class1ViewController, and to pass the data back from Class2ViewController to Class1ViewController you need to use delegate
For data transfer Class1ViewController to Class2ViewController, Open your Class1ViewController file and add the following prepareForSegue method if you are using storyboard
class Class1ViewController: UIViewController{
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Class2ViewController" {
if let class2ViewController = segue.destinationViewController as? Class2ViewController {
class2ViewController.inforFromClass1 = "Class 2 Variable set from Class 1"
}
}
}
}
However, If you are using xib and have a button for moving to Class2ViewController from Class1ViewController, following code should be written in IBAction of button that triggers Class2ViewController
class Class1ViewController: UIViewController{
#IBAction showClass2ViewController(){
let secondViewController = SecondViewController(nibName:"SecondViewController", bundle: NSBundle.mainBundle())
secondViewController.infoFromClass1 = "Class 2 Variable set from Class 1"
self.showViewController(secondViewController, sender: self)
}
}
This is how you set the variable on Class2ViewController while showing it from Class1ViewController
Now to pass message from Class2ViewController to Class1ViewController you need to use delegates. Open you Class2ViewController and add the following protocol at the top
#objc protocol Class2ViewContollerDelegate :class{
func printMessageFromClass2ViewController()
}
class Class2ViewController: UIViewController {
}
Add a weak reference to the delagate in Class2ViewController class and call it in its ViewDidAppear or any other method you like,
#objc protocol Class2ViewContollerDelegate :class{
func printMessageFromClass2ViewController()
}
class Class2ViewController: UIViewController {
weak var delegate: Class2ViewControllerDelegate?
override func viewDidAppear(animated: Bool) {
self.delegate?.printValueFromClass2ViewController
}
}
Now that we have define the protocol in Class2ViewController we need to implement it in Class1ViewController. Back In your Class1ViewController file implement the protocol like this
class Class1ViewController: UIViewController, Class2ViewControllerDelegate {
func printMessageFromClass2ViewController(){
print("hey I just printed message in Class1ViewController through its delegate in Class2ViewController")
}
// For Xibs
#IBAction showClass2ViewController(){
let secondViewController = SecondViewController(nibName: "SecondViewController", bundle: NSBundle.mainBundle())
secondViewController.infoFromClass1 = "Class 2 Variable set from Class 1"
self.showViewController(secondViewController, sender: self)
}
// For storyboards
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Class2ViewController" {
if let class2ViewController = segue.destinationViewController as?Class2ViewController {
class2ViewController.inforFromClass1 = "Class 2 Variable set from Class 1"
}
}
}
}
Related
This question might be asked already about hiding buttons, but I was wondering if I could just click a button which would affect the variables in another view controller. For example, I have firstViewController and endViewController. There's a button in endViewController that the user presses which should change a variable in the firstViewController. Is there a way to access the endViewController button from the firstViewController?
Edit
I haven't tried much so far except control clicking the endViewController button into the firstViewController (which didn't work).
class firstViewController: UIViewController {
#IBAction func nextButton(_ sender: Any) { //button that sits in endViewController
}
}
You can use the DELEGATE PATTERN to pass data back:
Here's a little help on delegates between two view controllers:
Step 1: Make a protocol in the UIViewController that you will be removing/will be sending the data.
protocol FooTwoViewControllerDelegate:class {
func myVCDidFinish(_ controller: FooTwoViewController, text: String)
}
Step2: Declare the delegate in the sending class (i.e. UIViewcontroller)
class FooTwoViewController: UIViewController {
weak var delegate: FooTwoViewControllerDelegate?
[snip...]
}
Step3: Use the delegate in a class method to send the data to the receiving method, which is any method that adopts the protocol.
#IBAction func saveColor(_ sender: UIBarButtonItem) {
delegate?.myVCDidFinish(self, text: colorLabel.text) //assuming the delegate is assigned otherwise error
}
Step 4: Adopt the protocol in the receiving class
class ViewController: UIViewController, FooTwoViewControllerDelegate {
Step 5: Implement the delegate method
func myVCDidFinish(_ controller: FooTwoViewController, text: String) {
colorLabel.text = "The Color is " + text
controller.navigationController.popViewController(animated: true)
}
Step 6: Set the delegate in the prepareForSegue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "mySegue" {
let vc = segue.destination as! FooTwoViewController
vc.colorString = colorLabel.text
vc.delegate = self
}
}
And that should work. This is of course just code fragments, but should give you the idea. For a long explanation of this code you can go over to my blog entry here:
segues and delegates
If you are interested in what's going on under the hood with a delegate I did write on that here:
under the hood with delegates
original answer
First View Controller
The code for the First View Controller is
import UIKit
class FirstViewController: UIViewController, DataEnteredDelegate {
#IBOutlet weak var label: UILabel!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showSecondViewController" {
let secondViewController = segue.destinationViewController as! SecondViewController
secondViewController.delegate = self
}
}
func userDidEnterInformation(info: String) {
label.text = info
}
}
Note the use of our custom DataEnteredDelegate protocol.
Second View Controller and Protocol
The code for the second view controller is
import UIKit
// protocol used for sending data back
protocol DataEnteredDelegate: class {
func userDidEnterInformation(info: String)
}
class SecondViewController: UIViewController {
// making this a weak variable so that it won't create a strong reference cycle
weak var delegate: DataEnteredDelegate? = nil
#IBOutlet weak var textField: UITextField!
#IBAction func sendTextBackButton(sender: UIButton) {
// call this method on whichever class implements our delegate protocol
delegate?.userDidEnterInformation(textField.text!)
// go back to the previous view controller
self.navigationController?.popViewControllerAnimated(true)
}
}
Note that the protocol is outside of the View Controller class.
That's it. Running the app now you should be able to send data back from the second view controller to the first.
Original post: https://stackoverflow.com/a/33229483/13783496
There are 2 methods:
You can create a segue from button in endviewcontroller to firstviewcontroller in storyboard. You can configure the func prepare(for segue: UIStoryboardSegue, sender: Any?) for it.
let endVC = endViewcontroller()
endVC.color = "blue"
You can keep the variable whose value needs to be changed as static datatype.
On the click action of button, you can access variable as,
EndViewController.color = "Red".
Kindly use static variables only if you want other Viewcontrollers to access it directly.
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 am trying to pass some data from one view to the next, however I am using a segue (using storyboard to do this) in order to open the second view in Modal presentation.
And now I am using delegates to pass some information from the open modal window back to the home view...
It doesn't work, it never reaches the main window. Doesn't crash.
I tried using this:
Inserting rows to tableView using modal popup (which is actually what I am trying to accomplish) but I was not able to make it work. I am also missing the part where it says
myPopUp.delegate = self
since I am using a segue through storyboard and I do not have a way to do this...
Please help!
This is the main controller:
import UIKit
class HomeViewController: UIViewController, AddRowDelegate {
public func didAddRow(name: String){
print("reached HomeViewController") // This does NOT print
}
override func viewDidLoad(){
super.viewDidLoad()
}
}
And this is the Modal Window:
import UIKit
protocol AddRowDelegate {
func didAddRow(name : String)
}
class ModalViewController: UIViewController {
var delegate : AddRowDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func SendInfoBack(_ sender: Any) {
delegate?.didAddRow(name: "whatever")
print("Modal Window") // This part DOES print.
}
}
In prepareForSegue set delegate
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showModal"{
let modalVC = segue.destination as! ModalViewController
modalVC.delegate = self
}
}
If i understand your question right you should the same data type at destination view and pass it through the prepare function
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toTabBar"{
let destView = segue.destination as! UIView
destView.data= dataToPass
}
}
.
I have a parent UIViewController and it has two different view containers - each of them has embedded UIViewController inside. It looks somehow like this:
I want to change the label on the right container when user presses the button stored on the left one.
So far I was able to do it while having a button placed in a parent view controller, then I was just using a protocol:
in my parent component I had:
class ParentController: UIViewController {
var delegateEmbedded:HandleEmbedded?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "segueToFirstEmbeddedController"){
if let embeddedView = segue.destinationViewController as? EmbeddedContainer {
self.delegateEmbedded = embeddedView
}
}
in my container-embedded UIViewController I had:
protocol HandleEmbedded: class {
func setName(label: String)
}
class EmbeddedContainer: UITableViewController, HandleYourChat{
func setName(label: String){
print("setting label to \(label)")
}
}
Situation above works when I have the button placed in a parent controller and want to change the label inside a container. But what happens and how should I pass the data when the button is also embedded, but in a different container than the label?
Do I have to pass the data through the parent controller? What's the best way for doing so?
To pass data from one embedded ViewController to another embedded ViewController, have the parent handle the transfer. Here I have provided a complete example with three ViewControllers and a single StringTaker protocol. Both the main ViewController and the LabelViewController implement this protocol. The main ViewController takes a string from the ButtonViewController and passes it on to the embedded LabelViewController.
ViewController.swift
import UIKit
protocol StringTaker: class {
func takeString(string: String)
}
class ViewController: UIViewController, StringTaker {
weak var stringTaker: StringTaker?
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "EmbedButtonViewController" {
let dvc = segue.destinationViewController as! ButtonViewController
dvc.delegate = self
} else if segue.identifier == "EmbedLabelViewController" {
let dvc = segue.destinationViewController as! LabelViewController
stringTaker = dvc
}
}
// Receive the string from the ButtonViewController
func takeString(string: String) {
// Pass it to the LabelViewController
stringTaker?.takeString(string)
}
}
ButtonViewController.swift
import UIKit
class ButtonViewController: UIViewController {
weak var delegate: StringTaker?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func generateString(sender: UIButton) {
let cities = ["Boston", "Paris", "Sydney", "Mumbai", "Lima"]
// Pick a random city
let city = cities[Int(arc4random_uniform(UInt32(cities.count)))]
// Pass the string to the delegate
delegate?.takeString(city)
}
}
LabelViewController.swift
import UIKit
class LabelViewController: UIViewController, StringTaker {
#IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func takeString(string: String) {
myLabel.text = string
}
}
Things to note:
The LabelViewController and the ButtonViewController know nothing about the ViewController that uses them. This makes it easier to reuse them. You could embed them in another viewController and as long as you properly implement the StringTaker protocol and set up the delegate, everything works.
The key to hooking this up in in naming the embed segues and then properly setting up the delegates in prepareForSegue. The segues can be found in the Document Outline view once the Container is added to the ViewController.
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
}
}