ข้อผิดพลาดของแอป 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 ทุกคนประสบความผิดพลาดแปลกนี้หรือไม่

UPDATE:

ฉันไม่รู้ว่ารหัสของฉันอยู่ที่ไหนที่ทำให้เกิดความผิดพลาดนี้ดังนั้นฉันจึงไม่สามารถโพสต์รหัสได้ที่นี่ขออภัย

ปรับปรุงครั้งที่สอง

ดูคำตอบด้านล่าง


3
คุณช่วยแสดงรหัสของคุณให้เราดูได้ไหม
David Gölzhäuser

43
ขออภัย แต่ฉันไม่เข้าใจปฏิกิริยาของคุณมากเกินไป ข้อผิดพลาดของสแต็กจะชัดเจนกับปัญหา ดังนั้นก่อนอื่นคุณสามารถให้ผู้ใช้ใส่รหัสเพิ่มเติมตามที่ขอให้เขา (เพียง 1 ชั่วโมงคำถามที่ถามและคุณขอให้ปิดทันที) ที่สองฉันได้รับ downvote โดยไม่มีเหตุผลเนื่องจากคำตอบของฉันชัดเจน คำถามคือ "ทุกคนประสบความผิดพลาดแปลก ๆ นี้" และฉันก็บอกว่าทำไมเขาถึงได้สิ่งนี้ แม้ว่าจะไม่ได้อยู่ในรหัสโดยเฉพาะ
Tancrede Chazallet

9
คำถามนี้ถูกต้องหนึ่งคำถาม ผู้ใช้ไม่สามารถให้รหัสที่แน่นอนของข้อผิดพลาดในสถานการณ์นี้ เพราะเขาไม่รู้ว่าตัวควบคุมดูว่ามีข้อผิดพลาดอะไรบ้าง
Ravindra Bagale

16
เราใช้ Crashlytics และมีผู้ใช้มากกว่า 30 คนที่ชนแอพของเราด้วย "ไม่สามารถเพิ่มตัวเองเป็นมุมมองย่อย" แน่นอนว่าเราไม่มีรหัสที่พยายามเพิ่มตัวเองเป็นมุมมองย่อย จาก backtrace ไม่มีการอ้างอิงถึงแอพของเราเลย
Richie Hyatt

49
การลงคะแนนเพื่อเปิดอีกครั้ง; คนที่ปิดตัวมันไม่ได้ทำอะไรมากนักพัฒนา iOS เนื่องจากนี่เป็นปัญหาทั่วไปที่นำเสนอโดย iOS7 และฆ่าแอพทั้งหมดที่ใช้งานได้ดีบน iOS6 (ฉันเคยเห็นในหลาย ๆ โครงการจาก บริษัท ต่าง ๆ ) เป็นที่น่าเสียดายว่าคำถามนี้เป็นที่นิยมมากใน Google แต่มีคนที่มีสายตาสั้นไม่กี่คนที่ปิดมัน
อดัม

คำตอบ:


51

ฉันกำลังคาดเดาจากสิ่งที่คล้ายกับที่ฉันเพิ่ง debugged เมื่อเร็ว ๆ นี้ ... หากคุณกด (หรือป๊อป) ตัวควบคุมมุมมองที่มีการเคลื่อนไหว: ใช่มันไม่สมบูรณ์ในทันทีและสิ่งเลวร้ายเกิดขึ้นถ้าคุณทำการผลักดัน เสร็จสมบูรณ์ คุณสามารถทดสอบได้อย่างง่ายดายว่าเป็นกรณีนี้หรือไม่โดยการเปลี่ยนการดำเนินการพุชและป๊อปเป็นภาพเคลื่อนไหวชั่วคราว: ไม่ (เพื่อให้การซิงโครไนซ์เสร็จสมบูรณ์) และดูว่าจะกำจัดความผิดพลาดหรือไม่ หากนี่เป็นปัญหาของคุณและคุณต้องการเปิดภาพเคลื่อนไหวกลับมากลยุทธ์ที่ถูกต้องคือการใช้โปรโตคอล UINavigationControllerDelegate ซึ่งรวมถึงวิธีการต่อไปนี้ซึ่งถูกเรียกหลังจากภาพเคลื่อนไหวเสร็จสมบูรณ์:

