带有嵌入式导航控制器的Popover不考虑后导航的大小


89

我有一个托管UINavigationController的UIPopoverController,其中包含一个小的层次的视图控制器。

我遵循了文档,并为每个视图控制器设置了视图的popover-context大小,如下所示:

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

(每个控制器的大小不同)

当我在层次结构中向前导航时,这可以按预期工作-弹出窗口会自动为尺寸更改设置动画效果,以与推送的控制器相对应。

但是,当我通过导航栏的“后退”按钮在视图堆栈中浏览“后退”时,弹出窗口不会改变大小,它的大小与到达的最深视图一样大。这对我来说似乎很糟;我希望弹出窗口在通过视图堆栈弹出时会尊重设置的大小。

我想念什么吗?

谢谢。


您在哪里设置弹出窗口大小?您是否在每次显示视图控制器时重置它(例如中的viewWillAppear:)?
Ole Begemann,2010年

您是指遵循什么文档?
汤姆·汉明

Answers:


94

好吧,我在同一个问题上挣扎。上面的解决方案都不适合我,这就是为什么我决定进行一些调查并找出其工作原理的原因。这是我发现的:-当您设置contentSizeForViewInPopover在您的视图控制器中,它不会由弹出窗口本身更改-即使在导航到其他控制器时弹出窗口大小可能会更改。-当导航到其他控制器时弹出窗口的大小发生变化时,返回时,弹出窗口的大小将无法恢复-在viewWillAppear中更改弹出窗口的大小会产生非常奇怪的动画(假设您在弹出窗口内使用popController)-我不建议这样做-对我来说,在控制器内设置硬编码大小根本不起作用-我的控制器有时必须大而有时小-会向他们展示内容的控制器对大小有想法

解决所有这些麻烦的方法如下:您必须重置currentSetSizeForPopoverviewDidAppear中的大小。但是您必须小心,当您设置的大小与在字段中设置的大小相同时currentSetSizeForPopover,弹出框将不会更改大小。为此,您可以先设置假尺寸(与之前设置的尺寸不同),然后再设置正确的尺寸。即使您的控制器嵌套在导航控制器中,该解决方案仍然有效,当您在控制器之间导航时,弹出窗口也会相应地更改其大小。

您可以使用以下帮助方法轻松地在UIViewController上创建类别,该方法可以通过设置大小来达到目的:


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

然后只需在-viewDidAppear所需控制器中调用它即可。


1
上面的类别是使它起作用的唯一(理智)方法。谢谢。
RickiG 2011年

这有效。我需要弄清楚当弹出窗口缩小时如何防止表格视图在收缩区域中变成“黑色”,但是此解决方案确实(最终!)确实允许弹出窗口移动到每个堆栈级别的正确大小。谢谢!
Ben Zotto

2
我将其包装在[UIView beginAnimations:nil context:nil];中。和[UIView commitAnimations];-减少震动。
达斯汀

2
对我来说,使用self.contentSizeForViewInPopover = CGSizeZero; 保存一行并具有相同的效果。非常感谢!
rob5408

如果添加self.contentSizeForPopover = CGSizeZero,则只能使该解决方案起作用。在我弹出的VC的viewWillDisappear方法中。
LightningStryk

18

这是我为iOS 7和8解决的方法:

在iOS 8中,iOS会将您在弹出窗口中所需的视图静默包装到presentingViewController视图控制器的presentedViewController中。有一个2014年的WWDC视频,解释了popovercontroller的新功能。

无论如何,对于在导航控制器堆栈上显示的视图控制器,它们都希望自己调整大小,这些视图控制器需要(在iOS 8下)调用此代码来动态设置preferredContentSize:

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

用您计算出的表或视图高度替换heightOfTable。

为了避免大量重复代码并创建常见的iOS 7和iOS 8解决方案,我在UITableViewController上创建了一个类别,以便在tableviews中调用viewDidAppear时执行此工作:

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

Category.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

您的presentingViewController作品提示。如果将设置为preferredContentSizein viewDidLoad,则会出现奇怪的行为:从弹出窗口中的另一个视图控制器向后导航会导致错误的弹出窗口大小更改。看起来好像设定了零弹出大小,但是大小正确。在这种情况下,弹出窗口占据整个屏幕高度。您知道为什么会这样吗?我没有实施600点的限制,因为根据我的经验,操作系统不允许您指定大于屏幕尺寸的尺寸。
测试

尝试使用viewDidAppear而不是viewDidLoad,以便在您向上浏览堆栈时执行代码。
Wesley Filleman 2014年

那就是我采取的方式。但是我不明白为什么如果您将其设置为viewDidLoad... ,它将无法正常工作
测试

1
不幸的是,这并不是Apple编写视图堆栈的方式。一旦将viewController从视图中隐藏,这些contentSize值就不会真正保留。这就是为什么每次通过“推”或“弹出”视图进入前景时都必须“提醒”弹出窗口的原因。我的建议是,如果您认为弹出窗口应保留此信息,请向Apple提交错误报告。
Wesley Filleman 2014年

12

这是对krasnyk答案的改进。
您的解决方案很棒,但动画效果不佳。
稍作改进就可以制作出精美的动画:

删除- (void) forcePopoverSize方法中的最后一行:

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

将[self forcePopoverSize]放在- (void)viewWillAppear:(BOOL)animated方法中:

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

    [self forcePopoverSize];
}

最后-在- (void)viewDidAppear:(BOOL)animated方法中设置所需的大小:

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

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

8

您需要在中再次设置内容大小viewWillAppear。通过调用其中设置popovercontroller大小的delagate方法。我也有同样的问题。但是当我添加了这个问题就解决了。

