Swift Call function from another ViewController [duplicate] - swift

I have two ViewControllers, FirstViewController and SecondViewController. Both have an own Swift file, FirstViewController.swift and SecondViewController.swift.
FirstViewController.swift contains:
class FirstViewController: UIViewController {
#IBAction func callFunctionInOtherClass(sender: AnyObject) {
// Call "func showAlert" in SecondViewController when clicking the UIButton in FirstViewController
}
}
SecondViewController.swift contains:
class SecondViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
func showAlert(sender: AnyObject) {
let alert = UIAlertView(title: "Working!", message: "This function was called from FirstViewController!\nTextField says: \(textField.text!)", delegate: nil, cancelButtonTitle: "Okay")
alert.show()
}
}
I want to be able to call the func showAlert() in SecondViewController when taping on the UIButton in FirstViewController.
I've already spent many nights to find a solution but none worked. Does anybody know what to do to reach this goal?
I uploaded a sample Xcode project here: CallFuntionInOtherClassX | filedropper.com
P.S.: Of course, I could post some code and explain what error I get, but I think it's not reasonable because I really don't know how to do that.

You may use NSNotificationCentre to accomplish this task.
In viewDidLoad method of your SecondViewController class register self as observer to receive notification broadcasts:-
override func viewDidLoad() {
NotificationCenter.default.addObserver(self, selector: #selector(showAlert), name: NSNotification.Name(rawValue: "callForAlert"), object: nil)
}
and in FirstViewController's button action method you should fire the notification by writing :-
#IBAction func callFunctionInOtherClass(sender: AnyObject) {
//Call "func showAlert" in SecondViewController when clicking the UIButton in FirstViewController
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "callForAlert"), object: nil)
}
Don't forget to call removeObserver in SecondViewController's viewDidUnload method.

EDIT: These functions have been revised in swift 3 as follows:
Code in FirstViewController
override function viewDidLoad(){
NotificationCenter.default.addObserver(self, selector: #selector(showAlert), name: NSNotification.Name(rawValue: "showAlert"), object: nil)
}
Code in SecondViewController:
#IBAction func callFunctionInOtherClass(sender: AnyObject) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "showAlert"), object: nil)
}

If you want to show alert view to second viewcontroller from firstview controller when go to it you can do something like,
self.performSegueWithIdentifier("your segue identifier", sender: self) // or whatever way if you are not using storyboard or segue
let alert = UIAlertView(title: "Working!", message: "This function was called from FirstViewController!", delegate: nil, cancelButtonTitle: "Okay")
alert.show()
If you want to set any variables of secondviewcontroller then you need to implement prepareForSegue method. (if you are using segue).
Second thing you can show alertview in viewDidload of secondViewController also.

Try this:
SecondViewController().showAlert(self)
In your second view controller
if let text = textField?.text {
dispatch_async(dispatch_get_main_queue(),{
let alert = UIAlertView(title: "Working!", message: "This function was called from FirstViewController!\nTextField says: \(text)", delegate: nil, cancelButtonTitle: "Okay")
alert.show()
})
}

Related

NotificationCenter - addObserver not called

I am trying a very simple code with NotificationCenter. But the addObserver is not getting called. Can any one of you check and let me know what i am missing. There are 2 simple class, one which post notification and another which listens to it. When i run the program, i just see "sending notification" in the console.
Thanks in advance.
Class 1:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("sending notification")
NotificationCenter.default.post(name: Notification.Name("test"), object: nil)
}
}
Class 2:
class secondvc: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("second vc")
NotificationCenter.default.addObserver(self,
selector: #selector(doThisWhenNotify(_:)),
name: Notification.Name("test"),
object: nil)
}
#objc func doThisWhenNotify(_ notification: Notification) {
print("inside notification")
}
}
If, at the time ViewController comes into existence, secondvc does not yet exist, then there is no one there to receive the posted notification and that is why you don't see the notification being received later when secondvc does come into existence.

What is the best way of updating a variable in a view controller from scene delegate?