navigationController:didShowViewController:animated:

โดยทั่วไปคุณต้องการย้ายรหัสบางส่วนตามที่จำเป็นในวิธีการนี้เพื่อให้แน่ใจว่าไม่มีการกระทำอื่นใดที่อาจทำให้เกิดการเปลี่ยนแปลงกับกอง NavigationController จะเกิดขึ้นจนกว่าภาพเคลื่อนไหวจะเสร็จสิ้นและสแต็กพร้อมสำหรับการเปลี่ยนแปลงเพิ่มเติม


ย้อนกลับไปเกี่ยวกับ iOS 4- บางอย่างที่ฉันเห็นบางสิ่งที่คล้ายกันในหนึ่งในแอปของเรา - IIRC หากคุณโผล่ภาพเคลื่อนไหวแล้วผลักภาพเคลื่อนไหวทันทีรหัส UI จะกลายเป็นสิ่งที่ไม่ดี จบลงด้วยการเปลี่ยนเพื่อไม่เคยทำสองการดำเนินการผลักดัน / ป๊อปอัพเคลื่อนไหวกลับไปหลัง แน่นอนตรรกะพื้นฐานทั้งหมดได้รับการเขียนใหม่ตั้งแต่นั้นมา แต่ก็ไม่ยากที่จะเชื่อว่าข้อผิดพลาดที่คล้ายกันยังไม่ได้มี
Hot Licks

ฉันมีปัญหาเดียวกัน ในกรณีของฉันสิ่งนี้เกิดขึ้นเพราะแอปดำเนินการคำสั่งที่เปลี่ยน UI ของคอนโทรลเลอร์มุมมองใหม่[newViewController setLabelTitle:...]หลังจากเรียก pushViewController ด้วยAnimated:YES.และฉันแก้ไขการย้ายเมธอด setLabelTitle เป็น viewDidLoad บน newViewController ขอบคุณที่ให้เบาะแสกับฉัน
jeprubio

ดีใจที่ช่วย! จุดที่ดีที่การย้ายรหัสไปยัง ViewController ใหม่เป็นอีกทางเลือกหนึ่งหากคุณทราบว่าจะใช้คลาสใด มากขึ้นฉันพบว่ามีประโยชน์ในการตรวจจับเมธอดต่างๆของโปรโตคอล UINavigationControllerDelegate ในทุกกรณี และฉันได้พบว่าในเหตุการณ์ที่เกิดขึ้นใน iOS8 มีคำสั่งต่างกันและบางสิ่งที่เคยซิงโครนัสมากขึ้นหรือน้อยลงกลับมาเร็วขึ้น แต่กำหนดเวลาให้ทำบนพื้นหลังแบบอะซิงโครนัส ขอบคุณ Apple!
RobP

14

เราเริ่มได้รับปัญหานี้เช่นกันและมีโอกาสสูงที่เราจะเกิดจากปัญหาเดียวกัน

ในกรณีของเราเราต้องดึงข้อมูลจากส่วนหลังในบางกรณีซึ่งหมายความว่าผู้ใช้อาจแตะบางสิ่งและจากนั้นอาจมีความล่าช้าเล็กน้อยก่อนที่จะเกิดการกดปุ่ม nav หากผู้ใช้เคาะอย่างรวดเร็วพวกเขาอาจจบลงด้วยการนำทางสองรายการจากตัวควบคุมมุมมองเดียวกันซึ่งก่อให้เกิดข้อยกเว้นนี้

วิธีการแก้ปัญหาของเราคือหมวดหมู่บน 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 ที่เกี่ยวข้องและรวมไว้ในการเรียกเพื่อพุช / ป๊อป

คำว่า "ล็อค" อาจเป็นถ้อยคำที่ไม่ดีนักเนื่องจากอาจมีบางอย่างเกิดขึ้นกับรูปแบบของการล็อกที่ต้องการปลดล็อค แต่เนื่องจากไม่มีวิธี "ปลดล็อค" ในทุกที่จึงอาจไม่เป็นไร

