it-swarm.asia

محاولة تقديم UIViewController على UIViewController الذي لم يكن عرضه في التسلسل الهرمي للنافذة

بدأت للتو في استخدام Xcode 4.5 وحصلت على هذا الخطأ في وحدة التحكم:

تحذير: محاولة تقديم <finishViewController: 0x1e56e0a0> في <ViewController: 0x1ec3e000> الذي لم يكن عرضه في التسلسل الهرمي للنافذة!

لا يزال يتم تقديم العرض وكل شيء في التطبيق يعمل بشكل جيد. هل هذا شيء جديد في نظام التشغيل iOS 6؟

هذا هو الرمز الذي أستخدمه للتغيير بين طرق العرض:

UIStoryboard *storyboard = self.storyboard;
finishViewController *finished = 
[storyboard instantiateViewControllerWithIdentifier:@"finishViewController"];

[self presentViewController:finished animated:NO completion:NULL];
546
Kyle Goslan

من أين تتصل بهذه الطريقة؟ واجهت مشكلة حيث كنت أحاول تقديم وحدة تحكم طريقة عرض مشروط ضمن طريقة viewDidLoad. كان الحل بالنسبة لي هو نقل هذه الدعوة إلى طريقة viewDidAppear:.

بلدي الافتراض هو أن طريقة عرض وحدة التحكم في العرض ليست في التسلسل الهرمي لعرض النافذة عند النقطة التي تم تحميلها (عند إرسال الرسالة viewDidLoad) ، لكنها هي في التسلسل الهرمي للنافذة بعد تقديمه (عند إرسال رسالة viewDidAppear:).


الحذر

إذا قمت بإجراء مكالمة إلى presentViewController:animated:completion: في viewDidAppear: ، فقد تواجه مشكلة حيث يتم دائمًا تقديم وحدة التحكم في طريقة العرض مشروطًا عندما تظهر طريقة عرض وحدة التحكم في طريقة العرض (الأمر المنطقي!) وبالتالي لن تتحول وحدة التحكم في طريقة العرض مشروطة أبدًا ...

ربما لا يكون هذا هو أفضل مكان لتقديم وحدة التحكم في طريقة العرض مشروطًا ، أو ربما يلزم الاحتفاظ ببعض الحالات الإضافية التي تسمح لوحدة التحكم في طريقة العرض الحالية بتقرير ما إذا كان ينبغي تقديم وحدة التحكم في طريقة العرض مشروطة على الفور أم لا.

1171
James Bedford

سبب محتمل آخر:

كان لدي هذه المشكلة عندما كنت قدم بطريق الخطأ وحدة تحكم العرض نفسه مرتين. (مرة واحدة مع performSegueWithIdentifer:sender: والذي تم استدعاؤه عند الضغط على الزر ، ومرة ​​أخرى مع فصل متصل مباشرة بالزر).

على نحو فعال ، تم إطلاق فئتين في نفس الوقت ، وحصلت على الخطأ: Attempt to present X on Y whose view is not in the window hierarchy!

57
Chris Nolet

يمكن استخدام viewWillLayoutSubviews و viewDidLayoutSubviews (iOS 5.0+) لهذا الغرض. يطلق عليهم أقدم من viewDidAppear.

34
Jonny

لعرض أي عرض فرعي للعرض الرئيسي ، يرجى استخدام الكود التالي

UIViewController *yourCurrentViewController = [UIApplication sharedApplication].keyWindow.rootViewController;

while (yourCurrentViewController.presentedViewController) 
{
   yourCurrentViewController = yourCurrentViewController.presentedViewController;
}

[yourCurrentViewController presentViewController:composeViewController animated:YES completion:nil];

لاستبعاد أي عرض فرعي من العرض الرئيسي ، يرجى استخدام الكود التالي

UIViewController *yourCurrentViewController = [UIApplication sharedApplication].keyWindow.rootViewController;

while (yourCurrentViewController.presentedViewController) 
{
   yourCurrentViewController = yourCurrentViewController.presentedViewController;
}

