Cómo presentar popover correctamente en iOS 8

118

Estoy tratando de agregar un UIPopoverView a mi aplicación Swift iOS 8, pero no puedo acceder a la propiedad PopoverContentSize, ya que el popover no se muestra en la forma correcta. mi código:

var popover: UIPopoverController? = nil 

    func addCategory() {

    var newCategory = storyboard.instantiateViewControllerWithIdentifier("NewCategory") as UIViewController
    var nav = UINavigationController(rootViewController: newCategory)
    popover = UIPopoverController(contentViewController: nav)
    popover!.setPopoverContentSize(CGSizeMake(550, 600), animated: true)
    popover!.delegate = self
    popover!.presentPopoverFromBarButtonItem(self.navigationItem.rightBarButtonItem, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
}

salida:

ingrese la descripción de la imagen aquí

Cuando hago lo mismo a través de UIPopoverPresentationController, todavía no lo hago. este es mi código:

func addCategory() {

    var popoverContent = self.storyboard.instantiateViewControllerWithIdentifier("NewCategory") as UIViewController
    var nav = UINavigationController(rootViewController: popoverContent)
    nav.modalPresentationStyle = UIModalPresentationStyle.Popover
    var popover = nav.popoverPresentationController as UIPopoverPresentationController
    popover.delegate = self
    popover.popoverContentSize = CGSizeMake(1000, 300)
    popover.sourceView = self.view
    popover.sourceRect = CGRectMake(100,100,0,0)

    self.presentViewController(nav, animated: true, completion: nil)

}

Obtengo exactamente el mismo resultado.

¿Cómo personalizo el tamaño de mi popover? ¡Cualquier ayuda sería muy apreciada!

Joris416
fuente
Hay un video de la WWDC en el sitio del desarrollador llamado "Una mirada al interior de los controladores de presentación" Explica cómo usar UIPopoverPresentationController
Wextux
He editado mi pregunta de acuerdo con el video de Apple sobre el controlador UIpopoverpresentationctontroller, ¡pero nada cambió! ¿tal vez ve algo que deba cambiar sobre esto? ¡Gracias por el aporte!
Joris416

Respuestas:

148

Bien, un compañero de casa lo miró y lo descubrió:

 func addCategory() {

    var popoverContent = self.storyboard?.instantiateViewControllerWithIdentifier("NewCategory") as UIViewController
    var nav = UINavigationController(rootViewController: popoverContent)
    nav.modalPresentationStyle = UIModalPresentationStyle.Popover
    var popover = nav.popoverPresentationController
    popoverContent.preferredContentSize = CGSizeMake(500,600)
    popover.delegate = self
    popover.sourceView = self.view
    popover.sourceRect = CGRectMake(100,100,0,0)

    self.presentViewController(nav, animated: true, completion: nil)

}

Esa es la manera.

Ya no habla con el popover en sí mismo, habla con el controlador de vista dentro de él para establecer el tamaño del contenido, llamando a la propiedad preferredContentSize

Joris416
fuente
15
Probablemente diciendo lo obvio, pero esto no solo está relacionado con la rapidez. También tuve que hacer esto en mi aplicación obj-c :)
Kevin R
4
Otro comentario sobre el código: puede usar "dejar" en lugar de "var". Apple lo recomienda para los casos en los que no es necesario reasignar el valor.
EPage_Ed
3
Esto tiene errores en el GM para iPhone. Si intenta presentar mientras el simulador está en vertical, siempre será a pantalla completa. Si gira a paisaje, se convierte en un popover. Si vuelves a girar a la posición vertical de nuevo, sigue siendo un popover.
jjxtra
1
La solución es configurar el popover ANTES de llamar a presentViewController. Esto es exactamente lo opuesto al ejemplo de Apple, donde explícitamente le dicen que configure el popover DESPUÉS de llamar a presentViewController.
jjxtra
1
@PsychoDad, ¿puede proporcionar un enlace a esta solución que mencionó? Todavía estoy atascado en "mientras el simulador está en vertical, siempre está en pantalla completa". Gracias
Nishant
53

