Popover avec contrôleur de navigation intégré ne respecte pas la taille sur la navigation arrière

89

J'ai un UIPopoverController hébergeant un UINavigationController, qui contient une petite hiérarchie de contrôleurs de vue.

J'ai suivi la documentation et pour chaque contrôleur de vue, j'ai défini la taille du contexte popover de la vue comme suit:

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

(taille différente pour chaque contrôleur)

Cela fonctionne comme prévu lorsque je progresse dans la hiérarchie - le popover anime automatiquement les changements de taille pour correspondre au contrôleur poussé.

Cependant, lorsque je navigue "Retour" dans la pile de vues via le bouton Retour de la barre de navigation, le popover ne change pas de taille - il reste aussi grand que la vue la plus profonde atteinte. Cela me semble cassé; Je m'attendrais à ce que le popover respecte les tailles configurées à mesure qu'il apparaît dans la pile de vues.

Est-ce que je manque quelque chose?

Merci.

Ben Zotto
la source
Où définissez-vous la taille du popover? Le réinitialisez-vous chaque fois qu'un contrôleur de vue est affiché (par exemple dans viewWillAppear:)?
Ole Begemann
Quelle documentation voulez-vous dire que vous avez suivie?
Tom Hamming

Réponses:

94

Ok, je luttais avec le même problème. Aucune des solutions ci-dessus n'a fonctionné assez bien pour moi, c'est pourquoi j'ai décidé de faire une petite enquête et de découvrir comment cela fonctionne. Voici ce que j'ai découvert: - Lorsque vous réglez lecontentSizeForViewInPopoverdans votre contrôleur de vue, il ne sera pas modifié par le popover lui-même - même si la taille du popover peut changer lors de la navigation vers un contrôleur différent. - Lorsque la taille du popover changera lors de la navigation vers un contrôleur différent, en revenant, la taille du popover ne sera pas restaurée - Changer la taille du popover dans viewWillAppear donne une animation très étrange (quand disons que vous popController à l'intérieur du popover) - Je ne le recommanderais pas - Pour moi, définir la taille codée en dur à l'intérieur du contrôleur ne fonctionnerait pas du tout - mes contrôleurs doivent être parfois grands parfois petits - le contrôleur qui les présentera a cependant une idée de la taille

Une solution à toute cette douleur est la suivante: Vous devez réinitialiser la taille de currentSetSizeForPopoverin viewDidAppear. Mais vous devez faire attention, lorsque vous définissez la même taille que celle déjà définie dans le champ, currentSetSizeForPopoverle popover ne changera pas la taille. Pour que cela se produise, vous pouvez d'abord définir la fausse taille (qui sera différente de celle définie auparavant), puis définir la taille appropriée. Cette solution fonctionnera même si votre contrôleur est imbriqué dans le contrôleur de navigation et que le popover changera sa taille en conséquence lorsque vous reviendrez entre les contrôleurs.

Vous pouvez facilement créer une catégorie sur UIViewController avec la méthode d'assistance suivante qui ferait l'affaire en définissant la taille:


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

Ensuite, appelez-le simplement dans -viewDidAppearle contrôleur souhaité.

Krasnyk
la source
1
La catégorie ci-dessus est la seule façon (sensée) de le faire fonctionner. Merci.
RickiG
Cela marche. J'ai besoin de comprendre comment empêcher que la vue de la table ne devienne "noire" dans la zone contractuelle lorsque le popover se réduit, mais cette solution permet (enfin!) Vraiment au popover de passer à la taille correcte pour chaque niveau de pile. Merci!
Ben Zotto
2
Je l'ai enveloppé dans [UIView beginAnimations: nil context: nil]; et [UIView commitAnimations]; - le rend moins choquant.
Dustin
2
Pour moi, en utilisant self.contentSizeForViewInPopover = CGSizeZero; a enregistré une ligne et a eu le même effet. Merci beaucoup!
rob5408
Je ne pourrais faire fonctionner cette solution que si j'ajoutais self.contentSizeForPopover = CGSizeZero; à mon avis, la méthode disparaîtra du VC d'où je sortais.
LightningStryk
18

Voici comment je l'ai résolu pour iOS 7 et 8:

Dans iOS 8, iOS encapsule silencieusement la vue souhaitée dans le popover dans le presentViewController du contrôleur de vue PresentViewController. Il y a une vidéo de la WWDC 2014 expliquant ce qui est nouveau avec le popovercontroller où ils abordent cela.

Quoi qu'il en soit, pour les contrôleurs de vue présentés sur la pile de contrôleurs de navigation qui veulent tous leur propre dimensionnement, ces contrôleurs de vue doivent (sous iOS 8) appeler ce code pour définir dynamiquement le PreferredContentSize:

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

Remplacez heightOfTable par votre table calculée ou la hauteur de vue.

Afin d'éviter beaucoup de code en double et de créer une solution commune iOS 7 et iOS 8, j'ai créé une catégorie sur UITableViewController pour effectuer ce travail lorsque viewDidAppear est appelé dans mes vues de table:

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

Category.h:

#import <UIKit/UIKit.h>

@interface UITableViewController (PreferredContentSize)

- (void) setPopOverViewContentSize;

@end

Catégorie.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
la source
Votre astuce avec les presentingViewControllerœuvres. Si je place le preferredContentSizedans, viewDidLoadil y a un comportement étrange: revenir en arrière à partir d'un autre contrôleur de vue dans le popover conduit à un changement de taille de popover incorrect. Il semble qu'une taille de popover de zéro a été définie, mais la taille est correcte. Dans un tel cas, le popover prend toute la hauteur de l'écran. Savez-vous peut-être pourquoi c'est le cas? Je n'ai pas la restriction avec 600 points implémentée car d'après mon expérience, le système d'exploitation ne vous permet pas de spécifier une taille supérieure à la taille de l'écran.
test
Essayez viewDidAppear au lieu de viewDidLoad afin que le code s'exécute lorsque vous remontez la pile.
Wesley Filleman
C'était comme ça que j'ai pris. Mais je ne comprends pas pourquoi cela ne fonctionnera pas si vous définissez cela dans viewDidLoad...
Test du
1
Malheureusement, ce n'est tout simplement pas la façon dont Apple a écrit la pile de vues. Ces valeurs contentSize ne persistent pas vraiment une fois que le viewController est masqué de la vue. C'est pourquoi vous devez "rappeler" le popover chaque fois qu'une vue entre au premier plan par un push ou un pop. Ma recommandation est de déposer un rapport de bogue auprès d'Apple si vous pensez que le popover devrait conserver ces informations.
Wesley Filleman
12

C'est une amélioration de la réponse de Krasnyk .
Votre solution est excellente, mais elle n'est pas correctement animée.
Une petite amélioration donne une belle animation:

Supprimez la dernière ligne de la - (void) forcePopoverSizeméthode:

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

Mettez [self forcePopoverSize] dans la - (void)viewWillAppear:(BOOL)animatedméthode:

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

    [self forcePopoverSize];
}