[yourCurrentViewController dismissViewControllerAnimated:YES completion:nil];
21
abdul sathar

لقد واجهت هذه المشكلة أيضًا عندما حاولت تقديم وحدة تحكم في عرض viewDidLoad. لقد جربت جيمس بيدفورد الإجابة. يعمل ، لكن تطبيقي سيعرض الخلفية أولاً لمدة ثانية أو ثانيتين.

بعد البحث ، أجد أخيرًا طريقة أخرى لحل هذه المشكلة وهي استخدام جهاز التحكم في عرض الطفل.

- (void)viewDidLoad
{
    ...
    [self.view addSubview:navigationViewController.view];
    [self addChildViewController:navigationViewController];
    ...
}
16
sunkehappy

ربما ، مثلي ، لديك جذر خاطئ viewController

أريد عرض ViewController في سياق non-UIViewController ،

لذلك لا يمكنني استخدام هذا الرمز:

[self presentViewController:]

لذلك ، أحصل على UIViewController:

[[[[UIApplication sharedApplication] delegate] window] rootViewController]

لسبب ما (الأخطاء المنطقية) ، rootViewController هو شيء آخر غير متوقع (UIViewController عادي). ثم أقوم بتصحيح الخطأ ، واستبدال rootViewController بـ UINavigationController ، وقد انتهت المشكلة.

14
Sanbrother

TL ؛ DR يمكنك فقط الحصول على 1 rootViewController وهو الأكثر حداثة. لذلك لا تحاول وجود مراقب عرض يقدم مراقب تحكم آخر عندما يكون قد تم تقديمه بالفعل ولم يتم رفضه.

بعد القيام ببعض الاختبارات الخاصة بي توصلت إلى خاتمة.

إذا كان لديك rootViewController تريد تقديم كل شيء ، فيمكنك مواجهة هذه المشكلة.

إليك رمز rootController (فتح هو اختصاري لتقديم مراقب تحكم من الجذر).

func open(controller:UIViewController)
{
    if (Context.ROOTWINDOW.rootViewController == nil)
    {
        Context.ROOTWINDOW.rootViewController = ROOT_VIEW_CONTROLLER
        Context.ROOTWINDOW.makeKeyAndVisible()
    }

    ROOT_VIEW_CONTROLLER.presentViewController(controller, animated: true, completion: {})
}

إذا قمت بالاتصال بـ open مرتين على التوالي (بغض النظر عن الوقت المنقضي) ، فسيكون هذا جيدًا عند الفتح الأول ، ولكن ليس على الفتح الثاني. ستؤدي المحاولة المفتوحة الثانية إلى حدوث الخطأ أعلاه.

ومع ذلك ، إذا أغلقت طريقة العرض التي تم تقديمها مؤخرًا ، ثم اتصلت بـ Open ، فسيتم تشغيله على ما يرام عندما أتصل بـ open (مرة أخرى على مراقب عرض آخر).

func close(controller:UIViewController)
{
    ROOT_VIEW_CONTROLLER.dismissViewControllerAnimated(true, completion: nil)
}

ما خلصت إليه هو أن rootViewController من MOST-RECENT-CALL فقط هو على التسلسل الهرمي للعرض (حتى إذا لم ترفضه أو تزيله طريقة عرض). حاولت اللعب مع جميع مكالمات برنامج التحميل (viewDidLoad ، و viewDidAppear ، وإجراء مكالمات إرسال متأخرة) ولقد وجدت أن الطريقة الوحيدة التي يمكنني بها الحصول على العمل هي الاتصال فقط بالحاضر من أعلى جهاز تحكم في طريقة العرض.

5
Aggressor

كانت مشكلتي أنني كنت أقوم بإجراء المجموعة في طريقة UIApplicationDelegate's didFinishLaunchingWithOptions قبل أن أسميها makeKeyAndVisible() في النافذة.

5
Adam Johns

