iOS uygulama hatası - Kendini alt görünüm olarak ekleyemiyorum


157

Bu kilitlenme raporunu aldım, ancak nasıl hata ayıklayacağımı bilmiyorum.

Fatal Exception NSInvalidArgumentException
Can't add self as subview
0 ...    CoreFoundation  __exceptionPreprocess + 130
1    libobjc.A.dylib     objc_exception_throw + 38
2    CoreFoundation  -[NSException initWithCoder:]
3    UIKit   -[UIView(Internal) _addSubview:positioned:relativeTo:] + 110
4    UIKit   -[UIView(Hierarchy) addSubview:] + 30
5    UIKit   __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke + 1196
6    UIKit   +[UIView(Animation) performWithoutAnimation:] + 72
7    UIKit   -[_UINavigationParallaxTransition animateTransition:] + 732
8    UIKit   -[UINavigationController _startCustomTransition:] + 2616
9    UIKit   -[UINavigationController _startDeferredTransitionIfNeeded:] + 418
10   UIKit   -[UINavigationController __viewWillLayoutSubviews] + 44
11   UIKit   -[UILayoutContainerView layoutSubviews] + 184
12   UIKit   -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 346
13   QuartzCore  -[CALayer layoutSublayers] + 142
14   QuartzCore  CA::Layer::layout_if_needed(CA::Transaction*) + 350
15   QuartzCore  CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 16
16   QuartzCore  CA::Context::commit_transaction(CA::Transaction*) + 228
17   QuartzCore  CA::Transaction::commit() + 314
18   QuartzCore  CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 56

İOS sürümü 7.0.3'tür. Bu tuhaf kazayı yaşayan var mı?

GÜNCELLEME:

Kodumun bu çökmeye neden olduğunu bilmiyorum, bu yüzden kodu buraya gönderemiyorum, üzgünüm.

İkinci GÜNCELLEME

Aşağıdaki cevaba bakınız.


3
Bize kodunuzu gösterebilir misiniz?
David Gölzhäuser

43
Üzgünüm ama aşırı tepkinizi anlamıyorum. Yığın hatası sorun üzerinde açıktır. İlk olarak, kullanıcının kendisine sorulduğu gibi daha fazla kod koymasına izin verebilirsiniz (sorulan sorudan sadece 1 saat sonra hemen kapatmak istersiniz). İkincisi, cevabım açık olduğu için sebepsiz bir oylama aldım. Soru "Bu garip çarpışmayı yaşayan var mı?" Bunu neden aldığını anlattım. Kodunda özel olarak yer almasa bile.
Tancrede Chazallet

9
Bu soru doğru. kullanıcı bu durumda tam hata kodu veremez. çünkü hangi bakış açısı denetleyicisinin yanlış gittiğini bilmiyor
Ravindra Bagale

16
Crashlytics kullanıyoruz ve "Alt görünüm olarak kendini ekleyemiyorum" ile uygulamamızı çökerten 30'dan fazla kullanıcımız var elbette alt görünüm olarak kendini eklemeye çalışan bir kodumuz yok. Backtrace itibaren bizim app hiç referans yoktur.
Richie Hyatt

49
Yeniden açmak için oylama; Görünüşe göre, bu, iOS7 tarafından sunulan ve iOS6'da iyi olan bir sürü uygulamayı öldüren yaygın bir sorun olduğu için çok fazla iOS geliştiricisi yapmıyor (bunu farklı şirketlerden birden fazla projede gördüm). Bu sorunun Google'da çok başarılı olması üzücü, ancak kısa görüşlü birkaç kişi bunu kapattı.
Adam

Yanıtlar:


51

