it-swarm-ko.tech

뷰가 창 계층 구조에 있지 않은 UIViewController에 UIViewController를 표시하려고했습니다.

Xcode 4.5를 사용하기 시작했고 콘솔에서이 오류가 발생했습니다.

경고 : <viewController : 0x1e3e000>에 <finishViewController : 0x1e56e0a0>을 표시하려고 시도합니다. 뷰는 창 계층 구조에 없습니다!

보기가 계속 표시되고 앱의 모든 것이 정상적으로 작동합니다. iOS 6의 새로운 기능입니까?

이것은 뷰를 변경하는 데 사용하는 코드입니다.

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

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

이 메소드를 어디에서 호출합니까? viewDidLoad 메서드 내에서 모달 뷰 컨트롤러를 표시하려고 할 때 문제가 발생했습니다. 나를위한 해결책은이 호출을 viewDidAppear: 메소드로 옮기는 것이 었습니다.

내 가정은 뷰 컨트롤러의 뷰 가 (창이로드 된 시점의 창보기 계층 구조에서) 아님 viewDidLoad 메시지가 전송 됨), is 제공된 후 창 계층 구조에서 viewDidAppear: 메시지가 전송됩니다).


주의

presentViewController:animated:completion:에서 viewDidAppear:을 (를) 호출하면 뷰 컨트롤러의 뷰가 나타날 때마다 모달 뷰 컨트롤러가 항상 표시되는 문제가 발생할 수 있습니다. 뷰 컨트롤러가 사라지지 않습니다 ...

어쩌면 이것이 모달 뷰 컨트롤러를 제시하기에 가장 좋은 장소가 아니거나, 또는 제시 뷰 컨트롤러가 모달 뷰 컨트롤러를 즉시 표시해야하는지 여부를 결정할 수있는 추가 상태를 유지해야 할 수도 있습니다.

1209
James Bedford

또 다른 잠재적 원인 :

실수로 동일한보기 컨트롤러를 두 번 발표했을 때이 문제가 발생했습니다. (버튼을 눌렀을 때 performSegueWithIdentifer:sender:로 한 번, 버튼에 직접 연결된 세그로 두 번째로).

사실 두 개의 섹이 동시에 발사되고 오류가 발생했습니다. Attempt to present X on Y whose view is not in the window hierarchy!

57
Chris Nolet

이 목적으로 viewWillLayoutSubviewsviewDidLayoutSubviews (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에 뷰 컨트롤러를 표시하려고 할 때이 문제가 발생했습니다. James Bedford의 대답을 시도했습니다. 그것은 작동하지만 내 애플 리케이션은 1 초 또는 2 초 동안 배경을 먼저 보여줍니다.

연구를 마친 후에 마침내이 문제를 해결할 다른 방법은 자식 뷰 컨트롤러를 사용하는 것입니다.

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

아마 저처럼, 당신은 잘못된 루트를 가지고 있습니다 viewController

non-UIViewController 컨텍스트에 ViewController을 표시하려고합니다.

그래서 나는 그런 코드를 사용할 수 없다 :

[self presentViewController:]

그래서, UIViewController 얻을 :

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

rootViewController은 어떤 이유로 (논리적 버그) 예상 된 것 (정상 UIViewController) 이외의 것입니다. 그런 다음 버그를 수정하고 rootViewControllerUINavigationController로 바꾸면 문제가 해결됩니다.

14
Sanbrother

TL, DR 당신은 오직 하나의 rootViewController와 가장 최근에 제시된 것을 가질 수 있습니다. 따라서 viewcontroller가 이미 해고되지 않은 viewcontroller를 제시하면 viewcontroller가 나타나지 않도록하십시오.

내 자신의 테스트를 한 후에 결론에 도달했습니다.

모든 것을 제시하고자하는 rootViewController가 있다면이 문제를 해결할 수 있습니다.

다음은 내 rootController 코드입니다 (open은 내 viewcontroller를 루트에서 보여주는 단축키입니다).

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을 호출하면 다시 뷰를 호출 할 때 잘 동작합니다 (또 다른 뷰 컨트롤러에서).

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

내가 결론을 내린 것은 MOST-RECENT-CALL의 rootViewController가보기 계층 구조에 있다는 것입니다 (보기를 지우지 않거나보기를 제거하지 않은 경우에도 마찬가지입니다). 모든 로더 호출 (viewDidLoad, viewDidAppear 및 지연된 디스패치 호출을 수행)을 시도해 보았습니다. 그리고 작동하도록 얻는 유일한 방법은 최상위 뷰 컨트롤러에서만 현재 호출하는 것입니다.

5
Aggressor

내 문제는 창에서 makeKeyAndVisible()을 호출하기 전에 UIApplicationDelegatedidFinishLaunchingWithOptions 메서드에서 segue를 수행하는 것이 었습니다.

5
Adam Johns

나는 거의 모든 곳에서 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
        })
    }
}
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

누군가가 도움이 될 경우, 내 문제는 매우 어리 석다. 물론 내 잘못이야. 모달을 호출하는 메소드가 트리거되었습니다. 그러나 알림을 올바르게 제거하지 못했기 때문에 어떤 시점에서 하나 이상의 알림이 표시되므로 모달이 여러 번 호출됩니다. 물론 모달을 한 번 호출 한 후에는 ViewController가 뷰 계층 구조에서 더 이상 필요하지 않으므로이 문제를 확인해야합니다. 제 상황은 당신이 기대하는 것처럼 다른 문제도 야기했습니다.

요약하면, 무엇을 하든지간에모달이 한번 이상 호출되지 않도록하십시오.

2
HotFudgeSunday

그것은 나에게 일어났다 스토리 보드의 단서는 어떤 종류의 고장이었다. segue를 삭제하면 (그리고 똑같은 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

dismiss 완료 블록 내에서 start() 함수를 이동하여 문제를 해결했습니다.

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

Start에는 UINavigationController에 대해 하나의 self.present()을 호출하고 UIImagePickerController을 호출하는 두 개의 호출이 포함됩니다.

그것을 위해 나를 고쳤다.

1
Alper

스위프트 3 ...

이 일이 일어난 또 다른 원인은 내게 일어난 일로, tableViewCell에서 스토리 보드의 다른 ViewController로 이어지는 것이었다. 셀을 클릭 할 때도 override func prepare(for segue: UIStoryboardSegue, sender: Any?) {}을 사용했습니다.

ViewController에서 ViewController로 segue를 만들어서이 문제를 해결했습니다.

1
Devbot10

슬프게도, 내 생각에 받아 들여진 해결책은 효과가 없었습니다. 다른 View Controller에서 풀어 낸 직후 새로운 View Controller로 이동하려고했습니다.

나는 unwind segue가 호출되었음을 나타내는 플래그를 사용하여 해결책을 찾았다.

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

기본적으로 위의 코드는 login/SignUp VC에서 unwind를 catch하고 대시 보드로 이동하거나 암호 VC을 잊어 버린 unwind 동작을 잡아 로그인 페이지로 이동합니다.

1
Fan Jin

이 문제가 발생했으며 근본 원인이 버튼 클릭 처리기 (TouchUpInside)에 여러 번 가입하고있었습니다.

우리가 다른 컨트롤러로 이동하기 위해 탐색을 추가 한 이후로 여러 번 호출 된 ViewWillAppear에 가입 한 다음 다시 되돌릴 수 있습니다.

1
Wes

이런 종류의 경고는 현재이 View Controller이 (가) 다른 Navigation Controller를 표시하는 동안 Navigation Controller를 통해 새로운 View Controller를 표시하려고한다는 것을 의미 할 수 있습니다. 문제를 해결하려면 먼저 현재 제시된 View Controller를 닫고 완료되면 새 것을 제시해야합니다. 경고의 또 다른 원인은 View Controllermain 이외의 스레드에 표시하려고 할 수 있습니다.

1
saltwat5r

방금이 문제도 있었지만 타이밍과 관련이 없습니다. 나는 장면을 다루기 위해 싱글 톤을 사용했고, 나는 그것을 발표자로 설정했다. 다른 말로하면 "자아"는 아무 것도 연결되지 않았습니다. 방금 내면의 "장면"을 새로운 발표자로 만들어 버렸습니다. (Voila는 당신이 그 의미를 알게 된 후에 그것의 감각을 잃습니다, heh).

그렇습니다. "마술처럼 올바른 방법을 찾는"것이 아니라 코드가 어디에 있고 무엇을하고 있는지 이해하는 것입니다. 나는 애플이 그것에 감정이 있다고하더라도 평범한 영어 경고 메시지를 주었다. 누가 그것을했는지 Apple dev에 대한 명성 !!

0
Stephen J

어떤 이유로 든 다른 솔루션이 잘 보이지 않는다면 다음과 같이 지연 시간이 0 인 프리젠 테이션의 낡은 workaround을 사용할 수 있습니다.

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