iOSアプリのエラー-自分をサブビューとして追加できません


157

このクラッシュレポートを受け取りましたが、デバッグ方法がわかりません。

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

iOSのバージョンは7.0.3です。誰かがこの奇妙なクラッシュを経験していますか?

更新:

コードのどこでこのクラッシュが発生したのかわからないので、コードをここに投稿することはできません。

2回目の更新

以下の答えを見てください。


3
コードを見せていただけますか?
DavidGölzhäuser2013年

43
申し訳ありませんが、私はあなたの過剰反応を理解できません。スタックエラーは問題で明らかです。そのため、最初に、ユーザーに尋ねられたとおりにコードを追加してもらうことができます(1時間だけ尋ねられ、すぐに閉じるように求められます)。第二に、私の答えが明確だったので、理由もなく反対票を受け取りました。質問は「誰もがこの奇妙なクラッシュを経験するのか?」です。そして、私は彼がこれを得た理由を話しました。特にコード内に配置されていなくても。
Tancrede Chazallet 2013年

9
この質問は正解です。この場合、ユーザーは正確なエラーコードを提供できません。彼はどのビューコントローラーで何か問題が発生していることを知らないためです
Ravindra Bagale

16
私たちはCrashlyticsを使用しており、「サブビューとして自分自身を追加することはできません」でアプリをクラッシュさせた30人を超えるユーザーがいます。もちろん、自分自身をサブビューとして追加しようとするコードはありません。バックトレースからは、アプリへの参照はまったくありません。
リッチーハイアット

49
再開する投票; これはiOS7によってもたらされる一般的な問題であり、iOS6で問題のなかったアプリ全体を殺してしまうため、iOSを閉じる人はiOS開発をあまり行いません(私はさまざまな会社の複数のプロジェクトで見ました)。この質問がGoogleのトップヒットであるのは残念ですが、近視眼の少数の人々がそれを閉じました。
アダム

回答:


51

私は最近デバッグしたのと同じようなものに基づいて推測しています... Animated:YESでビューコントローラーをプッシュ(またはポップ)すると、すぐには完了しません。アニメーションの前にもう一度プッシュまたはポップすると、悪いことが起こります。完了します。これが本当に当てはまるかどうかを簡単にテストするには、プッシュ操作とポップ操作を一時的にAnimated:NOに変更して(同期的に完了するように)、クラッシュが解消されるかどうかを確認します。これが本当に問題であり、アニメーションをオンに戻したい場合、正しい戦略はUINavigationControllerDelegateプロトコルを実装することです。これには、アニメーションの完了後に呼び出される次のメソッドが含まれます。

navigationController:didShowViewController:animated:

基本的に、必要に応じていくつかのコードをこのメソッドに移動し、アニメーションが終了してスタックがさらに変更できるようになるまで、NavigationControllerスタックを変更する可能性のある他のアクションが発生しないようにします。


iOS 4についてさかのぼって、IIRCのアプリの1つで似たようなものを見ました。2つのアニメーション化されたプッシュ/ポップ操作を連続して実行しないように変更されただけです。もちろん、基礎となるすべてのロジックはそれ以来書き直されていますが、同様のバグがまだ存在しないと信じることは難しくありません。
Hot Licks 14

同じ問題がありました。私の場合、これはアプリが[newViewController setLabelTitle:...]pushViewControllerを呼び出した直後に新しいビューコントローラーのUIを変更する命令を実行したために発生しました。Animated:YES.また、setLabelTitleメソッドをnewViewControllerのviewDidLoadに移動することを解決しました。手がかりをくれてありがとう。
jeprubio 2014年

助かりました。コードを新しいViewControllerに移動することも、それがどのクラスになるかがわかっている場合のオプションです。いずれにせよ、UINavigationControllerDelegateプロトコルのさまざまなメソッドをキャッチすることがますます役立つと感じています。そして、iOS8ではイベントが異なる順序で発生し、以前は同期が多かったものの一部は高速に戻りますが、バックグラウンドで非同期に実行するようにスケジュールしているため、次のような新しいタイミングバグがたくさん発生します。ありがとう、アップル!
RobP 2014年

14

この問題も発生し始めており、同じ問題が原因である可能性が非常に高いです。

私たちのケースでは、場合によってはバックエンドからデータをプルする必要がありました。これは、ユーザーが何かをタップする可能性があり、ナビゲーションプッシュが発生するまでに少し遅延が生じることを意味していました。ユーザーがすばやくタップしていると、同じビューコントローラーから2つのナビゲーションプッシュが発生し、この例外が発生する可能性があります。