I am using Spotify SDK. I want to change labels in some view controllers when a user changes his/her player state. Here is my scene delegate:
var playerViewController = MatchViewController()
func playerStateDidChange(_ playerState: SPTAppRemotePlayerState) {
playerViewController.stateChanged(playerState)
}
A view controller:
func stateChanged(_ playerState: SPTAppRemotePlayerState) {
// aLabel.text = playerState.track.name
}
The problem is labels or other outlets are nil when the state is changed because the view controllers are not loaded at that time. How can I fix that? (I tried isViewLoaded)
If you have a more than a few places to update according to a change that occurs at one place use observers. Here's how,
Post notification in SceneDelegate like this:
func playerStateDidChange(_ playerState: SPTAppRemotePlayerState) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "stateChanged"), object: nil, userInfo: ["playerState": playerState])
}
Observe in ViewControllers like this:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(stateChanged), name: NSNotification.Name("stateChanged"), object: nil)
}
#objc func stateChanged(_ notification: Notification) {
if let playerState = notification.userInfo?["playerState"] as? SPTAppRemotePlayerState {
print(playerState)
}
}
}

Call other viewcontroller withount making new instance

I have a mainTabBarController and inside it a mainViewController
I have the mainTabBarController's instance in the mainViewController
The problem is that when I add a notification call for the hello() function in the mainTabBarC, then it gets called twice
mainTabBarController:
class MainTabBarController : UITabBarController {
// Main Code
override func viewDidLoad() {
print("viewDidLoad")
NotificationCenter.default.addObserver(self, selector: #selector(hello), name: "sayHello", object: nil)
}
#objc func hello(){
print("Hello")
}
}
mainViewController:
class MainViewController: UITableViewController {
// Classes
let mainTabBarController = MainTabBarController()
}
And in AppDelegate I wanna call the hello function whenever app becomes active
func applicationDidBecomeActive(_ application: UIApplication) {
NotificationCenter.default.post(name: "sayHello", object: nil)
}
Now the problem is, that I have the mainTabBarC, and inside it I have the mainViewController which contains the mainTabBarC too..
And the hello() function will be called 2x times
How can I call a MainTabBarController function from MainViewController without creating a whole new instance?
Your MainViewController already holds a reference to MainTabBarController:
You could use it in two different ways:
// Option 1
if let tabBarController = tabBarController {
// do something with your tabBarController
}
// Option 2
guard let tabBarController = tabBarController else { return }
// do something with your tabBarController
I'm not sure if I'm doing it right, but the correct code in MainViewController might be:
var mainTabBarController: MainTabBarController!
override func viewDidLoad() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
mainTabBarController = storyboard.instantiateViewController(withIdentifier: "MainTabBarController") as! MainTabBarController
}

Swift - Error passing data between protocols / delegates (found nil)

