Gezinti denetleyicisi yığınını, alt görünümleri veya mod denetleyicilerini kullanmadan görünüm denetleyicilerinde değişiklik yapın!


142

NavigationControllers, yönetmek için ViewController yığınlarına ve sınırlı animasyon geçişlerine sahiptir.

Bir görünüm denetleyicisini mevcut bir görünüm denetleyicisine alt görünüm olarak eklemek, olayları alt görünüm denetleyicisine geçirmeyi gerektirir; bu, yönetmek için bir acıdır, küçük sıkıntılarla yüklenir ve genel olarak uygulama sırasında kötü bir saldırı gibi hissedilir (Apple ayrıca bunu yapıyor).

Kalıcı bir görünüm denetleyicisi sunmak, bir görünüm denetleyicisini bir başkasının üstüne yerleştirir ve yukarıda açıklanan olayları geçmesine rağmen, görünüm denetleyicisini gerçekten 'takas etmez', istifler.

Film şeritleri iOS 5 ile sınırlıdır ve neredeyse idealdir, ancak tüm projelerde kullanılamaz.

Birisi, yukarıdaki sınırlamalar olmaksızın görünüm denetleyicilerini değiştirme yolunda bir KATI KOD ÖRNEĞİ sunabilir ve aralarında animasyonlu geçişlere izin verebilir mi?

Yakın bir örnek, ancak animasyon yok: Gezinme denetleyicisi olmadan birden çok iOS özel görünüm denetleyicisi nasıl kullanılır?

Düzenleme: Gezinti Denetleyici kullanımı iyidir, ancak gösterilen geçiş denetleyicisinin tamamen (yığılmış değil) değiştirilmesi gereken animasyonlu geçiş stilleri (yalnızca slayt efektleri değil) olması gerekir. İkinci görünüm denetleyicisinin yığından başka bir görünüm denetleyicisini kaldırması gerekiyorsa, yeterince kapsüllenmez.

Edit 2: iOS 4, bu sorunun temel işletim sistemi olmalı, storyboard'lardan bahsederken (yukarıda) açıklığa kavuşturmalıydım.


1
Gezinti denetleyicisiyle özel animasyon geçişleri yapabilirsiniz. Bu kabul edilebilir olursa, lütfen bu kısıtlamayı sorunuzdan kaldırın, size bir kod örneği göndereceğim.
Richard Brightwell

@Richard, yığını yönetme zorluğunu atlar ve görünüm denetleyicileri arasında farklı animasyonlu geçiş stilleri barındırırsa, gezinti denetleyicisinin kullanımı iyidir!
TigerCoding

Tamam iyi. Sabırsız oldum ve kodu gönderdim. Bir şans ver. Benim için çalışıyor.
Richard Brightwell

@RichardBrightwell burada bir navigasyon denetleyicisi kullanarak görünüm denetleyicileri arasında özel animasyon geçişleri yapabileceğini söylediniz ... nasıl? Bir örnek gönderebilir misiniz? Teşekkürler.
Ördek

Yanıtlar:


108

EDIT: Herhangi bir yönde çalışan yeni cevap. Orijinal cevap yalnızca arayüz dikey yönde olduğunda çalışır. Bu, görünümü farklı bir görünüm olmadan değiştiren b / c görünüm geçiş animasyonları, pencereye eklenen ilk görünümün (ör.window.rootViewController.view.anotherView)En azından bir düzey altındaki görünümlerle gerçekleşmelidir.

Aradığım basit bir konteyner sınıfı uyguladım TransitionController. Bunu https://gist.github.com/1394947 adresinde bulabilirsiniz .

Bir yana, ayrı bir sınıf b / c uygulamayı yeniden kullanmak daha kolay tercih ediyorum. Bunu istemiyorsanız, aynı mantığı doğrudan uygulama temsilcinize uygulayarak TransitionControllersınıf gereksinimini ortadan kaldırabilirsiniz . Ancak ihtiyacınız olan mantık aynı olacaktır.

Aşağıdaki gibi kullanın:

Uygulama temsilcinizde

