Present Popover View Controller Swift - ios

When I try to display a popover view controller programmatically it won't work and I don't know why. I've copied from multiple sources on the web and nothing seems to work, I get the same error in the console every time showing Warning: Attempt to present <AddFriendsPopoverViewController> on <MainPageViewController> whose view is not in the window hierarchy! I am lost and can't seem to figure out what the problem is, thanks in advance!
Here is my swift code in my viewDidLoad() function:
let addFriendsPopoverViewController = AddFriendsPopoverViewController()
override func viewDidLoad() {
super.viewDidLoad()
if (PFUser.currentUser()?["numberOfFriends"])! as! NSObject == 0 {
print(PFUser.currentUser()?["numberOfFriends"])
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("AddFriendsPopoverViewController") as! UIViewController
vc.modalPresentationStyle = UIModalPresentationStyle.Popover
vc.preferredContentSize = CGSizeMake(50, 50)
let popoverMenuViewController = vc.popoverPresentationController
popoverMenuViewController!.permittedArrowDirections = .Any
popoverMenuViewController!.delegate = self
popoverMenuViewController!.sourceView = self.view
popoverMenuViewController!.sourceRect = CGRectMake(
100,
100,
0,
0)
self.presentViewController(vc, animated: true, completion: nil)
}
}
EDIT
I figured out that for a popover to work with iPhone the following code is required.
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController!) -> UIModalPresentationStyle {
// Return no adaptive presentation style, use default presentation behaviour
return .None
}

Your view is not in the view hierarchy until it has been presented and not during viewDidLoad:
Move your code to viewDidAppear:
if (PFUser.currentUser()?["numberOfFriends"])! as! NSObject == 0 {
addFriendsPopoverViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
addFriendsPopoverViewController.preferredContentSize = CGSizeMake(200, 200)
let popoverMenuViewController = addFriendsPopoverViewController.popoverPresentationController
popoverMenuViewController!.permittedArrowDirections = .Any
popoverMenuViewController!.delegate = self
popoverMenuViewController!.sourceView = self.view
popoverMenuViewController!.sourceRect = CGRect(
x: 50,
y: 50,
width: 1,
height: 1)
presentViewController(
addFriendsPopoverViewController,
animated: true,
completion: nil)
}

Your code is working right but u can not write that presentViewController code in ViewDidLoad method because viewdidLoad call till that time controller itself it not presented thats why it's not allow to presentViewController .
Write that same code in..
override func viewDidAppear(animated: Bool)
{
var controller = UIViewController()
controller.view.backgroundColor = UIColor .greenColor()
presentViewController(controller, animated: true, completion: nil)
}

I have made it simple for multiple use and its ready to use and go. just copy and paste this extension.
extension UIViewController: UIPopoverPresentationControllerDelegate{
#discardableResult func presentPopOver(_ vcIdentifier: String, _ isAnimate: Bool = true,sender:UIView,contentSize:CGSize = .init(width: 100, height: 100)) -> (UIViewController){
let popoverContentController = storyboard?.instantiateViewController(withIdentifier: vcIdentifier)
popoverContentController?.modalPresentationStyle = .popover
if let popoverPresentationController = popoverContentController?.popoverPresentationController {
popoverPresentationController.permittedArrowDirections = .up
popoverPresentationController.sourceView = sender
popoverPresentationController.sourceRect = sender.bounds
popoverContentController?.preferredContentSize = contentSize
popoverPresentationController.delegate = self
if let popoverController = popoverContentController {
present(popoverController, animated: isAnimate, completion: nil)
}
}
return popoverContentController ?? UIViewController()
}
public func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
public func popoverPresentationControllerDidDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) {
}
public func popoverPresentationControllerShouldDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) -> Bool{
return true
}}
**how to use for presenting pop over on button click:-**
#IBAction func sortClicked(_ sender: UIButton) {
presentPopOver("PopOverVC", sender: sender)
}
***presenting pop over and to get and pass data:-***
#IBAction func sortClicked(_ sender: UIButton) {
let vc = presentPopOver("PopOverVC", sender: sender) as? PopOverVC
vc?.arrayNames = ["name1","name2"]
vc?.callBack = {name in
print(name)
vc?.dismiss(animated: true)
}
}

