I have two controller, one controller is controllerOne.swift, in this I receive notifications and I need when one notification arrive, show a button on controllerTwo.swift.
My code is:
ControllerOne.swift
public func websocket(token: Any){
self.ws.open("ws://"+String(APIHOST)+":"+String(port)+"/ws?token="+String(describing: token))
self.ws.event.message = { message in
let res = self.convertToDictionary(text: message as! String)
if ((res!["notification"]) != nil) {
self.count_total_notifications_ws = self.count_total_notifications_ws! + 1
let presentView = UIApplication.shared.keyWindow?.rootViewController?.presentedViewController as? SWRevealViewController
let tabbarController = presentView?.frontViewController as? UITabBarController
if (tabbarController?.selectedIndex != 0) {
tabbarController?.tabBar.items?[0].badgeValue = self.count_total_notifications_ws?.description
}else{
//Here I need to show a showNotificationsbtn button
}
}
}
}
ControllerTwo.swift
class NewDashboardViewController: UIViewController, UITableViewDataSource, UITabBarControllerDelegate, UITableViewDelegate {
//This is the button that I need show
#IBOutlet weak var showNotificationsbtn: UIButton!
#IBAction func showNotifications(_ sender: Any) {true
self.viewDidAppear(true)
showNotificationsbtn.isHidden = true
}
}
Someone know how to I can do?
Thanks for your help.
In ViewControllerOne
if ((res!["notification"]) != nil) {
self.count_total_notifications_ws = self.count_total_notifications_ws! + 1
let presentView = UIApplication.shared.keyWindow?.rootViewController?.presentedViewController as? SWRevealViewController
let tabbarController = presentView?.frontViewController as? UITabBarController
if (tabbarController?.selectedIndex != 0) {
tabbarController?.tabBar.items?[0].badgeValue = self.count_total_notifications_ws?.description
}else{
//Here I need to show a showNotificationsbtn button
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "remoNotificationArrived"), object: nil, userInfo: nil )
}
}
In ViewControllerTwo
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
DispatchQueue.main.async {
NotificationCenter.default.addObserver(self, selector: #selector(self.showButton), name: NSNotification.Name(rawValue: "remoNotificationArrived"), object: nil)
}
}
func showButton(){
showNotificationsbtn.isHidden = false
}
First hide your button.
Now to unhide that button,you have multiple options.
1. Use delgate/protocol for communicating between viewcontrollers
2. You may add an observer
Related
I'm trying to send a notification on changing the navigation stack update. But it's not triggered. Here is my code. I have a requirement to change the root view controller on button action. I'm trying the below code, but it's not working for me.
import UIKit
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func replaceThirdViewControllerAsNavigationRoot() {
let nc = NotificationCenter.default
nc.post(name: Notification.Name("Notify"), object: nil)
self.navigationController?.viewControllers = [ThirdViewController.instance()]
}
}
class ThirdViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let nc = NotificationCenter.default
nc.addObserver(self, selector: #selector(userLoggedIn), name: Notification.Name("Notify"), object: nil)
}
#objc func userLoggedIn() {
print("-----")
}
}
extension UIViewController {
static func instance<T: UIViewController>() -> T {
let name = String(describing: self)
guard let controller = UIStoryboard.main.instantiateViewController(withIdentifier: name) as? T else {
fatalError("ViewController '\(name)' is not of the expected class \(T.self).")
}
return controller
}
}
extension UIStoryboard {
static var main: UIStoryboard {
return UIStoryboard.init(name: "Main", bundle: nil)
}
}
Thanks in advance.
The sequence of event is wrong. When you post a notification, you need to make sure that an observer already exists, otherwise the notification will be discarded.
In other words: make sure that
nc.addObserver(self, selector: #selector(userLoggedIn), name: Notification.Name("Notify"), object: nil)
runs before
nc.post(name: Notification.Name("Notify"), object: nil)
Im having button in all viewcontrollers to change language
LanguageViewController.swift
class LanguageViewController: UIViewController {
#IBAction func actionChange(_ sender: Any) {
L102Language.currentAppleLanguage()
L102Language.setAppleLAnguageTo(lang: "en")
// below code to refresh storyboard
self.viewDidLoad()
}
}
L102Language.swift
class func currentAppleLanguage() -> String{
let userdef = UserDefaults.standard
let langArray = userdef.object(forKey: APPLE_LANGUAGE_KEY) as! NSArray
let current = langArray.firstObject as! String
let endIndex = current.startIndex
let currentWithoutLocale = current.substring(to: current.index(endIndex, offsetBy: 2))
return currentWithoutLocale
}
/// set #lang to be the first in Applelanguages list
class func setAppleLAnguageTo(lang: String) {
let userdef = UserDefaults.standard
userdef.set([lang,currentAppleLanguage()], forKey: APPLE_LANGUAGE_KEY)
userdef.synchronize()
}
I inherited LanguageViewController in all my FirstViewCOntroller, SecondController as below
class FirstViewController: LanguageViewController {
}
class SecondController: LanguageViewController {
}
If I call self.viewDidLoad() it fails to change language from view defined in storyboard. How to reload storyboard, so that the language should change in all viewcontroller,if any button from any viewcontroller is clicked? Thanks!
You can use NotificationCenter for reloading the view controllers content, this will also reload the content of view controllers that are not visible.
extension Notification.Name {
static let didChangeLanguage = Notification.Name("didChangeLanguage")
}
override func viewDidLoad() {
//Add a listener
NotificationCenter.default.addObserver(self, selector: #selector(onDidChangeLanguage(_:)), name: .didChangeLanguage, object: nil)
}
#IBAction func actionChange(_ sender: Any) {
L102Language.currentAppleLanguage()
L102Language.setAppleLAnguageTo(lang: "en")
// Notify about the change.
NotificationCenter.default.post(name: .didChangeLanguage, object: self, userInfo: nil)
}
#objc func onDidChangeLanguage(_ notification:Notification) {
// reload content using selected language.
}
Correct me if I'm wrong. but I think you don't need to reload all view controllers. you just need to update them when they get displayed, view controllers are behind the presented one are not visible for the user.
for doing that you can do something like this:
var currentLanguage = ""
override func viewDidLoad() {
currentLanguage = currentAppleLanguage()
loadContentForLanguage(currentLanguage)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// this will be executed every time this sceen gets display
if currentLanguage != currentAppleLanguage() {
currentLanguage = currentAppleLanguage()
loadContentForLanguage(currentLanguage)
}
}
func loadContentForLanguage(_ currentLanguage: String) {
//here it goes whatever you currently have in viewDidLoad
}
My apologies if this does not compile, my swift is really rusty.
These both button shows same ViewController.
ViewController1.swift
#objc func btnEdit()
{
print("Edit")
let editDeptt = self.storyboard?.instantiateViewController(withIdentifier: "Add Department") as! AddDepartmentVC
self.navigationController?.pushViewController(editDeptt, animated: true)
}
#IBAction func btnNewDeptt(_ sender: Any)
{
let addDepttVC = self.storyboard?.instantiateViewController(withIdentifier: "Add Department") as! AddDepartmentVC
self.navigationController?.pushViewController(addDepttVC, animated: true)
}
ViewController2.swift
override func viewDidLoad() {
super.viewDidLoad()
//How to identify i come from which button
}
Add a property to your AddApartmentVC called action and set it before you push the view controller.
First create an enum:
enum Action {
case edit, newDept, unknown
}
Then define this property in your AddApartmentVC:
var action = Action.unknown
Then in your buttons' actions, set the property to the desired value:
editDeptt.action = .edit
or
addDepttVC.action = .newDept
Finally, in viewDidLoad(), check the value:
override func viewDidLoad() {
super.viewDidLoad()
switch(action) {
case .edit:
// do something for edit
case .newDept
// create a new Dept
case .unknown
print("what am I doing here?")
}
}
Add an instance variable name identify in your AddDepartmentVC and pass set the value of the same from your current VC like this
let editDeptt = self.storyboard?.instantiateViewController(withIdentifier: "Add Department") as! AddDepartmentVC
editDeptt.identify = "button1"
Then you can push it and check the variable in AddDepartmentVC
In ViewController2.swift, declare one variable like this
class ViewController2: UIViewController{
let var originTag: Int = 0
}
override func viewDidLoad() {
super.viewDidLoad()
if originTag == 0
{
// come from button 1 - btnEdit
}
else
{
// come from button 2 - btnNewDeptt
}
}
In ViewController 1, set originTag like this.
#objc func btnEdit()
{
print("Edit")
let editDeptt = self.storyboard?.instantiateViewController(withIdentifier: "Add Department") as! AddDepartmentVC
editDeptt.originTag = 0
self.navigationController?.pushViewController(editDeptt, animated: true)
}
#IBAction func btnNewDeptt(_ sender: Any)
{
let addDepttVC = self.storyboard?.instantiateViewController(withIdentifier: "Add Department") as! AddDepartmentVC
addDepttVC.originTag = 1
self.navigationController?.pushViewController(addDepttVC, animated: true)
}
Simply assign a tag for button -
Button1.tag=1
Button2.tag=2
Then check your button -
func onClickButton(sender: UIButton){
switch(sender.tag){
case 101 :
print("I am from button 1")
default :
print("I am from button 2")
}
}
use following it's perfectly working for you.
Take one public variable above class like below in Viewcontroller1.Swift
public var btnComingFrom = "first"
#objc func btnEdit()
{
btnComingFrom = "first"
print("Edit")
let editDeptt =
self.storyboard?.instantiateViewController(withIdentifier: "Add Department")
as! AddDepartmentVC
self.navigationController?.pushViewController(editDeptt, animated: true)
}
#IBAction func btnNewDeptt(_ sender: Any)
{
btnComingFrom = "second"
let addDepttVC = self.storyboard?.instantiateViewController(withIdentifier: "Add Department") as! AddDepartmentVC
self.navigationController?.pushViewController(addDepttVC, animated: true)
}
ViewController2.swift
override func viewDidLoad()
{
super.viewDidLoad()
if btnComingFrom == "first"
{
print("you are coming from first button")
}
if btnComingFrom == "second"
{
print("you are coming from second button")
}
}
I had searched stack over flow and all sites but unable to post notifications and I need to pass data from this class to another class I need to send the bool value to have validations can anyone help me how to pass the bool value ?
here is the code for it
radioSelected = false
NotificationCenter.default.addObserver(self, selector: #selector(paymentRadioEnable(n:)), name: NSNotification.Name.init(rawValue: "notification"), object: nil)
self.shippingmethodURL()
shippingTableView.delegate = self
shippingTableView.dataSource = self
shippingTableView.rowHeight = UITableViewAutomaticDimension
shippingTableView.estimatedRowHeight = shippingTableView.rowHeight
// Initialization code
}
func paymentRadioEnable(n:NSNotification){
}
func paymentRadioAction(button : KGRadioButton) {
_ = button.center
let centralPoint = button.superview?.convert(button.center, to:self.shippingTableView)
let indexPath = self.shippingTableView.indexPathForRow(at: centralPoint!)
if button.isSelected {
} else{
chekIndex = indexPath
radioSelected = true
self.shippingTableView.reloadData()
}
}
this is another class to which I need to post the bool value to check
#IBAction func continueButtonAction(_ sender: Any) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notification"), object: nil)
if radioSelected == false {
let radiobutton = SCLAlertView()
_ = radiobutton.showError("Warning", subTitle: "Please select shipping method", closeButtonTitle: "OK")
}else{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let addtoCartVC = storyboard.instantiateViewController(withIdentifier: "payment") as! PaymentMethodViewController
self.navigationController?.pushViewController(addtoCartVC, animated: true)
}
}
You can send the data into the object at the time of posting the Notification
let data : NSDictionary = ["DataKey" : "DataValue"]
NotificationCenter.default.post(name: NSNotification.Name(rawValue:
"notification"), object: data)
And after posting , you can get the same data in notification Handler.
My table view cell displays an entity with two different button elements. I want to be able to launch a view controller that displays a selection of food items if I click on the first button and a different view controller that displays a selection of beverages when I click on the second button.
I am able to correctly pass the data to the new view controllers, but can't seem to dismiss the current view and load the new one. My code is like this:
In the table view cell
#IBAction func foodBtnPressed(_ sender: Any) {
print("foodBtn pressed")
print("customer is \(customer?.name)")
vc.loadChooserScreen(toChoose: "Food", forCustomer: customer!)
}
#IBAction func beverageBtnPressed(_ sender: UIButton) {
print("beverageBtn pressed")
print("customer is \(customer?.name)")
vc.loadChooserScreen(toChoose: "Beverage", forCustomer: customer!)
}
In the table view controller
func loadChooserScreen(toChoose: String, forCustomer: Customer) {
print("Choose \(toChoose)")
print("For \(forCustomer.name)")
if toChoose == "Food" {
let foodVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "foodMenu") as? FoodVC
foodVC?.loadCustomerToEdit(customer: forCustomer)
dismissVC(sender: Any.self)
}
else if toChoose == "Beverage" {
let beverageVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "beverageMenu") as? BeverageVC
beverageVC?.loadCustomerToEdit(customer: forCustomer)
dismissVC(sender: Any.self)
}
else {
// do nothing
}
}
func dismissVC(sender: Any) {
print("Reached dismissVC function in selectionMenu")
dismiss(animated: true, completion: {
self.delegate!.dismissViewController()
})
}
In this view controller I also have the following protocol
protocol OrderVCProtocol {
func dismissViewController()
}
and have defined
var delegate: OrderVCProtocol!
In my root view controller
func dismissViewController() {
print("Reached dismissViewController function in rootView")
if let foodVC = self.storyboard?.instantiateViewController(withIdentifier: "foodMenu") {
self.present(foodVC, animated: true, completion: nil)
}
if let beverageVC = self.storyboard?.instantiateViewController(withIdentifier: "beverageMenu") {
self.present(beverageVC, animated: true, completion: nil)
}
}
And the delegate is set when the table view controller is called here
#IBAction func loadOrderView(_ sender: Any) {
let orderVC = self.storyboard?.instantiateViewController(withIdentifier: "orderView") as! OrderVC
orderVC.delegate = self
self.present(orderVC, animated: true, completion: nil)
}
Within my target view controllers I have the following function
func loadCustomerToEdit(customer: Customer) {
self.customerToEdit = customer
print("IN FoodVC THE CUSTOMER TO EDIT IS \(self.customerToEdit.name)")
}
and a corresponding one in the BeverageVC.
When I run the app, no errors are thrown and I get the following sample output in the console from my print statements:
foodBtn pressed
customer is Optional("John")
Choose Food
For Optional("John")
IN FoodVC THE CUSTOMER TO EDIT IS Optional("John")
Reached dismissVC function in selectionMenu
and a corresponding response if the beverage button is clicked.
Then nothing happens. So I know the data is correctly being passed to the new view controllers but I don't know how to dismiss the current screen and display the new one with the choices.
I hope my question is clear enough? I'm not sure what's wrong, but the console output clearly shows that the code runs fine until it tries to dismiss the current view.
EDITED TO ADD:
If I modify my dismissVC function in my tableview controller like this:
func dismissVC(sender: Any) {
print("Reached dismissVC function in selectionMenu")
delegate.dismissViewController()
}
the console view now throws
fatal error: unexpectedly found nil while unwrapping an Optional value
And if I modify it again to the following, It goes back to throwing no errors and getting stuck at the same place (i.e. printing the line "Stuck where delegate dismisses view"), showing that the delegate is still nil... but why is it nil when I'd set it in the root view and loaded it in this view?
func dismissVC(sender: Any) {
print("Reached dismissVC function in selectionMenu")
if delegate != nil {
delegate?.dismissViewController()
} else {
print("Stuck where delegate dismisses view")
}
I have solved my problem by implementing notifications via notification centre and delegates. Firstly, in my AppDelegate file I added this line at the bottom
let notifyCnt = NotificationCenter.default
Next, I modified my tableview cell functions to this
#IBAction func foodBtnPressed(_ sender: Any) {
notifyCnt.post(name: NSNotification.Name(rawValue: "toChoose"), object: nil, userInfo: ["toChoose": "Food", "forCustomer": customer])
}
#IBAction func beverageBtnPressed(_ sender: UIButton) {
notifyCnt.post(name: NSNotification.Name(rawValue: "toChoose"), object: nil, userInfo: ["toChoose": "Beverage", "forCustomer": customer])
}
Then, in the tableview controller I modified it to this:
protocol ChooserViewDelegate: class {
func loadChooserView(choice: String, forCustomer: Customer)
}
and defined
weak var delegate: ChooserViewDelegate?
and added this within my ViewDidLoad section
notifyCnt.addObserver(forName: Notification.Name(rawValue: "toChoose"), object: nil, queue: nil, using: loadChooserScreen)
and finally modified my chooser function like so:
func loadChooserScreen(notification: Notification) {
guard let userInfo = notification.userInfo,
let toChoose = userInfo["toChoose"] as? String,
let planToEdit = userInfo["customer"] as? Customer else {
print("No userInfo found in notification")
return
}
delegate?.loadChooserView(choice: toChoose, forCustomer: customer)
}
Then in my root view controller I have the following to replace what I had earlier:
/*Conform to ChooserViewDelegate Protocol */
func loadChooserView(choice: String, forCustomer: Customer) {
self.customer = forCustomer
dismiss(animated: false, completion: {
if choice == "Food" {
self.performSegue(withIdentifier: "food", sender: self.customer)
}
if choice == "Beverage" {
self.performSegue(withIdentifier: "beverage", sender: self.customer)
}
})
}
and I send over the data via prepareForSegue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "food" {
if let foodVC = segue.destination as? FoodVC {
storyboard?.instantiateViewController(withIdentifier: "food")
foodVC.customerToEdit = self.customerToEdit
foodVC.delegate = self
}
}
if segue.identifier == "beverage" {
if let beverageVC = segue.destination as? BeverageVC {
storyboard?.instantiateViewController(withIdentifier: "beverage")
beverageVC.customerToEdit = self.customerToEdit
beverageVC.delegate = self
}
}
}
So now everything loads and views correctly :)