还有一件事:如果您使用的Beta版本小于5,则弹出式窗口更难以管理。从beta 5开始,他们似乎更加友好。最终版本发布了,这很好。;)

希望这可以帮助。


我也讨厌这个 它也抓住了我。苹果:为什么我们不能用navcontroller将弹出窗口锁定到指定的大小?
Jann

2
设置内容大小viewWillAppear对我不起作用。明确设置弹出框的大小确实可以,但这是贫民窟。
Ben Zotto

@quixoto我不是您的问题所在,但我仍然使用相同的东西,并且效果很好。
Madhup Singh Yadav 2010年

5

-(void)viewDidLoad导航控制器中使用的所有视图控制器中,添加:

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

3

我在从以下位置导航回的视图控制器的viewWillDisappear:(BOOL)动画方法中重置了大小:

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

然后,当导航到的视图出现时,我适当地重置了大小:

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

嗯..不需要重置。我将self.contentSizeForViewInPopover = self.view.frame.size放在所有视图控制器的所有viewWillAppear中。
2011年

我该为fetchedResultsController fetchedObjects放置什么?无法
Jules 2014年

2

对于iOS 8,以下工作原理:

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

顺便说一句,我认为这应该与以前的iOS版本兼容...


我在使用iOS7 SDK编译的iOS8中遇到了一个应用程序的问题。这行得通,谢谢。
爬升

要在更改旋转角度时确定大小,请willTransitionToTraitCollectionanimateAlongsideTransition完成块的中调用此方法。
盖瓦

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

对我来说,这种解决方案有效。这是我的视图控制器中的方法,该方法扩展了UITableViewController,并且是UINavigationController的根控制器。

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

并且不要忘记为要推入导航堆栈的视图控制器设置内容大小

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

如果您可以想象攻击者,那么我认为这会更好一些:

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

2
更好的是CGSizeZero自己使用它代替CGSizeMake(0,0)
朱利安·克罗尔(JulianKról)2014年

1

接受的答案是不工作的罚款与iOS 8.我所做的就是创建我自己的子类的UINavigationController用于在酥料饼的使用和覆盖的方法preferredContentSize以这种方式:

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

此外,我决定将viewController(显示forcePopoverSizepopover)viewDidAppear设置为先前提到的导航(在popover中)的委托,而不是调用(由@krasnyk实现的方法),并在以下方法中执行(force方法的作用):

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

通过的委托方法viewController。一件重要的事情是,如果您不需要那种动画来使之平滑,则可以forcePopoverSize使用UINavigationControllerDelegate方法来完成,然后将其保留在中viewDidAppear


为什么不赞成投票?也许有些反馈意见不仅仅只是分歧:)
朱利安·克罗尔(JulianKról)2014年

你好,@JulianKról,你能看看这个stackoverflow.com/questions/28112617/…,我有同样的问题。
兰吉特(Ranjit)2015年

谢谢,这对我有帮助。可能想在您的评论的顶部清楚地表明问题是您需要更新navigationController preferredContentSize以及visibleVC preferredContentSize。因此,直接设置它们两者也可以。
Michael Kernahan

0

我遇到了同样的问题,但是您不想在viewWillAppear或viewWillDisappear方法中设置contentsize。

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

在将该控制器推送到NavigationController之前,为该控制器设置contentSizeForViewInPopover属性


0

通过在viewdidappear中添加以下内容,我很幸运:

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

尽管在您推/弹出不同大小的弹出框时,这可能无法很好地制作动画。但就我而言,效果完美!


0

您要做的就是:

-在popOvers contentView的viewWillAppear方法中,添加下面给出的代码段。首次加载popOver时,必须指定其大小。

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

0

我在popover控制器上遇到了这个问题,该控制器一开始的popoverContentSize = CGSizeMake(320,600),但是在通过其ContentViewController(UINavigationController)导航时会变得更大。

导航控制器仅推送和弹出自定义UITableViewControllers,因此在我的自定义表视图控制器类的viewDidLoad中,我设置了self.contentSizeForViewInPopover = CGSizeMake(320,556)

少了44个像素是Nav控制器的导航栏的原因,现在我不再有任何问题。


0

将其放入要在弹出框内推送的所有视图控制器中

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

在viewwillAppear方法中,并使用currentSetSizeForPopover设置所需的大小。
阿洛克

0

面对相同的问题,并通过在放置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

我只想提供另一个解决方案,因为这些都不适合我...

我实际上正在与此https://github.com/nicolaschengdev/WYPopoverController一起使用

首次调用弹出窗口时,请使用此选项。

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

然后在该弹出窗口中使用它。

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

}

然后重复以获取子视图...


0

这是在iOS7中执行此操作的正确方法,在导航堆栈的每个视图控制器中的viewDidLoad中设置首选的内容大小(仅执行一次)。然后在viewWillAppear中获得对弹出控件的引用,并在那里更新contentSize。

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

@krasnyk解决方案在以前的iOS版本中效果很好,但在iOS8中不起作用。以下解决方案为我工作。

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

这不是最好的解决方案,但它可以工作。

新的UIPopoverPresentationController也具有调整大小的问题:(。


1
也许不是从AppDelegate属性访问视图控制器,而是考虑我的解决方案(似乎更
简洁

是的,这是最快的解决方案,无需修改任何现有代码库。顺便说一句vl尝试解决方案
Clement Prem

0

您需要在中设置preferredContentSizeNavigationController 的属性viewWillAppear

-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationController.preferredContentSize = CGSizeMake(320, 500);}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.