Related

How to show custom view on each screen on dismiss of a controller in swift?

I am trying to show a custom view on a dismiss of a controller. It's working fine if I am sowing it on popViewController but not working when I am using dismiss method. If anybody knows the solution please help me out.
// presenting screen from HomeController
guard let controller = UIStoryboard(name: "EmptyWorkout", bundle: nil).instantiateViewController(withIdentifier: "LogWoroutVC") as? LogWorkoutViewController else { return }
controller.modalPresentationStyle = .overFullScreen
self.present(controller, animated: true, completion: nil)
// dimissing screen from LogWorkoutViewController
#IBAction func backButtonAction(_ sender: UIButton) {
let window = UIApplication.shared.windows.last
let timerView = UINib(nibName: "WorkoutTimer", bundle: nil).instantiate(withOwner: nil, options: nil).first as? WorkoutTimer
if let timerView = timerView {
let screenSize = UIScreen.main.bounds
let screenWidth = screenSize.width
if let tabBarYPosition = self.tabBarController?.tabBar.frame.origin.y {
let yPosition = tabBarYPosition - 60
timerView.frame = CGRect(x: 0, y: yPosition, width: screenWidth, height: 60)
window?.addSubview(timerView)
}
}
self.dismiss(animated: true, completion: nil)
}
According to the docs, the presenting controller is responsible for the actual dismiss. When the presented controller dismisses itself, it will ask the presenter to do it for it. In here you need to use the closure or protocol. for e.g use like
on your LogWorkoutViewController VC / presented VC declare the variable as like
var dismissedVC : ((Bool) -> Void)?
There is a Boolean property inside UIViewController called isBeingDismissed that you can use for this purpose:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if isBeingDismissed {
// TODO: called the closures
dismissedVC!(true)
}
}
on your button action call only dismiss VC
#IBAction func backButtonAction(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
on your HomeController when you present call the closure also.
guard let controller = UIStoryboard(name: "EmptyWorkout", bundle: nil).instantiateViewController(withIdentifier: "LogWoroutVC") as? LogWorkoutViewController else { return }
controller.modalPresentationStyle = .overFullScreen
controller.dismissedVC = { result in
// call your WorkoutTimer view
self.workoutTimer()
}
self.present(controller, animated: true, completion: nil)
func workoutTimer() {
let window = UIApplication.shared.windows.last
let timerView = UINib(nibName: "WorkoutTimer", bundle: nil).instantiate(withOwner: nil, options: nil).first as? WorkoutTimer
if let timerView = timerView {
let screenSize = UIScreen.main.bounds
let screenWidth = screenSize.width
if let tabBarYPosition = self.tabBarController?.tabBar.frame.origin.y {
let yPosition = tabBarYPosition - 60
timerView.frame = CGRect(x: 0, y: yPosition, width: screenWidth, height: 60)
window?.addSubview(timerView)
}
}
}

How to present some VC at modalVC's parent VC without hierarchy warnings?