Yakın zamanda hata ayıkladığım benzer bir şeye dayanarak spekülasyon yapıyorum ... Animasyonlu bir görünüm denetleyicisini iterseniz (veya pop) yaparsanız: EVET hemen tamamlanmaz ve animasyondan önce başka bir itme veya pop yaparsanız kötü şeyler olur tamamlamalar. Push ve Pop işlemlerinizi geçici olarak Animasyonlu: HAYIR (eşzamanlı olarak tamamlanmaları için) olarak değiştirerek ve bunun çökmeyi ortadan kaldırıp çözmediğini görerek bunun gerçekten geçerli olup olmadığını kolayca test edebilirsiniz. Bu gerçekten sizin probleminizse ve animasyonu tekrar AÇMAK istiyorsanız, doğru strateji UINavigationControllerDelegate protokolünü uygulamaktır. Bu, animasyon tamamlandıktan sonra çağrılan aşağıdaki yöntemi içerir:

navigationController:didShowViewController:animated:

Temel olarak, animasyon tamamlanana ve yığın daha fazla değişiklik için hazır olana kadar NavigationController yığınında değişikliğe neden olabilecek hiçbir işlem yapılmamasını sağlamak için gerektiğinde bazı kodları bu yönteme taşımak istersiniz.


İOS 4 hakkında bir şey - Uygulamalarımızdan birinde benzer bir şey gördüm - IIRC, canlandırdıysanız ve hemen canlandırdıysanız UI kodu kötü bir şekilde mucked olur. Sonunda sadece iki animasyonlu push / pop işlemini arka arkaya yapmayacak şekilde son buldu. Tabii ki, o zamandan beri tüm temel mantık yeniden yazıldı, ancak benzer bir hatanın hala orada olmadığına inanmak zor değil.
Sıcak Yalama

Aynı sorunu yaşadım. Benim durumumda bu, app [newViewController setLabelTitle:...]pushViewController ile çağırdıktan hemen sonra yeni görünüm denetleyicisinin kullanıcı arabirimini değiştiren bir talimat yürüttüğü için gerçekleşti Animated:YES.ve setLabelTitle yöntemini newViewController'da viewDidLoad'a taşımayı çözdüm. Bana ipucunu verdiğin için teşekkürler.
jeprubio

Yardımcı oldu sevindim! Hangi sınıf olacağını biliyorsanız, yeni ViewController için kod taşımak da bir seçenek iyi bir nokta. Gittikçe daha fazla, her durumda UINavigationControllerDelegate protokolünün çeşitli yöntemlerini yakalamak için yararlı buluyorum. Ve iOS8 olaylarında farklı siparişlerde tetiklendiğini ve az ya da çok senkronize olan bazı şeylerin şimdi hızlı döndüğünü ancak arka plan üzerinde eşzamansız olarak yapılması gereken şeyleri planlayarak, bunun gibi birçok yeni zamanlama hatası oluşturduğunu gördüm. Teşekkürler, Apple!
RobP

14

Biz de bu konuyu ele almaya başladık ve bizimkimizin de aynı sorundan kaynaklanma ihtimali yüksekti.

Bizim durumumuzda, bazı durumlarda verileri arka uçtan almak zorunda kaldık, bu da bir kullanıcının bir şeye dokunabileceği ve nav itmesi gerçekleşmeden önce hafif bir gecikmenin olacağı anlamına geliyordu. Bir kullanıcı hızla dolaşıyorsa, aynı görünüm denetleyicisinden iki gezinme zorlamasıyla sonuçlanabilir ve bu da bu istisnayı tetikler.

Bizim çözümümüz UINavigationController'da, üst vc belirli bir noktadan aynı olana kadar itmeleri / patlamaları önleyen bir kategoridir.

.h dosyası:

@interface UINavigationController (SafePushing)

- (id)navigationLock; ///< Obtain "lock" for pushing onto the navigation controller

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated navigationLock:(id)navigationLock; ///< Uses a horizontal slide transition. Has no effect if the view controller is already in the stack. Has no effect if navigationLock is not the current lock.
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated navigationLock:(id)navigationLock; ///< Pops view controllers until the one specified is on top. Returns the popped controllers. Has no effect if navigationLock is not the current lock.
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated navigationLock:(id)navigationLock; ///< Pops until there's only a single view controller left on the stack. Returns the popped controllers. Has no effect if navigationLock is not the current lock.

@end

.m dosyası:

@implementation UINavigationController (SafePushing)

