Il popover con il controller di navigazione incorporato non rispetta le dimensioni nel back nav


89

Ho un UIPopoverController che ospita un UINavigationController, che contiene una piccola gerarchia di controller di visualizzazione.

Ho seguito i documenti e per ogni controller di visualizzazione, ho impostato la dimensione del contesto popover della vista in questo modo:

[self setContentSizeForViewInPopover:CGSizeMake(320, 500)];

(dimensioni diverse per ogni controller)

Funziona come previsto mentre navigo in avanti nella gerarchia: il popover anima automaticamente le modifiche alle dimensioni in modo che corrispondano al controller inserito.

Tuttavia, quando navigo "Indietro" nella pila di visualizzazioni tramite il pulsante Indietro della barra di navigazione, il popover non cambia dimensione: rimane grande quanto la vista più profonda raggiunta. Questo mi sembra rotto; Mi aspetto che il popover rispetti le dimensioni impostate mentre si apre attraverso lo stack di visualizzazione.

Mi sto perdendo qualcosa?

Grazie.


Dove stai impostando la dimensione del popover? Lo stai ripristinando ogni volta che viene visualizzato un controller di visualizzazione (ad esempio in viewWillAppear:)?
Ole Begemann

Quale documentazione intendi seguire?
Tom Hamming

Risposte:


94

Ok, stavo lottando con lo stesso problema. Nessuna delle soluzioni di cui sopra ha funzionato abbastanza bene per me, ecco perché ho deciso di fare una piccola indagine e scoprire come funziona. Questo è quello che ho scoperto: - Quando imposti il ​​filecontentSizeForViewInPopovernel tuo controller di visualizzazione non verrà modificato dal popover stesso, anche se la dimensione del popover potrebbe cambiare durante la navigazione in controller diversi. - Quando la dimensione del popover cambierà durante la navigazione su controller diversi, mentre si torna indietro, la dimensione del popover non si ripristina - Cambiando le dimensioni del popover in view WillAppear dà un'animazione molto strana (quando diciamo che popController all'interno del popover) - Non lo consiglierei - Per me l'impostazione della dimensione hardcoded all'interno del controller non funzionerebbe affatto - i miei controller devono essere a volte grandi a volte piccoli - il controller che li presenterà ha l'idea delle dimensioni però

Una soluzione per tutto questo dolore è la seguente: devi reimpostare la dimensione di currentSetSizeForPopoverin viewDidAppear. Ma devi stare attento, quando imposterai la stessa dimensione già impostata nel campo, currentSetSizeForPopoveril popover non cambierà la dimensione. Affinché ciò avvenga, puoi prima impostare la dimensione falsa (che sarà diversa da quella impostata in precedenza), quindi impostare la dimensione corretta. Questa soluzione funzionerà anche se il tuo controller è annidato all'interno del controller di navigazione e il popover cambierà le sue dimensioni di conseguenza quando tornerai tra i controller.

Puoi facilmente creare una categoria su UIViewController con il seguente metodo di supporto che farebbe il trucco con l'impostazione della dimensione:


- (void) forcePopoverSize {
    CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
    CGSize fakeMomentarySize = CGSizeMake(currentSetSizeForPopover.width - 1.0f, currentSetSizeForPopover.height - 1.0f);
    self.contentSizeForViewInPopover = fakeMomentarySize;
    self.contentSizeForViewInPopover = currentSetSizeForPopover;
}

Quindi basta invocarlo nel -viewDidAppearcontroller desiderato.


1
La categoria di cui sopra è l'unico (sano) modo in cui posso farlo funzionare. Grazie.
RickiG

Funziona. Ho bisogno di capire come evitare che la visualizzazione della tabella diventi "nera" nell'area di contrazione quando il popover si restringe, ma questa soluzione (finalmente!) Consente davvero al popover di spostarsi alla dimensione corretta per ogni livello di stack. Grazie!
Ben Zotto

2
L'ho racchiuso in [UIView beginAnimations: nil context: nil]; e [UIView commitAnimations]; - lo rende meno stridente.
Dustin

2
Per me, utilizzando self.contentSizeForViewInPopover = CGSizeZero; salvato una riga e ha avuto lo stesso effetto. Grazie molto!
rob5408

