Como apresentar o popover corretamente no iOS 8

118

Estou tentando adicionar um UIPopoverView ao meu aplicativo Swift iOS 8, mas não consigo acessar a propriedade PopoverContentSize, pois o popover não aparece na forma correta. meu 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)
}

resultado:

insira a descrição da imagem aqui

Quando estou fazendo a mesma coisa por meio de UIPopoverPresentationController, ainda não consigo fazer. este é o meu 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)

}

Recebo exatamente a mesma saída.

Como faço para personalizar o tamanho do meu popover? Qualquer ajuda seria muito apreciada!

Joris416
fonte
Há um vídeo WWDC no site do desenvolvedor chamado "A Look Inside Presentation Controllers" Ele explica como usar o UIPopoverPresentationController
Wextux
Editei minha pergunta de acordo com o vídeo da apple sobre o UIpopoverpresentationctontroller, mas nada mudou! você pode ver alguma coisa que eu deva mudar sobre isso? Obrigado pela contribuição!
Joris416

Respostas:

148

Ok, um colega de casa deu uma olhada e descobriu:

 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)

}

Esse é o caminho.

Você não fala mais com o próprio popover, você fala com o controlador de visualização dentro dele para definir o tamanho do conteúdo, chamando a propriedade preferredContentSize

Joris416
fonte
15
Provavelmente afirmando o óbvio, mas isso não está apenas relacionado ao swift. Eu também tive que fazer isso em meu aplicativo obj-c :)
Kevin R
4
Outro comentário sobre o código - você pode usar "let" em vez de "var". A Apple o recomenda para os casos em que você não precisa reatribuir o valor.
EPage_Ed
3
Isso é bugado no GM para iPhone. Se você tentar apresentar enquanto o simulador estiver no modo retrato, será sempre em tela inteira. Se você girar para paisagem, ele se tornará um popover. Se você girar de volta para o retrato novamente, ele permanece um popover.
jjxtra
1
A solução é configurar o popover ANTES de chamar presentViewController. Isso é exatamente o oposto do exemplo da Apple onde eles explicitamente dizem para você configurar o popover APÓS chamar presentViewController.
jjxtra
1
@PsychoDad pode fornecer um link para esta solução que mencionou. Ainda estou preso em "enquanto o simulador está no retrato, ele está sempre em tela cheia". Obrigado
Nishant
53

Na verdade, é muito mais simples do que isso. No storyboard, você deve criar o viewcontroller que deseja usar como popover e criar uma classe viewcontroller para ele como de costume. Faça uma transição conforme mostrado abaixo a partir do objeto que você deseja abrir o popover, neste caso o UIBarButtonchamado "Config".

insira a descrição da imagem aqui

No "controlador de visualização mãe", implemente o UIPopoverPresentationControllerDelegatemétodo e o delegado:

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

Substitua o prepareForSequemétodo desta forma:

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 você está pronto. E agora você pode tratar a visualização popover como qualquer outra visualização, ou seja, adicione campos e quais não! E você obtém o controlador de conteúdo usando o popoverPresentationController.presentedViewControllermétodo no UIPopoverPresentationController.

Também em um iPhone, você teria que substituir

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

        return UIModalPresentationStyle.none
    } 
user1700737
fonte
28

Encontrei um exemplo completo de como fazer tudo isso funcionar para que você possa sempre exibir um popover, não importa o dispositivo / orientação https://github.com/frogcjn/AdaptivePopover_iOS8_Swift .

A chave é implementar UIAdaptivePresentationControllerDelegate

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

Em seguida, estenda o exemplo acima (da Imagine Digital):

