mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-20 13:19:16 +00:00
wip
This commit is contained in:
parent
f38e31dc22
commit
aee63afc1a
@ -3,6 +3,7 @@ import SwiftUI
|
|||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import Display
|
import Display
|
||||||
|
|
||||||
|
let SHOW_SAFE_AREA = false
|
||||||
|
|
||||||
@objc(AppDelegate)
|
@objc(AppDelegate)
|
||||||
final class AppDelegate: NSObject, UIApplicationDelegate {
|
final class AppDelegate: NSObject, UIApplicationDelegate {
|
||||||
@ -18,7 +19,6 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
|
|||||||
hostView.containerView.backgroundColor = UIColor.white
|
hostView.containerView.backgroundColor = UIColor.white
|
||||||
self.window = window
|
self.window = window
|
||||||
|
|
||||||
|
|
||||||
let navigationController = NavigationController(
|
let navigationController = NavigationController(
|
||||||
mode: .single,
|
mode: .single,
|
||||||
theme: NavigationControllerTheme(
|
theme: NavigationControllerTheme(
|
||||||
@ -30,7 +30,49 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
|
|||||||
|
|
||||||
mainWindow.viewController = navigationController
|
mainWindow.viewController = navigationController
|
||||||
|
|
||||||
navigationController.setViewControllers([mySwiftUIViewController(0)], animated: false)
|
let rootViewController = mySwiftUIViewController(0)
|
||||||
|
|
||||||
|
if SHOW_SAFE_AREA {
|
||||||
|
// Add insets visualization
|
||||||
|
rootViewController.view.layoutMargins = .zero
|
||||||
|
rootViewController.view.subviews.forEach { $0.removeFromSuperview() }
|
||||||
|
|
||||||
|
let topInsetView = UIView()
|
||||||
|
let leftInsetView = UIView()
|
||||||
|
let rightInsetView = UIView()
|
||||||
|
let bottomInsetView = UIView()
|
||||||
|
|
||||||
|
[topInsetView, leftInsetView, rightInsetView, bottomInsetView].forEach {
|
||||||
|
$0.backgroundColor = .systemRed
|
||||||
|
$0.alpha = 0.3
|
||||||
|
rootViewController.view.addSubview($0)
|
||||||
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
}
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
topInsetView.topAnchor.constraint(equalTo: rootViewController.view.topAnchor),
|
||||||
|
topInsetView.leadingAnchor.constraint(equalTo: rootViewController.view.leadingAnchor),
|
||||||
|
topInsetView.trailingAnchor.constraint(equalTo: rootViewController.view.trailingAnchor),
|
||||||
|
topInsetView.bottomAnchor.constraint(equalTo: rootViewController.view.safeAreaLayoutGuide.topAnchor),
|
||||||
|
|
||||||
|
leftInsetView.topAnchor.constraint(equalTo: rootViewController.view.topAnchor),
|
||||||
|
leftInsetView.leadingAnchor.constraint(equalTo: rootViewController.view.leadingAnchor),
|
||||||
|
leftInsetView.bottomAnchor.constraint(equalTo: rootViewController.view.bottomAnchor),
|
||||||
|
leftInsetView.trailingAnchor.constraint(equalTo: rootViewController.view.safeAreaLayoutGuide.leadingAnchor),
|
||||||
|
|
||||||
|
rightInsetView.topAnchor.constraint(equalTo: rootViewController.view.topAnchor),
|
||||||
|
rightInsetView.trailingAnchor.constraint(equalTo: rootViewController.view.trailingAnchor),
|
||||||
|
rightInsetView.bottomAnchor.constraint(equalTo: rootViewController.view.bottomAnchor),
|
||||||
|
rightInsetView.leadingAnchor.constraint(equalTo: rootViewController.view.safeAreaLayoutGuide.trailingAnchor),
|
||||||
|
|
||||||
|
bottomInsetView.bottomAnchor.constraint(equalTo: rootViewController.view.bottomAnchor),
|
||||||
|
bottomInsetView.leadingAnchor.constraint(equalTo: rootViewController.view.leadingAnchor),
|
||||||
|
bottomInsetView.trailingAnchor.constraint(equalTo: rootViewController.view.trailingAnchor),
|
||||||
|
bottomInsetView.topAnchor.constraint(equalTo: rootViewController.view.safeAreaLayoutGuide.bottomAnchor)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
navigationController.setViewControllers([rootViewController], animated: false)
|
||||||
|
|
||||||
self.window?.makeKeyAndVisible()
|
self.window?.makeKeyAndVisible()
|
||||||
|
|
||||||
|
|||||||
@ -6,288 +6,62 @@ import UIKit
|
|||||||
import LegacyUI
|
import LegacyUI
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
|
||||||
public class SwiftUIViewControllerInteraction {
|
struct MySwiftUIView: View {
|
||||||
let push: (ViewController) -> Void
|
weak var wrapperController: LegacyController?
|
||||||
let present: (
|
|
||||||
_ controller: ViewController,
|
|
||||||
_ in: PresentationContextType,
|
|
||||||
_ with: ViewControllerPresentationArguments?
|
|
||||||
) -> Void
|
|
||||||
let dismiss: (_ animated: Bool, _ completion: (() -> Void)?) -> Void
|
|
||||||
|
|
||||||
init(
|
|
||||||
push: @escaping (ViewController) -> Void,
|
|
||||||
present: @escaping (
|
|
||||||
_ controller: ViewController,
|
|
||||||
_ in: PresentationContextType,
|
|
||||||
_ with: ViewControllerPresentationArguments?
|
|
||||||
) -> Void,
|
|
||||||
dismiss: @escaping (_ animated: Bool, _ completion: (() -> Void)?) -> Void
|
|
||||||
) {
|
|
||||||
self.push = push
|
|
||||||
self.present = present
|
|
||||||
self.dismiss = dismiss
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//
|
|
||||||
//public protocol SwiftUIView: View {
|
|
||||||
// var controllerInteraction: SwiftUIViewControllerInteraction? { get set }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//struct MySwiftUIView: SwiftUIView {
|
|
||||||
// var controllerInteraction: SwiftUIViewControllerInteraction?
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// var num: Int64
|
|
||||||
//
|
|
||||||
// var body: some View {
|
|
||||||
// Color.orange
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//struct CustomButtonStyle: ButtonStyle {
|
|
||||||
// func makeBody(configuration: Configuration) -> some View {
|
|
||||||
// configuration.label
|
|
||||||
// .padding()
|
|
||||||
// .background(Color.blue)
|
|
||||||
// .foregroundColor(.white)
|
|
||||||
// .cornerRadius(8)
|
|
||||||
// .frame(height: 44) // Set a fixed height for all buttons
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//private final class SwiftUIViewControllerNode<Content: SwiftUIView>: ASDisplayNode {
|
|
||||||
// private let hostingController: UIHostingController<Content>
|
|
||||||
// private var isDismissed = false
|
|
||||||
// private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
|
|
||||||
//
|
|
||||||
// init(swiftUIView: Content) {
|
|
||||||
// self.hostingController = UIHostingController(rootView: swiftUIView)
|
|
||||||
// super.init()
|
|
||||||
//
|
|
||||||
// // For debugging
|
|
||||||
// self.backgroundColor = .red.withAlphaComponent(0.3)
|
|
||||||
// hostingController.view.backgroundColor = .blue.withAlphaComponent(0.3)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override func didLoad() {
|
|
||||||
// super.didLoad()
|
|
||||||
//
|
|
||||||
// // Defer the setup to ensure we have a valid view controller hierarchy
|
|
||||||
// DispatchQueue.main.async { [weak self] in
|
|
||||||
// self?.setupHostingController()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private func setupHostingController() {
|
|
||||||
// guard let viewController = findViewController() else {
|
|
||||||
// assert(true, "Error: Could not find a parent view controller")
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// viewController.addChild(hostingController)
|
|
||||||
// view.addSubview(hostingController.view)
|
|
||||||
// hostingController.didMove(toParent: viewController)
|
|
||||||
//
|
|
||||||
// // Ensure the hosting controller's view has a size
|
|
||||||
// hostingController.view.frame = self.bounds
|
|
||||||
//
|
|
||||||
// print("SwiftUIViewControllerNode setup - Node frame: \(self.frame), Hosting view frame: \(hostingController.view.frame)")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private func findViewController() -> UIViewController? {
|
|
||||||
// var responder: UIResponder? = self.view
|
|
||||||
// while let nextResponder = responder?.next {
|
|
||||||
// if let viewController = nextResponder as? UIViewController {
|
|
||||||
// return viewController
|
|
||||||
// }
|
|
||||||
// responder = nextResponder
|
|
||||||
// }
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override func layout() {
|
|
||||||
// super.layout()
|
|
||||||
// hostingController.view.frame = self.bounds
|
|
||||||
// print("SwiftUIViewControllerNode layout - Node frame: \(self.frame), Hosting view frame: \(hostingController.view.frame)")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func containerLayoutUpdated(
|
|
||||||
// layout: ContainerViewLayout,
|
|
||||||
// navigationHeight: CGFloat,
|
|
||||||
// transition: ContainedViewLayoutTransition
|
|
||||||
// ) {
|
|
||||||
// if self.isDismissed {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// self.validLayout = (layout, navigationHeight)
|
|
||||||
//
|
|
||||||
// let frame = CGRect(
|
|
||||||
// origin: CGPoint(x: 0, y: 0),
|
|
||||||
// size: CGSize(
|
|
||||||
// width: layout.size.width,
|
|
||||||
// height: layout.size.height
|
|
||||||
// )
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// transition.updateFrame(node: self, frame: frame)
|
|
||||||
//
|
|
||||||
// print("containerLayoutUpdated - New frame: \(frame)")
|
|
||||||
//
|
|
||||||
// // Ensure hosting controller view is updated
|
|
||||||
// hostingController.view.frame = bounds
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func animateOut(completion: @escaping () -> Void) {
|
|
||||||
// guard let (layout, navigationHeight) = validLayout else {
|
|
||||||
// completion()
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// self.isDismissed = true
|
|
||||||
// let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring)
|
|
||||||
//
|
|
||||||
// let frame = CGRect(
|
|
||||||
// origin: CGPoint(x: 0, y: 0),
|
|
||||||
// size: CGSize(
|
|
||||||
// width: layout.size.width,
|
|
||||||
// height: layout.size.height
|
|
||||||
// )
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// transition.updateFrame(node: self, frame: frame, completion: { _ in
|
|
||||||
// completion()
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override func didEnterHierarchy() {
|
|
||||||
// super.didEnterHierarchy()
|
|
||||||
// print("SwiftUIViewControllerNode entered hierarchy")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override func didExitHierarchy() {
|
|
||||||
// super.didExitHierarchy()
|
|
||||||
// hostingController.willMove(toParent: nil)
|
|
||||||
// hostingController.view.removeFromSuperview()
|
|
||||||
// hostingController.removeFromParent()
|
|
||||||
// print("SwiftUIViewControllerNode exited hierarchy")
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//public final class SwiftUIViewController<Content: SwiftUIView>: ViewController {
|
|
||||||
// private var swiftUIView: Content
|
|
||||||
//
|
|
||||||
// public init(
|
|
||||||
// _ swiftUIView: Content,
|
|
||||||
// navigationBarTheme: NavigationBarTheme = NavigationBarTheme(
|
|
||||||
// buttonColor: ACCENT_COLOR,
|
|
||||||
// disabledButtonColor: .gray,
|
|
||||||
// primaryTextColor: .black,
|
|
||||||
// backgroundColor: .clear,
|
|
||||||
// enableBackgroundBlur: true,
|
|
||||||
// separatorColor: .gray,
|
|
||||||
// badgeBackgroundColor: THEME.navigationBar.badgeBackgroundColor,
|
|
||||||
// badgeStrokeColor: THEME.navigationBar.badgeStrokeColor,
|
|
||||||
// badgeTextColor: THEME.navigationBar.badgeTextColor
|
|
||||||
// ),
|
|
||||||
// navigationBarStrings: NavigationBarStrings = NavigationBarStrings(
|
|
||||||
// back: "Back",
|
|
||||||
// close: "Close"
|
|
||||||
// )
|
|
||||||
// ) {
|
|
||||||
// self.swiftUIView = swiftUIView
|
|
||||||
// super.init(navigationBarPresentationData: NavigationBarPresentationData(
|
|
||||||
// theme: navigationBarTheme,
|
|
||||||
// strings: navigationBarStrings
|
|
||||||
// ))
|
|
||||||
//
|
|
||||||
// self.swiftUIView.controllerInteraction = SwiftUIViewControllerInteraction(
|
|
||||||
// push: { [weak self] c in
|
|
||||||
// guard let strongSelf = self else { return }
|
|
||||||
// strongSelf.push(c)
|
|
||||||
// },
|
|
||||||
// present: { [weak self] c, context, args in
|
|
||||||
// guard let strongSelf = self else { return }
|
|
||||||
// strongSelf.present(c, in: context, with: args)
|
|
||||||
// },
|
|
||||||
// dismiss: { [weak self] animated, completion in
|
|
||||||
// guard let strongSelf = self else { return }
|
|
||||||
// strongSelf.dismiss(animated: animated, completion: completion)
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @available(*, unavailable)
|
|
||||||
// required init(coder _: NSCoder) {
|
|
||||||
// fatalError("init(coder:) has not been implemented")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override public func loadDisplayNode() {
|
|
||||||
// self.displayNode = SwiftUIViewControllerNode<Content>(swiftUIView: swiftUIView)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override public func containerLayoutUpdated(
|
|
||||||
// _ layout: ContainerViewLayout,
|
|
||||||
// transition: ContainedViewLayoutTransition
|
|
||||||
// ) {
|
|
||||||
// super.containerLayoutUpdated(layout, transition: transition)
|
|
||||||
//
|
|
||||||
// (self.displayNode as! SwiftUIViewControllerNode<Content>).containerLayoutUpdated(
|
|
||||||
// layout: layout,
|
|
||||||
// navigationHeight: navigationLayout(layout: layout).navigationFrame.maxY,
|
|
||||||
// transition: transition
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public func animateOut(completion: @escaping () -> Void) {
|
|
||||||
// (self.displayNode as! SwiftUIViewControllerNode<Content>)
|
|
||||||
// .animateOut(completion: completion)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//func mySwiftUIViewController(_ num: Int64) -> ViewController {
|
|
||||||
// let controller = SwiftUIViewController(MySwiftUIView(num: num))
|
|
||||||
// controller.title = "Controller: \(num)"
|
|
||||||
// return controller
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
public protocol SwiftUIView: View {
|
|
||||||
var controllerInteraction: SwiftUIViewControllerInteraction? { get set }
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MySwiftUIView: SwiftUIView {
|
|
||||||
var controllerInteraction: SwiftUIViewControllerInteraction?
|
|
||||||
|
|
||||||
var num: Int64
|
var num: Int64
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Color.orange
|
VStack {
|
||||||
// VStack {
|
Text("Hello, World!")
|
||||||
// Button("Push") {
|
.font(.title)
|
||||||
// self.controllerInteraction?.push(mySwiftUIViewController(num + 1))
|
.foregroundColor(.black)
|
||||||
// }
|
|
||||||
// Button("Modal") {
|
Spacer(minLength: 0)
|
||||||
// self.controllerInteraction?.present(mySwiftUIViewController(num + 1), .window(.root), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
|
||||||
// }
|
Button("Push") {
|
||||||
// Button("Dismiss") {
|
self.wrapperController?.push(mySwiftUIViewController(num + 1))
|
||||||
// self.controllerInteraction?.dismiss(true, {})
|
}.buttonStyle(AppleButtonStyle())
|
||||||
// }
|
Spacer()
|
||||||
// }
|
Button("Modal") {
|
||||||
|
self.wrapperController?.present(mySwiftUIViewController(num + 1), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
|
}.buttonStyle(AppleButtonStyle())
|
||||||
|
Spacer()
|
||||||
|
if num > 0 {
|
||||||
|
Button("Dismiss") {
|
||||||
|
self.wrapperController?.dismiss()
|
||||||
|
}.buttonStyle(AppleButtonStyle())
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// .frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.background(Color.green)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct AppleButtonStyle: ButtonStyle {
|
||||||
|
func makeBody(configuration: Configuration) -> some View {
|
||||||
|
configuration.label
|
||||||
|
.font(.headline)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.padding()
|
||||||
|
.frame(minWidth: 0, maxWidth: .infinity)
|
||||||
|
.background(Color.blue)
|
||||||
|
.cornerRadius(10)
|
||||||
|
.scaleEffect(configuration.isPressed ? 0.95 : 1)
|
||||||
|
.opacity(configuration.isPressed ? 0.9 : 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mySwiftUIViewController(_ num: Int64) -> ViewController {
|
func mySwiftUIViewController(_ num: Int64) -> ViewController {
|
||||||
let legacyController = LegacyController(presentation: .navigation, theme: defaultPresentationTheme, strings: defaultPresentationStrings)
|
let legacyController = LegacyController(presentation: .navigation, theme: defaultPresentationTheme, strings: defaultPresentationStrings)
|
||||||
legacyController.statusBar.statusBarStyle = defaultPresentationTheme.rootController.statusBarStyle.style
|
legacyController.statusBar.statusBarStyle = defaultPresentationTheme.rootController.statusBarStyle.style
|
||||||
legacyController.title = "Controller: root"
|
legacyController.title = "Controller: \(num)"
|
||||||
|
|
||||||
let controller = UIHostingController(rootView: MySwiftUIView(num: num))
|
let swiftUIView = MySwiftUIView(wrapperController: legacyController, num: num)
|
||||||
|
let controller = UIHostingController(rootView: swiftUIView)
|
||||||
legacyController.bind(controller: controller)
|
legacyController.bind(controller: controller)
|
||||||
legacyController.addChild(controller)
|
|
||||||
controller.didMove(toParent: legacyController)
|
|
||||||
|
|
||||||
|
|
||||||
return legacyController
|
return legacyController
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import Display
|
|||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import LegacyComponents
|
import LegacyComponents
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
public enum LegacyControllerPresentation {
|
public enum LegacyControllerPresentation {
|
||||||
case custom
|
case custom
|
||||||
@ -475,6 +476,10 @@ open class LegacyController: ViewController, PresentableController {
|
|||||||
self?.dismiss()
|
self?.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if self.legacyController.isHosting {
|
||||||
|
self.addChild(self.legacyController)
|
||||||
|
self.legacyController.didMove(toParent: legacyController)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override open func loadDisplayNode() {
|
override open func loadDisplayNode() {
|
||||||
@ -614,6 +619,11 @@ open class LegacyController: ViewController, PresentableController {
|
|||||||
if previousSizeClass != updatedSizeClass {
|
if previousSizeClass != updatedSizeClass {
|
||||||
self.sizeClass.set(SSignal.single(updatedSizeClass.rawValue as NSNumber))
|
self.sizeClass.set(SSignal.single(updatedSizeClass.rawValue as NSNumber))
|
||||||
}
|
}
|
||||||
|
if let sai = self.controllerNode.controllerView?.safeAreaInsets {
|
||||||
|
print("Safe area 1", sai)
|
||||||
|
}
|
||||||
|
print("Safe area 2", self.controllerNode.safeAreaInsets)
|
||||||
|
print("Safe area 3", self.view.safeAreaInsets)
|
||||||
}
|
}
|
||||||
|
|
||||||
override open func dismiss(completion: (() -> Void)? = nil) {
|
override open func dismiss(completion: (() -> Void)? = nil) {
|
||||||
@ -636,3 +646,24 @@ open class LegacyController: ViewController, PresentableController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension LegacyController {
|
||||||
|
|
||||||
|
// private func syncLegacyControllerSafeArea() {
|
||||||
|
// let recommendedSafeAreaInsets = self.contextImpl.safeAreaInset()
|
||||||
|
// let currentSafeAreaInsets = self.legacyController.view.safeAreaInsets
|
||||||
|
// let additionalInsets = UIEdgeInsets(top: recommendedSafeAreaInsets.top - currentSafeAreaInsets.top, left: recommendedSafeAreaInsets.left - currentSafeAreaInsets.left, bottom: recommendedSafeAreaInsets.bottom - currentSafeAreaInsets.bottom, right: recommendedSafeAreaInsets.left - currentSafeAreaInsets.right)
|
||||||
|
// self.legacyController.additionalSafeAreaInsets = additionalInsets
|
||||||
|
// self.legacyController.viewSafeAreaInsetsDidChange()
|
||||||
|
// self.legacyController.view.setNeedsLayout()
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Swiftgram
|
||||||
|
private protocol AnyUIHostingViewController: AnyObject {}
|
||||||
|
extension UIHostingController: AnyUIHostingViewController {}
|
||||||
|
|
||||||
|
extension UIViewController {
|
||||||
|
var isHosting: Bool { self is AnyUIHostingViewController }
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user