Ошибка приложения 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. Кто-нибудь испытал эту странную аварию?

ОБНОВИТЬ:

Я не знаю, где в моем коде произошел этот сбой, поэтому я не могу опубликовать код здесь, извините.

Второе ОБНОВЛЕНИЕ

Смотрите ответ ниже.

Арнольд
источник
3
Можете ли вы показать нам свой код?
Давид Гольжойзер
43
Извините, но я не понимаю вашу чрезмерную реакцию. Ошибка стека очевидна в проблеме. Итак, во-первых, вы можете позволить пользователю добавить больше кода в соответствии с его запросом (только 1 час на заданный вопрос, и вы просите его закрыть немедленно). Во-вторых, я получил отрицательное голосование без причины, так как мой ответ ясен. Вопрос в том, «Кто-нибудь испытал эту странную аварию?». И я рассказал, почему он получил это. Даже если это не указано в его коде.
Tancrede Chazallet
9
Этот вопрос один правильный. пользователь не может дать точный код ошибки в этой ситуации. потому что он не знает, в каком контроллере представления что-то идет не так
Равиндра Багале
16
Мы используем Crashlytics и у нас более 30 пользователей, которые разбили наше приложение с «Не могу добавить себя как подпредставление», конечно, у нас нет кода, который пытается добавить себя в качестве подпредставления. От следа нет никакой ссылки на наше приложение вообще.
Ричи Хаятт
49
Голосование возобновить; люди, закрывающие его, не делают много iOS-разработчика, по-видимому, так как это общая проблема, представленная iOS7 и убивающая целую кучу приложений, которые были хороши на iOS6 (я видел это в нескольких проектах разных компаний). Жаль, что этот вопрос является самым популярным в Google, но несколько близоруких людей закрыли его.
Адам

Ответы:

51

Я размышляю, основываясь на чем-то похожем, что я недавно отлаживал ... если вы нажимаете (или выдвигаете) контроллер представления с помощью Animated: ДА, он не завершается сразу же, и происходят плохие вещи, если вы делаете еще одно нажатие или высовываете перед анимацией завершается. Вы можете легко проверить, действительно ли это так, временно изменив операции Push и Pop на Animated: NO (чтобы они выполнялись синхронно) и посмотрев, устраняет ли это сбой. Если это действительно ваша проблема, и вы хотите снова включить анимацию, то правильная стратегия - реализовать протокол UINavigationControllerDelegate. Это включает в себя следующий метод, который вызывается после завершения анимации:

navigationController:didShowViewController:animated:

По сути, вы хотите переместить некоторый код по мере необходимости в этот метод, чтобы гарантировать, что никакие другие действия, которые могут вызвать изменение в стеке NavigationController, не будут выполняться, пока анимация не будет завершена и стек не будет готов к дополнительным изменениям.

RobP
источник
Когда-то о iOS 4 - кое-что подобное я видел в одном из наших приложений - IIRC, если вы вставили анимацию, а затем сразу же нажали анимированно, код пользовательского интерфейса был бы испорчен. Закончилось просто изменением, чтобы никогда не выполнять две анимированные операции push / pop вплотную. Конечно, с тех пор вся основная логика была переписана, но нетрудно поверить, что подобная ошибка по-прежнему отсутствует.
Hot Licks
Я была такая же проблема. В моем случае это произошло потому, что приложение выполнило инструкцию, которая изменяет пользовательский интерфейс нового контроллера представления [newViewController setLabelTitle:...]сразу после вызова pushViewController с помощью Animated:YES.И я решил переместить метод setLabelTitle в viewDidLoad на новом ViewViewController. Спасибо, что дали мне подсказку.
января
Рад, что помог! Хорошо, что перемещение кода в новый ViewController также является опцией, если вы знаете, какой это будет класс. Все больше и больше я нахожу полезным поймать различные методы протокола UINavigationControllerDelegate, в любом случае. И я обнаружил, что в iOS8 события запускаются в разных порядках, и некоторые вещи, которые раньше были более или менее синхронными, теперь возвращаются быстро, но планируют вещи для выполнения на фоне асинхронно, создавая множество новых ошибок синхронизации, подобных этим. Спасибо, Apple!
RobP
14

