NotificationCenter does not work between two views - ios

I am trying to create a simple project with notification center
So the idea is to have two view controllers as shown on the picture
When I click on the button SelectionVC gets loaded and if I click
on one of the buttons on SelectionVC I wanna create notification and
go back to first view controller and change the label text, but so far I can not sort it out.
ViewController code
import UIKit
let mainNotificationName = Notification.Name("mainNotification")
let catalogueNotificationName =
Notification.Name("catalogueNotification")
class ViewController: UIViewController {
#IBOutlet weak var labelText: UILabel!
override func viewWillAppear(_ animated: Bool) {
NotificationCenter.default.addObserver(forName: mainNotificationName, object: nil, queue: nil) { (notification) in
print(notification)
self.labelText.text = "Main Notification"
}
NotificationCenter.default.addObserver(forName: catalogueNotificationName, object: nil, queue: nil) { (notification) in
print(notification)
self.labelText.text = "Catalogue Notification"
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonTapped(_ sender: UIButton) {
let selectionVC = storyboard?.instantiateViewController(withIdentifier: "selectionVC") as! SelectionVC
present(selectionVC, animated: true, completion: nil)
}
}
SelectionVC code
import UIKit
class SelectionVC: UIViewController {
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 mainButtonTapped(_ sender: UIButton) {
NotificationCenter.default.post(name: mainNotificationName, object: nil)
let targetVC = storyboard?.instantiateViewController(withIdentifier: "viewController") as! ViewController
self.present(targetVC, animated: true, completion: nil)
}
#IBAction func catalogueButtonTapped(_ sender: UIButton) {
NotificationCenter.default.post(name: catalogueNotificationName, object: nil)
let targetVC = storyboard?.instantiateViewController(withIdentifier: "viewController") as! ViewController
self.present(targetVC, animated: true, completion: nil)
}
}
Can you please help me sort it out.

Change these actions :
#IBAction func mainButtonTapped(_ sender: UIButton) {
NotificationCenter.default.post(name: mainNotificationName, object: nil)
let targetVC = storyboard?.instantiateViewController(withIdentifier: "viewController") as! ViewController
self.present(targetVC, animated: true, completion: nil)
}
#IBAction func catalogueButtonTapped(_ sender: UIButton) {
NotificationCenter.default.post(name: catalogueNotificationName, object: nil)
let targetVC = storyboard?.instantiateViewController(withIdentifier: "viewController") as! ViewController
self.present(targetVC, animated: true, completion: nil)
}
To :
#IBAction func mainButtonTapped(_ sender: UIButton) {
NotificationCenter.default.post(name: mainNotificationName, object: nil)
self.dismiss(animated: true, completion: nil)
}
#IBAction func catalogueButtonTapped(_ sender: UIButton) {
NotificationCenter.default.post(name: catalogueNotificationName, object: nil)
self.dismiss(animated: true, completion: nil)
}
And add notification observer in viewDidLoad() not in viewWillAppear(). And change text on Main thread (just use DispatchQueue.main.async {}).

Related

How to pass delegate thru Navtigation Controller?