I try to present VCs but buttons in popover menu, but I have hierarchy warnings like this:
Warning: Attempt to present "UIViewController: 0x14def7500" on "MyProject.MainViewController: 0x14f976400" whose view is not in the window hierarchy!
I have MainViewController and PopupMenu VCs classes:
Swift 4.0
class MainViewController: UIViewController, UIPopoverPresentationControllerDelegate {
//... here is my VC code
// showing Popup Menu VC
#IBAction func showPopupMenu(sender: UIButton) {
menuVC = PopupMenu()
menuVC?.modalPresentationStyle = .popover
menuVC?.preferredContentSize = CGSize(width: 150, height: 250)
if let pvc = menuVC?.popoverPresentationController {
pvc.permittedArrowDirections = .up
pvc.delegate = self
pvc.sourceView = sender
pvc.sourceRect = sender.bounds
}
self.present(menuVC!, animated: true, completion: nil)
}
// showing VC from popupMenu VC
#IBAction func showVCFromPopup(from target: PopupMenu, vc: UIViewController) {
target.dismiss(animated: false, completion: nil) // dismiss popup
if target.isBeingDismissed { // check is popup dismissed
vc.modalPresentationStyle = .overCurrentContext
self.present(vc, animated: true, completion: nil)
}
}
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
return .none
}
}// end of class
class PopupMenu: UIViewController {
var button = UIButton()
// here is init's
override func viewDidLoad() {
//... some other code
button.addTarget(self, action: #selector(vcOpen(sender:)), for: .touchUpInside)
}
#IBAction func vcOpen(sender: UIButton) {
if sender == button {
let vc = UIViewController()
if parent != nil { print("PARENT")} // Never will work, no ideas why, so MainVC isn't a parent of PopupMenu
if let mainVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MainViewController") as? MainViewController {
print("# ACTION: Opening VC")
mainVC.showVCFromPopup(target: self, as: vc!) // opening VC
}
}
}
}
But I have warning.
Maybe anyone will find mistakes in my code or have any ideas how to do this.
Thanks for all answers!
I edited you code to pass a reference of the mainVC to the PopupMenu:
class MainViewController: UIViewController, UIPopoverPresentationControllerDelegate {
// showing Popup Menu VC
#IBAction func showPopupMenu(sender: UIButton) {
menuVC = PopupMenu()
menuVC?.modalPresentationStyle = .popover
menuVC?.preferredContentSize = CGSize(width: 150, height: 250)
menuVC?.MainVC = self <--- here
if let pvc = menuVC?.popoverPresentationController {
pvc.permittedArrowDirections = .up
pvc.delegate = self
pvc.sourceView = sender
pvc.sourceRect = sender.bounds
}
self.present(menuVC!, animated: true, completion: nil)
}
}
class PopupMenu: UIViewController {
var mainVC: UIViewController <-- here
#IBAction func vcOpen(sender: UIButton) {
if sender == button {
mainVC.showVCFromPopup(target: self, as: vc!) <-- here
}
}
}

UIPopoverPresentationController on iPhone with iOS 10

