How to send data from RightViewController to MainView Controller in Swift 4? - ios

Hello, I am using MMDrawerController for right side menu. I have 2 ViewController First is HomeVC with Product Listing data in UICollectionView and there's 1 filter button.
When i press that filter button I push to filter screen RightViewVC. Now what I want is, I want to pass that selected filter values to HomeVC. How can I do this?

You can do this by multiple way
1. By using Block Method
When you push to RightViewVC write below code.
let nextViewController = self.storyboard?.instantiateViewController(withIdentifier: "RightViewVC") as! RightViewVC
nextViewController.delegate = self as! customeDelegate
nextViewController.onApplyFilterTap = {(_ arrSelectedFilter: NSMutableArray) -> Void in
self.collectionView.reloadData()
}
self.show(nextViewController, sender: self)
Define this in RightViewVC controller. I created array you can change it as per your requirement.
var onApplyFilterTap: ((_ arrSelectedFilter: NSMutableArray) -> Void)? = nil
You need to call like this
self.arrFilterSelection.add(whichButtonClicked)
self.arrFilterSelection.add(locationTextView.text!)
self.arrFilterSelection.add(byPriceToTextField.text!)
self.arrFilterSelection.add(byPriceFromTextField.text!)
self.arrFilterSelection.add(timeTextview.text!)
onApplyFilterTap!(self.arrFilterSelection)
2. By using NotificationCenter
Write below in your HomeVC
NotificationCenter.default.addObserver(self, selector: #selector(refreshProductListBasedonSelectedFilterValue(_:)), name: NSNotification.Name(rawValue: "refreshProductListBasedonSelectedFilterValue"), object: nil)
#objc func refreshProductListBasedonSelectedFilterValue(_ notification: Notification) {
let info = notification.object as? NSDictionary
let arrSelectedFilteredValues = info?.value(forKey: "selectedFilter") as! NSMutableArray
self.collectionView.reloadData()
}
From RightViewVC you need to call like this.
let dict = NSMutableDictionary()
dict.setValue(self.arrFilterSelection, forKey: "selectedFilter")
NotificationCenter.default.post(name: NSNotification.Name("refreshProductListBasedonSelectedFilterValue"), object: dict)
3. You can also use delegate

Related

Swift - observe static member change, without using property observer

