Popover com controlador de navegação embutido não respeita o tamanho na navegação traseira

89

Eu tenho um UIPopoverController hospedando um UINavigationController, que contém uma pequena hierarquia de controladores de exibição.

Eu segui os documentos e para cada controlador de visualização, eu defini o tamanho do contexto popover da visualização assim:

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

(tamanho diferente para cada controlador)

Isso funciona conforme o esperado conforme eu navego para frente na hierarquia - o popover anima automaticamente as mudanças de tamanho para corresponder ao controlador enviado.

No entanto, quando navego "Voltar" pela pilha de visualização por meio do botão Voltar da barra de navegação, o popover não muda de tamanho - permanece tão grande quanto a visualização mais profunda alcançada. Isso parece quebrado para mim; Eu esperaria que o popover respeitasse os tamanhos que são configurados conforme ele aparece na pilha de exibição.

Estou esquecendo de algo?

Obrigado.

Ben Zotto
fonte
Onde você está definindo o tamanho do popover? Você está redefinindo-o sempre que um controlador de visualização é exibido (por exemplo, em viewWillAppear:)?
Ole Begemann
Que documentação você quer dizer que seguiu?
Tom Hamming

Respostas:

94

Ok, eu estava lutando com o mesmo problema. Nenhuma das soluções acima funcionou muito bem para mim, por isso decidi fazer uma pequena investigação e descobrir como isso funciona. Isto é o que descobri: - Quando você define ocontentSizeForViewInPopoverem seu controlador de visualização, ele não será alterado pelo próprio popover - embora o tamanho do popover possa ser alterado durante a navegação para um controlador diferente. - Quando o tamanho do popover muda durante a navegação para um controlador diferente, ao voltar, o tamanho do popover não é restaurado - Alterar o tamanho do popover em viewWillAppear dá uma animação muito estranha (digamos que você popController dentro do popover) - Eu não o recomendaria - Para mim, definir o tamanho do código dentro do controlador não funcionaria de jeito nenhum - meus controladores às vezes têm que ser grandes, às vezes pequenos - o controlador que irá apresentá-los tem uma ideia sobre o tamanho embora

Uma solução para toda essa dor é a seguinte: Você deve redefinir o tamanho de currentSetSizeForPopoverem viewDidAppear. Mas você tem que ter cuidado, quando você definir o mesmo tamanho que já estava definido no campo currentSetSizeForPopover, o popover não mudará o tamanho. Para que isso aconteça, você pode primeiro definir o tamanho falso (que será diferente do que foi definido antes), seguido de definir o tamanho adequado. Esta solução funcionará mesmo se o seu controlador estiver aninhado dentro do controlador de navegação e o popover mudará seu tamanho de acordo quando você navegar de volta entre os controladores.

Você poderia criar facilmente uma categoria em UIViewController com o seguinte método auxiliar que resolveria a configuração do tamanho:


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

Em seguida, basta invocá-lo -viewDidAppeardo controlador desejado.

krasnyk
fonte
1
A categoria acima é a única maneira (sã) de fazer funcionar. Obrigado.
RickiG
Isso funciona. Preciso descobrir como evitar que a visualização da tabela fique "preta" na área de contração quando o popover encolher, mas essa solução (finalmente!) Realmente permite que o popover se mova para o tamanho correto para cada nível de pilha. Obrigado!
Ben Zotto,
2
Envolvi-o em [UIView beginAnimations: nil context: nil]; e [UIView commitAnimations]; - torna menos chocante.
Dustin
2
Para mim, usando self.contentSizeForViewInPopover = CGSizeZero; salvou uma linha e teve o mesmo efeito. Muito Obrigado!
rob5408
Eu só poderia fazer essa solução funcionar se adicionasse self.contentSizeForPopover = CGSizeZero; no meu método viewWillDisappear do VC de onde estava saindo.
LightningStryk
18

Veja como resolvi isso para iOS 7 e 8:

No iOS 8, o iOS está silenciosamente envolvendo a visualização que você deseja no popover no PresentViewController do controlador de visualização apresentandoViewController. Há um vídeo WWDC de 2014 explicando o que há de novo no popovercontroller, onde eles tocam nisso.

De qualquer forma, para controladores de visualização apresentados na pilha de controladores de navegação que desejam seu próprio dimensionamento, esses controladores de visualização precisam (no iOS 8) chamar este código para definir dinamicamente o preferredContentSize:

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

Substitua heightOfTable por sua tabela computada ou altura de visualização.

Para evitar muitos códigos duplicados e criar uma solução comum iOS 7 e iOS 8, criei uma categoria em UITableViewController para realizar este trabalho quando viewDidAppear é chamado em minhas tableviews:

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

Categoria.h:

#import <UIKit/UIKit.h>

