简体   繁体   English

使用 storyboard 和自定义 UITabBarController 显示警报

[英]Display an alert using storyboard and custom UITabBarController

I am dealing with a problem using UITabBarController.我正在处理使用 UITabBarController 的问题。 I have a small project using storyboards (XCode 13, IOS 15 as base system).我有一个使用故事板的小项目(XCode 13,IOS 15 作为基本系统)。 I created a TabBarController but I later discovered I could not manage it effectively programmatically.我创建了一个 TabBarController,但后来我发现我无法以编程方式有效地管理它。 Reading various docs, I discovered I could use two scenes from my storyboard and creating the tabbar programmatically.阅读各种文档后,我发现我可以使用 storyboard 中的两个场景并以编程方式创建标签栏。 So I did this in SceneDelegate.swift :所以我在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()

This works perfectly and I can easily put a condition whether userDefaults are not set, load directly the settings.这完美地工作,我可以很容易地设置一个条件是否userDefaults没有设置,直接加载设置。

In my class SettingsViewController I want to add an action where, upon pressing the button, you get an alert:在我的 class 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)
}

But this makes the app crashing with unrecognized selector sent to instance 0x7f82f9705c30'但这会使应用程序崩溃, unrecognized selector sent to instance 0x7f82f9705c30'

I've tried to debug the problem, and I understood I can't make the alert in this way because the view is really the tabBar and not the my scene.我试图调试问题,我明白我不能以这种方式发出警报,因为视图实际上是 tabBar 而不是我的场景。 But here I got stuck.但在这里我卡住了。 I tried to implement the UITabBarControllerDelegate , in StarredViewController , but I can't get it working.我试图在StarredViewController中实现UITabBarControllerDelegate ,但我无法让它工作。

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

I start thinking my main setup with SceneDelegate and AppDelegate is wrong.我开始认为我使用SceneDelegateAppDelegate的主要设置是错误的。 Most of previous tutorials or threads I've found seems to fail even to compile because using deprecated versions.我发现的大多数以前的教程或线程似乎都无法编译,因为使用了已弃用的版本。

This is a way to present an alert from any presented View Controller.这是一种从任何呈现的视图 Controller 呈现警报的方法。

Add some extensions:添加一些扩展:

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

Now you can show your alert in this way:现在您可以通过这种方式显示警报:

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)

This is the code to trigger an alert.这是触发警报的代码。 With addAction, you can add possible answers.使用 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))
}

you can find more information here: how to show an alert您可以在此处找到更多信息: 如何显示警报

I solved the problem.我解决了这个问题。 Actually, all my assumptions and the question were wrong.实际上,我所有的假设和问题都是错误的。

TL;DR the storyboard is corrupted or got damaged when I removed the tab bar from it to make it programmatically. TL; DR storyboard 已损坏或损坏,当我从其中删除标签栏以以编程方式进行制作时。

Here the long version.这里是长版。 Before entering in this trouble, I had a storyboard with two views and a tab bar controller.在进入这个麻烦之前,我有一个 storyboard 有两个视图和一个标签栏 controller。 It was working perfectly.它工作得很好。 At one point, I decided I wanted to make a choice during the app starting and, in case of missing defaults, load immediately the settings view.有一次,我决定在应用程序启动期间做出选择,如果缺少默认值,请立即加载设置视图。 I found that, to do this, I had to move my tab bar down to the scene delegate and remove it from storyboards.我发现,要做到这一点,我必须将标签栏向下移动到场景委托并将其从情节提要中删除。 I did it, so storyboard was showing to views no linked, and I instantiated the tab bar from the scene delegate.我做到了,所以 storyboard 显示给没有链接的视图,我从场景委托实例化了选项卡栏。

Weirdly, the tab bar being rendered was still showing some properties previously set on the storyboard, even if that component was deleted.奇怪的是,正在渲染的标签栏仍然显示之前在 storyboard 上设置的一些属性,即使该组件已被删除。

Then, you know the problem.然后,你就知道问题所在了。 My reasoning did not make any sense.我的推理没有任何意义。 The UITabBarController can't show any alert. UITabBarController不能显示任何警报。 Alerts can be presented on a UIViewController only.警报只能在UIViewControllerUIViewController So, it was pointless to keep trying to make an alert out from a tab bar.因此,继续尝试从标签栏发出警报是没有意义的。 This wrong understanding led me also to wrong research which reported various similar questions (probably misleading).这种错误的理解也导致我进行了错误的研究,报告了各种类似的问题(可能具有误导性)。

I finally made a counter test.我终于做了一个反测试。 Created a brand new project with storyboard.使用 storyboard 创建了一个全新的项目。 Created two views on the storyboard and defined a tab bar controller on the scene delegate.在 storyboard 上创建了两个视图,并在场景委托上定义了一个标签栏 controller。 It worked as expected.它按预期工作。 I linked each view to a specific UIViewController .我将每个视图链接到特定的UIViewController Created a button on the view, added the IBAction and it worked.在视图上创建了一个按钮,添加了IBAction并且它起作用了。 Then, I created the alert in the IBAction and, this time, worked exactly.然后,我在IBAction中创建了警报,这一次,工作正常。 I ended with the same code, and the only different is that I did not create and removed a tab bar from the storyboard.我以相同的代码结束,唯一不同的是我没有从 storyboard 中创建和删除标签栏。

I knew that storyboard can get damaged and, probably, I did it.我知道 storyboard 可能会损坏,而且我可能做到了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM