Popover con controlador de navegación integrado no respeta el tamaño en la navegación trasera

89

Tengo un UIPopoverController que aloja un UINavigationController, que contiene una pequeña jerarquía de controladores de vista.

Seguí los documentos y para cada controlador de vista, configuré el tamaño del contexto de ventana emergente de la vista así:

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

(tamaño diferente para cada controlador)

Esto funciona como se esperaba mientras navego hacia adelante en la jerarquía: la ventana emergente anima automáticamente los cambios de tamaño para que se correspondan con el controlador empujado.

Sin embargo, cuando navego "Atrás" a través de la pila de vistas a través del botón Atrás de la barra de navegación, la ventana emergente no cambia de tamaño; permanece tan grande como la vista más profunda alcanzada. Esto me parece roto; Espero que la ventana emergente respete los tamaños que se configuran a medida que aparece en la pila de vistas.

¿Me estoy perdiendo de algo?

Gracias.

Ben Zotto
fuente
¿Dónde está configurando el tamaño del popover? ¿Lo restablece cada vez que se muestra un controlador de vista (por ejemplo, en viewWillAppear:)?
Ole Begemann
¿Qué documentación quiere decir que siguió?
Tom Hamming

Respuestas:

94

Ok, estaba luchando con el mismo problema. Ninguna de las soluciones anteriores me funcionó bastante bien, es por eso que decidí hacer una pequeña investigación y descubrir cómo funciona esto. Esto es lo que descubrí: - Cuando configura elcontentSizeForViewInPopoveren su controlador de vista, el popover no lo cambiará, aunque el tamaño del popover puede cambiar mientras se navega a un controlador diferente. - Cuando el tamaño del popover cambiará mientras navega a un controlador diferente, mientras retrocede, el tamaño del popover no se restaura - Cambiar el tamaño del popover en viewWillAppear da una animación muy extraña (cuando digamos que popController dentro del popover) - No lo recomendaría; para mí, configurar el tamaño codificado dentro del controlador no funcionaría en absoluto; mis controladores tienen que ser a veces grandes a veces pequeños; el controlador que los presentará tiene una idea sobre el tamaño

Una solución para todo ese dolor es la siguiente: debe restablecer el tamaño de currentSetSizeForPopoveren viewDidAppear. Pero debe tener cuidado, cuando establezca el mismo tamaño que ya se estableció en el campo currentSetSizeForPopover, el popover no cambiará el tamaño. Para que esto suceda, primero puede establecer el tamaño falso (que será diferente al que se estableció antes) y luego establecer el tamaño adecuado. Esta solución funcionará incluso si su controlador está anidado dentro del controlador de navegación y popover cambiará su tamaño en consecuencia cuando navegue de regreso entre los controladores.

Puede crear fácilmente una categoría en UIViewController con el siguiente método de ayuda que haría el truco al establecer el tamaño:


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

Luego, invocalo en el -viewDidAppearcontrolador deseado.

krasnyk
fuente
1
La categoría anterior es la única (sensata) forma en que puedo hacer que funcione. Gracias.
RickiG
Esto funciona. Necesito averiguar cómo evitar que la vista de la mesa se vuelva "negra" en el área de contracción cuando el popover se encoge, pero esta solución (¡finalmente!) Realmente permite que el popover se mueva al tamaño correcto para cada nivel de pila. ¡Gracias!
Ben Zotto
2
Lo envolví en [UIView beginAnimations: nil context: nil]; y [UIView commitAnimations]; - lo hace menos discordante.
Dustin
2
Para mí, usando self.contentSizeForViewInPopover = CGSizeZero; salvó una línea y tuvo el mismo efecto. ¡Muchas gracias!
rob5408
Solo podría hacer que esta solución funcione si agregué self.contentSizeForPopover = CGSizeZero; en mi método viewWillDisappear del VC del que estaba saliendo.
LightningStryk
18

Así es como lo resolví para iOS 7 y 8:

En iOS 8, iOS envuelve silenciosamente la vista que desea en la ventana emergente en el presentViewController del controlador de vista PresentationViewController. Hay un video de la WWDC de 2014 que explica qué hay de nuevo con el controlador de popover donde tocan esto.

De todos modos, para los controladores de vista presentados en la pila de controladores de navegación que quieren su propio tamaño, estos controladores de vista necesitan (en iOS 8) llamar a este código para establecer dinámicamente el tamaño de contenido preferido:

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

Reemplace heightOfTable con su tabla calculada o altura de vista.

Para evitar una gran cantidad de código duplicado y crear una solución común de iOS 7 y iOS 8, creé una categoría en UITableViewController para realizar este trabajo cuando se llama a viewDidAppear en mis vistas de tabla:

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

Categoría.h:

#import <UIKit/UIKit.h>

@interface UITableViewController (PreferredContentSize)