Potrei far funzionare questa soluzione solo se aggiungessi self.contentSizeForPopover = CGSizeZero; a mio avviso WillDisappear metodo del VC da cui stavo saltando.
LightningStryk

18

Ecco come l'ho risolto per iOS 7 e 8:

In iOS 8, iOS avvolge silenziosamente la visualizzazione che desideri nel popover nel presentViewController del presentanteViewController view controller. C'è un video del WWDC del 2014 che spiega cosa c'è di nuovo con il popovercontroller dove toccano questo.

Ad ogni modo, per i controller di visualizzazione presentati nello stack del controller di navigazione che desiderano tutti il ​​proprio dimensionamento, questi controller di visualizzazione devono (sotto iOS 8) chiamare questo codice per impostare dinamicamente il preferitoContentSize:

self.presentingViewController.presentedViewController.preferredContentSize = CGSizeMake(320, heightOfTable);

Sostituisci heightOfTable con la tabella calcolata o l'altezza della vista.

Per evitare un sacco di codice duplicato e per creare una soluzione iOS 7 e iOS 8 comune, ho creato una categoria su UITableViewController per eseguire questo lavoro quando viewDidAppear viene chiamato nelle mie tableviews:

- (void)viewDidAppear:(BOOL)animated 
{
    [super viewDidAppear:animated];
    [self setPopOverViewContentSize];
}

Category.h:

#import <UIKit/UIKit.h>

@interface UITableViewController (PreferredContentSize)

- (void) setPopOverViewContentSize;

@end

Categoria.m:

#import "Category.h"

@implementation UITableViewController (PreferredContentSize)

- (void) setPopOverViewContentSize
{
    [self.tableView layoutIfNeeded];
    int heightOfTable = [self.tableView contentSize].height;

    if (heightOfTable > 600)
        heightOfTable = 600;

    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0)
            self.preferredContentSize=CGSizeMake(320, heightOfTable);
        else
            self.presentingViewController.presentedViewController.preferredContentSize = CGSizeMake(320, heightOfTable);
    }
}

@end

Il tuo consiglio con le presentingViewControlleropere. Se imposto il preferredContentSizein si viewDidLoadverifica uno strano comportamento: tornare indietro da un altro controller di visualizzazione nel popover porta a una modifica della dimensione del popover errata. Sembra che sia stata impostata una dimensione popover pari a zero, ma la dimensione è corretta. In tal caso il popover occupa l'intera altezza dello schermo. Sai forse perché è così? Non ho implementato la restrizione con 600 punti perché nella mia esperienza il sistema operativo non ti consente di specificare una dimensione superiore a quella dello schermo.
test il

Prova viewDidAppear invece di viewDidLoad in modo che il codice venga eseguito quando torni indietro nello stack.
Wesley Filleman

Questo è il modo in cui ho preso. Ma non capisco perché non funzionerà se lo imposti in viewDidLoad...
test del

1
Sfortunatamente non è solo il modo in cui Apple ha scritto lo stack di visualizzazione. Questi valori contentSize non persistono realmente una volta che viewController è nascosto alla visualizzazione. Ecco perché devi "ricordare" il popover ogni volta che una vista viene in primo piano con una pressione o uno schiocco. Il mio consiglio è di presentare una segnalazione di bug ad Apple se pensi che il popover debba conservare queste informazioni.
Wesley Filleman

12

Questo è un miglioramento rispetto alla risposta di krasnyk .
La tua soluzione è ottima, ma non è animata in modo fluido.
Un piccolo miglioramento offre una bella animazione:

rimuovi l'ultima riga nel - (void) forcePopoverSizemetodo:

- (void) forcePopoverSize {
    CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
    CGSize fakeMomentarySize = CGSizeMake(currentSetSizeForPopover.width - 1.0f, currentSetSizeForPopover.height - 1.0f);
    self.contentSizeForViewInPopover = fakeMomentarySize;
}

Inserisci [self forcePopoverSize] nel - (void)viewWillAppear:(BOOL)animatedmetodo:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [self forcePopoverSize];
}

E infine - imposta la dimensione desiderata nel - (void)viewDidAppear:(BOOL)animatedmetodo:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
    self.contentSizeForViewInPopover = currentSetSizeForPopover;
}

8