// add a property for the TransitionController

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    MyViewController *vc = [[MyViewContoller alloc] init...];
    self.transitionController = [[TransitionController alloc] initWithViewController:vc];
    self.window.rootViewController = self.transitionController;
    [self.window makeKeyAndVisible];
    return YES;
}

Herhangi bir görünüm denetleyicisinden yeni bir görünüm denetleyicisine geçmek için

- (IBAction)flipToView
{
    anotherViewController *vc = [[AnotherViewController alloc] init...];
    MyAppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
    [appDelegate.transitionController transitionToViewController:vc withOptions:UIViewAnimationOptionTransitionFlipFromRight];
}

EDIT: Aşağıdaki orijinal cevap - yalnızca portait yönlendirme için çalışır

Bu örnek için aşağıdaki varsayımları yaptım:

  1. rootViewControllerPencerenizin işareti olarak atanmış bir görünüm denetleyiciniz var

  2. Yeni bir görünüme geçtiğinizde, geçerli viewController'ı yeni görünüme sahip olan viewController ile değiştirmek istiyorsunuz. Herhangi bir zamanda, yalnızca geçerli viewController canlıdır (örn. Ayrılmış).

Kod farklı şekilde çalışmak için kolayca değiştirilebilir, kilit nokta animasyonlu geçiş ve tek görünüm denetleyicisidir. Bir görünüm denetleyicisini atama dışında hiçbir yerde tutmadığınızdan emin olun window.rootViewController.

Uygulama temsilcisindeki geçişi canlandırmak için kod

- (void)transitionToViewController:(UIViewController *)viewController
                    withTransition:(UIViewAnimationOptions)transition
{
    [UIView transitionFromView:self.window.rootViewController.view
                        toView:viewController.view
                      duration:0.65f
                       options:transition
                    completion:^(BOOL finished){
                        self.window.rootViewController = viewController;
                    }];
}

Bir görünüm denetleyicisinde örnek kullanım

- (IBAction)flipToNextView
{
    AnotherViewController *anotherVC = [[AnotherVC alloc] init...];
    MyAppDelegate *appDelegate = (MyAppDelegate *)[UIApplication sharedApplication].delegate;
    [appDelegate transitionToViewController:anotherVC
                             withTransition:UIViewAnimationOptionTransitionFlipFromRight];
}

1
Evet çok güzel! Sadece hile yapmakla kalmaz, aynı zamanda çok basit ve temiz bir kod örneğidir. Çok teşekkürler!
TigerCoding

Peyzaj sizin için sorun yaratmıyor mu? Ayrıca: Bu, viewControllers'ın willAppear ve didAppear yöntemlerini tetikliyor mu?
Felix Lamouroux

1
Görünen aramaların yapıldığını biliyorum çünkü onları kaydettim. Bunun yönelim değişikliklerini neden etkileyeceğini de anlamıyorum. Neden böyle olacağını düşündünüz mü?
TigerCoding

1
@XJones harika bir çözüm, ilk başlatmadan sonra karşılaştığım bir sorun dışında iPad uygulamamda oldukça iyi çalışıyor transVC = [TransitionController ... initWithViewController: aUINavCtrlObj]; window.rootVC transVC =; aUINavCtrlObj'deki görünüm üstten 20 piksel doldurulur (diğer bir deyişle alt 20 piksel tüm yönlerde ekran dışı (UIWindow) sınırları dışındadır), ancak [transVC transitionToViewController: anotherVC] uygulamasından sonra dolgu sona erdi. TransitionController loadView, wantsFullScreenLayout = NO çalıştı, ne yapar sadece statusBar altında 20 piksel siyah alan ekler.
Abduliam Rehmanius

1
@AbduliamRehmanius: Aynı problemim vardı. Ben hattını 25 değiştirerek sabit TransitionController.mkarşı UIView *view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];dikkatli testte yüzden, ama sadece iOS'un çok yeni sürümünde bu yöntemi kullandık.
Phil Calvin

67

