Accessing item of a navigationController childview in swift - swift

newbie to ios app coding here so any help would greatly be appreciated.
I have an app that when rotated to landscape opens a side menu automatically and I need it to disable the menu button in the child view of the navigationController. Here's my code.
Navigation controller
import Foundation
class GDNavigationController:UINavigationController{
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
//Send notification when the device is rotated.
NSNotificationCenter.defaultCenter().addObserver(self, selector: "rotated", name: UIDeviceOrientationDidChangeNotification, object: nil)
}
func rotated(){
/*
Put the code here to access the menu button
*/
if(UIDeviceOrientationIsLandscape(UIDevice.currentDevice().orientation))
{
//disable the menu button here
self.revealViewController().setFrontViewPosition(FrontViewPosition.Right, animated: false)
}
if(UIDeviceOrientationIsPortrait(UIDevice.currentDevice().orientation))
{
//enable the menu button here
self.revealViewController().setFrontViewPosition(FrontViewPosition.Left, animated: false)
}
}
}
My ViewController code
import Foundation
class LocalCollectionController: UICollectionViewController{
#IBOutlet var MenuViewButton: UIBarButtonItem!
override func viewDidLoad() {
MenuViewButton.target = self.revealViewController()
MenuViewButton.action = Selector("revealToggle:")
}
}
I have different navigationControllers load with different viewControllers based on which menu item is selected. The different navigationControllers share the same subclass but I never know which viewController is loaded, this is what I need to find out and how to access the button in that viewController.
Can anyone help?

So I figured it out, change my navigation controller to this
import Foundation
class GDNavigationController:UINavigationController{
var BBItem = UIBarButtonItem()
override func viewDidAppear(animated: Bool) {
BBItem = self.topViewController.navigationItem.leftBarButtonItem!
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
//Send notification when the device is rotated.
NSNotificationCenter.defaultCenter().addObserver(self, selector: "rotated", name: UIDeviceOrientationDidChangeNotification, object: nil)
self.rotated()
}
func rotated(){
if(UIDeviceOrientationIsLandscape(UIDevice.currentDevice().orientation))
{
self.revealViewController().setFrontViewPosition(FrontViewPosition.Right, animated: true)
self.BBItem.enabled = false
}
if(UIDeviceOrientationIsPortrait(UIDevice.currentDevice().orientation))
{
self.revealViewController().setFrontViewPosition(FrontViewPosition.Left, animated: true)
self.BBItem.enabled = true
}
}
}

Related

Change variable while dismissing modal controller

EDIT: I have decided to change the way my app works, so this problem is solved. Thanks to everyone who helped!
I have a modal controller where when I press a button it dismisses the view. What I want to do is change a variable in another view controller when I dismiss it, is that possible? Or, if this doesn't work, is there a way for me to access the changed variable of another swift file? I will add my code below:
class PopupViewController: UIViewController {
var event = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func dismiss(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
#IBAction func event910(_ sender: Any) {
event = "storyTime"
dismiss(animated: true, completion: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! ViewController
vc.event = event
}
}
I want to pass the changed variable "event" to another view controller, how can I do this?
Delegate View Controller is as follows. : -
it is the place where you will send the data to the next swift file
protocol myprotocol {
func anyfunction(_ param1:String)
}
struct mystruct1 {
var delegate:myprotocol?
// where you want tot start the delegate / send the data to the next file
func anymethod(){
delegate.anyfunction(sendTheDataYouWant)
}
}
// it is here you will receive the data
class anyclass:UIViewController ,myprotocol {
let class1 = mystruct1()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
class1.delegate = self
}
func anyfunction(param1:String){
// here Save the data you want
// because this function will be triggered as delegate will be called
}
}
ps:- I reccomend you to read https://docs.swift.org/swift-book/LanguageGuide/Protocols.html
& apple docs

do something after first view controller appears

I have a View Controller with a button..
This button calls a "Present as Popover Seague" to a second view controller.
The second view controller has a close button with this function:
#IBAction func exit(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
Now I would like to do something in the first controller, after the second Controller is dismissed.
In the first view controller I tried this functions:
override func viewDidAppear(_ animated: Bool) {
print("viewDidAppear")
}
override func viewWillAppear(_ animated: Bool) {
print("viewWillAppear")
}
but no console log will shown.
Where is my mistake?
FirstViewContorller
import UIKit
class firstVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//ERROR
secondVC.dismissCompletion = {
print("dismissCompletion")
}
}
}
SecondVC (popover)
import UIKit
class secondVC: UIViewController {
var dismissCompletion: (() -> Void)?
// EXIT POPOVER
#IBAction func exit(_ sender: UIButton) {
self.dismiss(animated: true, completion: dismissCompletion)
}
}
The viewDidAppear() method of the main view controller won't be called because of the popover presentation style you use. If you choose to present the second view controller full screen - those methods will fire.
If we're sticking with the popover, the first thing you need to do is in your second view controller, the one that's being presented, add a property for a closure that will be executed upon its dismiss:
class PopoverViewController: UIViewContoller {
var dismissCompletion: (() -> Void)?
#IBAction func exit(_ sender: UIButton) {
self.dismiss(animated: true, completion: dismissCompletion)
}
}
And in your main view controller you define what needs to be done upon the popover's dismiss:
popoverViewContoller.dismissCompletion = {
// do stuff
}
UPDATE:
I assume you've setup the segue in your storyboard. I also assume that in your storyboard you've given the view controllers their respective class names:
This is what your code should look like:
class FirstViewController: UIViewController {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let secondVC = segue.destination as? SecondViewController else {
return
}
secondVC.dismissCompletion = {
print("Popover dismissed")
}
}
}
class SecondViewController: UIViewController {
var dismissCompletion: (() -> Void)?
#IBAction func exit(_ sender: Any) {
dismiss(animated: true, completion: dismissCompletion)
}
}
Please note the classes naming and the way I got the secondVC instance.

