Включить представления SwiftUI в существующее приложение UIKit

Возможно ли строить представления с SwiftUI рядом с существующим приложением UIKit?

У меня есть приложение, написанное на Objective-C. Я начал миграцию на Swift 5. Мне интересно, могу ли я использовать SwiftUI вместе с моими существующими представлениями UIKit.xib.

То есть я хочу, чтобы некоторые представления, созданные с помощью SwiftUI, и некоторые другие представления, созданные с помощью UIKit в том же приложении. Не смешивая два, конечно.

SomeObjCSwiftProject/
    SwiftUIViewController.swift
    SwiftUIView.xib
    UIKitViewController.swift
    UIKitView.xib

Работать рядом друг с другом

Ответ 1

редактировать 06.05.19: Добавлена информация о UIHostingController, предложенная @Departamento B в его ответе. Кредиты идут к нему!


Использование SwiftUI с UIKit

Можно использовать SwiftUI компоненты в существующих UIKit сред обертывания SwiftUI View в UIHostingController, как это:

let swiftUIView = SomeSwiftUIView() // swiftUIView is View
let viewCtrl = UIHostingController(rootView: swiftUIView)

Также возможно переопределить UIHostingController и настроить его в соответствии с UIHostingController потребностями, например, установив вручную preferredStatusBarStyle SwiftUI если он не работает через SwiftUI как ожидалось.

UIHostingController здесь.


Использование UIKit с SwiftUI

Если существующий UIKit вид следует использовать в SwiftUI среде, UIViewRepresentable протокол, чтобы помочь! Это задокументировано здесь, и его можно увидеть в действии в этом официальном руководстве Apple.


Совместимость

Обратите внимание, что компоненты UIKit и SwiftUI могут использоваться только в сочетании, если приложение предназначено для iOS 13+, поскольку SwiftUI доступен только для iOS 13+. Смотрите этот пост для получения дополнительной информации.

Ответ 2

UIHostingController

Хотя на данный момент документация для этого класса еще не написана, UIHostingController<Content> кажется, является тем, что вы ищете: https://developer.apple.com/documentation/swiftui/uihostingcontroller

Я только что попробовал это в моем приложении со следующей строкой кода:

let vc = UIHostingController(rootView: BenefitsSwiftUIView())

Где BenefitsSwiftUIView просто по умолчанию "Hello World" View из SwiftUI. Это работает именно так, как вы ожидаете. Это также работает, если вы подкласс UIHostingController.

Ответ 3

Вы можете использовать их вместе. Вы можете "передать" UIView в View с UIViewRepresentable соответствия UIViewRepresentable. Подробности можно найти в официальном учебнике.

Тем не менее, вы также должны учитывать его совместимость.

Вот фрагмент кода из протокола View 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.
    /// ...
}

Так что это не обратно совместимо.

  • iOS 13. 0+
  • macOS 10. 15+
  • watchOS 6. 0+

Ответ 4

Старый проект:

Вы не можете использовать swiftUI и UIKit вместе.

Ссылайтесь на официальные документы Apple

Бета-версия Xcode 11 не поддерживает работу с SwiftUI в проекте, настроенном для использования UIKit для Mac.

Новый проект:

Да, может сделать это.

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol View : _View {


 }

SwiftUI не имеет обратной совместимости, поэтому ваша цель развертывания должна быть> = 13.0

Ответ 5

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