Apple'ın yeni viewController muhafaza sistemini kullanabilirsiniz. Daha ayrıntılı bilgi için WWDC 2011 oturum videosu " UIViewControllerSınırlama Uygulanması " konusuna bakın.

UIViewControllerİOS5'te yeni olan Containment, üst görünüm denetleyicisine ve içinde bulunan bir dizi alt görünüm denetleyicisine sahip olmanızı sağlar. UISplitViewController bu şekilde çalışır. Bunu yaparken bir üst öğedeki görünüm denetleyicilerini yığınlayabilirsiniz, ancak özel uygulamanız için yalnızca üst bir görünür viewController'dan diğerine geçişi yönetmek için kullanıyorsunuz. Bu, Apple'ın onayladığı şeyleri yapmak ve tek bir çocuk görünümünden animasyon yapmaktır. Artı tüm farklı UIViewAnimationOptiongeçişleri kullanmak olsun !

Ayrıca, UIViewContainment ile, oryantasyon olayları sırasında çocuk görünümünü kontrol etmenin dağınıklığı konusunda endişelenmenize gerek yok. ParentViewController öğenizin döndürme olaylarını child viewControllers'a ilettiğinden emin olmak için aşağıdakileri kullanabilirsiniz.

- (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers{
    return YES;
}

İlk childViewController'ı ayarlamak için ebeveyninizin viewDidLoad yönteminde aşağıdakileri veya benzerlerini yapabilirsiniz:

[self addChildViewController:self.currentViewController];
[self.view addSubview:self.currentViewController.view];
[self.currentViewController didMoveToParentViewController:self];
[self.currentViewController.swapViewControllerButton setTitle:@"Swap" forState:UIControlStateNormal];

alt viewController'ı değiştirmeniz gerektiğinde, üst viewController içinde aşağıdakilerin satırları boyunca bir şey çağırırsınız:

-(void)swapViewControllers:(childViewController *)addChildViewController:aNewViewController{
     [self addChildViewController:aNewViewController];
     __weak __block ViewController *weakSelf=self;
     [self transitionFromViewController:self.currentViewController
                       toViewController:aNewViewController
                               duration:1.0
                                options:UIViewAnimationOptionTransitionCurlUp
                             animations:nil
                             completion:^(BOOL finished) {
                                   [aNewViewController didMoveToParentViewController:weakSelf];

                                   [weakSelf.currentViewController willMoveToParentViewController:nil];
                                   [weakSelf.currentViewController removeFromParentViewController];

                                   weakSelf.currentViewController=[aNewViewController autorelease];
                             }];
 }

Burada tam bir örnek proje yayınladım: https://github.com/toolmanGitHub/stackedViewControllers . Bu diğer proje , UIViewControllerContainment'i çeşitli giriş görünümünde nasıl kullanacağınızı gösterir.Tüm ekranı kaplamamış kontrolör türleri. İyi şanslar


1
Harika bir örnek. Kodu göndermek için zaman ayırdığınız için teşekkür ederiz. Öte yandan, yalnızca iOS 5 ile sınırlıdır. Soruda belirtildiği gibi: "[Storyboard'lar] iOS 5 ile sınırlıdır ve neredeyse idealdir, ancak tüm projelerde kullanılamaz." Müşterilerin büyük bir yüzdesi (yaklaşık% 40?) Hala iOS 4 kullanıyorsa, iOS 4 ve daha yeni sürümlerde çalışan bir şey sağlamaktır.
TigerCoding

Eğer aramamız gerekmez [self.currentViewController willMoveToParentViewController:nil];geçişten önce de?
Felix Lamouroux

@FelixLam - UIViewController kapsamındaki dokümanlar başına, yalnızca addChildViewController yöntemini geçersiz kıldığınızda willMoveToParentViewController öğesini çağırırsınız. Bu örnekte ben çağırıyorum ama geçersiz kılmıyorum.
timthetoolman

2
WWDC'deki demoda, geçişi başlatmadan önce aradıklarını hatırlıyorum, çünkü geçiş, currentVC'nin nil'e taşınacağı anlamına gelmiyor. Bir UITabBarController durumunda geçiş herhangi bir vc'nin üst öğesini değiştirmez. Üst öğe kaldırma didMoveToParentViewController: nil çağırır, ancak irade ... çağrı yok. IMHO
Felix Lamouroux

7

Tamam, sorunun bir navigasyon kontrolörü kullanmadan söylediklerini biliyorum, ama olmamak için bir neden yok. OP uyumaya gidebileceğim yorumlara zamanında cevap vermiyordu. Bana oy verme. :)