Swift: How to dismiss a ViewController programmatically?

I got a little problem.
On my main view controller I got a bar button that opens a slide menu, which is a regular view controller using a slide in transition. The slide menu has a button to open another view controller. When the new view controller is opened, you have the option to cancel, which dismisses the current view controller. The problem is, that the user ends up in the menu view once again, instead of the main view controller. Would be very happy to know what I am doing wrong :)
func openSupport() {
guard let creditViewContoller = storyboard?.instantiateViewController(withIdentifier: "support") as? CreditViewController else { return }
present(creditViewContoller, animated: true)
}
#IBAction func buttonSupport(_ sender: UIButton) {
let menuView = MenuViewController()
menuView.dismiss(animated: true, completion: nil)
openSupport()
print("Tap on Support")
}
you can dismiss view controller simply by using
self.dismiss(animated: true, completion: nil)
Consider
#IBAction func buttonSupport(_ sender: UIButton) {
let menuView = MenuViewController() // (1)
menuView.dismiss(animated: true, completion: nil) // (2)
openSupport() // (3)
print("Tap on Support")
}
This:
Creates new MenuViewController but never presents it;
Calls dismiss on view controller that was never presented; and
Calls openSupport from this MenuViewController instance (which was never dismissed).
Bottom line, you want to let the main view controller that presented the menu do the presenting. So, the menu view controller should:
Define a protocol for it to inform the presenting view controller to transition to the next scene:
protocol MenuViewControllerDelegate: class {
func menu(_ menu: MenuViewController, present viewController: UIViewController)
}
And then the menu view controller can, when it’s done dismissing, tell its delegate what it should present:
class MenuViewController: UIViewController {
weak var delegate: MenuViewControllerDelegate?
#IBAction func didTapSupport(_ sender: Any) {
dismiss(animated: true) {
guard let controller = self.storyboard?.instantiateViewController(withIdentifier: "support") else { return }
self.delegate?.menu(self, present: controller)
}
}
#IBAction func didTapCancel(_ sender: Any) {
dismiss(animated: true)
}
}
Then the main view controller needs to
Make sure to set the delegate of the menu view controller:
class ViewController: UIViewController {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? MenuViewController {
destination.delegate = self
}
}
}
and
Make sure to present the view controller that the menu controller asked it to:
extension ViewController: MenuViewControllerDelegate {
func menu(_ menu: MenuViewController, present viewController: UIViewController) {
present(viewController, animated: true)
}
}
There are lots of different ways of achieving this, so don’t get lost in the details here. But the idea is to have some system by which the menu view controller can request whomever is to present the support view to do so, not try to do it itself.

How to run code in your main view controller in swift when a pop up closes

I'm currently writing my first swift app. Currently there is one view/view controller that loads when the app is run as well as a popup window tied to a separate view-controller (like so: https://www.youtube.com/watch?v=S5i8n_bqblE). When I close the pop-up I want to update several things on my original view and run some code. However, neither func viewDidLoad() nor func viewDidAppear() seems to run. And I can't do anything from the pop-up view since I don't have access to the components in the main view-controller from it. What should I do?
The pop-up is "presented modally" if that makes a difference?
I'm assuming you have a MainViewController from which you're presenting the PopupVC. You can use delegate pattern here.
Define a PopupVCDelegate as follow
protocol PopupVCDelegate {
func popupDidDisappear()
}
In your PopupVC define a delegate property of type PopupVCDelegate. And in the closePopup method, call the delegate method popupDidDisappear
class PopupVC: UIViewController {
public var delegate: PopupVCDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func closePopup(_ sender: Any) {
dismiss(animated: true, completion: nil)
delegate?.popupDidDisappear()
}
}
Now any class that adopts this delegate will be able to receive the callback when the closePopup is called. So make your MainViewController to adopt this delegate.
class MainViewController: UIViewController, PopupVCDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
func showPopup() {
let popupViewController = //Instantiate your popup view controller
popupViewController.delegate = self
//present your popup
}
func popupDidDisappear() {
//This method will be called when the popup is closed
}
}
Another way is to fire a notification through NSNotificationCenter on closePopup and add an observer in MainViewController to listen to that notification. But it is not recommended in this scenario.
Edit
As you have asked for the NSNotificationCenter method. Please change your classes as follow
class PopupVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func closePopup(_ sender: Any) {
dismiss(animated: true, completion: nil)
NotificationCenter.default.post(name: NSNotification.Name("notificationClosedPopup"), object: nil)
}
}
class MainViewController: UIViewController, PopupVCDelegate {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(onPopupClosed), name: NSNotification.Name(rawValue: "notificationClosedPopup"), object: nil)
}
#objc func onPopupClosed() {
//This method will be called when the popup is closed
}
deinit {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "notificationClosedPopup"), object: nil)
}
}