- (id)navigationLock
{
    return self.topViewController;
}

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated navigationLock:(id)navigationLock
{
    if (!navigationLock || self.topViewController == navigationLock) 
        [self pushViewController:viewController animated:animated];
}

- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated navigationLock:(id)navigationLock
{
    if (!navigationLock || self.topViewController == navigationLock)
        return [self popToRootViewControllerAnimated:animated];
    return @[];
}

- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated navigationLock:(id)navigationLock
{
    if (!navigationLock || self.topViewController == navigationLock)
        return [self popToViewController:viewController animated:animated];
    return @[];
}

@end

Şimdiye kadar bu bizim için sorunu çözmüş görünüyor. Misal:

id lock = _dataViewController.navigationController.navigationLock;
[[MyApi sharedClient] getUserProfile:_user.id success:^(MyUser *user) {
    ProfileViewController *pvc = [[ProfileViewController alloc] initWithUser:user];
    [_dataViewController.navigationController pushViewController:pvc animated:YES navigationLock:lock];
}];

Temel olarak kural şudur: kullanıcı ile ilgili olmayan herhangi bir gecikme ilgili gezinme denetleyicisinden bir kilit alır ve bunu push / pop çağrısına dahil eder.

"Kilit" kelimesi, kilidin açılması gereken bir çeşit kilitlenme olduğunu ima edebileceği için biraz zayıf ifadeler olabilir, ancak hiçbir yerde "kilit açma" yöntemi olmadığından, muhtemelen tamamdır.

(Bir sidenote olarak, "kullanıcı ile ilgili olmayan gecikmeler" kodun neden olduğu herhangi bir gecikmedir, yani eşzamansız bir şeydir. Animasyonla itilen bir nav denetleyicisine dokunan kullanıcılar sayılmaz ve bunlar için navigationLock: sürümünü yapmaya gerek yoktur davaları.)


Bu çözümü denediğinizi söylediğinizden, sorunu sizin için çözdü mü?
Mike D

Şimdiye kadar Evet. Sorun yeniden ortaya çıkmadı. Cevabı güncelleyeceğim.
Kalle

4
Sizinkine göre değiştirilmiş bir sürüm kullandım: gist.github.com/mdewolfe/9369751 . Düzeltmiş gibi görünüyor.
Mike D

2
@Kalle Bu çözüm push / pop için çalışır. Ama segue kullanırsam bu hatayı nasıl çözebilirim?
Geek

@Kadle Bunu uygulamama yardım edebilir misiniz? Bkz. Stackoverflow.com/q/23247713/1323014 THX
Marckaraujo

12

Bu kod sorunu çözüyor: https://gist.github.com/nonamelive/9334458

Özel bir API kullanıyor, ancak App Store güvenli olduğunu onaylayabilirim. (Bu kodu kullanan uygulamalarımdan biri App Store tarafından onaylandı.)

@interface UINavigationController (DMNavigationController)

- (void)didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;

@end

@interface DMNavigationController ()

@property (nonatomic, assign) BOOL shouldIgnorePushingViewControllers;

@end

@implementation DMNavigationViewController

#pragma mark - Push

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if (!self.shouldIgnorePushingViewControllers)
    {
        [super pushViewController:viewController animated:animated];
    }

    self.shouldIgnorePushingViewControllers = YES;
}

#pragma mark - Private API

// This is confirmed to be App Store safe.
// If you feel uncomfortable to use Private API, you could also use the delegate method navigationController:didShowViewController:animated:.
- (void)didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    [super didShowViewController:viewController animated:animated];
    self.shouldIgnorePushingViewControllers = NO;
}

Bu şimdiye kadarki en iyi çözüm oldu, diğerlerinden bazılarıyla hala rastgele çift itme sorununu alırdım ya da donmuş bir navigasyon denetleyicisi alırdım.
blueice

Bu kod benim için derlenmiyor, bir şey eksik mi?
Maxime B

8

Uygulamamda bu kilitlenme hakkında daha fazla ayrıntı açıklayacağım ve bunu yanıt olarak işaretleyeceğim.

