This commit is contained in:
Kylmakalle 2024-09-30 00:01:37 +03:00
parent f38e31dc22
commit aee63afc1a
3 changed files with 119 additions and 272 deletions

View File

@ -3,6 +3,7 @@ import SwiftUI
import AsyncDisplayKit
import Display
let SHOW_SAFE_AREA = false
@objc(AppDelegate)
final class AppDelegate: NSObject, UIApplicationDelegate {
@ -18,7 +19,6 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
hostView.containerView.backgroundColor = UIColor.white
self.window = window
let navigationController = NavigationController(
mode: .single,
theme: NavigationControllerTheme(
@ -30,7 +30,49 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
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()

View File

@ -6,288 +6,62 @@ import UIKit
import LegacyUI
import TelegramPresentationData
public class SwiftUIViewControllerInteraction {
let push: (ViewController) -> Void
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?
struct MySwiftUIView: View {
weak var wrapperController: LegacyController?
var num: Int64
var body: some View {
Color.orange
// VStack {
// Button("Push") {
// self.controllerInteraction?.push(mySwiftUIViewController(num + 1))
// }
// Button("Modal") {
// self.controllerInteraction?.present(mySwiftUIViewController(num + 1), .window(.root), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
// }
// Button("Dismiss") {
// self.controllerInteraction?.dismiss(true, {})
// }
// }
VStack {
Text("Hello, World!")
.font(.title)
.foregroundColor(.black)
Spacer(minLength: 0)
Button("Push") {
self.wrapperController?.push(mySwiftUIViewController(num + 1))
}.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 {
let legacyController = LegacyController(presentation: .navigation, theme: defaultPresentationTheme, strings: defaultPresentationStrings)
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.addChild(controller)
controller.didMove(toParent: legacyController)
return legacyController
}

View File

@ -4,6 +4,7 @@ import Display
import SwiftSignalKit
import LegacyComponents
import TelegramPresentationData
import SwiftUI
public enum LegacyControllerPresentation {
case custom
@ -475,6 +476,10 @@ open class LegacyController: ViewController, PresentableController {
self?.dismiss()
}
}
if self.legacyController.isHosting {
self.addChild(self.legacyController)
self.legacyController.didMove(toParent: legacyController)
}
}
override open func loadDisplayNode() {
@ -614,6 +619,11 @@ open class LegacyController: ViewController, PresentableController {
if previousSizeClass != updatedSizeClass {
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) {
@ -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 }
}