(ในฐานะ sidenote "ความล่าช้าของผู้ใช้ที่ไม่เกี่ยวข้อง" คือความล่าช้าใด ๆ ที่ทำให้รหัสเกิดขึ้นเช่นอะไรก็ตามแบบอะซิงโครนัสผู้ใช้ที่แตะบนตัวควบคุม nav ซึ่งเป็นภาพเคลื่อนไหวแบบพุชจะไม่นับรวม กรณี.)


เมื่อคุณบอกว่าคุณลองใช้วิธีแก้ปัญหานี้แล้วมันช่วยแก้ปัญหาให้คุณได้หรือไม่?
Mike D

จนถึงตอนนี้ ปัญหาไม่ได้เกิดใหม่ ฉันจะอัปเดตคำตอบ
Kalle

4
ผมใช้รุ่นที่ปรับเปลี่ยนขึ้นอยู่กับคุณ: gist.github.com/mdewolfe/9369751 ดูเหมือนว่าจะได้รับการแก้ไขแล้ว
Mike D

2
@Kalle วิธีนี้ใช้สำหรับการกด / ป๊อป แต่จะแก้ไขข้อผิดพลาดนี้ได้อย่างไรถ้าฉันใช้งานซีจี
Geek

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

นี่เป็นวิธีแก้ปัญหาที่ดีที่สุดจนถึงขณะนี้กับคนอื่น ๆ ที่ฉันจะยังคงได้รับปัญหาการกดสองครั้งแบบสุ่มหรือฉันจะได้รับตัวควบคุมการนำทางแช่แข็ง
blueice

รหัสนี้ไม่ได้รวบรวมสำหรับฉันมีอะไรหายไป?
Maxime B

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

ตัวควบคุมนี้เป็นตัวแทนของการควบคุมการดูเว็บ หากโน้ตมีลิงค์ให้แตะลิงค์จะไปที่เว็บเบราว์เซอร์ในแอพ

- (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 => ผิดพลาด!

ฉันแก้ไขข้อผิดพลาดนี้โดยการตรวจสอบประเภทนำทาง:

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

หวังว่านี่จะช่วยได้


1
มันจะไม่เป็นตัวเลือกที่ดีในการผลักคอนโทรลเลอร์โดยไม่มีภาพเคลื่อนไหวเมื่อถูกเรียกจากviewDidLoadหรือไม่
ริเวร่า

6

ฉันมีปัญหาเดียวกันสิ่งที่ทำงานได้ง่ายสำหรับฉันคือการเปลี่ยนแบบเคลื่อนไหว: ใช่เป็นแบบเคลื่อนไหว: ไม่

ดูเหมือนว่าปัญหานี้เกิดจากภาพเคลื่อนไหวที่ไม่สมบูรณ์ตามเวลาที่กำหนด

หวังว่านี่จะช่วยใครซักคน


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]; ตัวจดจำลายมือถูกปิดใช้งานเมื่อใด และคุณจะรู้ได้อย่างไรว่าผู้รับมอบสิทธิ์ควรเป็นอย่างไร ด้วยบรรทัดเหล่านั้นสำหรับฉันมันทำลายท่าทางป๊อปหลังจาก popping ครั้งเดียว
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)

ข้อผิดพลาดหายไปและฉันสามารถเห็นมุมมองทั้งสอง แค่คิดว่านี่จะช่วยให้ทุกคนที่ต้องการดูตัวอย่าง


ฉันลองใช้วิธีนี้ด้วยเช่นกัน แต่สแต็คเทรซจะแตกต่างกันและแสดงบรรทัดรหัสของคุณที่ทำให้เกิดการขัดข้อง ฉันเชื่อว่าปัญหาที่มานั้นแตกต่างจากคำถาม
Ben

1

ค้นหารหัสของคุณสำหรับ "addSubview"

ในหนึ่งในสถานที่ที่คุณเรียกว่าวิธีนี้คุณพยายามที่จะเพิ่มมุมมองไปยังอาร์เรย์มุมมองย่อยของตัวเองโดยใช้วิธีนี้

ตัวอย่างเช่น:

[self.view addSubview:self.view];

หรือ:

[self.myLabel addSubview:self.myLabel];

ยินดีที่ทราบว่าคุณพบข้อผิดพลาดของคุณและตอนนี้ฉันเข้าใจว่าทำไมคุณถึงได้รับ "ไม่สามารถเพิ่มตัวเองเป็นมุมมองย่อย" ณ จุดที่ View2 ของคุณเป็นตัวควบคุมมุมมองรูทของตัวควบคุมการนำทางของคุณคุณได้ผลัก View2 ซึ่งทำให้เกิดสิ่งนี้: [View2.view addSubview:View2.view]ดังนั้นการเพิ่มตัวเองเป็นมุมมองย่อย
Michal Shatz

1

ฉันคิดว่าการกด / popping มุมมองตัวควบคุมด้วยแอนิเมชั่น ณ จุดใดควรจะดีอย่างสมบูรณ์และ SDK ควรจัดการคิวการโทรสำหรับเรา

ดังนั้นมันไม่ได้และวิธีแก้ปัญหาทั้งหมดพยายามที่จะเพิกเฉยต่อการกดครั้งต่อไปซึ่งอาจถือได้ว่าเป็นข้อผิดพลาดเนื่องจาก stack stack สุดท้ายไม่ใช่รหัสที่ต้องการ

ฉันใช้คิวการโทรแบบพุชแทน:

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


ฉันลองใช้วิธีแก้ปัญหาของคุณซึ่งดูดีมาก แต่ฉันมีปัญหา เมื่อผลักดันตัวควบคุมมุมมอง 2 ตัวด้วยภาพเคลื่อนไหวที่ไม่มีใครเหมือนกันฉันจะเห็นตัวแรกอย่างสั้น ๆ สิ่งนี้ไม่เคยเกิดขึ้นมาก่อน มีความคิดใดที่ฉันสามารถแก้ไขได้
มกราคม

ฉันกำลังพยายามสร้างโครงการที่สามารถทำให้เกิดความผิดพลาดประเภทนี้ได้อย่างต่อเนื่อง (โครงการจริงของฉันได้รับรายงานข้อขัดข้องเช่นนี้) ฉันทำแอพง่าย ๆ ที่มีตัวควบคุมการนำทางตัวควบคุมรูทและปุ่มที่กดตัวควบคุมมุมมองใหม่ 4 ตัวลงบนสแต็ค nav ทันทีจากนั้นก็โผล่ออกมาตัวสุดท้าย หากไม่มีคลาสย่อยพิเศษใด ๆ หรือดูเหมือนว่าจะทำงานได้ดี Apple ได้แก้ไขสิ่งนี้เมื่อเร็ว ๆ นี้หรือไม่?
Cruinh

1

ขออภัยที่มาสายสำหรับปาร์ตี้ ฉันเพิ่งมีปัญหานี้ซึ่งในแถบนำทางของฉันไปสู่สถานะที่เสียหายเนื่องจากการผลักดันตัวควบคุมมุมมองมากกว่าหนึ่งในเวลาเดียวกัน สิ่งนี้เกิดขึ้นเนื่องจากตัวควบคุมมุมมองอื่นถูกผลักในขณะที่ตัวควบคุมมุมมองแรกยังคงเคลื่อนไหว การรับคำใบ้จากคำตอบแบบไม่เคลือบฟันฉันได้คำตอบง่ายๆที่ใช้ในกรณีของฉัน คุณเพียงแค่ต้อง subclass UINavigationControllerและแทนที่วิธีการ pushViewController และตรวจสอบว่าภาพเคลื่อนไหวตัวควบคุมมุมมองก่อนหน้านี้จะเสร็จสิ้นเป็นยัง คุณสามารถฟังความสมบูรณ์ของแอนิเมชั่นโดยทำให้คลาสของคุณเป็นตัวแทนUINavigationControllerDelegateและตั้งค่าให้ผู้รับมอบสิทธิ์เป็นselfและการตั้งตัวแทนไป

ฉันได้อัพโหลดส่วนสำคัญที่นี่เพื่อทำให้สิ่งต่างๆง่ายขึ้น

