mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2026-04-12 16:01:33 +00:00
Independent Playground app for simulator
SGSwiftUI lib to embed views into Telegram's stack ObservableObject wip WIP Injecting SwiftUI via LegacyController New translations sglocalizable.strings (Chinese Traditional) (#57) wip Init SwiftUIViewController Update .swiftformat Move Playground to example Create .swiftformat Launch Playgound project on simulator Inject SwiftUI view with overflow to AsyncDisplayKit Playground UIKit base and controllers WIP: Bump version New translations sglocalizable.strings (Ukrainian) (#55)
This commit is contained in:
@@ -6,7 +6,7 @@ load(
|
||||
"xcodeproj",
|
||||
)
|
||||
load(
|
||||
"//Swiftgram/Playground:custom_bazel_path.bzl", "custom_bazel_path"
|
||||
"@build_configuration//:variables.bzl", "telegram_bazel_path"
|
||||
)
|
||||
|
||||
objc_library(
|
||||
@@ -24,6 +24,13 @@ swift_library(
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/LegacyUI:LegacyUI",
|
||||
"//submodules/LegacyComponents:LegacyComponents",
|
||||
"//submodules/MediaPlayer:UniversalMediaPlayer",
|
||||
"//Swiftgram/SGSwiftUI:SGSwiftUI",
|
||||
],
|
||||
data = [
|
||||
"//Telegram:GeneratedPresentationStrings/Resources/PresentationStrings.data",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
@@ -38,12 +45,15 @@ ios_application(
|
||||
infoplists = ["Resources/Info.plist"],
|
||||
minimum_os_version = "14.0",
|
||||
visibility = ["//visibility:public"],
|
||||
strings = [
|
||||
"//Telegram:AppStringResources",
|
||||
],
|
||||
launch_storyboard = "Resources/LaunchScreen.storyboard",
|
||||
deps = [":PlaygroundMain", ":PlaygroundLib"],
|
||||
)
|
||||
|
||||
xcodeproj(
|
||||
bazel_path = custom_bazel_path(),
|
||||
bazel_path = telegram_bazel_path,
|
||||
name = "Playground_xcodeproj",
|
||||
build_mode = "bazel",
|
||||
project_name = "Playground",
|
||||
|
||||
@@ -4,17 +4,7 @@ Small app to quickly iterate on components testing without building an entire me
|
||||
|
||||
## Generate Xcode project
|
||||
|
||||
### From root
|
||||
|
||||
```shell
|
||||
./Swiftgram/Playground/generate_project.py
|
||||
```
|
||||
|
||||
### From current directory
|
||||
|
||||
```shell
|
||||
./generate_project.py
|
||||
```
|
||||
Same as main project described in [../../Readme.md](../../Readme.md), but with `--target="Swiftgram/Playground"` parameter.
|
||||
|
||||
## Run generated project on simulator
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@ import UIKit
|
||||
import SwiftUI
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import LegacyUI
|
||||
|
||||
let SHOW_SAFE_AREA = false
|
||||
|
||||
@objc(AppDelegate)
|
||||
final class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
@@ -18,7 +20,6 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
hostView.containerView.backgroundColor = UIColor.white
|
||||
self.window = window
|
||||
|
||||
|
||||
let navigationController = NavigationController(
|
||||
mode: .single,
|
||||
theme: NavigationControllerTheme(
|
||||
@@ -30,7 +31,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()
|
||||
|
||||
|
||||
@@ -1,256 +1,85 @@
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import Foundation
|
||||
import LegacyUI
|
||||
import SGSwiftUI
|
||||
import SwiftUI
|
||||
import TelegramPresentationData
|
||||
import UIKit
|
||||
|
||||
public class SwiftUIViewControllerInteraction {
|
||||
let push: (ViewController) -> Void
|
||||
let present: (
|
||||
_ controller: ViewController,
|
||||
_ in: PresentationContextType,
|
||||
_ with: ViewControllerPresentationArguments?
|
||||
) -> Void
|
||||
let dismiss: (_ animated: Bool, _ completion: (() -> Void)?) -> Void
|
||||
struct MySwiftUIView: View {
|
||||
weak var wrapperController: LegacyController?
|
||||
|
||||
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 }
|
||||
var navigationHeight: CGFloat { get set }
|
||||
}
|
||||
|
||||
struct MySwiftUIView: SwiftUIView {
|
||||
var controllerInteraction: SwiftUIViewControllerInteraction?
|
||||
@Binding var navigationHeight: CGFloat
|
||||
|
||||
|
||||
var num: Int64
|
||||
|
||||
var body: some View {
|
||||
Color.orange
|
||||
.padding(.top, 2.0 * (_navigationHeight ?? 0))
|
||||
ScrollView {
|
||||
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()
|
||||
}
|
||||
ForEach(1..<20, id: \.self) { i in
|
||||
Button("TAP: \(i)") {
|
||||
print("Tapped \(i)")
|
||||
}.buttonStyle(AppleButtonStyle())
|
||||
}
|
||||
|
||||
}
|
||||
.background(Color.green)
|
||||
}
|
||||
}
|
||||
|
||||
struct CustomButtonStyle: ButtonStyle {
|
||||
struct AppleButtonStyle: ButtonStyle {
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.label
|
||||
.padding()
|
||||
.background(Color.blue)
|
||||
.font(.headline)
|
||||
.foregroundColor(.white)
|
||||
.cornerRadius(8)
|
||||
.frame(height: 44) // Set a fixed height for all buttons
|
||||
.padding()
|
||||
.frame(minWidth: 0, maxWidth: .infinity)
|
||||
.background(Color.blue)
|
||||
.cornerRadius(10)
|
||||
.scaleEffect(configuration.isPressed ? 0.95 : 1)
|
||||
.opacity(configuration.isPressed ? 0.9 : 1)
|
||||
}
|
||||
}
|
||||
|
||||
private final class SwiftUIViewControllerNode<Content: SwiftUIView>: ASDisplayNode {
|
||||
private let hostingController: UIHostingController<Content>
|
||||
private var isDismissed = false
|
||||
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
|
||||
public func mySwiftUIViewController(_ num: Int64) -> ViewController {
|
||||
let legacyController = LegacySwiftUIController(
|
||||
presentation: .modal(animateIn: true),
|
||||
theme: defaultPresentationTheme,
|
||||
strings: defaultPresentationStrings
|
||||
)
|
||||
legacyController.statusBar.statusBarStyle = defaultPresentationTheme.rootController
|
||||
.statusBarStyle.style
|
||||
legacyController.title = "Controller: \(num)"
|
||||
|
||||
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)
|
||||
}
|
||||
let swiftUIView = SGSwiftUIView<MySwiftUIView>(
|
||||
navigationBarHeight: legacyController.navigationBarHeightModel,
|
||||
containerViewLayout: legacyController.containerViewLayoutModel,
|
||||
content: { MySwiftUIView(wrapperController: legacyController, num: num) }
|
||||
)
|
||||
let controller = UIHostingController(rootView: swiftUIView, ignoreSafeArea: true)
|
||||
legacyController.bind(controller: controller)
|
||||
|
||||
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
|
||||
hostingController.rootView.navigationHeight = navigationHeight
|
||||
}
|
||||
|
||||
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()
|
||||
})
|
||||
hostingController.rootView.navigationHeight = navigationHeight
|
||||
}
|
||||
|
||||
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
|
||||
return legacyController
|
||||
}
|
||||
|
||||
20
Swiftgram/SGSwiftUI/BUILD
Normal file
20
Swiftgram/SGSwiftUI/BUILD
Normal file
@@ -0,0 +1,20 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "SGSwiftUI",
|
||||
module_name = "SGSwiftUI",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
# "-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/LegacyUI:LegacyUI",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData"
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
145
Swiftgram/SGSwiftUI/Sources/SGSwiftUI.swift
Normal file
145
Swiftgram/SGSwiftUI/Sources/SGSwiftUI.swift
Normal file
@@ -0,0 +1,145 @@
|
||||
import Display
|
||||
import Foundation
|
||||
import LegacyUI
|
||||
import SwiftUI
|
||||
import TelegramPresentationData
|
||||
|
||||
public class ObservedValue<T>: ObservableObject {
|
||||
@Published var value: T
|
||||
|
||||
init(_ value: T) {
|
||||
self.value = value
|
||||
}
|
||||
}
|
||||
|
||||
public struct SGSwiftUIView<Content: View>: View {
|
||||
let content: Content
|
||||
|
||||
@ObservedObject var navigationBarHeight: ObservedValue<CGFloat>
|
||||
@ObservedObject var containerViewLayout: ObservedValue<ContainerViewLayout?>
|
||||
|
||||
public init(
|
||||
navigationBarHeight: ObservedValue<CGFloat>,
|
||||
containerViewLayout: ObservedValue<ContainerViewLayout?>,
|
||||
@ViewBuilder content: () -> Content
|
||||
) {
|
||||
self.navigationBarHeight = navigationBarHeight
|
||||
self.containerViewLayout = containerViewLayout
|
||||
self.content = content()
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
content
|
||||
.modifier(CustomSafeAreaPadding(navigationBarHeight: navigationBarHeight, containerViewLayout: containerViewLayout))
|
||||
.background(Color.yellow)
|
||||
}
|
||||
}
|
||||
|
||||
public struct CustomSafeAreaPadding: ViewModifier {
|
||||
@ObservedObject var navigationBarHeight: ObservedValue<CGFloat>
|
||||
@ObservedObject var containerViewLayout: ObservedValue<ContainerViewLayout?>
|
||||
|
||||
public func body(content: Content) -> some View {
|
||||
content
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.padding(.top, totalTopSafeArea > navigationBarHeight.value ? totalTopSafeArea : navigationBarHeight.value)
|
||||
.padding(.bottom, (containerViewLayout.value?.safeInsets.bottom ?? 0) + (containerViewLayout.value?.intrinsicInsets.bottom ?? 0))
|
||||
.padding(.leading, containerViewLayout.value?.safeInsets.left ?? 0)
|
||||
.padding(.trailing, containerViewLayout.value?.safeInsets.right ?? 0)
|
||||
}
|
||||
|
||||
var totalTopSafeArea: CGFloat {
|
||||
(containerViewLayout.value?.safeInsets.top ?? 0) +
|
||||
(containerViewLayout.value?.intrinsicInsets.top ?? 0)
|
||||
}
|
||||
}
|
||||
|
||||
public final class LegacySwiftUIController: LegacyController {
|
||||
public var navigationBarHeightModel: ObservedValue<CGFloat>
|
||||
public var containerViewLayoutModel: ObservedValue<ContainerViewLayout?>
|
||||
|
||||
override public init(presentation: LegacyControllerPresentation, theme: PresentationTheme? = nil, strings: PresentationStrings? = nil, initialLayout: ContainerViewLayout? = nil) {
|
||||
navigationBarHeightModel = ObservedValue<CGFloat>(0.0)
|
||||
containerViewLayoutModel = ObservedValue<ContainerViewLayout?>(initialLayout)
|
||||
super.init(presentation: presentation, theme: theme, strings: strings, initialLayout: initialLayout)
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
let newNavigationBarHeight = navigationLayout(layout: layout).navigationFrame.maxY
|
||||
if navigationBarHeightModel.value != newNavigationBarHeight {
|
||||
navigationBarHeightModel.value = newNavigationBarHeight
|
||||
}
|
||||
if containerViewLayoutModel.value != layout {
|
||||
containerViewLayoutModel.value = layout
|
||||
}
|
||||
}
|
||||
|
||||
override public func bind(controller: UIViewController) {
|
||||
super.bind(controller: controller)
|
||||
addChild(legacyController)
|
||||
legacyController.didMove(toParent: legacyController)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
public required init(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
extension UIHostingController {
|
||||
public convenience init(rootView: Content, ignoreSafeArea: Bool) {
|
||||
self.init(rootView: rootView)
|
||||
|
||||
if ignoreSafeArea {
|
||||
disableSafeArea()
|
||||
}
|
||||
}
|
||||
|
||||
func disableSafeArea() {
|
||||
guard let viewClass = object_getClass(view) else {
|
||||
return
|
||||
}
|
||||
|
||||
func encodeText(string: String, key: Int16) -> String {
|
||||
let nsString = string as NSString
|
||||
let result = NSMutableString()
|
||||
for i in 0 ..< nsString.length {
|
||||
var c: unichar = nsString.character(at: i)
|
||||
c = unichar(Int16(c) + key)
|
||||
result.append(NSString(characters: &c, length: 1) as String)
|
||||
}
|
||||
return result as String
|
||||
}
|
||||
|
||||
let viewSubclassName = String(cString: class_getName(viewClass)).appending(encodeText(string: "`JhopsfTbgfBsfb", key: -1))
|
||||
|
||||
if let viewSubclass = NSClassFromString(viewSubclassName) {
|
||||
object_setClass(view, viewSubclass)
|
||||
} else {
|
||||
guard
|
||||
let viewClassNameUtf8 = (viewSubclassName as NSString).utf8String,
|
||||
let viewSubclass = objc_allocateClassPair(viewClass, viewClassNameUtf8, 0)
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
if let method = class_getInstanceMethod(UIView.self, #selector(getter: UIView.safeAreaInsets)) {
|
||||
let safeAreaInsets: @convention(block) (AnyObject) -> UIEdgeInsets = { _ in
|
||||
.zero
|
||||
}
|
||||
|
||||
class_addMethod(
|
||||
viewSubclass,
|
||||
#selector(getter: UIView.safeAreaInsets),
|
||||
imp_implementationWithBlock(safeAreaInsets),
|
||||
method_getTypeEncoding(method)
|
||||
)
|
||||
}
|
||||
|
||||
objc_registerClassPair(viewSubclass)
|
||||
object_setClass(view, viewSubclass)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,6 +138,10 @@ genrule(
|
||||
"GeneratedPresentationStrings/Sources/PresentationStrings.m",
|
||||
"GeneratedPresentationStrings/Resources/PresentationStrings.data",
|
||||
],
|
||||
# MARK: Swiftgram
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
|
||||
minimum_os_version = "12.0"
|
||||
@@ -253,7 +257,9 @@ filegroup(
|
||||
"//Swiftgram/SGStrings:SGLocalizableStrings",
|
||||
] + [
|
||||
"{}.lproj/Localizable.strings".format(language) for language in empty_languages
|
||||
]
|
||||
],
|
||||
# MARK: Swiftgram
|
||||
visibility = ["//visibility:public",],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
|
||||
@@ -34,6 +34,9 @@ def generate_xcodeproj(build_environment: BuildEnvironment, disable_extensions,
|
||||
project_bazel_arguments.append(argument)
|
||||
project_bazel_arguments += ['--override_repository=build_configuration={}'.format(configuration_path)]
|
||||
|
||||
if target_name == "Swiftgram/Playground":
|
||||
project_bazel_arguments += ["--swiftcopt=-no-warnings-as-errors", "--copt=-Wno-error"]#, "--swiftcopt=-DSWIFTGRAM_PLAYGROUND", "--copt=-DSWIFTGRAM_PLAYGROUND=1"]
|
||||
|
||||
if target_name == 'Telegram':
|
||||
if disable_extensions:
|
||||
project_bazel_arguments += ['--//{}:disableExtensions'.format(app_target)]
|
||||
@@ -51,6 +54,10 @@ def generate_xcodeproj(build_environment: BuildEnvironment, disable_extensions,
|
||||
call_executable(bazel_generate_arguments)
|
||||
|
||||
# MARK: Swiftgram
|
||||
if target_name == "Swiftgram/Playground":
|
||||
xcodeproj_path = 'Swiftgram/Playground/Playground.xcodeproj'
|
||||
call_executable(['open', xcodeproj_path])
|
||||
return
|
||||
xcodeproj_path = 'Telegram/Swiftgram.xcodeproj'
|
||||
call_executable(['open', xcodeproj_path])
|
||||
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
#import <LegacyComponents/TGVideoEditAdjustments.h>
|
||||
|
||||
// MARK: Swiftgram
|
||||
#import <VideoToolbox/VideoToolbox.h>
|
||||
#import <MediaPlayer/MediaPlayer.h>
|
||||
//
|
||||
|
||||
@interface TGMediaVideoFileWatcher : NSObject
|
||||
{
|
||||
NSURL *_fileURL;
|
||||
|
||||
@@ -468,7 +468,7 @@ open class LegacyController: ViewController, PresentableController {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func bind(controller: UIViewController) {
|
||||
open func bind(controller: UIViewController) {
|
||||
self.legacyController = controller
|
||||
if let controller = controller as? TGViewController {
|
||||
controller.customRemoveFromParentViewController = { [weak self] in
|
||||
|
||||
Reference in New Issue
Block a user