Мы также начали сталкиваться с этой проблемой, и, скорее всего, наша проблема была вызвана той же проблемой.

В нашем случае нам приходилось извлекать данные из серверной части в некоторых случаях, что означало, что пользователь может что-то нажать, и тогда перед навигацией будет небольшая задержка. Если пользователь быстро переключается, он может получить два навигационных нажатия от одного и того же контроллера представления, что вызвало именно это исключение.

Наше решение - это категория в 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];
}];

По сути, правило таково: перед любыми задержками, не связанными с пользователем, захватите блокировку у соответствующего контроллера Nav и включите ее в вызов push / pop.

Слово «блокировка» может быть слегка некачественным, поскольку оно может указывать на наличие какой-либо формы блокировки, требующей разблокировки, но, поскольку нигде нет метода «разблокировки», вероятно, все в порядке.

(Как примечание, «не связанные с пользователем задержки» - это любые задержки, вызываемые кодом, то есть что-либо асинхронное. Пользователи, нажимающие на контроллер Nav, который анимированно толкается, не учитываются, и для них не нужно делать версию navigationLock: version). случаев.)

Kalle
источник
Так как вы сказали, что пытались это решение, это решило проблему для вас?
Майк Д
До сих пор да. Проблема не всплыла. Я обновлю ответ.
Калле
4
Я использовал модифицированную версию, основанную на вашей: gist.github.com/mdewolfe/9369751 . Похоже, это исправило это.
Майк Д
2
@Kalle Это решение работает для push / pop. Но как решить эту ошибку, если я использую segue?
Компьютерщик
@Kadle Можете ли вы помочь мне реализовать это? Посмотрите stackoverflow.com/q/23247713/1323014 THX
Marckaraujo
12

Этот код решает проблему: https://gist.github.com/nonamelive/9334458

Он использует частный API, но я могу подтвердить, что он безопасен в App Store. (Одно из моих приложений, использующих этот код, было одобрено 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;
}
nonamelive
источник
До сих пор это было лучшее решение, с некоторыми другими я либо получал бы случайную проблему двойного нажатия, либо получал бы замороженный навигационный контроллер.
Blueice
Этот код не компилируется для меня, чего-то не хватает?
Максим Б
8

Я опишу более подробную информацию об этой аварии в моем приложении и пометить это как ответ.

Мое приложение имеет UINavigationController с корневым контроллером UITableViewController, который содержит список объектов заметок. Объект заметки имеет свойство содержимого в html. Выбор заметки перейдет в детальный контроллер.

- (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. Если заметка содержит ссылки, нажмите на ссылку, чтобы перейти в веб-браузер приложения.

- (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 - источник iframe (google.com). Этот метод делегата вызывает метод pushViewController, находясь в viewDidLoad => crash!

Я исправил этот сбой, проверив навигационный тип:

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

Надеюсь это поможет

Арнольд
источник
1
Разве не было бы хорошим вариантом нажать контроллер без анимации при вызове из viewDidLoad?
Ривера
6

У меня была та же проблема, что просто работало для меня, менял Animated: Yes на Animated: No.

Похоже, проблема была в том, что анимация не была завершена вовремя.

Надеюсь, это кому-нибудь поможет.

Lion789
источник
3

Чтобы воспроизвести эту ошибку, попробуйте нажать два контроллера вида одновременно. Или толкать и высовывать одновременно. Пример:

введите описание изображения здесь Я создал категорию, которая перехватывает эти вызовы и делает их безопасными, следя за тем, чтобы никаких других нажатий не происходило во время выполнения. Просто скопируйте код в свой проект, и из-за метода метания вам будет хорошо.

#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
Дан
источник
Одна проблема с этим решением состоит в том, что если вы вызываете popToRootViewControllerили popToViewController:когда вы уже подключены к корневому контроллеру представления или к viewController, то didShowViewControllerне будут вызываться и будут зависать viewTransitionInProgress.
Divergio
1
Можете ли вы объяснить эти строки: self.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)viewController; [self.interactivePopGestureRecognizer setEnabled:YES]; Когда был отключен распознаватель? А откуда вы знаете, каким должен быть делегат? С этими строками, для меня это ломает поп-жест после щелчка один раз.
Divergio
Я попытался реализовать это, и через некоторое время он блокирует навигационный контроллер, вероятно, из-за того, что упомянул @divergio.
blueice
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)