เพียงให้แน่ใจว่าคุณตั้งคลาสใหม่นี้เป็น NavigationController ในกระดานเรื่องราวของคุณ


จนถึงตอนนี้ดูเหมือนว่าจะมีการแก้ไขข้อผิดพลาดในแอพที่ฉันกำลังทำงานอยู่ ... บวกกับการแก้ปัญหาค่อนข้างตรงไปตรงมาและชัดเจนเกี่ยวกับประเด็น: ภาพเคลื่อนไหวตัวควบคุมมุมมองแรกยังไม่สมบูรณ์ ผู้ที่มีปัญหาเดียวกันควรตรวจสอบเรื่องนี้
alasker

0

จากคำแนะนำที่ดีของ @RobP ฉันได้สร้างคลาสย่อย UINavigationControllerเพื่อป้องกันปัญหาดังกล่าว มันจัดการการกดและ / หรือการ popping และคุณสามารถดำเนินการได้อย่างปลอดภัย:

[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 จากนั้นจะเห็น popping เคลื่อนไหวของ vc3 หาก 'acceptConflictingCommands' เป็นเท็จคำขอ push / pop ทั้งหมดจะถูกยกเลิกจนกว่า vc1 จะถูกผลักอย่างเต็มที่ - ดังนั้นการโทร 3 ครั้งอื่น ๆ จะถูกยกเลิก


คำสั่งเหล่านั้นขัดแย้งกันจริงหรือไม่? ฉันเพิ่งโยนโครงการใหม่อย่างรวดเร็วเพื่อดูความผิดพลาดนี้เกิดขึ้นโดยใช้โค้ดอย่างที่คุณมีอยู่ข้างบน (แต่ใน Swift) และจริง ๆ แล้วมันดำเนินการกดแต่ละครั้งและป๊อปทั้งหมดตามลำดับ หนึ่งสิ่งหลังจากสิ่งอื่น. ไม่มีความผิดพลาด. โดยไม่ต้องใช้คลาสย่อยใด ๆ เพียงแค่ UINavigationController ปกติของ apple
Cruinh

จริง ๆ แล้วมันกระแทกกับ ObjC และ iOS 7 ฉันไม่สามารถยืนยันได้ว่ามันจะยังคงเกิดขึ้นในขณะนี้ คุณแน่ใจหรือไม่ว่าคุณกำลังดำเนินการคำสั่งด้วยการanimated:trueตั้งค่าสถานะ?
hris.to

ใช่ฉันใช้ภาพเคลื่อนไหว: ตั้งค่าสถานะจริง
Cruinh

0

วิธีการแก้ปัญหาของ nonamelive น่ากลัว แต่ถ้าคุณไม่ต้องการใช้ api ส่วนตัวคุณก็สามารถทำตามUINavigationControllerDelegateวิธีการได้หรือเปลี่ยนภาพเคลื่อนไหวYESเป็นNOเพื่อนี่คือตัวอย่างของรหัสคุณสามารถสืบทอดได้ หวังว่ามันจะเป็นประโยชน์ :)

https://github.com/antrix1989/ANNavigationController


0

ฉันค้นหาปัญหานี้บ่อยครั้งมันอาจผลักดัน VC สองตัวขึ้นไปในเวลาเดียวกันซึ่งทำให้เกิดปัญหาเกี่ยวกับการเคลื่อนไหวคุณสามารถอ้างถึงสิ่งนี้:ไม่สามารถเพิ่มตัวเองเป็นมุมมองย่อย崩溃解决办法

เพียงตรวจสอบให้แน่ใจว่ามี VC หนึ่งตัวเกี่ยวกับความคืบหน้าการเปลี่ยนแปลงในเวลาเดียวกัน, ขอให้โชคดี


0

บางครั้งคุณพยายามเพิ่มมุมมองในมุมมองของตนเองโดยไม่ตั้งใจ

halfView.addSubview(halfView)

เปลี่ยนเป็นมุมมองย่อยของคุณ

halfView.addSubview(favView)

0

