Cant dismiss Popover UITableViewController - ios

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
}
}

Related

tableView.selectRow(...) doesn't work when I use UIAdaptivePresentationControllerDelegate (Swift 5)

In my app I use .present(...) to show one View Controller on top of the other (these are the same VC). When I dismiss it (pull down from screen) I want to select some row in my first VC. For it I use UIAdaptivePresentationControllerDelegate in which I have print(...) and tableView.selectRow(...) function. Print works fine, but tableView.selectRow doesn't work. What could be the issue?
extension UIViewController {
class func loadFromStoryboard<T: UIViewController>() -> T {
let name = String(describing: T.self)
let storyboard = UIStoryboard(name: name, bundle: nil)
if let viewController = storyboard.instantiateInitialViewController() as? T {
return viewController
} else {
fatalError("Error: No initial view controller in \(name) stroryboard")
}
}
}
class MyTabBarController: UITabBarController {
let firstVC: FirstVC = FirstVC.loadFromStoryboard()
...
override func viewDidLoad() {
super.viewDidLoad()
loadViewControllers()
}
private func generateViewController(rootViewController: UIViewController, image: UIImage?, title: String) -> UIViewController {
let navigationVC = UINavigationController(rootViewController: rootViewController)
...
return navigationVC
}
private func loadViewControllers() {
viewControllers = [
generateViewController(rootViewController: firstVC, image: ..., title: "...")
]
}
...
private func openVC() {
let storyboard = UIStoryboard(name: "FirstVC", bundle: nil)
let vc = storyboard.instantiateViewController(identifier: "FirstVC") as! FirstVC
vc.presentationController?.delegate = vc
self.present(vc, animated: true, completion: nil)
}
...
}
//FirstVC
extension FirstVC: UIAdaptivePresentationControllerDelegate {
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
print("VC was dismissed")
self.tableView.selectRow(at: IndexPath(row: 0, section: 0), animated: true, scrollPosition: .none)
}
}
In the console I see "VC was dismissed", but row doesn't select.
P.S. I use Xcode 12.2 and iOS 14.2.
The tableView is an IBOutlet, if it's important.
You should set:
vc.presentationController?.delegate = self
Instead of:
vc.presentationController?.delegate = vc
Because you lose delegate as soon as you dismiss vc. You should listen dismissal of FirstVC on self where you present FirstVC.
If you just miss that it's ok. But if you can't understand why I can share more details :)

Swift Navigate from third ViewController to the first ViewController

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()
}

Present UIViewController as a popover [duplicate]

I am trying to make a popover menu with the following code:
import UIKit
class BeobachtungViewController: UIViewController, UIPopoverPresentationControllerDelegate {
#IBAction func addClicked(_ sender: AnyObject) {
// get a reference to the view controller for the popover
let popController = UIStoryboard(name: "Personenakte", bundle: nil).instantiateViewController(withIdentifier: "popoverId")
// set the presentation style
popController.modalPresentationStyle = UIModalPresentationStyle.popover
// set up the popover presentation controller
popController.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.up
popController.popoverPresentationController?.delegate = self
popController.popoverPresentationController?.sourceView = sender as! UIView // button
popController.popoverPresentationController?.sourceRect = sender.bounds
// present the popover
self.present(popController, animated: true, completion: nil)
}
// UIPopoverPresentationControllerDelegate method
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
// Force popover style
return UIModalPresentationStyle.none
}
}
This is working on iPad, but, on an iPhone, the popup takes the whole iPhone screen. I just want a small window with an arrow. I found several tutorials but none worked for me.
Change your delegate method to:
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
// return UIModalPresentationStyle.FullScreen
return UIModalPresentationStyle.none
}

Can we handle the error "Cannot convert the value of type BOOL to expected argument UIiew"

I am tired of trying to PopOver the view controller and searched every where, tried myself also.This issue has again arrived and i am confused what to do next
func showPopover(base: UIView)
{
let storyboard : UIStoryboard = UIStoryboard(name: "Messaging", bundle: nil)
if let viewController = storyboard.instantiateViewControllerWithIdentifier("PreferencesViewController") as?PreferencesViewController
{
let navController = UINavigationController(rootViewController: viewController)
navController.modalPresentationStyle = .Popover
if let pctrl = navController.popoverPresentationController
{
pctrl.delegate = self
pctrl.sourceView = base
pctrl.sourceRect = base.bounds
self.presentViewController(navController, animated: true, completion: nil)
}
}
}
I am calling this method in any one of the actions clicked from UIBarButtons
func optionChoosed(hello:Bool)
{
if (hello)
{
self.showPopover(hello)
}
}
it says Cannot convert the value of type BOOL to expected argument UIiew.. can we fix this or am i going wrong direction.
class SHNewStylesViewController: UIViewController, UIPopoverPresentationControllerDelegate {
var popover: UIPopoverPresentationController? = nil
//MARK: - View life cycle
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
//MARK: - IBActions
#IBAction func showPopover(sender: AnyObject) {
let genderViewController = storyboard!.instantiateViewControllerWithIdentifier("ViewControllerTwoIdentifier") as! ViewControllerTwo
genderViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
genderViewController.preferredContentSize = CGSize(width: 200.0, height: 400.0) // To change the popover size
popover = genderViewController.popoverPresentationController!
popover!.barButtonItem = sender as? UIBarButtonItem
popover!.delegate = self
presentViewController(genderViewController, animated: true, completion:nil)
}
//MARK: - Popover Presentation Controller Delegate methods
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.None
}
}
In my case showPopover is a IBAction of my bar button item. You can you that code inside the showPopover method wherever you want.
Thanks:)

How to dismiss UIPopoverPresentationController on tap inside it. (iOS, Swift)

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)

Resources