Loading function from another Class crashes in Swift IOS - ios

I want to load the function checkStatus() (which is part of my ViewController1) from my ViewController 2 before the Navigation Controller pops back to the ViewController1.
Unfortunately, when calling the function, the app crashes as soon as it loads and I am really frustrated becaue I do not know what I did wrong.
The ViewControllers are embeded in a Navigation Controller.
Code in ViewController1:
func checkStatus(){
/* setting Label texts (Outlets) to a specific value but it is
irrelevant as the compiler does not even get to
this point. The program crashes as soon as the function is called (tried it with prints).*/
Code in ViewController2:
#IBAction func didTapBack(_ sender: UIButton){
// first the function is animating something inside the VC2
ViewController1().checkStatus() // function gets called here
self.navigationController?.popToRootViewController(animated: false)
}
I am grateful for any kind of help.

You can use Delegate Pattern to call a function in your case.
ViewController code:
import UIKit
class ViewController: UIViewController, SecondViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func gotoSecondVC(_ sender: UIButton) {
let secondVC = self.storyboard?.instantiateViewController(identifier: "SecondViewController") as! SecondViewController
secondVC.delegate = self
self.navigationController?.pushViewController(secondVC, animated: true)
}
func checkStatus() {
print("\(#function) called...")
}
}
SecondViewController code:
import UIKit
protocol SecondViewControllerDelegate: class {
func checkStatus()
}
class SecondViewController: UIViewController {
weak var delegate: SecondViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func backButtonTapped(_ sender: UIButton) {
delegate?.checkStatus()
self.navigationController?.popViewController(animated: true)
}
}

Related

Pass data from second VC to first VC and also show in textfield Swift4

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.

How to call a first view controller function from second view controller on a button tap in swift ?

