Come presentare correttamente il popover in iOS 8


118

Sto cercando di aggiungere un UIPopoverView alla mia app Swift per iOS 8, ma non riesco ad accedere alla proprietà PopoverContentSize, poiché il popover non viene visualizzato nella forma corretta. il mio codice:

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)
}

produzione:

inserisci qui la descrizione dell'immagine

Quando sto facendo la stessa cosa tramite UIPopoverPresentationController, non riesco ancora a farlo. questo è il mio codice:

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)

}

Ottengo lo stesso identico output.

Come personalizzo le dimensioni del mio popover? Qualsiasi aiuto sarebbe molto apprezzato!


C'è un video WWDC sul sito per sviluppatori chiamato "A Look Inside Presentation Controller" Spiega come utilizzare UIPopoverPresentationController
Wextux

Ho modificato la mia domanda in base al video Apple riguardante l'UIpopoverpresentationctontroller, ma non è cambiato nulla! forse vedi qualcosa che dovrei cambiare al riguardo? Grazie per il contributo però!
Joris416

Risposte:


148

Ok, un coinquilino ha dato un'occhiata e ha capito:

 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)

}

Questo è il modo in cui.

Non parli più con il popover stesso, parli con il controller della vista al suo interno per impostare la dimensione del contenuto, chiamando la proprietà preferredContentSize


15
Probabilmente affermando l'ovvio, ma questo non è solo legato alla rapidità. Ho dovuto farlo anche nella mia app obj-c :)
Kevin R

4
Un altro commento sul codice: puoi usare "let" invece di "var". Apple lo consiglia per i casi in cui non è necessario riassegnare il valore.
EPage_Ed

3
Questo è disturbato nel GM per iPhone. Se provi a presentare mentre il simulatore è in verticale, è sempre a schermo intero. Se ruoti in orizzontale, diventa un popover. Se ruoti di nuovo in verticale, rimane un popover.
jjxtra

1
La soluzione è impostare il popover PRIMA di chiamare presentViewController. Questo è esattamente l'opposto dell'esempio di Apple in cui ti dicono esplicitamente di impostare il popover DOPO aver chiamato presentViewController.
jjxtra

1
@PsychoDad puoi fornire il link a questa soluzione che hai menzionato. Sono ancora bloccato su "mentre il simulatore è in verticale, è sempre a schermo intero". Grazie
Nishant

53

In realtà è molto più semplice di così. Nello storyboard dovresti creare il viewcontroller che desideri utilizzare come popover e creare una classe viewcontroller come al solito. Crea un segue come mostrato di seguito dall'oggetto di cui vuoi aprire il popover, in questo caso il UIBarButtonnome "Config".

inserisci qui la descrizione dell'immagine

Nel "controller di visualizzazione madre" implementa il UIPopoverPresentationControllerDelegatemetodo e il delegato:

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

Sostituisci il prepareForSequemetodo in questo modo:

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)
        }
    }
}

E hai finito. E ora puoi trattare la visualizzazione popover come qualsiasi altra visualizzazione, ad es. aggiungi campi e cosa no! E ottieni il controller dei contenuti usando il popoverPresentationController.presentedViewControllermetodo in UIPopoverPresentationController.

Anche su un iPhone dovresti sovrascrivere

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

        return UIModalPresentationStyle.none
    } 

28

Ho trovato un esempio completo di come far funzionare tutto in modo da poter sempre visualizzare un popover indipendentemente dal dispositivo / orientamento https://github.com/frogcjn/AdaptivePopover_iOS8_Swift .

La chiave è implementare UIAdaptivePresentationControllerDelegate

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

Quindi estendi l'esempio sopra (da Imagine Digital):

nav.popoverPresentationController!.delegate = implOfUIAPCDelegate

Uso UIPopoverPresentationControllerDelegate
onmyway133

3
Corretto, UIPopoverPresentationControllerDelegate estende UIAdaptivePresentationControllerDelegate. Quindi, per definizione, entrambi contengono il metodo "adaptivePresentationStyleForPresentationController". Ho fornito l'interfaccia di base poiché è lì che il metodo è documentato nei documenti API di Apple.
David Hunt

