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)
Related
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"
}
}
}
}
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()
})
I have a table view using a custom cell (xib).
This custom cell have an image and a description.
If user tapped the image (not the cell) , it should display other page/view.
this is the function called when image tapped
#objc func imageTapped(){
let sb = UIStoryboard(name: "SbIdentifier", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: "ProfileViewController") as! ProfileViewController
print("image tapped")
//self.window?.rootViewController?.parent?.navigationController?.pushViewController(vc, animated: true)
}
I tried used the push view controller but nothing happened.
You can use delegate pattern here to handle tap on profile image in tableViewCell to push ProfileViewController.
First create the CellImageTappableDelegate protocol
protocol CellImageTappableDelegate: class {
func didTapProfileImage(with imageUrl: String)
}
then create the delegate property in Your CustomCell
weak var imageTapDelegate: CellImageTappableDelegate?
now enable isUserInteractionEnabled property on UIImageView
image.isUserInteractionEnabled = true
then add the tapGestureRecognizer to your imageView and implement the selector
let tapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(CustomProfileCell.didTapProfileImage(_:)))
profileImageView.addGestureRecognizer(tapGestureRecognizer)
func didTapProfileImage(_ tapGesture: UITapGestureRecognizer) {
self.imageTapDelegate?.didTapProfileImage(with: self.profileImageUrl)
}
in tableView datasource cellForRowAt: method in your UIViewController subclass assign the delegate to self and implement the protocol method
cell.imageTapDelegate = self
func didTapProfileImage(with imageUrl: String) {
print("Profile image tapped")
let storyboard = UIStoryboard(name: "SbIdentifier", bundle: nil)
let profileVC = storyboard.instantiateViewController(withIdentifier: "ProfileViewController") as! ProfileViewController
profileVC.imageUrl = imageUrl
self.navigationController?.pushViewController(profileVC, animated: true)
}
RootViewController would not have a parent, which would definitely not have a navigationController. If the navigationController is the first viewController in your hierarchy, you can try:
(self.window?.rootViewController as? UINavigationController)?.pushViewController(vc, animated: true)
I have a UITableViewController displayed as a popover as follows
popoverViewController = self.storyboard?.instantiateViewController(withIdentifier: "ItemsTableView") as? ItemsTableViewController
popoverViewController?.itemeSelectionDelegate = self
popoverViewController?.modalPresentationStyle = .popover
let popoverPresentationViewController = popoverViewController?.popoverPresentationController
let itemCell = cell as! ItemContentCell
popoverPresentationViewController?.permittedArrowDirections = UIPopoverArrowDirection.any
popoverPresentationViewController?.sourceView = cell
popoverPresentationViewController?.sourceRect = itemCell.itemName.bounds
present(popoverViewController!, animated: true, completion: nil)
popoverViewController is a member var, I am using it to dismiss when from popoverViewController an item is selected, I am calling a custom delegate method from which I am dismissing it using "Dismiss" method but it sometimes works sometimes it doesn't.
func itemSelected(item: Item) {
print("selected item")
popoverViewController?.dismiss(animated: false, completion: nil)
}
Is there any other method to dismiss popover ?
You have to implement UIPopoverPresentationControllerDelegate method public func popoverPresentationControllerShouldDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) -> Bool.
Here is demo implementation.
extension YouViewController: UIPopoverPresentationControllerDelegate {
func popoverPresentationControllerShouldDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) -> Bool {
return true
}
}
Summary:
I am able to get a full-screen popover but NOT a custom-size popover through a button action within a custom table cell. I am able to get it working though when the button is not within a custom cell. Upon debugging it seems that when the button is within the custom table cell the delegate object of the popoverPresentationController is not being called which is the one that resizes the popover window.
Details:
Following is the code that works when button is outside of UITableViewCell:
import UIKit
import AVKit
import CoreGraphics
class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
// This delegate function ensures that popup size is constrained to
// what's specified in preferredContentSize earlier.
return UIModalPresentationStyle.none
}
// This is triggered by a button in UIViewController.. nothing to do
// with the UITable here. This implementation works.
#IBAction func buttonpressed(_ sender: AnyObject) {
let sb = self.storyboard
let vc: AnyObject! = sb!.instantiateViewController(withIdentifier: "popover")
let pc = vc as! PopOverWindow
pc.modalPresentationStyle = UIModalPresentationStyle.popover
pc.preferredContentSize = CGSize(width: 100, height: 100)
// Needs to be called BEFORE we access
// popoverPresentationController below. Counter-intuitive but this
// is how apple docs recommend it
self.present(pc, animated: true, completion: nil)
let popover = pc.popoverPresentationController
popover?.permittedArrowDirections = UIPopoverArrowDirection.any
popover?.delegate = self
let button = sender as! UIButton
// Need both statements below so that popover knows where to be
// tethered
popover?.sourceView = sender as? UIView
// the position of the popover where it's showed
// do not use buttons.frame
popover?.sourceRect = button.bounds
}
Important function here is adaptivePresentationStyle() which makes sure that the popup is not full screen.
Now, let's try to the same in a custom UITableViewCell. Biggest change here is that UITableViewCell is NOT a descendant of UIViewController
#IBAction func infoButtonTouchDown(sender: AnyObject) {
let sb = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let vc: AnyObject! = sb.instantiateViewControllerWithIdentifier("popover")
let pc = vc as! PopOverWindow
pc.modalPresentationStyle = .Popover
pc.preferredContentSize = CGSizeMake(50,100)
// Custom View Cell is not UIViewController so I need to following
// to be able to call presentViewController later
// I could have also done
//self.window!.rootViewController?.presentViewController(..)
// but the results were the same
let t = self.window?.rootViewController as! UITabBarController
let n = t.selectedViewController as! UINavigationController
let m = n.visibleViewController as! MyTableViewController
// Important: this needs to be called BEFORE we access
// pc.popoverPresentationController object. Apple Docs say it.
m.presentViewController(pc, animated: true, completion: nil)
let popover = pc.popoverPresentationController
popover?.permittedArrowDirections = UIPopoverArrowDirection.Any
// I tried doing popover?.delegate = d to make the underlying table
// as the delegate object but the results were same
popover?.delegate = self
let viewForSource = sender as! UIView
popover?.sourceView = m.view
// the position of the popover where it's showed
// just some random position in view.
popover?.sourceRect = CGRectMake(100,100,1,1)
}
The TROUBLE in above code is that the delegate function adaptivePresentationStyle() is not being called and consequently I end up getting a full-screen popover and not a half-screen.
Any help is hugely appreciated!