ฉันพบปัญหานี้เช่นกัน เมื่อฉันทำการวิเคราะห์บันทึกของ Firebase ฉันพบว่าปัญหานี้เกิดขึ้นเฉพาะเมื่อแอปเริ่มเย็น ดังนั้นฉันจึงเขียนตัวอย่างที่สามารถทำซ้ำความผิดพลาดนี้

.

ฉันยังพบว่าเมื่อตัวควบคุมรูทวิวของหน้าต่างแสดงขึ้นการทำการกดหลายครั้งจะไม่ทำให้เกิดปัญหาเดียวกันอีก (คุณสามารถคอมเม้นต์ testColdStartUp (rootNav) ใน AppDelegate.swift และยกเลิกการคอมเมนต์ testColdStartUp () ความคิดเห็นใน ViewController.swift)

ps: ฉันวิเคราะห์ฉากของความผิดพลาดนี้ในแอพของฉัน เมื่อผู้ใช้คลิกที่การแจ้งเตือนแบบพุชเพื่อเริ่มแอปพลิเคชันแอปนั้นยังคงอยู่ในหน้าเปิดตัวและคลิกที่การผลักเพื่อข้ามอีกครั้ง ในเวลานี้แอปอาจปรากฏ Crash ปัจจุบันของฉันวิธีแก้ไขคือการแคชพุชพุชหรือการเชื่อมโยงเริ่มเย็นเพื่อเปิดเพจข้ามแอพรอให้ rootviewcontroller แสดงขึ้นจากนั้นหน่วงเวลาการดำเนินการ


-2

ลองใช้การนำทางโดยใช้วิธีการหน่วงเวลาสำหรับการทำภาพเคลื่อนไหวการนำทางล่าสุด

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


-2

ไม่สามารถเพิ่มมุมมองเป็นมุมมองย่อยได้ด้วยตนเอง

มุมมองรักษาลำดับชั้นของผู้ปกครองเด็กดังนั้นถ้าคุณเพิ่มมุมมองเป็นมุมมองย่อยในตัวเองมันจะผ่านข้อยกเว้น

ถ้าคลาสเป็น UIViewController ดังนั้นเมื่อต้องการดูมุมมองคุณใช้ self.view

ถ้าคลาสนั้นเป็น UIView Class ดังนั้นเมื่อต้องการดูมุมมองของคุณเอง


-3

คุณไม่สามารถเพิ่มตัวเองเป็นมุมมองย่อยหากมันจะเป็น UiViewController Class คุณสามารถเพิ่มตัวเองเป็น subview ถ้ามันจะเป็น UiView Class


-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

@Popeye คุณมีความคิดที่ดีขึ้นหรือไม่
David Gölzhäuser

ไม่เพราะพวกเขายังไม่ได้ให้ข้อมูล / รหัสเพียงพอที่จะทำซ้ำปัญหา ดังนั้นจึงไม่มีวิธีตอบคำถามนี้ดูเหมือนว่าคุณจะบอกพวกเขาถึงวิธีการทำสิ่งที่ไม่เกี่ยวข้องกับปัญหาของพวกเขา
Popeye

2
ฉันคิดว่ามันมีประโยชน์มากกว่าสำหรับพวกเขาที่จะปิดปัญหา เข้าใจแล้ว! +1 สำหรับ David G ที่พยายามช่วยเหลือใครบางคนใน StackOverflow ฉันหวังว่าฉันจะได้ -1 คะแนนโหวตใกล้ของคุณ !!! สิ่งนี้ยังคงเกิดขึ้นกับผู้ใช้และอาจเป็นข้อบกพร่องใน iOS7 สำหรับสิ่งที่เรารู้ ดังนั้นเพียงเพราะใครบางคนไม่สามารถโพสต์รหัสการละเมิดได้หมายความว่าคำถามนั้นไม่ถูกต้องและมีค่าสำหรับผู้ใช้รายอื่นที่จะเห็น แม้ว่าจะเป็นเพียงการเห็นว่าคนอื่นกำลังเห็นปัญหาเดียวกันโดยไม่มีเหตุผลเชิงเหตุผลว่าทำไมพวกเขาเห็นมัน -rrh
Richie Hyatt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.