是否可以通过SwiftUI与现有UIKit应用程序并排构建视图?
我有一个用Objective-C编写的现有应用程序。我已经开始迁移到Swift5。我想知道是否可以将SwiftUI与现有的UIKit .xib视图一起使用。
也就是说,我希望在同一应用程序中使用SwiftUI构建一些视图,并使用UIKit构建一些其他视图。当然不要混合两者。
SomeObjCSwiftProject/
SwiftUIViewController.swift
SwiftUIView.xib
UIKitViewController.swift
UIKitView.xib
Run Code Online (Sandbox Code Playgroud)
彼此一起工作
fre*_*dpi 36
编辑05/06/19:添加了有关UIHostingController的信息,如@Departamento B在其答案中所建议。学分归他所有!
可以通过将a包裹成这样,SwiftUI在现有UIKit环境中使用组件:SwiftUI ViewUIHostingController
let swiftUIView = SomeSwiftUIView() // swiftUIView is View
let viewCtrl = UIHostingController(rootView: swiftUIView)
Run Code Online (Sandbox Code Playgroud)
也可以根据需要改写UIHostingController和自定义它,例如,preferredStatusBarStyle如果无法正常使用,则通过手动设置SwiftUI。
UIHostingController记录在这里。
如果UIKit应该在SwiftUI环境中使用现有视图,则可以使用该UIViewRepresentable协议!它已在此处记录,可以在此 Apple官方教程中实际使用。
兼容性
请注意,仅当该应用程序面向iOS 13+时,才可以UIKit与和SwiftUI组件一起使用,这SwiftUI仅适用于iOS 13+。有关更多信息,请参见此帖子。
Mik*_*Lee 27
如果要将 SwiftUI 嵌入到 UIKit 视图控制器中,请使用容器视图。
class ViewController: UIViewController {
@IBOutlet weak var theContainer: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let childView = UIHostingController(rootView: SwiftUIView())
addChild(childView)
childView.view.frame = theContainer.bounds
theContainer.addSubview(childView.view)
childView.didMove(toParent: self)
}
}
Run Code Online (Sandbox Code Playgroud)
Jas*_*ues 10
/**
* Adapts a SwiftUI view for use inside a UIViewController.
*/
class SwiftUIAdapter<Content> where Content : View {
private(set) var view: Content!
weak private(set) var parent: UIViewController!
private(set) var uiView : WrappedView
private var hostingController: UIHostingController<Content>
init(view: Content, parent: UIViewController) {
self.view = view
self.parent = parent
hostingController = UIHostingController(rootView: view)
parent.addChild(hostingController)
hostingController.didMove(toParent: parent)
uiView = WrappedView(view: hostingController.view)
}
deinit {
hostingController.removeFromParent()
hostingController.didMove(toParent: nil)
}
}
Run Code Online (Sandbox Code Playgroud)
class FeedViewController: UIViewController {
var adapter : SwiftUIAdapter<FeedView>!
override required init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
adapter = SwiftUIAdapter(view: FeedView(), parent: self)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
/** Override load view to load the SwiftUI adapted view */
override func loadView() {
view = adapter.uiView;
}
}
Run Code Online (Sandbox Code Playgroud)
对于这个非常简单的情况,包装视图使用手动布局(layoutSubViews)而不是自动布局。
class WrappedView: UIView {
private (set) var view: UIView!
init(view: UIView) {
self.view = view
super.init(frame: CGRect.zero)
addSubview(view)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func layoutSubviews() {
super.layoutSubviews()
view.frame = bounds
}
}
Run Code Online (Sandbox Code Playgroud)
尽管此刻该类的文档尚未编写,但UIHostingController<Content>似乎正是您所需要的:https : //developer.apple.com/documentation/swiftui/uihostingcontroller
我刚刚在我的应用中使用以下代码行对其进行了尝试:
let vc = UIHostingController(rootView: BenefitsSwiftUIView())
Run Code Online (Sandbox Code Playgroud)
其中BenefitsSwiftUIView仅仅是默认的“Hello World” View的SwiftUI。这完全符合您的预期。如果您继承了子类,它也可以工作UIHostingController。
我尚未提及的一项涉及 Xcode 11 beta 5 (11M382q) 涉及更新应用程序的 info.plist 文件。
对于我的场景,我正在使用现有的基于 Swift 和 UIKit 的应用程序并将其完全迁移为 iOS 13 和纯 SwiftUI 应用程序,因此向后兼容性对我来说不是问题。
对 AppDelegate 进行必要的更改后:
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration",
sessionRole: connectingSceneSession.role)
}
Run Code Online (Sandbox Code Playgroud)
并添加一个 SceneDelegate 类:
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: HomeList())
self.window = window
window.makeKeyAndVisible()
}
}
}
Run Code Online (Sandbox Code Playgroud)
我遇到了一个问题,我的 SceneDelegate 没有被调用。这是通过将以下内容添加到我的 info.plist 文件中来解决的:
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string></string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneStoryboardFile</key>
<string>LaunchScreen</string>
</dict>
</array>
</dict>
</dict>
Run Code Online (Sandbox Code Playgroud)
保持同步的主要项目是:
SceneDelegate文件UISceneConfiguration这样做之后,我就可以加载我新创建的 HomeList 视图(一个 SwiftUI 对象)
如果您遇到布局问题,您必须向 UIHostingController 的视图添加约束,
class ViewController: UIViewController {
@IBOutlet weak var theContainer: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let childView = UIHostingController(rootView: SwiftUIView())
addChild(childView)
childView.view.frame = theContainer.bounds
theContainer.addConstrained(subview: childView.view)
childView.didMove(toParent: self)
}
}
Run Code Online (Sandbox Code Playgroud)
使用这个扩展:
extension UIView {
func addConstrained(subview: UIView) {
addSubview(subview)
subview.translatesAutoresizingMaskIntoConstraints = false
subview.topAnchor.constraint(equalTo: topAnchor).isActive = true
subview.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
subview.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
subview.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
9748 次 |
| 最近记录: |