从 SwiftUI 推送到 UIViewController
Posted
技术标签:
【中文标题】从 SwiftUI 推送到 UIViewController【英文标题】:Push to UIViewController from SwiftUI 【发布时间】:2021-07-09 21:17:01 【问题描述】:我的应用是UIKit
和SwiftUI
的混合体。我有一个用例,用户可能需要从SwiftUI.View
中点击Button
,但推送到UIViewController
或另一个UIHostingController
。
我的项目使用Storyboard
。
我正在使用UINavigationController
和UITabBarController
。
我正在研究两种情况。
1.) 从我在主屏幕上首次启动时,我可以点击一个按钮,在它的操作中我有:
let vc = UIHostingController(rootView: RootView())
self.navigationController?.pushViewController(vc, animated: true)
这按预期工作。
2.) 我点击另一个选项卡,它默认为我的SwiftUI
RootView,它托管在我在Storyboard
中使用的自定义UIHostingController
中。在这里,如果我点击一个按钮来触发推送,它不会推送。我刚刚看到View
我正在更新。
我也在使用自定义UINavigationController
。从我的Storyboard
,制表关系转到自定义UINavigationController
,然后它的根是适当的UIViewController
。在一种情况下,虽然它是我的自定义 UIHostingController
,所以我可以从选项卡选择中加载 SwiftUI
视图。
这是我尝试处理从我的 SwiftUI 视图推送到视图控制器的操作:
final class AppData: ObservableObject
weak var window: UIWindow? // Will be nil in SwiftUI previewers
init(window: UIWindow? = nil)
self.window = window
public func pushViewController(_ viewController: UIViewController, animated: Bool = true)
let nvc = window?.rootViewController?.children.first?.children.first as? UINavigationController
nvc?.pushViewController(viewController, animated: animated)
// This is what is triggered from the Button action.
func optionSelected(option: String)
if let optionId = Common.getIDByOption(option: option)
let vc = UIHostingController(rootView: RootView())
appData.pushViewController(vc, animated: true)
会发生什么:
我确实看到数据发生了变化,但它们都在我已经使用的同一个视图上。 我需要推送到新的UIHostingController
。
【问题讨论】:
您能提供更多上下文吗?此视图是否由UINavigationController
或NavigationLink
控制?
@Błażej 我在上面添加了更多上下文。如果需要任何额外的上下文,请告诉我。
【参考方案1】:
如果您以 UITabBarController
和 UINavigationController
处理导航的方式混合 UIKit
和 SwiftUI
。我建议你剪掉NavigationView
和NavigationLink
。背后的原因很简单。 SwiftUI.View
将在每次切换到选项卡时重新创建。因此,您将从头开始。你可以绕着它走,但在这种情况下,使用UINavigationController
会更容易。
假设您的应用有两个标签。我会将SwiftUI.View
放入UIHostingController
,然后放入UINavigationController
。在SwiftUI.View
中,我将关闭let onTap: () -> Void
,只要您需要将下一个UIHostingController
或UIViewController
推送到UINavigationController
,就会调用它。
例子
//: A UIKit based Playground for presenting user interface
import UIKit
import SwiftUI
import PlaygroundSupport
final class MainViewController: UITabBarController
let firstTab: UINavigationController =
let navigationController = UINavigationController()
navigationController.tabBarItem = UITabBarItem(title: "First", image: nil, tag: 1)
return navigationController
()
let secondTab: UINavigationController =
let navigationController = UINavigationController()
navigationController.tabBarItem = UITabBarItem(title: "Second", image: nil, tag: 1)
return navigationController
()
override func viewDidLoad()
super.viewDidLoad()
setUpFirstTab()
setUpSecondTab()
viewControllers = [firstTab, secondTab]
func setUpFirstTab()
let firstView = FirstView number in
let secondViewHostingController = UIHostingController(rootView: SecondView(number: number))
self.firstTab.pushViewController(secondViewHostingController, animated: true)
let firstViewHostingController = UIHostingController(rootView: firstView)
firstTab.tabBarItem = UITabBarItem(title: "First", image: nil, tag: 1)
firstTab.viewControllers = [firstViewHostingController]
func setUpSecondTab()
secondTab.tabBarItem = UITabBarItem(title: "Second", image: nil, tag: 2)
secondTab.viewControllers = [FirstViewController()]
// Views for the first tab
struct FirstView: View
let onTap: (Int) -> Void
@State var number: Int = 0
var body: some View
VStack
Stepper("Number \(number)", value: $number)
Button
onTap(number)
label:
Text("Go to the next view")
struct SecondView: View
let number: Int
var body: some View
Text("Final view with number \(number)")
// Views controllers for the second tab
final class FirstViewController: UIViewController
override func viewDidLoad()
super.viewDidLoad()
let button: UIButton = UIButton(type: .roundedRect, primaryAction: UIAction(title: "show next view controller") _ in
self.navigationController?.pushViewController(SecondViewController(), animated: true)
)
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
final class SecondViewController: UIViewController
override func viewDidLoad()
super.viewDidLoad()
view.backgroundColor = .systemPink
【讨论】:
以上是关于从 SwiftUI 推送到 UIViewController的主要内容,如果未能解决你的问题,请参考以下文章
是否可以在 SwiftUI 中提供服务并将数据推送到视图并更新 UI?
在 SwiftUI 中,为啥 Spacer 会将文本推送到 TextEditor 框架之外?