Popover со встроенным навигационным контроллером не учитывает размер на задней панели - PullRequest
88 голосов
/ 02 мая 2010

У меня есть UIPopoverController, на котором размещен UINavigationController, который содержит небольшую иерархию контроллеров представления.

Я следовал документации и для каждого контроллера представления я устанавливал размер popover-context представления следующим образом:

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

(размер отличается для каждого контроллера)

Это работает, как и ожидалось, поскольку я перемещаюсь вперед в иерархии - поповер автоматически анимирует изменения размера, чтобы соответствовать выдвинутому контроллеру.

Однако, когда я перемещаюсь «Назад» по стеку вида с помощью кнопки «Назад» на панели навигации, размер всплывающего окна не изменяется - он остается настолько большим, насколько достигнуто самое глубокое представление. Это кажется сломанным для меня; Я ожидаю, что поповер учитывает размеры, которые устанавливаются, когда он попадает в стек представлений.

Я что-то упустил?

Спасибо.

Ответы [ 21 ]

91 голосов
/ 17 февраля 2011

Хорошо, я боролся с той же проблемой.Ни одно из перечисленных выше решений не помогло мне, поэтому я решил провести небольшое расследование и выяснить, как это работает.Вот что я обнаружил: - Когда вы устанавливаете contentSizeForViewInPopover в вашем контроллере представления, он не будет изменен самим поповером - даже если размер поповера может измениться при переходе на другой контроллер.- Когда размер поповера изменится при переходе к другому контроллеру при возврате назад, размер поповера не восстанавливается - Изменение размера поповера в viewWillAppear дает очень странную анимацию (когда, скажем, вы popController внутри поповера) -Я не рекомендовал бы это - для меня установка жестко закодированного размера внутри контроллера не будет работать вообще - мои контроллеры должны быть иногда большими, иногда маленькими - контроллер, который представит их, имеет представление о размере, хотя

Решение всех этих проблем заключается в следующем: вы должны сбросить размер currentSetSizeForPopover в viewDidAppear.Но вы должны быть осторожны, когда вы установите тот же размер, который уже был установлен в поле 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 нужного контроллера.

18 голосов
/ 17 ноября 2014

Вот как я решил это для iOS 7 и 8:

В iOS 8 iOS молча оборачивает представление, которое вы хотите во всплывающем окне, в представленный ViewController контроллера представления представленияingController. Есть видео WWDC 2014, объясняющее, что нового появилось в поповерконтроллере, где они касаются этого.

В любом случае, для контроллеров представления, представленных в стеке навигационных контроллеров, которые хотят иметь свой собственный размер, эти контроллеры представления должны (под iOS 8) вызывать этот код для динамической установки предпочитаемого размера объекта:

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

Замените heightOfTable на вашу вычисленную таблицу или высоту представления.

Чтобы избежать большого количества дублирующегося кода и создать общее решение для iOS 7 и iOS 8, я создал категорию на UITableViewController для выполнения этой работы, когда 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
12 голосов
/ 13 октября 2011

Это улучшение ответа 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 голосов
/ 02 мая 2010

Вам необходимо снова установить размер контента в viewWillAppear. Вызывая метод delagate, в котором вы устанавливаете размер popovercontroller. У меня тоже была такая же проблема. Но когда я добавил это, проблема решилась.

Еще одна вещь: если вы используете бета-версии меньше 5. Тогда всплывающие окна сложнее управлять. Они кажутся более дружелюбными с бета-версии 5. Хорошо, что окончательная версия вышла. ;)

Надеюсь, это поможет.

5 голосов
/ 11 сентября 2012

В -(void)viewDidLoad всех контроллеров представления, которые вы используете в навигационном контроллере, добавьте:

[self setContentSizeForViewInPopover:CGSizeMake(320, 500)];
3 голосов
/ 01 июня 2010

Я сбрасываю размер в 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;
}
2 голосов
/ 11 сентября 2015
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 !
2 голосов
/ 29 октября 2014

Для 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 ...

1 голос
/ 20 августа 2010

Для меня это решение работает. Это метод из моего контроллера представления, который расширяет 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 голос
/ 22 октября 2014

Принятый ответ не работает нормально с iOS 8. Я создал собственный подкласс UINavigationController для использования в этом поповере и переопределил метод preferredContentSize следующим образом:

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

Более того, вместо вызова forcePopoverSize (метод, реализованный @krasnyk) в viewDidAppear я решил установить viewController (который показывает popover) в качестве делегата для ранее упомянутой навигации (в popover) и делать (какой метод принудительного вызова) делает) в:

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

метод делегата для переданного viewController. Одна важная вещь, выполнение forcePopoverSize в методе UINavigationControllerDelegate хорошо, если вам не нужна плавная анимация, если это так, тогда оставьте ее в viewDidAppear.

...