I am developing an application with swift 3.0. Where what I want to do is, from the "MainMapVC" class, which is the view where you have a map with a date slider (see the attached image). I want to move the slider and send that slider position (1,2 or 3) to LeftSideViewController which is the side view (the legend) updating the content depending on the selected date.
View of MainMapVC:
View of MainMapVC with Legend:
Well, and I've come to the point where I have to pass a value between the two view controllers. But problem is that I get the error "fatal error: unexpectedly found nil while unwrapping an optional value". Basically I have a "nil" delegate.
But do not find where the error is, because the definition of the delegate is like "var delegate: MainMapVCDelegate!" And I call it "delegate.moveSliderDates (datePos: Int (roundedValue))" in the "MainMapVC" class.
Does anyone know where I failed in the statement of the delegate?Thanks :)
I attach the code of the two classes so that you see the whole code.
Class MainMapVC (first way):
import UIKit
protocol MainMapVCDelegate: class {
func moveSliderDates(datePos: Int)
}
class MainMapVC: UIViewController, UISearchBarDelegate, CLLocationManagerDelegate, GMSMapViewDelegate {
//MARK: VARIABLES
weak var delegate: MainMapVCDelegate? = nil
let step: Float = 1
#IBAction func moveSliderDates(_ sender: UISlider) {
let roundedValue = round(sender.value / step) * step
sender.value = roundedValue
delegate?.moveSliderDates(datePos: Int(roundedValue))
}
}
The delegate value inside the moveSliderDates function is "nil":
delegate?.moveSliderDates(datePos: Int(roundedValue))
Class LeftSideViewController (first way):
import UIKit
class LeftSideViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, customCellDelegate, MainMapVCDelegate {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MainMapVC" {
let secondViewController = segue.destination as! MainMapVC
secondViewController.delegate = self
}
}
func moveSliderDates(datePos: Int){
print(datePos)
print("/////////////")
tableSideLeft.reloadData()
}
not enter inside this function because the delegate of "MainVC" is "nil":
Class MainMapVC (second way):
let step: Float = 1
#IBAction func moveSliderDates(_ sender: UISlider) {
let roundedValue = round(sender.value / step) * step
sender.value = roundedValue
let data:[String: Int] = ["data": Int(roundedValue)]
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: data)
}
Class LeftSideViewController (second way):
func listnerFunction(_ notification: NSNotification) {
if let data = notification.userInfo?["data"] as? String {
print(data)
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(listnerFunction(_:)), name: NSNotification.Name(rawValue: "notificationName"), object: nil)
}
Never goes into the function listnerFunction
You get the error because you defined your delegate as force unwrapped noy-nil version by this code var delegate: LeftSideDelegate!
Instead, you need to change it like this. You should not create strong reference cycle for delegate.
weak var delegate: LeftSideDelegate? = nil
Then for all your delegate calles, do the wrapped version delegate call
delegate?.changeZindexDelivery()
Other than that, change your line protocol LeftSideDelegate { into protocol LeftSideDelegate : class {
Passing data between view controllers using delegate
First, in the class where you want to pass the data to another view controller, declare protocol in this way
protocol SampleDelegate: class {
func delegateFunctionCall(data: String)
}
Then, create delegate variable as optional with type weak var. Call delegate method with you want to pass data or trigger action
class SecondViewController: UIViewController {
weak var delegate: SampleDelegate? = nil
#IBAction func sendTextBackButton(sender: AnyObject) {
delegate?.delegateFunctionCall(data: textField.text!)
}
}
Finally in your view controller that you want to receive action or data, implement the protocol. When you are initiating the second view controller, set it's delegate variable to be the current view controller
class FirstViewController: UIViewController, SampleDelegate {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSecondViewController" {
let secondViewController = segue.destination as! SecondViewController
secondViewController.delegate = self
}
}
func delegateFunctionCall(data: String) {
label.text = data
}
}
Passing data between view controllers using notification
In the destination view controller, register a handler function that is ready to be called. You can add this registration code in view did load
NotificationCenter.default.addObserver(self, selector: #selector(listnerFunction(_:)), name: NSNotification.Name(rawValue: "notificationName"), object: nil)
func listnerFunction(_ notification: NSNotification) {
if let data = notification.userInfo?["data"] as? String {
// do something with your data
}
}
Then in another view controller, if you want to pass data, simply call this
let data:[String: String] = ["data": "YourData"]
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: data)

How to bring value from a UIviewController using delegate without instantiating object

I have 3 UIViewControllers say:
v1ViewController
v2ViewController
v3ViewController.
I have pushed controllers as v2 on v1 and v3 on v2.
Now I want to bring some value back to v1ViewController from v3ViewController using delegates.
On v3Viewcontroller I wrote it:
for vc in self.navigationController!.viewControllers{
if vc is v1ViewController{
delegate?.returnFilteredImage(imageView.image!)
self.navigationController?.popToViewController(vc, animated: true)
}
}
How can I use delegates because in v1ViewController I haven't create object of v3Viewcontroller; consequently I cannot connect delegate to self.
So how can i do that.
1. Using delgates
create a protocol and implement in your firstviewcontroller
protocol My {
func returnFilteredImage(image: UIImage)
}
class FirstViewController: UIViewcontroller, My {
...
func returnFilteredImage(image: UIImage) {
}
}
and in your thirdViewController create a property and assign FirstViewController delegate to this.
class ThirdViewController: UIViewController {
var delegate: My?
...
override func viewDidLoad() {
super.viewDidLoad()
for vc in self.navigationController!.viewControllers{
if vc is FirstViewController {
let vc1 = vc as! FirstViewController
self.delegate = vc1
self.delegate?.returnFilteredImage(imageView.image!)
self.navigationController?.popToViewController(vc, animated: true)
}
}
}
}
2. Using local notifications
check here
You can do this using local notifications
class FirstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//add observer
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.didgetImage(_:)), name: "receiveImageNotification", object: nil)
}
func didgetImage(notification: NSNotification) {
if let image = notification.userInfo?["image"] as? UIImage {
// do something with your image
}
}
}
and from third view controller, first notification
let imageDataDict:[String: UIImage] = ["image": image]
NSNotificationCenter.defaultCenter().postNotificationName("receiveImageNotification", object: self, userInfo: imageDataDict)
Hope this helps :)