Popover z wbudowanym kontrolerem nawigacji nie uwzględnia rozmiaru tylnej nawigacji

89

Mam UIPopoverController hostujący UINavigationController, który zawiera małą hierarchię kontrolerów widoku.

Postępowałem zgodnie z dokumentami i dla każdego kontrolera widoku ustawiam rozmiar kontekstu popover w następujący sposób:

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

(rozmiar inny dla każdego kontrolera)

Działa to zgodnie z oczekiwaniami, gdy przechodzę do przodu w hierarchii - popover automatycznie animuje zmiany rozmiaru, aby odpowiadały wypchniętemu kontrolerowi.

Jednak gdy przechodzę „Wstecz” przez stos widoków za pomocą przycisku Wstecz na pasku nawigacji, okienko podręczne nie zmienia rozmiaru - pozostaje tak duże, jak osiągnięty najgłębszy widok. Wydaje mi się, że to zepsute; Spodziewałbym się, że popover będzie uwzględniał rozmiary, które są ustawione, gdy wyskakuje przez stos widoku.

Czy coś mi brakuje?

Dzięki.

Ben Zotto
źródło
Gdzie ustawiasz rozmiar popover? Czy resetujesz go za każdym razem, gdy wyświetlany jest kontroler widoku (np. W viewWillAppear:)?
Ole Begemann
Jaką dokumentację rozumiesz?
Tom Hamming,

Odpowiedzi:

94

Ok, zmagałem się z tym samym problemem. Żadne z powyższych rozwiązań nie sprawdziło się dla mnie całkiem przyjemnie, dlatego zdecydowałem się trochę zbadać i dowiedzieć się, jak to działa. Oto, co odkryłem: - Kiedy ustawiszcontentSizeForViewInPopoverw kontrolerze widoku nie zostanie zmieniony przez samo okienko popover - nawet jeśli rozmiar okna popover może się zmienić podczas przechodzenia do innego kontrolera. - Kiedy rozmiar popover zmieni się podczas nawigacji do innego kontrolera, podczas cofania rozmiar popover nie przywróci - Zmiana rozmiaru popover w widoku WillAppear daje bardzo dziwną animację (kiedy powiedzmy, że popController wewnątrz popover) - Nie polecałbym tego - dla mnie ustawienie zakodowanego rozmiaru wewnątrz kontrolera w ogóle by nie działało - moje kontrolery muszą być czasami duże, a czasem małe - kontroler, który je przedstawi, ma jednak pojęcie o rozmiarze

Rozwiązanie całego tego bólu jest następujące: Musisz zresetować rozmiar currentSetSizeForPopoverin viewDidAppear. Ale musisz uważać, gdy ustawisz taki sam rozmiar, jaki był już ustawiony w polu, currentSetSizeForPopoverpopover nie zmieni rozmiaru. Aby tak się stało, możesz najpierw ustawić fałszywy rozmiar (który będzie inny niż ustawiony wcześniej), a następnie ustawić odpowiedni rozmiar. To rozwiązanie będzie działać, nawet jeśli kontroler jest zagnieżdżony w kontrolerze nawigacji, a okno podręczne odpowiednio zmieni swój rozmiar, gdy będziesz nawigować z powrotem między kontrolerami.

Możesz łatwo utworzyć kategorię na UIViewController za pomocą następującej metody pomocniczej, która załatwiłaby sprawę z ustawieniem rozmiaru:


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

Następnie po prostu wywołaj go w -viewDidAppearwybranym kontrolerze.

krasnyk
źródło
1
Powyższa kategoria jest jedynym (rozsądnym) sposobem, w jaki mogę to zrobić. Dzięki.
RickiG,
To działa. Muszę wymyślić, jak zapobiec „czernieniu” widoku tabeli w obszarze kurczenia się, gdy popover się kurczy, ale to rozwiązanie (w końcu!) Naprawdę pozwala przesunąć popover do odpowiedniego rozmiaru dla każdego poziomu stosu. Dzięki!
Ben Zotto,
2
Zawinąłem to w [UIView beginAnimations: nil context: nil]; i [UIView commitAnimations]; - sprawia, że ​​jest mniej drażniący.
Dustin
2
Dla mnie, używając self.contentSizeForViewInPopover = CGSizeZero; zapisał linię i miał ten sam efekt. Wielkie dzięki!
rob5408
Mogę tylko uruchomić to rozwiązanie, jeśli dodam self.contentSizeForPopover = CGSizeZero; moim zdaniem WillDisappear metody VC, z której wyskakiwałem.
LightningStryk
18