I have a requirement where I have to call a first view controller function from second view controller on a button tap.
class FirstViewController: UIViewController {
#IBAction func firstButtonPressed(_ sender: Any) {
// Doing ABC
}
#IBAction func showSecondVC_ sender: Any) {
// showingSecondVC
}
}
class secondViewController: UIViewController {
#IBAction func SecondButtonPressed(_ sender: Any)
// Dismiss second vc & call First View controller method so that it does ABC.
}
My first question is can we initiate First VC IBAction directly from second VC ? Is it possible ?
I am thinking to do following
class FirstViewController: UIViewController {
#IBAction func firstButtonPressed(_ sender: Any) {
// call DoABC
}
func DoABC {
// Doing ABC
}
}
class secondViewController: UIViewController {
#IBAction func SecondButtonPressed(_ sender: Any)
// Dismiss second vc
// Call Firstvc.DoABC ?? How to do this ??
}
How to call the first vc method from the second vc ??
You have a few options here:
Split out the logic, call the same code from each view controller
Use a closure callback
Use the delegate pattern as a method of calling back
Option 1 - Split out the logic:
class FirstViewController: UIViewController {
let abcPerformer = ABCPerformer()
#IBAction func firstButtonPressed(_ sender: Any) {
abcPerformer.doABC()
}
#IBAction func showSecondVC_ sender: Any) {
// showingSecondVC
}
}
class SecondViewController: UIViewController {
let abcPerformer = ABCPerformer()
#IBAction func SecondButtonPressed(_ sender: Any) {
// Dismiss second vc & call First View controller method so that it does ABC.
abcPerformer.doABC()
}
}
struct ABCPerformer {
func doABC() {
// do ABC
}
}
Option 2 - Create a callback:
class FirstViewController: UIViewController {
#IBAction func firstButtonPressed(_ sender: Any) {
doABC()
}
#IBAction func showSecondVC_ sender: Any) {
// showingSecondVC
secondVC.doABC = doABC
}
func doABC() {
// do ABC
}
}
class SecondViewController: UIViewController {
var doABC: (() -> Void)?
#IBAction func SecondButtonPressed(_ sender: Any) {
// Dismiss second vc & call First View controller method so that it does ABC.
doABC?()
}
}
Option 3 - Use a delegate:
protocol ABCProtocol {
func doABC()
}
class FirstViewController: UIViewController, ABCProtocol {
#IBAction func firstButtonPressed(_ sender: Any) {
doABC()
}
#IBAction func showSecondVC_ sender: Any) {
// showingSecondVC
secondVC.delegate = self
}
func doABC() {
// do ABC
}
}
class SecondViewController: UIViewController {
weak var delegate: ABCProtocol?
#IBAction func SecondButtonPressed(_ sender: Any) {
// Dismiss second vc & call First View controller method so that it does ABC.
delegate?.doABC()
}
}
There is probably more options too, but these should give you enough choice to make a decision
Create a protocol, say, SecondViewControllerDelegate.
Add a method signature to that protocol, something like secondViewControllerDidPressButton.
Add a var to secondViewController: var delegate: SecondViewControllerDelegate
Update firstViewController to implement that protocol.
In prepareForSegue of firstViewController, assign firstViewController as the delegate for the secondViewController that is about to be presented.
Update secondViewController to call self.delegate.secondViewControllerDidPressButton when the button is pressed.
You can Use custom delegate for that Like below and add function "presentPage" wherever you want to call.
protocol MainDelegate {
func presentPage(page : Int)
}
To present your second view controller from First, You can use push or present transition.
#IBAction func firstButtonPressed(_ sender: Any) {
// call DoABC
//presenting VC
let secondVC = SecondViewController() //change this to your class name
self.presentViewController(secondVC, animated: true, completion: nil)
//for push :
navigationController?.pushViewController(SecondViewController, animated: true)
}
You can use pop/dismiss VC accordingly in your second view to get back first view.
class secondViewController: UIViewController {
#IBAction func SecondButtonPressed(_ sender: Any)
// Dismiss second vc // Call Firstvc.DoABC ?? How to do this ??
//if you used Present in first step then use dismiss
self.dismissViewControllerAnimated(false, completion: nil)
//if you used push in first step then use pop
self.navigationController?.popViewController(animated: true) }

swift delegate function is not called