私たちのソリューションは、UINavigationControllerのカテゴリであり、特定の時点のトップVCが同じでない限り、プッシュ/ポップを防止します。

.hファイル:

@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ファイル:

@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

これまでのところ、これで問題は解決したようです。例:

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

基本的に、ルールは次のとおりです。ユーザーに関連しない遅延が発生する前に、関連するナビゲーションコントローラーからロックを取得し、それをpush / popへの呼び出しに含めます。

「ロック」という言葉は、ロック解除を必要とする何らかの形式のロックが発生していることをほのめかす可能性があるため、少し不適切な表現かもしれませんが、「ロック解除」メソッドはどこにもないので、おそらく大丈夫です。

(補足として、「ユーザーに関連しない遅延」とは、コードが引き起こしている遅延、つまり非同期のことです。アニメーションでプッシュされるナビゲーションコントローラーをタップしてもカウントされず、navigationLock:バージョンを実行する必要はありません。ケース)


このソリューションを試しているとおっしゃっていましたが、問題は解決しましたか?
Mike D

これまでのところ、はい。問題は再発していません。答えを更新します。
Kalle

4
私はあなたに基づいて修正されたバージョンを使用しました:gist.github.com/mdewolfe/9369751。それはそれを修正したように見えます。
Mike D

2
@Kalleこのソリューションはプッシュ/ポップで機能します。しかし、セグエを使用する場合、このエラーを解決するにはどうすればよいですか?
Geek

@Kadleこれを実装するのを手伝ってくれませんか?stackoverflow.com/q/23247713/1323014 THXを
ご覧

12

このコードは問題を解決しますhttps : //gist.github.com/nonamelive/9334458

プライベートAPIを使用していますが、App Storeで安全であることを確認できます。(このコードを使用する私のアプリの1つがApp Storeによって承認されました。)

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

これはこれまでのところ最高の解決策でしたが、他のいくつかでは、ランダムにダブルプッシュの問題が発生するか、ナビゲーションコントローラがフリーズします。
blueice 2014

このコードはコンパイルできません、何か不足していますか?
Maxime B

8

このクラッシュの詳細をアプリで説明し、回答済みとしてマークします。

私のアプリにはUINavigationControllerがあり、ルートコントローラーはノートオブジェクトのリストを含むUITableViewControllerです。noteオブジェクトには、htmlのcontentプロパティがあります。ノートを選択すると、詳細コントローラに移動します。

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

詳細コントローラー

このコントローラーにはUIWebViewがあり、ルートコントローラーから渡されたノートコンテンツを表示します。

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

このコントローラーは、webviewコントロールのデリゲートです。ノートにリンクが含まれている場合は、リンクをタップしてアプリ内Webブラウザーに移動します。

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

上記のクラッシュレポートを毎日受け取りました。コードのどこでこのクラッシュが発生したのかわかりません。ユーザーの助けを借りて調査したところ、ようやくこのクラッシュを修正することができました。このhtmlコンテンツはクラッシュを引き起こします:

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

詳細コントローラのviewDidLoadメソッドで、このhtmlをwebviewコントロールにロードしました。その直後に、上記のデリゲートメソッドがrequest.URLですぐに呼び出されました。URLはiframeのソース(google.com)です。このデリゲートメソッドは、viewDidLoadでpushViewControllerメソッドを呼び出します=>クラッシュ!

このクラッシュを解決するには、navigationTypeを確認します。

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

お役に立てれば


1
から呼び出されたときにアニメーションなしでコントローラを押すのは良いオプションではないでしょうviewDidLoadか?
Rivera

6

同じ問題がありましたが、単純に機能したのはAnimated:YesをAnimated:Noに変更することでした。

この問題はアニメーションが時間内に完了しなかったことが原因であるようです。

これが誰かを助けることを願っています。


3

このバグを再現するには、2つのView Controllerを同時にプッシュしてみてください。または同時に押してポップします。例:

ここに画像の説明を入力してください これらの呼び出しをインターセプトし、進行中に他のプッシュが発生しないようにすることで、それらを安全にするカテゴリを作成しました。コードをプロジェクトにコピーするだけで、メソッドのスウィズリングにより、問題ありません。

#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

