Popover presentation controller - view has spacing around, doesn't fit full width - ios

I am having an issue while displaying UITableViewController as a popover. The popover appears but its width in not full screen - there is some kind of margin on leading and trailing side. Check screenshot for more info:
This is the code I am using to setup and present controller:
let controller = UITableViewController()
controller.preferredContentSize = CGSize(width: self.view.bounds.width, height: self.popOverPresentationControllerHeight)
controller.modalPresentationStyle = .popover
controller.tableView.separatorStyle = .none
controller.tableView.allowsMultipleSelection = true
controller.tableView.dataSource = self.filterOptionsDataSource
controller.tableView.delegate = self
controller.tableView.isScrollEnabled = false
controller.tableView.register(FilterOptionCell.self, forCellReuseIdentifier: FilterOptionCell.identifier)
controller.tableView.register(UINib(nibName: FilterOptionCell.identifier, bundle: nil), forCellReuseIdentifier: FilterOptionCell.identifier)
controller.tableView.register(FilterConfirmCell.self, forCellReuseIdentifier: FilterConfirmCell.identifier)
controller.tableView.register(UINib(nibName: FilterConfirmCell.identifier, bundle: nil), forCellReuseIdentifier: FilterConfirmCell.identifier)
controller.popoverPresentationController?.popoverLayoutMargins = UIEdgeInsetsMake(0, 0, 0, 0)
controller.popoverPresentationController?.delegate = self
self.present(controller, animated: true, completion: {
UIView.animate(withDuration: 0.25) {
controller.view.superview?.layer.cornerRadius = 4
self.view.alpha = 0.4
}
})
controller.popoverPresentationController?.backgroundColor = UIColor.white
controller.popoverPresentationController?.sourceView = sender
controller.popoverPresentationController?.sourceRect = sender.bounds
There is also implemented adaptivePresentationStyle function but still, no difference:
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
return UIModalPresentationStyle.none
}
ADDITIONAL QUESTION
Is there any way top arrow can be changed / modified? This arrow seems a bit too big for my use so is there a way to make it smaller or just replace it with custom one?
EDIT
After some investigating and printing out controller.view.superview?.frame I noticed its width is 20 points smaller than it should be which explains that space on both sides of the popup. But what's the way to get rid of it?

you can try this.
controller.popoverLayoutMargins = UIEdgeInsets(top: 0, left: 1, bottom: 0, right: 1)
Using this property, you can apply a margin from the end of the screen.
https://developer.apple.com/documentation/uikit/uipopovercontroller/1624657-popoverlayoutmargins?language=objc

You should try this code,
override open func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
var size = self.tableView.contentSize
size.width = 150.0 //Here, you can set the width.
self.preferredContentSize = size
}
and for brief understanding, how i create and present a popoverController.
#objc open static func instantiate() -> PopOverViewController {
let storyboardsBundle = getStoryboardsBundle() //Or Bundle.main
let storyboard:UIStoryboard = UIStoryboard(name: "PopOver", bundle: storyboardsBundle)
let popOverViewController:PopOverViewController = storyboard.instantiateViewController(withIdentifier: "PopOverViewController") as! PopOverViewController
popOverViewController.modalPresentationStyle = UIModalPresentationStyle.popover
popOverViewController.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.up
// arrow color
popOverViewController.popoverPresentationController?.backgroundColor = UIColor.darkGray
return popOverViewController
}
To Present in a Specific ViewController,
let popover = PopOverViewController.instantiate()
popover.popoverPresentationController?.sourceView = self.underlyingTextView
popover.popoverPresentationController?.sourceRect = rect
popover.presentationController?.delegate = self
viewController.present(popover, animated: true, completion: nil)
//MARK: UIAdaptivePresentationControllerDelegate
public func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.none
}
public func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
return UIModalPresentationStyle.none
}
In ViewDidLoad(), you can configure your tableView.

Related

How to change label text of a label in a popover view controller when pressing a button in the main view?

