Поповер із вбудованим контролером навігації не відповідає розміру задньої навігації


89

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

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

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

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

Це працює, як очікувалося, коли я рухаюся вперед по ієрархії - поповер автоматично анімує зміни розміру, щоб відповідати натиснутому контролеру.

Однак, коли я переходжу "Назад" через стек подання за допомогою кнопки "Назад" на панелі навігації, поповер не змінює розмір - він залишається таким великим, як найглибший з отриманих видів. Мені це здається зламаним; Я би очікував, що поповер поважатиме розміри, які встановлені, коли він вискакує через стек подання.

Мені чогось не вистачає?

Дякую.


Де ви встановлюєте розмір поповер? Ви скидаєте його кожен раз, коли відображається контролер перегляду (наприклад, у viewWillAppear:)?
Оле Бегеманн,

Яку документацію ви маєте на увазі, за якою стежили?
Том Хеммінг,

Відповіді:


94

Гаразд, я боровся з тією ж проблемою. Жодне з наведених вище рішень не спрацювало для мене досить добре, саме тому я вирішив провести невелике розслідування та з’ясувати, як це працює. Це те, що я виявив: - Коли ви встановлюєтеcontentSizeForViewInPopoverу вашому контролері перегляду він не буде змінений самим поповером - навіть незважаючи на те, що розмір поповера може змінюватися під час переходу до іншого контролера. - Коли розмір поповера буде змінюватися під час переходу до іншого контролера, а при поверненні назад розмір поповера не відновлюється - Зміна розміру поповера у viewWillAppear дає дуже дивну анімацію (коли, скажімо, ви попконтролер всередині поповера) - Я б не рекомендував цього - для мене встановлення жорстко закодованого розміру всередині контролера взагалі не працювало б - мої контролери іноді мають бути великими, іноді малими - контролер, який буде їх представляти, має уявлення про розмір, хоча

Рішення для всього цього болю таке: Ви повинні скинути розмір currentSetSizeForPopoverin 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; на мій поглядWillDisappear метод ВК, з якого я вискакував.
LightningStryk

18

Ось як я це вирішив для iOS 7 і 8:

У iOS 8 iOS мовчки обертає потрібний вигляд у покажчику в представленийViewController контролера подання presentingViewController. Є відео WWDC 2014 року, в якому пояснюється, що нового у контролері popover, де вони торкаються цього.

У будь-якому випадку, для контролерів перегляду, представлених у стеку контролера навігації, які всі хочуть власні розміри, цим контролерам подання потрібно (під iOS 8) викликати цей код, щоб динамічно встановлювати preferContentSize:

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

Категорія м:

#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роботами. Якщо я встановлюpreferredContentSize in viewDidLoad, це дивна поведінка: повернення назад з іншого контролера перегляду в поповері призводить до неправильної зміни розміру поповера. Схоже, розмір поповеру дорівнює нулю, але розмір правильний. У такому випадку поповер займає всю висоту екрану. Чи знаєте ви, можливо, чому це так? У мене немає обмеження на 600 балів, оскільки на моєму досвіді операційна система не дозволяє вказати розмір, що перевищує розмір екрана.
тестування

Спробуйте viewDidAppear замість viewDidLoad, щоб код виконувався під час навігації назад по стеку.
Уеслі Філлеман

Це був спосіб, яким я пішов. Але я не розумію, чому це не спрацює, якщо ви встановите це в viewDidLoad...
тестування

1
На жаль, це просто не так, як Apple написала стек подань. Ці значення contentSize насправді не зберігаються, як тільки viewController буде приховано від зору. Ось чому вам доведеться "нагадувати" поповер кожного разу, коли вигляд виходить на передній план натисканням або натисканням. Я рекомендую подати звіт про помилку в Apple, якщо ви вважаєте, що програвач повинен зберегти цю інформацію.
Wesley Filleman

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


У мене виникла проблема з додатком в iOS8, складеним з iOS7 sdk. Це спрацювало, дякую.
Підніміться

Щоб виправити проклейки при зміні обертання, викличте цей метод в willTransitionToTraitCollection, в animateAlongsideTransitionблоці завершення.
Гева

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)
Джуліан Кроль

1

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

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

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

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

делегований метод для пройденого viewController. Одна важлива річ, робити forcePopoverSizeв UINavigationControllerDelegateметоді добре, якщо вам не потрібно, щоб анімація була плавною, якщо так, то залиште це viewDidAppear.


чому голосування вниз? можливо, якийсь відгук відрізняється від просто розбіжностей :)
Джуліан Круль,

привіт @ JulianKról, ви можете подивитися на цій stackoverflow.com/questions/28112617 / ... , у мене така ж проблема.
Ранджит

Дякую, це мені допомогло. Можливо, хочете чітко пояснити у верхній частині коментаря, що проблема полягає в тому, що вам потрібно оновити навігаційний контролер preferContentSize, а також visibleVC preferContentSize. Тож установка їх обох безпосередньо також працює.
Michael Kernahan

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

У мене виникла проблема з контролером 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;

in viewwillAppear method.and currentSetSizeForPopover встановіть бажаний розмір.
alok

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 отримайте посилання на контролер 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];
}

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 try ur solution
Clement Prem

0

Вам потрібно встановити preferredContentSizeвластивість NavigationController у viewWillAppear:

-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationController.preferredContentSize = CGSizeMake(320, 500);}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.