My scenario, I am having tabbar with three viewcontroller and first tabbar viewcontroller I am showing tableview with some data. Whenever I am clicking the tableview cell I am passing the data to one special present model popupcontroller. If I dismiss popup controller I need to show directly to tabbar index 1 (I mean tabbar second viewcontroller) also need to pass the values.
Here, below code I tried
After select the tableview cell passing values to popupview controller
Popup close button click to pass the same value to tabbar index 1 (tabbar second viewcontroller)
Inside popup view controller
var item : Datum! //Datum Codable Root
#IBAction func close_click(_ sender: Any) {
NotificationCenter.default.post(name: Notification.Name(rawValue: "disconnectPaxiSocket"), object: nil)
if let presenter = presentingViewController as? HomeViewController {
presenter.updatitem = item
}
dismiss(animated: true, completion: nil)
}
Once popup dismissed showing tabbar first viewcontroller (index 0), so I added NotificationCenter to call function and change the index.
First of all you don't need to use NotificationCenter for that because you can simply achieve it by passing the UITabBarController object to next view controller when you select any cell in tableView like shown below:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
vc.selectedIndex = indexPath.row
vc.tabBar = self.tabBarController
self.present(vc, animated: true, completion: nil)
}
and you need to declare var tabBar: UITabBarController? into DetailViewController which will hold the value passed by previous view controller.
Then when you dismiss the view controller you can simply select the another index of UITabBarController with self.tabBar?.selectedIndex = 1 and you can simply pass the value to first index view controller like:
#IBAction func close_click(_ sender: Any) {
dismiss(animated: true, completion: {
if let secondTab = self.tabBar?.viewControllers?[1] as? SecondViewController {
secondTab.selectedIndexFromFirstTab = self.selectedIndex
secondTab.tfData = self.userTF.text!
}
self.tabBar?.selectedIndex = 1
})
}
and you need to declare objects in SecondViewController like:
var selectedIndexFromFirstTab = 0
var tfData = ""
Which will hold the data of previous view.
Now your result will look like:
For more info you can refer THIS demo project.
EDIT:
So from your code I have seen that you have embed in navigation controllers to your tabBar controllers so you need to update close_click method like shown below:
#IBAction func close_click(_ sender: Any) {
dismiss(animated: true, completion: {
if let navView = self.tabBar?.viewControllers?[1] as? UINavigationController {
if let secondTab = navView.viewControllers[0] as? HomeViewController {
secondTab.selectedIndexFromFirstTab = self.selectedIndex
secondTab.item = self.item
secondTab.tfData = "Sample"
}
}
self.tabBar?.selectedIndex = 1
})
}
You could use the completion handler of the dismiss method.
dismiss(animated: true, completion: nil)
Instead of passing nil, Pass a method to change the Tab bar to second tab.
The completion handler in the dismiss view controller, gets called when the dismissing of the view controller is completed.
viewController.dismissViewControllerAnimated(true, completion: {
FunctionToChangeToSecondTab()
})
Related
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(identifier:
"PersonViewController") as? PersonViewController
vc?.names = persons[indexPath.row].emer!
vc?.lastnames = persons[indexPath.row].mbiemer!
vc?.delegate = self
PersonViewController.indexes = indexPath.row
self.navigationController?.pushViewController(vc!, animated: true)
}`
I have a situations like this:
First ViewController is a collectionView, the second is a viewcontroller which is allowed to add new Person when I tap a button and works perfectly. I have used delegates and Core Data for local memory.
Also the second ViewController has another button to edit person. When I press button a new viewController appears with extension UIAdaptivePresentationControllerDelegate. This viewcontroller consists of 2 buttons and 2 textfields. So when I want to press save button I want to go to the first viewcontroller (collectionview list) and when to press cancel to go back to the second viewcontroller.
Viewcontrollers are created with pushViewController method.
Please anyone help what should I use?
then in PersonViewController I call this inside button edit.
#objc func editCell(){
let vc = storyboard?.instantiateViewController(identifier:
"ModalPresentationViewController") as?
ModalPresentationViewController
navigationController?.pushViewController(vc!, animated: true)
}
Now the code in the las ViewController which is ModalViewController
#objc func savePerson(){
if editNameTextfield.text == "" || editlastNameTextfield.text == ""{
self.errorLbl.alpha = 1
}
else{
let vc = ViewController()
guard let textName = editNameTextfield.text else{
return
}
guard let textLastName = editlastNameTextfield.text else{
return
}
let index = PersonViewController.indexes
DispatchQueue.main.async {[self] in
if editDelegate != nil{
self.editDelegate!.editPerson(editedName: textName, editedLastname: textLastName, index: index)
}
}
// What should I call here??
}
}
You can use something like:
func popTo(_ type: AnyClass) {
guard let controllers = navigationController?.viewControllers else {return}
for controller in controllers {
if controller.classForCoder == type {
navigationController?.popToViewController(controller, animated: true)
break
}
}
}
And invoke the function depending on the condition as:
popTo(A.classForCoder())
EDIT: Above solution works only if ViewControllers B and C presented via a navigation controller and are in the same navigation stack.
Since you present the ViewController C modally, below answer should work:
Make these changes to your ViewController B that is presenting C:
class B: UIViewController, PresenterDelegate {
// some functions
func popToPrevious() {
navigationController.popViewController(animated: false)
}
#objc func editCell(){
let vc = storyboard?.instantiateViewController(identifier:
"ModalPresentationViewController") as?
ModalPresentationViewController
vc.presenterDelegate = self
present(vc, animated: true, completion: nil)
}
}
And in your ViewController C:
class C {
var presenterDelegate: PresenterDelegate? = nil
//
#objc func savePerson(){
//
//
dismiss(animated: true, completion: {
self.presenterDelegate?.popToPrevious()
})
}
}
Also add PresenterDelegate protocol to a new file or below ViewControllers B or C
protocol PresenterDelegate {
func popToPrevious()
}
My scenario, I am having Tabbar with three viewcontroller. Here, tabbar first viewcontroller I am showing tableview. If I click the tableview cell it will show one popup present model viewcontroller. In this present popup viewcontroller I am maintaining two bar button cancel and done. If I click done It will dismiss and show tabbar main view controller. While dismiss time I need to pass some values with button flag from present popup view controller to tabbar main viewcontroller.
Here, below my dismiss popup pass viewcontroller code (VC 2)
#IBAction func apply_click(_ sender: Any) {
print("Dimiss Filter")
dismiss(animated: true, completion: {
if let navView = self.tabBar?.viewControllers?[0] as? UINavigationController {
if let secondTab = navView.viewControllers[0] as? HomeViewController {
secondTab.selectedIndexFromFirstTab = self.selectedIndex
//secondTab.item = self.item
secondTab.tfData = "YES"
}
}
self.tabBar?.selectedIndex = 0
})
}
Here, Tabbar main view controller code (receiving values) (VC 1)
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("SELECTED INDEX:\(selectedIndexFromFirstTab)")
print("RESPONSE:\(tfData)")
}
I am not receiving values, how to solve this issue.
From my previous answer you need to make few changes to existing code if you want to pass values from your child view to main tab bar controller.
For that first of all you need to declare a method into your main TabBarViewController
func tabPressedWithIndex(index: Int, valueToPass: String) {
print("selected Index: \(index)")
print("Value from user: \(valueToPass)")
}
Now you need to pass that tab bar controller to your detail view with didSelectRowAt method and it will look like:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
vc.selectedIndex = indexPath.row
vc.tabBar = self.tabBarController as? TabBarViewController // Here you need to assign tab bar controller.
self.present(vc, animated: true, completion: nil)
}
Now next thing is when you click on dismiss button from detail view controller you need to add one line below self.tabBar?.selectedIndex = 1:
self.tabBar?.tabPressedWithIndex(index: 1, valueToPass: self.userTF.text!)
Now this will pass the values to main tab bar controller and method tabPressedWithIndex will call and print your data in your main tab.
Check out demo project for more info.
You can achieve it multiple ways. Using blocks/closures, protocols or if you are using RxSwift than using controlled property or using controlled events. Because I can't demonstrate everything here am gonna write protocol
Using Protocol
Step 1:
Declare a protocol in your modal view controller
#objc protocol ModalViewControllerProtocol {
func dismiss(with data: String)
}
Step 2:
ViewController that presents this ModalViewController make it to confirm the protocol
extension HomeViewController: ModalViewControllerProtocol {
func dismiss(with data: String) {
//use the data here
self.presentedViewController?.dismiss(animated: true, completion: nil)
}
}
Step 3:
Declare a variable to hold delegate reference in ModalViewController
weak var delegate: ModalViewControllerProtocol? = nil
Step 4:
In your ViewCOntroller that presents the modalViewController pass self as a delegate to ModalViewController before presenting
let modalVC = //...
modalVC.delegate = self
self.present(modalVC, animated: true, completion: nil)
Finally in IBAction of ModalViewController simply call
#IBAction func apply_click(_ sender: Any) {
self.delegate?.dismiss(with: "your_data_here")
}
Using Block/Closure
Step 1:
In your modal ViewController declare a property that accepts a block/closure
var completionBlock: (((String) -> ()))? = nil
Step 2:
In your ViewController that presents this ModalViewController, pass a block before presenting it
let modalVC = //...
modalVC.completionBlock = {(data) in
debugPrint(data)
self.presentedViewController?.dismiss(animated: true, completion: nil)
}
self.present(modalVC, animated: true, completion: nil)
Step 3:
Finally in your ModalViewController IBAction simply execute the block passed
if let block = completionBlock {
block("your data here")
}
Hope it helps
This is the solution
self.dismiss(animated: true) {
if let tabController = self.presentingViewController as? UITabBarController {
if let navController = tabController.selectedViewController as? UINavigationController {
if let secondTab = navController.viewControllers.first as? HomeViewController {
secondTab.tfData = "YES"
}
} else {
if let secondTab = tabController.selectedViewController as? HomeViewController {
secondTab.tfData = "YES"
}
}
}
}
How to present another view controller after dismiss from navigation controller in swift ?
I am using Navigation controller.
ViewController1.swift
func pushTo(viewController: UIViewController) {
let showLocalPwd = self.storyboard?.instantiateViewController(withIdentifier: "LocalPwdVC") as! LocalPwdVC
self.navigationController?.present(showLocalPwd, animated: true, completion: nil)
}
ViewController2.swift
#IBAction func btnVerify(_ sender: Any)
{
self.dismiss(animated: true, completion: {
let vc = self.storyboard.instantiateViewController(withIdentifier: "DataVC") as! DataVC
self.navigationController.pushViewController(vc, animated: true)
})
}
After dismissing the View Controller, it will not goes to next viewcontroller i.e. DataVC
If you want to present the view controller then create a protocol in your dismissViewController
protocol dismissViewController {
func presentCompletedViewController()
}
// Then create a delegate
var delegate = dismissViewController? = nil
// If you are using action to dismiss your ViewController then call delegate
#IBAction func YourButton(_ sender: Any) {
self.dismiss(animated: true) {
self.delegate!.presentCompletedViewController()
}
}
//And then call this in your main or homeViewController
class HomeViewController: UIViewController, dismissViewController {
func presentCompletedViewController(){
// enter code to present view controller
}
// And at last don't forget to call delegate
yourVc.delegate = self
you need to call this delegate in which you are presenting your dismissViewController
I have several view controllers (UIViewController). Each view controller has its own storyboard. And I want the app to call deinit when it transitions from one view controller to another. But it won't.
The app starts with HomeViewController. And it will transition to SelectViewController when the user taps a button (UIButton).
class HomeViewController: BasicViewController {
#IBAction func selectTapped(_ sender: UIButton) {
let storyboard = UIStoryboard(name:"Select",bundle:nil)
let controller = storyboard.instantiateViewController(withIdentifier: "SelectView") as! UINavigationController
let viewController = controller.topViewController as! SelectViewController
self.navigationController?.pushViewController(viewController, animated: true)
}
deinit {
print("HOME HAS BEEN REMOVED FROM MEMORY")
}
}
The app never calls deinit when it leaves HomeViewController. I wonder if that's because it's pushing a view controller? If I set the view controller as follows, the app will crash. What am I doing wrong? I have never done it with different storyboards. Thanks.
class HomeViewController: BasicViewController {
#IBAction func selectTapped(_ sender: UIButton) {
let storyboard = UIStoryboard(name:"Select",bundle:nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "SelectView") as! SelectViewController
self.navigationController?.setViewControllers([viewController], animated: true)
}
}
UPDATE 1
I guess it's just the following.
#IBAction func selectTapped(_ sender: UIButton) {
let storyboard = UIStoryboard(name:"Select",bundle:nil)
let controller = storyboard.instantiateViewController(withIdentifier: "SelectView") as! UINavigationController
self.present(controller, animated: true, completion: nil)
}
UPDATE 2
I also clear the view controller stack when SelectViewController appears as follows.
override func viewWillAppear(_ animated:Bool) {
super.viewWillAppear(animated)
var navigationArray = self.navigationController?.viewControllers
navigationArray?.removeAll()
}
Actually, It is not about storyboards. As per the Apple's documentation, "deinit" method gets automatically called when an object is deallocated from memory, not when a view controller is pushed from current one.
When you push "SelectViewController" from "HomeViewController", instance of "HomeViewController" does not deallocated from memory, hence deinit method does not called.
Here is a link to Apple's Documentation for Deinitialization. Alternatively, you can use "viewDidDisappear" method of the view controller to perform an operation if it satisfies your need.
From your code snippet, I understand that you expect deinit is called after pushViewController. But, by calling pushViewController, your navigation controller pushes your HomeViewContoller to navigation stack, which means navigation controller holds strong reference to your HomeViewController object. So, deinit is not called. If you don't need HomeViewController, you have to manually remove it from navigation stack w/in SelectViewController.
See this how. How to remove a specific view controller from uinavigationcontroller stack?
In SelectViewController,
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let allControllers = NSMutableArray(array: navigationController!.viewControllers)
allControllers.removeObject(at: allControllers.count - 2)
self.navigationController!.setViewControllers(allControllers as [AnyObject] as! [UIViewController], animated: false)
}
I've been successfully able to implement a popviewcontroller, which contains a tableview of 4 cells. I want to close the popViewController when the cell is selected, but I cannot seem to do this. In my tableViewController, I have a method inside didSelectRowAtIndexPath, which calls this MasterViewController's method "updateToSelectedTab". I am able to print out the indexpath of the selected cell here, but I cannot remove the popViewController when the cell is selected. How do I do this?
//Here is our MasterViewController class
#IBAction func pop(sender: AnyObject) {
let contentView = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("popViewController") as! PopViewController
contentView.modalPresentationStyle = UIModalPresentationStyle.Popover
contentView.preferredContentSize = CGSizeMake(aButton.frame.width, 240.0)
let popoverMenuViewController = contentView.popoverPresentationController!
popoverMenuViewController.delegate = self
popoverMenuViewController.permittedArrowDirections = UIPopoverArrowDirection.Any
popoverMenuViewController.sourceView = view
popoverMenuViewController.permittedArrowDirections = UIPopoverArrowDirection(rawValue:0)
popoverMenuViewController.sourceRect = CGRectMake(aButton.frame.origin.x, aButton.frame.origin.y - 60, aButton.frame.width, aButton.frame.height)
presentViewController(contentView, animated: true, completion: nil)
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.None
}
//This method is called from didSelectRowAtIndexPath of the View Controller that handles the UIPopoverPresentationController
func updateToSelectedTab(tab: Int){
print("Current tab \(tab)")
//Need to dismiss controller here
}
All I had to do was add this code in select row at index path delegate method:
dismissViewControllerAnimated(true, completion: nil)