I'm relatively new to Swift. I have a main view controller, ViewControllerMain, and a popover view controller, PopUpVC, which only has a label. I have a function, infoClicked that displays the popover with another function (showPopover) when the info button is clicked. When I click the button, I want to change the label text of the popover. However, with the current code, the label always displays "Default".
Here is my code:
class ViewControllerMain: UIViewController, UIPopoverPresentationControllerDelegate, GetTimesUsed {
let tipController: PopUpVC = PopUpVC().self
#IBAction func infoClicked(_ sender: UIButton) {
tipController.tipText = "Success"
showPopover()
}
func showPopover() {
let myViewController = storyboard?.instantiateViewController(withIdentifier: "popupController")
myViewController?.preferredContentSize = CGSize(width: 350, height: 200)
myViewController?.modalPresentationStyle = .popover
let popOver = myViewController?.popoverPresentationController
popOver?.delegate = self
UIView.animate(withDuration: 1, animations: {
self.GifView.alpha = 0.7
})
DispatchQueue.main.asyncAfter(deadline: .now() + 0.45) {
UIView.animate(withDuration: 0.5, animations: {
self.present(myViewController!, animated: true, completion: nil)
})
}
popOver?.permittedArrowDirections = .down
popOver?.sourceView = self.view
var passthroughViews: [AnyObject]?
passthroughViews = [infoButton]
myViewController?.popoverPresentationController?.passthroughViews = (NSMutableArray(array: passthroughViews!) as! [UIView])
popOver?.sourceRect = infoButton.frame
}
}
class PopUpVC: UIViewController {
#IBOutlet weak var tip: UILabel!
var tipText: String = "Default"
override func viewDidLoad() {
super.viewDidLoad()
tip.text = tipText
// Do any additional setup after loading the view.
}
}
Thank you for your help.
As mentioned in comments, you seem to be instantiating the popup controller 2 times, so try it like this in your showPopOver code:
let myViewController = storyboard?.instantiateViewController(withIdentifier: "popupController") as! PopUpVC
myViewController.preferredContentSize = CGSize(width: 350, height: 200)
myViewController.modalPresentationStyle = .popover
myViewController.tipText = '' //set to what ever

How to create a short modal overlay like the iOS Share Sheet menu?

I’m trying to create a custom, short menu that slides up from the bottom of the screen and stays at the bottom (like the iOS share sheet). I’m having a hard time trying to figure out how to do it. I tried presenting a view controller as a modal and setting the preferred content size, but it still presents it as full screen. How can I present a short, modal-like overlay?
You could use a UIPresentationController and a UIViewControllerTransitioningDelegate.
As a starting point here a few lines of code:
UIViewControllerTransitioningDelegate
class OverlayTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return OverlayPresentationController(presentedViewController:presented, presenting:presenting)
}
}
UIPresentationController
class OverlayPresentationController: UIPresentationController {
private let dimmedBackgroundView = UIView()
private let height: CGFloat = 200.0
override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(backgroundTapped))
self.dimmedBackgroundView.addGestureRecognizer(tapGestureRecognizer)
}
override var frameOfPresentedViewInContainerView: CGRect {
var frame = CGRect.zero
if let containerBounds = containerView?.bounds {
frame = CGRect(x: 0,
y: containerBounds.height - height,
width: containerBounds.width,
height: height)
}
return frame
}
override func presentationTransitionWillBegin() {
if let containerView = self.containerView, let coordinator = presentingViewController.transitionCoordinator {
containerView.addSubview(self.dimmedBackgroundView)
self.dimmedBackgroundView.backgroundColor = .black
self.dimmedBackgroundView.frame = containerView.bounds
self.dimmedBackgroundView.alpha = 0
coordinator.animate(alongsideTransition: { _ in
self.dimmedBackgroundView.alpha = 0.5
}, completion: nil)
}
}
override func dismissalTransitionDidEnd(_ completed: Bool) {
self.dimmedBackgroundView.removeFromSuperview()
}
#objc private func backgroundTapped() {
self.presentedViewController.dismiss(animated: true, completion: nil)
}
}
How to call it
let overlayTransitioningDelegate = OverlayTransitioningDelegate()
#IBAction func onOpenModalOverlay(_ sender: Any) {
let overlayVC = OverlayViewController()
overlayVC.transitioningDelegate = self.overlayTransitioningDelegate
overlayVC.modalPresentationStyle = .custom
self.present(overlayVC, animated: true, completion: nil)
}
Demo
The OverlayViewController is a normal ViewController. Here I used an ugly green background color to make it easier to recognize the overlay.