Uygulamamın kök denetleyicisiyle birlikte bir UINavigationController'ı var. Not nesnelerinin listesini içeren bir UITableViewController. Note nesnesi, html'de bir content özelliğine sahiptir. Bir not seçin ayrıntı denetleyicisine gidecektir.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    //get note object
    DetailViewController *controller = [[DetailViewController alloc] initWithNote:note];
    [self.navigationController pushViewController:controller animated:YES];
}

Ayrıntı denetleyicisi

Bu denetleyicinin bir UIWebView özelliği vardır, kök denetleyiciden geçen not içeriğini görüntüler.

- (void)viewDidLoad
{
    ...
    [_webView loadHTMLString:note.content baseURL:nil];
    ...
}

Bu denetleyici, web görüntüleme denetiminin temsilcisidir. Not bağlantılar içeriyorsa, uygulama içi web tarayıcısına gidecek bağlantıya dokunun.

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    WebBrowserViewController *browserController = [[WebBrowserViewController alloc] init];
    browserController.startupURL = request.URL;
    [self.navigationController pushViewController:webViewController animated:YES];
    return NO;
}

Yukarıdaki kilitlenme raporunu her gün aldım. Kodumda bu çökmeye neden olduğunu bilmiyorum. Bazıları bir kullanıcının yardımıyla araştırdıktan sonra, sonunda bu çökmeyi çözebildim. Bu html içeriği çökmeye neden olacaktır:

...
<iframe src="http://google.com"></iframe>
...

Ayrıntı denetleyicisinin viewDidLoad yönteminde, bu html'yi web görünümü denetimine yükledim, hemen sonra yukarıdaki temsilci yöntemi hemen request.URL ile iframe'in kaynağı (google.com) çağrıldı. Bu delege yöntemi viewDidLoad => crash sırasında pushViewController yöntemini çağırır!

NavigationType'ı kontrol ederek bu çökmeyi düzelttim:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    if (navigationType != UIWebViewNavigationTypeOther)
    {
        //go to web browser controller
    }
}

Bu yardımcı olur umarım


1
Denetleyiciyi çağrıldığında animasyon olmadan itmek iyi bir seçenek olmaz viewDidLoadmı?
Rivera

6

Aynı sorunu yaşadım, sadece benim için çalışan şey Animasyonlu: Evet, Animasyonlu: Hayır olarak değişiyordu.

Sorun, animasyonun zamanında tamamlanmamasından kaynaklanıyor gibi görünüyor.

Umarım bu birine yardımcı olur.


3

Bu hatayı yeniden oluşturmak için, aynı anda iki görünüm denetleyicisini itmeyi deneyin. Ya da aynı anda itmek ve patlatmak. Misal:

resim açıklamasını buraya girin Bu çağrıları engelleyen ve devam ederken başka hiçbir itme yapılmadığından emin olarak onları güvence altına alan bir kategori oluşturdum. Sadece kodu projenize kopyalayın ve yöntem swizzling nedeniyle gitmek için iyi olacak.

#import "UINavigationController+Consistent.h"
#import <objc/runtime.h>
/// This char is used to add storage for the isPushingViewController property.
static char const * const ObjectTagKey = "ObjectTag";

@interface UINavigationController ()
@property (readwrite,getter = isViewTransitionInProgress) BOOL viewTransitionInProgress;

@end

@implementation UINavigationController (Consistent)

- (void)setViewTransitionInProgress:(BOOL)property {
    NSNumber *number = [NSNumber numberWithBool:property];
    objc_setAssociatedObject(self, ObjectTagKey, number , OBJC_ASSOCIATION_RETAIN);
}


- (BOOL)isViewTransitionInProgress {
    NSNumber *number = objc_getAssociatedObject(self, ObjectTagKey);

    return [number boolValue];
}


#pragma mark - Intercept Pop, Push, PopToRootVC
/// @name Intercept Pop, Push, PopToRootVC

- (NSArray *)safePopToRootViewControllerAnimated:(BOOL)animated {
    if (self.viewTransitionInProgress) return nil;
    if (animated) {
        self.viewTransitionInProgress = YES;
    }
    //-- This is not a recursion, due to method swizzling the call below calls the original  method.
    return [self safePopToRootViewControllerAnimated:animated];

}