Oto jak rozwiązałem to na iOS 7 i 8:

W systemie iOS 8 system iOS dyskretnie zawija żądany widok w wyskakującym okienku do presentViewController kontrolera widoku PresentViewController. Jest wideo WWDC z 2014 roku wyjaśniające, co nowego w kontrolerze popover, w którym dotykają tego.

W każdym razie w przypadku kontrolerów widoku prezentowanych na stosie kontrolerów nawigacji, które wszystkie chcą mieć własny rozmiar, te kontrolery widoku muszą (w systemie iOS 8) wywołać ten kod w celu dynamicznego ustawienia preferowanego rozmiaru:

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

Zastąp heightOfTable obliczoną tabelą lub wysokością widoku.

Aby uniknąć dużej ilości duplikatów kodu i stworzyć wspólne rozwiązanie dla iOS 7 i iOS 8, utworzyłem kategorię na UITableViewController, aby wykonać tę pracę, gdy viewDidAppear jest wywoływany w moich tableviews:

- (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
Wesley Filleman
źródło
Twoja rada z presentingViewControllerpracami. Jeśli ustawię preferredContentSizew, viewDidLoadnastępuje dziwne zachowanie: powrót z innego kontrolera widoku w wyskakującym okienku prowadzi do nieprawidłowej zmiany rozmiaru okna podręcznego. Wygląda na to, że ustawiono zerowy rozmiar popover, ale rozmiar jest prawidłowy. W takim przypadku popover zajmuje całą wysokość ekranu. Czy wiesz, być może, dlaczego tak się dzieje? Nie mam ograniczenia przy zaimplementowanych 600 punktach, ponieważ z mojego doświadczenia wynika, że ​​system operacyjny nie pozwala na określenie rozmiaru większego niż rozmiar ekranu.
testowanie
Spróbuj viewDidAppear zamiast viewDidLoad, aby kod był wykonywany podczas przechodzenia do kopii zapasowej stosu.
Wesley Filleman,
To był mój sposób. Ale nie rozumiem, dlaczego to nie zadziała, jeśli ustawisz to w viewDidLoad...
testowanie
1
Niestety nie jest to sposób, w jaki Apple napisał stos widoków. Te wartości contentSize tak naprawdę nie są utrwalane po ukryciu elementu viewController. Dlatego musisz „przypominać” wyskakujące okienko za każdym razem, gdy widok pojawia się na pierwszym planie, naciskając lub pop. Zalecam zgłoszenie błędu do Apple, jeśli uważasz, że popover powinien zachować te informacje.
Wesley Filleman,
12

To ulepszenie odpowiedzi Krasnyka .
Twoje rozwiązanie jest świetne, ale nie jest płynnie animowane.
Mała poprawa daje ładną animację:

Usuń ostatnią linię w - (void) forcePopoverSizemetodzie:

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

Umieść [self forcePopoverSize] w - (void)viewWillAppear:(BOOL)animatedmetodzie:

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

    [self forcePopoverSize];
}

I na koniec - ustaw żądany rozmiar w - (void)viewDidAppear:(BOOL)animatedmetodzie:

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

    CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
    self.contentSizeForViewInPopover = currentSetSizeForPopover;
}
adnako
źródło
8

Musisz ponownie ustawić rozmiar zawartości w viewWillAppear . Wywołując metodę delagate, w której ustawiasz rozmiar kontrolera popover. Miałem też ten sam problem. Ale kiedy to dodałem, problem został rozwiązany.

Jeszcze jedno: jeśli używasz wersji beta mniejszych niż 5. Wtedy popover jest trudniejszy do zarządzania. Wydają się być bardziej przyjazne od wersji beta 5. Dobrze, że ostateczna wersja została wydana. ;)