I have 2 VC's, and one navigation controller between them. How can i set First screen as a delegate of Second?
What i tried:
Present SecondVC from frist (it presents it without navigation)
Setting delegate in NavVC viewDidLoad()
FirstVC:
class MainVC: UIViewController, SecondVCDelegate {
func passData(text: String) {
// do stuff
}
#IBAction func openNextVC(_ sender: Any) {
let nextVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NavVC") as! NavVC
present(nextVC, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Navigation Controller:
class NavVC: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
SecondVC:
protocol SecondVCDelegate {
func passData(text: String)
}
class SecondVC: UIViewController {
var delegate: SecondVCDelegate?
#IBAction func save(_ sender: Any) {
// do stuff
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Main task here would be to access second view controller instance from navigation controller instance(navC). And to achieve this, you have to first access the rootViewController from your navigation controller instance(nextVC) as below:
let nextVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NavVC") as! NavVC
let secondVC = nextVC?.viewControllers.first as? SecondVC
secondVC.delegate = self
present(nextVC, animated: true, completion: nil)
The above code is applicable when second view controller is the root of your navigation controller. If second view controller is not the root controller to navC, then you have to iterate the controllers to get second view controller instance. Once second controller instance is there, then you can assign the delegate to the same as below.
for controller in navC.viewControllers {
if controller.isKind(of: SecondVC.self) {
if let secondVc = controller as? SecondVC {
secondVc.delegate = self
}
break
}
}
I handled the same thing. Try this on MainVC:
class MainVC: UIViewController, SecondVCDelegate {
func passData(text: String) {
// do stuff
}
#IBAction func openNextVC(_ sender: Any) {
guard let secondController = self.storyboard?.instantiateViewController(withIdentifier: "AddGroupVC") as? AddGroupVC else { return }
secondController.delegate = { [weak self] in
print("call your delegates here")
}
self.navigationController?.present(secondController, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
On second View Controller, call the delegates in viewWillDisappear :
class SecondVC: UIViewController {
var delegate: SecondVCDelegate?
#IBAction func save(_ sender: Any) {
// do stuff
self.dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillDisappear(_ animated: Bool) {
self.delegate
}
}

How to assign value to an outlet after delegating it?

So my app has 3 screens: Main, Second, and Result (very simple)
On the Main screen, i show a label and button to change it
On the Second one I change label with textinput and pass it to Result
(has
navigation controller)
On the Last screen I show the result and 2 buttons: save and
cancel
My problem is I can't assign value to Main's outlet because it's nil, and I can't do anything with viewDidLoad() because it works only once when the app starts.
What can I do to fix this? is there any function to reload view so I can assign value in viewDidLoad?
Storyboard screenshot
The whole app is here: https://drive.google.com/file/d/1mvL2fVxjOHbL4dReCwJ8poIq9G9-ezny/view
MainVC:
class MainVC: UIViewController, ResultVCDelegate {
func passData(text: String) {
// label.text = text -- throws error
}
#IBOutlet weak var label: UILabel!
#IBAction func change(_ sender: Any) {
let nextVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NavVC")
present(nextVC, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
SecondVC:
class SecondVC: UIViewController {
#IBOutlet weak var inputText: UITextField!
#IBAction func save(_ sender: Any) {
let nextVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ResultVC") as! ResultVC
nextVC.labelText = inputText.text!
navigationController?.pushViewController(nextVC, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
ResultVC:
protocol ResultVCDelegate {
func passData(text: String)
}
class ResultVC: UIViewController {
var delegate: ResultVCDelegate?
var labelText = ""
#IBOutlet weak var label: UILabel!
#IBAction func saveAndGoHome(_ sender: Any) {
let mainVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MainVC") as! MainVC
self.delegate = mainVC
delegate?.passData(text: labelText)
dismiss(animated: true, completion: nil)
}
#IBAction func cancel(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
label.text = labelText.isEmpty ? label.text : labelText
}
}
BTW: I did similar app with two screens and it worked like a charm... strange
Following on from my comment.
Looking at your code, the issue is that you are not passing a reference to your MainVC to your ResultVC, and instead you are creating a new instance of MainVC, this results in a a crash because the view controller hasn't been properly created. But you don't want to create a new instance, as you need a reference to the original MainVC that you created.
You can get this by passing the reference, you will need to update all three of your ViewControllers. Something like this should work. Note I haven't tested this but this is the general principle.
class MainVC: UIViewController, ResultVCDelegate {
func passData(text: String) {
label.text = text
}
#IBOutlet weak var label: UILabel!
#IBAction func change(_ sender: Any) {
let nextVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NavVC")
nextVC.delegate = self // Here we add the delegate (the reference to MainVC)
present(nextVC, animated: true, completion: nil)
}
}
We need to add the delegate here and then forward it on to the ResultVC.
class SecondVC: UIViewController {
weak var delegate: ResultVCDelegate? // Add the delegate that will hold a reference to MainVC
#IBOutlet weak var inputText: UITextField!
#IBAction func save(_ sender: Any) {
let nextVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ResultVC") as! ResultVC
nextVC.labelText = inputText.text!
nextVC.delegate = delegate // Here we pass the reference to MainVC
navigationController?.pushViewController(nextVC, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
We can remove the code for instantiating the MainVC from the storyboard and instead just use the delegate to pass the value back to MainVC.
class ResultVC: UIViewController {
weak var delegate: ResultVCDelegate?
var labelText = ""
#IBOutlet weak var label: UILabel!
#IBAction func saveAndGoHome(_ sender: Any) {
// Just use the delegate no need to create a new instance
delegate?.passData(text: labelText)
dismiss(animated: true, completion: nil)
}
#IBAction func cancel(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
label.text = labelText.isEmpty ? label.text : labelText
}
}
-> Main VC (Where you need to get data)
class MainVC: UIViewController, ResultVCDelegate {
func passData(text: String) {
// label.text = text -- throws error
print(text)
}
#IBOutlet weak var label: UILabel!
#IBAction func change(_ sender: Any) {
let nextVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NavVC")
nextVC.delegate = self
present(nextVC, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
-> Result VC (from where you need to send data)
protocol ResultVCDelegate: AnyObject {
func passData(text: String)
}
class ResultVC: UIViewController {
weak var delegate: ResultVCDelegate?
var labelText = "Please Enter Something"
#IBOutlet weak var label: UILabel!
#IBAction func saveAndGoHome(_ sender: Any) {
self.delegate?.passData(text: labelText)
dismiss(animated: true, completion: nil)
}
#IBAction func cancel(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
label.text = labelText.isEmpty ? label.text : labelText
}
}

How to bring out the viewController after menu item is selected?

Im trying to bring out the viewcontroller after i select an item in the menu. Currently, when I press something it just change the page, but it gets stuck on the right side. How do i bring that out.
This is the code that i wrote.
class ContainerVC: UIViewController {
var sideMenuOpen = false
#IBOutlet weak var mainView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self,
selector: #selector(toggleSideMenu),
name: NSNotification.Name("ToggleSideMenu"),
object: nil)
}
#IBOutlet weak var sideMenuConstraint: NSLayoutConstraint!
#objc func toggleSideMenu() {
if sideMenuOpen {
sideMenuOpen = false
sideMenuConstraint.constant = -270
} else {
sideMenuOpen = true
sideMenuConstraint.constant = 0
}
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
}
And this is what i wrote in my SideMenuVC
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.row)
// NotificationCenter.default.post(name: NSNotification.Name ("ToggleSideMenu"), object: nil)
switch indexPath.row {
case 0: NotificationCenter.default.post(name: NSNotification.Name ("ShowProfile"), object: nil)
case 8: //signs out of Firebase
try? Auth.auth().signOut()
//signs out of Facebook
FBSDKAccessToken.setCurrent(nil)
let main = UIStoryboard(name: "Main", bundle: nil)
let second = main.instantiateViewController(withIdentifier: "loginPage")
self.present(second, animated: true, completion: nil)
default: break
//https://www.youtube.com/watch?v=Hl_Re_KLhcY
}
}
And this is what i wrote in my HomeVC
#IBAction func menuButton() {
print("Toggle Side Menu")
NotificationCenter.default.post(name: NSNotification.Name("ToggleSideMenu"), object: nil)
}
i would like the selected page to come out.
Just add NotificationCenter.default.post(name: NSNotification.Name ("ToggleSideMenu"), object: nil) to your didSelectRowAt

Custom protocol delegate method not calling

I am trying to change lable of one view controller from another view controller using custom protocol but its delegate method is not being called
ViewController3 code:
when i click on close button it's delegate method is not being called in my ViewController2.
protocol ViewController3Delegate: class {
func changeLable(_ text: String)
}
class ViewController3: UIViewController {
weak var delegate: ViewController3Delegate?
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func btnCloseAction(_ sender: Any) {
delegate?.changeLable("fillter applied")
self.dismiss(animated: true, completion: nil)
}
}
ViewController2 code:
class ViewController2: UIViewController,ViewController3Delegate {
#IBOutlet weak var lblReport: UILabel!
let VC3 = ViewController3(nibName: "ViewController3", bundle: nil)
override func viewDidLoad() {
super.viewDidLoad()
VC3.delegate = self
}
func changeLable(_ text: String) {
print("delegate called")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Anybody knows where i am wrong, please suggest me some solution
You have not defined a delegate in your ViewController2 which will be used to the delegate in ViewController3 .
See This :
protocol ViewController3Delegate: class {
func changeLable(_ text: String)
}
class ViewController3: UIViewController {
weak var delegate: ViewController3Delegate?
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func btnCloseAction(_ sender: Any) {
if delgate !=nil {
delegate?.changeLable("fillter applied")
self.dismiss(animated: true, completion: nil)
}
}
}
And then in your ViewController2 class :
class ViewController2: UIViewController,ViewController3Delegate {
#IBOutlet weak var lblReport: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func changeLable(_ text: String) {
print("delegate called")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier = "Yoursegueientifier"
{
let vc = segue.destination as! ViewController3
vc.delegate = self
}
}
}
Note : You have to define your segueidentifer name in your storyboard
You should first present ViewController3 from ViewController2 like this:
let VC3 = ViewController3(nibName: "ViewController3", bundle: nil)
VC3.delegate = self
self.presentViewController(VC3, animated: true, completion: nil)
Also, you can delete this line above viewDidLoad
let VC3 = ViewController3(nibName: "ViewController3", bundle: nil)

Using UIPageView images to open ViewController w/ NavigationController

I am trying to make three images in a UIPageView to open three different NavigationController. Here is my layout as of right now. I connected three Custom Segue to each NavigationController and under ContentViewController, I added the following #IBAction method:
#IBAction func navJns(sender: AnyObject?){
let storyboard = UIStoryboard(name: "Nav", bundle: nil)
let controller = storyboard.instantiateViewControllerWithIdentifier("Jeans") as! Jeans
self.modalPresentationStyle = UIModalPresentationStyle.Custom
presentViewController(controller, animated: true, completion: nil)
}
#IBAction func navBlusas(sender: AnyObject?){
let storyboard = UIStoryboard(name: "Nav", bundle: nil)
let controller = storyboard.instantiateViewControllerWithIdentifier("Blusas") as! Blusas
self.modalPresentationStyle = UIModalPresentationStyle.Custom
presentViewController(controller, animated: true, completion: nil)
}
#IBAction func navLeggings(sender: AnyObject?){
let storyboard = UIStoryboard(name: "Nav", bundle: nil)
let controller = storyboard.instantiateViewControllerWithIdentifier("Leggings") as! Leggings
self.modalPresentationStyle = UIModalPresentationStyle.Custom
presentViewController(controller, animated: true, completion: nil)
}
And under the viewDidLoad method:
var tapGesturePageView = UITapGestureRecognizer(target: self, action: "navJns:"); self.imageView.addGestureRecognizer(tapGesturePageView)
var tapGesturePageView2 = UITapGestureRecognizer(target: self, action: "navBlusas:"); self.imageView.addGestureRecognizer(tapGesturePageView2)
var tapGesturePageView3 = UITapGestureRecognizer(target: self, action: "navLeggings:"); self.imageView.addGestureRecognizer(tapGesturePageView3)
When I compile and run the app, it shows the three images in the PageView but nothing is happening.
Any advice would be helpful!
Here is the rest of the code:
ContentViewController.swift
import UIKit
class ContentViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
var pageIndex: Int!
var imageFile: String!
override func viewDidLoad()
{
super.viewDidLoad()
self.imageView.image = UIImage(named: self.imageFile)
var tapGesturePageView = UITapGestureRecognizer(target: self, action: "navJns:"); self.imageView.addGestureRecognizer(tapGesturePageView)
var tapGesturePageView2 = UITapGestureRecognizer(target: self, action: "navBlusas:"); self.imageView.addGestureRecognizer(tapGesturePageView2)
var tapGesturePageView3 = UITapGestureRecognizer(target: self, action: "navLeggings:"); self.imageView.addGestureRecognizer(tapGesturePageView3)
}
#IBAction func navJns(sender: AnyObject?){
let storyboard = UIStoryboard(name: "Nav", bundle: nil)
let controller = storyboard.instantiateViewControllerWithIdentifier("Jeans") as! Jeans
self.modalPresentationStyle = UIModalPresentationStyle.Custom
presentViewController(controller, animated: true, completion: nil)
}
#IBAction func navBlusas(sender: AnyObject?){
let storyboard = UIStoryboard(name: "Nav", bundle: nil)
let controller = storyboard.instantiateViewControllerWithIdentifier("Blusas") as! Blusas
self.modalPresentationStyle = UIModalPresentationStyle.Custom
presentViewController(controller, animated: true, completion: nil)
}
#IBAction func navLeggings(sender: AnyObject?){
let storyboard = UIStoryboard(name: "Nav", bundle: nil)
let controller = storyboard.instantiateViewControllerWithIdentifier("Leggings") as! Leggings
self.modalPresentationStyle = UIModalPresentationStyle.Custom
presentViewController(controller, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController, UIPageViewControllerDataSource {
var pageViewController: UIPageViewController!
var pageImages: NSArray!
override func viewDidLoad() {
super.viewDidLoad()
self.pageImages = NSArray(objects: "jeans.png", "blusas.png", "leggings.png")
self.pageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageViewController") as! UIPageViewController
self.pageViewController.dataSource = self
var startVC = self.viewControllerAtIndex(0) as ContentViewController
var viewControllers = NSArray(object: startVC)
self.pageViewController.setViewControllers(viewControllers as [AnyObject], direction: .Forward, animated: true, completion: nil)
self.pageViewController.view.frame = CGRectMake(0, 30, self.view.frame.width, self.view.frame.size.height - 60)
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
self.pageViewController.didMoveToParentViewController(self)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func viewControllerAtIndex(index: Int) -> ContentViewController?
{
if index >= self.pageImages.count
{
return nil
}
var vc: ContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ContentViewController") as! ContentViewController
vc.imageFile = self.pageImages[index] as! String
vc.pageIndex = index
return vc
}
// MARK: - Page View Controller Data Source
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController?
{
var vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if (index == 0) || (index == NSNotFound)
{
return nil
}
index--
return self.viewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
var vc = viewController as! ContentViewController
var index = vc.pageIndex as Int
if (index == NSNotFound)
{
return nil
}
index++
return self.viewControllerAtIndex(index)
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int
{
return 0
}
}

Resources