I'm trying to display a ViewController as a popover on an iPhone. I have already been through several answers on SO and the rest of the web but none have worked so far. I wrote a simple app to test this.
ViewController.swift:
import UIKit
class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(clicked(_:)))
}
func clicked(_ sender: Any) {
let vc = UIViewController()
vc.view.backgroundColor = UIColor.blue
vc.preferredContentSize = CGSize(width: 200, height: 200)
vc.modalPresentationStyle = .popover
present(vc, animated: true, completion: nil)
let ppc = vc.popoverPresentationController
ppc?.permittedArrowDirections = .any
ppc?.delegate = self
ppc?.barButtonItem = navigationItem.rightBarButtonItem
}
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
return .none
}
}
The storyboard has an empty ViewController embedded in a NavigationController.
Running this, I expected a popover view controller to show under the "done" button. Instead, the blue view controller is presented full screen.
Is there a way to change this behaviour?
You are connecting delegate after presenting view. How it will return .none from delegate and show as popover. Use this :-
func clicked(_ sender: Any) {
let vc = UIViewController()
vc.view.backgroundColor = UIColor.blue
vc.modalPresentationStyle = .popover
vc.preferredContentSize = CGSize(width: 200, height: 200)
let ppc = vc.popoverPresentationController
ppc?.permittedArrowDirections = .any
ppc?.delegate = self
ppc?.barButtonItem = navigationItem.rightBarButtonItem
ppc?.sourceView = sender
present(vc, animated: true, completion: nil)
}
import UIKit
class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(clicked(_:)))
}
func clicked(_ sender: Any) {
let vc = UIViewController()
vc.view.backgroundColor = UIColor.blue
vc.preferredContentSize = CGSize(width: 200, height: 200)
vc.modalPresentationStyle = .popover
let ppc = vc.popoverPresentationController
ppc?.permittedArrowDirections = .any
ppc?.delegate = self
ppc!.sourceView = sender as? UIView
ppc?.barButtonItem = navigationItem.rightBarButtonItem
present(vc, animated: true, completion: nil)
}
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
}
The solutions above no longer work in recent iOS versions 12 and above. To get it working again, override -modalPresentationStyle within the viewController to be presented as a popover and return UIModalPresentationPopover.
Additionally provide a popoverPresentationController.delegate, implement adaptivePresentationStyleForPresentationController:traitCollection: and return UIModalPresentationNone.
#interface PopoverViewController : UIViewController
#end
#implementation PopoverViewController
- (UIModalPresentationStyle)modalPresentationStyle
{
return UIModalPresentationPopover;
}
#end
#interface ViewController ()<UIPopoverPresentationControllerDelegate>
#end
#implementation ViewController
- (IBAction)openPopover:(id)sender
{
UIViewController* testVC = [[PopoverViewController alloc] init];
testVC.view.backgroundColor = UIColor.yellowColor;
UIPopoverPresentationController* popPresenter = [testVC popoverPresentationController];
popPresenter.permittedArrowDirections = UIPopoverArrowDirectionUp;
popPresenter.delegate = self;
popPresenter.sourceView = sender;
popPresenter.sourceRect = [sender bounds];
[self presentViewController:testVC animated:YES completion:^{}];
}
#pragma mark protocol (UIPopoverPresentationControllerDelegate)
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection
{
return UIModalPresentationNone;
}
#end
Add:
vc.popoverPresentationController?.delegate = self
just before the line:
present(vc, animated: true, completion: nil)

Swift 3 - adaptivePresentationStyle is never called

I am trying to show a popover in an iPhone. I am following the suggestions that i found here and using the delegate "adaptivePresentationStyle", but this function is never called and the ViewController it will always presented in fullscreen mode. I have the "UIPopoverPresentationControllerDelegate" and the functions bellow:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let identifier = segue.destination.restorationIdentifier ?? ""
if identifier == "NavigationSetup" {
if let destinationNav = segue.destination as? UINavigationController {
let destination = destinationNav.topViewController as! SetupTableViewController
destination.popoverPresentationController?.delegate = self
destination.popoverPresentationController?.backgroundColor = UIColor.blue
if self.myApp.isIpad{
destination.preferredContentSize = CGSize(width: 600, height: 620)
}else{
destination.preferredContentSize = CGSize(width: 0.8 * self.view.frame.size.width, height: 0.8 * self.view.frame.size.height)
}
self.cellAnimations.fade(image: self.imageBlur, initOpacity: 0, endOpacity: 1, time: 0.3, completion: nil)
destination.setupDismiss = {[weak self] () in
if let weakSelf = self{
weakSelf.cellAnimations.fade(image: weakSelf.imageBlur, initOpacity: 1, endOpacity: 0, time: 0.3, completion: nil)
}
}
}
}
}
func adaptivePresentationStyle(for controller:UIPresentationController) -> UIModalPresentationStyle {
print("adaptive was called")
return .none
}
So, what i am missing here ?
First, set a breakpoint to make sure this line is being called:
destination.popoverPresentationController?.delegate = self
Even better, rewrite like this, and set a breakpoint to make sure the inner line is being called:
if let pop = destination.popoverPresentationController {
pop.delegate = self
}
If it is, good! In that case the problem is probably that implementing the wrong delegate method. You want this:
func adaptivePresentationStyle(for controller: UIPresentationController,
traitCollection: UITraitCollection) -> UIModalPresentationStyle {
Maybe you did not present it well
let vc = UIViewController()
vc.modalPresentationStyle = .custom;
vc.transitioningDelegate = self;
self.present(vc, animated: true, completion: nil)
Presenting with custom Modal presentation and making it animated while presenting should do the trick

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:)

Resources