Jak poprawnie zaprezentować popover w iOS 8


118

Próbuję dodać UIPopoverView do mojej aplikacji Swift iOS 8, ale nie mogę uzyskać dostępu do właściwości PopoverContentSize, ponieważ okienko popover nie jest wyświetlane we właściwym kształcie. mój kod:

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

wynik:

wprowadź opis obrazu tutaj

Kiedy robię to samo za pomocą UIPopoverPresentationController, nadal nie mogę tego zrobić. to jest mój kod:

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)

}

Otrzymuję dokładnie ten sam wynik.

Jak dostosować rozmiar mojego popovera? Każda pomoc byłaby bardzo mile widziana!


W witrynie programistów znajduje się film WWDC o nazwie „A Look Inside Presentation Controllers”. Wyjaśnia, jak korzystać z UIPopoverPresentationController
Wextux

Zredagowałem moje pytanie zgodnie z filmem Apple dotyczącym kontrolera UIpopoverpresentation, ale nic się nie zmieniło! czy widzisz może coś, co powinienem zmienić w tym? W każdym razie dziękuję za wkład!
Joris416

Odpowiedzi:


148

Okej, współlokator przyjrzał się temu i zorientował się:

 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)

}

To jest sposób.

Nie rozmawiasz już z samym wyskakującym okienkiem, rozmawiasz z kontrolerem widoku znajdującym się w nim, aby ustawić rozmiar zawartości, wywołując właściwość preferredContentSize


15
Prawdopodobnie stwierdzenie oczywiste, ale nie jest to tylko związane z szybkością. Musiałem to również zrobić w mojej aplikacji obj-c :)
Kevin R

4
Kolejny komentarz do kodu - możesz użyć „let” zamiast „var”. Apple zaleca to w przypadkach, w których nie musisz ponownie przypisywać wartości.
EPage_Ed

3
Jest to spowodowane błędem w GM dla iPhone'a. Jeśli spróbujesz przedstawić prezentację, gdy symulator jest w orientacji pionowej, jest on zawsze wyświetlany na pełnym ekranie. Jeśli obrócisz do poziomu, zmieni się w popover. Jeśli ponownie obrócisz do portretu, pozostanie popover.
jjxtra

1
Rozwiązaniem jest skonfigurowanie popover PRZED wywołaniem presentViewController. Jest to dokładnie odwrotne do przykładu Apple, w którym jawnie nakazują skonfigurowanie popover PO wywołaniu metody presentViewController.
jjxtra

1
@PsychoDad możesz podać link do wspomnianego rozwiązania. Nadal utknąłem na "gdy symulator jest w orientacji pionowej, zawsze jest na pełnym ekranie". Dzięki
Nishant,

53

W rzeczywistości jest to znacznie prostsze. W scenorysie powinieneś utworzyć kontroler widoku, którego chcesz użyć jako popover i jak zwykle utworzyć dla niego klasę viewcontroller. Zrób przejście, jak pokazano poniżej, od obiektu, który chcesz otworzyć, w tym przypadku o UIBarButtonnazwie „Konfiguracja”.

wprowadź opis obrazu tutaj

W „mother viewcontroller” zaimplementuj UIPopoverPresentationControllerDelegatemetodę i delegate:

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

Zastąp prepareForSequemetodę w następujący sposób:

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

I jesteś skończony. Możesz teraz traktować widok popover jak każdy inny widok, tj. dodaj pola, a co nie! Kontroler zawartości uzyskujesz za pomocą popoverPresentationController.presentedViewControllermetody w UIPopoverPresentationController.

Również na iPhonie musiałbyś nadpisać

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

        return UIModalPresentationStyle.none
    } 

28

Znalazłem kompletny przykład, jak to wszystko działało, aby zawsze można było wyświetlić popover bez względu na urządzenie / orientację https://github.com/frogcjn/AdaptivePopover_iOS8_Swift .

Kluczem jest zaimplementowanie UIAdaptivePresentationControllerDelegate

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

Następnie rozwiń powyższy przykład (z Imagine Digital):

nav.popoverPresentationController!.delegate = implOfUIAPCDelegate

Używam UIPopoverPresentationControllerDelegate
onmyway133

3
Prawidłowo, UIPopoverPresentationControllerDelegate rozszerza UIAdaptivePresentationControllerDelegate. Dlatego z definicji obie zawierają metodę „adaptivePresentationStyleForPresentationController”. Udostępniłem podstawowy interfejs, ponieważ jest to miejsce, w którym metoda jest udokumentowana w dokumentach API Apple.
David Hunt

