EDIT:
Added approach 3 (see comments below).
I want to create little popovers in my app but I can't use the storyboard since the anchor points for those popovers will be UIControls that the user creates.
I want to create real popovers like those described here (not fullscreen ones). It works great using the storyboard but in this case i need it to work without segues.
Also I found this post where the person tries to do something very similiar. I tried to use the solution from that post as below in //approach 1. In //approach 2 I tried the solution on this site (even though it's for iPads only I think but I was out of ideas...).
// long press gesture: show additional control elements
func showLongPressMenu(recognizer: UILongPressGestureRecognizer) {
// approach 1
let newPopoverVC1 = UIViewController(nibName: "LinkAreaPopupView", bundle: NSBundle.mainBundle())
newPopoverVC1.modalPresentationStyle = .Popover
newPopoverVC1.preferredContentSize = CGSizeMake(137.0, 28.0)
var newPopoverController = newPopoverVC1.popoverPresentationController!
newPopoverController.delegate = self
newPopoverController.permittedArrowDirections = .Any
newPopoverController.sourceView = ???
newPopoverController.sourceRect = ???
presentViewController(newPopoverVC1, animated: true, completion: nil)
// approach 2
let newPopoverVC2 = UIViewController(nibName: "LinkAreaPopupView", bundle: NSBundle.mainBundle())
newPopoverVC2.modalPresentationStyle = .Popover
let popAnchorRect = self.frame
let newPopover = UIPopoverController(contentViewController: newPopoverVC2)
newPopover.presentPopoverFromRect(popAnchorRect, inView: ???, permittedArrowDirections: .Any, animated: true)
// approach 3
let vc = UIViewController()
vc.preferredContentSize = CGSizeMake(137.0, 28.0)
vc.modalPresentationStyle = .Popover
if let pres = vc.popoverPresentationController {
pres.delegate = self
}
// THIS DOES NOT WORK
self.superview.presentViewController(vc, animated: true, completion: nil)
let popView = LinkAreaPopupView()
vc.view.addSubview(popView)
popView.frame = vc.view.bounds
popView.autoresizingMask = .FlexibleWidth | .FlexibleHeight
if let pop = vc.popoverPresentationController {
pop.sourceView = (self as UIView)
pop.sourceRect = (self as UIView).bounds
}
}
And the delegate function:
// don't allow to substitute the presentation style of popover controllers (to fullscreen for example)
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return .None
}
The "LinkAreaPopupView" is a XIB file containing the menu I want to show.
My problem is that the class that has this code is a sublass of UIControl and will be created by the user. It doesn't know the "presentViewController()" function. The "presentPopoverFromRect()" function is not allowed to be called on iPhones (throws an error saying something about iPads). And obviously I'm missing some arguments too (the "???" parts).
Hope I didn't forget anything important. Thanks in advance
After hours of trying I got it working. Together with this tutorial video which shows how to use views from XIB files and the comment from #matt I achieved to show popups from UIControls.
To instantiate my XIB file I created a UIView sublcass like this:
import UIKit
class LinkAreaPopupView: UIView {
// outlets
#IBOutlet var view: UIView!
#IBOutlet weak var btnDeleteArea: UIButton!
#IBOutlet weak var btnLinkTo: UIButton!
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
NSBundle.mainBundle().loadNibNamed("LinkAreaPopupView", owner: self, options: nil)
self.addSubview(self.view)
}
override init(frame: CGRect) {
super.init(frame: frame)
NSBundle.mainBundle().loadNibNamed("LinkAreaPopupView", owner: self, options: nil)
self.addSubview(self.view)
}
}
Then in my UIControl sublcass in the function that shall create the popups I added this:
// long press gesture: show additional control elements
func showLongPressMenu(recognizer: UILongPressGestureRecognizer) {
if recognizer.state == UIGestureRecognizerState.Began {
// approach 3 - https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch09p477popoversOnPhone/PopoverOnPhone/ViewController.swift
let vc = UIViewController()
vc.preferredContentSize = CGSizeMake(137.0, 28.0)
vc.modalPresentationStyle = .Popover
if let pres = vc.popoverPresentationController {
pres.delegate = self
}
(self.delegate as! UIViewController).presentViewController(vc, animated: true, completion: nil)
let popView = LinkAreaPopupView()
vc.view.addSubview(popView)
popView.frame = vc.view.bounds
popView.autoresizingMask = .FlexibleWidth | .FlexibleHeight
if let pop = vc.popoverPresentationController {
pop.sourceView = (self as UIView)
pop.sourceRect = (self as UIView).bounds
}
}
}
The missing puzzle piece was to have the UIControl's delegate set to the UIViewController that lies underneath. And finally I had to cast (self.delegate as! UIViewController) because I implemented my own protocol to that delegate before and that's why I couldn't call .presentViewController(vc, animated: true, completion: nil) on it.
Voila.
Related
I don't ask for a lot out of life, but I'd give a lot to know how to get a navigation bar on this popover:
Here's my storyboard:
And here's my code presenting the popover:
#IBAction func doButton(_ sender: Any) {
let vc = PopViewController()
vc.preferredContentSize = CGSize(width: 260,height: 300)
vc.modalPresentationStyle = .popover
vc.view.backgroundColor = UIColor.green
if let pres = vc.presentationController {
pres.delegate = (self as UIAdaptivePresentationControllerDelegate)
}
self.present(vc, animated: true)
if let pop = vc.popoverPresentationController {
pop.sourceView = (sender as! UIView)
pop.sourceRect = (sender as! UIView).bounds
pop.backgroundColor = vc.view.backgroundColor
}
}
}
extension ViewController : UIPopoverPresentationControllerDelegate {
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
return .none
}
}
(Thanks to #matt, BTW, for the above code!)
I've searched high and low on Google yet haven't found an answer that I could understand. I tried to add the nav bar in Storyboard, but no dice--the only element that would accept it was the prototype cell in the table view.
Please don't redirect to something written 7 years ago--I've already read most of them, and I'm using Swift now. Certainly, I may have overlooked a clear answer, and I'll be humble and contrite if that's the case. But meantime, I'd sure appreciate some help if ya got it!
Thanks!
Change your right tableview controller to a navigation controller with a tableViewController root. The navigation Controller gets an identifier : "navigation".
Now you can change the first code of doButton function, and keep the rest.
#IBAction func doButton(_ sender: Any) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "navigation") as! UINavigationController
vc.preferredContentSize = CGSize(width: 260,height: 300)
vc.modalPresentationStyle = .popover
vc.view.backgroundColor = UIColor.green
if let pres = vc.presentationController {
pres.delegate = (self as UIAdaptivePresentationControllerDelegate)
}
self.present(vc, animated: true)
if let pop = vc.popoverPresentationController {
pop.sourceView = (sender as! UIView)
pop.sourceRect = (sender as! UIView).bounds
pop.backgroundColor = vc.view.backgroundColor
}
}
finally , you will get the nav bar in your popover.
There is such code, if user logged on - ViewController changes :
func ifLogged() {
let preferences = UserDefaults.standard
let token = "token"
if preferences.object(forKey: token) == nil {
// Doesn't exist and stop at the same viewController
} else {
let newViewController = GeneralChooser()
self.navigationController?.pushViewController(newViewController, animated: false)
// push new generalChooser when logged
}
}
But in this case I do not see view elements, just background color from ViewHelper class
class GeneralChooser: UIViewController{
override func viewDidLoad() {
super.viewDidLoad()
ViewHelper.BackGroundColor(view: self.view)
There is BackgroundColor func below
class func BackGroundColor(view: UIView){
let startColor = UIColor(red:0.15, green:0.50, blue:0.70, alpha:1.0)
let endColor = UIColor(red:0.58, green:0.65, blue:0.81, alpha:1.0)
let newLayer = CAGradientLayer()
newLayer.colors = [startColor.cgColor,endColor.cgColor]
newLayer.zPosition = -1
newLayer.frame = view.frame
view.layer.addSublayer(newLayer)
If i do not call this function in GeneralChooser class, the screen is black and there are not view elements, but in initial ViewController i see view elements, just background color is default.
What am I doing wrong. Thanks for all.
The problem is in this line
let newViewController = GeneralChooser()
this line doesn't load xib or reference the storyboard object associated with that class
Use instantiateViewController and give the viewController storyboard ID in storyboard say GeneralChooserView
let vc = self.storyboard?.instantiateViewController(withIdentifier:"GeneralChooserView")
self.navigationController?.pushViewController(vc!, animated: true)
Or if you are using xibs with GeneralChooser as xib file for GeneralChooser class
let vc = GeneralChooser(nibName: "GeneralChooser", bundle: nil)
self.navigationController?.pushViewController(vc, animated: true)
I want to have a small UItableView that popup when clicked and shows some numbers in the list.
I tried to use popoverPresentationController but it appears full screen for iOS(iPhone) devices.
below is the code for same -
let filterVC = TableViewController(nibName: "TableViewController", bundle: nil)
filterVC.preferredContentSize = CGSize(width: 300, height: 200)
filterVC.modalPresentationStyle = UIModalPresentationStyle.popover
present(filterVC, animated: true, completion: nil)
let popoverPresentationController = filterVC.popoverPresentationController
if let pop = filterVC.popoverPresentationController {
pop.delegate = self
}
popoverPresentationController?.sourceView = sender as? UIView
popoverPresentationController?.sourceRect = sender.frame
//-------
with below method also
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
// Return no adaptive presentation style, use default presentation behaviour
return .none
}
//-----
Any hint in right direction would be highly appreciated.
working sample would be greatly helpful
What I am trying to achieve as below
UPDATE
There is a useful library you may want to give a try.
It's because your pop.delegate was assigned after you present the filterVC.
Move this
if let pop = filterVC.popoverPresentationController {
pop.delegate = self
pop.sourceView = sender
pop.sourceRect = sender.bounds
}
present(filterVC, animated: true, completion: nil)
to the init of your filterVC should do the trick. Btw, I didn't see anywhere you have assigned sourceView and sourceRect for your popoverPresentationController. Moving pop.delegate = self to this part should be appropriate. Something like
init(for sender: UIView)) {
super.init(nibName: nil, bundle: nil)
modalPresentationStyle = .popover
guard let pop = popoverPresentationController else { return }
pop.sourceView = sender
pop.sourceRect = sender.bounds
pop.delegate = self
}
I want to display iAd in a popover. I use a shared instance class to call the displayAd method. here is my shared instance class :
class Share : NSObject ,UIPopoverPresentationControllerDelegate {
static let sharedInstance = Share()
func displayAd(sender:UIViewController) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("PopOverVC") as UIViewController
vc.preferredContentSize = CGSize(width: 310, height: 250)
let navC = UINavigationController(rootViewController: vc)
navC.modalPresentationStyle = UIModalPresentationStyle.Popover
let popOver = navC.popoverPresentationController
popOver?.delegate = self
popOver?.sourceView = sender.view
popOver?.sourceRect = CGRectMake(CGRectGetMidX(sender.view.bounds), CGRectGetMidY(sender.view.bounds),0,0)
popOver?.permittedArrowDirections = UIPopoverArrowDirection(rawValue:0)
navC.navigationBarHidden = true
sender.presentViewController(navC, animated: true) {}
}
}
I use the displayAd function to display a popover (which contains PopOverVC which is an iAd)
and then this is the PopOverVC class:
class PopOverVC: UIViewController,ADBannerViewDelegate {
var ad = ADBannerView()
#IBOutlet var Banner: ADBannerView!
#IBAction func CloseBtn(sender: UIButton) {
self.dismissViewControllerAnimated(true, completion: nil)
}
override func viewDidLoad() {
Banner = ad
}
func bannerViewDidLoadAd(banner: ADBannerView!) {
sharedAd.hidden = false
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
sharedAd.hidden = true
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Now, what I want to do is call displayAd method from some other View Controller and then check if iAd is available and then show it.
Right now my code shows the PopOver and then check for availability of iAd and if not available, it closes.
I don't want to show PopOver unless the iAd is available.
Is there anyway to achieve this?
thanks
You can simply ask the ADBannerView with the bannerLoaded property:
Banner views automatically download new advertisements in the background. This property returns true if an advertisement is loaded; false otherwise.
However, as Daniel pointed out above, you have six weeks to ship a replacement for your iAd code, so I very strongly recommend you work on that instead!
I'm trying to create effect similar to Snapchat in Swift - swiping between UIImagePicker with custom controls and other VCs.
The problem is:
when CameraVC is presented for the first time background is black and swipe between VCs works only on controls (on empty space where should be image from camera it isn't) and warning shows up "Attempt to present UIImagePickerController on CameraVC whose view is not in the window hierarchy!"
when I swipe to another VC and then back to CameraVC UIImagePicker is presented properly and everything works great instead of swiping between VCs which is not working at all. There's also no "window hierarchy" warning
So I think the reason why it's not working is that UIImagePicker is presenting over PageViewController not "in" it, but I have no idea how to fix this.
I'm presenting PageViewController like this:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var vcPageView = storyboard.instantiateViewControllerWithIdentifier("PageViewID") as! UIViewController
self.presentViewController(vcPageView, animated: false, completion: nil)
}
Loading VCs to table in PageViewController:
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
self.dataSource = self
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var vc0 = storyboard.instantiateViewControllerWithIdentifier("CameraID") as! UIViewController
var vc1 = storyboard.instantiateViewControllerWithIdentifier("vc2ID") as! UIViewController
self.myViewControllers = [vc0, vc1]
self.setViewControllers([myViewControllers[0]], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
}
And finally CameraVC:
#IBOutlet var cameraOverlay: UIView!
var camera = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera){
self.camera.delegate = self
self.camera.sourceType = UIImagePickerControllerSourceType.Camera;
self.camera.mediaTypes = [kUTTypeImage]
self.camera.allowsEditing = false
self.camera.showsCameraControls = false
self.cameraOverlay.frame = self.camera.cameraOverlayView!.frame
self.cameraOverlay.bringSubviewToFront(self.cameraOverlay)
self.camera.cameraOverlayView = self.cameraOverlay
self.cameraOverlay = nil
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.topMostViewController().presentViewController(self.camera, animated: false, completion: nil)
}
topMostViewController code:
extension UIViewController {
func topMostViewController() -> UIViewController {
// Handling Modal views
if let presentedViewController = self.presentedViewController {
return presentedViewController.topMostViewController()
}
// Handling UIViewController's added as subviews to some other views.
else {
for view in self.view.subviews
{
// Key property which most of us are unaware of / rarely use.
if let subViewController = view.nextResponder() {
if subViewController is UIViewController {
let viewController = subViewController as! UIViewController
return viewController.topMostViewController()
}
}
}
return self
}
}
Try this, In the CameraVC move the camera code in the viewDidLoad() to the viewDidAppear()