Popover со встроенным контроллером навигации не учитывает размер задней панели навигации

89

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

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

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

(размер разный для каждого контроллера)

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

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

Я что-то упускаю?

Спасибо.

Бен Зотто
источник
Где ты настраиваешь размер поповер? Вы сбрасываете его каждый раз, когда отображается контроллер представления (например, в viewWillAppear:)?
Оле Бегеманн
Какую документацию вы имели в виду, вы следовали?
Tom Hamming

Ответы:

94

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

Решение для всей этой боли заключается в следующем: вам нужно сбросить размер currentSetSizeForPopoverв viewDidAppear. Но вы должны быть осторожны, когда вы установите тот же размер, который уже был установлен в поле, 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
Это работает. Мне нужно выяснить, как сделать так, чтобы представление таблицы не стало «черным» в сужающейся области, когда всплывающее окно сжимается, но это решение (наконец!) Действительно позволяет всплывающему окну двигаться к правильному размеру для каждого уровня стека. Благодарность!
Бен Зотто
2
Я обернул его в [UIView beginAnimations: nil context: nil]; и [UIView commitAnimations]; - делает его менее раздражающим.
Дастин
2
Для меня использование self.contentSizeForViewInPopover = CGSizeZero; сохранил строку и имел тот же эффект. Большое спасибо!
rob5408
Я мог бы заставить это решение работать, только если бы я добавил self.contentSizeForPopover = CGSizeZero; на мой взгляд, будет исчезать метод VC, из которого я появлялся.
LightningStryk
18

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

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

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

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
Уэсли Филлеман
источник
Ваш отзыв с presentingViewControllerработами. Если я устанавливаю preferredContentSizein, viewDidLoadпроисходит странное поведение: переход назад с другого контроллера представления в всплывающем окне приводит к неправильному изменению размера всплывающего окна. Похоже, что размер всплывающего окна равен нулю, но размер правильный. В этом случае всплывающее окно занимает всю высоту экрана. Вы знаете, может быть, почему это так? У меня нет ограничения в 600 точек, потому что, по моему опыту, операционная система не позволяет указывать размер больше, чем размер экрана.
тестирование
Попробуйте viewDidAppear вместо viewDidLoad, чтобы код выполнялся при переходе вверх по стеку.
Уэсли Филлеман
Я так поступил. Но я не понимаю, почему это не сработает, если вы установите это в viewDidLoad...
тестирование
1
К сожалению, Apple не так написала стек представлений. Эти значения contentSize на самом деле не сохраняются, когда viewController скрыт от просмотра. Вот почему вы должны «напоминать» всплывающему окну каждый раз, когда представление выходит на передний план нажатием или выталкиванием. Я рекомендую отправить отчет об ошибке в Apple, если вы считаете, что всплывающее окно должно сохранять эту информацию.
Уэсли Филлеман
12

Это улучшение ответа Красника .
Ваше решение отличное, но оно не анимировано плавно.
Небольшое улучшение дает приятную анимацию:

Удалите последнюю строку в - (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. Вызывая метод delagate, в котором вы устанавливаете размер popovercontroller. У меня была такая же проблема. Но когда я добавил это, проблема решилась.

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

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

Мадхуп Сингх Ядав
источник
Я тоже это ненавижу. Меня это тоже зацепило. Apple: почему мы не можем заблокировать всплывающее окно с помощью navcontroller до указанного размера ?!
Янн
2
Установка размера содержимого viewWillAppearу меня не сработала. Явная установка размера всплывающего окна действительно сработала, но это гетто.
Бен Зотто,
@quixoto Я не знаю, в чем была ваша проблема, но я все еще использую то же самое, и он отлично работает.
Мадхуп Сингх Ядав
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 всех контроллеров представления.
alones
Что бы я поставил для fetchedResultsController fetchedObjects? Не могу заставить это работать
Джулс
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 ...

Zeroid
источник
У меня была проблема с приложением в iOS8, скомпилированным с iOS7 sdk. Это сработало, спасибо.
Climbatize
Чтобы исправить размер при изменении поворота, вызовите этот метод willTransitionToTraitCollectionв animateAlongsideTransitionблоке завершения.
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
источник
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;
}
user1375355
источник
2
даже лучше будет использовать CGSizeZeroвместо того, чтобы делать это самостоятельноCGSizeMake(0,0)
Джулиан Крол
1

Общепринятый ответ не работает нормально с прошивкой 8. То , что я сделал создавал свой собственный подкласс UINavigationControllerдля использования в этом и пироге переопределить метод preferredContentSizeследующим образом:

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

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

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

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

Юлиан Крол
источник
почему голосование вниз? может быть, обратная связь отличается от простого несогласия :)
Джулиан Крол
привет @ JulianKról, не могли бы вы взглянуть на этот stackoverflow.com/questions/28112617/… , у меня такая же проблема.
Ранджит
Спасибо, мне это помогло. Возможно, вы захотите прояснить в верхней части своего комментария, что проблема в том, что вам нужно обновить navigationController preferredContentSize, а также visibleVC preferredContentSize. Так что установка их обоих напрямую тоже работает.
Майкл Кернахан,
0

Я столкнулся с той же проблемой, но вы не хотите устанавливать размер содержимого в методе viewWillAppear или 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];

установите свойство contentSizeForViewInPopover для этого контроллера, прежде чем отправлять этот контроллер в navigationController

Викас Савант
источник
0

Мне повезло, я поместил в viewdidappear следующее:

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

Хотя это может не очень хорошо анимироваться в случае, когда вы нажимаете / открываете всплывающие окна разного размера. Но в моем случае работает отлично!

Крис
источник
0

Все, что вам нужно сделать, это:

-В методе viewWillAppear для popOvers contentView добавьте фрагмент, указанный ниже. Вам нужно будет указать размер popOver в первый раз при его загрузке.

CGSize size = CGSizeMake(width,height);
self.contentSizeForViewInPopover = size;
Дипукджаян
источник
0

У меня была эта проблема с контроллером всплывающего окна, у которого popoverContentSize = CGSizeMake (320, 600) в начале, но он увеличивался при навигации через его ContentViewController (UINavigationController).

Контроллер навигации только нажимал и выдвигал пользовательские UITableViewControllers, поэтому в моем классе контроллера пользовательского табличного представления viewDidLoad я установил self.contentSizeForViewInPopover = CGSizeMake (320, 556)

На 44 пикселя меньше приходится на панель навигации контроллера навигации, и теперь у меня больше нет проблем.

лейкосаима
источник
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, рассмотрите мое решение (кажется более чистым)
Джулиан Крол
Да, это было самое быстрое решение без изменения существующей кодовой базы. Btw vl попробуйте ваше решение
Клемент Прем
0

Вам необходимо установить preferredContentSizeсвойство NavigationController в viewWillAppear:

-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationController.preferredContentSize = CGSizeMake(320, 500);}
Пабло Алонсо Гонсалес
источник