带嵌入式导航控制器的Popover不支持背面导航的尺寸

时间:2010-05-02 03:53:12

标签: ios uiviewcontroller uinavigationcontroller uipopovercontroller

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

我按照文档和每个视图控制器,我设置视图的popover-context大小如下:

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

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

当我在层次结构中向前导航时,这可以正常工作 - 弹出窗口自动动画大小更改以对应于推送的控制器。

但是,当我通过导航栏的“后退”按钮浏览视图堆栈中的“后退”时,弹出窗口不会改变大小 - 它仍然与最深的视图一样大。这似乎对我不利;我希望popover能够尊重弹出视图堆栈时设置的大小。

我错过了什么吗?

感谢。

21 个答案:

答案 0 :(得分:91)

好的,我正在努力解决同样的问题。以上解决方案都不适合我,这就是为什么我决定做一些调查并找出它是如何工作的原因。 这是我发现的: - 当您在视图控制器中设置contentSizeForViewInPopover时,弹出窗口本身不会更改它 - 即使弹出窗口大小可能会在导航到不同控制器时发生变化。 - 当导航到不同的控制器时弹出窗口的大小会发生变化,同时返回时,弹出框的大小不会恢复 - 在viewWillAppear中改变popover的大小给出了非常奇怪的动画(当我们在弹出框中说你的popController时) - 我不推荐它 - 对我来说,设置控制器内的硬编码大小根本不起作用 - 我的控制器有时候很大,有时很小 - 控制器会让他们对大小有所了解

所有痛苦的解决方案如下: 您必须在viewDidAppear中重置currentSetSizeForPopover的大小。但是你必须要小心,当你设置的字段大小与字段currentSetSizeForPopover中设置的大小相同时,弹出窗口不会改变大小。为此,您可以首先设置假大小(与之前设置的大小不同),然后设置适当的大小。即使您的控制器嵌套在导航控制器内,此解决方案仍然有效,当您在控制器之间导航时,popover将相应地更改其大小。

您可以使用以下帮助方法在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 :(得分:18)

以下是我为iOS 7和8解决的问题:

在iOS 8中,iOS默认将弹出窗口中的视图包装到presentViewController视图控制器的presentViewController中。 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

答案 2 :(得分: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;
}

答案 3 :(得分:8)

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

还有一件事:如果你使用的是小于5的beta版本。那么弹出窗口就更难管理了。从beta版本5开始,它们似乎更加友好。最终版本已经出局了。 ;)

希望这有帮助。

答案 4 :(得分:5)

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

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

答案 5 :(得分: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;
}

答案 6 :(得分: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 !

答案 7 :(得分: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;
}

BTW我认为,这应该与以前的iOS版本兼容......

答案 8 :(得分:1)

如果你能想象出这个问题,我认为这稍微好一些:

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

答案 9 :(得分:1)

{8}在iOS 8中运行不正常。我所做的是创建自己的UINavigationController子类以便在该弹出窗口中使用,并以这种方式覆盖方法preferredContentSize: / p>

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

此外,我没有在forcePopoverSize中调用viewDidAppear(由@krasnyk实现的方法),而是决定将viewController(显示popover)设置为前面提到的导航(在弹出窗口中)的委托,并且(使用什么方法):

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

传递viewController的委托方法。一件重要的事情是,如果你不需要那个动画是平滑的,那么在forcePopoverSize方法中进行UINavigationControllerDelegate是很好的,那么请将它保留在viewDidAppear中。

答案 10 :(得分: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]; 
 }

答案 11 :(得分:0)

你所要做的就是:

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

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

答案 12 :(得分:0)

我有一个popover控制器的问题,其开头的popoverContentSize = CGSizeMake(320,600),但在浏览其ContentViewController(UINavigationController)时会变大。

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

少了44个像素来计算导航控制器的导航栏,现在我不再有任何问题了。

答案 13 :(得分:0)

将它放在你在弹出框内推送的所有视图控制器中

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

答案 14 :(得分: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];

答案 15 :(得分: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;

}

然后重复儿童观点......

答案 16 :(得分:0)

这是iOS7中执行此操作的正确方法, 在导航堆栈中的每个视图控制器中的viewDidLoad中设置首选内容大小(仅执行一次)。然后在viewWillAppear中获取对popover控制器的引用并在那里更新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];
}

答案 17 :(得分: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也有调整大小问题:(。

答案 18 :(得分:0)

通过在viewdidappear中添加以下内容我获得了好运:

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

虽然在推送/弹出不同大小的弹出式窗口时,这可能不会很好。但就我而言,完美无缺!

答案 19 :(得分: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属性

答案 20 :(得分:0)

您需要在preferredContentSize中设置NavigationController的viewWillAppear属性:

-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationController.preferredContentSize = CGSizeMake(320, 500);}