De hecho, es mucho más sencillo que eso. En el guión gráfico, debe crear el controlador de vista que desea usar como ventana emergente y crear una clase de controlador de vista para él como de costumbre. Haga una transición como se muestra a continuación desde el objeto que desea abrir el popover, en este caso el UIBarButtonllamado "Config".

ingrese la descripción de la imagen aquí

En el "controlador de vista madre" implemente el UIPopoverPresentationControllerDelegatemétodo delegado y:

func popoverPresentationControllerDidDismissPopover(popoverPresentationController: UIPopoverPresentationController) {
    //do som stuff from the popover
}

Anule el prepareForSequemétodo de esta manera:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
     //segue for the popover configuration window
    if segue.identifier == "yourSegueIdentifierForPopOver" {
        if let controller = segue.destinationViewController as? UIViewController {
            controller.popoverPresentationController!.delegate = self
            controller.preferredContentSize = CGSize(width: 320, height: 186)
        }
    }
}

Y tu estas listo. Y ahora puede tratar la vista emergente como cualquier otra vista, es decir. agregar campos y lo que no! Y obtienes el controlador de contenido usando el popoverPresentationController.presentedViewControllermétodo en UIPopoverPresentationController.

También en un iPhone tendrías que sobrescribir

func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {

        return UIModalPresentationStyle.none
    } 
usuario1700737
fuente
28

Encontré un ejemplo completo de cómo hacer que todo esto funcione para que siempre pueda mostrar un popover sin importar el dispositivo / orientación https://github.com/frogcjn/AdaptivePopover_iOS8_Swift .

La clave es implementar UIAdaptivePresentationControllerDelegate

func adaptivePresentationStyleForPresentationController(PC: UIPresentationController!) -> UIModalPresentationStyle {
    // This *forces* a popover to be displayed on the iPhone
    return .None
}

Luego, amplíe el ejemplo anterior (de Imagine Digital):

nav.popoverPresentationController!.delegate = implOfUIAPCDelegate
David Hunt
fuente
Uso UIPopoverPresentationControllerDelegate
onmyway133
3
Correcto, UIPopoverPresentationControllerDelegate extiende UIAdaptivePresentationControllerDelegate. Entonces, por definición, ambos contienen el método 'adaptivePresentationStyleForPresentationController'. Proporcioné la interfaz base ya que es allí donde se documenta el método en los documentos API de Apple.
David Hunt
1
Tenga en cuenta que este es un comportamiento indocumentado. El documento dice que este método delegado debe devolver " UIModalPresentationFullScreeno bien UIModalPresentationOverFullScreen". Además, "Si no implementa este método o devuelve cualquier estilo que no sea UIModalPresentationFullScreeno UIModalPresentationOverFullScreen, el controlador de presentación ajusta el estilo de presentación al UIModalPresentationFullScreenestilo".
Tom
1
La documentación actual advierte que desde iOS 8.3 en adelante debe usar - adaptivePresentationStyleForPresentationController: traitCollection: y que el estilo devuelto debe ser "UIModalPresentationFullScreen, UIModalPresentationOverFullScreen, UIModalPresentationFormSheet o UIModalPresentation.
Dale
25

Swift 2.0

Bueno, hice ejercicio. Echar un vistazo. Hizo un ViewController en StoryBoard. Asociado con la clase PopOverViewController.

import UIKit

class PopOverViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()    
        self.preferredContentSize = CGSizeMake(200, 200)    
        self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Done, target: self, action: "dismiss:")    
    }    
    func dismiss(sender: AnyObject) {
        self.dismissViewControllerAnimated(true, completion: nil)
    }
}      

Ver ViewController:

//  ViewController.swift

import UIKit

class ViewController: UIViewController, UIPopoverPresentationControllerDelegate
{
    func showPopover(base: UIView)
    {
        if let viewController = self.storyboard?.instantiateViewControllerWithIdentifier("popover") as? PopOverViewController {    

            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)
            }
        }
    }    
    override func viewDidLoad(){
        super.viewDidLoad()
    }    
    @IBAction func onShow(sender: UIButton)
    {
        self.showPopover(sender)
    }    
    func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
        return .None
    }
}  