È necessario impostare nuovamente la dimensione del contenuto in viewWillAppear. Chiamando il metodo delagate in cui si imposta la dimensione del popovercontroller. Ho avuto anche lo stesso problema. Ma quando ho aggiunto questo il problema è stato risolto.

Un'altra cosa: se stai usando versioni beta inferiori alla 5. Allora i popover sono più difficili da gestire. Sembrano essere più amichevoli dalla versione beta 5. È positivo che la versione finale sia uscita. ;)

Spero che questo ti aiuti.


Lo odio anche io. Ha catturato anche me. Apple: perché non possiamo bloccare un popover con navcontroller a una dimensione specificata ?!
Jann

2
L'impostazione della dimensione del contenuto viewWillAppearnon ha funzionato per me. L'impostazione delle dimensioni del popover in modo esplicito ha funzionato, ma questo è ghetto.
Ben Zotto

@quixoto Non so quale fosse il tuo problema ma uso ancora la stessa cosa e funziona perfettamente.
Madhup Singh Yadav

5

In -(void)viewDidLoadtutti i controller di visualizzazione che stai utilizzando nel controller di navigazione, aggiungi:

[self setContentSizeForViewInPopover:CGSizeMake(320, 500)];

3

Ho reimpostato la dimensione nella viewWillDisappear: (BOOL) metodo animato del controller della vista che viene riportato indietro da:

-(void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    CGSize contentSize = [self contentSizeForViewInPopover];
    contentSize.height = 0.0;
    self.contentSizeForViewInPopover = contentSize;
}

Quindi, quando viene visualizzata la vista a cui si torna indietro, reimpostare le dimensioni in modo appropriato:

-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    CGSize contentSize;
    contentSize.width = self.contentSizeForViewInPopover.width;
    contentSize.height = [[self.fetchedResultsController fetchedObjects] count] *  self.tableView.rowHeight;
    self.contentSizeForViewInPopover = contentSize;
}

Hmm .. il ripristino non era necessario. Ho messo self.contentSizeForViewInPopover = self.view.frame.size in tutti i viewWillAppear di tutti i controller di visualizzazione.
solo

Cosa metterei per fetchedResultsController fetchedObjects? Non riesco a farlo funzionare
Jules

2

Per iOS 8 funziona quanto segue:

- (void) forcePopoverSize {
    CGSize currentSetSizeForPopover = self.preferredContentSize;
    CGSize fakeMomentarySize = CGSizeMake(currentSetSizeForPopover.width - 1.0f, currentSetSizeForPopover.height - 1.0f);
    self.preferredContentSize = fakeMomentarySize;
    self.navigationController.preferredContentSize = fakeMomentarySize;
    self.preferredContentSize = currentSetSizeForPopover;
    self.navigationController.preferredContentSize = currentSetSizeForPopover;
}

A proposito, penso che dovrebbe essere compatibile con le versioni precedenti di iOS ...


Ho avuto il problema con un'app in iOS8 compilata con iOS7 sdk. Ha funzionato, grazie.
Salita l'

Per correggere il dimensionamento quando si modifica la rotazione, chiamare questo metodo in willTransitionToTraitCollection, in animateAlongsideTransitionblocco di completamento.
Geva

2
Well i worked out. Have a look.


Made a ViewController in StoryBoard. Associated with PopOverViewController class.


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




See ViewController:


//
//  ViewController.swift
//  iOS8-PopOver
//
//  Created by Alvin George on 13.08.15.
//  Copyright (c) 2015 Fingent Technologies. All rights reserved.
//

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


Note: The func showPopover(base: UIView) method should be placed before ViewDidLoad. Hope it helps !

1

Per me questa soluzione funziona. Questo è un metodo dal mio controller di visualizzazione che estende UITableViewController ed è il controller principale per UINavigationController.

-(void)viewDidAppear:(BOOL)animated {
     [super viewDidAppear:animated];
     self.contentSizeForViewInPopover = self.tableView.bounds.size;
}

E non dimenticare di impostare la dimensione del contenuto per il controller di visualizzazione che inserirai nello stack di navigazione

- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath{
    dc = [[DetailsController alloc] initWithBookmark:[[bookmarksArray objectAtIndex:indexPath.row] retain] bookmarkIsNew:NO];
    dc.detailsDelegate = self;
    dc.contentSizeForViewInPopover = self.contentSizeForViewInPopover;
    [self.navigationController pushViewController:dc animated:YES]; 
 }

1

se puoi immaginare l'assambler, penso che sia leggermente migliore:

- (void) forcePopoverSize {
    CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
    self.contentSizeForViewInPopover = CGSizeMake (0, 0);
    self.contentSizeForViewInPopover = currentSetSizeForPopover;
}

2
sarà ancora meglio usarlo CGSizeZeroinvece di farlo da solo inCGSizeMake(0,0)
Julian Król

1

La risposta accettata non funziona bene con iOS 8. Quello che ho fatto è stato creare la mia sottoclasse UINavigationControllerda utilizzare in quel popover e sovrascrivere il metodo preferredContentSizein questo modo:

- (CGSize)preferredContentSize {
    return [[self.viewControllers lastObject] preferredContentSize];
}

Inoltre, invece di chiamare forcePopoverSize(metodo implementato da @krasnyk) in viewDidAppearho deciso di impostare un viewController (che mostra popover) come delegato per la navigazione menzionata in precedenza (in popover) e fare (quale metodo force fa) in:

-(void)navigationController:(UINavigationController *)navigationController
      didShowViewController:(UIViewController *)viewController 
                   animated:(BOOL)animated  

metodo delegato per un passato viewController. Una cosa importante, fare forcePopoverSizein un UINavigationControllerDelegatemetodo va bene se non hai bisogno che l'animazione sia fluida, in tal caso lasciala inserita viewDidAppear.


perché votare giù? forse qualche feedback diverso dal semplice disaccordo :)
Julian Król

ciao @ JulianKról, puoi dare un'occhiata a questo stackoverflow.com/questions/28112617/… , ho lo stesso problema.
Ranjit

Grazie, questo mi ha aiutato. Potrei voler chiarire all'inizio del tuo commento che il problema è che devi aggiornare il navigationController preferredContentSize così come il visibleVC preferredContentSize. Quindi anche l'impostazione di entrambi funziona direttamente.
Michael Kernahan

0

Stavo affrontando lo stesso problema, ma non vuoi impostare la dimensione del contenuto nel metodo viewWillAppear o viewWillDisappear.

AirPrintController *airPrintController = [[AirPrintController alloc] initWithNibName:@"AirPrintController" bundle:nil];
airPrintController.view.frame = [self.view frame];
airPrintController.contentSizeForViewInPopover = self.contentSizeForViewInPopover;
[self.navigationController pushViewController:airPrintController animated:YES];
[airPrintController release];

impostare la proprietà contentSizeForViewInPopover per quel controller prima di spingere quel controller a navigationController


0

Ho avuto fortuna inserendo quanto segue nella viewdidappear:

[self.popoverController setPopoverContentSize:self.contentSizeForViewInPopover animated:NO];

Anche se questo potrebbe non animarsi bene nel caso in cui stai spingendo / facendo scoppiare popover di dimensioni diverse. Ma nel mio caso, funziona perfettamente!


0

Tutto quello che devi fare è:

-Nel metodo viewWillAppear di popOvers contentView, aggiungi lo snippet indicato di seguito. Dovrai specificare la dimensione di popOver la prima volta quando viene caricato.

CGSize size = CGSizeMake(width,height);
self.contentSizeForViewInPopover = size;

0

Ho avuto questo problema con un controller popover il cui popoverContentSize = CGSizeMake (320, 600) all'inizio, ma sarebbe diventato più grande durante la navigazione attraverso il suo ContentViewController (un UINavigationController).

Il controller di navigazione stava solo spingendo e facendo scoppiare UITableViewControllers personalizzati, quindi nel viewDidLoad della mia classe di controller di visualizzazione tabella personalizzata ho impostato self.contentSizeForViewInPopover = CGSizeMake (320, 556)

I 44 pixel in meno devono tenere conto della barra di navigazione del controller Nav, e ora non ho più problemi.


0

Mettilo in tutti i controller di visualizzazione che stai spingendo all'interno del popover

CGSize currentSetSizeForPopover = CGSizeMake(260, 390);
CGSize fakeMomentarySize = CGSizeMake(currentSetSizeForPopover.width - 1.0f,
                                      currentSetSizeForPopover.height - 1.0f);