subview scroll is not working SWIFT

Goal: to make a viewcontroller have multiple pages and can be swapped through a segmented controller, pages content are scrollable vertically
details:
I made a pagviewcontroller and embedded it as a subview to main viewcontroller
//add pageviewcontroller as subview to viewcontroller
if let vc = storyboard?.instantiateViewControllerWithIdentifier("ProfileEditController"){
self.addChildViewController(vc)
self.view.addSubview(vc.view)
EditTabs = vc as! UIPageViewController
EditTabs.dataSource = self
EditTabs.delegate = self
//define First page
EditTabs.setViewControllers([pagesAtIndexPath(0)!], direction:.Forward, animated: true, completion: nil)
EditTabs.didMoveToParentViewController(self)
//bring segmented view buttons to front of pageViews
self.view.bringSubviewToFront(self.topTabs)
}
I called pageViewController functions, and I am adding pages through restoration Identifiers
I managed segmented view controller by getting pageindex and setting viewcontroller like this:
EditTabs.setViewControllers([pagesAtIndexPath(0)!], direction:.Reverse, animated: true, completion: nil)
in story board the sub pages has scroll view inside to hold the content
I tested subpages scroll view by calling it through segue and its working fine
Case:
everything work fine Only scroll view of subpages are not working at all
How to solve this issue?
your guidelines will be much appreciated
Thanks,
I have created a view controller with PageViewController with three View Controllers that have scroll view in it. It works fine.
import UIKit
class ViewController: UIViewController, UIPageViewControllerDataSource {
var viewControllers : [UIViewController]?
override func viewDidLoad() {
super.viewDidLoad()
CreatePageView()
}
func CreatePageView() {
SetupViewControllers()
let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
pageViewController.dataSource = self
pageViewController.setViewControllers([(viewControllers?[0])!] , direction: .forward, animated: false, completion: nil)
pageViewController.view.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height);
pageViewController.view.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height);
addChildViewController(pageViewController)
view.addSubview(pageViewController.view)
pageViewController.didMove(toParentViewController: self)
pageViewController.view.backgroundColor = UIColor.blue
}
func SetupViewControllers() {
let firstVC = UIViewController()
firstVC.view.tag = 100
firstVC.view.backgroundColor = UIColor.red
AddScrollView(bgView: firstVC.view)
let secondVC = UIViewController()
secondVC.view.tag = 101
secondVC.view.backgroundColor = UIColor.brown
AddScrollView(bgView: secondVC.view)
let thirdVC = UIViewController()
thirdVC.view.tag = 102
thirdVC.view.backgroundColor = UIColor.purple
AddScrollView(bgView: thirdVC.view)
viewControllers = [firstVC,secondVC,thirdVC]
}
func AddScrollView(bgView: UIView) {
let scrollView = UIScrollView()
scrollView.frame = CGRect.init(x: 10, y: 10, width: bgView.frame.width-20, height: bgView.frame.height-20)
scrollView.backgroundColor = UIColor.init(red: 0.34, green: 0.45, blue: 0.35, alpha: 0.9)
bgView.addSubview(scrollView)
scrollView.contentSize = CGSize.init(width: scrollView.frame.size.width, height: scrollView.frame.size.height+200)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
if viewController.view.tag == 101 {
return viewControllers?[0]
}
else if viewController.view.tag == 102{
return viewControllers?[1]
}
else{
return viewControllers?[2]
}
}
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
if viewController.view.tag == 101 {
return viewControllers?[0]
}
else if viewController.view.tag == 102{
return viewControllers?[1]
}
else{
return viewControllers?[2]
}
}
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return (viewControllers?.count)!
}
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
return 0
}
}
Similar issues are raised fairly often on Stack. For my own issue in very similar circumstances, I too was able to use the scrollview without issue in it's own view, but when used as a subview in another subview, I needed to set the scrollview dimensions programatically.
Swift 2
scrollView.setFrame(CGRectMake(0, 0, DEVICE_WIDTH, DEVICE_HEIGHT))
or
Swift 3
scrollView.setFrame(CGRect(x: 0, y: 0, width: DEVICE_WIDTH, height: DEVICE_HEIGHT))
A further (and often viewed as a better) suggestion, automatically set the size based on contents.
var contentRect = CGRectZero
for view in self.scrollView.subviews {
contentRect = CGRectUnion(contentRect, view.frame)
}
self.scrollView.contentSize = contentRect.size
Hope this helps.

