I have 2 UIViewControllers, ViewController, SecondViewController. I defined delegate function in VC, and using in Second VC. But delegate functions not calling in Second VC.
This is mu first VC code
import UIKit
//Step1:
protocol testDelegate {
func testFunction(string1: String, string2:String)
func math(a:Int, b:Int)
}
class ViewController: UIViewController {
//Step2:
var delegateVariable: testDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func moveToSecondVC(_ sender: Any) {
let nav = self.storyboard?.instantiateViewController(withIdentifier: "SVC") as! SecondViewController
//Step3:
delegateVariable?.testFunction(string1: "String1", string2: "String2")
delegateVariable?.math(a:30, b:10)
self.navigationController?.pushViewController(nav, animated: true)
}
}
My second VC code
import UIKit
//Step4:
class SecondViewController: UIViewController , testDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//Step5:
let svc = ViewController()
svc.delegateVariable = self
}
#IBAction func btn(_ sender: Any) {
//Step5:
let svc = ViewController()
svc.delegateVariable = self
}
//Step6:
func testFunction(string1: String, string2: String) {
print(string1+string2)
}
func math(a:Int, b:Int) {
print(a+b)
print(a-b)
print(a*b)
}
}
Here i'm just passing small amount of data for practice, but can any one please suggest some high level delegate example tutorial links for me.
This is why nothing is happening...
let svc = ViewController()
svc.delegateVariable = self
You are creating a NEW ViewController, not using the one that is actually in use.
It does not look like you are using the delegate pattern properly. Your ViewController should not be calling code on other view controllers.
SecondViewController should "do stuff" and then let ViewController know what it has done.
For the Math function you could just use a new class (not a view controller) and create and use this as needed. You do not need a ViewController for this.
An example of using a delegate might be something like:
protocol CreateProfileDelegate: class {
func didCreateProfile(profile: Profile?)
func didCancelCreateProfile()
}
class ViewController: UIViewController {
func showCreateProfile() {
let vc = CreateProfileViewController()
vc.delegate = self
present(vc, animated: true)
}
}
extension ViewController: CreateProfileDelegate {
func didCreateProfile(profile: Profile?) {
// show the profile?
}
func didCancelCreateProfile() {
// show an alert maybe?
}
}
This way the SecondViewController (CreateProfileViewController) basically tells the first that something has happened so that it can react to it.
in SecondViewController you are setting....
let svc = ViewController()
svc.delegateVariable = self
That just create an object of ViewController() class and then you set the delegate. So when the obj. of the scope is finished then the memory of the object will be increased automatically.
The flow should like below....
Create an object of the Viewcontroller in SecondViewController and set the delegate
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
vc.delegateVariable = self
Then push the view controller in to the navigation stack.
self.navigationController?.pushViewController(svc, animated: true)
Implement the delegate method of testDelegate in SecondViewController
func testFunction(string1: String, string2: String) {
print(string1+string2)
}
func math(a:Int, b:Int) {
}
EDIT
The final code of the SecondViewController Will be...
import UIKit
class SecondViewController: UIViewController , testDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btn(_ sender: Any) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
vc.delegateVariable = self
self.navigationController?.pushViewController(svc, animated: true)
}
//MARK:- TestDelegate Methods
func testFunction(string1: String, string2: String) {
print(string1+string2)
}
func math(a:Int, b:Int) {
print(a+b)
print(a-b)
print(a*b)
}
}
Related
Refernce image Im new to protocols, I tried the following implementation to achieve the protocols, but im getting nil value for the protocol object
import UIKit
//MARK: step 1 Add Protocol here.
protocol MyDelegate: class {
func changeBackgroundColor(_ color: UIColor?)
}
class ViewController: UIViewController {
//MARK: step 2 Create a delegate property here.
weak var delegate: MyDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//MARK: step 3 Add the delegate method call here.
delegate?.changeBackgroundColor(UIColor.red)
}
}
Here I am getting delegate value nil and protocol not getting called.
here is the implementation
import UIKit
class HomeViewController: UIViewController, MyDelegate {
func changeBackgroundColor1(_ color: UIColor?) {
self.view.backgroundColor = color
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
callVC()
}
func callVC() {
let vc = ViewController()
vc.delegate = self
}
func changeBackgroundColor(_ color: UIColor?) {
self.view.backgroundColor = color
}
In short:
You have forgotten to set the delegate to the object that conforms the protocol.
Detailed Answer:
Protocol is an agreement between two objects so that you know a delegate will have some specified functions ready to be called. Let's explain with an example:
Suppose that you have two view controllers called A and B. You have defined your protocol in global (as you did in your code). Then you create a property called delegate in A which will hold a weak reference of any object conforms the protocol. In this case, this held object reference is B.
So you need to have a property in A like below:
weak var delegate: MyDelegate?
Then determine this delegate to be the reference of what you need. In our example, it's B. So you need to set it in B as below.
// Somewhere you have the reference of the object or where you initialize it.
instanceOfA.delegate = self
Finally you conform the protocol in B like:
extension B: MyProtocol {
func changeBackgroundColor(_ color: UIColor?) {
// some implementation goes here
}
}
There you are. Now, you can make sure that you have delegate object and protocol methods are getting called if you have completed steps above correctly.
The problem lies in your callVC method:
func callVC()
{
let vc = ViewController()
vc.delegate = self
}
You create an instance of your ViewController, but you are not doing anything with it. No methods, aside from initializer, will be called on that view controller because it's not a part of active navigation stack. Also, since it's just a local variable (not retained anywhere) it will be deallocated immediately after leaving the method scope.
What you need to do is to present the view controller somehow - either with a navigation controller, or as a child to the current view controller
func callVC()
{
let vc = ViewController()
vc.delegate = self
addChildViewController(vc)
view.addSubview(vc.view)
vc.didMove(toParentViewController: self)
}
or if you're using navigation controller
func callVC()
{
let vc = ViewController()
vc.delegate = self
navigationController?.pushViewController(vc, animated: true)
}
Hi Smart i think the problem is when you set the delegate
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
callVC()
}
func callVC() {
let vc = ViewController()
vc.delegate = self
}
Because you set the delegate for the class ViewController, but you don't present the viewController at all; you should set the delegate before presenting the ViewController, one way to do it is to present the ViewController and in function callVc add this
func callVC() {
let vc = ViewController()
vc.delegate = self
//Present the viewController with this
present(vc, animated: true, completion: nil)
}
Your optional delegate variable is nil. You need to set it first from the UIViewController you are segueing from. For example.
class OtherViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.performSegue(withIdentifier: "addHereTheSegueIdFromStoryBoard", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addHereTheSegueIdFromStoryBoard" {
guard let vc = segue.destination as? ViewController else {return}
vc.delegate = self
}
}
}
import UIKit
class ViewController: UIViewController {
weak var delegate: MyDelegate?
override func viewDidLoad() {
super.viewDidLoad()
if delegate != nil {
delegate?.changeBackgroundColor(UIColor.red)
}
}
}
Call like
class SecondViewController: UIViewController(), MyDelegate {
func callVC() {
let vc = ViewController()
vc.delegate = self
}
func changeBackgroundColor(_ color: UIColor?) {
//Code here
}
}
I'm getting data from second VC to first VC using protocol or delegates, Data is receiving in first VC but the problem is that Data is not showing in Textfield. Here is my Complete Code for understanding. Any Effort is appreciated.
FirstVC class
import UIKit
class firstViewController: UIViewController, UITextFieldDelegate, MyProtocol {
var valueSentFromSecondViewController : String?
#IBOutlet weak var myTextField : UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func myTextFieldACTIONWhenEditingDidBegin(_ sender: Any) {
myTextField.isUserInteractionEnabled = false
let secondVC = self.storyboard?.instantiateViewController(withIdentifier: "secondViewController") as! secondViewController
secondVC.delegate = self
self.navigationController?.pushViewController(secondVC, animated: true)
}
func setResultsAfterEvaluation(valueSent: String) {
self.valueSentFromSecondViewController = valueSent
print(valueSentFromSecondViewController!) // Ahtazaz(DATA showing here)
myTextField.text = valueSentFromSecondViewController //This's the problem, Why not showing here in this this TextField
}
}
Now, SecondVC Class
import UIKit
protocol MyProtocol {
func setResultsAfterEvaluation(valueSent: String)
}
class secondViewController: UIViewController {
var delegate : MyProtocol?
var sentValue : String?
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func btn(_ sender: Any) {
let firstVC = self.storyboard?.instantiateViewController(withIdentifier: "firstViewController") as! firstViewController
self.navigationController?.pushViewController(firstVC, animated: true)
sentValue = "Ahtazaz"
delegate?.setResultsAfterEvaluation(valueSent: sentValue!)
}
}
You are pushing SecondVC. Then in SecondVC you are pushing again FirstVC.
I think this is where you are making mistake.
let firstVC = self.storyboard?.instantiateViewController(withIdentifier: "firstViewController") as! firstViewController
You are creating a new instance of FirstVC. Then you push it which is wrong. Call your delegate and then Pop back to previous(FirstVC) controller
Try this code in your button action
#IBAction func btn(_ sender: Any) {
sentValue = "Ahtazaz"
delegate?.setResultsAfterEvaluation(valueSent: sentValue!)
self.navigationController?.popViewController(animated: true)
}
This should be the correct approach rather than pushing the controller again.
The steps are Simple to use Delegates for passing the data to previous VC
Second VC:
At the top of VC declare the protocol as follows:
protocol MenuListingDelegate {
func callBackOfMenuSelected(arrSelectedCategory:[Int],isFromWhichPopup:Int)
}
Then inside that define the variable like this
var delegate:MenuListingDelegate?
And then provide the data to the delegate like this. In my case i provide that on click of button before pop View Controller
self.delegate?.callBackOfMenuSelected(strToPass: "Hello")
Now in First VC:
At the top define the Delegate method like this:
class DayDetailVC: UIViewController,MenuListingDelegate {}
And fetch the Data like this
//MARK:- Menu Listing Delegate
func callBackOfMenuSelected(strToPass: String) {
print(strToPass)
}
Note:- Do not forget to declare the delegate of the secondVC where we use this. secondVC.delegate = self.
Edit Check the following cases
Case 1:- Check the outlets of the myTextField i guess the issue is there. If everything is correct remove the Outlet and the set that again
Case 2:- Still if doesnt work then try setting like this
func setResultsAfterEvaluation(valueSent: String) {
myTextField.text = "\(valueSent)"
}
Hope this helps.
Edit 2
I have seen you have used pushViewController in the following lines:
So you can simply use the following line of code to pass the data to firstVC
In SecondVC add following Code:
let firstVC = self.storyboard?.instantiateViewController(withIdentifier: "firstViewController") as! firstViewController
firstVC.valueSentFromSecondViewController = "Hello World"
self.navigationController?.pushViewController(firstVC, animated: true)
Now in FirstVC
Use like in viewDidLoad() or anywhere you want
print(valueSentFromSecondViewController) //Hello World
Cheers it Done.
Choose the way you want.
Note:- But i will suggest you to use popViewController instead of
pushViewController when returning back from SecondVC -> FirstVC. Rest depends upon your requirements.
Hope this helps.
I have a ViewController which have a button, it calls some method in MyClass.swift. I'm trying to navigate from MyClass.swift to UIViewController MainViewController when I press that button. I implemented this method and I get "navigating..." in the log but nothing happen.
class Myclass {
// some code ...
func someFunc() {
// some code ...
navigateToMainViewController()
}
func navigateToMainViewController() {
let storyboard: UIStoryboard? = UIApplication.shared.keyWindow?.rootViewController?.storyboard
if let myMVC = storyboard?.instantiateViewController(withIdentifier: "MainViewController") {
print("navigating....")
let navController = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController
navController?.pushViewController(myMVC, animated: true)
}else {
print("Something wrong..")
}
}
// some code ...
}
Thank your for your help.
Change your navigation Function and add a parameter of viewController to it, and take a variable in myClass of type UIViewController:
var viewController: UIViewController!
func someFunc(_ viewController: UIViewController) {
self.viewController = viewController
self.navigateToMainViewController()
}
func navigateToMainViewController() {
let storyboard: UIStoryboard? = UIApplication.shared.keyWindow?.rootViewController?.storyboard
if let myMVC = storyboard?.instantiateViewController(withIdentifier: "MainViewController") {
print("navigating....")
self.viewController.navigationController?.pushViewController(myMVC, animated: true)
}else {
print("Something wrong..")
}
}
Then From viewController, Call someFunc() with instance of ViewController:
class ViewController: UIViewController {
#IBAction func buttonPressed(_ sender: UIButton) {
myClassObject.someFunc(self)
}
}
I have two UIViewController, when I click a button, it goes from the first view controller to the second one. And before that, I animated a UIView to move to another place. After dismissing the second View Controller, I want to move the UIView in the first view controller back to where it originally was. However, when I call a function from the second View Controller to animate the UIview in the first view controller after dismissing the second one, It could not get the UIView's properties, and cannot do anything with it. I think because the first UIViewController is not loaded yet. Is that the problem? And How should I solve this?
There are two solutions you can either use swift closures
class FirstViewController: UIViewController {
#IBAction func start(_ sender: Any) {
guard let secondController = self.storyboard?.instantiateViewController(withIdentifier: "SecondController") as? SecondController else { return }
secondController.callbackClosure = { [weak self] in
print("Do your stuff")
}
self.navigationController?.pushViewController(secondController, animated: true)
}
}
//----------------------------
class SecondController: UIViewController {
var callbackClosure: ((Void) -> Void)?
override func viewWillDisappear(_ animated: Bool) {
callbackClosure?()
}
}
or you can use protocols
class FirstViewController: UIViewController {
#IBAction func start(_ sender: Any) {
guard let secondController = self.storyboard?.instantiateViewController(withIdentifier: "SecondController") as? SecondController else { return }
secondController.delegate = self
self.navigationController?.pushViewController(secondController, animated: true)
}
}
extension ViewController : ViewControllerSecDelegate {
func didBackButtonPressed(){
print("Do your stuff")
}
}
//--------------------------
protocol SecondControllerDelegate : NSObjectProtocol {
func didBackButtonPressed()
}
class SecondController: UIViewController {
weak var delegate: SecondControllerDelegate?
override func viewWillDisappear(_ animated: Bool) {
delegate?.didBackButtonPressed()
}
}
You can try to use a closure. Something like this:
class FirstViewController: UIViewController {
#IBOutlet weak var nextControllerButton: UIButton!
private let animatableView: UIView = UIView()
private func methodsForSomeAnimation() {
/*
perform some animation with 'animatableView'
*/
}
#IBAction func nextControllerButtonAction() {
// you can choose any other way to initialize controller :)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let secondController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController else { return }
secondController.callbackClosure = { [weak self] in
self?.methodsForSomeAnimation()
}
present(secondController, animated: true, completion: nil)
}
}
class SecondViewController: UIViewController {
#IBOutlet weak var dismissButton: UIButton!
var callbackClosure: ((Void) -> Void)?
#IBAction func dismissButtonAction() {
callbackClosure?()
dismiss(animated: true, completion: nil)
/*
or you call 'callbackClosure' in dismiss completion
dismiss(animated: true) { [weak self] in
self?.callbackClosure?()
}
*/
}
}
When you present your second view controller you can pass an instance of the first view controller.
The second VC could hold an instance of the first VC like such:
weak var firstViewController: NameOfController?
then when your presenting the second VC make sure you set the value so it's not nil like so:
firstViewController = self
After you've done this you'll be able to access that viewControllers functions.
iOS 11.x Swift 4.0
In calling VC you put this code ...
private struct Constants {
static let ScannerViewController = "Scan VC"
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == Constants.ScannerViewController {
let svc = destination as? ScannerViewController
svc?.firstViewController = self
}
}
Where you have named the segue in my case "Scan VC", this is what it looks like in Xcode panel.
Now in scan VC we got this just under the class declaration
weak var firstViewController: HiddingViewController?
Now later in your code, when your ready to return I simply set my concerned variables in my firstViewController like this ...
self.firstViewController?.globalUUID = code
Which I have setup in the HiddingViewController like this ...
var globalUUID: String? {
didSet {
startScanning()
}
}
So basically when I close the scanning VC I set the variable globalUUID which in term starts the scanning method here.
When you are saying it could not get the UIView's properties it's because you put it as private ? Why you don't replace your UIView in the first controller when it disappears before to go to your secondViewController. I think it's a case where you have to clean up your view controller state before to go further to your second view controller.
Check IOS lifecycle methods : viewWillDisappear or viewDidDisappear through Apple documentation and just do your animation in one of these methods.
Very simple solution actually... Just put your animation in the viewDidAppear method. This method is called every time the view loads.
class firstViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// insert animation here to run when FirstViewController appears...
}
}
I need to send some data back from secondView to First View by popView.
How can i send back the data by popViewControllerAnimated?
Thanks!
You can pass data back using delegate
Create protocol in ChildViewController
Create delegate variable in ChildViewController
Extend ChildViewController protocol in MainViewController
Give reference to ChildViewController of MainViewController when navigate
Define delegate Method in MainViewController
Then you can call delegate method from ChildViewController
Example
In ChildViewController: Write code below...
protocol ChildViewControllerDelegate
{
func childViewControllerResponse(parameter)
}
class ChildViewController:UIViewController
{
var delegate: ChildViewControllerDelegate?
....
}
In MainViewController
// extend `delegate`
class MainViewController:UIViewController,ChildViewControllerDelegate
{
// Define Delegate Method
func childViewControllerResponse(parameter)
{
.... // self.parameter = parameter
}
}
There are two options:
A) with Segue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
let goNext = segue.destinationViewController as ChildViewController
goNext.delegate = self
}
B) without Segue
let goNext = storyboard?.instantiateViewControllerWithIdentifier("childView") as ChildViewController
goNext.delegate = self
self.navigationController?.pushViewController(goNext, animated: true)
Method Call
self.delegate?.childViewControllerResponse(parameter)
If you want to send data by popping, you'd do something like:
func goToFirstViewController() {
let a = self.navigationController.viewControllers[0] as A
a.data = "data"
self.navigationController.popToRootViewControllerAnimated(true)
}
Extending Dheeraj's answer in case your ViewController is not first VC in the stack, here is the solution:
func popViewController() {
guard let myVC = self.navigationController?.viewControllers.first({ $0 is MyViewController }) else { return }
myVC.data = "data"
self.navigationController?.popViewController(animated: true)
}
However, this solution will break if you have 2 or more than 2 MyViewController in the stack. So, use wisely.
Answer given here is a little complex, quite simple to just use UINavigationControllerDelegate
class FirstNavigationController: UIViewController {
var value: String?
}
class SecondNavigationController: UIViewController, UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
guard let vc = navigationController.topViewController as? FirstNavigationController else { return }
vc.value = "Hello again"
}
}
self.navigationController?.popViewController(animated: true)
let vc = self.navigationController?.viewControllers.last as! MainViewController
vc.textfield.text = "test"
this popviewcontroller solutions