1
Zauważ, że jest to nieudokumentowane zachowanie. Dokument mówi, że ta metoda delegata musi zwracać „albo UIModalPresentationFullScreenalbo UIModalPresentationOverFullScreen”. Ponadto „Jeśli nie zaimplementujesz tej metody lub nie zwrócisz stylu innego niż UIModalPresentationFullScreenlub UIModalPresentationOverFullScreen, kontroler prezentacji dostosuje styl prezentacji do UIModalPresentationFullScreenstylu”.
Tom

1
Bieżąca dokumentacja informuje, że od iOS 8.3 i nowszych należy używać - adaptivePresentationStyleForPresentationController: traitCollection: oraz że zwracany styl musi mieć wartość „UIModalPresentationFullScreen, UIModalPresentationOverFullScreen, UIModalPresentationFormSheet lub UIModalPresentationNone”.
Dale

25

Swift 2.0

Cóż, wypracowałem. Spójrz. Utworzono ViewController w StoryBoard. Skojarzone z klasą 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)
    }
}      

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

Uwaga: metodę func showPopover (base: UIView) należy umieścić przed ViewDidLoad. Mam nadzieję, że to pomoże !


cześć @ Alvin, mam zamiar wyświetlić widok z adnotacji mapy. więc zrobiłem to samo co ty. różnica polega na tym, że zamiast widoku zapełnię tableviewcontroller. Teraz problem nie dotyczy metody delegata. „PopoverPresentationControllerDidDismissPopover”. kiedy zwolnię kontrolera. możesz pomóc ? (pytanie nie dotyczy postu)
Subin K Kuriakose

1
Dlaczego showPopover(base: UIView)metodę należy umieścić wcześniej viewDidLoad()?
Eimantas

15

W iOS9 UIPopoverController jest amortyzowany. Więc można użyć poniższego kodu dla wersji Objective-C powyżej 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]; }

Pytanie dotyczy w szczególności Swifta, a nie Objective-C.
Eric Aya

8

Tutaj konwertuję kod Swift „Joris416” na 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;
}

Pamiętaj, aby dodać
UIPopoverPresentationControllerDelegate, UIAdaptivePresentationControllerDelegate


Pytanie dotyczy w szczególności Swifta, a nie Objective-C.
Eric Aya

4

Najlepiej wyjaśnić to na blogu iOS8 dzień po dniu

Krótko mówiąc, po ustawieniu modalPresentationStyle UIViewControllera na .Popover można uzyskać UIPopoverPresentationClass (nową klasę iOS8) za pośrednictwem właściwości popoverPresentationController kontrolera.


3

Stworzyłem szybką odpowiedź Imagine Digitals w wersji Objective-C powyżej. Nie sądzę, żebym niczego przegapił, ponieważ wydaje się, że działa podczas wstępnych testów, jeśli coś zauważysz, daj mi znać, a zaktualizuję to

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

Myślę, że popover.sourceView = self.view;
pominąłeś

Pytanie dotyczy w szczególności Swifta, a nie Objective-C.
Eric Aya

4
Zdaję sobie z tego sprawę, ale Google prowadzi Cię tutaj, nawet jeśli szukasz celu-C. Tak się tutaj znalazłem.
narco

3

moje dwa centy za 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
}

i eksperymentuj w:

func adaptivePresentationStyle ...

    return .popover

lub: return .pageSheet .... i tak dalej ..


2

Zaimplementuj UIAdaptivePresentationControllerDelegate w swoim Viewcontroller. Następnie dodaj :

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

1

Poniżej znajduje się dość obszerny przewodnik dotyczący konfigurowania i prezentowania okien popover. https://www.appcoda.com/presentation-controllers-tutorial/

Podsumowując, realna implementacja (z pewnymi aktualizacjami oryginalnej składni artykułu dla Swift 4.2 ), którą można następnie wywołać z innego miejsca, wyglądałaby następująco:

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

Wiele z tego zostało już omówionych w odpowiedzi z @mmc, ale artykuł pomaga wyjaśnić niektóre z tych użytych elementów kodu, a także pokazuje, jak można go rozszerzyć.

Zawiera również wiele dodatkowych szczegółów na temat używania delegowania do obsługi stylu prezentacji na iPhonie i iPadzie oraz umożliwia odrzucenie popover, jeśli kiedykolwiek zostanie wyświetlony na pełnym ekranie. Ponownie zaktualizowany dla 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)
}

Mam nadzieję że to pomoże.


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.