647

Xcode 4.5 を使い始めたところ、コンソールに次のエラーが表示されました。

警告: ビューがウィンドウ階層にない < ViewController: 0x1ec3e000> に < finishViewController: 0x1e56e0a0 > を表示しようとしています!

ビューはまだ表示されており、アプリ内のすべてが正常に機能しています。これは iOS 6 の新機能ですか?

これは、ビュー間で変更するために使用しているコードです:

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

[self presentViewController:finished animated:NO completion:NULL];
4

35 に答える 35

1381

このメソッドはどこから呼び出していますか? viewDidLoadメソッド内でモーダル ビュー コントローラーを表示しようとしたときに問題が発生しました。私にとっての解決策は、この呼び出しをviewDidAppear:メソッドに移動することでした。

私の推測では、View Controllerのビューは、ロードされた時点(メッセージが送信されたとき)にはウィンドウのビュー階層にありませんが、表示された後(メッセージが送信されたとき)にウィンドウ階層にあると思います。 .viewDidLoadviewDidAppear:


注意

で を呼び出すとpresentViewController:animated:completion:viewDidAppear:ビュー コントローラーのビューが表示されるたびにモーダル ビュー コントローラーが常に表示されるという問題が発生する可能性があり (これは理にかなっています!)、表示されているモーダル ビュー コントローラーが消えることはありません。 .

おそらく、これはモーダル ビュー コントローラーを表示するのに最適な場所ではないか、表示側のビュー コントローラーがモーダル ビュー コントローラーをすぐに表示するかどうかを決定できるようにするために、いくつかの追加の状態を保持する必要があるかもしれません。

于 2012-09-07T14:36:46.623 に答える
38

viewWillLayoutSubviewsおよびviewDidLayoutSubviews(iOS 5.0+) をこの目的に使用できます。これらは、viewDidAppear よりも前に呼び出されます。

于 2013-02-22T03:10:41.253 に答える
19

でを表示しようとしたときにも、この問題が発生しUIViewControllerましたviewDidLoad。James Bedfordの答えはうまくいきましたが、私のアプリは最初に1〜2秒間背景を表示しました。

いくつかの調査の後、私はこれを使用してこれを解決する方法を見つけましたaddChildViewController

- (void)viewDidLoad
{
    ...
    [self.view addSubview: navigationViewController.view];
    [self addChildViewController: navigationViewController];
    ...
}
于 2012-12-06T05:35:24.370 に答える
14

おそらく、私のように、あなたは間違ったルートを持っていますviewController

コンテキストでViewControlleraを表示したいのですが、non-UIViewController

だから私はそのようなコードを使用することはできません:

[self presentViewController:]

したがって、UIViewController を取得します。

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

なんらかの理由 (論理的なバグ) により、rootViewControllerは予想外のものです (通常のUIViewController)。次に、バグを修正して に置き換えrootViewControllerたところUINavigationController、問題はなくなりました。

于 2013-07-24T02:30:33.130 に答える
8

TL;DR rootViewController は 1 つしか持つことができず、それは最近提示されたものです。そのため、ビュー コントローラーが既に表示されているビュー コントローラーが閉じられていないときに、そのビュー コントローラーを別のビュー コントローラーに表示させようとしないでください。

私自身のテストのいくつかを行った後、私は結論に達しました。

すべてを提示したい rootViewController がある場合、この問題に遭遇する可能性があります。

これが私の rootController コードです ( open は、ルートからビューコントローラーを表示するためのショートカットです)。

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 を 2 回続けて呼び出すと (経過時間に関係なく)、最初の open では問題なく動作しますが、2 回目の open では機能しません。2 回目の開こうとすると、上記のエラーが発生します。

ただし、最近表示されたビューを閉じてから open を呼び出すと、(別のビューコントローラーで) open を再度呼び出すと問題なく動作します。

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

私が結論付けたのは、MOST-RECENT-CALL のみの rootViewController がビュー階層上にあるということです (それを却下したり、ビューを削除したりしていなくても)。すべてのローダー呼び出し (viewDidLoad、viewDidAppear、および遅延ディスパッチ呼び出しの実行) を試してみましたが、それを機能させる唯一の方法は、最上位のビュー コントローラーから present を呼び出すことだけであることがわかりました。

于 2015-08-17T19:48:23.963 に答える
5

私の問題は、ウィンドウを呼び出す前に、UIApplicationDelegatedidFinishLaunchingWithOptionsメソッドでセグエを実行していたことです。makeKeyAndVisible()

于 2017-05-17T01:59:47.783 に答える
4

