【问题标题】:Display an alert using storyboard and custom UITabBarController使用故事板和自定义 UITabBarController 显示警报
【发布时间】:2021-12-30 19:46:32
【问题描述】:

我正在处理使用 UITabBarController 的问题。我有一个使用故事板的小项目(XCode 13,IOS 15 作为基本系统)。我创建了一个 TabBarController,但后来我发现我无法以编程方式有效地管理它。阅读各种文档后,我发现我可以使用故事板中的两个场景并以编程方式创建标签栏。 所以我在SceneDelegate.swift

let queryViewControllerTab = storyBoard.instantiateViewController(withIdentifier: "QueryViewController")    
let settingsViewControllerTab = storyBoard.instantiateViewController(withIdentifier: "SettingsViewController")
let starredViewControllerTab = storyBoard.instantiateViewController(withIdentifier: "StarredViewController")
starredViewControllerTab.tabBarItem.title = "Starred"
starredViewControllerTab.tabBarItem.image = UIImage(systemName: "star")
// TODO: Discover why first two views keep reading image I setup previously in storyboard

let tabBarController = UITabBarController()
tabBarController.viewControllers = [queryViewControllerTab, settingsViewControllerTab, starredViewControllerTab]
tabBarController.selectedViewController = settingsViewControllerTab

self.window?.rootViewController = tabBarController
self.window?.makeKeyAndVisible()

这很好用,我可以很容易地设置一个条件是否userDefaults 没有设置,直接加载设置。

在我的班级SettingsViewController 我想添加一个动作,在按下按钮时,您会收到警报:

@IBAction func saveButtonPressed(_ sender: UIButton) {
        //        keychain.set(tokenInput.text ?? "", forKey: keychainKey)
        let alert = UIAlertController(title: "My Alert", message: "This is an alert.", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Default action"), 
                style: .default, handler: { _ in
        NSLog("The \"OK\" alert occured.")
        }))
        tabBarController.present(alert, animated: true, completion: nil)
}

但这会使应用程序因unrecognized selector sent to instance 0x7f82f9705c30'而崩溃

我已尝试调试问题,但我明白我无法以这种方式发出警报,因为视图实际上是 tabBar 而不是我的场景。但在这里我卡住了。 我尝试在StarredViewController 中实现UITabBarControllerDelegate,但无法正常工作。

extension StarredViewController: UITabBarControllerDelegate {
    func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
        print("did select tab bar item!")
    }
}

我开始认为我使用SceneDelegateAppDelegate 的主要设置是错误的。 我发现的大多数以前的教程或线程似乎都无法编译,因为使用了已弃用的版本。

【问题讨论】:

    标签: ios swift storyboard uitabbarcontroller


    【解决方案1】:

    我解决了这个问题。实际上,我所有的假设和问题都是错误的。

    TL;DR 当我从中删除标签栏以编程方式制作时,故事板已损坏或损坏。

    这里是长版本。在遇到这个麻烦之前,我有一个带有两个视图和一个标签栏控制器的故事板。它工作得很好。有一次,我决定在应用程序启动期间做出选择,如果缺少默认值,请立即加载设置视图。我发现,要做到这一点,我必须将标签栏向下移动到场景委托并将其从情节提要中删除。我做到了,所以情节提要向未链接的视图显示,我从场景委托实例化了选项卡栏。

    奇怪的是,正在渲染的标签栏仍然显示之前在情节提要上设置的一些属性,即使该组件已被删除。

    那么,你就知道问题所在了。我的推理没有任何意义。 UITabBarController 无法显示任何警报。警报只能是 presented 上的 UIViewController。因此,继续尝试从标签栏发出警报是没有意义的。 这种错误的理解也导致我进行了错误的研究,报告了各种类似的问题(可能具有误导性)。

    我终于做了一个反测试。用故事板创建了一个全新的项目。在情节提要上创建了两个视图,并在场景委托上定义了一个选项卡栏控制器。它按预期工作。我将每个视图链接到特定的UIViewController。在视图上创建了一个按钮,添加了 IBAction 并且它起作用了。然后,我在IBAction 中创建了警报,这一次,工作正常。 我以相同的代码结束,唯一不同的是我没有从情节提要中创建和删除标签栏。

    我知道故事板可能会损坏,而且很可能我做到了。

    【讨论】:

      【解决方案2】:

      这是触发警报的代码。使用 addAction,您可以添加可能的答案。

      do {
          try //some method call or something else
      } catch {
          let alert = UIAlertController(title: "There was an error while saving!", message: "Please try again", preferredStyle: .alert)
          alert.addAction(UIAlertAction(title: "I understand", style: .cancel, handler: nil))
      }
      

      您可以在此处找到更多信息: how to show an alert

      要访问根控制器,您可以使用以下代码:

      let viewController = UIApplication.shared.windows.first!.rootViewController as! YourViewController
      

      【讨论】:

      • 那是在 SwiftUI 而不是 UIKIt 上。虽然我面临的问题不是设置警报,而是让它显示,因为警报应该由根控制器而不是当前视图引发,显然。
      • @Ste 我更新了关于根控制器的答案。此代码适用于 SwiftUI 和情节提要 :)
      • 谢谢。无论如何,我发现了这个问题,它与UITabBarController 无关。检查答案。
      【解决方案3】:

      这是一种从任何呈现的视图控制器呈现警报的方式。

      添加一些扩展:

      import UIKit
      
      extension UIViewController {
          var customVisibleViewController: UIViewController? {
              if let navigationController = self as? UINavigationController {
                  return navigationController.topViewController?.customVisibleViewController
              } else if let tabBarController = self as? UITabBarController {
                  return tabBarController.selectedViewController?.customVisibleViewController
              } else if let presentedViewController = presentedViewController {
                  return presentedViewController.customVisibleViewController
              } else if self is UIAlertController {
                  return nil
              } else {
                  return self
              }
          }
      }
      
      extension UIApplication {
          /// The top most view controller
          static var topMostViewController: UIViewController? {
              return UIApplication.shared.keyWindow?.rootViewController?.customVisibleViewController
          }
      }
      

      现在您可以通过这种方式显示警报:

      let alert = UIAlertController(title: "My Alert", message: "This is an alert.", preferredStyle: .alert)
      alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Default action"), 
                  style: .default, handler: { _ in
      NSLog("The \"OK\" alert occured.")
      }))
      UIApplication.topMostViewController?.present(alert, animated: true, completion: nil)
      

      【讨论】:

      • 理论上是有道理的,但是unrecognized selector sent to instance 0x7fce797084e0 也会崩溃。我将扩展添加到我的SettingsViewController 中,在其中,在 IBAction 中,我添加了用于提高警报的部分。
      猜你喜欢
      • 2013-10-14
      • 2012-05-31
      • 2017-12-06
      • 2012-01-29
      • 1970-01-01
      • 2020-08-05
      • 2016-07-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多