I know the same question is asked many times. I read most of the answers from stack overflow and tried. But it did not help my problem.
I have two view controllers
protocol UpdateDataDelegate {
func loadData()
}
viewcontroller2 {
var delegate: UpdateDataDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
fun saveData() {
self.delegate?.loadData()
}
}
viewcontroller1 : UpdateDataDelegate {
var vc2 = viewcontroller2()
override func viewDidLoad() {
super.viewDidLoad()
vc2.delegate = self
}
func loadData() {
}
}
But function loadData() from viewcontroller1 is not called.
Since I don't have the complete code before me I can only assume that the delegate is not assumed properly.
If the delegate is not initialised properly it cannot pass value to the other viewController.
You can check delegate is properly initialised by:
if let delegate = delegate{
//Do your works here
}else{
print("The delegate is nil")
}
if the delegate is nil is printed in console, then the problem might be in the way the delegate was initialised
This might be because you are setting the delegate and opening an another instance of the viewController which was not assigned the delegate value.
In the code you provided I see that you are setting the delegate as
var vc2 = viewcontroller2()
vc2.delegate = self
But I cannot see the code that you used to move to the viewController2. Now we have to present this assigned viewController. Instead of using segue to move to the viewcontroller2 present this vc using the code below
present(vc2, animated: true, completion: nil)
You should place this according to your code logic.(where your segue is triggered)
Situation 2:
If you are using segue to move to the viewController2 then the delegate should be assigned in the prepareforSegue method as below
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc2 = segue.destination as? ViewController2{
vc2.delegate = self
}
}
let me know how it goes.
A simple playground for what you are trying to do, even if I have not clear what you are trying to achieve:
import UIKit
protocol UpdateDataDelegate: class {
func loadData()
}
class ViewController2: UIViewController {
weak var delegate: UpdateDataDelegate?
func saveData() {
self.delegate?.loadData()
}
}
class ViewController1: UIViewController {
}
extension ViewController1: UpdateDataDelegate {
func loadData() {
print("loadData called")
}
}
let viewController1 = ViewController1()
let viewController2 = ViewController2()
viewController2.delegate = viewController1
viewController2.saveData()
Few notes:
classes should be upper case. So, ViewController1 instead viewcontroller1
delegates should be weak otherwise you create reference cycles
class should be used for UpdateDataDelegate protocol otherwise compiler will complain since weak cannot be applied to class and class-bound protocol types
prefer extension to conform to protocols. It makes the code easy to read
The only thing I see missing in your code is call to saveData() of ViewController2 that will in turn call loadData() of ViewController1.
So just add:
override func viewDidLoad()
{
super.viewDidLoad()
vc2.delegate = self
vc2.saveData() //Add this line to your code
}
You are good to go now :)
Edit:
protocol UpdateDataDelegate
{
func loadData()
}
class ViewController2: UIViewController
{
var delegate: UpdateDataDelegate?
func saveData()
{
self.delegate?.loadData()
}
}
class ViewController1: UIViewController, UpdateDataDelegate
{
var vc2 = ViewController2()
override func viewDidLoad()
{
super.viewDidLoad()
vc2.delegate = self
vc2.saveData()
}
func loadData()
{
print("Done")
}
}
I have used the above code and it is working fine for me. How are you executing it? I have used storyboard and used ViewController1 as the Initial View Controller.
I assume that you need to load data when your delegate has been set up. In this case you can use magic didSet:
weak var delegate: UpdateDataDelegate? {
didSet {
self.saveData()
}
}
So right after setting the delegate the needed method will be called.

Deallocation of ViewController

i'm very excited about memory leaks and performance problems with iOS. Currently i've learnt that preventing leaks with getting avoid by retain cycles. I have a snippet below which is containts two viewcontrollers and i'm passing data with delegation. But when i equalized delegate var as nil, the deinit of viewcontroller was not called.
import UIKit
class ViewController: UIViewController, Navigator {
func passData(data: String) {
print("Passed data: " + data)
}
override func viewDidLoad() {
super.viewDidLoad()
}
deinit {
print("deinited: " + self.description)
}
#IBAction func goSecond(_ sender: UIButton) {
let secondVC = self.storyboard?.instantiateViewController(withIdentifier: "secondVC") as! SecondVC
secondVC.delegate = self
self.present(secondVC, animated: false, completion: nil)
}
}
//second vc
import UIKit
protocol Navigator: class{
func passData(data:String)
}
class SecondVC: UIViewController {
weak var delegate:Navigator?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func GoFirst(_ sender: UIButton) {
delegate?.passData(data: "I'm second VC and Passing")
self.delegate = nil
}
}
You are misunderstood the deinit method's job. The deinit is supposed to be called when the instance of a view controller has no reference left to it. So, just simply removing the references of the properties of a view controller doesn't do the whole job.
And you have a misconception of making self.delegate = nil in your SecondVC. This should have been done in your first ViewController.
To make sense of everything, I've done a sample project where you can learn how deinits work. The main code goes here:
First View Controller
class FirstViewController: UIViewController, Navigator {
override func viewDidLoad() {
super.viewDidLoad()
}
deinit {
print("First view controller's deinit called")
}
func passData(data: String) {
print("In First view controller: \(data)")
}
#IBAction func gotoSecond(_ sender: UIButton) {
let viewcontroller = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
viewcontroller.delegate = self
show(viewcontroller, sender: self)
}
}
Second View Controller
protocol Navigator {
func passData(data:String)
}
class SecondViewController: UIViewController {
weak var delegate:Navigator?
override func viewDidLoad() {
super.viewDidLoad()
}
deinit {
print("Second view controller's deinit called")
}
#IBAction func closeButton(_ sender: UIButton) {
delegate?.passData(data: "Delegation from second view controller")
dismiss(animated: true, completion: nil) //when this line executes, the instance of this class is de-referenced. This makes the call to deinit method of this class.
}
}
So, when dismiss happens for second view controller, the reference count goes to 0 for second view controller and this does the job for calling deinit method of second view controller.
But you technically don't call the deinit of the first view
controller as you don't actually de-reference the first view
controller.
You can find the whole project here.