Nota: El método func showPopover (base: UIView) debe colocarse antes de ViewDidLoad. Espero eso ayude !

AG
fuente
hola @Alvin, voy a abrir una vista de la anotación del mapa. así que hice lo mismo que tú. la diferencia es que voy a completar tableviewcontroller en lugar de view. Ahora el problema no es presionar el método delegado. "PopoverPresentationControllerDidDismissPopover". cuando despido el controlador. puede usted ayudar ? (la pregunta no está relacionada con la publicación)
Subin K Kuriakose
1
¿Por qué showPopover(base: UIView)debería colocarse un método antes viewDidLoad()?
Eimantas
15

En iOS9, UIPopoverController está depreciado. Entonces, puede usar el siguiente código para la versión de Objective-C por encima de iOS9.x,

- (IBAction)onclickPopover:(id)sender {
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
UIViewController *viewController = [sb instantiateViewControllerWithIdentifier:@"popover"];

viewController.modalPresentationStyle = UIModalPresentationPopover;
viewController.popoverPresentationController.sourceView = self.popOverBtn;
viewController.popoverPresentationController.sourceRect = self.popOverBtn.bounds;
viewController.popoverPresentationController.permittedArrowDirections = UIPopoverArrowDirectionAny;
[self presentViewController:viewController animated:YES completion:nil]; }
Vijay
fuente
La pregunta pide específicamente a Swift, no a Objective-C.
Eric Aya
8

Aquí convierto el código Swift "Joris416" a Objective-c,

-(void) popoverstart
{
    ViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:@"PopoverView"];
    UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:controller];
    nav.modalPresentationStyle = UIModalPresentationPopover;
    UIPopoverPresentationController *popover = nav.popoverPresentationController;
    controller.preferredContentSize = CGSizeMake(300, 200);
    popover.delegate = self;
    popover.sourceView = self.view;
    popover.sourceRect = CGRectMake(100, 100, 0, 0);
    popover.permittedArrowDirections = UIPopoverArrowDirectionAny;
    [self presentViewController:nav animated:YES completion:nil];
}

-(UIModalPresentationStyle) adaptivePresentationStyleForPresentationController: (UIPresentationController * ) controller
{
    return UIModalPresentationNone;
}

Recuerde AGREGAR
UIPopoverPresentationControllerDelegate, UIAdaptivePresentationControllerDelegate

usuario2941395
fuente
La pregunta pide específicamente a Swift, no a Objective-C.
Eric Aya
4

Esto se explica mejor en el blog del día a día de iOS8

En resumen, una vez que haya configurado el modalPresentationStyle de su UIViewController en .Popover, puede obtener una UIPopoverPresentationClass (una nueva clase de iOS8) a través de la propiedad popoverPresentationController del controlador.

Clafou
fuente
3

Hice una versión Objective-C de la respuesta rápida de Imagine Digitals arriba. No creo que me haya perdido nada, ya que parece funcionar en las pruebas preliminares, si detecta algo, hágamelo saber y lo actualizaré.

-(void) presentPopover
{
    YourViewController* popoverContent = [[YourViewController alloc] init]; //this will be a subclass of UIViewController
    UINavigationController* nav =  [[UINavigationController alloc] initWithRootViewController:popoverContent];
    nav.modalPresentationStyle = UIModalPresentationPopover;
    UIPopoverPresentationController* popover = nav.popoverPresentationController;
    popoverContent.preferredContentSize = CGSizeMake(500,600);
    popover.delegate = self;
    popover.sourceRect = CGRectMake(100,100,0,0); //I actually used popover.barButtonItem = self.myBarButton;

    [self presentViewController:nav animated:YES completion:nil];
}
narco
fuente
Creo que te popover.sourceView = self.view;
quedaste
La pregunta pide específicamente a Swift, no a Objective-C.
Eric Aya
4
Me doy cuenta de eso, pero Google te trae aquí incluso si estás buscando el objetivo-C. Así es como terminé aquí.
narco
3

