I have a CoreData project where I've got a FilterViewController set up and I want to display the results in a DisplayResultsViewController.
I know it's something to do with the delegate, but I'm stumped how to fix it. I know I'm not the first person to move data from one VC to another, but I can't figure out the answer.
// Top of FilterViewController
import UIKit
import CoreData
protocol FilterViewControllerDelegate: class {
func filterViewController(filter: FilterViewController,
didSelectPredicate predicate:NSPredicate?,
sortDescriptor:NSSortDescriptor?)
}
// Predicates set here
// IBAction to trigger the segue
#IBAction func filter(sender: UIBarButtonItem) {
delegate!.filterViewController(self,
didSelectPredicate: selectedPredicate,
sortDescriptor: selectedSortDescriptor)
dismissViewControllerAnimated(true, completion:nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toDisplaySearchResults" {
let navController = segue.destinationViewController as UINavigationController
let filterVC = navController.topViewController as StretchSelectorViewController
filterVC.coreDataStack = coreDataStack
filterVC.delegate = self // **ERROR here
}
}
You haven't shown the code that declares the class containing the method that fails (and thus determines what self is) so we're missing the key line of code. But in general, your view controller should be declared something like:
class MyViewController: UIViewController, ViewControllerDelegate {
....
}
... where the first class after the colon (UIViewController) indicates the superclass and the following subsequent items(s) identify protocols to which your class is declaring conformance. The error indicates that your class doesn't conform to the ViewControllerDelegate protocol, so it must be missing that declaration.
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.
Trying to pass data from one view controller MainScreenVC to Another RatesVC with protocol and extension, but that's not working, app crashing everytime . I'm clearly see that problem with code on second VC(because print showing correct data after action on first VC) but not sure where is error.
StoryBoard and 1st VC Example
Second VC
1st View controller
import UIKit
protocol transferNameOfCurrency {
func currencySelected(nameOfCurrency: String)
}
class MainScreenVC: UIViewController {
var transferCurrencyDelegate: transferNameOfCurrency?
var nameOfTheCurrency: String?
#IBAction func updateRates(_ sender: Any) {
nameOfTheCurrency = "EUR"
transferCurrencyDelegate?.currencySelected(nameOfCurrency:
nameOfTheCurrency)
print(nameOfTheCurrency)
}
}
2nd ViewController
import UIKit
class RatesVC: UIViewController {
var currencySelected: String?
override func viewDidLoad() {
super.viewDidLoad()
if let push = self.storyboard?.instantiateViewController(withIdentifier: "MainScreenVC") as? MainScreenVC
{
push.transferCurrencyDelegate = self
}
// Do any additional setup after loading the view.
}
}
extension RatesVC: transferNameOfCurrency {
func currencySelected(nameOfCurrency: String) {
currencySelected = nameOfCurrency
print(currencySelected)
}
}
The most obvious problem lies here:
if let push = self.storyboard?.instantiateViewController(withIdentifier: "MainScreenVC") as? MainScreenVC {
push.transferCurrencyDelegate = self
}
You have to realize that instantiateViewController creates a new view controller - it's not the reference to the view controller presented at the screen. In that code you just created a completely new view controller and then set its delegate to self, but otherwise nothing else.
Without knowing the context it is really hard to suggest anything - prepare(for:) segue might be the place where you want to set the delegate. Anyway, the problem is that you have to obtain a reference to the controller that is presented on the screen, the one that is supposed to be reacting to those events.
Moreover, from the memory management aspect, you should really consider making the delegate property a weak one to prevent memory leaks.
EDIT
So after seeing the minimal working example you provided at link, I think I can provide the solution on how to get that string to the SecondVC.
Your first view controller with comments:
import UIKit
class ViewController: UIViewController {
var newLine: String = "EUR"
#IBAction func push(_ sender: Any) {
// here the secondVC does not exist yet, calling delegate.transferWord() here would have no sense
// performSegue will create that secondVC, but now it does not exist, nor it is set up as the delegate
self.performSegue(withIdentifier: "ViewController", sender: navigationController)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let secondVC = segue.destination as? SecondVC, segue.identifier == "ViewController" {
// at this moment secondVC did not load its view yet, trying to access it would cause crash
// because transferWord tries to set label.text directly, we need to make sure that label
// is already set (for experiment you can try comment out next line)
secondVC.loadViewIfNeeded()
// but here secondVC exist, so lets call transferWord on it
secondVC.transferWord(word: newLine)
}
}
}
No need for delegates here, because your ViewController is the one pushing the SecondVC to the Navigation controller - that means that you can access it directly in prepare(for:), as you can see above.
Now the SecondVC is super simple (I omitted unnecessary code):
import UIKit
class SecondVC: UIViewController {
#IBOutlet weak var label: UILabel!
func transferWord(word: String) {
label.text = word
}
}
Storyboards can stay as they are.
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"
}
}
}
}
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
}
}
I had a working app with the CBCentralManager and CBPeripheral components in the same view controller, but now wish to separate the logic so that I can have a separate connection screen. My plan was to create the CBCentralManager on the Connection Page, discover & connect the peripheral, segue to the Dashboard page, and then use the CBPeripheral there.
My code (stripped down) is as follows:
var globalBTDevice : CBPeripheral! // Only using this as a global variable because I can't get this to pass using prepareForSegue
class ConnectionPageViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, CBCentralManagerDelegate {
var centralManager : CBCentralManager!
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
globalBTDevice = self.allFoundDevices[indexPath.row]
centralManager.stopScan()
self.performSegueWithIdentifier("connectedPeripheralSegue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "connectedPeripheralSegue" {
let destinationVC = segue.destinationViewController as DashboardViewController! // ERROR here: cannot convert value of type "UIViewController" to type "DashboardViewController!" in coercion.
globalBTDevice.delegate = destinationVC
}
centralManager.connectPeripheral(globalBTDevice, options: nil)
}
}
and
class DashboardViewController: UIViewController, CBPeripheralDelegate {
// All delegate methods implemented here
}
I have a segue set up between the 2 view controllers with Identifier "connectedPeripheralSegue".
Also, the DashboardViewController is actually for a tab of a TabBarController - not sure if this makes a difference.
So the issue I get is that I can't cast the destination view controller as a DashboardViewController on the line marked ERROR. It seems to be caused by the VC implementing the CBPeripheralDelegate Protocol, as if I remove that, then I can cast (however that makes the code useless, as I need this in that class). If I cast to UIViewController instead of DashboardViewController, then setting the delegate on the next line fails with "Cannot assign value of type "UIViewController!" to type "CBPeripheralDelegate?" (which makes sense).
I'm totally out of ideas on how to fix this. Can anyone help?
Thanks!
You should use as? operator for optional typecasting. Below code should work,
let destinationVC = segue.destinationViewController as? DashboardViewController