@interface UITableViewController (PreferredContentSize)

- (void) setPopOverViewContentSize;

@end

Category.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
fonte
Sua dica com presentingViewControllerobras. Se eu definir o preferredContentSizeem, viewDidLoadhaverá um comportamento estranho: navegar de volta a partir de outro controlador de visualização no popover leva a uma alteração incorreta no tamanho do popover. Parece que o tamanho zero do popover foi definido, mas o tamanho está correto. Nesse caso, o popover ocupa toda a altura da tela. Você sabe talvez por que esse é o caso? Não tenho a restrição com 600 pontos implementados porque na minha experiência o sistema operacional não permite especificar um tamanho maior do que o tamanho da tela.
teste de
Tente viewDidAppear em vez de viewDidLoad para que o código seja executado quando você navegar de volta para cima na pilha.
Wesley Filleman
Foi assim que tomei. Mas não entendo por que não funcionará se você definir isso em viewDidLoad...
teste em
1
Infelizmente, não foi assim que a Apple escreveu a pilha de visualizações. Esses valores contentSize realmente não persistem, uma vez que viewController está oculto. É por isso que você precisa "lembrar" o popover cada vez que uma visualização vem para o primeiro plano por um push ou pop. Minha recomendação é enviar um relatório de bug à Apple se você acha que o popover deve reter essas informações.
Wesley Filleman
12

Esta é uma melhoria na resposta de krasnyk .
Sua solução é ótima, mas não é animada sem problemas.
Uma pequena melhoria fornece uma boa animação:

Remova a última linha do - (void) forcePopoverSizemétodo:

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

Coloque [self forcePopoverSize] no - (void)viewWillAppear:(BOOL)animatedmétodo:

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

    [self forcePopoverSize];
}

E finalmente - defina o tamanho desejado no - (void)viewDidAppear:(BOOL)animatedmétodo:

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

    CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
    self.contentSizeForViewInPopover = currentSetSizeForPopover;
}
Adnako
fonte
8

Você precisa definir o tamanho do conteúdo novamente em viewWillAppear. Chamando o método delagate no qual você define o tamanho do popovercontroller. Eu também tive o mesmo problema. Mas quando adicionei isso o problema foi resolvido.

Mais uma coisa: se você estiver usando versões beta menores que 5. Então, os popovers são mais difíceis de gerenciar. Eles parecem ser mais amigáveis ​​a partir da versão beta 5. É bom que a versão final tenha sido lançada. ;)

Espero que isto ajude.

Madhup Singh Yadav
fonte
Eu também odeio isso. Ele me pegou também. Apple: por que não podemos bloquear um popover com navcontroller para um tamanho específico ?!
Janeiro
2
Definir o tamanho do conteúdo viewWillAppearnão funcionou para mim. Definir o tamanho do popover explicitamente funcionou, mas isso é gueto.
Ben Zotto
@quixoto Não sei qual era o seu problema, mas continuo usando a mesma coisa e funciona perfeitamente.
Madhup Singh Yadav
5

Em -(void)viewDidLoadde todos os controladores de visualização que você está usando no controlador de navegação, adicione:

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

Eu redefino o tamanho no viewWillDisappear: (BOOL) método animado do controlador de visualização que está sendo navegado de volta:

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

Então, quando a visualização que está sendo navegada de volta aparece, eu redefino o tamanho de maneira apropriada:

-(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
fonte
Hmm .. a redefinição não foi necessária. Coloquei self.contentSizeForViewInPopover = self.view.frame.size em todos os viewWillAppear de todos os controladores de exibição.
sozinho em
O que eu colocaria para fetchedResultsController fetchedObjects? Não consigo fazer isso funcionar
Jules
2

Para iOS 8, funciona o seguinte:

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

BTW, eu acho, isso deve ser compatível com as versões anteriores do iOS ...

Zeroid
fonte
Tive o problema com um aplicativo em iOS8 compilado com iOS7 SDK. Isso funcionou, obrigado.
Escalada de
Para corrigir o tamanho ao alterar a rotação, chame este método em willTransitionToTraitCollection, no animateAlongsideTransitionbloco de conclusão.
Geva de
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
fonte
1

Para mim, essa solução funciona. Este é um método do meu controlador de visualização que estende UITableViewController e é o controlador raiz para UINavigationController.

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

E não se esqueça de definir o tamanho do conteúdo para o controlador de visualização que você vai colocar na pilha de navegação

- (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
fonte
1

se você pode imaginar o assambler, acho que é um pouco melhor:

- (vazio) forcePopoverSize {
    CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
    self.contentSizeForViewInPopover = CGSizeMake (0, 0);
    self.contentSizeForViewInPopover = currentSetSizeForPopover;
}
user1375355
fonte
2
melhor ainda será usar em CGSizeZerovez de fazer você mesmo emCGSizeMake(0,0)
Julian Król
1

A resposta aceita não está funcionando bem com o iOS 8. O que eu fiz foi criar minha própria subclasse de UINavigationControllerpara uso naquele popover e substituir o método preferredContentSizedesta maneira:

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

Além disso, em vez de chamar forcePopoverSize(método implementado por @krasnyk) em viewDidAppear, decidi definir um viewController (que mostra o popover) como um delegado para a navegação mencionada anteriormente (no popover) e fazer (o que o método force faz) em:

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

método delegado para um aprovado viewController. Uma coisa importante, fazer forcePopoverSizeem um UINavigationControllerDelegatemétodo é bom se você não precisa que a animação seja suave, então deixe-a ativada viewDidAppear.

Julian Król
fonte
por que votar? talvez algum feedback diferente de apenas desacordo :)
Julian Król
Olá @ JulianKról, você pode dar uma olhada neste stackoverflow.com/questions/28112617/… , eu tenho o mesmo problema.
Ranjit
Obrigado, isso me ajudou. Talvez você queira deixar claro no início de seu comentário que o problema é que você precisa atualizar o navigationController preferredContentSize, bem como o visibleVC preferredContentSize. Portanto, definir os dois diretamente também funciona.
Michael Kernahan
0

Eu estava enfrentando o mesmo problema, mas você não deseja definir o tamanho do conteúdo no método viewWillAppear ou 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];