- (void) setPopOverViewContentSize;

@end

Categoría.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
Wesley Filleman
fuente
Tu consejo presentingViewControllerfunciona. Si fijo el preferredContentSizede viewDidLoadque hay un comportamiento extraño: Navegando hacia atrás desde otro controlador de vista en las derivaciones popover a un cambio de tamaño popover mal. Parece que se estableció un tamaño de popover de cero, pero el tamaño es correcto. En tal caso, el popover ocupa toda la altura de la pantalla. ¿Sabes quizás por qué es así? No tengo implementada la restricción con 600 puntos porque, en mi experiencia, el sistema operativo no te permite especificar un tamaño superior al tamaño de la pantalla.
prueba el
Pruebe viewDidAppear en lugar de viewDidLoad para que el código se ejecute cuando vuelva a navegar por la pila.
Wesley Filleman
Ese fue el camino que tomé. Pero no entiendo por qué no funcionará si configura esto en viewDidLoad...
prueba el
1
Desafortunadamente, esa no es la forma en que Apple escribió la pila de vistas. Estos valores contentSize realmente no persisten una vez que viewController está oculto a la vista. Es por eso que tienes que "recordar" la ventana emergente cada vez que una vista pasa a primer plano, ya sea con un empujón o con un pop. Mi recomendación es presentar un informe de error a Apple si cree que el popover debería retener esta información.
Wesley Filleman
12

Esta es una mejora en la respuesta de krasnyk .
Su solución es excelente, pero no está animada sin problemas.
Una pequeña mejora proporciona una animación agradable:

elimine la última línea del - (void) forcePopoverSizemétodo:

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

Ponga [self forcePopoverSize] en el - (void)viewWillAppear:(BOOL)animatedmétodo:

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

    [self forcePopoverSize];
}

Y finalmente, establezca el tamaño deseado en el - (void)viewDidAppear:(BOOL)animatedmétodo:

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

    CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
    self.contentSizeForViewInPopover = currentSetSizeForPopover;
}
adnako
fuente
8

Debe volver a establecer el tamaño del contenido en viewWillAppear. Llamando al método delagate en el que estableces el tamaño de popovercontroller. También tuve el mismo problema. Pero cuando agregué esto, el problema se resolvió.

Una cosa más: si está utilizando versiones beta inferiores a 5. Entonces los popovers son más difíciles de administrar. Parecen ser más amigables desde la versión beta 5. Es bueno que la versión final esté disponible. ;)

Espero que esto ayude.

Madhup Singh Yadav
fuente
Yo también odio esto. También me atrapó. Apple: ¿por qué no podemos bloquear un popover con navcontroller a un tamaño específico?
Jann
2
Establecer el tamaño del contenido en viewWillAppearno funcionó para mí. Establecer el tamaño del popover explícitamente funcionó, pero eso es un gueto.
Ben Zotto
@quixoto No sé cuál fue tu problema, pero sigo usando lo mismo y funciona perfectamente.
Madhup Singh Yadav
5

En el -(void)viewDidLoadde todos los controladores de vista que está utilizando en el controlador de navegación, agregue:

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

Restablecí el tamaño en el método animado viewWillDisappear: (BOOL) del controlador de vista que se está navegando desde:

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

Luego, cuando aparece la vista a la que se está navegando, restablezco el tamaño de manera apropiada:

-(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;
}
Greg C
fuente
Hmm ... no era necesario reiniciar. Puse self.contentSizeForViewInPopover = self.view.frame.size en todas las viewWillAppear de todos los controladores de vista.
solo
¿Qué pondría para fetchedResultsController fetchedObjects? No puedo hacer que esto funcione
Jules
2

Para iOS 8 funciona lo siguiente:

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

Por cierto, creo que esto debería ser compatible con versiones anteriores de iOS ...

Zeroid
fuente
Tuve el problema con una aplicación en iOS8 compilada con iOS7 sdk. Esto funcionó, gracias.
Climbatize
Para corregir el tamaño al cambiar la rotación, llame a este método en willTransitionToTraitCollection, en el animateAlongsideTransitionbloque de finalización.
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 !
AG
fuente
1

Para mí, esta solución funciona. Este es un método de mi controlador de vista que extiende UITableViewController y es el controlador raíz para UINavigationController.

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

Y no olvide configurar el tamaño del contenido para el controlador de vista que va a insertar en la pila de navegación

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

si puedes imaginar al asaltante, creo que esto es un poco mejor:

- (void) forcePopoverSize {
    CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
    self.contentSizeForViewInPopover = CGSizeMake (0, 0);
    self.contentSizeForViewInPopover = currentSetSizeForPopover;
}
usuario1375355
fuente
2
incluso mejor será usarlo en CGSizeZerolugar de hacerlo usted mismo enCGSizeMake(0,0)
Julian Król
1

