Jak korzystać z automatycznego układu z przejściami kontenerów?
Jak można użyć Auto Layout z metodą przejścia kontenera UIViewController:
-(void)transitionFromViewController:(UIViewController *)fromViewController
toViewController:(UIViewController *)toViewController
duration:(NSTimeInterval)duration
options:(UIViewAnimationOptions)options
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion;
Tradycyjnie, używając sprężyn / rozpórek, ustawiasz początkowe klatki (tuż przed wywołaniem tej metody) i ustawiasz końcowe klatki w bloku animacji, który PRZEKAZUJESZ do metody.
Ta metoda umożliwia dodawanie widoku do hierarchii widoku i uruchamianie animacji za Ciebie.
Problem polega na tym, że nie możemy dodać początkowych ograniczeń w tym samym miejscu (przed wywołaniem metody), ponieważ widok nie został jeszcze dodany do hierarchii widoku.
Jakieś pomysły, Jak mogę użyć tej metody wraz z Auto Layout?
Poniżej znajduje się przykład (Dziękuję) zrobienia tego przy użyciu sprężyn/rozpórek (ramek) http://www.cocoanetics.com/2012/04/containing-viewcontrollers
- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController
{
// XXX We can't add constraints here because the view is not yet in the view hierarchy
// animation setup
toViewController.view.frame = _containerView.bounds;
toViewController.view.autoresizingMask = _containerView.autoresizingMask;
// notify
[fromViewController willMoveToParentViewController:nil];
[self addChildViewController:toViewController];
// transition
[self transitionFromViewController:fromViewController
toViewController:toViewController
duration:1.0
options:UIViewAnimationOptionTransitionCurlDown
animations:^{
}
completion:^(BOOL finished) {
[toViewController didMoveToParentViewController:self];
[fromViewController removeFromParentViewController];
}];
}
4 answers
Zaczynam myśleć o metodzie użyteczności transitionFromViewController: toViewController: czas trwania: opcje: animacje: Ukończenie nie może być wykonane tak, aby działało czysto z automatycznym układem.
Na razie zastąpiłem użycie tej metody wywołaniami bezpośrednio do każdej z metod przechowawczych "niższego poziomu". Jest to nieco więcej kodu, ale wydaje się, że daje większą kontrolę.
Wygląda tak:
- (void) performTransitionFromViewController:(UIViewController*)fromVc toViewController:(UIViewController*)toVc {
[fromVc willMoveToParentViewController:nil];
[self addChildViewController:toVc];
UIView *toView = toVc.view;
UIView *fromView = fromVc.view;
[self.containerView addSubview:toView];
// TODO: set initial layout constraints here
[self.containerView layoutIfNeeded];
[UIView animateWithDuration:.25
delay:0
options:0
animations:^{
// TODO: set final layout constraints here
[self.containerView layoutIfNeeded];
} completion:^(BOOL finished) {
[toVc didMoveToParentViewController:self];
[fromView removeFromSuperview];
[fromVc removeFromParentViewController];
}];
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-03 01:09:11
Prawdziwym rozwiązaniem wydaje się ustawienie ograniczeń w bloku animacji transitionFromViewController:toViewController:duration:options:animations:
.
[self transitionFromViewController:fromViewController
toViewController:toViewController
duration:1.0
options:UIViewAnimationOptionTransitionCurlDown
animations:^{
// SET UP CONSTRAINTS HERE
}
completion:^(BOOL finished) {
[toViewController didMoveToParentViewController:self];
[fromViewController removeFromParentViewController];
}];
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-28 18:36:32
Istnieją dwa rozwiązania w zależności od tego, czy po prostu trzeba ustawić widok za pomocą układu automatycznego (easy), czy też trzeba animować zmiany ograniczeń układu automatycznego (harder).
TL; wersja DR
Jeśli potrzebujesz tylko pozycjonować widok poprzez układ automatyczny, możesz użyć metody -[UIViewController transitionFromViewController:toViewController:duration:options:animations:completion:]
i zainstalować ograniczenia w bloku animacji.
Aby animować zmiany ograniczenia układu automatycznego, należy użyć ogólnego wywołania +[UIView animateWithDuration:delay:options:animations:completion:]
i dodać kontroler potomny regularnie.
Rozwiązanie 1: Pozycjonowanie widoku przez układ Automatyczny
Zajmijmy się pierwszą, łatwą sprawą. W tym scenariuszu widok powinien być ustawiony za pomocą układu automatycznego, aby zmiana wysokości paska stanu (np. poprzez wybranie Toggle in-Call Status Bar), między innymi, nie zepchnęła rzeczy z ekranu.Dla odniesienia, otooficjalny kod Apple dotyczący przejścia z jednego kontrolera widoku do inny:
- (void) cycleFromViewController: (UIViewController*) oldC
toViewController: (UIViewController*) newC
{
[oldC willMoveToParentViewController:nil]; // 1
[self addChildViewController:newC];
newC.view.frame = [self newViewStartFrame]; // 2
CGRect endFrame = [self oldViewEndFrame];
[self transitionFromViewController: oldC toViewController: newC // 3
duration: 0.25 options:0
animations:^{
newC.view.frame = oldC.view.frame; // 4
oldC.view.frame = endFrame;
}
completion:^(BOOL finished) {
[oldC removeFromParentViewController]; // 5
[newC didMoveToParentViewController:self];
}];
}
Zamiast używać ramek jak w powyższym przykładzie, musimy dodać ograniczenia. Pytanie tylko, gdzie je dodać. Nie możemy ich dodać w marker (2) powyżej, ponieważ {[7] } nie jest zainstalowana w hierarchii widoku. Jest on instalowany dopiero w momencie wywołania transitionFromViewController...
(3). Oznacza to, że możemy albo zainstalować ograniczenia zaraz po wywołaniu transitionFromViewController, albo możemy to zrobić jako pierwsza linia w bloku animacji. Oba powinny zadziałać. Jeśli chcesz to zrobić najwcześniej czas, a następnie umieszczenie go w bloku animacji jest droga do zrobienia. Więcej na temat kolejności wywoływania tych bloków zostanie omówione poniżej.
W podsumowaniu, do pozycjonowania za pomocą auto layoutu, użyj szablonu, takiego jak:
- (void)cycleFromViewController:(UIViewController *)oldViewController
toViewController:(UIViewController *)newViewController
{
[oldViewController willMoveToParentViewController:nil];
[self addChildViewController:newViewController];
newViewController.view.alpha = 0;
[self transitionFromViewController:oldViewController
toViewController:newViewController
duration:0.25
options:0
animations:^{
newViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
// create constraints for newViewController.view here
newViewController.view.alpha = 1;
}
completion:^(BOOL finished) {
[oldViewController removeFromParentViewController];
[newViewController didMoveToParentViewController:self];
}];
// or create constraints right here
}
Rozwiązanie 2: animowanie zmian ograniczeń
Animowanie zmian ograniczeń nie jest tak proste, ponieważ nie otrzymujemy wywołania zwrotnego między momentem dołączenia widoku do hierarchii a momentem wywołania bloku animacji za pomocą transitionFromViewController...
metoda.
Dla odniesienia, tutaj jest standardowy sposób dodawania / usuwania kontrolera widoku potomnego:
- (void) displayContentController: (UIViewController*) content;
{
[self addChildViewController:content]; // 1
content.view.frame = [self frameForContentController]; // 2
[self.view addSubview:self.currentClientView];
[content didMoveToParentViewController:self]; // 3
}
- (void) hideContentController: (UIViewController*) content
{
[content willMoveToParentViewController:nil]; // 1
[content.view removeFromSuperview]; // 2
[content removeFromParentViewController]; // 3
}
Porównując te dwie metody i oryginalny cycleFromViewController: opublikowany powyżej, widzimy, że transitionFromViewController dba o dwie rzeczy dla nas:
[self.view addSubview:self.currentClientView];
[content.view removeFromSuperview];
Poprzez dodanie logowania (pominiętego w tym poście), możemy dowiedzieć się, kiedy te metody są wywoływane.
Po zrobieniu tego, wydaje się, że metoda jest zaimplementowana w sposób podobny do następującego:
- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
{
[self.view addSubview:toViewController.view]; // A
animations(); // B
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[fromViewController.view removeFromSuperview];
completion(YES);
});
}
Teraz jest jasne, dlaczego nie można użyć transitionFromViewController do animowania zmian ograniczeń. Pierwszy raz można zainicjować ograniczenia po dodaniu widoku (linia A). Ograniczenia powinny być animowane w bloku animations()
(Linia B), ale nie ma możliwości uruchomienia kodu między tymi dwoma liniami.
Dlatego musimy użyć ręcznego bloku animacji, wzdłuż z standardową metodą animowania zmian ograniczeń :
- (void)cycleFromViewController:(UIViewController *)oldViewController
toViewController:(UIViewController *)newViewController
{
[oldViewController willMoveToParentViewController:nil];
[self addChildViewController:newViewController];
[self.view addSubview:newViewController.view];
newViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
// TODO: create initial constraints for newViewController.view here
[newViewController.view layoutIfNeeded];
// TODO: update constraint constants here
[UIView animateWithDuration:0.25
animations:^{
[newViewController.view layoutIfNeeded];
}
completion:^(BOOL finished) {
[oldViewController.view removeFromSuperview];
[oldViewController removeFromParentViewController];
[newViewController didMoveToParentViewController:self];
}];
}
Ostrzeżenia
Nie jest to równoważne z tym, jak storyboard osadza kontroler widoku kontenera. Na przykład, jeśli porównasz wartość translatesAutoresizingMaskIntoConstraints
osadzonego widoku za pomocą storyboardu z powyższą metodą, raportuje YES
dla storyboardu i NO
(oczywiście, ponieważ wyraźnie ustawiliśmy ją na NO) dla metody, którą polecam powyżej.
Może to prowadzić do niespójności w aplikacji, ponieważ niektóre części systemu zdają się zależeć od przechowalni UIViewController używanej z translatesAutoresizingMaskIntoConstraints
ustawioną na NO
. Na przykład na iPadzie Air (8.4) może pojawić się dziwne zachowanie podczas obracania z pionowego do poziomego.
Proste rozwiązanie wydaje się być, aby translatesAutoresizingMaskIntoConstraints
ustawić na NO
, a następnie ustawić newViewController.view.frame = newViewController.view.superview.bounds
. Jednak, jeśli nie jesteś bardzo ostrożny z tym, kiedy ta metoda jest wywoływana, najprawdopodobniej da ci nieprawidłowy układ wizualny. (Uwaga: sposób, w jaki storyboard zapewnia rozmiar widoku jest prawidłowy przez ustawienie właściwości autoresize
osadzonego widoku na W+H
. Wydrukowanie ramki zaraz po dodaniu subview ujawni również różnicę między storyboard a podejściem programatycznym, co sugeruje, że Apple ustawia ramkę bezpośrednio na zamkniętym widoku.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:15
Mam nadzieję, że twoje pytanie zyska na popularności, ponieważ myślę, że jest dobre. Nie mam dla Ciebie ostatecznej odpowiedzi, ale mogę opisać moje własne doświadczenia z sytuacjami podobnymi do Twoich.
Oto wniosek, który wyciągnąłem z moich doświadczeń: nie można używać układu auto bezpośrednio w widoku głównym kontrolera widoku. Jak tylko ustawię translatesAutoresizingMaskIntoConstraints
na NO
w widoku głównym, zaczynam dostawać błędy - lub gorzej.
viewDidLoad
w aplikacji używającej układu automatycznego:
self.pageViewController = ...
...
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
// could not get constraints to work here (using autoresizing mask)
self.pageViewController.view.frame = self.view.bounds;
[self.pageViewController didMoveToParentViewController:self];
W ten sposób Apple ładuje kontroler widoku potomnego w szablonie Xcode "aplikacja oparta na stronach" –i odbywa się to w projekcie z włączonym układem automatycznym.
Więc na Twoim miejscu spróbowałbym ustawić ramki, aby animować Przejście kontrolera widoku i zobaczymy, co się stanie. Daj znać, jak to działa.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-02 23:04:18