セグエを呼び出したり、このブロック内でコードをプッシュしたりできます。

override func viewDidLoad() {
    super.viewDidLoad()
    OperationQueue.main.addOperation {
        // push or present the page inside this block
    }
}
于 2020-07-22T22:03:19.917 に答える
3

同じ問題がありました。問題は、通知によって performSegueWithIdentifier がトリガーされ、メインスレッドに通知を送信するとすぐに警告メッセージが消えたことです。

于 2015-09-09T10:41:17.970 に答える
3

それが誰かを助ける場合に備えて、私の問題は非常にばかげていました。もちろん、完全に私のせいです。通知により、モーダルを呼び出していたメソッドがトリガーされました。しかし、通知を正しく削除していなかったため、ある時点で複数の通知が発生し、モーダルが複数回呼び出されました。もちろん、モーダルを一度呼び出すと、それを呼び出すビューコントローラーはビュー階層に存在しなくなります。そのため、この問題が発生します。ご想像のとおり、私の状況は他の多くの問題も引き起こしました。

要約すると、何をしていても、モーダルが複数回呼び出されていないことを確認してください。

于 2016-09-06T13:56:45.883 に答える
3

ビデオを再生した AVPlayer オブジェクトがある場合は、最初にビデオを一時停止する必要があります。

于 2015-06-18T20:11:26.537 に答える
3

viewController事実上どこからでもいくつかを表示したいことを考慮して、最終的に私(Swift)に機能するようなコードになりました。利用可能な rootViewController がない場合、このコードは明らかにクラッシュします。これはオープン エンディングです。また、通常必要な UI スレッドへの切り替えも含まれていません。

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
        })
    }
}
于 2016-02-22T17:42:19.353 に答える
3

私も同じ問題を抱えていました。ナビゲーション コントローラーを埋め込み、それを介してコントローラーを提示する必要がありました。以下はサンプルコードです。

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

}
于 2014-02-20T09:34:52.030 に答える
3

これを試してみるとうまくいきます。リンク

UIViewController *top = [UIApplication sharedApplication].keyWindow.rootViewController;
[top presentViewController:secondView animated:YES completion: nil];
于 2016-05-23T06:37:25.177 に答える
2

start()関数をdismiss完了ブロック内に移動して修正しました。

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

Start にはself.present()、UINavigationController 用と 用の2 つの呼び出しが含まれていUIImagePickerControllerます。

それは私のためにそれを修正しました。

于 2018-05-17T09:23:27.693 に答える
1

コンテナに埋め込まれたビュー コントローラからセグエを実行するときにも、この警告が表示されることがあります。正しい解決策は、コンテナーのビュー コントローラーからではなく、コンテナーの親からセグエを使用することです。

于 2015-12-02T21:06:54.160 に答える
1

行の下に書く必要があります。

self.searchController.definesPresentationContext = true

それ以外の

self.definesPresentationContext = true

UIViewControllerで

于 2016-02-18T10:17:54.840 に答える
1

スイフト5

モーダルが読み込まれる前にビューコントローラーが一瞬表示され、醜いグリッチのように見えるため、私はpresent inviewDidLayoutSubviewsを呼び出しますviewDidAppear

ウィンドウの存在を確認し、コードを一度だけ実行してください

var alreadyPresentedVCOnDisplay = false

override func viewDidLayoutSubviews() {
        
    super.viewDidLayoutSubviews()
    
    // we call present in viewDidLayoutSubviews as
    // presenting in viewDidAppear causes a split second showing 
    // of the view controller before the modal is loaded
    
    guard let _ = view?.window else {
        // window must be assigned
        return
    }
    
    if !alreadyPresentedVCOnDisplay {
        alreadyPresentedVCOnDisplay = true
        present(...)
    }
    
}
于 2020-09-09T16:42:46.993 に答える
1

メイン ウィンドウでは、アラートの表示と互換性のないトランジションが常に発生する可能性があります。アプリケーションのライフサイクルでいつでもアラートを表示できるようにするには、ジョブを実行するための別のウィンドウが必要です。

/// 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"];
于 2018-01-13T11:39:29.053 に答える
0

何らかの理由で他のソリューションが適切に見えない場合でも、次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:

于 2014-09-20T12:44:22.813 に答える
0

これは、ナビゲーション コントローラーが利用可能な場合、任意のビュー コントローラーを表示するために機能します。 self.navigationController?.present(MyViewController, animation: true, completion: nil) また、アラートやメールコントローラーも表示できます。

于 2019-09-11T02:48:58.427 に答える