self.contentSizeForViewInPopover = fakeMomentarySize;
self.contentSizeForViewInPopover = currentSetSizeForPopover;

nel metodo viewwillAppear e currentSetSizeForPopover impostano la dimensione desiderata.
alok

0

Ha affrontato lo stesso problema e risolto impostando la dimensione della visualizzazione del contenuto sul controller di navigazione e sul controller di visualizzazione prima che fosse posizionato l'inizializzazione di UIPopoverController.

     CGSize size = CGSizeMake(320.0, _options.count * 44.0);
    [self setContentSizeForViewInPopover:size];
    [self.view setFrame:CGRectMake(0.0, 0.0, size.width, size.height)];
    [navi setContentSizeForViewInPopover:size];

    _popoverController = [[UIPopoverController alloc] initWithContentViewController:navi];

0

Vorrei solo offrire un'altra soluzione, poiché nessuna di queste ha funzionato per me ...

In realtà lo sto usando con questo https://github.com/nicolaschengdev/WYPopoverController

Quando chiami per la prima volta il tuo popup usa questo.

if ([sortTVC respondsToSelector:@selector(setPreferredContentSize:)]) {
   sortTVC.preferredContentSize = CGSizeMake(popoverContentSortWidth,
        popoverContentSortHeight);
}
else 
{
   sortTVC.contentSizeForViewInPopover = CGSizeMake(popoverContentSortWidth, 
        popoverContentSortHeight);
}

Quindi in quel popup usa questo.

-(void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:YES];

  if ([self respondsToSelector:@selector(setPreferredContentSize:)]) {
    self.preferredContentSize = CGSizeMake(popoverContentMainWidth, 
        popoverContentMainheight);
  }
  else 
  {
    self.contentSizeForViewInPopover = CGSizeMake(popoverContentMainWidth, 
        popoverContentMainheight);
  }
}

-(void)viewDidDisappear:(BOOL)animated {
 [super viewDidDisappear:YES];

self.contentSizeForViewInPopover = CGSizeZero;

}

Quindi ripeti per le visualizzazioni figlio ...


0

Questo è il modo corretto in iOS7 per farlo, imposta la dimensione del contenuto preferita in viewDidLoad in ogni controller di visualizzazione nello stack di navigazione (fatto solo una volta). Quindi in viewWillAppear ottiene un riferimento al controller popover e aggiorna contentSize lì.

-(void)viewDidLoad:(BOOL)animated
{
    ...

    self.popoverSize = CGSizeMake(420, height);
    [self setPreferredContentSize:self.popoverSize];
}

-(void)viewWillAppear:(BOOL)animated
{
    ...

    UIPopoverController *popoverControllerReference = ***GET REFERENCE TO IT FROM SOMEWHERE***;
    [popoverControllerReference setPopoverContentSize:self.popoverSize];
}

0

La soluzione @krasnyk funzionava bene nelle versioni precedenti di iOS ma non funzionava in iOS8. La seguente soluzione ha funzionato per me.

    - (void) forcePopoverSize {
        CGSize currentSetSizeForPopover = self.preferredContentSize;
       //Yes, there are coupling. We need to access the popovercontroller. In my case, the popover controller is a weak property in the app's rootVC.
        id mainVC = [MyAppDelegate appDelegate].myRootVC;
        if ([mainVC valueForKey:@"_myPopoverController"]) {
            UIPopoverController *popover = [mainVC valueForKey:@"_myPopoverController"];
            [popover setPopoverContentSize:currentSetSizeForPopover animated:YES];
        }
    }

Non è la soluzione migliore, ma funziona.

Il nuovo UIPopoverPresentationController presenta anche il problema di ridimensionamento :(.


1
forse invece di raggiungere il controller della vista dalla proprietà AppDelegate, considera la mia soluzione (sembra essere più pulita)
Julian Król

Sì, questa è stata la soluzione più rapida senza modificare nessuno dei miei codici esistenti. Btw vl prova la tua soluzione
Clement Prem

0

È necessario impostare la preferredContentSizeproprietà del NavigationController in viewWillAppear:

-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationController.preferredContentSize = CGSizeMake(320, 500);}
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.