Ошибка ушла, и я смог увидеть оба мнения. Просто подумал, что это поможет любому, кто хочет увидеть пример.

halapgos1
источник
Я также попробовал этот подход, но трассировка стека была бы другой и фактически показывала строку вашего кода, вызывающую сбой. Я считаю, что источник вопроса отличается от вопроса.
Бен
1

Найдите в вашем коде «addSubview».

В одном из мест, где вы вызывали этот метод, вы пытались добавить представление в его собственный массив подвидов, используя этот метод.

Например:

[self.view addSubview:self.view];

Или:

[self.myLabel addSubview:self.myLabel];
Михал Шац
источник
Рад слышать, что вы нашли свою ошибку, и теперь я точно понимаю, почему вы получили "Не могу добавить себя как подпредставление". В тот момент, когда ваш View2 был корневым контроллером представления вашего контроллера навигации, вы нажали View2, что вызвало это: [View2.view addSubview:View2.view]таким образом, добавив self в качестве subview.
Михал Шац
1

Я думаю, что нажатие / выталкивание контроллеров представления с анимацией в любой момент должно быть прекрасно, и SDK должен любезно обработать очередь вызовов для нас.

Следовательно, это не так, и все решения пытаются игнорировать последующие изменения, что можно считать ошибкой, поскольку окончательный стек навигации не соответствует предназначению кода.

Вместо этого я реализовал очередь push-вызовов:

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

Он не обрабатывает смешанные очереди push и pops, но это хороший старт для исправления большинства наших сбоев.

Суть: https://gist.github.com/rivera-ernesto/0bc628be1e24ff5704ae

Rivera
источник
Я пробовал ваше решение, которое кажется очень хорошим, но у меня проблема. При нажатии двух контроллеров представления с анимированным NO один за другим, я очень кратко вижу первый. Этого не было раньше. Любая идея, что я могу сделать, чтобы это исправить?
января
Я пытаюсь создать проект, который может постоянно вызывать такого рода сбои (мой реальный проект получает подобные отчеты о сбоях). Я сделал простое приложение с контроллером навигации, корневым контроллером и кнопкой, которая сразу помещает 4 новых контроллера представления в стек навигации, а затем выскакивает из последнего. Без какого-либо специального подкласса или чего-либо еще, кажется, все работает нормально. Apple это недавно исправила?
Cruinh
1

Извините за опоздание на вечеринку. У меня недавно была эта проблема, когда моя навигационная панель переходила в поврежденное состояние из-за одновременного нажатия более одного контроллера представления. Это происходит потому, что другой контроллер представления перемещается, пока первый контроллер представления все еще анимируется. Взяв подсказку из неамелевского ответа, я нашел простое решение, которое работает в моем случае. Вам просто нужно создать подкласс UINavigationControllerи переопределить метод pushViewController и проверить, завершена ли предыдущая анимация контроллера представления. Вы можете прослушать завершение анимации, сделав свой класс делегатом UINavigationControllerDelegateи задав для него делегат self.

Я загрузил суть здесь, чтобы все было просто.

Просто убедитесь, что вы установили этот новый класс как NavigationController в вашей раскадровке.

nikhil.thakkar
источник
До сих пор, похоже, исправлены сбои в приложении, над которым я работал ... плюс, решение довольно простое и ясное в том, что первая анимация viewcontroller еще не завершена. Люди, имеющие такую ​​же проблему, должны это проверить.
alasker
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' помечает его как истинное (по умолчанию), пользователь увидит анимированное нажатие vc1, vc2, vc3, а затем увидит анимированный щелчок vc3. Если значение acceptConflictingCommands равно false, все запросы push / pop будут отбрасываться до тех пор, пока vc1 не будет полностью передан - следовательно, остальные 3 вызова будут отброшены.