I've got next situation: from the viewDidAppear I have to call the specific function ( to change the view controller to another one) when the special condition applied ( static member changes to True, another class access this static property and send True once the certain conditions apply)
If I do it like this with property observer:
//this is property of load view controller
static var isFilled: Bool = false {
didSet{
if isFilled == true {
print("data is Filled")
//load view controller - my present VC, which I want to switch
var loadVC = LoadViewController()
loadVC.changeViewController(vc: loadVC )
This is changeViewController function:
func changeViewController(vc: UIViewController) {
let sb = UIStoryboard(name: "Main", bundle: nil )
//main view controller - controller, which i want to Go to
var mainViewController = sb.instantiateViewController(withIdentifier: "ViewController") as! ViewController
vc.present(mainViewController, animated: true, completion: nil)
}
It'll throw an error attempt to present viewcontroller whose view is not in the window hierarchy
And, it's performed inside LoadViewController class
As I understand, the only way to avoid this error is to call the function from the viewDidApper, which can't be used in this scenario, because i have to call it only when condition applied. Are there any alternatives to perform that w/o using property observer?
I'm sure there are multiple ways to perform this, and there are might be misconceptions from my side. I'm pretty fresh developer, and completely new to Swift. All the suggestions would be much appreciated.
You could use NotifiationCenter by registering a notification and then calling it whenever you want to update something, like "updateConversations" in a chat app. For example in viewDidLoad(), register your notification:
NotificationCenter.default.addObserver(self, selector: #selector(self.updateConversations(notification:)), name: NSNotification.Name(rawValue: "updateConversations"), object: nil)
Add this function into the class:
#objc func updateConversations(notification: NSNotification) {
if let id = notification.userInfo?["id"] as? Int,
let message = notification.userInfo?["message"] as? String {
// do stuff
}
}
Use the notification from anywhere in your app:
let info = ["id" : 1234, "message" : "I almost went outside today."]
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "updateConversationsList"), object: self, userInfo: info)

Swift: Is it possible to use delegation when both views aren't in direct connection with each other?

I have three ViewController: ViewController1, ViewController2 and ViewController3.
ViewController1 pushes ViewController2 on the screen and both views can interact with each other through delegation.
ViewController2 pushes ViewController3 on the screen.
Now is it possible to establish a delegation between ViewController1 and ViewController3? I want ViewController3 to be able to manipulate a label of ViewController1.
Best way in this scenario would be Notification Pattern
// Notification posting
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "fromViewController3"), object: nil, userInfo: myDict)
// Register in ViewController1 to receive notification in your class ViewController1
NotificationCenter.default.addObserver(self, selector: #selector(self.manipulateMyLabel(_:)), name: NSNotification.Name(rawValue: "fromViewController3"), object: nil)
// handle notification
func manipulateMyLabel(_ notification: NSNotification) {
print(notification.userInfo ?? "")
if let dict = notification.userInfo as NSDictionary? {
if let newString = dict["labelValue"] as? String {
// user newString to update the label text or something.
}
}
}

Function returning emptying String when using Notification and observers?

In the following code I want to return a string from first controller to third controller. But it returns an empty string, when trying by notification and observers.
First View Controller
override function ViewDidLoad(){
NotificationCenter.default.addObserver(self, selector:
#selector(token(notification:)), name: .token, object: nil)
}
#objc func token (notification:Notification) -> String!{
return self.token! //return token
}
extension Notification.Name {
static let token = Notification.Name("Token")
}
ThirdViewController*
override function ViewDidLoad(){
let token = NotificationCenter.default.post(name: .token, object: nil)
print(token) // () printing empty
}
If I understood you problem correctly you want to pass object from first controller to third, you can use segue for it. This is the example how you can pass it to second, the same thing to pass forward from second to third
#IBAction func goForawrd(_ sender: UIButton) {
performSegue(withIdentifier: "second", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "second" {
let vc = segue.destination as? SecondViewController
vc?.object = yourObjectYouWantToPass
}
}
or if you don't want to use segue you can use next code
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondVC = storyboard.instantiateViewController(withIdentifier: "second")
secondVC.object = yourObjectYouWantToPass
don't forget to set viewController identifier before
This
NotificationCenter.default.post(name: .token, object: nil) doesn't return anything you get the observer wrongly it posts the notification and if there is an observer it'll be forwarded to it , so this flow occurs
1-
NotificationCenter.default.post(name: .token, object: nil)
2-
NotificationCenter.default.addObserver(self, selector:
#selector(token(notification:)), name: .token, object: nil
3-
#objc func token (notification:Notification) {}
if you need to send data from first to second set it when you segue/present/push , if you need to send data from second to first use a delegate
Okay so you are printing nil since that is not the value of the token just the reference for the post method.
Since it is not a bidirectional thing you cannot retrieve the value there.
If you want to achieve this by notifications these are the required steps:
Send a notification about you need a token
When notification arrived on first controller grab what you need and send another notification with the value you need, and handle it where you need.
First View Controller
override function ViewDidLoad(){
NotificationCenter.default.addObserver(self, selector:
#selector(token(notification:)), name: .tokenGet, object: nil)
}
#objc func token (notification:Notification) {
NotificationCenter.default.post(name: .tokenSet, object: token)
}
extension Notification.Name {
static let tokenGet = Notification.Name("TokenGet")
static let tokenSet = Notification.Name("TokenSet")
}
ThirdViewController*
override function ViewDidLoad(){
NotificationCenter.default.addObserver(self, selector:
#selector(token(notification:)), name: .tokenSet, object: nil)
NotificationCenter.default.post(name: .tokenGet, object: nil)
}
func tokenSet(notification: Notification) {
/// here you can get the value from notification
}
Note that i would NOT do in this way. Pass the token through the view controllers or create a class which is responsible for token handling and pass that around.

Passing data between view controllers inside UITabBarController without segues

I have been looking for answer to this question for a long time. I can use prepareforSegue if I am using segues. As we know, UITabBar doesn't have segues like navigation controller. In one of my view controllers, I am fetching data from firebase, and I have observer set up to listen for any changes. I want to use data fetched from firebase in this controller to access in another view controller. For example, I want to access this entryIDs in another view controller, and also listen to any changes made to this array, so I can reload data in my collectionView. To sum it up, how do I access array from one view controller to another inside UITabBarController, and also listen to any changes made to it?
var entries = [String: DiaryEntry]()
var entryIDs = [String]()
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
override func viewDidLoad() {
super.viewDidLoad()
// Register cell classes
self.collectionView!.register(DiaryCell.self, forCellWithReuseIdentifier: "homeCell")
collectionView?.backgroundColor = UIColor.white
if let userID = FIRAuth.auth()?.currentUser?.uid {
FirebaseService.service.getUserEntriesRef(uid: userID).observe(.value, with: { [weak weakSelf = self] (snapshot) in
weakSelf?.entries = [String: DiaryEntry]()
weakSelf?.entryIDs = [String]()
let enumerator = snapshot.children
while let entry = enumerator.nextObject() as? FIRDataSnapshot {
weakSelf?.entryIDs.append(entry.key)
weakSelf?.entries[entry.key] = DiaryEntry(snapshot: entry)
}
weakSelf?.entryIDs.reverse()
weakSelf?.collectionView?.reloadData()
})
}
// Do any additional setup after loading the view.
}
On sending info
let infoDict = ["info" : self.info]
NotificationCenter.default.post(name: Notification.Name(rawValue: "passInfo"), object: nil, userInfo : infoDict as! [String : AnyObject])
on receiving info
NotificationCenter.default.addObserver(self, selector: #selector(self.doSomething), name: NSNotification.Name(rawValue: "passInfo"), object: nil)
func doSomething(_ notification : Notification) {
let info = notification.userInfo!["info"] as! Int
//Do something
}

How would I have one view controller access a variable from another view controller?

Everything I've seen on stack is passing the data from an input, onto another view controller on a button press. Let's say I have var banana that is an array of dictionaries, but once my function in ViewA.swift is done loading up banana, I want another viewController, say ViewB.swift to manipulate that data as it sees fit. I do NOT have a segue going from one view controller to the other.
EDIT: It's actually two TableViewControllers****
I've looked into NSNotificationCenter, but that doesn't seem to work with my variable type, which is an array of dictionaries
Use NSNotificationCenter for accessing data.
Try Below code
//Sent notification
let dictionary = ["key":"value"]
NSNotificationCenter.defaultCenter().postNotificationName("passData", object: nil, userInfo: dictionary)
//Receive notification
NSNotificationCenter.defaultCenter().addObserver(self,
selector:"myMethod:", name: "passData", object: nil)
//Receive notification method
func myMethod(notification: NSNotification){
print("data: \(notification.userInfo!["key"])")
Without using segue, you can instantiate the View controller, and set the public parameteres.
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("someViewController") as! ViewB
/* Here you have the reference to the view, so you can set the parameters*/
vc.parameterInViewB = banana
/* At this point you can present the view to the user.*/
self.presentViewController(vc, animated: true, completion: nil)
Make sure that you have given all ViewControllers an identifier, then instantiate them with:
guard let viewControllerB = storyboard?.instantiateViewControllerWithIdentifier("ViewControllerB") as? ViewControllerB else {
fatalError(); return
}
// then access the variable/property of ViewControllerB
viewControllerB.banana = whatEver
Added for clarification
This one works for me.
Just make sure that you have given the TableViewController an identifier otherwise you will not be able to instantiate it. Also make sure that you cast the result of instantiateViewControllerWithIdentifier to your TableViewController class otherwise you won't be able to access it's variables (I've seen that you were struggling with this; if you get an error that UIViewController doesn't have a member "myArray" then you probably have forgotten to cast the result)
class TableViewController: UITableViewController {
var myArray = [String]()
}
class ViewController: UIViewController {
func someEventWillTriggerThisFunction() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let tableViewController = storyboard.instantiateViewControllerWithIdentifier("TableViewController") as? TableViewController else {
fatalError(); return
}
tableViewController.myArray = ["Value1", "Value2", "Value3"]
/* if you want to present the ViewController use this: */
self.presentViewController(tableViewController, animated: true, completion: nil)
}
}

Resources