how to present a popover in custom cell in swift - swift

For presenting popover I am following the below code.
func showPopOver() {
let secondStoryboard = UIStoryboard(name: "Second", bundle: nil)
viewObj = secondStoryboard.instantiateViewControllerWithIdentifier("ViewController") as! ViewController
viewObj.modalPresentationStyle = UIModalPresentationStyle.Popover
viewObj.preferredContentSize = CGSizeMake(400,500)
let popoverPresentationController = viewObj.popoverPresentationController
popoverPresentationController?.delegate = self
popoverPresentationController?.sourceView = self.view //walletButton
popoverPresentationController?.sourceRect = CGRectMake(0, button.frame.origin.y+100, 0, 0)
presentViewController(viewObj, animated: true, completion: nil)
}
//MARK:- UIPopoverPresentationControllerDelegate methods... starts
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle{
return UIModalPresentationStyle.None
}
func prepareForPopoverPresentation(popoverPresentationController: UIPopoverPresentationController) {
print("prepare for presentation")
}
func popoverPresentationControllerDidDismissPopover(popoverPresentationController: UIPopoverPresentationController) {
print("did dismiss")
}
func popoverPresentationControllerShouldDismissPopover(popoverPresentationController: UIPopoverPresentationController) -> Bool {
print("should dismiss")
return false
}
its working fine in normal view controller, but i need to show a popover view in custom tableview cell.
class flow like
|-UITableViewController
|-CustomCell:UITableViewCell
|-UIButton: button action-> here we need to show a popover with 3/4 frame value of the tableview frame.
whenever user tapped out of the popover view then dismiss the popover view.

Related

How to set the PageViewController to cover the whole screen and not be modal?

I am implementing a UIPageViewController to my app to try to build a UI like Tinder, in which you can swipe left and right to not only like or dislike a person, but to navigate different screens, i.e. chat screen, profile screen, matches screen etc.
In my case, after signing in, a UIPageViewController that contains 4 other UIViewControllers will pop up.
However, the UIPageViewController is modal and doesn't cover the whole screen(as there is a small gap at the top which allows the user to swipe the modal down and away).
I tried using code like this:
self.window = UIWindow(frame: UIScreen.main.bounds)
if let window = self.window {
window.rootViewController = PageViewController()
window.makeKeyAndVisible()
}
at my AppDelegate, or setting Full Screen at the storyboard, but did't work.
I wonder how I should do this?
Or maybe UIPageViewController is not the right choice to achieve this swipe from screen to screen navigation style Tinder has?
Anyway, here is the code of my PageViewController:
import UIKit
class PageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var controllers = [UIViewController]()
override func viewDidLoad() {
super.viewDidLoad()
let vc = TodayPicksViewController()
controllers.append(vc)
let vc1 = TopPicksViewController()
vc1.view.backgroundColor = .yellow
controllers.append(vc1)
let vc2 = ChatViewController()
vc2.view.backgroundColor = .gray
controllers.append(vc2)
let vc3 = (storyboard?.instantiateViewController(withIdentifier: String(describing: ProfileViewController.self)) as? ProfileViewController)!
controllers.append(vc3)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
DispatchQueue.main.asyncAfter(deadline: .now()+2, execute: {
self.presentPageVC()
})
}
func presentPageVC() {
guard let first = controllers.first else {
return
}
let vc = UIPageViewController(transitionStyle: .scroll,
navigationOrientation: .horizontal,
options: nil)
vc.delegate = self
vc.dataSource = self
vc.setViewControllers([first],
direction: .forward,
animated: true,
completion: nil)
present(vc, animated: true)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let index = controllers.firstIndex(of: viewController), index > 0 else {
return nil
}
let before = index - 1
return controllers[before]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let index = controllers.firstIndex(of: viewController), index < (controllers.count - 1) else {
return nil
}
let after = index + 1
return controllers[after]
}
}
By default when you present a ViewController in Swift it doesn't cover the fullscreen. To make it cover the fullscreen you need to set the modalPresentationStyle on the ViewController.
So in your presentPageVC method you need to add the following line :
vc.modalPresentationStyle = .fullScreen
So your method will now look like this:
func presentPageVC() {
guard let first = controllers.first else {
return
}
let vc = UIPageViewController(transitionStyle: .scroll,
navigationOrientation: .horizontal,
options: nil)
vc.delegate = self
vc.dataSource = self
vc.setViewControllers([first],
direction: .forward,
animated: true,
completion: nil)
vc.modalPresentationStyle = .fullScreen // <- add this before presenting your ViewController
present(vc, animated: true)
}
To read more about the different presentation styles that you can have check out the documentation here.

How to present viewController from tabBar, like instagram post item in tabBar [Swift]

In my iOS app I've a tabBarController and I want to use one of this buttons to PRESENT a viewController, like for instagram "+" item (when you want to add new posts).
Now I've tried with the following code, but when I dismiss viewController, it go to + page, and I don't want this.
I don't know if I explained myself well, but it works like the button on the instagram tabBar.
class TabBarController: UITabBarController, UITabBarControllerDelegate {
var window: UIWindow!
override func viewDidLoad() {
super.viewDidLoad()
tabBar.tintColor = .white
window = UIApplication.shared.connectedScenes
.filter({$0.activationState == .foregroundActive})
.map({$0 as? UIWindowScene})
.compactMap({$0})
.first?.windows
.filter({$0.isKeyWindow}).first
}
func presentView() {
let storyBoard = UIStoryboard(name: "CreateComment", bundle: nil)
let viewController = storyBoard.instantiateViewController(identifier: "NAV_CONTROLLER")
viewController.modalPresentationStyle = .fullScreen
// I tried to use window because I want to present this viewController over all other viewControllers.
window?.rootViewController = self // tabBarController
window?.makeKeyAndVisible()
present(viewController, animated: true, completion: nil)
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
guard let items = tabBar.items else { return }
switch item {
case items.first: break
case items.second: presentView()
case items.last: break
default: break
}
}
}

iOS 13 - Right Bar Button Item goes offset in Modal Sheet presentation

I have this strange problem with iOS 13 and its new sheet cards style modal presentation.
From ViewController1 I modal present ViewController2 embedded in a NavigationController, and everything works fine.
From ViewController2, I then modal present ViewController3 embedded in a NavigationController, and I get the Right Bar Button offset.
Here is a video of the problem: does anybody have a fix?
Main View Controller
import UIKit
let vc1identifier = "vc1identifier"
let vc2identifier = "vc2identifier"
class ViewController: UIViewController {
#IBAction func tap1(_ sender: UIButton) {
if let navigation = self.storyboard?.instantiateViewController(withIdentifier: vc1identifier) as? UINavigationController,
let nextVC = navigation.contentViewController as? UnoViewController {
//self.navigationController?.pushViewController(nextVC, animated: true)
self.present(navigation, animated: true, completion: nil)
}
}
}
extension UIViewController {
var contentViewController: UIViewController {
if let navcon = self as? UINavigationController {
return navcon.visibleViewController!
} else {
return self
}
}
}
Second View Controller
import UIKit
class UnoViewController: UIViewController {
#IBOutlet weak var barButton: UIBarButtonItem!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
barButton.title = "GotoVC2"
}
#IBAction func pressThis(_ sender: UIBarButtonItem) {
if let navigation = self.storyboard?.instantiateViewController(withIdentifier: vc2identifier) as? UINavigationController,
let nextVC = navigation.contentViewController as? DueViewController {
self.present(navigation, animated: true, completion: nil)
}
}
}
I came across the same issue.
Solution is easy, you just need to tell navigationbar it needs layout like this
override public func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if #available(iOS 13.0, *) {
navigationController?.navigationBar.setNeedsLayout()
}
}