Geçerli görünüm denetleyicisini nasıl açacağınız ve bir gezinme denetleyicisi kullanarak yeni bir görünüm denetleyicisine nasıl geçeceğiniz aşağıda açıklanmıştır:

UINavigationController *myNavigationController = self.navigationController;
[[self retain] autorelease];

[myNavigationController popViewControllerAnimated:NO];

PreferencesViewController *controller = [[PreferencesViewController alloc] initWithNibName:nil bundle:nil];

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 0.65];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:myNavigationController.view cache:YES];
[myNavigationController pushViewController:controller animated:NO];
[UIView commitAnimations];

[controller release];

Bu yığın denetleyicileri görüntülemiyor mu?
TigerCoding

1
Evet, çünkü bir navigasyon kontrolörü kullanıyoruz. Bununla birlikte, sorunuzun özü olduğunu düşündüğüm, ne tür geçişler yapabileceğinizle ilgili sınırlamaların üstesinden gelir.
Richard Brightwell

Kapat, ancak büyük sorunlardan biri yığın üzerinde yönetmek için çoklu görünüm denetleyicisi olmasıdır. Görüntü denetleyicilerini tamamen değiştirmenin yolunu arıyorum. =)
TigerCoding

Ah. Benim de böyle bir şeyim olabilir ... bana bir dakika ver. Değilse, bu cevabı sileceğim.
Richard Brightwell

Tamam, iki farklı kod parçasını bir araya getirdim. Bence bu istediğini yapacak.
Richard Brightwell

3

Bu sorunla yeni karşılaştığım ve sınırlı başarıya önceden var olan tüm cevaplarda varyasyonları denediğim için, sonunda nasıl çözdüğümü göndereceğim:

Özel segues bu yazıda açıklandığı gibi , aslında özel segues yapmak gerçekten kolaydır. Ayrıca Arayüz Oluşturucu'ya bağlanmak çok kolaydır, IB'deki ilişkileri görünür tutarlar ve segue'in kaynak / hedef görünümü denetleyicileri tarafından fazla destek gerektirmezler.

Yukarıda bağlanan yayın, navigationController yığınındaki geçerli üstten görünüm denetleyicisini, üstten kaydırmalı bir animasyon kullanarak yenisiyle değiştirmek için iOS 4 kodunu sağlar.

Benim durumumda, benzer bir değiştirme segueinin olmasını istedim, ancak bir FlipFromLeftgeçişle. Ayrıca yalnızca iOS 5+ için desteğe ihtiyacım vardı. Kod:

RAFlipReplaceSegue.h sitesinden:

#import <UIKit/UIKit.h>

@interface RAFlipReplaceSegue : UIStoryboardSegue
@end

RAFlipReplaceSegue.m sitesinden:

#import "RAFlipReplaceSegue.h"

@implementation RAFlipReplaceSegue

-(void) perform
{
    UIViewController *destVC = self.destinationViewController;
    UIViewController *sourceVC = self.sourceViewController;
    [destVC viewWillAppear:YES];

    destVC.view.frame = sourceVC.view.frame;

    [UIView transitionFromView:sourceVC.view
                        toView:destVC.view
                      duration:0.7
                       options:UIViewAnimationOptionTransitionFlipFromLeft
                    completion:^(BOOL finished)
                    {
                        [destVC viewDidAppear:YES];

                        UINavigationController *nav = sourceVC.navigationController;
                        [nav popViewControllerAnimated:NO];
                        [nav pushViewController:destVC animated:NO];
                    }
     ];
}

@end

Şimdi, başka bir segue türü ayarlamak için kontrol tuşuna basıp sürükleyin, ardından Özel bir segue yapın ve özel segue sınıfının adını yazın, et voilà!