defina a propriedade contentSizeForViewInPopover para esse controlador antes de enviar esse controlador para navigationController

Vikas Sawant
fonte
0

Tive sorte colocando o seguinte em viewdidappear:

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

Embora isso possa não ter uma boa animação no caso de você empurrar / abrir popovers de tamanhos diferentes. Mas no meu caso, funciona perfeitamente!

Chris
fonte
0

Tudo o que você precisa fazer é:

- No método viewWillAppear do popOvers contentView, adicione o trecho fornecido abaixo. Você terá que especificar o tamanho do popOver na primeira vez que for carregado.

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

Tive esse problema com um controlador de popover cujo popoverContentSize = CGSizeMake (320, 600) no início, mas ficaria maior ao navegar por seu ContentViewController (um UINavigationController).

O controlador de navegação estava apenas empurrando e popping UITableViewControllers personalizados, portanto, em viewDidLoad da minha classe de controlador de visualização de tabela personalizada, configurei self.contentSizeForViewInPopover = CGSizeMake (320, 556)

Os 44 pixels a menos são responsáveis ​​pela barra de navegação do controlador de Nav, e agora não tenho mais problemas.

leucosaima
fonte
0

Coloque isso em todos os controladores de visualização que você está empurrando dentro do popover

CGSize currentSetSizeForPopover = CGSizeMake(260, 390);
CGSize fakeMomentarySize = CGSizeMake(currentSetSizeForPopover.width - 1.0f,
                                      currentSetSizeForPopover.height - 1.0f);
self.contentSizeForViewInPopover = fakeMomentarySize;
self.contentSizeForViewInPopover = currentSetSizeForPopover;
alok
fonte
em viewwillAppear method.and currentSetSizeForPopover defina o tamanho desejado.
alok
0

Enfrentou o mesmo problema e o corrigiu definindo o tamanho da visualização do conteúdo para o controlador de navegação e o controlador de visualização antes que o init do UIPopoverController fosse colocado.

     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
fonte
0

Só gostaria de oferecer outra solução, pois nenhuma delas funcionou para mim ...

Na verdade, estou usando com este https://github.com/nicolaschengdev/WYPopoverController

Quando você chamar seu pop-up pela primeira vez, use-o.

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

Então, nesse pop-up, use isso.

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

}

Em seguida, repita para visualizações filhas ...

Jules
fonte
0

Esta é a maneira correta no iOS7 de fazer isso. Defina o tamanho do conteúdo preferido em viewDidLoad em cada controlador de visualização na pilha de navegação (feito apenas uma vez). Então, em viewWillAppear, obtenha uma referência ao controlador popover e atualize o 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];
}
Anders
fonte
0

A solução @krasnyk funcionou bem nas versões anteriores do iOS, mas não funcionou no iOS8. A solução a seguir funcionou para mim.

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

Não é a melhor solução, mas funciona.

O novo UIPopoverPresentationController também tem o problema de redimensionamento :(.

Clement Prem
fonte
1
talvez em vez de chegar ao controlador de visualização da propriedade AppDelegate, considere minha solução (parece ser mais limpa)
Julian Król
Sim, esta foi a solução mais rápida sem modificar nenhum dos meus codebase existentes. Btw vl tente sua solução
Clement Prem
0

Você precisa definir a preferredContentSizepropriedade do NavigationController em viewWillAppear:

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