Mam nadzieję że to pomoże.

Madhup Singh Yadav
źródło
Też tego nienawidzę. Mnie też złapał. Apple: dlaczego nie możemy zablokować popover za pomocą navcontrollera do określonego rozmiaru ?!
Jann
2
Ustawienie rozmiaru treści w viewWillAppearnie działa dla mnie. Ustawienie rozmiaru popovera wyraźnie zadziałało, ale to getto.
Ben Zotto
@quixoto Nie wiem, jaki był twój problem, ale nadal używam tego samego i działa idealnie.
Madhup Singh Yadav
5

We -(void)viewDidLoadwszystkich kontrolerach widoku, których używasz w kontrolerze nawigacji, dodaj:

[self setContentSizeForViewInPopover:CGSizeMake(320, 500)];
MouzmiSadiq
źródło
3

Zresetowałem rozmiar w viewWillDisappear: (BOOL) animowana metoda kontrolera widoku, do którego wracam z:

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

Następnie, gdy pojawia się widok, do którego wracamy, odpowiednio resetuję rozmiar:

-(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;
}
Greg C.
źródło
Hmm ... reset nie był potrzebny. Umieściłem self.contentSizeForViewInPopover = self.view.frame.size we wszystkich kontrolerach widokuWillAppear.
alones
Co umieściłbym dla funkcji fetchedResultsController fetchedObjects? Nie mogę tego uruchomić
Jules
2

W przypadku iOS 8 działa:

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

Swoją drogą myślę, że to powinno być kompatybilne z poprzednimi wersjami iOS ...

Zeroid
źródło
Miałem problem z aplikacją na iOS8 skompilowaną za pomocą SDK iOS7. To zadziałało, dzięki.
Wspinaj się
Aby rozwiązać przy zmianie wielkości rotacji, nazywają tę metodę willTransitionToTraitCollection, w animateAlongsideTransitionbloku zakończenia.
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
źródło
1

U mnie to rozwiązanie działa. Jest to metoda z mojego kontrolera widoku, który rozszerza UITableViewController i jest kontrolerem głównym dla UINavigationController.

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

I nie zapomnij ustawić rozmiaru zawartości dla kontrolera widoku, który zamierzasz umieścić w stosie nawigacyjnym

- (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]; 
 }
Koteg
źródło
1

jeśli możesz sobie wyobrazić asamblera, myślę, że jest trochę lepiej:

- (void) forcePopoverSize {
    CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
    self.contentSizeForViewInPopover = CGSizeMake (0, 0);
    self.contentSizeForViewInPopover = currentSetSizeForPopover;
}
user1375355
źródło
2
jeszcze lepiej będzie używać CGSizeZerozamiast robić to samemu wCGSizeMake(0,0)
Julian Król
1

Odpowiedź Zaakceptowany nie działa dobrze z iOS 8. Co zrobiłem było stworzenie własną podklasę UINavigationControllerdo użytku w tym popover i zastąpić metodę preferredContentSizew ten sposób:

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

Co więcej, zamiast wywoływać forcePopoverSize(metoda implementowana przez @krasnyk) w viewDidAppear, zdecydowałem się ustawić viewController (który pokazuje popover) jako delegata dla wcześniej wspomnianej nawigacji (w popover) i zrobić (co robi metoda force) w:

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

metoda delegata dla przekazanej metody viewController. Jedna ważna rzecz, robienie forcePopoverSizew UINavigationControllerDelegatemetodzie jest w porządku, jeśli nie chcesz, aby animacja była płynna, jeśli tak, zostaw ją viewDidAppear.

Julian Król
źródło
dlaczego głosowanie w dół? może jakieś uwagi inne niż tylko niezgody :)
Julian Król
cześć @ JulianKról, czy możesz rzucić okiem na ten stackoverflow.com/questions/28112617/ ... Mam ten sam problem.
Ranjit
Dzięki, to mi pomogło. Może chcesz, aby w górnej części komentarza było jasno określone, że problem polega na tym, że musisz zaktualizować navigationControllerferredContentSize, a także visibleVC preferowanyContentSize. Więc ustawienie obu z nich bezpośrednio działa również.
Michael Kernahan
0