Film şeridi olmadan programlı olarak bunu yapmanın bir yolu var mı?
zakdances

Bildiğim kadarıyla, bir segue kullanmak için onu tanımlamalı ve bir storyboard'da bir tanımlayıcı vermelisiniz. UIViewController –performSegueWithIdentifier:sender:yöntemiyle kodda bir segue başlatabilirsiniz .
Ryan Artecona

1
Hiçbir zaman doğrudan viewDidXXX ve viewWillXXX öğelerini aramamalısınız.
Ben Sinclair

2

Uzun zamandır bununla mücadele ettim ve sorunlarımdan biri burada listeleniyor , bu sorunu yaşayıp yaşamadığınızdan emin değilim. Ancak, iOS 4 ile çalışması gerekiyorsa tavsiye ederim.

İlk olarak, yeni bir NavigationControllersınıf oluşturun. Burası tüm kirli işleri yapacağımız yerdir - diğer sınıflar bu gibi örnek yöntemlerini "temiz bir şekilde" çağırabilecektir pushViewController:. Sizin .h:

@interface NavigationController : UIViewController {
    NSMutableArray *childViewControllers;
    UIViewController *currentViewController;
}

- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL))completion;
- (void)addChildViewController:(UIViewController *)childController;
- (void)removeChildViewController:(UIViewController *)childController;

Alt görünüm denetleyicileri dizisi, yığımızdaki tüm görünüm denetleyicileri için bir depo görevi görür. Otomatik olarak tüm döndürme ve yeniden boyutlandırma kodunu NavigationController'görünümünden currentController.

Şimdi, uygulamamızda:

- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL))completion
{
    currentViewController = [toViewController retain];
    // Put any auto- and manual-resizing handling code here

    [UIView animateWithDuration:duration animations:animations completion:completion];

    [fromViewController.view removeFromSuperview];
}

- (void)addChildViewController:(UIViewController *)childController {
    [childViewControllers addObject:childController];
}

- (void)removeChildViewController:(UIViewController *)childController {
    [childViewControllers removeObject:childController];
}

Şimdi kendi özel uygulayabilirsiniz pushViewController:, popViewControllerbu yöntem çağrıları kullanarak ve böyle.

İyi şanslar ve umarım bu yardımcı olur!


Yine görünüm denetleyicilerini mevcut görünüm denetleyicilerinin alt görünümleri olarak eklemeye başvurmalıyız. Bu, mevcut Gezinti Denetleyicisinin yaptığı şeydir, ancak temel olarak onu ve tüm yöntemlerini yeniden yazmamız gerektiği anlamına gelir. Gerçekte, viewWillAppear ve benzeri yöntemleri dağıtmaktan kaçınmalıyız. Bunu yapmanın temiz bir yolu olmadığını düşünmeye başlıyorum. Ancak, zaman ve çaba harcadığınız için teşekkür ederim!
TigerCoding

Sanırım, gerektiğinde görünüm denetleyicileri ekleme ve çıkarma sistemi ile bu çözüm, bu yöntemleri iletmenizi engelliyor.
aopsfan

Emin misiniz? Daha önce benzer bir şekilde görünüm denetleyicilerini değiştirirdim ve her zaman mesajları iletmem gerekiyordu. Aksi onaylayabilir misiniz?
TigerCoding

Hayır o should otomatik tetik, emin değilim, fakat tamamen ortadan sürece görünüm kontrolörleri görünümleri kaldırmak gibi ben varsayılabilir ve onlar görünüyor olarak ekleyin viewWillAppear, viewDidAppearve böyle.
aopsfan

-1
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UINavigationController *viewController = (UINavigationController *)[storyboard instantiateViewControllerWithIdentifier:@"storyBoardIdentifier"];
viewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentViewController:viewController animated:YES completion:nil];

Bu Kodu Deneyin.


Bu kod, bir görünüm denetleyicisinden bir gezinme denetleyicisine sahip başka bir görünüm denetleyicisine geçiş sağlar.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.