Using a HostingViewController , a SwiftUI view can be treated either as an entire scene (occupying the full screen) or as an individual component within an existing UIKit scene.
SwiftUI makes it easy to refactor and reuse code in many features. If both views only used List to display gems, with no other view inside, it would be straightforward to extract the code into another view and use it in both features.
edit 05/06/19: Added information about UIHostingController as suggested by @Departamento B in his answer. Credits go to him!
One can use SwiftUI
components in existing UIKit
environments by wrapping a SwiftUI
View
into a UIHostingController
like this:
let swiftUIView = SomeSwiftUIView() // swiftUIView is View
let viewCtrl = UIHostingController(rootView: swiftUIView)
It's also possible to override UIHostingController
and customize it to one's needs, e. g. by setting the preferredStatusBarStyle
manually if it doesn't work via SwiftUI
as expected.
UIHostingController
is documented here.
If an existing UIKit
view should be used in a SwiftUI
environment, the UIViewRepresentable
protocol is there to help! It is documented here and can be seen in action in this official Apple tutorial.
Compatibility
Please note that you cannot use SwiftUI
on iOS versions < iOS 13, as SwiftUI
is only available on iOS 13 and above. See this post for more information.
If you want to use SwiftUI
in a project with a target below iOS 13, you need to tag your SwiftUI
structs with @available(iOS 13.0.0, *)
attribute.
If you want to embed SwiftUI into a UIKit view controller, use a Container View.
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)
}
}
Reference
Although at the moment the documentation for the class has not been written, UIHostingController<Content>
seems to be what you're looking for: https://developer.apple.com/documentation/swiftui/uihostingcontroller
I've just tried it in my app with the following line of code:
let vc = UIHostingController(rootView: BenefitsSwiftUIView())
Where BenefitsSwiftUIView
is just the default "Hello World" View
from SwiftUI
. This works exactly as you expect it. It also works if you subclass UIHostingController
.
One item I have not seen mentioned yet, and involves Xcode 11 beta 5 (11M382q) involves updating your app's info.plist file.
For my scenario, I am taking an existing Swift & UIKit based application and fully migrating it to be an iOS 13 & pure SwiftUI app, so backwards compatibility is not a concern for me.
After making the necessary changes to AppDelegate:
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration",
sessionRole: connectingSceneSession.role)
}
And adding in a SceneDelegate class:
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()
}
}
}
I was encountering a problem where my SceneDelegate was not being called. This was fixed by adding the following into my info.plist file:
<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>
And a screenshot to see:
The main items to keep in sync are:
SceneDelegate
fileUISceneConfiguration
After doing this, I was then able to load my newly created HomeList view (A SwiftUI object)
If you're looking to create a SwiftUI
view from a legacy Objective C project, then this technique worked perfectly for me,
See Adding SwiftUI to Objective-C Apps
Kudos to our friend who wrote that up.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With