このソリューションの問題の1つは、呼び出すpopToRootViewControllerpopToViewController:、すでにルートビューコントローラーまたはviewControllerに移動している場合にポップされた場合、didShowViewController呼び出されずにスタックすることviewTransitionInProgressです。
divergio 2014年

1
これらの行を説明できますか:self.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)viewController; [self.interactivePopGestureRecognizer setEnabled:YES]; 認識機能が無効になったのはいつですか?そして、どのようにデリゲートがどうあるべきかを知っていますか?これらの行が入っていると、私にとっては、一度ポップした後にポップジェスチャーが壊れます。
divergio 2014年

私はこれを実装しようとしましたが、しばらくするとナビゲーションコントローラがロックされます。これは、おそらく@divergioが言及したためです。
blueice 2014

2

この問題も発生しました。私のコードをお見せしましょう:

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)
 }

この行が原因でエラーが発生します:

firstView.addSubView(firstView)

自分をサブビューに追加することはできません。コード行を次のように変更しました:

firstView.addSubView(secondView)

エラーはなくなり、両方のビューを確認できました。これが例を見たい人に役立つだろうと思っただけです。


私もこのアプローチを試しましたが、スタックトレースは異なり、実際にクラッシュを引き起こしているコードの行を示します。ソースの問題は質問とは異なると思います。
ベン

1

コードで「addSubview」を検索します。

このメソッドを呼び出した場所の1つで、このメソッドを使用して独自のサブビュー配列にビューを追加しようとしました。

例えば:

[self.view addSubview:self.view];

または:

[self.myLabel addSubview:self.myLabel];

エラーが見つかりました。「サブビューとして自分自身を追加できません」が表示された理由を正確に理解しました。View2がナビゲーションコントローラーのルートビューコントローラーであるポイントで、View2をプッシュしました。これにより[View2.view addSubview:View2.view]、サブビューとしてselfが追加されました。
Michal Shatz、2014年

1

任意の時点でアニメーションを使用してビューコントローラーをプッシュ/ポップすることは完全に問題なく、SDKは呼び出しのキューを丁寧に処理する必要があると思います。

したがって、そうではなく、すべてのソリューションが後続のプッシュを無視しようとします。これは、最終的なナビゲーションスタックがコードが意図したものではないため、バグと見なされる可能性があります。

代わりにプッシュコールキューを実装しました。

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

プッシュとポップの混合キューを処理しませんが、ほとんどのクラッシュを修正するのに適しています。

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


私は非常に良いと思われるあなたの解決策を試みてきましたが、私は問題を抱えています。アニメーション化されたNOの2つのビューコントローラーを次々にプッシュすると、最初のビューコントローラーが非常に簡単に表示されます。これは以前には起こりませんでした。それを修正するために私が何ができるか考えていますか?

私は常にこの種のクラッシュを引き起こす可能性のあるプロジェクトを構築しようとしています(私の実際のプロジェクトは、このようなクラッシュレポートを受け取ります)。ナビゲーションコントローラー、ルートコントローラー、および4つの新しいビューコントローラーをナビゲーションスタックにすぐにプッシュし、最後のコントローラーをポップするボタンを使用して、シンプルなアプリを作成しました。特別なサブクラス化などがなければ、実際には問題なく動作するようです。アップルは最近これを修正しましたか?
Cruinh

1

パーティーに遅れてすみません。最近、この問題が発生しました。同時に複数のビューコントローラーを押すと、ナビゲーションバーが破損した状態になります。これは、最初のビューコントローラーがまだアニメーションしているときに他のビューコントローラーがプッシュされるために発生します。nonameliveの答えからヒントを得て、自分のケースで機能する簡単なソリューションを思いつきました。UINavigationControllerpushViewControllerメソッドをサブクラス化してオーバーライドし、前のビューコントローラーアニメーションがまだ終了しているかどうかを確認するだけです。クラスをデリゲートにUINavigationControllerDelegateし、デリゲートをに設定することで、アニメーションの完了を聞くことができますself

簡単にするためにここに要点をアップロードしました。

この新しいクラスをストーリーボードのNavigationControllerとして設定してください。


これまでのところ、私が取り組んでいたアプリのクラッシュは修正されたようです...加えて、解決策は非常に簡単で、ポイントについて明確です:最初のビューコントローラーのアニメーションはまだ完了していません。同じ問題を抱えている人はこれをチェックしてください。
アラスカ、2015年

0