Et enfin - définissez la taille souhaitée dans la - (void)viewDidAppear:(BOOL)animatedméthode:

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

    CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
    self.contentSizeForViewInPopover = currentSetSizeForPopover;
}
adnako
la source
8

Vous devez redéfinir la taille du contenu dans viewWillAppear . En appelant la méthode delagate dans laquelle vous définissez la taille de popovercontroller. J'ai également eu le même problème. Mais quand j'ai ajouté cela, le problème a été résolu.

Une dernière chose: si vous utilisez des versions bêta inférieures à 5. Alors les popovers sont plus difficiles à gérer. Ils semblent plus conviviaux à partir de la version bêta 5. C'est bien que la version finale soit sortie. ;)

J'espère que cela t'aides.

Madhup Singh Yadav
la source
Je déteste ça aussi. Cela m'a aussi attrapé. Apple: pourquoi ne pouvons-nous pas verrouiller un popover avec navcontroller à une taille spécifiée?!
Jann
2
La définition de la taille du contenu viewWillAppearn'a pas fonctionné pour moi. Définir explicitement la taille du popover a fonctionné, mais c'est ghetto.
Ben Zotto
@quixoto Je ne sais pas quel était votre problème mais j'utilise toujours la même chose et cela fonctionne parfaitement.
Madhup Singh Yadav
5