انتهى بي الأمر برمز يعمل أخيرًا معي (Swift) ، مع مراعاة أنك تريد عرض بعض viewController من أي مكان تقريبًا. من الواضح أن هذا الرمز سوف يتعطل عندما لا يكون هناك rootViewController متاح ، وهذا هو النهاية المفتوحة. كما أنه لا يتضمن التبديل المطلوب عادة إلى مؤشر ترابط واجهة المستخدم باستخدام

dispatch_sync(dispatch_get_main_queue(), {
    guard !NSBundle.mainBundle().bundlePath.hasSuffix(".appex") else {
       return; // skip operation when embedded to App Extension
    }

    if let delegate = UIApplication.sharedApplication().delegate {
        delegate.window!!.rootViewController?.presentViewController(viewController, animated: true, completion: { () -> Void in
            // optional completion code
        })
    }
}
4
igraczech

إذا كان لديك كائن AVPlayer مع تشغيل الفيديو ، فعليك إيقاف الفيديو مؤقتًا أولاً.

3
Vlad

انها تعمل بشكل جيد حاول هذا. رابط

UIViewController *top = [UIApplication sharedApplication].keyWindow.rootViewController;
[top presentViewController:secondView animated:YES completion: nil];
3
vijay

كان لي نفس القضية. كانت المشكلة هي أن performSegueWithIdentifier تم تشغيله عن طريق إشعار ، بمجرد أن أضع الإشعار في سلسلة الرسائل الرئيسية ، تم حذف رسالة التحذير.

3
Red

في وضعي ، لم أتمكن من وضع الألغام في تجاوز الصف. إذن ، هذا ما حصلت عليه:

let viewController = self // I had viewController passed in as a function,
                          // but otherwise you can do this


// Present the view controller
let currentViewController = UIApplication.shared.keyWindow?.rootViewController
currentViewController?.dismiss(animated: true, completion: nil)

if viewController.presentedViewController == nil {
    currentViewController?.present(alert, animated: true, completion: nil)
} else {
    viewController.present(alert, animated: true, completion: nil)
}
2
George_E

كان لي نفس المشكلة. اضطررت إلى تضمين وحدة التحكم في الملاحة وتقديم وحدة التحكم من خلالها. أدناه هو رمز عينة.

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    UIImagePickerController *cameraView = [[UIImagePickerController alloc]init];
    [cameraView setSourceType:UIImagePickerControllerSourceTypeCamera];
    [cameraView setShowsCameraControls:NO];

    UIView *cameraOverlay = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 768, 1024)];
    UIImageView *imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"someImage"]];
    [imageView setFrame:CGRectMake(0, 0, 768, 1024)];
    [cameraOverlay addSubview:imageView];

    [cameraView setCameraOverlayView:imageView];

    [self.navigationController presentViewController:cameraView animated:NO completion:nil];
//    [self presentViewController:cameraView animated:NO completion:nil]; //this will cause view is not in the window hierarchy error

}
2
resting

في حال كانت تساعد أي شخص ، كانت مشكلتي سخيفة للغاية. خطأي تماما بالطبع. كان الإشعار يشغل طريقة كانت تستدعي الوسائط. لكنني لم أقم بإزالة الإشعار بشكل صحيح ، لذلك في وقت ما ، سيكون لدي أكثر من إشعار ، لذلك سيتم الاتصال بالوسائط عدة مرات. بالطبع ، بعد أن قمت بالاتصال بالشرط مرة واحدة ، فإن مراقب المشاهدة الذي يطلق عليه لم يعد في التسلسل الهرمي للعرض ، ولهذا السبب نرى هذه المشكلة. تسبب موقفي في مجموعة من المشكلات الأخرى أيضًا ، كما تتوقع.

لتلخيص ذلك ، كل ما تفعله تأكد من عدم استدعاء المشروط أكثر من مرة .

2
HotFudgeSunday

حدث لي أن segue في لوحة العمل كان نوعا من كسر . حذف السلسلة (وإنشاء نفس السلسلة بالضبط مرة أخرى) حل المشكلة.