1
Notare che questo è un comportamento non documentato. Il documento dice che questo metodo delegato deve restituire "o UIModalPresentationFullScreeno UIModalPresentationOverFullScreen". Inoltre, "Se non si implementa questo metodo o non si restituisce uno stile diverso da UIModalPresentationFullScreeno UIModalPresentationOverFullScreen, il controller di presentazione adatta lo stile di presentazione allo UIModalPresentationFullScreenstile."
Tom

1
La documentazione attuale consiglia che da iOS 8.3 in poi dovresti usare - adaptivePresentationStyleForPresentationController: traitCollection: e che lo stile restituito deve essere "UIModalPresentationFullScreen, UIModalPresentationOverFullScreen, UIModalPresentationFormSheet o UIModalPresentationNone"
Dale

25

Swift 2.0

Bene, ho risolto. Dare un'occhiata. Crea un ViewController in StoryBoard. Associato alla classe 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)
    }
}      

Vedi 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: il metodo func showPopover (base: UIView) deve essere posizionato prima di ViewDidLoad. Spero che sia d'aiuto !


ciao @ Alvin, farò apparire una vista dall'annotazione della mappa. quindi ho fatto la stessa cosa che hai fatto tu. la differenza è che popolerò tableviewcontroller invece di view. Ora il problema non è colpire il metodo delegato. "PopoverPresentationControllerDidDismissPopover". quando chiudo il controller. Puoi aiutare ? (la domanda non è correlata al post)
Subin K Kuriakose

1
Perché un showPopover(base: UIView)metodo dovrebbe essere posizionato prima viewDidLoad()?
Eimantas

15

In iOS9 UIPopoverController è ammortizzato. Quindi puoi usare il codice seguente per la versione Objective-C sopra 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]; }

La domanda richiede specificamente Swift, non Objective-C.
Eric Aya

8

Qui converto il codice Swift "Joris416" in 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;
}

Ricordati di AGGIUNGERE
UIPopoverPresentationControllerDelegate, UIAdaptivePresentationControllerDelegate


La domanda richiede specificamente Swift, non Objective-C.
Eric Aya

4

Questo è spiegato meglio nel blog iOS8 Day-by-Day

In breve, dopo aver impostato modalPresentationStyle del tuo UIViewController su .Popover, puoi ottenere un UIPopoverPresentationClass (una nuova classe iOS8) tramite la proprietà popoverPresentationController del controller.


3

Ho creato una versione Objective-C della risposta rapida Imagine Digitals sopra. Non credo di essermi perso nulla perché sembra funzionare in fase di test preliminare, se noti qualcosa fammelo sapere e lo aggiornerò

-(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];
}

Penso che tu abbia lasciato fuori popover.sourceView = self.view;
ghr

La domanda richiede specificamente Swift, non Objective-C.
Eric Aya

4
Lo capisco, ma google ti porta qui anche se stai cercando l'obiettivo-C. È così che sono finito qui.
narco

3

i miei due centesimi per 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
}

e sperimenta in:

func adaptivePresentationStyle ...

    return .popover

oppure: return .pageSheet .... e così via ..


2

Implementa UIAdaptivePresentationControllerDelegate nel tuo Viewcontroller. Poi aggiungi :

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

1

Di seguito è riportata una guida piuttosto completa su come configurare e presentare i popover. https://www.appcoda.com/presentation-controllers-tutorial/

In sintesi, un'implementazione praticabile (con alcuni aggiornamenti dalla sintassi dell'articolo originale per Swift 4.2 ), per poi essere richiamata da altrove, sarebbe qualcosa del genere:

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)
}

Molto di questo è già stato trattato nella risposta di @mmc, ma l'articolo aiuta a spiegare alcuni di quegli elementi di codice utilizzati e mostra anche come potrebbe essere espanso.

Fornisce inoltre molti dettagli aggiuntivi sull'utilizzo della delega per gestire lo stile di presentazione per iPhone e iPad e per consentire l'annullamento del popover se viene mai mostrato a schermo intero. Ancora una volta, aggiornato per 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)
}

Spero che questo ti aiuti.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.