Pass data backward from detailViewController to masterViewController

I am trying to pass data back from the second viewController.
I can do that without NavigationController. But now I need to use NavigationController. Then my code does work as before. The data wont pass.
Here is the simple code:
In first viewController
class ViewController: UIViewController, backfromSecond {
#IBOutlet weak var text: UILabel!
var string : String?
override func viewDidLoad() {
super.viewDidLoad()
self.string = "Start here"
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.text.text = self.string
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationViewController = segue.destination as? secondViewController{
destinationViewController.delegate = self
}
}
func back(text: String) {
self.string = text
print(text)
}
}
And Second viewController:
protocol backfromSecond {
func back(text: String)
}
class secondViewController: UIViewController {
var string : String = "nothing here"
var delegate : backfromSecond?
override func viewDidLoad() {
super.viewDidLoad()
delegate?.back(text: string)
// Do any additional setup after loading the view.
}
}
What is wrong here?
Suppose A & B are two controllers and you first navigated from A to B with some data. And now you want to POP from B to A with some data.
Unwind Segues is the best and recommended way to do this.
Here are the steps.
Open A.m
define following method
#IBAction func unwindSegueFromBtoA(segue: UIStoryNoardSegue) {
}
open storyboard
Select B ViewController and click on ViewController outlet. press control key and drag to 'Exit' outlet and leave mouse here. In below image, selected icon is ViewController outlet and the last one with Exit sign is Exit Outlet.
You will see 'unwindSegueFromBtoA' method in a popup . Select this method .
Now you will see a segue in your view controler hierarchy in left side. You will see your created segue near StoryBoard Entry Piont in following Image.
Select this and set an identifier to it. (suggest to set the same name as method - unwindSegueFromBtoA)
Open B.m . Now, wherever you want to pop to A. use
self.performSegueWithIdentifier("unwindSegueFromBtoA", sender: dataToSend)
Now when you will pop to 'A', 'unwindSegueFromBtoA' method will be called. In unwindSegueFromBtoA of 'A' you can access any object of 'B'.
That's it..!
I think your problem is in the prepare for segue method. If the view controller is on a navigation stack i think your code should be something like
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationViewController = segue.destination as? UINavigationController).topViewController as! secondViewController{
destinationViewController.delegate = self
}
}
You can use unwind segues to pass data back.
Here's a tutorial
https://spin.atomicobject.com/2014/10/25/ios-unwind-segues/
This works me well.
1st VC
class ViewController: UIViewController, backfromSecond {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func Passingfrom1stVCTo2ndVC(_ sender: AnyObject) {
if let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController3") as? ViewController3{
vc.dataFrom1StVC = "message send from 1st VC"
vc.delegate = self
self.navigationController?.pushViewController(vc, animated: true)
}
}
func back(text: String) {
print("data\(text)")
}
}
2nd VC.
protocol backfromSecond: class {
func back(text: String)
}
class ViewController3: UIViewController {
var dataFrom1StVC : String? = nil
week var delegate : backfromSecond?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func DataSendFrom2ndVCTo1stVC(_ sender: AnyObject) {
self.delegate?.back(text: "Message Send From 2nd vc to 1st VC")
self.navigationController?.popViewController(animated: true)
}
}
I hope it will work you. If any problem then ask me i will help you.

Resources