- (NSArray *)safePopToViewController:(UIViewController *)viewController animated:(BOOL)animated {
    if (self.viewTransitionInProgress) return nil;
    if (animated) {
        self.viewTransitionInProgress = YES;
    }
    //-- This is not a recursion, due to method swizzling the call below calls the original  method.
    return [self safePopToViewController:viewController animated:animated];
}


- (UIViewController *)safePopViewControllerAnimated:(BOOL)animated {
    if (self.viewTransitionInProgress) return nil;
    if (animated) {
        self.viewTransitionInProgress = YES;
    }
    //-- This is not a recursion, due to method swizzling the call below calls the original  method.
    return [self safePopViewControllerAnimated:animated];
}



- (void)safePushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    self.delegate = self;
    //-- If we are already pushing a view controller, we dont push another one.
    if (self.isViewTransitionInProgress == NO) {
        //-- This is not a recursion, due to method swizzling the call below calls the original  method.
        [self safePushViewController:viewController animated:animated];
        if (animated) {
            self.viewTransitionInProgress = YES;
        }
    }
}


// This is confirmed to be App Store safe.
// If you feel uncomfortable to use Private API, you could also use the delegate method navigationController:didShowViewController:animated:.
- (void)safeDidShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    //-- This is not a recursion. Due to method swizzling this is calling the original method.
    [self safeDidShowViewController:viewController animated:animated];
    self.viewTransitionInProgress = NO;
}


// If the user doesnt complete the swipe-to-go-back gesture, we need to intercept it and set the flag to NO again.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    id<UIViewControllerTransitionCoordinator> tc = navigationController.topViewController.transitionCoordinator;
    [tc notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        self.viewTransitionInProgress = NO;
        //--Reenable swipe back gesture.
        self.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)viewController;
        [self.interactivePopGestureRecognizer setEnabled:YES];
    }];
    //-- Method swizzling wont work in the case of a delegate so:
    //-- forward this method to the original delegate if there is one different than ourselves.
    if (navigationController.delegate != self) {
        [navigationController.delegate navigationController:navigationController
                                     willShowViewController:viewController
                                                   animated:animated];
    }
}


+ (void)load {
    //-- Exchange the original implementation with our custom one.
    method_exchangeImplementations(class_getInstanceMethod(self, @selector(pushViewController:animated:)), class_getInstanceMethod(self, @selector(safePushViewController:animated:)));
    method_exchangeImplementations(class_getInstanceMethod(self, @selector(didShowViewController:animated:)), class_getInstanceMethod(self, @selector(safeDidShowViewController:animated:)));
    method_exchangeImplementations(class_getInstanceMethod(self, @selector(popViewControllerAnimated:)), class_getInstanceMethod(self, @selector(safePopViewControllerAnimated:)));
    method_exchangeImplementations(class_getInstanceMethod(self, @selector(popToRootViewControllerAnimated:)), class_getInstanceMethod(self, @selector(safePopToRootViewControllerAnimated:)));
    method_exchangeImplementations(class_getInstanceMethod(self, @selector(popToViewController:animated:)), class_getInstanceMethod(self, @selector(safePopToViewController:animated:)));
}

@end

Bu çözümle ilgili bir sorun, arama yaparsanız popToRootViewControllerveya popToViewController:zaten kök görünüm denetleyicisinde veya viewController'da patlatıldığında, didShowViewControllerçağrılmayacak ve sıkışacak olmasıdır viewTransitionInProgress.
divergio

1
Şu satırları açıklayabilir misiniz: self.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)viewController; [self.interactivePopGestureRecognizer setEnabled:YES]; Tanıyıcı ne zaman devre dışı bırakıldı? Peki temsilci ne olmalı? Bu çizgilerle, benim için bir kez patladıktan sonra pop hareketini kırar.
divergio

Bunu uygulamayı denedim ve bir süre sonra muhtemelen @divergio'nun bahsettiği şeyden dolayı navigasyon kontrol cihazını kilitledi.
blueice

2

Bu sorunu da yaşadım. Size kodumu göstereyim:

