I am having trouble conforming to the delegate and passing information between two view controllers. I can't seem to understand what I am doing wrong. I want to be able to pass information from one segmented control in CustomCell VC, to receive and display it after a button has been tapped in the CreateEvent VC. Here is the relevant code:
CustomCell VC:
class CustomCell: CreateEventVCDelegate {
var ageDescription : String = String() // setup global variable
func sendageDesciptiongetData(data:String) {
print(ageDescription)
}
#IBAction func ageChanged(sender: UISegmentedControl) {
switch age.selectedSegmentIndex {
case 0:
print("Under 18")
var ageDescription = "under 18"
case 1:
print("Over 18")
var ageDescription = "over 18"
case 2:
print("Strictly over 21")
var ageDescription = "strictly over 21"
default:
print("Other")
}
}
}
CreateEvent VC:
protocol CreateEventVCDelegate {
func sendageDescriptiongetData(ageDescription: String)
}
class CreateEventVC: UIViewController {
var delegate: CreateEventVCDelegate?
#IBAction func saveButtonTapped(sender: UIBarButtonItem) {
var ageDescription = ""
delegate!.sendageDescriptiongetData(ageDescription)
}
}
I think you problem is that you need an instance of CreateEventVC inside CustomCell so you can write:
createEventVC.delegate = self
inside CustomCell.
Anyway I think you don't need a delegation, if you have an instance of CustomCell inside CreateEventVC you can call a public/internal function of CreateEventVC from CreateEventVC:
cell.sendageDesciptiongetData(data)
It seems you have a typo:
You are missing 'r' in CustomCell method
func sendageDesciptiongetData(data:String)
instead of
func sendageDescriptiongetData(data:String)
Related
I cannot find an example of the Swift Delegate pattern without the segue included. I want the boss class(boss view) to send orders to the worker class(worker view) and print what the boss says to print. The Delegate is coming up 'nil' when I press either of the buttons (doThisButton/doThatButton) on the boss page.
Edit: My Views are set up as follows: the 'TheBoss' VC has three buttons, 'doThisButton', 'doThatButton', and 'goToTheWorker' button. the 'TheWorker' VC has a text box. The 'doThisButton'/'doThatButton' send information to the text box in 'TheWorker' VC, but do not make 'TheWorker' VC appear. the 'goToTheWorker' button is a show segue that was set up in storyboard to open the 'TheWorker' VC.
This is the 'Boss' class
import UIKit
protocol TheBossesOrdersDelegate {
func doThis(numberOne: Int, stringOne: String)
func doThat(numberTwo: Int, stringTwo: String)
}
class TheBoss: UIViewController {
// Declair Delegate
var delegate: TheBossesOrdersDelegate!
#IBAction func doThisButton(_ sender: UIButton) {
delegate.doThis(numberOne: 75, stringOne: "Do This")
}
#IBAction func doThatButton(_ sender: UIButton) {
delegate.doThat(numberTwo: 125, stringTwo: "Do That")
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
This is the 'Worker' class
import UIKit
class TheWorker: UIViewController, TheBossesOrdersDelegate {
let theBoss = TheBoss()
func doThis(numberOne: Int, stringOne: String) {
print("the boss send this number to print: \(numberOne) and this string: \(stringOne)")
theWorkersTextBox.text = "Number: \(numberOne) String:\(stringOne)"
}
func doThat(numberTwo: Int, stringTwo: String) {
print("the boss send this number to print: \(numberTwo) and this string: \(stringTwo)")
theWorkersTextBox.text = "Number: \(numberTwo) String:\(stringTwo)"
}
#IBOutlet weak var theWorkersTextBox: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
theBoss.delegate = self
}
}
It looks like your code has the worker create the boss. Since you do that, you need to tell the boss who it's delegate is:
import UIKit
class TheWorker: UIViewController, TheBossesOrdersDelegate {
let theBoss = {
let result = TheBoss()
result.delegate = self //This is the line you are missing
return result
}()
//The rest of your TheWorker class's code...
}
EDIT:
Based on your comment, you have a confused mess. Post the Boss class code for the button that displays a worker view controller. And your title says "w/o segue", but then in your comment you say "the boss is presented first and contains a button that connects to the worker via a show segue." What? you said without a segue.
I have a viewController with another containerView insider set up to appear temporarily (added programmatically). The containerView is a sort of operation bar, which allows you to change values of the viewController. The protocol called from an IBAction of a button however, does not call the protocol set up inside the viewController class.
Here is the code from both classes:
class viewController: UIViewController, updateListDelegate {
let dataSource = containerView()
override func viewDidLoad() {
super.viewDidLoad()
dataSource.delegate = self
}
func updateList(sender: containerView) {
print("is called") //is not printed
}
}
The code from the containerView:
protocol updateListDelegate {
func updateList(containerView)
}
class containerView: UIViewController {
var delegate: updateListDelegate?
#IBAction func AddSong(_ sender: UIButton) {
self.delegate?.updateList(sender: self)
}
}
If this method is only to be called from one object, then, in my opinion, I would not define a protocol. If multiple objects are to call this method, then I would define a protocol. This is typically how you would call a method backwards, using a basic delegate.
class ViewController: UIViewController {
let container = ContainerView()
override func viewDidLoad() {
super.viewDidLoad()
container.viewControllerDelegate = self
// push to this instance of container at some point
}
func doSomething() {
print("great success")
}
}
class ContainerView: UIViewController {
weak var viewControllerDelegate: ViewController?
#objc func someAction() {
if let viewControllerDelegate = viewControllerDelegate {
viewControllerDelegate.doSomething()
}
}
}
// prints "great success" when someAction() called
One of the most common mistakes people make is not keeping track of instances. For delegates to work, you must be sure you are using the specific instances that you've instantiated and assigned those delegates to.
I'm trying to use the change event on a UISwitch to move from the current ViewController to a new one. My UISwitch is registered in my CustomView for the Custom Cell in my UITableView. The action is registered an calls a class in my View Controller as below
import UIKit
public class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var operatedSwitch: UISwitch!
#IBAction func operatedSwitchChange() {
updateValveOps.valveUpdate()
}
When it gets to my class in my ViewController it calls a method in the main class which should move to my new ViewController as below
import UIKit
class updateValveOps {
class func valveUpdate() {
let valveOps = ValveOperationsController()
valveOps.ValveOpsUpdate()
}
}
class ValveOperationsController: UIViewController {
.
.
func ValveOpsUpdate() {
performSegueWithIdentifier("ValveOpsToUpdateSegue", sender: nil)
}
However, this causes a Sigabrt error. I've also tried pushing from the current view to the new View Controller but then for some reason it returns back to the calling View Controller! What am I doing wrong?
Try this in your #IBAction func operatedSwitchChange() { .. }
UIApplication.sharedApplication().keyWindow?.visibleViewController()?.performSegueWithIdentifier("ValveOpsToUpdateSegue", sender: nil)
And add this extension to your project (copy-paste this into new swift file):
public extension UIWindow {
func visibleViewController() -> UIViewController? {
if let rootViewController: UIViewController = self.rootViewController {
return UIWindow.getVisibleViewControllerFrom(rootViewController)
}
return nil
}
class func getVisibleViewControllerFrom(vc:UIViewController?) -> UIViewController? {
if vc == nil {
return nil
}
if let navigationController = vc as? UINavigationController {
return UIWindow.getVisibleViewControllerFrom( navigationController.visibleViewController)
} else if let tabBarController = vc as? UITabBarController {
return UIWindow.getVisibleViewControllerFrom(tabBarController.selectedViewController)
} else {
if let presentedViewController = vc?.presentedViewController {
return UIWindow.getVisibleViewControllerFrom(presentedViewController.presentedViewController)
} else {
return vc
}
}
}
}
I managed to find a way to get this working by using Protocol. First I added a Protocol to my ValveOperationsController and referenced it as below
protocol CustomCellDelegator {
func callSegueFromCell()
}
class ValveOperationsController: UIViewController, CustomCellDelegator {
Then I added a delegate to my cell in cellForRowInIndexPath
cell.delegate = self
Then I added the method called in my Protocol in to my ViewController
func callSegueFromCell() {
performSegueWithIdentifier("ValveOpsToUpdateSegue", sender: nil )
}
Then going to my CustomTableViewCell I added my delegate
var delegate:CustomCellDelegator!
Then in the event called when the Switch changes I added the call to my Protocol
if(self.delegate != nil){ //Just to be safe.
self.delegate.callSegueFromCell()
}
When the event is called when the switch changes it calls the Protocol which passes it to my method and the Segue operates successfully
I want to update the label in my DashboardViewController from my AccountViewController when the back button is pressed in AccountViewController.
I have tried passing back a variable from 2nd view to 1st view and updating the label in viewDidLoad and in viewWillAppear but it never updates the label when the 1st view is back on screen.
I tried creating a function in 1st view to update the label with a string passed into the function and calling that function from 2nd view but it says that the label is nil so it couldn't be updated.
My latest attempt was to create a delegate but that didn't work either.
Here is my delegate attempt.
class DashboardViewController: UIViewController, AccountViewControllerDelegate {
#IBOutlet weak var welcome_lbl: UILabel!
func nameChanged(name: String){
var full_name = "Welcome \(name)"
welcome_lbl.text = "\(full_name)"
}
override func viewDidLoad() {
super.viewDidLoad()
AccountViewController.delegate = self
}
}
And then in my AccountViewController I have this
protocol AccountViewControllerDelegate{
func name_changed(name: String)
}
class AccountViewController: UIViewController, UITextFieldDelegate {
var info_changed = false
static var delegate: AccountViewControllerDelegate!
#IBAction func back_btn(sender: AnyObject) {
if(info_changed){
AccountViewController.delegate.name_changed(name_tf.text!)
}
self.dismissViewControllerAnimated(true, completion: nil)
}
Did I mess up the delegate process somehow ? Or is there an easier way to do this?
First. Your delegate should be a normal property of AccountViewController. There is no need to update your name when user press back. You can change DashboardViewController`s name when user change name in AccountViewController. When user go back to DashboardViewController. It`s already show the changed name.
protocol AccountViewControllerDelegate{
func name_changed(name: String)
}
class AccountViewController: UIViewController, UITextFieldDelegate {
var delegate: AccountViewControllerDelegate?
// when user change name through textfield or other control
func changeName(name: String) {
delegate?.name_changed(name)
}
}
Second. When DashboardViewController show AccountViewController. I think it should be push. Set DashboardViewController instance be AccountViewController instance`s delegate.
class DashboardViewController: UIViewController, AccountViewControllerDelegate {
#IBOutlet weak var welcome_lbl: UILabel!
func nameChanged(name: String){
var full_name = "Welcome \(name)"
welcome_lbl.text = "\(full_name)"
}
// present or push to AccountViewController
func showAccountViewController {
let accountViewController = AccountViewController()
accountViewController.delegate = self
// do push view controller
}
}
I have 2 controllers
and have got 1 global variable, the problem is if I go to controller 2 and click on button northAmericaClick, it will navigate back to control 1, but the value of global variable won't change!
this is my code
controller 1
class OurViewController: UIViewController {
#IBOutlet weak var menuButton: UIBarButtonItem!
#IBOutlet weak var selectedServer: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
selectedServer.setTitle(selected server, forState: UIControlState.Normal) // selected server this is global variable
}
controller 2
class selectServerController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func northAmericaClick(sender: AnyObject) {
selectedserver = "North America"
self.navigationController?.popViewControllerAnimated(true)
}
From
You need to use a delegate. Here is an example how do use a delegate in Swift.
On your first ViewController, set your delegate when you load the second VC:
For example, if you are using the Storyboard Editor:
var secondViewController = (segue.destinationViewController.visibleViewController as MySecondViewControllerClass)
secondViewController.delegate = self
Write a Protocol and define a func to write you values back
For example, create a file called "Protocol.swift" and write something like that:
protocol writeValueBackDelegate {
func writeValueBack(value: String)
}
Add the function to your FirstViewController
func writeValueBack(value: String) {
// this is my value from my second View Controller
}
And to your ViewControllerClass
class ViewController: UIViewController, writeValueBackDelegate
Go to the Second View Controller, and add the delegate here:
class SecondViewController: ViewController {
// delegate for FirstViewController
var delegate: writeValueBackDelegate?
On your Second View Controller, you can now use this to call the func in the first View Controller an pass data.
delegate?.writeValueBack("That is a value")
You also need to indicate that your first view controller implements the protocol: class ViewController: UIViewController, writeValueBackDelegate {
A part of doing it with delegate you also can create singleton class ViewControllersDataModel class and share the variable using it:
import Foundation
class ViewControllersDataModel {
static let sharedInstance = ViewControllersDataModel()
var selectedserver: String = ""
private init() {
}
}
And call it like this:
ViewControllersDataModel.sharedInstance.selectedserver = "Selected Option";
Ok, I can do this with this code, only check when viewWillDisapear and call the parent of this view controller in the navicationController:
override func viewWillDisappear(animated: Bool) {
if ((self.navigationController!.viewControllers.last?.isKindOfClass(ActivityMyViewController)) == true){
let backView:MyViewController = self.navigationController!.viewControllers.last as! MyDetailViewController
backView // do whatever you want
}
}
I hope this code can help you, good luck
thanks guys for helping ;)
it was very simple
i just use then when it comeback ^^"
override func viewWillAppear(animated: Bool) {
selectedServer.setTitle(selectedserv, forState: UIControlState.Normal)
}