1
vonox7

يجب أن تكتب أدناه السطر.

self.searchController.definesPresentationContext = true

بدلا من

self.definesPresentationContext = true

في UIViewController

1
Coder_A_D

يمكنك أيضًا الحصول على هذا التحذير عند إجراء سلسلة من وحدة تحكم عرض مضمنة في حاوية. الحل الصحيح هو استخدام segue من أصل الحاوية ، وليس من وحدة تحكم عرض الحاوية.

1
Borzh

من خلال نافذتك الرئيسية ، من المحتمل دائمًا أن تكون هناك أوقات تمر بمرحلة انتقالية تتعارض مع تقديم تنبيه. للسماح بعرض التنبيهات في أي وقت من دورة حياة التطبيق ، يجب أن يكون لديك نافذة منفصلة للقيام بالمهمة.

/// independant window for alerts
@interface AlertWindow: UIWindow

+ (void)presentAlertWithTitle:(NSString *)title message:(NSString *)message;

@end

@implementation AlertWindow

+ (AlertWindow *)sharedInstance
{
    static AlertWindow *sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[AlertWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
    });
    return sharedInstance;
}

+ (void)presentAlertWithTitle:(NSString *)title message:(NSString *)message
{
    // Using a separate window to solve "Warning: Attempt to present <UIAlertController> on <UIViewController> whose view is not in the window hierarchy!"
    UIWindow *shared = AlertWindow.sharedInstance;
    shared.userInteractionEnabled = YES;
    UIViewController *root = shared.rootViewController;
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
    alert.modalInPopover = true;
    [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
        shared.userInteractionEnabled = NO;
        [root dismissViewControllerAnimated:YES completion:nil];
    }]];
    [root presentViewController:alert animated:YES completion:nil];
}

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];

    self.userInteractionEnabled = NO;
    self.windowLevel = CGFLOAT_MAX;
    self.backgroundColor = UIColor.clearColor;
    self.hidden = NO;
    self.rootViewController = UIViewController.new;

    [NSNotificationCenter.defaultCenter addObserver:self
                                           selector:@selector(bringWindowToTop:)
                                               name:UIWindowDidBecomeVisibleNotification
                                             object:nil];

    return self;
}

/// Bring AlertWindow to top when another window is being shown.
- (void)bringWindowToTop:(NSNotification *)notification {
    if (![notification.object isKindOfClass:[AlertWindow class]]) {
        self.hidden = YES;
        self.hidden = NO;
    }
}

@end

الاستخدام الأساسي الذي ، حسب التصميم ، سينجح دائمًا:

[AlertWindow presentAlertWithTitle:@"My title" message:@"My message"];
1
Cœur

قمت بإصلاحه عن طريق تحريك الدالة start() داخل كتلة إكمال dismiss:

self.tabBarController.dismiss(animated: false) {
  self.start()
}

تحتوي البداية على مكالمتين إلى self.present() واحد من أجل UINavigationController وواحد آخر لـ UIImagePickerController.

هذا ثابت بالنسبة لي.

1
Alper

مع سويفت 3 ...

سبب آخر محتمل لهذا الأمر ، الذي حدث لي ، كان وجود مقطع من tableViewCell إلى ViewController آخر على لوحة العمل. لقد استخدمت أيضًا override func prepare(for segue: UIStoryboardSegue, sender: Any?) {} عند النقر فوق الخلية.

لقد أصلحت هذه المشكلة عن طريق إنشاء segue من ViewController إلى ViewController.

1
Devbot10

للأسف ، لم ينجح الحل المقبول في حالتي. كنت أحاول الانتقال إلى وحدة تحكم عرض جديدة مباشرة بعد الاسترخاء من وحدة تحكم عرض أخرى.

لقد وجدت حلاً باستخدام إشارة للإشارة إلى أي مقطع استرخاء كان يسمى.