override func viewDidLoad() { 
  super.viewDidLoad()

  //First, I create a UIView
  let firstFrame = CGRect(x: 50, y: 70, height: 200, width: 200)
  let firstView = UIView(frame: firstFrame)
  firstView.addBackgroundColor = UIColor.yellow
  view.addSubview(firstView) 

  //Now, I want to add a subview inside firstView
  let secondFrame = CGRect(x: 20, y:50, height: 15, width: 35)
  let secondView = UIView(frame: secondFrame)
  secondView.addBackgroundColor = UIColor.green
  firstView.addSubView(firstView)
 }

Hata bu satırdan kaynaklanıyor:

firstView.addSubView(firstView)

Alt görünüme kendiniz ekleyemezsiniz. Kod satırını şöyle değiştirdim:

firstView.addSubView(secondView)

Hata gitti ve ben her iki görüşlerini görmek mümkün. Bunun bir örnek görmek isteyen herkese yardımcı olacağını düşündüm.


Ben de bu yaklaşımı denedim ama stacktrace farklı olurdu ve aslında kilitlenmeye neden kod satırını gösterir. Kaynak sorunun sorudan farklı olduğuna inanıyorum.
Ben

1

Kodunuzda "addSubview" ifadesini arayın.

Bu yöntemi çağırdığınız yerlerden birinde, bu yöntemi kullanarak kendi alt görünümler dizisine bir görünüm eklemeye çalıştınız.

Örneğin:

[self.view addSubview:self.view];

Veya:

[self.myLabel addSubview:self.myLabel];

Hatayı bulduğunuzu duyduğuma sevindim ve şimdi "Kendini alt görünüm olarak ekleyemiyorum" iletisini neden aldığınızı anlıyorum. [View2.view addSubview:View2.view]View2'nizin navigasyon denetleyicinizin kök görünüm denetleyicisi olduğu bir noktada View2'yi ittiniz, bu da buna neden oldu: böylece, kendini alt görünüm olarak ekledi.
Michal Shatz

1

Ben herhangi bir noktada animasyon ile görünüm denetleyicileri itme / haşhaş mükemmel iyi olmalı ve SDK nezaketle bizim için çağrı kuyruğu ele gerektiğini düşünüyorum.

Bu nedenle ve tüm çözümler, sonraki gezinme yığını kodun amaçladığı gibi olmadığı için bir hata olarak kabul edilebilecek sonraki itmeleri göz ardı etmeye çalışır.

Bunun yerine bir push çağrıları sırası uyguladım:

// SafeNavigationController.h

@interface SafeNavigationController : UINavigationController
@end

 

// SafeNavigationController.m

#define timeToWaitBetweenAnimations 0.5

@interface SafeNavigationController ()

@property (nonatomic, strong) NSMutableArray * controllersQueue;
@property (nonatomic)         BOOL animateLastQueuedController;
@property (nonatomic)         BOOL pushScheduled;
@property (nonatomic, strong) NSDate * lastAnimatedPushDate;

@end

@implementation SafeNavigationController

- (void)awakeFromNib
{
    [super awakeFromNib];

    self.controllersQueue = [NSMutableArray array];
}

- (void)pushViewController:(UIViewController *)viewController
                  animated:(BOOL)animated
{
    [self.controllersQueue addObject:viewController];
    self.animateLastQueuedController = animated;

    if (self.pushScheduled)
        return;

    // Wait for push animation to finish
    NSTimeInterval timeToWait = self.lastAnimatedPushDate ? timeToWaitBetweenAnimations + [self.lastAnimatedPushDate timeIntervalSinceNow] : 0.0;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((timeToWait > 0.0 ? timeToWait : 0.0) * NSEC_PER_SEC)),
                   dispatch_get_main_queue(), ^
                   {
                       [self pushQueuedControllers];

                       self.lastAnimatedPushDate = self.animateLastQueuedController ? [NSDate date] : nil;
                       self.pushScheduled = NO;
                   });
    self.pushScheduled = YES;
}