PopoverPresentationController delegate methods not called

I have a class to construct a UIPopoverPresentationController in an empty swift file:
import Foundation
import UIKit
class Popovers : NSObject, UIPopoverPresentationControllerDelegate {
func presentLoginScreen() {
// Presenting login Popover
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginScreen = storyboard.instantiateViewControllerWithIdentifier("LogInScreen")
let window = UIApplication.sharedApplication().windows.last!
let height = window.screen.bounds.size.height
let width = window.screen.bounds.size.width
loginScreen.modalPresentationStyle = UIModalPresentationStyle.Popover
loginScreen.preferredContentSize = CGSizeMake(width * 0.85 , height * 0.35)
let loginScreenPopover = loginScreen.popoverPresentationController
loginScreenPopover?.permittedArrowDirections = UIPopoverArrowDirection.init(rawValue: 0)
loginScreenPopover?.delegate = self
loginScreenPopover?.sourceView = window.rootViewController?.view
loginScreenPopover?.sourceRect = CGRectMake(CGRectGetMidX(window.screen.bounds), CGRectGetMidY(window.screen.bounds), 0, 0)
window.rootViewController?.presentViewController(loginScreen, animated: true, completion: nil)
let blur = UIVisualEffectView(effect: UIBlurEffect(style: .Light)) as UIVisualEffectView
let bounds = window.screen.bounds
blur.frame = bounds
blur.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
window.rootViewController!.view.addSubview(blur)
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.None
}
func popoverPresentationControllerDidDismissPopover(popoverPresentationController: UIPopoverPresentationController) {
// Remove blur layer
print("method called")
let window = UIApplication.sharedApplication().windows.last
window?.subviews.last?.removeFromSuperview()
}
}
But whenever I dismiss the PopoverPresentationController the delegate method popoverPresentationControllerDidDismissPopover is never fired.
I have set the delegate of the loginScreenPopover to self and let the Popovers class inherit from the UIPopoverPresentationControllerDelegate but neither seems to be working.
It is on an iPhone, if that is relevant to the question.
I have found the answer. In the ViewDidLoad() from the LogInScreen I set the delegate to:
self.popoverPresentationController.delegate = getPopovers()
Where the getPopovers() function is a function to retrieve the Popover object created somewhere else in the code. Now in my Popovers class I can conform to the UIPopoverPresentationControllerDelegate and use those methods.

UIPopoverPresentationController showing as full-window modal popup

I have a project for which I'm using a UIPopoverPresentationController, which I want to show as a popover for both iPhone and iPad. Even in a standalone project with just a blank Storyboard with a single UIButton, the following code keeps resulting in a full-screen modal popup instead. I've seen a question similar to this asked before on SO multiple times, but everyone seems to solve it by returning .None for adaptivePresentationStyleForPresentationController, but even including that isn't fixing it for me. I have a single ViewController with the following code--what am I missing?
import UIKit
class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {
#IBOutlet weak var thatButton: UIButton!
#IBAction func presentPopoverController(sender: UIButton) {
let popVC = UIViewController() //(nibName: "CameraPopover", bundle: nil)
popVC.view.backgroundColor = UIColor.blueColor()
popVC.modalPresentationStyle = .Popover
popVC.preferredContentSize = CGSize(width: 100, height: 100)
popVC.view.layer.borderWidth = 5
popVC.view.layer.borderColor = UIColor.blackColor().CGColor
self.presentViewController(popVC, animated: true, completion: nil)
let popController = popVC.popoverPresentationController
popController!.delegate = self
}
func prepareForPopoverPresentation(popoverPresentationController: UIPopoverPresentationController) {
popoverPresentationController.permittedArrowDirections = .Down
popoverPresentationController.sourceView = self.thatButton
popoverPresentationController.sourceRect = self.thatButton.frame
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return .None
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
return .None
}
}

Resources