@IBAction func unwindFromAuthenticationWithSegue(segue: UIStoryboardSegue) {
    self.shouldSegueToMainTabBar = true
}

@IBAction func unwindFromForgetPasswordWithSegue(segue: UIStoryboardSegue) {
    self.shouldSegueToLogin = true
}

ثم قدم المطلوب VC مع present(_ viewControllerToPresent: UIViewController)

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    if self.shouldSegueToMainTabBar {
        let mainTabBarController = storyboard.instantiateViewController(withIdentifier: "mainTabBarVC") as! MainTabBarController
        self.present(mainTabBarController, animated: true)
        self.shouldSegueToMainTabBar = false
    }
    if self.shouldSegueToLogin {
        let loginController = storyboard.instantiateViewController(withIdentifier: "loginVC") as! LogInViewController
        self.present(loginController, animated: true)
        self.shouldSegueToLogin = false
    }
}

بشكل أساسي ، سيسمح لي التعليمة البرمجية أعلاه بالالتقاط من تسجيل الدخول/SignUp VC والانتقال إلى لوحة القيادة أو التقاط إجراء الاسترخاء من خلال نسيت كلمة المرور VC والانتقال إلى صفحة تسجيل الدخول.

1
Fan Jin

واجهت هذه المشكلة ، وكان السبب الرئيسي هو الاشتراك في معالج النقر على زر (TouchUpInside) عدة مرات.

تم الاشتراك في ViewWillAppear ، والذي كان يتم استدعاء عدة مرات منذ أن قمنا بإضافة التنقل للانتقال إلى وحدة تحكم أخرى ، ثم الاسترخاء مرة أخرى.

1
Wes

يمكن أن يعني هذا التحذير أنك تحاول تقديم View Controller جديد عبر Navigation Controller بينما يقدم Navigation Controller حاليًا View Controller آخر. لإصلاحه ، يجب عليك رفض View Controller المقدمة حاليًا في البداية وعند اكتمال تقديم الكود الجديد. يمكن أن يكون سبب آخر للتحذير محاولة عرض View Controller على مؤشر ترابط آخر غير main.

1
saltwat5r

لقد واجهت هذه المشكلة فقط أيضًا ، لكن لا علاقة لها بالتوقيت. كنت أستخدم منفردًا للتعامل مع المشاهد ، وقمت بتعيينه كمقدم. بمعنى آخر ، لم يتم ربط "الذات" بأي شيء. أنا فقط جعلت "المشهد" الداخلي المقدم الجديد وفويلا ، نجحت. (فويلا يفقد اللمسات بعد أن تعلمت معناها ، هيه).

إذن ، نعم ، الأمر لا يتعلق بـ "إيجاد الطريق الصحيح بطريقة سحرية" ، بل يتعلق بفهم المكان الذي ترمز إليه الشفرة وما الذي تفعله. أنا سعيد لأن شركة أبل قد وجهت رسالة تحذير باللغة الإنجليزية ، حتى مع وجود عاطفة لها. مجد إلى ديف أبل الذي فعل ذلك!

0
Stephen J

إذا كانت الحلول الأخرى لا تبدو جيدة لسبب ما ، فلا يزال بإمكانك استخدام هذا الاسم القديم workaround لتقديم مع تأخير 0 ، مثل هذا:

dispatch_after(0, dispatch_get_main_queue(), ^{
    finishViewController *finished = [self.storyboard instantiateViewControllerWithIdentifier:@"finishViewController"];
    [self presentViewController:finished animated:NO completion:NULL];    
});

على الرغم من أنني لم أشاهد أي ضمان موثق بأن VC الخاص بك سيكون في وضع التسلسل الهرمي للعرض في وقت إرسال جدولة كتلة للتنفيذ ، لاحظت أنها ستعمل بشكل جيد.

استخدام تأخير على سبيل المثال 0.2 ثانية هو أيضا خيار. وأفضل شيء - بهذه الطريقة لن تحتاج إلى العبث مع متغير منطقي في viewDidAppear:

0
Dannie P