보기가 창 계층 구조에 없는 UIViewController를 UIViewController에 표시하려고 합니다.
Xcode 4.5를 막 사용하기 시작했는데 콘솔에서 다음 오류가 발생했습니다.
경고:창 계층 구조에 없는 보기의 <ViewController: 0x1e56e0a0>을 표시하려고 합니다.
뷰는 여전히 제공되고 있으며 앱의 모든 것이 정상적으로 작동합니다.이것은 iOS 6에서 새로운 것입니까?
다음은 보기 간에 변경할 때 사용하는 코드입니다.
UIStoryboard *storyboard = self.storyboard;
finishViewController *finished =
[storyboard instantiateViewControllerWithIdentifier:@"finishViewController"];
[self presentViewController:finished animated:NO completion:NULL];
이 방법을 어디서 부르십니까?시스템 내에서 모달 뷰 컨트롤러를 제시하려고 시도하는 문제가 발생했습니다.viewDidLoad
방법.저를 위한 해결책은 이 전화를 다음으로 옮기는 것이었습니다.viewDidAppear:
방법.
보기 컨트롤러의 보기가 로드된 시점(다음과 같은 경우)에 창의 보기 계층 구조에 있지 않은 것으로 가정합니다.viewDidLoad
메시지가 전송됨), 그러나 메시지가 표시된 후에는 창 계층 구조에 있습니다.viewDidAppear:
메시지가 전송됨).
주의.
이 경를우걸로 ,presentViewController:animated:completion:
에 시대에viewDidAppear:
보기 컨트롤러의 보기가 나타날 때마다 모달 보기 컨트롤러가 항상 표시되므로 표시되는 모달 보기 컨트롤러가 사라지지 않는 문제가 발생할 수 있습니다.
여기가 모달 뷰 컨트롤러를 표시하기에 최적의 위치가 아닐 수도 있고, 현재 뷰 컨트롤러가 모달 뷰 컨트롤러를 즉시 표시할지 여부를 결정할 수 있는 추가 상태를 유지해야 할 수도 있습니다.
또 다른 잠재적 원인:
실수로 동일한 뷰 컨트롤러를 두 번 제시했을 때 이 문제가 발생했습니다. (한 번은 에서)performSegueWithIdentifer:sender:
버튼을 눌렀을 때 호출되었으며, 세그가 버튼에 직접 연결된 상태에서 두 번째로 호출되었습니다.)
두 에 발사되고 저는 다음과 오류를 : 사상두대의세구동발고었다되사, 저다과같얻었니습오류.Attempt to present X on Y whose view is not in the window hierarchy!
viewWillLayoutSubviews
그리고.viewDidLayoutSubviews
5할 수 (iOS 5.0+)는 다음과 같습니다.viewDidAppear보다 먼저 호출됩니다.
기본 뷰에 대한 하위 뷰를 표시하려면 다음 코드를 사용하십시오.
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];
저는 또한 제가 발표하려고 할 때 이 문제에 직면했습니다.UIViewController
viewDidLoad
제임스 베드포드의 답변은 먹혔지만, 제 앱은 1~2초 동안 배경을 먼저 보여주었습니다.
몇 가지 조사 후에, 저는 이것을 해결할 방법을 찾았습니다.addChildViewController
.
- (void)viewDidLoad
{
...
[self.view addSubview: navigationViewController.view];
[self addChildViewController: navigationViewController];
...
}
아마, 나처럼, 당신도 잘못된 뿌리를 가지고 있을 것입니다.viewController
는 다표다니합시를 .ViewController
순식간에non-UIViewController
세션,
그래서 나는 그런 코드를 사용할 수 없습니다:
[self presentViewController:]
UIView 컨트롤러가 있습니다.
[[[[UIApplication sharedApplication] delegate] window] rootViewController]
이유로 (버그), 어떤논로유 (논리적 버그),rootViewController
예상과 인 경우).UIViewController
. 그런 다음 버그를 수정하고, 교체합니다.rootViewController
UINavigationController
그리고 문제는 사라졌습니다.
Swift 5 - 배경 나사산
경보 컨트롤러가 백그라운드 스레드에서 실행되는 경우 "표시 시도 중...보기가 창 계층 구조에 없는 경우" 오류가 발생할 수 있습니다.
그래서 이것은:
present(alert, animated: true, completion: nil)
다음과 같이 수정되었습니다.
DispatchQueue.main.async { [weak self] in
self?.present(alert, animated: true, completion: nil)
}
TL;DR rootViewController는 1개만 가질 수 있으며 가장 최근에 표시된 컨트롤러입니다.따라서 이미 무시되지 않은 뷰 컨트롤러가 표시된 경우 뷰 컨트롤러가 다른 뷰 컨트롤러를 표시하도록 하지 마십시오.
제가 직접 몇 가지 테스트를 해본 결과 결론이 났습니다.
모든 것을 표시하려는 루트뷰 컨트롤러가 있는 경우 이 문제가 발생할 수 있습니다.
루트 컨트롤러 코드는 다음과 같습니다(루트에서 뷰 컨트롤러를 표시하는 바로 가기는 열기입니다).
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)
}
MOST-RENT-CALL의 rootViewController는 보기 계층에 있습니다(보기를 해제하거나 제거하지 않은 경우에도).저는 모든 로더 호출(viewDidLoad, viewDidAppear, 지연된 디스패치 호출)을 사용해 보았는데, 제가 작동할 수 있는 유일한 방법은 최상위 뷰 컨트롤러에서 현재 호출만 하는 것이라는 것을 알게 되었습니다.
Swift 4.2에서도 비슷한 문제가 있었지만 뷰 사이클에서 제 견해가 제시되지 않았습니다.동시에 여러 개의 segue를 제시해야 한다는 것을 알게 되었습니다.그래서 dispatch AsyncAfter를 사용했습니다.
func updateView() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
// for programmatically presenting view controller
// present(viewController, animated: true, completion: nil)
//For Story board segue. you will also have to setup prepare segue for this to work.
self?.performSegue(withIdentifier: "Identifier", sender: nil)
}
}
내 문제는 내가 세그를 공연한다는 것이었습니다.UIApplicationDelegate
의didFinishLaunchingWithOptions
하기 makeKeyAndVisible()
창문에
제 상황에서는 제 것을 클래스 오버라이드에 넣을 수 없었습니다.제가 얻은 것은 다음과 같습니다.
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)
}
이 블록 안에 있는 segue 또는 present, push 코드를 호출할 수 있습니다.
override func viewDidLoad() {
super.viewDidLoad()
OperationQueue.main.addOperation {
// push or present the page inside this block
}
}
저도 같은 문제가 있었습니다.저는 내비게이션 컨트롤러를 내장하고 그것을 통해 컨트롤러를 제시해야 했습니다.아래는 샘플 코드입니다.
- (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
}
재생된 비디오가 있는 AVPlayer 개체가 있는 경우 먼저 비디오를 일시 중지해야 합니다.
저도 같은 문제가 있었습니다.문제는 performSegueWithIdentifier가 알림에 의해 트리거되었다는 것입니다. 알림을 메인 스레드에 올리자마자 경고 메시지가 사라졌습니다.
작동이 잘 됩니다. 이것을 사용해보세요.링크
UIViewController *top = [UIApplication sharedApplication].keyWindow.rootViewController;
[top presentViewController:secondView animated:YES completion: nil];
만약 누군가에게 도움이 된다면, 제 문제는 매우 바보같았습니다.물론 전적으로 제 잘못입니다.알림이 모달을 호출하는 메서드를 트리거하고 있습니다.그러나 알림을 제대로 제거하지 못했기 때문에 어느 시점에 두 개 이상의 알림이 표시되어 모달이 여러 번 호출되었습니다.물론 모달을 한 번 호출한 후에는 모달을 호출하는 뷰 컨트롤러가 뷰 계층에 더 이상 존재하지 않기 때문에 이 문제가 발생합니다.예상하신 대로 저의 상황은 많은 다른 문제를 야기했습니다.
요약하자면, 무엇을 하든지 간에 모달이 두 번 이상 호출되지 않도록 해야 합니다.
당신이 일부를 표시하고 싶어하는 것을 고려하면, 나는 마침내 내게 작동하는 코드(Swift)를 갖게 되었습니다.viewController
거의 모든 곳에서.사용 가능한 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
})
}
}
것을 제시하려고 한다는 것을 의미할 수 있습니다.View Controller
를 통하여Navigation Controller
이동에안▁▁whileNavigation Controller
또 다른 현재다항있제습니다고하시을목른ing▁another▁currently있을 제시하고 있습니다.View Controller
수정하려면 현재 제시된 내용을 해제해야 합니다.View Controller
처음에 그리고 완료되면 새로운 것을 제시합니다.경고의 또 다른 원인은 다음과 같습니다.View Controller
…이 아닌 main
.
나는 그것을 움직여서 고쳤습니다.start()
내의 dismiss
완료 블록:
self.tabBarController.dismiss(animated: false) {
self.start()
}
시작에 두 개의 호출이 포함되어 있습니다.self.present()
는 UINavigation Controller용입니다.UIImagePickerController
.
그것이 저를 위해 고쳐주었습니다.
rootViewController를 순환하는 동안 최상위 뷰 컨트롤러를 상수로 저장하여 이 오류를 해결했습니다.
if var topController = UIApplication.shared.keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
topController.present(controller, animated: false, completion: nil)
// topController should now be your topmost view controller
}
컨테이너에 포함된 보기 컨트롤러에서 segue를 수행할 때도 이 경고가 표시될 수 있습니다.올바른 솔루션은 컨테이너의 뷰 컨트롤러가 아닌 컨테이너의 상위에서 segue를 사용하는 것입니다.
아래 줄에 글을 써야 합니다.
self.searchController.definesPresentationContext = true
대신에
self.definesPresentationContext = true
UIView 컨트롤러에서
스위프트 3와 함께...
제게 일어난 또 다른 가능한 원인은 테이블 ViewCell에서 스토리보드의 다른 ViewController로 segue를 보내는 것이었습니다.저도 사용했습니다.override func prepare(for segue: UIStoryboardSegue, sender: Any?) {}
셀을 클릭했을 때
ViewController에서 ViewController로 segue를 만들어 이 문제를 해결했습니다.
이 문제가 있었는데, 근본 원인은 버튼 클릭 핸들러(TouchUpInside)를 여러 번 구독한 것입니다.
ViewWillAppear에서 구독하고 있었는데, 다른 컨트롤러로 이동하기 위한 탐색 기능을 추가한 이후로 여러 번 호출되었습니다.
저는 우연히 스토리보드의 세그가 고장이 났다는 것을 알게 되었습니다.segue를 삭제하고 동일한 segue를 다시 생성하면 문제가 해결되었습니다.
기본 창에서는 항상 알림 표시와 호환되지 않는 전환이 발생할 수 있습니다.응용 프로그램 수명 주기에서 언제든지 알림을 표시할 수 있도록 하려면 작업을 수행할 수 있는 별도의 창이 있어야 합니다.
/// 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"];
안타깝게도, 수용된 해결책은 제 경우에 효과가 없었습니다.다른 View Controller에서 해제한 후 바로 새 View Controller로 이동하려고 했습니다.
저는 깃발을 사용하여 어떤 언윈드 세그가 호출되었는지 알려줌으로써 해결책을 찾았습니다.
@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
}
}
기본적으로 위의 코드를 사용하면 로그인/사인업 VC에서 해제된 내용을 파악하고 대시보드로 이동하거나 암호 VC를 잊어버린 경우 해제된 작업을 파악하고 로그인 페이지로 이동할 수 있습니다.
나는 이 버그가 Xcode 업데이트 후에 도착했다는 것을 발견했는데, 나는 스위프트 5라고 생각합니다.뷰 컨트롤러를 풀고 바로 segue를 실행했을 때 문제가 발생했습니다.
관련 버그를 수정하는 중에 해결책이 도착했습니다. 사용자가 페이지를 쓸어 내려 segue를 풀 수 있게 되었습니다.이것은 제 프로그램의 논리를 깨트렸습니다.
모든 뷰 컨트롤러의 프레젠테이션 모드를 자동에서 전체 화면으로 변경하여 수정되었습니다.
인터페이스 작성기의 특성 패널에서 이 작업을 수행할 수 있습니다.또는 프로그램적으로 수행하는 방법은 다음 답변을 참조하십시오.
스위프트 5
를 는나선물부다릅니을▁present로 부릅니다.viewDidLayoutSubviews
에제시 바된같이에 제시된 와 같이.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(...)
}
}
언급URL : https://stackoverflow.com/questions/11862883/attempt-to-present-uiviewcontroller-on-uiviewcontroller-whose-view-is-not-in-the
'sourcecode' 카테고리의 다른 글
이클립스에서 블록에 대한 의견을 어떻게 제시합니까? (0) | 2023.05.14 |
---|---|
문자열에서 발생한 특정 문자 수 (0) | 2023.05.14 |
WPF 응용 프로그램 전체 화면 만들기(표지 시작 메뉴) (0) | 2023.05.14 |
코드 VB.net 을 계속하기 전에 0.5초 기다립니다. (0) | 2023.05.14 |
SQL Server로 업데이트하려면 선택 (0) | 2023.05.14 |