- (void)pushQueuedControllers
{
    for (NSInteger index = 0; index < (NSInteger)self.controllersQueue.count - 1; index++)
    {
        [super pushViewController:self.controllersQueue[index]
                         animated:NO];
    }
    [super pushViewController:self.controllersQueue.lastObject
                     animated:self.animateLastQueuedController];

    [self.controllersQueue removeAllObjects];
}

@end

Karışık itme ve pop kuyruklarını işlemez, ancak kazalarımızın çoğunu düzeltmek için iyi bir başlangıçtır.

Gist: https://gist.github.com/rivera-ernesto/0bc628be1e24ff5704ae


Çok iyi görünen çözümünüzü deniyorum ama bir sorunum var. Animasyonlu 2 görünüm kontrolörünü birbiri ardına iterken, ilkini çok kısaca görüyorum. Bu daha önce olmadı. Düzeltmek için ne yapabilirim?
Ocak

Sürekli olarak bu tür kilitlenmelere neden olabilecek bir proje oluşturmaya çalışıyorum (gerçek projem bunun gibi kilitlenme raporları alıyor). Bir gezinme denetleyicisi, kök denetleyicisi ve hemen 4 yeni görünüm denetleyicisini gezinme yığınına iten ve sonuncusunu çıkaran basit bir uygulama yaptım. Herhangi bir özel alt sınıflama veya herhangi bir şey olmadan aslında iyi çalışıyor gibi görünüyor. Apple bunu yakın zamanda düzeltti mi?
Cruinh

1

Partiye geç kaldığım için üzgünüm. Son zamanlarda, aynı anda birden fazla görünüm denetleyicisini itmesi nedeniyle gezinme çubuğumun bozuk duruma geçtiği bu sorunu yaşadım. Bunun nedeni, ilk görünüm denetleyicisi hala hareket halindeyken diğer görünüm denetleyicisinin itilmesidir. İnançsız cevaptan ipucu alarak davamda çalışan basit bir çözüm buldum. Sadece UINavigationControllerpushViewController yöntemini alt sınıfa ayırıp geçersiz kılmanız ve önceki görünüm denetleyicisi animasyonunun henüz tamamlanıp tamamlanmadığını kontrol etmeniz gerekir . Sınıfınızı temsilci yapıp temsilci olarak UINavigationControllerDelegateayarlayarak animasyonun tamamlanmasını dinleyebilirsiniz self.

İşleri basitleştirmek için buraya bir özgeçmiş yükledim .

Bu yeni sınıfı film şeridinizde NavigationController olarak ayarladığınızdan emin olun.


Şimdiye kadar üzerinde çalıştığım uygulamadaki çökmeleri düzeltmiş gibi görünüyor ... artı, çözüm nokta hakkında oldukça basit ve açık: ilk viewcontroller animasyonu henüz tamamlanmadı. Aynı sorunu yaşayan insanlar bunu kontrol etmelidir.
alasker

0

@RobP büyük ipucuna dayanarak bu tür sorunları önlemek için UINavigationController alt sınıfını yaptım . İtme ve / veya patlatmayı idare eder ve şunları yapabilirsiniz:

[self.navigationController pushViewController:vc1 animated:YES];
[self.navigationController pushViewController:vc2 animated:YES];
[self.navigationController pushViewController:vc3 animated:YES];
[self.navigationController popViewControllerAnimated:YES];

'AcceptConflictingCommands' işaretlerse doğru (varsayılan olarak) kullanıcı vc1, vc2, vc3'ün animasyonlu olarak itilmesini görür ve ardından vc3'ün animasyonlu patlamasını görür. 'AcceptConflictingCommands' yanlışsa, tüm push / pop istekleri vc1 tamamen itilene kadar atılır - bu nedenle diğer 3 arama atılır.