Miałem ten sam problem, ale nie chcesz ustawiać contentize w metodzie viewWillAppear lub 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];

ustaw właściwość contentSizeForViewInPopover dla tego kontrolera przed wypchnięciem tego kontrolera do navigationController

Vikas Sawant
źródło
0

Miałem szczęście, umieszczając następujące elementy w widoku:

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

Chociaż może to nie być ładnie animowane w przypadku, gdy popychasz / wyskakujesz popover o różnych rozmiarach. Ale w moim przypadku działa idealnie!

Chris
źródło
0

Wszystko, co musisz zrobić, to:

-W metodzie viewWillAppear elementu popOvers contentView, dodaj fragment podany poniżej. Będziesz musiał określić rozmiar popOver po raz pierwszy, gdy jest ładowany.

CGSize size = CGSizeMake(width,height);
self.contentSizeForViewInPopover = size;
Deepukjayan
źródło
0

Miałem ten problem z kontrolerem popover, którego popoverContentSize = CGSizeMake (320, 600) na początku, ale stawał się większy podczas nawigacji po jego ContentViewController (a UINavigationController).

Kontroler nawigacyjny tylko wypychał i wyświetlał niestandardowe UITableViewControllers, więc w klasie viewDidLoad mojej niestandardowej tabeli kontrolera tabeli ustawiłem self.contentSizeForViewInPopover = CGSizeMake (320, 556)

44 mniej pikseli ma uwzględniać pasek nawigacyjny kontrolera Nav, a teraz nie mam już żadnych problemów.

leukosaima
źródło
0

Umieść to we wszystkich kontrolerach widoku, które pchasz w popover

CGSize currentSetSizeForPopover = CGSizeMake(260, 390);
CGSize fakeMomentarySize = CGSizeMake(currentSetSizeForPopover.width - 1.0f,
                                      currentSetSizeForPopover.height - 1.0f);
self.contentSizeForViewInPopover = fakeMomentarySize;
self.contentSizeForViewInPopover = currentSetSizeForPopover;
alok
źródło
w metodzie viewwillAppear. i currentSetSizeForPopover ustaw żądany rozmiar.
alok
0

Rozwiązano ten sam problem i naprawiono go, ustawiając rozmiar widoku zawartości na kontroler nawigacji i kontroler widoku przed umieszczeniem init 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];
Tomasz Dubik
źródło
0

Chciałbym tylko zaproponować inne rozwiązanie, ponieważ żadne z nich nie zadziałało ...

Właściwie używam go z tym https://github.com/nicolaschengdev/WYPopoverController

Kiedy po raz pierwszy dzwonisz do wyskakującego okienka, użyj tego.

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

Następnie w tym wyskakującym okienku użyj tego.

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

}

Następnie powtórz dla widoków podrzędnych ...

Jules
źródło
0

Jest to poprawny sposób w iOS7, aby to zrobić, ustaw preferowany rozmiar zawartości w viewDidLoad w każdym kontrolerze widoku w stosie nawigacji (tylko raz). Następnie w viewWillAppear pobierz odniesienie do kontrolera popover i zaktualizuj tam 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];
}
Anders
źródło
0

Rozwiązanie @krasnyk działało dobrze w poprzednich wersjach iOS, ale nie działało w iOS8. Poniższe rozwiązanie zadziałało dla mnie.

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

Nie jest to najlepsze rozwiązanie, ale działa.

Nowy UIPopoverPresentationController również ma problem ze zmianą rozmiaru :(.

Clement Prem
źródło
1
może zamiast sięgać do kontrolera widoku z właściwości AppDelegate rozważ moje rozwiązanie (wydaje się być czystsze)
Julian Król
Tak, to było najszybsze rozwiązanie bez modyfikowania żadnego z moich istniejących kodów. Btw vl wypróbuj nasze rozwiązanie
Clement Prem
0

Musisz ustawić preferredContentSizewłaściwość NavigationController w viewWillAppear:

-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationController.preferredContentSize = CGSizeMake(320, 500);}
Pablo Alonso González
źródło