Dans le -(void)viewDidLoadde tous les contrôleurs de vue que vous utilisez dans le contrôleur de navigation, ajoutez:

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

J'ai réinitialisé la taille dans la méthode animée viewWillDisappear: (BOOL) du contrôleur de vue qui est en cours de navigation depuis:

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

Ensuite, lorsque la vue sur laquelle vous revenez apparaît, je réinitialise la taille de manière appropriée:

-(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
la source
Hmm .. la réinitialisation n'était pas nécessaire. J'ai mis self.contentSizeForViewInPopover = self.view.frame.size dans tous les viewWillAppear de tous les contrôleurs de vue.
seul le
Que mettrais-je pour fetchedResultsController fetchedObjects? Je ne peux pas faire fonctionner ça
Jules
2

Pour iOS 8, les travaux suivants:

- (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 je pense que cela devrait être compatible avec les versions précédentes d'iOS ...

Zéroïde
la source
J'ai eu le problème avec une application dans iOS8 compilée avec le sdk iOS7. Cela a fonctionné, merci.
Grimper
Pour corriger le dimensionnement lors du changement de rotation, appelez cette méthode dans willTransitionToTraitCollection, dans le animateAlongsideTransitionbloc de complétion.
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
la source
1

Pour moi, cette solution fonctionne. Ceci est une méthode de mon contrôleur de vue qui étend UITableViewController et est le contrôleur racine pour UINavigationController.

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

Et n'oubliez pas de définir la taille du contenu pour le contrôleur de vue que vous allez pousser dans la pile de navigation

- (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
la source
1

si vous pouvez imaginer l'assambler, je pense que c'est un peu mieux:

- (void) forcePopoverSize {
    CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
    self.contentSizeForViewInPopover = CGSizeMake (0, 0);
    self.contentSizeForViewInPopover = currentSetSizeForPopover;
}
user1375355
la source
2
encore mieux utilisera à la CGSizeZeroplace le faire par vous-même enCGSizeMake(0,0)
Julian Król
1

La réponse acceptée ne fonctionne pas correctement avec iOS 8. Ce que j'ai fait a été de créer ma propre sous-classe de UINavigationControllerpour l'utiliser dans cette popover et de remplacer la méthode preferredContentSizede cette manière:

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

De plus, au lieu d'appeler forcePopoverSize(méthode implémentée par @krasnyk) dans, viewDidAppearj'ai décidé de définir un viewController (qui montre popover) comme délégué pour la navigation précédemment mentionnée (dans popover) et de faire (ce que fait la méthode force) dans:

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

déléguer une méthode pour un fichier viewController. Une chose importante, faire forcePopoverSizedans une UINavigationControllerDelegateméthode est bien si vous n'avez pas besoin que cette animation soit fluide, alors laissez-la viewDidAppear.

Julian Król
la source
pourquoi voter en baisse? peut-être des commentaires différents que juste un désaccord :)
Julian Król
bonjour @ JulianKról, pouvez-vous jeter un oeil à ce stackoverflow.com/questions/28112617/… , j'ai le même problème.
Ranjit
Merci, cela m'a aidé. Vous voudrez peut-être indiquer clairement en haut de votre commentaire que le problème est que vous devez mettre à jour le navigationController favoriteContentSize ainsi que le visibleVC preferContentSize. Donc, le réglage des deux fonctionne directement aussi.
Michael Kernahan
0

J'étais confronté au même problème, mais vous ne voulez pas définir la taille du contenu dans la méthode 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];

définir la propriété contentSizeForViewInPopover pour ce contrôleur avant de pousser ce contrôleur vers navigationController

Vikas Sawant
la source
0

J'ai eu de la chance en mettant ce qui suit dans le viewdidappear:

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

Bien que cela puisse ne pas s'animer correctement dans le cas où vous poussez / sautez des popovers de différentes tailles. Mais dans mon cas, fonctionne parfaitement!

Chris
la source
0

Tout ce que vous avez à faire est:

-Dans la méthode viewWillAppear du popOvers contentView, ajoutez l'extrait de code ci-dessous. Vous devrez spécifier la taille du popOver la première fois lors de son chargement.

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

J'ai eu ce problème avec un contrôleur popover dont popoverContentSize = CGSizeMake (320, 600) au début, mais qui devenait plus grand lors de la navigation dans son ContentViewController (un UINavigationController).

Le contrôleur de navigation ne faisait que pousser et faire apparaître des UITableViewControllers personnalisés, donc dans viewDidLoad de ma classe de contrôleur de vue de table personnalisée, j'ai défini self.contentSizeForViewInPopover = CGSizeMake (320, 556)

Les 44 pixels de moins doivent tenir compte de la barre de navigation du contrôleur Nav, et maintenant je n'ai plus de problèmes.

leukosaima
la source
0

Mettez ceci dans tous les contrôleurs de vue que vous poussez à l'intérieur du popover

CGSize currentSetSizeForPopover = CGSizeMake(260, 390);
CGSize fakeMomentarySize = CGSizeMake(currentSetSizeForPopover.width - 1.0f,
                                      currentSetSizeForPopover.height - 1.0f);
self.contentSizeForViewInPopover = fakeMomentarySize;
self.contentSizeForViewInPopover = currentSetSizeForPopover;
alok
la source
dans viewwillAppear method.and currentSetSizeForPopover définissez la taille souhaitée.
alok
0

J'ai rencontré le même problème et l'a résolu en définissant la taille de la vue du contenu sur le contrôleur de navigation et le contrôleur de vue avant que l'initialisation de UIPopoverController ne soit placée.

     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
la source
0

J'aimerais simplement proposer une autre solution, car aucune de ces solutions n'a fonctionné pour moi ...

Je l'utilise en fait avec ce https://github.com/nicolaschengdev/WYPopoverController

Lorsque vous appelez votre popup pour la première fois, utilisez ceci.

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

Ensuite, dans cette fenêtre contextuelle, utilisez ceci.

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

}

Puis répétez pour les vues enfants ...

Jules
la source
0

C'est la manière correcte dans iOS7 de le faire, définissez la taille de contenu préférée dans viewDidLoad dans chaque contrôleur de vue dans la pile de navigation (une seule fois). Ensuite, dans viewWillAppear, obtenez une référence au contrôleur de popover et mettez à jour le contentSize à cet endroit.

-(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
la source
0

La solution @krasnyk fonctionnait bien dans les versions précédentes d'iOS mais ne fonctionnait pas sous iOS8. La solution suivante a fonctionné pour moi.

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

Ce n'est pas la meilleure solution, mais cela fonctionne.

Le nouveau UIPopoverPresentationController a également le problème de redimensionnement :(.

Clément Prem
la source
1
peut-être qu'au lieu d'atteindre le contrôleur de vue depuis la propriété AppDelegate, considérez ma solution (semble être plus propre)
Julian Król
Oui, c'était la solution la plus rapide sans modifier aucune de ma base de code existante. Btw vl try ur solution
Clement Prem
0

Vous devez définir la preferredContentSizepropriété de NavigationController dans viewWillAppear:

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