edit 05/06/19: Informationen zu UIHostingController hinzugefügt, wie von @Departamento B in seiner Antwort vorgeschlagen. Credits gehen an ihn!
Verwenden von SwiftUI in UIKit
Man kann SwiftUI
Komponenten in vorhandenen UIKit
Umgebungen verwenden, indem man a SwiftUI
View
in eine der folgenden UIHostingController
umschließt:
let swiftUIView = SomeSwiftUIView()
let viewCtrl = UIHostingController(rootView: swiftUIView)
Es ist auch möglich, es zu überschreiben UIHostingController
und an die eigenen Bedürfnisse anzupassen, z. B. durch preferredStatusBarStyle
manuelles Einstellen, wenn es nicht SwiftUI
wie erwartet funktioniert .
UIHostingController
ist hier dokumentiert .
Verwenden von UIKit in SwiftUI
Wenn eine vorhandene UIKit
Ansicht in einer SwiftUI
Umgebung UIViewRepresentable
verwendet werden soll, hilft das Protokoll! Es ist hier dokumentiert und kann in diesem offiziellen Apple-Tutorial in Aktion gesehen werden .
Kompatibilität
Bitte beachten Sie, dass Sie nicht SwiftUI
unter iOS-Versionen <iOS 13 verwenden können, da dies SwiftUI
nur unter iOS 13 und höher verfügbar ist. Weitere Informationen finden Sie in diesem Beitrag.
Wenn Sie SwiftUI
in einem Projekt mit einem Ziel unter iOS 13 verwenden möchten , müssen Sie Ihre SwiftUI
Strukturen mit @available(iOS 13.0.0, *)
Attributen versehen.
UIViewRepresentable
scheint eher das Gegenteil zu tun, was es ermöglicht, aUIView
zu einerSwiftUI
Hierarchie hinzuzufügenUIHostingController
Obwohl die Dokumentation für die Klasse derzeit noch nicht geschrieben wurde,
UIHostingController<Content>
scheint sie genau das zu sein, wonach Sie suchen: https://developer.apple.com/documentation/swiftui/uihostingcontrollerIch habe es gerade in meiner App mit der folgenden Codezeile versucht:
let vc = UIHostingController(rootView: BenefitsSwiftUIView())
Wo
BenefitsSwiftUIView
ist nur der default „Hallo Welt“View
ausSwiftUI
. Dies funktioniert genau so, wie Sie es erwarten. Es funktioniert auch, wenn Sie UnterklasseUIHostingController
.quelle
Wenn Sie SwiftUI in einen UIKit View Controller einbetten möchten, verwenden Sie eine 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) } }
Referenz
quelle
Ein Element, das ich noch nicht erwähnt habe und das Xcode 11 Beta 5 (11M382q) beinhaltet, beinhaltet das Aktualisieren der info.plist-Datei Ihrer App.
In meinem Szenario nehme ich eine vorhandene Swift & UIKit-basierte Anwendung und migriere sie vollständig zu einer iOS 13 & reinen SwiftUI-App, sodass die Abwärtskompatibilität für mich kein Problem darstellt.
Nachdem Sie die erforderlichen Änderungen an AppDelegate vorgenommen haben:
// MARK: UISceneSession Lifecycle func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) }
Und Hinzufügen einer SceneDelegate-Klasse:
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() } } }
Ich hatte ein Problem, bei dem mein SceneDelegate nicht aufgerufen wurde. Dies wurde behoben, indem Folgendes in meine info.plist-Datei eingefügt wurde:
<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>
Und ein Screenshot zu sehen:
Die wichtigsten Elemente, die synchron gehalten werden müssen, sind:
SceneDelegate
Datei befindetUISceneConfiguration
Danach konnte ich meine neu erstellte HomeList-Ansicht (ein SwiftUI-Objekt) laden.
quelle
Wenn Sie eine
SwiftUI
Ansicht aus einem älteren Objective C-Projekt erstellen möchten, hat diese Technik für mich perfekt funktioniert.Siehe Hinzufügen von SwiftUI zu Objective-C-Apps
Ein großes Lob an unseren Freund, der das geschrieben hat.
quelle
Mit dem Storyboard
Sie können die
HotingViewController
Komponente im Interface Builder verwenden:Dann, wenn Sie eine einfache
HotingController
wie diese haben:class MySwiftUIHostingController: UIHostingController<Text> { required init?(coder: NSCoder) { super.init(coder: coder, rootView: Text("Hello World")) } }
Sie können es als benutzerdefinierte Klasse des Controllers festlegen:
Mit Code
let mySwiftUIHostingController = UIHostingController(rootView: Text("Hello World"))
Und dann können Sie es wie ein normaler verwenden
UIViewController
Wichtige Notiz
Vergessen Sie nicht, zu importieren,
SwiftUI
wo immer Sie es brauchenUIHostingController
quelle
Wenn Sie mit Layoutproblemen konfrontiert sind, müssen Sie der Ansicht von UIHostingController Einschränkungen hinzufügen.
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) } }
mit dieser Erweiterung:
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 } }
quelle
Sie können sie zusammen verwenden. Sie können ‚Transfer‘ ein ,
UIView
umView
durchUIViewRepresentable
Konformität. Details finden Sie im offiziellen Tutorial .Sie sollten jedoch auch die Kompatibilität berücksichtigen.
Hier ist das Code-Snippet aus dem Protokoll
View
von SwiftUI :/// /// You create custom views by declaring types that conform to the `View` /// protocol. Implement the required `body` property to provide the content /// and behavior for your custom view. @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) public protocol View : _View { /// The type of view representing the body of this view. /// /// When you create a custom view, Swift infers this type from your /// implementation of the required `body` property. /// ... }
Es ist also nicht abwärtskompatibel.
quelle
import Foundation #if canImport(SwiftUI) import SwiftUI internal final class SomeRouter { fileprivate weak var presentingViewController: UIViewController! function navigateToSwiftUIView() { if #available(iOS 13, *) { let hostingController = UIHostingController(rootView: contentView()) presentingViewController?.navigationController?.pushViewController(hostingController, animated: true) return } //Keep the old way when not 13. } #endif
quelle
Andere haben gezeigt, wie UIHostingController verwendet wird .
Ich kann zeigen, wie Sie einen UIViewController von SwiftUI UIViewControllerRepresentable präsentieren können :
struct YourViewControllerWrapper: UIViewControllerRepresentable { typealias UIViewControllerType = YourViewController func makeUIViewController(context: UIViewControllerRepresentableContext<YourViewControllerWrapper>) -> YourViewController { let storyBoard = UIStoryboard(name: "YourStoryboard", bundle: Bundle.main) return storyBoard.instantiateViewController(withIdentifier: "YourViewController") as! YourViewController } func updateUIViewController(_ uiViewController: YourViewController, context: UIViewControllerRepresentableContext<YourViewController>) { // do nothing } }
quelle