Send data only if view controller is being popped off stack by swipe or back button press but not by switching tabs

I looked through SO and compiled these methods below but none of them works for me.
I have a TabBarController with 2 tabs. In the Second tab I have a NavigationController > TableViewController > DetailViewController.
In my DetailViewController I have a custom delegate to send some data to the TableViewController when the Back Button is pressed or the view is Swiped to Dismiss (right swipe). I only want the data sent when the Back Button or Swipe to Dismiss is fully finished and not get sent when the tab is switched or if swiping 3/4 of the way but the user decides NOT to complete the back swipe (basically they stay on the same DetailVC scene).
I tried all of these methods below and they either get triggered when the tab is switched to the first tab, when the DetailVC gets pushed on and popped off, or during the 1/2 way Swipe to Dismiss the DetailVC they still run meaning the data should not have been sent.
DetailViewController:
protocol DetailViewDelegate: class {
func sendSomeData(value: Bool)
}
class DetailViewController: UIViewController{
weak var delegate: DetailViewDelegate?
//1. runs when Tab switches, the Back Button is pressed, and Swipe to Dismiss is triggered
override func viewWillDisappear(_ animated : Bool) {
super.viewWillDisappear(animated)
if (self.isMovingFromParentViewController) || (self.isBeingDismissed){
//doesn't run at all
}else{
//runs whenever view is no longer on scene
sendData()
}
}
//2. runs when Tab switches, Back Button is pressed, Swipe to Dismiss is triggered, and when the view is Pushed on AND Popped off
override func didMove(toParentViewController parent: UIViewController?) {
if parent != nil {
sendData()
}else{
//if parent == nil doesn't run at all
}
}
//3. if switching from the second tab it doesn't run but when switching back to the second tab it does run, also runs when view is being Pushed on and Not Popped on
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
sendData()
}
//4. if switching from the second tab it doesn't run but when switching back to the second tab it does run, also runs when view is being Pushed on and Not Popped on
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
sendData()
}
//MARK:- Custom Func
fileprivate func sendData(){
let value = true
delegate?.sendSomeData(value: value)
}
}
TableViewController:
class TableVC: UIViewController, DetailViewDelegate, UITableViewData..., UITableViewDele...{
var setValue = false
func sendSomeData(value: Bool){
//setValue should only update to true if DetailVC's Back Button is pressed or Right Swipe to Dismiss is fully complete
self.setValue = value
}
}
The TableView never has a problem receiving the data. The problem is when I switch tabs (data still gets sent) or a swipe to dismiss on the DetailVC isn't fully completed (data still gets sent).
What's the best way to send the data from the DetailVC but making sure the Back Button is pressed or Right Swipe to Dismiss is fully complete?
You need to use custom back button and a delegate to call the parent.
this is your parent ViewController:
import UIKit
class ViewController: UIViewController, ViewControllerSecondDelegate {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Next" {
let vc = segue.destination as? ViewControllerSecond
vc?.delegate = self
}
}
func secondDelegate() {
print("delegate") //GetData()
}
}
and this is the child view controller, which you want to back from it to your parent:
import UIKit
protocol ViewControllerSecondDelegate {
func secondDelegate()
}
class ViewControllerSecond: UIViewController, UIGestureRecognizerDelegate {
var isTouched = false
var isPopTouch = true
var delegate: ViewControllerSecondDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(false, animated:false)
let myBackButton:UIButton = UIButton.init(type: .custom)
myBackButton.addTarget(self, action: #selector(ViewControllerSecond.popToRoot(sender:)), for: .touchUpInside)
myBackButton.setTitle("Back", for: .normal)
myBackButton.setTitleColor(.blue, for: .normal)
myBackButton.sizeToFit()
let myCustomBackButtonItem:UIBarButtonItem = UIBarButtonItem(customView: myBackButton)
self.navigationItem.leftBarButtonItem = myCustomBackButtonItem
self.navigationController?.interactivePopGestureRecognizer?.delegate = self
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if isTouched {
isPopTouch = true
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.isPopTouch = false
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
if isPopTouch {
delegate?.secondDelegate()
}
}
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer == self.navigationController?.interactivePopGestureRecognizer {
self.isTouched = true
}
return true
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.isTouched = false
}
func popToRoot(sender:UIBarButtonItem){
delegate?.secondDelegate()
self.navigationController?.popToRootViewController(animated: true)
}
}
the above code, handle back button and back gesture.