@RobPの素晴らしいヒントに基づいて、このような問題を防ぐためにUINavigationControllerサブクラスを作成しました。プッシュやポップを処理し、安全に実行できます。

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

'acceptConflictingCommands'フラグがtrueの場合(デフォルト)、ユーザーはvc1、vc2、vc3のアニメーションプッシュを表示し、次にvc3のアニメーションポップを表示します。「acceptConflictingCommands」がfalseの場合、vc1が完全にプッシュされるまで、すべてのプッシュ/ポップリクエストが破棄されます。したがって、他の3つの呼び出しは破棄されます。


それらのコマンドは実際に競合していますか?上記のようなコード(ただしSwiftを使用)を使用して、このクラッシュが発生することを確認するための新しいプロジェクトをすぐにまとめ、実際に各プッシュとポップをすべて順番に実行しました。次々と。クラッシュはありません。サブクラスを使用しない。アップルの通常のUINavigationControllerだけです。
Cruinh

それは確かにObjCとiOS 7でクラッシュしていました。今でもそれが発生するかどうかは確認できません。animated:trueフラグ付きのコマンドを実行していますか?
hris.to 2015

はい、animated:trueフラグを使用していました。
Cruinh

0

nonameliveのソリューションは素晴らしいです。ただし、プライベートAPIを使用しない場合は、メソッドをUINavigationControllerDelegate実行するか、アニメーションYESをに変更できますNO。これがコードのサンプルです。継承できます。お役に立てば幸いです:)

https://github.com/antrix1989/ANNavigationController



0

誤って独自のビューにビューを追加しようとしたことがあります。

halfView.addSubview(halfView)

これをサブビューに変更します。

halfView.addSubview(favView)

0

私もこの問題に遭遇しました。Firebaseのログ分析を行ったところ、この問題はアプリがコールドスタートしたときにのみ発生することがわかりました。そこでこのクラッシュを再現できるデモを作成しました。

また、ウィンドウのルートビューコントローラーが表示されている場合、複数のプッシュを実行しても同じ問題が再び発生することはありません。(AppDelegate.swiftでtestColdStartUp(rootNav)をコメントし、ViewController.swiftでtestColdStartUp()コメントをコメント解除できます)

PS:このクラッシュのシーンをアプリで分析しました。ユーザーがプッシュ通知をクリックしてアプリをコールドスタートすると、アプリはまだ起動ページにあり、別のプッシュをクリックしてジャンプします。現時点では、アプリがクラッシュを表示することがあります。私の現在の解決策は、プッシュまたはユニバーサルリンクのコールドスタートをキャッシュしてアプリのジャンプページを開き、rootviewcontrollerが表示されるのを待ってから、実行を遅らせることです。


-2

最後のナビゲーションアニメーションを完了するために、遅延メソッドを使用してナビゲーションを試してください。

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


-2

ビュー自体のビューをサブビューとして追加することはできません。

ビューは親子階層を維持するため、ビュー自体をサブビューとして追加すると、例外が発生します。

クラスがUIViewControllerの場合、そのビューを取得するにはself.viewを使用します。

クラスがUIViewクラスの場合、そのビューを取得するにはselfを使用します。


-3

UiViewControllerクラスになる場合は、セルフをサブビューとして追加できません。UiViewクラスになる場合は、セルフをサブビューとして追加できます。


-9

サブビューをビューに追加したい場合は、次のようにできます。

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

よくできました。これは素晴らしいコードです。これがこの質問と何をしているのか、そしてそれがどのようにそれを解決するのか教えてくれませんか?
Popeye

@ポパイ良いアイデアはありますか?
DavidGölzhäuser2013年

いいえ、問題を再現するのに十分な情報/コードを提供していないためです。したがって、これに答えることができる方法はありません。それは、あなたが彼らの問題とは何の関係もない何かをする方法を彼らに伝えるように見えます。
Popeye

2
彼らが問題を閉じるだけの方がはるかに役立つと思います。とった!StackOverflowで誰かを実際に助けようとしたDavid Gの+1。私はあなたの近い投票を-1にしたいと思います!!! これはまだユーザーに起こっていることであり、私たちが知っているすべてのことはiOS7のバグかもしれません。したがって、誰かが問題のコードを投稿できないからといって、その質問が有効でなく、他のユーザーが見る価値がないというわけではありません。他の人々が同じ問題を見ているのは、論理的な理由がなくても、同じ問題を見ているだけの場合でもです。-rrh
リッチーハイアット
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.