有没有办法在同一个 iOS Xcode 项目中使用故事板和 SwiftUI?
Is there any way to use storyboard and SwiftUI in same iOS Xcode project?
由于 Swift 5 介绍了用于创建视图的 SwiftUI 框架,但我们目前正在使用 Storyboard 进行 UI 设计。
所以我只想知道在同一个 iOS 单视图应用程序中使用 Storyboard 和 Swift UI 的过程。
您需要向 Storyboard 添加 "Host View Controller"。 SwiftUI 表单将显示在主机视图控制器中,并且可以从任何情节提要表单中调用。
请注意,Xcode 11 在 Mohave 上的库中不会显示主机视图控制器,您必须在 Catalina 上。这是一个错误,因为一旦您在 Catalina 上创建了一个带有主机视图控制器的项目,该项目将 运行 在 Mohave 上正常运行,事实上,您甚至可以将该主机视图控制器复制到其他 Storyboards,它会炒锅很好。
正如 Edgell 提到的,有一个名为 HostViewController
的新 ViewController
,可以在其中托管一个 SwiftUI
页面。
在 WWDC 上有关于将 SwiftUI 集成到现有项目中的完整讨论,很好地回答了您的问题。
集成 SwiftUI:
https://developer.apple.com/videos/play/wwdc2019/231/
刚开始看SwiftUI
。分享一个小例子。
- 在
storyboard
添加 Hosting View Controller
- Subclass
UIHostingController
与您自己的 class (ChildHostingController
)
ChildHostingController
应该是这样的:
import UIKit
import SwiftUI
struct SecondView: View {
var body: some View {
VStack {
Text("Second View").font(.system(size: 36))
Text("Loaded by SecondView").font(.system(size: 14))
}
}
}
class ChildHostingController: UIHostingController<SecondView> {
required init?(coder: NSCoder) {
super.init(coder: coder,rootView: SecondView());
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
有关详细信息,请查看 Custom UIHostingController
Apple Docs UIhostingController(不幸的是它还没有被记录下来)
Integrating SwiftUI Video
是的,你可以做到!以下是您可以执行的步骤:
转到您当前的 Xcode 项目 -> Storyboard,单击 + 号(右上角)并搜索 Hosting Controller (就像您对按钮或标签所做的那样)。
将托管控制器拖到您的情节提要中。创建从您的 UI 元素(我使用的是按钮)到该托管控制器和 select 推送的 Segue 连接。
创建从该 Segue 到您的视图控制器的出口连接(这是一项新功能 - 就像您为标签创建出口一样),并命名它。
- 在此出口连接内声明您的视图(您可以这样做,不必使用 PrepareForSegue 方法),然后 return 它。
例如:我在当前项目中创建了一个 SwiftUI 视图(在 Xcode 中:文件 -> 新建 -> 文件 -> SwiftUI 视图)并调用它详情查看。我的插座连接如下所示:
import UIKit
import SwiftUI
class ViewController: UIViewController {
@IBSegueAction func showDetails(_ coder: NSCoder) -> UIViewController? {
let detailsView = DetailsView()
return UIHostingController(coder: coder, rootView: detailsView)
}
override func viewDidLoad() {
super.viewDidLoad()
// some code
}
}
就是这样!现在运行吧。
将 UIKit 与 SwiftUI 混合使用
两者都可以在故事板中嵌入和混合 SwiftUI,反之亦然
UIViewControllerRepresentable 用于在 SwiftUI 中嵌入故事板
在 Storyboard 上创建 ViewController 并为其指定 Storyboard ID
import SwiftUI
struct ContentView: View {
var body: some View {
StoryboardViewController()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct StoryboardViewController: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> some UIViewController {
let storyboard = UIStoryboard(name: "Storyboard", bundle: Bundle.main)
let controller = storyboard.instantiateViewController(identifier: "Main")
return controller
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
}
}
UIKit 和 SwiftUI 之间的通信可以通过 @Binding 变量。这将使 SwiftUI 视图注入变量状态并控制它,func updateUIViewController 将在更改时调用以完成工作。
UIViewRepresentable 对视图使用相同的方式
import SwiftUI
struct ContentView: View {
@State var color = UIColor.green
var body: some View {
SampleView(color: $color).frame(width: 100, height: 100
, alignment: .center)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct SampleView: UIViewRepresentable {
@Binding var color: UIColor
func makeUIView(context: Context) -> some UIView {
return UIView()
}
func updateUIView(_ uiView: UIViewType, context: Context) {
uiView.backgroundColor = color
}
}
UIHostingController 用于在 UIKit 中嵌入 SwiftUI
首先将以编程方式创建
let childViewController = UIHostingController(rootView: SwiftUIContentView())
addChild(childViewController)
childViewController.view.frame = frame
view.addSubview(childViewController.view)
childViewController.didMove(toParent: self)
Second with Xcode 11 通过将 Hosting View Controller 添加到故事板并为其创建一个 segue,然后通过 Control-drag 从 segue 创建一个 @IBSegueAction到您的 ViewController 然后创建 SwiftUI 视图的实例并将其传递给 HostingViewController initializer
第三种 是将 Hosting View Controller 添加到情节提要中,然后按照上一个答案中所述对其进行子类化
由于 Swift 5 介绍了用于创建视图的 SwiftUI 框架,但我们目前正在使用 Storyboard 进行 UI 设计。
所以我只想知道在同一个 iOS 单视图应用程序中使用 Storyboard 和 Swift UI 的过程。
您需要向 Storyboard 添加 "Host View Controller"。 SwiftUI 表单将显示在主机视图控制器中,并且可以从任何情节提要表单中调用。
请注意,Xcode 11 在 Mohave 上的库中不会显示主机视图控制器,您必须在 Catalina 上。这是一个错误,因为一旦您在 Catalina 上创建了一个带有主机视图控制器的项目,该项目将 运行 在 Mohave 上正常运行,事实上,您甚至可以将该主机视图控制器复制到其他 Storyboards,它会炒锅很好。
正如 Edgell 提到的,有一个名为 HostViewController
的新 ViewController
,可以在其中托管一个 SwiftUI
页面。
在 WWDC 上有关于将 SwiftUI 集成到现有项目中的完整讨论,很好地回答了您的问题。
集成 SwiftUI: https://developer.apple.com/videos/play/wwdc2019/231/
刚开始看SwiftUI
。分享一个小例子。
- 在
storyboard
添加Hosting View Controller
- Subclass
UIHostingController
与您自己的 class (ChildHostingController
) ChildHostingController
应该是这样的:
import UIKit
import SwiftUI
struct SecondView: View {
var body: some View {
VStack {
Text("Second View").font(.system(size: 36))
Text("Loaded by SecondView").font(.system(size: 14))
}
}
}
class ChildHostingController: UIHostingController<SecondView> {
required init?(coder: NSCoder) {
super.init(coder: coder,rootView: SecondView());
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
有关详细信息,请查看 Custom UIHostingController
Apple Docs UIhostingController(不幸的是它还没有被记录下来)
Integrating SwiftUI Video
是的,你可以做到!以下是您可以执行的步骤:
转到您当前的 Xcode 项目 -> Storyboard,单击 + 号(右上角)并搜索 Hosting Controller (就像您对按钮或标签所做的那样)。
将托管控制器拖到您的情节提要中。创建从您的 UI 元素(我使用的是按钮)到该托管控制器和 select 推送的 Segue 连接。 创建从该 Segue 到您的视图控制器的出口连接(这是一项新功能 - 就像您为标签创建出口一样),并命名它。
- 在此出口连接内声明您的视图(您可以这样做,不必使用 PrepareForSegue 方法),然后 return 它。
例如:我在当前项目中创建了一个 SwiftUI 视图(在 Xcode 中:文件 -> 新建 -> 文件 -> SwiftUI 视图)并调用它详情查看。我的插座连接如下所示:
import UIKit
import SwiftUI
class ViewController: UIViewController {
@IBSegueAction func showDetails(_ coder: NSCoder) -> UIViewController? {
let detailsView = DetailsView()
return UIHostingController(coder: coder, rootView: detailsView)
}
override func viewDidLoad() {
super.viewDidLoad()
// some code
}
}
就是这样!现在运行吧。
将 UIKit 与 SwiftUI 混合使用
两者都可以在故事板中嵌入和混合 SwiftUI,反之亦然
UIViewControllerRepresentable 用于在 SwiftUI 中嵌入故事板
在 Storyboard 上创建 ViewController 并为其指定 Storyboard ID
import SwiftUI
struct ContentView: View {
var body: some View {
StoryboardViewController()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct StoryboardViewController: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> some UIViewController {
let storyboard = UIStoryboard(name: "Storyboard", bundle: Bundle.main)
let controller = storyboard.instantiateViewController(identifier: "Main")
return controller
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
}
}
UIKit 和 SwiftUI 之间的通信可以通过 @Binding 变量。这将使 SwiftUI 视图注入变量状态并控制它,func updateUIViewController 将在更改时调用以完成工作。
UIViewRepresentable 对视图使用相同的方式
import SwiftUI
struct ContentView: View {
@State var color = UIColor.green
var body: some View {
SampleView(color: $color).frame(width: 100, height: 100
, alignment: .center)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct SampleView: UIViewRepresentable {
@Binding var color: UIColor
func makeUIView(context: Context) -> some UIView {
return UIView()
}
func updateUIView(_ uiView: UIViewType, context: Context) {
uiView.backgroundColor = color
}
}
UIHostingController 用于在 UIKit 中嵌入 SwiftUI
首先将以编程方式创建
let childViewController = UIHostingController(rootView: SwiftUIContentView())
addChild(childViewController)
childViewController.view.frame = frame
view.addSubview(childViewController.view)
childViewController.didMove(toParent: self)
Second with Xcode 11 通过将 Hosting View Controller 添加到故事板并为其创建一个 segue,然后通过 Control-drag 从 segue 创建一个 @IBSegueAction到您的 ViewController 然后创建 SwiftUI 视图的实例并将其传递给 HostingViewController initializer
第三种 是将 Hosting View Controller 添加到情节提要中,然后按照上一个答案中所述对其进行子类化