nav.popoverPresentationController!.delegate = implOfUIAPCDelegate
David Hunt
fonte
Eu uso UIPopoverPresentationControllerDelegate
onmyway133
3
Correto, UIPopoverPresentationControllerDelegate estende UIAdaptivePresentationControllerDelegate. Portanto, por definição, ambos contêm o método 'adaptivePresentationStyleForPresentationController'. Forneci a interface base, pois é onde o método é documentado nos documentos API da Apple.
David Hunt,
1
Observe que este é um comportamento não documentado. O documento diz que este método delegado deve retornar " UIModalPresentationFullScreenou UIModalPresentationOverFullScreen". Além disso, "Se você não implementar este método ou retornar qualquer estilo diferente de UIModalPresentationFullScreenou UIModalPresentationOverFullScreen, o controlador de apresentação ajustará o estilo de apresentação ao UIModalPresentationFullScreenestilo."
Tom
1
A documentação atual informa que, a partir do iOS 8.3, você deve usar - adaptivePresentationStyleForPresentationController: traitCollection: e que o estilo retornado deve ser "UIModalPresentationFullScreen, UIModalPresentationOverFullScreen, UIModalPresentationFormSheet ou UIModalPresentationNodalPresentationFullScreen".
Dale
25

Swift 2.0

Bem, eu malhei. Dar uma olhada. Feito um ViewController no StoryBoard. Associado à 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)
    }
}      

Veja 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: O método func showPopover (base: UIView) deve ser colocado antes de ViewDidLoad. Espero que ajude !

AG
fonte
oi @Alvin, vou abrir uma visualização da anotação do mapa. então eu fiz o mesmo que você. a diferença é que vou preencher tableviewcontroller em vez de view. Agora o problema não é atingir o método delegado. "PopoverPresentationControllerDidDismissPopover". quando eu dispenso o controlador. você pode ajudar ? (a pergunta não está relacionada à postagem)
Subin K Kuriakose
1
Por que um showPopover(base: UIView)método deve ser colocado antes viewDidLoad()?
Eimantas
15

No iOS9, o UIPopoverController é depreciado. Então, pode usar o código abaixo para a versão Objective-C acima 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
fonte
A questão pede especificamente para Swift, não para Objective-C.
Eric Aya
8

Aqui, eu converto o código Swift "Joris416" em 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;
}

Lembre-se de ADICIONAR
UIPopoverPresentationControllerDelegate, UIAdaptivePresentationControllerDelegate

user2941395
fonte
A questão pede especificamente para Swift, não para Objective-C.
Eric Aya
4

Isso é melhor explicado no blog iOS8 Day-by-Day

Resumindo, depois de definir modalPresentationStyle do UIViewController como .Popover, você pode obter um UIPopoverPresentationClass (uma nova classe iOS8) por meio da propriedade popoverPresentationController do controlador.

Clafou
fonte
3

Fiz uma versão Objective-C da resposta rápida da Imagine Digitals acima. Acho que não perdi nada, pois parece funcionar em testes preliminares, se você encontrar algo, me avise e eu irei atualizá-lo

-(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
fonte
Acho que você deixou de fora popover.sourceView = self.view;
ghr
A questão pede especificamente para Swift, não para Objective-C.
Eric Aya
4
Eu sei disso, mas o Google traz você até aqui, mesmo se você estiver procurando por objetivo-C. Foi assim que acabei aqui.
narco
3

meus dois centavos pelo 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 experimente em:

função adaptivePresentationStyle ...

    return .popover

ou: return .pageSheet .... e assim por diante ..

ingconti
fonte
2

Implemente UIAdaptivePresentationControllerDelegate em seu Viewcontroller. Em seguida, adicione:

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

A seguir, um guia bastante abrangente sobre como configurar e apresentar popovers. https://www.appcoda.com/presentation-controllers-tutorial/

Em resumo, uma implementação viável (com algumas atualizações da sintaxe do artigo original para Swift 4.2 ), para então ser chamada de outro lugar, seria algo como o seguinte:

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

Muito disso já foi abordado na resposta de @mmc, mas o artigo ajuda a explicar alguns dos elementos de código usados ​​e também mostra como eles podem ser expandidos.

Ele também fornece muitos detalhes adicionais sobre o uso da delegação para lidar com o estilo de apresentação para iPhone vs. iPad e para permitir a dispensa do popover se ele for mostrado em tela inteira. Novamente, atualizado 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 isto ajude.

TheNeil
fonte
0

Pra quem quer estudar!

Criei um projeto Open Source para quem deseja estudar e usar a visualização Popover para qualquer propósito. Você pode encontrar o projeto aqui. https://github.com/tryWabbit/KTListPopup

KTListNewResize

tryKuldeepTanwar
fonte