mis dos centavos por xcode 9.1 / swift 4.

class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {

    override func viewDidLoad(){
        super.viewDidLoad()

        let when = DispatchTime.now() + 0.5

        DispatchQueue.main.asyncAfter(deadline: when, execute: { () -> Void in
            // to test after 05.secs... :)
            self.showPopover(base: self.view)

        })

}


func showPopover(base: UIView) {
    if let viewController = self.storyboard?.instantiateViewController(withIdentifier: "popover") as? PopOverViewController {

        let navController = UINavigationController(rootViewController: viewController)
        navController.modalPresentationStyle = .popover

        if let pctrl = navController.popoverPresentationController {
            pctrl.delegate = self

            pctrl.sourceView = base
            pctrl.sourceRect = base.bounds

            self.present(navController, animated: true, completion: nil)
        }
    }
}


@IBAction func onShow(sender: UIButton){
    self.showPopover(base: sender)
}

func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle{
    return .none
}

y experimentar en:

func adaptivePresentationStyle ...

    return .popover

o: return .pageSheet .... y así sucesivamente ..

ingconti
fuente
2

Implemente UIAdaptivePresentationControllerDelegate en su Viewcontroller. Luego añade :

func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle{
    return .none
}
Nyakiba
fuente
1

Lo siguiente tiene una guía bastante completa sobre cómo configurar y presentar popovers. https://www.appcoda.com/presentation-controllers-tutorial/

En resumen, una implementación viable (con algunas actualizaciones de la sintaxis del artículo original para Swift 4.2 ), que luego se llamará desde otro lugar, sería algo como lo siguiente:

func showPopover(ofViewController popoverViewController: UIViewController, originView: UIView) {
    popoverViewController.modalPresentationStyle = UIModalPresentationStyle.popover
    if let popoverController = popoverViewController.popoverPresentationController {
        popoverController.delegate = self
        popoverController.sourceView = originView
        popoverController.sourceRect = originView.bounds
        popoverController.permittedArrowDirections = UIPopoverArrowDirection.any
    }
    self.present(popoverViewController, animated: true)
}

Mucho de esto ya se cubrió en la respuesta de @mmc, pero el artículo ayuda a explicar algunos de esos elementos de código utilizados y también muestra cómo podría expandirse.

También proporciona una gran cantidad de detalles adicionales sobre el uso de la delegación para manejar el estilo de presentación para iPhone vs. iPad, y para permitir el descarte del popover si alguna vez se muestra en pantalla completa. Nuevamente, actualizado para Swift 4.2 :

func adaptivePresentationStyle(for: UIPresentationController) -> UIModalPresentationStyle {
    //return UIModalPresentationStyle.fullScreen
    return UIModalPresentationStyle.none
}

func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
    if traitCollection.horizontalSizeClass == .compact {
        return UIModalPresentationStyle.none
        //return UIModalPresentationStyle.fullScreen
    }
    //return UIModalPresentationStyle.fullScreen
    return UIModalPresentationStyle.none
}

func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
    switch style {
    case .fullScreen:
        let navigationController = UINavigationController(rootViewController: controller.presentedViewController)
        let doneButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.done, target: self, action: #selector(doneWithPopover))
        navigationController.topViewController?.navigationItem.rightBarButtonItem = doneButton
        return navigationController
    default:
        return controller.presentedViewController
    }
}

// As of Swift 4, functions used in selectors must be declared as @objc
@objc private func doneWithPopover() {
    self.dismiss(animated: true, completion: nil)
}

Espero que esto ayude.

TheNeil
fuente
0

¡Para los que quieran estudiar!

Creé un proyecto de código abierto para aquellos que quieran estudiar y usar la vista Popover para cualquier propósito. Puedes encontrar el proyecto aquí. https://github.com/tryWabbit/KTListPopup

KTListNewResize

tryKuldeepTanwar
fuente