La respuesta aceptada no funciona bien con iOS 8. Lo que hice fue crear mi propia subclase de UINavigationControllerpara usar en ese popover y anular el método preferredContentSizede esta manera:

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

Además, en lugar de llamar forcePopoverSize (método implementado por @krasnyk) en viewDidAppear, decidí establecer un viewController (que muestra popover) como delegado para la navegación mencionada anteriormente (en popover) y hacer (lo que hace el método force) en:

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

método delegado para una pasada viewController. Una cosa importante, hacer forcePopoverSizeen un UINavigationControllerDelegatemétodo está bien si no necesita que la animación sea fluida, si es así, déjela viewDidAppear.

Julian Król
fuente
¿Por qué votar en contra? tal vez algunos comentarios diferentes al desacuerdo :)
Julian Król
hola @ JulianKról, ¿puedes echar un vistazo a este stackoverflow.com/questions/28112617/…? Tengo el mismo problema.
Ranjit
Gracias, esto me ayudó. Es posible que desee dejar en claro en la parte superior de su comentario que el problema es que necesita actualizar navigationController favoriteContentSize y visibleVC favoriteContentSize. Así que configurar ambos directamente también funciona.
Michael Kernahan
0

Estaba enfrentando el mismo problema, pero no desea establecer el tamaño del contenido en el método 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];

establecer la propiedad contentSizeForViewInPopover para ese controlador antes de enviar ese controlador a navigationController

Vikas Sawant
fuente
0

Tuve suerte al poner lo siguiente en viewdidappear:

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

Aunque esto puede no animarse bien en el caso de empujar / hacer estallar popovers de diferentes tamaños. Pero en mi caso, ¡funciona perfectamente!

Chris
fuente
0

Todo lo que tienes que hacer es:

-En el método viewWillAppear de popOvers contentView, agregue el fragmento que se proporciona a continuación. Tendrá que especificar el tamaño del popOver la primera vez que se cargue.

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

Tuve este problema con un controlador popover cuyo popoverContentSize = CGSizeMake (320, 600) al principio, pero se agrandaría al navegar a través de su ContentViewController (un UINavigationController).

El controlador de navegación solo estaba presionando y haciendo estallar UITableViewControllers personalizados, por lo que en mi clase de controlador de vista de tabla personalizada viewDidLoad configuré self.contentSizeForViewInPopover = CGSizeMake (320, 556)

Los 44 píxeles menos son para dar cuenta de la barra de navegación del controlador Nav, y ahora ya no tengo ningún problema.

leukosaima
fuente
0

Pon esto en todos los controles de vista que estás empujando dentro del popover

CGSize currentSetSizeForPopover = CGSizeMake(260, 390);
CGSize fakeMomentarySize = CGSizeMake(currentSetSizeForPopover.width - 1.0f,
                                      currentSetSizeForPopover.height - 1.0f);
self.contentSizeForViewInPopover = fakeMomentarySize;
self.contentSizeForViewInPopover = currentSetSizeForPopover;
alok
fuente
en el método viewwillAppear y currentSetSizeForPopover establece el tamaño deseado.
alok
0

Se enfrentó al mismo problema y lo solucionó configurando el tamaño de la vista de contenido en el controlador de navegación y el controlador de vista antes de que se colocara el inicio de 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];
Tomasz Dubik
fuente
0

Solo me gustaría ofrecer otra solución, ya que ninguna de estas funcionó para mí ...

De hecho, lo estoy usando con este https://github.com/nicolaschengdev/WYPopoverController

Cuando llame por primera vez a su ventana emergente, use esto.

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

Luego, en esa ventana emergente, usa esto.

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

}

Luego repita para las vistas de los niños ...

Jules
fuente
0

Esta es la forma correcta en iOS7 de hacer esto, establezca el tamaño de contenido preferido en viewDidLoad en cada controlador de vista en la pila de navegación (solo se hace una vez). Luego, en viewWillAppear obtenga una referencia al controlador popover y actualice el contentSize allí.

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

La solución @krasnyk funcionó bien en versiones anteriores de iOS pero no en iOS8. La siguiente solución funcionó para mí.

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

No es la mejor solución, pero funciona.

El nuevo UIPopoverPresentationController también tiene el problema de cambio de tamaño :(.

Clemente Prem
fuente
1
tal vez, en lugar de llegar al controlador de vista desde la propiedad AppDelegate, considere mi solución (parece ser más limpia)
Julian Król
Sí, esta fue la solución más rápida sin modificar nada de mi base de código existente. Por cierto vl prueba tu solución
Clement Prem
0

Debe establecer la preferredContentSizepropiedad del NavigationController en viewWillAppear:

-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationController.preferredContentSize = CGSizeMake(320, 500);}
Pablo Alonso González
fuente