[英]Push to UIViewController from SwiftUI
My app is a hybrid of UIKit
& SwiftUI
.我的应用程序是UIKit
和SwiftUI
的混合SwiftUI
。 I have a use case where a user may need to tap on a Button
from a SwiftUI.View
but push to a UIViewController
or another UIHostingController
.我有一个用例,用户可能需要点击SwiftUI.View
的Button
但推送到UIViewController
或另一个UIHostingController
。
My project uses Storyboard
.我的项目使用Storyboard
。
I'm using UINavigationController
& UITabBarController
.我正在使用UINavigationController
和UITabBarController
。
There are two scenarios I am looking at.我正在考虑两种情况。
1.) From my initial launch on my home screen, I can tap a button and within its action I have: 1.) 从我在主屏幕上的初始启动开始,我可以点击一个按钮,并在其操作中:
let vc = UIHostingController(rootView: RootView())
self.navigationController?.pushViewController(vc, animated: true)
This works as expected.这按预期工作。
2.) I tap on a different tab and it defaults to my SwiftUI
RootView which is hosted in a custom UIHostingController
which I use in my Storyboard
. 2.) 我点击一个不同的选项卡,它默认为我的SwiftUI
RootView,它托管在我在Storyboard
使用的自定义UIHostingController
中。 Here, if I tap on a button to trigger the push, it doesn't push.在这里,如果我点击一个按钮来触发推送,它不会推送。 I just see the View
I am on update.我只看到我正在更新的View
。
I'm also using a custom UINavigationController
.我也在使用自定义UINavigationController
。 From my Storyboard
, the tabs relationship goes to the custom UINavigationController
& then its root is the appropriate UIViewController
.从我的Storyboard
,选项卡关系转到自定义UINavigationController
& 然后它的根是适当的UIViewController
。 In one scenario though it's my custom UIHostingController
so I can load a SwiftUI
View initially from the tab selection.在一种情况下,虽然它是我的自定义UIHostingController
因此我可以最初从选项卡选择加载SwiftUI
视图。
Here is what I have tried doing to handle push to a View Controller from my SwiftUI View:这是我尝试从我的 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)
}
}
What happens:怎么了:
UIHostingController
.我需要推送到新的UIHostingController
。If you're mixing UIKit
and SwiftUI
in a way where UITabBarController
and UINavigationController
are handling the navigation.如果您以UITabBarController
和UINavigationController
处理导航的方式混合UIKit
和SwiftUI
。 I advise you to cut NavigationView
and NavigationLink
.我建议你剪掉NavigationView
和NavigationLink
。 The reason behind it is simple.背后的原因很简单。 SwiftUI.View
will be recreated on each switch to tab. SwiftUI.View
将在每次切换到选项卡时重新创建。 Hence, you would start from the begining.因此,您将从头开始。 You could walk around it, but in this situation, easier will be use UINavigationController
.您可以四处走动,但在这种情况下,使用UINavigationController
会更容易。
Let's assume you're app has two tabs.假设您的应用程序有两个选项卡。 I would put SwiftUI.View
in UIHostingController
and then in UINavigationController
.我会把SwiftUI.View
在UIHostingController
,然后在UINavigationController
。 In SwiftUI.View
I would put closure let onTap: () -> Void
which would be called whenever you need to push next UIHostingController
or UIViewController
to UINavigationController
.在SwiftUI.View
我将关闭let onTap: () -> Void
每当您需要将下一个UIHostingController
或UIViewController
推送到UINavigationController
时都会调用它。
Example例子
//: 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
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.