3

Я пытаюсь показать UIAlertController в моем ViewController в функции, вызванной NSNotification. Однако я получаю сообщение об ошибке:NSNotification: попытка представить UIAlertController на ViewController, чей вид отсутствует в иерархии окон

Attempt to present <UIAlertController: 0x7fe013d05d40> on <submarine.ViewController: 0x7fe011f20370> whose view is not in the window hierarchy!

NSNotification размещен от блока завершения (обратного вызова я думаю) от чего-то еще в моем пользовательском интерфейсе. Поскольку это обратный вызов, он не отображается. Поэтому я думал, что попробую NSNotificationCentre решить проблему без использования rootViewController для отображения предупреждения.

Мой код:

override func viewDidAppear(animated: Bool) { 
    // Handle onboarding 
    if needsOnboarding() { 
     handleOnboarding() // This create the completion block that posts the NSNotification 
    } 

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "showTermsAlert:", name:"showTermsAlert", object: nil) 
} 

func showTermsAlert(notification: NSNotification) { 
    let termsAlert:UIAlertController = UIAlertController(title: "Terms And Conditions", message: "Please view the terms below before accepting them.", preferredStyle: UIAlertControllerStyle.Alert) 

    termsAlert.addAction(UIAlertAction(title: "View Terms", style: .Default, handler: { (action: UIAlertAction!) in 
     UIApplication.sharedApplication().openURL(NSURL(string: "my_terms_url")!) 
    })) 

    termsAlert.addAction(UIAlertAction(title: "I Agree to the Terms", style: .Default, handler: { (action: UIAlertAction!) in 
     self.onboardingFinished() 
    })) 

    self.presentViewController(termsAlert, animated: true, completion: nil) 
} 

Кто-нибудь есть идея, почему это происходит? Я не понимаю, почему это не в иерархии окон - оно отображается с помощью viewController self и создается в функции верхнего уровня внутри VC.

Спасибо!

EDIT: оригинальный код внутри handleOnboarding() в:

библиотека используется: Onboard

func handleOnboarding() { 

     let secondPage = OnboardingContentViewController(title: "What's going on?", body: "Submarine routes your data through our network, around any filters and restrictions, giving you unrestricted and unmonitored internet access.", image: UIImage(named: "back"), buttonText: "Next") {() -> Void in 
      // do something here when users press the button, like ask for location services permissions, register for push notifications, connect to social media, or finish the onboarding process 
     } 
     secondPage.movesToNextViewController = true 

     let thirdPage = OnboardingContentViewController(title: "Terms of Use", body: "You must agree to our Terms of Use to use Submarine.\nIf you don't, please close Submarine.", image: UIImage(named: "back"), buttonText: "View Terms") {() -> Void in 

      let termsAlert:UIAlertController = UIAlertController(title: "Terms And Conditions", message: "Please view the terms below before accepting them.", preferredStyle: UIAlertControllerStyle.Alert) 

      termsAlert.addAction(UIAlertAction(title: "View Terms", style: .Default, handler: { (action: UIAlertAction!) in 
       UIApplication.sharedApplication().openURL(NSURL(string: "my_policy_url")!) 
      })) 

      termsAlert.addAction(UIAlertAction(title: "I Agree to the Terms", style: .Default, handler: { (action: UIAlertAction!) in 
       self.onboardingFinished() 
      })) 

      self.presentViewController(termsAlert, animated: true, completion: nil) 

//   NSNotificationCenter.defaultCenter().postNotificationName("showTermsAlert", object: nil) 
     } 

     // Image 
     let onboardingVC = OnboardingViewController(backgroundImage: UIImage(named: "back"), contents: [secondPage, thirdPage]) 

     self.navigationController?.presentViewController(onboardingVC, animated: false, completion: nil) 

    } 

ответ

2

Это происходит, когда контроллер представления Предъявление больше не входит в иерархии контроллера, и это не вид больше не находится в иерархию представлений любого окна. Скорее всего, контроллер был уволен или всплыл, но он услышал уведомление и попытался представить контроллер предупреждения.

Необходимо более тщательно управлять состояниями вашего контроллера. Возможно, удалите наблюдателя, когда контроллер будет удален или выведен из иерархии вашего контроллера.

+0

Обычно 'viewWillDisappear:' метод является хорошим местом для удаления наблюдателей. – Sulthan

+0

@ Султан Может быть, в этом случае, возможно, нет. Я специально не хотел делать конкретные предложения, так как у нас недостаточно информации. Кроме того, как правило, логика 'viewDidAppear:'/'viewWillDisappear:' является ошибкой и часто проблематична, как мы видим здесь. –

+0

@Sulthan so in handleOboarding() Я нажимаю новый VC на NavigationController, а затем в обратном вызове, который возникает после действия пользователя в этом контроллере, я пытаюсь показать предупреждение, вызвав 'showTermsAlert()'. Это немного прояснилось? –

0

Есть несколько вещей, которые я бы изменил в вашем коде. Добавьте звонок в супер в viewDidAppear: и прекратите использовать NSNotification s для вашей презентации. Вы не знаете, какой поток showTermsAlert получит этот шаблон. Вы можете сделать свое намерение более явным, напрямую позвонив showTermsAlert, и это также гарантирует, что вы находитесь в основном потоке.

override func viewDidAppear(animated: Bool) { 
    super.viewDidAppear(animated) 
    // Handle onboarding 
    if needsOnboarding() { 
     self.showTermsAlert() 
    } 
} 

func showTermsAlert() { 
    let termsAlert:UIAlertController = UIAlertController(title: "Terms And Conditions", message: "Please view the terms below before accepting them.", preferredStyle: UIAlertControllerStyle.Alert) 

    termsAlert.addAction(UIAlertAction(title: "View Terms", style: .Default, handler: { (action: UIAlertAction!) in 
    UIApplication.sharedApplication().openURL(NSURL(string: "my_terms_url")!) 
})) 

    termsAlert.addAction(UIAlertAction(title: "I Agree to the Terms", style: .Default, handler: { (action: UIAlertAction!) in 
    self.onboardingFinished() 
})) 

    self.presentViewController(termsAlert, animated: true, completion: nil) 
} 
+0

Я не могу помещать его в if-block для 'needsOnboarding() {}', потому что строка, которая была там ('handleOnboarding()'), создает новый VC, толкает его на NavigationController и затем делает что-то, что создает вызов, когда он будет завершен. Именно в этом обратном вызове (после того, как это закончилось), мне нужно вызвать 'showTermsAlert()'. Это немного прояснилось? –

+0

Я так думаю. основанный на этом, я думаю, что 'showTermsAlert()' можно было вызвать слишком рано, прежде чем новый VC был полностью удален из иерархии экрана и окна? Также убедитесь, что вы вызываете 'self.PresentViewController' в основном потоке. – Nick

+0

Просто отправил обновление, в том числе код для 'handleOnboarding()', именно так я пытался это сделать до NSNotification. Я сейчас посмотрю на главную тему потока :) –