hris.to
источник
эти команды на самом деле противоречат друг другу? Я просто собрал новый быстрый проект, чтобы увидеть, как произошел этот сбой, с использованием кода, подобного тому, что у вас был выше (но в Swift), и он фактически выполнял каждый push и pop, все в последовательности. один за другим. Без падений. Без использования каких-либо подклассов. просто яблочный обычный UINavigationController.
Cruinh
Это был действительно сбой с ObjC и iOS 7. Я не могу подтвердить, происходит ли это все еще сейчас. Вы уверены, что выполняете команды с animated:trueфлагом?
18
Да, я использовал флаг animated: true.
Cruinh
0

Решение nonamelive является удивительным. Но если вы не хотите использовать частный API, вы можете просто достичь UINavigationControllerDelegateметода. Или вы можете изменить анимацию YESна NO. Вот пример кода, вы можете наследовать его. Надеюсь, это полезно :)

https://github.com/antrix1989/ANNavigationController

NSKevin
источник
0

Я много раз искал эту проблему, возможно, она выдвигает два или более VC одновременно, что вызывает проблему с анимацией, вы можете сослаться на это: Can't Add Self as Subview 崩溃 解决 办法

просто убедитесь, что есть один VC на переходный процесс одновременно, удачи.

MichaelMao
источник
0

Иногда вы по ошибке пытались добавить представление к собственному представлению.

halfView.addSubview(halfView)

измените это на ваш подвид.

halfView.addSubview(favView)
Винот Вино
источник
0

Я также столкнулся с этой проблемой. Когда я сделал анализ журнала Firebase, я обнаружил, что эта проблема возникает только при холодном запуске приложения. Поэтому я написал демо, которое может воспроизвести этот сбой.

,

Я также обнаружил, что при отображении корневого viewcontroller окна выполнение нескольких нажатий больше не вызовет той же проблемы. (Вы можете комментировать testColdStartUp (rootNav) в AppDelegate.swift и раскомментировать комментарий testColdStartUp () в ViewController.swift)

PS: я проанализировал сцену этого сбоя в моем приложении. Когда пользователь нажимает push-уведомление для холодного запуска приложения, приложение все еще находится на странице Launch и нажимает еще один push для перехода. В это время в приложении может появиться Crash. Мое текущее решение - кэшировать принудительный запуск или «холодный» запуск универсальной ссылки, чтобы открыть страницу перехода приложения, дождаться отображения rootviewcontroller и затем отложить выполнение.

Джадер Ян
источник
-2

попробуйте навигацию, используя метод задержки, для завершения последней анимации навигации,

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

Наим Парача
источник
-2

Представление не может быть добавлено в качестве подпредставления само по себе.

Представления поддерживают иерархию «родитель-потомок», поэтому, если вы добавляете представление как подпредставление само по себе, оно будет происходить через исключение.

если класс UIViewController, то чтобы получить его представление, вы используете self.view.

если класс UIView Class, то чтобы получить его представление, вы используете self.

Мрадул Кумар
источник
-3

Вы не можете добавить себя как подпредставление, если это будет класс UiViewController. Вы можете добавить себя как подпредставление, если это будет класс UiView.

user1533983
источник
-9

Если вы хотите добавить Subview к View, вы можете сделать это следующим образом;

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 У тебя есть идея получше?
Давид Гольжойзер
Нет, потому что они не предоставили достаточно информации / кода, чтобы воспроизвести проблему. Таким образом, невозможно ответить на этот вопрос, просто вы говорите им, как делать что-то, что не имеет ничего общего с их проблемой.
Попай
2
Я думаю, им было гораздо полезнее просто закрыть вопрос. Понял! +1 за Дэвида Г. за то, что он действительно пытался помочь кому-то в StackOverflow. Я хотел бы -1 ваши близкие голоса !!! Это все еще происходит для пользователей, и это может быть ошибка в iOS7 для всех, кого мы знаем. То, что кто-то не может опубликовать нарушающий код, не означает, что вопрос недействителен и ценен для просмотра другими пользователями. Даже если это просто видеть, что другие люди видят ту же проблему без какой-либо логической причины, почему они видят ее. -rrh
Ричи Хаятт