I know that the normal behavior on iPad is to show a popover view while on iPhone it switches to a full screen, but I don't want the full screen on the smaller devices. Is there a way to prevent this?
In UIKit we could override the adaptivepresentationstyle func like this(from https://stackoverflow.com/a/50428131/412154)
class ViewController: UIViewController {
#IBAction func doButton(_ sender: Any) {
let vc = MyPopoverViewController()
vc.preferredContentSize = CGSize(400,500)
vc.modalPresentationStyle = .popover
if let pres = vc.presentationController {
pres.delegate = self
}
self.present(vc, animated: true)
if let pop = vc.popoverPresentationController {
pop.sourceView = (sender as! UIView)
pop.sourceRect = (sender as! UIView).bounds
}
}
}
extension ViewController : UIPopoverPresentationControllerDelegate {
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
return .none
}
}
wondering if anyone found something similar to override for swiftui
thanks for the help!
Here is another solution that subclasses uihostingcontroller but I don’t know how I would incorporate it into my swiftui views since I’m trying to achieve this for some deeper views
sampleproject
Heres and alternative that worked better for me
resizable popover
I have a view hierarchy like this
TabBarController -> NavigationController -> TableViewController
When I tap the button want to push new VC without tabBar, as you can see on the GIF tabBar is hiding properly but the elements on screen aren't placed correctly when VC is pushed and it takes about half a second for it to adjust properly. Why is that?
In my TableViewController I have
#IBAction func presentPlayerPressed() {
performSegue(withIdentifier: "player", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? PlayerViewController {
destination.hidesBottomBarWhenPushed = true
}
}
But I also tried setting hides bottom bar on push in Storyboard and it gives the same effect
Also this doesn't change anything
#IBAction func presentPlayerPressed() {
let vc = storyboard!.instantiateViewController(withIdentifier: "playerVC")
vc.hidesBottomBarWhenPushed = true
navigationController?.pushViewController(vc, animated: true)
}
I couldn't show popover controller as popover in iPhone whereas it works very well with iPad.
Any ideas on how to do that in iPhone.
As far as I have searched I couldn't find any.
anyways to make the popover appear in iphone as it is in iPad is appreciated !
Set yourself as the popover view controller's delegate before presenting it, and implement the delegate method adaptivePresentationStyle(for:traitCollection:) to return .none. This will cause the popover to stop adapting on iPhone as a fullscreen presented view controller and turn into an actual popover just like on the iPad.
This is a complete working example that presents the popover in response to a button tap:
class ViewController: UIViewController {
#IBAction func doButton(_ sender: Any) {
let vc = MyPopoverViewController()
vc.preferredContentSize = CGSize(400,500)
vc.modalPresentationStyle = .popover
if let pres = vc.presentationController {
pres.delegate = self
}
self.present(vc, animated: true)
if let pop = vc.popoverPresentationController {
pop.sourceView = (sender as! UIView)
pop.sourceRect = (sender as! UIView).bounds
}
}
}
extension ViewController : UIPopoverPresentationControllerDelegate {
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
return .none
}
}
I have a simple Tab Bar project where I add an image over the tab bar which looks good at first.
A second view is pushed form FirstViewController with hidesBottomBarWhenPushed = true. I then hide the button which was added, for good reasons, on the view and not the tabbar as a subview.
Everything ok until I press back button and when I unhide the view (or recreate it) show under the tab bar.
In what method should I put the showButton() method so it shows over the tabbar again?
Any tips?
CustomTabBarController.swift
class CustomTabBarController: UITabBarController {
var bigButton: UIImageView!
func addCenterButton() {
let image = UIImage(named: "bigButtonBackground")
bigButton = UIImageView(image: image)
bigButton.frame = CGRectMake(0.0, 0.0, image!.size.width, image!.size.height);
bigButton.autoresizingMask = .FlexibleRightMargin | .FlexibleLeftMargin | .FlexibleBottomMargin | .FlexibleTopMargin
let heightDifference = image!.size.height - tabBar.frame.size.height
var center = tabBar.center
center.y = center.y - heightDifference/2.0
bigButton.center = center
view.addSubview(bigButton)
}
func hideButton() {
bigButton.hidden = true
}
func showButton() {
bigButton.hidden = false
view.bringSubviewToFront(bigButton)
}
}
FirstViewController.swift
class FirstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
(tabBarController as! CustomTabBarController).addCenterButton()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
(tabBarController as! CustomTabBarController).showButton()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let viewController = segue.destinationViewController as! UIViewController
(tabBarController as! CustomTabBarController).hideButton()
viewController.hidesBottomBarWhenPushed = true
}
}
I was dealing with a similar situation and ended up subclassing UITabBarController. In the subclass' viewDidLayoutSubviews method, calling bringSubviewToFront(view: UIView) and passing in the view that was going behind the tab bar fixed the issue. Make sure you call this method on the UITabBarController's view.
I wish to create a small popover about 50x50px from a UIButton. I have seen methods using adaptive segue's but I have my size classes turn of thus meaning I can not use this features!
How else can I create this popover? Can I create it with code inside my button IBACtion? Or is there still a way I can do this with storyboards?
You can do one of the following two options :
Create an action for the UIButton in your UIViewController and inside present the ViewController you want like a Popover and your UIViewController has to implement the protocol UIPopoverPresentationControllerDelegate, take a look in the following code :
#IBAction func showPopover(sender: AnyObject) {
var popoverContent = self.storyboard?.instantiateViewControllerWithIdentifier("StoryboardIdentifier") as! UIViewController
popoverContent.modalPresentationStyle = .Popover
var popover = popoverContent.popoverPresentationController
if let popover = popoverContent.popoverPresentationController {
let viewForSource = sender as! UIView
popover.sourceView = viewForSource
// the position of the popover where it's showed
popover.sourceRect = viewForSource.bounds
// the size you want to display
popoverContent.preferredContentSize = CGSizeMake(200,500)
popover.delegate = self
}
self.presentViewController(popoverContent, animated: true, completion: nil)
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return .None
}
According to the book of #matt Programming iOS 8:
A popover presentation controller, in iOS 8, is a presentation controller (UIPresentationController), and presentation controllers are adaptive. This means that, by default, in a horizontally compact environment (i.e. on an iPhone), the .Popover modal presentation style will be treated as .FullScreen. What appears as a popover on the iPad will appear as a fullscreen presented view on the iPhone, completely replacing the interface.
To avoid this behavior in the iPhone you need to implement the delegate method adaptivePresentationStyleForPresentationController inside your UIViewController to display the Popover correctly.
The other way in my opinion is more easy to do, and is using Interface Builder, just arrange from the UIButton to create a segue to the ViewController you want and in the segue select the Popover segue.
I hope this help you.
Swift 4 Here is fully working code. So here you will see popup window with size of 250x250:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// in case if you don't want to make it via IBAction
button.addTarget(self, action: #selector(tapped), for: .touchUpInside)
}
#objc
private func tapped() {
guard let popVC = storyboard?.instantiateViewController(withIdentifier: "popVC") else { return }
popVC.modalPresentationStyle = .popover
let popOverVC = popVC.popoverPresentationController
popOverVC?.delegate = self
popOverVC?.sourceView = self.button
popOverVC?.sourceRect = CGRect(x: self.button.bounds.midX, y: self.button.bounds.minY, width: 0, height: 0)
popVC.preferredContentSize = CGSize(width: 250, height: 250)
self.present(popVC, animated: true)
}
}
// This is we need to make it looks as a popup window on iPhone
extension ViewController: UIPopoverPresentationControllerDelegate {
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
}
Take into attention that you have to provide popVC identifier to one viewController you want to present as a popup.
Hope that helps!
Here you can present a popover on button click.
func addCategory( _ sender : UIButton) {
var popoverContent = self.storyboard?.instantiateViewControllerWithIdentifier("NewCategory") as UIViewController
var nav = UINavigationController(rootViewController: popoverContent)
nav.modalPresentationStyle = UIModalPresentationStyle.Popover
var popover = nav.popoverPresentationController
popoverContent.preferredContentSize = CGSizeMake(50,50)
popover.delegate = self
popover.sourceView = sender
popover.sourceRect = sender.bounds
self.presentViewController(nav, animated: true, completion: nil)
}
Swift 4 Version
Doing most work from the storyboard
I added a ViewController, went to it's attribute inspector and ticked the "Use Preferred Explicit size". After that I changed the Width and Height values to 50 each.
Once this was done I ctrl clicked and dragged from the Button to the ViewController I added choosing "Present as Popover" and naming the segue Identifier as "pop"
Went to the ViewController where I had my Button and added the following code:
class FirstViewController: UIViewController, UIPopoverPresentationControllerDelegate {
#IBOutlet weak var popoverButton: UIButton! // the button
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "pop" {
let popoverViewController = segue.destination
popoverViewController.modalPresentationStyle = .popover
popoverViewController.presentationController?.delegate = self
popoverViewController.popoverPresentationController?.sourceView = popoverButton
popoverViewController.popoverPresentationController?.sourceRect = CGRect(x: 0, y: 0, width: popoverButton.frame.size.width, height: popoverButton.frame.size.height)
}
}
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.none
}
override func viewDidLoad() {
super.viewDidLoad()
}
}