bu komutlar aslında çatışıyor mu? Yukarıda (ancak Swift'te) olduğu gibi kod kullanarak bu çökmenin gerçekleştiğini görmek için hızlı ve yeni bir proje attım ve aslında her itme ve pop'u hepsi sırayla yürüttü. birbiri ardına. Çökme yok. Alt sınıf kullanmadan. sadece elmanın düzenli UINavigationController.
Cruinh

Gerçekten ObjC ve iOS 7 ile çöküyordu. Şimdi hala olup olmadığını doğrulayamıyorum. Komutları animated:truebayrakla yürüttüğünüzden emin misiniz ?
hris.to

Evet animasyonlu: gerçek bayrağı kullanıyordum.
Cruinh



0

Bazen yanlışlıkla kendi görünümüne bir görünüm eklemeye çalıştınız.

halfView.addSubview(halfView)

bunu alt görünümünüzle değiştirin.

halfView.addSubview(favView)

0

Bu sorunla da karşılaştım. Firebase günlük analizi yaptığımda, bu sorunun yalnızca uygulama soğuk başlatıldığında oluştuğunu buldum. Bu kazayı yeniden üretebilecek bir demo yazdım .

.

Ayrıca, pencerenin kök görünüm denetleyicisi görüntülendiğinde, birden fazla basmanın aynı soruna neden olmayacağını da buldum. (AppDelegate.swift öğesinde testColdStartUp (rootNav) öğesine yorum yapabilir ve ViewController.swift öğesinde testColdStartUp () yorumunun yorumunu kaldırabilirsiniz)

ps: Uygulamamda bu çökmenin sahnesini analiz ettim. Kullanıcı uygulamayı başlatmak için push bildirimini tıkladığında, uygulama hala Başlat sayfasındadır ve atlamak için başka bir push'i tıklar. Şu anda uygulama Crash görünebilir. Benim akım Çözüm Uygulama atlama sayfasını açmak için push veya Evrensel bağlantı soğuk başlangıcını önbelleğe almak, rootviewcontroller'ın görüntülenmesini beklemek ve ardından yürütmeyi geciktirmektir.


-2

son navigasyon animasyonunu tamamlamak için gecikme yöntemini kullanarak navigasyonunuzu deneyin,

[self performSelector:<#(SEL)#> withObject:<#(id)#> afterDelay:<#(NSTimeInterval)#>]


-2

Bir görünüm kendi içinde alt görünüm olarak eklenemez.

Görünümler bir üst-alt hiyerarşisini korur, bu nedenle kendi içinde bir alt görünüm olarak bir görünüm eklerseniz istisna yoluyla olur.

bir sınıf UIViewController ise, onun görünümünü almak için self.view kullanın.

bir sınıf UIView Sınıf ise o zaman kendi görüşünü almak için kendi kullanın.


-3

bir UiViewController Sınıfı olacaksa kendini alt görünüm olarak ekleyemezsiniz. UiView Sınıfı olacaksa kendini alt görünüm olarak ekleyebilirsiniz.


-9

Bir Görünüme Alt Görünüm eklemek isterseniz, bunu şu şekilde yapabilirsiniz;

UIView *mainview = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)]; //Creats the mainview
    UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)]; //Creates the subview, you can use any kind of Views (UIImageView, UIWebView, UIView…)

    [mainview addSubview:subview]; //Adds subview to mainview

Aferin, güzel bir kod parçası. Şimdi bunun bu soru ile ne ilgisi olduğunu ve nasıl çözdüğünü söyleyebilir misiniz?
Temel Reis

@Popeye Daha iyi bir fikrin var mı?
David Gölzhäuser

Hayır, çünkü sorunu çoğaltmak için yeterli bilgi / kod sağlamadılar. Yani bunun cevaplanmasının bir yolu yok, sadece sorunlarıyla ilgisi olmayan bir şeyi nasıl yapacaklarını söylüyorsun.
Temel Reis

2
Sanırım sadece sorunu kapatmak onlar için çok daha yararlı oldu. Anladım! StackOverflow'da birine yardım etmeye çalıştığı için David G için +1. Keşke senin yakın oy -1 olabilir! Bu hala kullanıcılar için oluyor ve bildiğimiz her şey için iOS7'de bir hata olabilir. Bu nedenle, birisinin rahatsız edici kodu gönderememesi, sorunun diğer kullanıcıların görmesi için geçerli ve değerli olmadığı anlamına gelmez. Başkalarının da aynı sorunu gördüklerini mantıklı bir neden görmeden görmek bile olsa. -rrh
Richie Hyatt
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.