UIPopoverPresentationController popover displays over the entire view

I am trying to create a rightBarButtonItem that appears throughout my app. When this barItem is clicked I want to show a modal popup using UIPopoverPresentationController. I have been able to get the button to show up on the barItem on all the views. However when i click on the button the xib takes over the entire view (including nav bar, see image below). Please see the class below:
class MyAppsNavigationController: UINavigationController, UINavigationControllerDelegate, UIPopoverPresentationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
self.navigationBar.barTintColor = Colors.Red01.color()
self.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName : UIColor.white]
}
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
viewController.navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "Ellipsis"), style: .plain, target: self, action: #selector(displayMenu(sender:)))
}
func displayMenu(sender: UIBarButtonItem)
{
let filterVC = DropdownMenuController(nibName: "DropdownMenuController", bundle: nil)
let nav = UINavigationController(rootViewController: filterVC)
nav.modalPresentationStyle = UIModalPresentationStyle.popover
//nav.isNavigationBarHidden = true
nav.preferredContentSize = CGSize(width: 200, height: 300)
let popover = nav.popoverPresentationController! as UIPopoverPresentationController
popover.permittedArrowDirections = .up
popover.delegate = self
popover.barButtonItem = self.navigationItem.rightBarButtonItem
popover.sourceView = self.view;
var frame:CGRect = (sender.value(forKey: "view")! as AnyObject).frame
frame.origin.y = frame.origin.y+20
popover.sourceRect = frame
popover.delegate = self
self.present(nav, animated: true, completion: nil)
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
}
Result when clicked on the button:
When clicked the popup takes over entire view:
Any chance you're not using the right delegate method? I think this looks better:
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
Also, in this case sourceView and sourceRect is not needed: specifying a barButtonItem for the popover presentation controller is sufficient.
https://developer.apple.com/documentation/uikit/uipopoverpresentationcontroller/1622314-barbuttonitem

popoverController not seguing

Hi I have a popover view that is showing an array. I am wondering if there is a way that I could somehow segue back which item's in the array are selected.
#IBAction func popOverButton(_ sender: UIButton)
{
let controller = TableViewController()
//This is just a regular tableViewController nothing special
controller.modalPresentationStyle = .popover
// configure the Popover presentation controller
let popController: UIPopoverPresentationController? = controller.popoverPresentationController
popController?.permittedArrowDirections = [.down]
popController?.delegate = self
popController?.sourceView = sender
popController?.sourceRect = sender.bounds
popController?.backgroundColor = .white
self.parent?.present(controller, animated: true, completion: { })
}
Here is what it looks like
Any help is appreciated thanks
Easiest way is to create a delegate and when a cell is selected pass the selection back to the presenting view controller. Then setting up the UITableViewDelegate method didSelectRowAtIndexPath to call the delegate method. Something like this:
#protocol PopoverOptionSelectionDelegate {
func itemSelected(item:String);
}
Implement the method in your presenting VC
class PresnetingViewController, PopoverOptionSelectionDelegate {
#IBAction func popOverButton(_ sender: UIButton) {
let controller = TableViewController()
controller.delegate = self //----Important---//
//This is just a regular tableViewController nothing special
controller.modalPresentationStyle = .popover
// configure the Popover presentation controller
let popController: UIPopoverPresentationController? =
controller.popoverPresentationController
popController?.permittedArrowDirections = [.down]
popController?.delegate = self
popController?.sourceView = sender
popController?.sourceRect = sender.bounds
popController?.backgroundColor = .white
self.parent?.present(controller, animated: true, completion: { })
}
func itemSelected(item:String) {
//DISMISS YOUR POPOVER MAYBE AND DO SOMETHING WITH "ITEM" HERE
}
}
class TableViewController,UITableViewDelegate {
weak var delegate:PopoverOptionSelectionDelegate?
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
delegate?.itemSelected(self.itemsArray[indexPath.row])
}
}