Upgraded navigation

This commit is contained in:
Peter 2019-09-14 00:13:50 +04:00
parent 99c6240172
commit 538e3dbe70
57 changed files with 1799 additions and 1909 deletions

View File

@ -432,7 +432,7 @@ public protocol SharedAccountContext: class {
func resolveUrl(account: Account, url: String) -> Signal<ResolvedUrl, NoError>
func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void)
func openAddContact(context: AccountContext, firstName: String, lastName: String, phoneNumber: String, label: String, present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, completed: @escaping () -> Void)
func openAddPersonContact(context: AccountContext, peerId: PeerId, present: @escaping (ViewController, Any?) -> Void)
func openAddPersonContact(context: AccountContext, peerId: PeerId, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void)
func presentContactsWarningSuppression(context: AccountContext, present: (ViewController, Any?) -> Void)
func navigateToCurrentCall()

View File

@ -194,6 +194,7 @@ public final class CallListController: ViewController {
@objc func callPressed() {
let controller = self.context.sharedContext.makeContactSelectionController(ContactSelectionControllerParams(context: self.context, title: { $0.Calls_NewCall }))
controller.navigationPresentation = .modal
self.createActionDisposable.set((controller.result
|> take(1)
|> deliverOnMainQueue).start(next: { [weak controller, weak self] peer in

View File

@ -84,9 +84,13 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, source: ChatC
if !isSavedMessages, let peer = peer as? TelegramUser {
if !transaction.isPeerContact(peerId: peer.id) {
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_AddToContacts, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor) }, action: { _, f in
context.sharedContext.openAddPersonContact(context: context, peerId: peerId, present: { controller, arguments in
context.sharedContext.openAddPersonContact(context: context, peerId: peerId, pushController: { controller in
if let navigationController = chatListController?.navigationController as? NavigationController {
navigationController.pushViewController(controller)
}
}, present: { c, a in
if let chatListController = chatListController {
chatListController.present(controller, in: .window(.root), with: arguments)
chatListController.present(c, in: .window(.root), with: a)
}
})
f(.default)
@ -224,6 +228,10 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, source: ChatC
})))
}
if let item = items.last, case .separator = item {
items.removeLast()
}
return items
}
}

View File

@ -1020,8 +1020,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
@objc private func composePressed() {
let controller = self.context.sharedContext.makeComposeController(context: self.context)
self.present(controller, in: .window(.root))
//(self.navigationController as? NavigationController)?.replaceAllButRootController(self.context.sharedContext.makeComposeController(context: self.context), animated: true)
(self.navigationController as? NavigationController)?.pushViewController(controller)
}
public func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {

View File

@ -387,6 +387,7 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple
}
let controller = ItemListController(context: context, state: signal)
controller.navigationPresentation = .modal
presentControllerImpl = { [weak controller] c, a in
controller?.present(c, in: .window(.root), with: a)
}

View File

@ -523,7 +523,7 @@ public class ContactsController: ViewController {
switch status {
case .allowed:
let contactData = DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: "", lastName: "", phoneNumbers: [DeviceContactPhoneNumberData(label: "_$!<Mobile>!$_", value: "+")]), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [])
strongSelf.present(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
(strongSelf.navigationController as? NavigationController)?.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .create(peer: nil, contactData: contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
guard let strongSelf = self else {
return
}
@ -534,7 +534,7 @@ public class ContactsController: ViewController {
} else {
(strongSelf.navigationController as? NavigationController)?.pushViewController(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .vcard(nil, stableId, contactData), completed: nil, cancelled: nil))
}
}), completed: nil, cancelled: nil), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}), completed: nil, cancelled: nil))
case .notDetermined:
DeviceAccess.authorizeAccess(to: .contacts)
default:

View File

@ -38,6 +38,8 @@ public class InviteContactsController: ViewController, MFMessageComposeViewContr
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
self.navigationPresentation = .modal
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
self.title = self.presentationData.strings.Contacts_InviteFriends

View File

@ -575,6 +575,7 @@ public extension ContainedViewLayoutTransition {
func updateSublayerTransformScale(node: ASDisplayNode, scale: CGFloat, completion: ((Bool) -> Void)? = nil) {
if !node.isNodeLoaded {
node.subnodeTransform = CATransform3DMakeScale(scale, scale, 1.0)
completion?(true)
return
}
let t = node.layer.sublayerTransform
@ -606,12 +607,13 @@ public extension ContainedViewLayoutTransition {
func updateSublayerTransformScaleAndOffset(node: ASDisplayNode, scale: CGFloat, offset: CGPoint, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
if !node.isNodeLoaded {
node.subnodeTransform = CATransform3DMakeScale(scale, scale, 1.0)
completion?(true)
return
}
let t = node.layer.sublayerTransform
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
let currentOffset = CGPoint(x: t.m41, y: t.m42)
if currentScale.isEqual(to: scale) && currentOffset == offset {
let currentOffset = CGPoint(x: t.m41 / currentScale, y: t.m42 / currentScale)
if abs(currentScale - scale) <= CGFloat.ulpOfOne && abs(currentOffset.x - offset.x) <= CGFloat.ulpOfOne && abs(currentOffset.y - offset.y) <= CGFloat.ulpOfOne {
if let completion = completion {
completion(true)
}
@ -647,6 +649,7 @@ public extension ContainedViewLayoutTransition {
func updateSublayerTransformScale(node: ASDisplayNode, scale: CGPoint, completion: ((Bool) -> Void)? = nil) {
if !node.isNodeLoaded {
node.subnodeTransform = CATransform3DMakeScale(scale.x, scale.y, 1.0)
completion?(true)
return
}
let t = node.layer.sublayerTransform
@ -713,6 +716,7 @@ public extension ContainedViewLayoutTransition {
func updateTransformScale(node: ASDisplayNode, scale: CGPoint, completion: ((Bool) -> Void)? = nil) {
if !node.isNodeLoaded {
node.subnodeTransform = CATransform3DMakeScale(scale.x, scale.y, 1.0)
completion?(true)
return
}
let t = node.layer.transform

View File

@ -759,7 +759,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
}
}
private func lowestSectionNode() -> ASDisplayNode? {
public func lowestSectionNode() -> ASDisplayNode? {
var lowestHeaderNode: ASDisplayNode?
var lowestHeaderNodeIndex: Int?
for (_, headerNode) in self.sectionNodes {

View File

@ -0,0 +1,377 @@
import Foundation
import UIKit
import AsyncDisplayKit
import SwiftSignalKit
final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
private final class Child {
let value: ViewController
var layout: ContainerViewLayout
init(value: ViewController, layout: ContainerViewLayout) {
self.value = value
self.layout = layout
}
}
private final class PendingChild {
enum TransitionType {
case push
case pop
}
let value: Child
let transitionType: TransitionType
let transition: ContainedViewLayoutTransition
let disposable: MetaDisposable = MetaDisposable()
var isReady: Bool = false
init(value: Child, transitionType: TransitionType, transition: ContainedViewLayoutTransition, update: @escaping (PendingChild) -> Void) {
self.value = value
self.transitionType = transitionType
self.transition = transition
var localIsReady: Bool?
self.disposable.set((value.value.ready.get()
|> filter { $0 }
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] _ in
if localIsReady == nil {
localIsReady = true
} else if let strongSelf = self {
update(strongSelf)
}
}))
if let localIsReady = localIsReady {
self.isReady = true
} else {
localIsReady = false
}
}
deinit {
self.disposable.dispose()
}
}
private final class TopTransition {
let previous: Child
let coordinator: NavigationTransitionCoordinator
init(previous: Child, coordinator: NavigationTransitionCoordinator) {
self.previous = previous
self.coordinator = coordinator
}
}
private struct State {
var layout: ContainerViewLayout?
var canBeClosed: Bool?
var top: Child?
var transition: TopTransition?
var pending: PendingChild?
}
private(set) var controllers: [ViewController] = []
private var state: State = State(layout: nil, canBeClosed: nil, top: nil, transition: nil, pending: nil)
private(set) var isReady: Bool = false
var isReadyUpdated: (() -> Void)?
var controllerRemoved: (ViewController) -> Void
var keyboardManager: KeyboardManager? {
didSet {
if self.keyboardManager !== oldValue {
self.keyboardManager?.surfaces = self.state.top?.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? []
}
}
}
init(controllerRemoved: @escaping (ViewController) -> Void) {
self.controllerRemoved = controllerRemoved
super.init()
}
override func didLoad() {
super.didLoad()
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
panRecognizer.delegate = self
panRecognizer.delaysTouchesBegan = false
panRecognizer.cancelsTouchesInView = true
self.view.addGestureRecognizer(panRecognizer)
}
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if let _ = otherGestureRecognizer as? UIPanGestureRecognizer {
return true
}
return false
}
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
guard let layout = self.state.layout else {
return
}
guard self.state.transition == nil else {
return
}
let beginGesture = self.controllers.count > 1
if beginGesture {
let topController = self.controllers[self.controllers.count - 1]
let bottomController = self.controllers[self.controllers.count - 2]
if let topController = topController as? ViewController {
if !topController.attemptNavigation({ [weak self] in
//let _ = self?.popViewController(animated: true)
}) {
return
}
}
topController.viewWillDisappear(true)
let topView = topController.view!
bottomController.containerLayoutUpdated(layout, transition: .immediate)
bottomController.viewWillAppear(true)
let bottomView = bottomController.view!
let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self.view, topView: topView, topNavigationBar: topController.navigationBar, bottomView: bottomView, bottomNavigationBar: bottomController.navigationBar, didUpdateProgress: { [weak self] progress, transition in
if let strongSelf = self {
strongSelf.keyboardManager?.surfaces = strongSelf.state.top?.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? []
/*for i in 0 ..< strongSelf._viewControllers.count {
if let controller = strongSelf._viewControllers[i].controller as? ViewController {
if i < strongSelf._viewControllers.count - 1 {
controller.updateNavigationCustomData((strongSelf.viewControllers[i + 1] as? ViewController)?.customData, progress: 1.0 - progress, transition: transition)
} else {
controller.updateNavigationCustomData(nil, progress: 1.0 - progress, transition: transition)
}
}
}*/
}
})
bottomController.displayNode.recursivelyEnsureDisplaySynchronously(true)
self.state.transition = TopTransition(previous: Child(value: bottomController, layout: layout), coordinator: navigationTransitionCoordinator)
}
case .changed:
if let navigationTransitionCoordinator = self.state.transition?.coordinator, !navigationTransitionCoordinator.animatingCompletion {
let translation = recognizer.translation(in: self.view).x
let progress = max(0.0, min(1.0, translation / self.view.frame.width))
navigationTransitionCoordinator.progress = progress
}
case .ended, .cancelled:
if let navigationTransitionCoordinator = self.state.transition?.coordinator, !navigationTransitionCoordinator.animatingCompletion {
let velocity = recognizer.velocity(in: self.view).x
if velocity > 1000 || navigationTransitionCoordinator.progress > 0.2 {
//(self.view as! NavigationControllerView).inTransition = true
navigationTransitionCoordinator.animateCompletion(velocity, completion: { [weak self] in
guard let strongSelf = self, let layout = strongSelf.state.layout, let transition = strongSelf.state.transition, let top = strongSelf.state.top else {
return
}
//(self.view as! NavigationControllerView).inTransition = false
let topController = top.value
let bottomController = transition.previous.value
strongSelf.keyboardManager?.updateInteractiveInputOffset(layout.size.height, transition: .immediate, completion: {})
topController.view.endEditing(true)
strongSelf.state.transition = nil
strongSelf.controllerRemoved(top.value)
//topController.viewDidDisappear(true)
//bottomController.viewDidAppear(true)
})
} else {
/*if self.viewControllers.count >= 2 {
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
topController.viewWillAppear(true)
bottomController.viewWillDisappear(true)
}*/
//(self.view as! NavigationControllerView).inTransition = true
navigationTransitionCoordinator.animateCancel({ [weak self] in
guard let strongSelf = self, let top = strongSelf.state.top, let transition = strongSelf.state.transition else {
return
}
//(self.view as! NavigationControllerView).inTransition = false
strongSelf.state.transition = nil
top.value.viewDidAppear(true)
transition.previous.value.viewDidDisappear(true)
})
}
}
default:
break
}
}
func update(layout: ContainerViewLayout, canBeClosed: Bool, controllers: [ViewController], transition: ContainedViewLayoutTransition) {
let previousControllers = self.controllers
self.controllers = controllers
for i in 0 ..< controllers.count {
if i == 0 {
if canBeClosed {
controllers[i].navigationBar?.previousItem = .close
} else {
controllers[i].navigationBar?.previousItem = nil
}
} else {
controllers[i].navigationBar?.previousItem = .item(controllers[i - 1].navigationItem)
}
}
self.state.layout = layout
self.state.canBeClosed = canBeClosed
if controllers.last !== self.state.top?.value {
if controllers.last !== self.state.pending?.value.value {
self.state.pending = nil
if let last = controllers.last {
let transitionType: PendingChild.TransitionType
if !previousControllers.contains(where: { $0 === last }) {
transitionType = .push
} else {
transitionType = .pop
}
self.state.pending = PendingChild(value: self.makeChild(layout: layout, value: last), transitionType: transitionType, transition: transition, update: { [weak self] pendingChild in
self?.pendingChildIsReady(pendingChild)
})
}
}
}
if let pending = self.state.pending {
if pending.isReady {
self.state.pending = nil
let previous = self.state.top
self.state.top = pending.value
self.topTransition(from: previous, to: pending.value, transitionType: pending.transitionType, layout: layout, transition: pending.transition)
if !self.isReady {
self.isReady = true
self.isReadyUpdated?()
}
}
}
if controllers.isEmpty && self.state.top != nil {
let previous = self.state.top
self.state.top = nil
self.topTransition(from: previous, to: nil, transitionType: .pop, layout: layout, transition: .immediate)
}
if let top = self.state.top {
self.applyLayout(layout: layout, to: top, transition: transition)
}
if self.state.transition == nil {
self.keyboardManager?.surfaces = self.state.top?.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? []
}
}
private func topTransition(from fromValue: Child?, to toValue: Child?, transitionType: PendingChild.TransitionType, layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
if case .animated = transition, let fromValue = fromValue, let toValue = toValue {
self.keyboardManager?.surfaces = fromValue.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? []
if let currentTransition = self.state.transition {
assertionFailure()
}
fromValue.value.viewWillDisappear(true)
toValue.value.viewWillAppear(true)
toValue.value.setIgnoreAppearanceMethodInvocations(true)
if let layout = self.state.layout {
toValue.value.displayNode.frame = CGRect(origin: CGPoint(), size: layout.size)
}
let mappedTransitionType: NavigationTransition
let topController: ViewController
let bottomController: ViewController
switch transitionType {
case .push:
mappedTransitionType = .Push
self.addSubnode(toValue.value.displayNode)
topController = toValue.value
bottomController = fromValue.value
case .pop:
mappedTransitionType = .Pop
self.insertSubnode(toValue.value.displayNode, belowSubnode: fromValue.value.displayNode)
topController = fromValue.value
bottomController = toValue.value
}
toValue.value.setIgnoreAppearanceMethodInvocations(false)
let topTransition = TopTransition(previous: fromValue, coordinator: NavigationTransitionCoordinator(transition: mappedTransitionType, container: self.view, topView: topController.view, topNavigationBar: topController.navigationBar, bottomView: bottomController.view, bottomNavigationBar: bottomController.navigationBar, didUpdateProgress: nil))
self.state.transition = topTransition
topTransition.coordinator.animateCompletion(0.0, completion: { [weak self, weak topTransition] in
guard let strongSelf = self, let topTransition = topTransition, strongSelf.state.transition === topTransition else {
return
}
strongSelf.state.transition = nil
topTransition.previous.value.setIgnoreAppearanceMethodInvocations(true)
topTransition.previous.value.displayNode.removeFromSupernode()
topTransition.previous.value.setIgnoreAppearanceMethodInvocations(false)
topTransition.previous.value.viewDidDisappear(true)
if let toValue = strongSelf.state.top, let layout = strongSelf.state.layout {
toValue.value.displayNode.frame = CGRect(origin: CGPoint(), size: layout.size)
strongSelf.applyLayout(layout: layout, to: toValue, transition: .immediate)
toValue.value.viewDidAppear(true)
strongSelf.keyboardManager?.surfaces = toValue.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? []
}
})
} else {
self.keyboardManager?.surfaces = toValue?.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? []
if let fromValue = fromValue {
fromValue.value.viewWillDisappear(false)
fromValue.value.setIgnoreAppearanceMethodInvocations(true)
fromValue.value.displayNode.removeFromSupernode()
fromValue.value.setIgnoreAppearanceMethodInvocations(false)
fromValue.value.viewDidDisappear(false)
}
if let toValue = toValue {
self.applyLayout(layout: layout, to: toValue, transition: .immediate)
toValue.value.displayNode.frame = CGRect(origin: CGPoint(), size: layout.size)
toValue.value.viewWillAppear(false)
toValue.value.setIgnoreAppearanceMethodInvocations(true)
self.addSubnode(toValue.value.displayNode)
toValue.value.setIgnoreAppearanceMethodInvocations(false)
toValue.value.viewDidAppear(false)
}
}
}
private func makeChild(layout: ContainerViewLayout, value: ViewController) -> Child {
value.containerLayoutUpdated(layout, transition: .immediate)
return Child(value: value, layout: layout)
}
private func applyLayout(layout: ContainerViewLayout, to child: Child, transition: ContainedViewLayoutTransition) {
if child.layout != layout {
child.layout = layout
child.value.containerLayoutUpdated(layout, transition: transition)
}
}
private func pendingChildIsReady(_ child: PendingChild) {
if let pending = self.state.pending, pending === child {
pending.isReady = true
self.performUpdate()
}
}
private func performUpdate() {
if let layout = self.state.layout, let canBeClosed = self.state.canBeClosed {
self.update(layout: layout, canBeClosed: canBeClosed, controllers: self.controllers, transition: .immediate)
}
}
}

View File

@ -0,0 +1,793 @@
import Foundation
import UIKit
import AsyncDisplayKit
import SwiftSignalKit
public enum NavigationStatusBarStyle {
case black
case white
}
public final class NavigationControllerTheme {
public let statusBar: NavigationStatusBarStyle
public let navigationBar: NavigationBarTheme
public let emptyAreaColor: UIColor
public init(statusBar: NavigationStatusBarStyle, navigationBar: NavigationBarTheme, emptyAreaColor: UIColor) {
self.statusBar = statusBar
self.navigationBar = navigationBar
self.emptyAreaColor = emptyAreaColor
}
}
public struct NavigationAnimationOptions : OptionSet {
public let rawValue: Int
public init(rawValue: Int) {
self.rawValue = rawValue
}
public init() {
self.rawValue = 0
}
public static let removeOnMasterDetails = NavigationAnimationOptions(rawValue: 1 << 0)
}
private final class NavigationControllerContainerView: UIView {
override class var layerClass: AnyClass {
return CATracingLayer.self
}
}
public enum NavigationEmptyDetailsBackgoundMode {
case image(UIImage)
case wallpaper(UIImage)
}
private final class NavigationControllerView: UITracingLayerView {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override class var layerClass: AnyClass {
return CATracingLayer.self
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
return super.hitTest(point, with: event)
}
}
private enum ControllerTransition {
case none
case appearance
}
private final class ControllerRecord {
let controller: UIViewController
var transition: ControllerTransition = .none
init(controller: UIViewController) {
self.controller = controller
}
}
public enum NavigationControllerMode {
case single
case automaticMasterDetail
}
public enum MasterDetailLayoutBlackout : Equatable {
case master
case details
}
private enum RootContainer {
case flat(NavigationContainer)
case split(NavigationSplitContainer)
}
open class NavigationController: UINavigationController, ContainableController, UIGestureRecognizerDelegate {
public var isOpaqueWhenInOverlay: Bool = true
public var blocksBackgroundWhenInOverlay: Bool = true
public var isModalWhenInOverlay: Bool = false
public var updateTransitionWhenPresentedAsModal: ((CGFloat, ContainedViewLayoutTransition) -> Void)?
private let _ready = Promise<Bool>(true)
open var ready: Promise<Bool> {
return self._ready
}
private var masterDetailsBlackout: MasterDetailLayoutBlackout?
private var backgroundDetailsMode: NavigationEmptyDetailsBackgoundMode?
public var lockOrientation: Bool = false
public var deferScreenEdgeGestures: UIRectEdge = UIRectEdge()
private let mode: NavigationControllerMode
private var theme: NavigationControllerTheme
public private(set) weak var overlayPresentingController: ViewController?
private var controllerView: NavigationControllerView {
return self.view as! NavigationControllerView
}
private var rootContainer: RootContainer?
private var rootModalFrame: NavigationModalFrame?
private var modalContainers: [NavigationModalContainer] = []
private var validLayout: ContainerViewLayout?
private var validStatusBarStyle: NavigationStatusBarStyle?
private var scheduledLayoutTransitionRequestId: Int = 0
private var scheduledLayoutTransitionRequest: (Int, ContainedViewLayoutTransition)?
private var _presentedViewController: UIViewController?
open override var presentedViewController: UIViewController? {
return self._presentedViewController
}
private var _viewControllers: [ViewController] = []
override open var viewControllers: [UIViewController] {
get {
return self._viewControllers.map { $0 as! ViewController }
} set(value) {
self.setViewControllers(value, animated: false)
}
}
override open var topViewController: UIViewController? {
return self._viewControllers.last
}
private var _displayNode: ASDisplayNode?
public var displayNode: ASDisplayNode {
return self._displayNode!
}
var statusBarHost: StatusBarHost?
var keyboardManager: KeyboardManager?
public func updateMasterDetailsBlackout(_ blackout: MasterDetailLayoutBlackout?, transition: ContainedViewLayoutTransition) {
self.masterDetailsBlackout = blackout
if isViewLoaded {
self.view.endEditing(true)
}
self.requestLayout(transition: transition)
}
public func updateBackgroundDetailsMode(_ mode: NavigationEmptyDetailsBackgoundMode?, transition: ContainedViewLayoutTransition) {
self.backgroundDetailsMode = mode
self.requestLayout(transition: transition)
}
public init(mode: NavigationControllerMode, theme: NavigationControllerTheme, backgroundDetailsMode: NavigationEmptyDetailsBackgoundMode? = nil) {
self.mode = mode
self.theme = theme
self.backgroundDetailsMode = backgroundDetailsMode
super.init(nibName: nil, bundle: nil)
}
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
preconditionFailure()
}
public required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
}
public func combinedSupportedOrientations(currentOrientationToLock: UIInterfaceOrientationMask) -> ViewControllerSupportedOrientations {
var supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .allButUpsideDown)
if let controller = self.viewControllers.last {
if let controller = controller as? ViewController {
if controller.lockOrientation {
if let lockedOrientation = controller.lockedOrientation {
supportedOrientations = supportedOrientations.intersection(ViewControllerSupportedOrientations(regularSize: lockedOrientation, compactSize: lockedOrientation))
} else {
supportedOrientations = supportedOrientations.intersection(ViewControllerSupportedOrientations(regularSize: currentOrientationToLock, compactSize: currentOrientationToLock))
}
} else {
supportedOrientations = supportedOrientations.intersection(controller.supportedOrientations)
}
}
}
return supportedOrientations
}
public func updateTheme(_ theme: NavigationControllerTheme) {
let statusBarStyleUpdated = self.theme.statusBar != theme.statusBar
self.theme = theme
if self.isViewLoaded {
if statusBarStyleUpdated {
self.validStatusBarStyle = self.theme.statusBar
let normalStatusBarStyle: UIStatusBarStyle
switch self.theme.statusBar {
case .black:
normalStatusBarStyle = .default
case .white:
normalStatusBarStyle = .lightContent
}
self.statusBarHost?.setStatusBarStyle(normalStatusBarStyle, animated: false)
}
self.controllerView.backgroundColor = theme.emptyAreaColor
}
}
open func preferredContentSizeForLayout(_ layout: ContainerViewLayout) -> CGSize? {
return nil
}
public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
if !self.isViewLoaded {
self.loadView()
}
self.validLayout = layout
transition.updateFrame(view: self.view, frame: CGRect(origin: self.view.frame.origin, size: layout.size))
self.updateContainers(layout: layout, transition: transition)
}
private func updateContainers(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
let navigationLayout = makeNavigationLayout(layout: layout, controllers: self._viewControllers)
var transition = transition
var modalContainers: [NavigationModalContainer] = []
for i in 0 ..< navigationLayout.modal.count {
var existingModalContainer: NavigationModalContainer?
loop: for currentModalContainer in self.modalContainers {
for controller in navigationLayout.modal[i].controllers {
if currentModalContainer.container.controllers.contains(where: { $0 === controller }) {
existingModalContainer = currentModalContainer
break loop
}
}
}
let modalContainer: NavigationModalContainer
let containerTransition: ContainedViewLayoutTransition
if let existingModalContainer = existingModalContainer {
modalContainer = existingModalContainer
containerTransition = transition
} else {
modalContainer = NavigationModalContainer(theme: self.theme, controllerRemoved: { [weak self] controller in
self?.controllerRemoved(controller)
})
self.modalContainers.append(modalContainer)
if !modalContainer.isReady {
modalContainer.isReadyUpdated = { [weak self, weak modalContainer] in
guard let strongSelf = self, let modalContainer = modalContainer else {
return
}
if let layout = strongSelf.validLayout {
strongSelf.updateContainers(layout: layout, transition: .animated(duration: 0.5, curve: .spring))
}
}
}
modalContainer.updateDismissProgress = { [weak self, weak modalContainer] _ in
guard let strongSelf = self, let modalContainer = modalContainer else {
return
}
if let layout = strongSelf.validLayout {
strongSelf.updateContainers(layout: layout, transition: .immediate)
}
}
modalContainer.interactivelyDismissed = { [weak self, weak modalContainer] in
guard let strongSelf = self, let modalContainer = modalContainer else {
return
}
let controllers = strongSelf._viewControllers.filter { controller in
return !modalContainer.container.controllers.contains(where: { $0 === controller })
}
strongSelf.setViewControllers(controllers, animated: false)
}
containerTransition = .immediate
}
containerTransition.updateFrame(node: modalContainer, frame: CGRect(origin: CGPoint(), size: layout.size))
modalContainer.update(layout: layout, controllers: navigationLayout.modal[i].controllers, transition: containerTransition)
modalContainers.append(modalContainer)
}
for container in self.modalContainers {
if !modalContainers.contains(where: { $0 === container }) {
transition = container.dismiss(transition: transition, completion: { [weak container] in
container?.removeFromSupernode()
})
}
}
self.modalContainers = modalContainers
var previousModalContainer: NavigationModalContainer?
var visibleModalCount = 0
var topModalDismissProgress: CGFloat = 0.0
for i in (0 ..< navigationLayout.modal.count).reversed() {
let modalContainer = self.modalContainers[i]
if modalContainer.supernode == nil && modalContainer.isReady {
if let previousModalContainer = previousModalContainer {
self.displayNode.insertSubnode(modalContainer, belowSubnode: previousModalContainer)
} else {
self.displayNode.addSubnode(modalContainer)
}
modalContainer.animateIn(transition: transition)
}
if modalContainer.supernode != nil {
visibleModalCount += 1
if previousModalContainer == nil {
topModalDismissProgress = modalContainer.dismissProgress
modalContainer.container.keyboardManager = self.keyboardManager
} else {
modalContainer.container.keyboardManager = nil
}
previousModalContainer = modalContainer
}
}
switch navigationLayout.root {
case let .flat(controllers):
if let rootContainer = self.rootContainer {
switch rootContainer {
case let .flat(flatContainer):
if previousModalContainer == nil {
flatContainer.keyboardManager = self.keyboardManager
} else {
flatContainer.keyboardManager = nil
}
transition.updateFrame(node: flatContainer, frame: CGRect(origin: CGPoint(), size: layout.size))
flatContainer.update(layout: layout, canBeClosed: false, controllers: controllers, transition: transition)
case let .split(splitContainer):
let flatContainer = NavigationContainer(controllerRemoved: { [weak self] controller in
self?.controllerRemoved(controller)
})
self.displayNode.insertSubnode(flatContainer, at: 0)
self.rootContainer = .flat(flatContainer)
flatContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
flatContainer.update(layout: layout, canBeClosed: false, controllers: controllers, transition: .immediate)
splitContainer.removeFromSupernode()
}
} else {
let flatContainer = NavigationContainer(controllerRemoved: { [weak self] controller in
self?.controllerRemoved(controller)
})
self.displayNode.insertSubnode(flatContainer, at: 0)
self.rootContainer = .flat(flatContainer)
flatContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
flatContainer.update(layout: layout, canBeClosed: false, controllers: controllers, transition: .immediate)
}
case let .split(masterControllers, detailControllers):
if let rootContainer = self.rootContainer {
switch rootContainer {
case let .flat(flatContainer):
let splitContainer = NavigationSplitContainer(theme: self.theme, controllerRemoved: { [weak self] controller in
self?.controllerRemoved(controller)
})
self.displayNode.insertSubnode(splitContainer, at: 0)
self.rootContainer = .split(splitContainer)
splitContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
splitContainer.update(layout: layout, masterControllers: masterControllers, detailControllers: detailControllers, transition: .immediate)
flatContainer.removeFromSupernode()
case let .split(splitContainer):
transition.updateFrame(node: splitContainer, frame: CGRect(origin: CGPoint(), size: layout.size))
splitContainer.update(layout: layout, masterControllers: masterControllers, detailControllers: detailControllers, transition: transition)
}
} else {
let splitContainer = NavigationSplitContainer(theme: self.theme, controllerRemoved: { [weak self] controller in
self?.controllerRemoved(controller)
})
self.displayNode.insertSubnode(splitContainer, at: 0)
self.rootContainer = .split(splitContainer)
splitContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
splitContainer.update(layout: layout, masterControllers: masterControllers, detailControllers: detailControllers, transition: .immediate)
}
}
switch layout.metrics.widthClass {
case .compact:
if visibleModalCount != 0 {
let rootModalFrame: NavigationModalFrame
var modalFrameTransition: ContainedViewLayoutTransition = transition
var forceStatusBarAnimation = false
if let current = self.rootModalFrame {
rootModalFrame = current
transition.updateFrame(node: rootModalFrame, frame: CGRect(origin: CGPoint(), size: layout.size))
rootModalFrame.update(layout: layout, transition: modalFrameTransition)
rootModalFrame.updateDismissal(transition: transition, progress: topModalDismissProgress, completion: {})
forceStatusBarAnimation = true
} else {
rootModalFrame = NavigationModalFrame(theme: self.theme)
self.rootModalFrame = rootModalFrame
rootModalFrame.frame = CGRect(origin: CGPoint(), size: layout.size)
rootModalFrame.update(layout: layout, transition: .immediate)
rootModalFrame.animateIn(transition: transition)
if let rootContainer = self.rootContainer {
var rootContainerNode: ASDisplayNode
switch rootContainer {
case let .flat(container):
rootContainerNode = container
case let .split(container):
rootContainerNode = container
}
self.displayNode.insertSubnode(rootModalFrame, aboveSubnode: rootContainerNode)
}
rootModalFrame.updateDismissal(transition: transition, progress: topModalDismissProgress, completion: {})
}
if topModalDismissProgress < 0.5 {
self.statusBarHost?.setStatusBarStyle(.lightContent, animated: transition.isAnimated || forceStatusBarAnimation)
} else {
let normalStatusBarStyle: UIStatusBarStyle
switch self.theme.statusBar {
case .black:
normalStatusBarStyle = .default
case .white:
normalStatusBarStyle = .lightContent
}
self.statusBarHost?.setStatusBarStyle(normalStatusBarStyle, animated: transition.isAnimated || forceStatusBarAnimation)
}
if let rootContainer = self.rootContainer {
var rootContainerNode: ASDisplayNode
switch rootContainer {
case let .flat(container):
rootContainerNode = container
case let .split(container):
rootContainerNode = container
}
let maxScale = (layout.size.width - 16.0 * 2.0) / layout.size.width
var topInset: CGFloat = 0.0
if let statusBarHeight = layout.statusBarHeight {
topInset += statusBarHeight
}
let maxOffset: CGFloat = (topInset - (layout.size.height - layout.size.height * maxScale) / 2.0)
let scale = 1.0 * topModalDismissProgress + (1.0 - topModalDismissProgress) * maxScale
let offset = (1.0 - topModalDismissProgress) * maxOffset
transition.updateSublayerTransformScaleAndOffset(node: rootContainerNode, scale: scale, offset: CGPoint(x: 0.0, y: offset))
}
} else {
if let rootModalFrame = self.rootModalFrame {
self.rootModalFrame = nil
rootModalFrame.updateDismissal(transition: transition, progress: 1.0, completion: { [weak rootModalFrame] in
rootModalFrame?.removeFromSupernode()
})
let normalStatusBarStyle: UIStatusBarStyle
switch self.theme.statusBar {
case .black:
normalStatusBarStyle = .default
case .white:
normalStatusBarStyle = .lightContent
}
self.statusBarHost?.setStatusBarStyle(normalStatusBarStyle, animated: transition.isAnimated)
}
if let rootContainer = self.rootContainer {
var rootContainerNode: ASDisplayNode
switch rootContainer {
case let .flat(container):
rootContainerNode = container
case let .split(container):
rootContainerNode = container
}
transition.updateSublayerTransformScaleAndOffset(node: rootContainerNode, scale: 1.0, offset: CGPoint())
}
}
case .regular:
if let rootModalFrame = self.rootModalFrame {
self.rootModalFrame = nil
rootModalFrame.updateDismissal(transition: .immediate, progress: 1.0, completion: { [weak rootModalFrame] in
rootModalFrame?.removeFromSupernode()
})
let normalStatusBarStyle: UIStatusBarStyle
switch self.theme.statusBar {
case .black:
normalStatusBarStyle = .default
case .white:
normalStatusBarStyle = .lightContent
}
self.statusBarHost?.setStatusBarStyle(normalStatusBarStyle, animated: false)
}
if let rootContainer = self.rootContainer {
var rootContainerNode: ASDisplayNode
switch rootContainer {
case let .flat(container):
rootContainerNode = container
case let .split(container):
rootContainerNode = container
}
ContainedViewLayoutTransition.immediate.updateSublayerTransformScaleAndOffset(node: rootContainerNode, scale: 1.0, offset: CGPoint())
}
}
if self.validStatusBarStyle != self.theme.statusBar {
self.validStatusBarStyle = self.theme.statusBar
let normalStatusBarStyle: UIStatusBarStyle
switch self.theme.statusBar {
case .black:
normalStatusBarStyle = .default
case .white:
normalStatusBarStyle = .lightContent
}
self.statusBarHost?.setStatusBarStyle(normalStatusBarStyle, animated: false)
}
}
private func controllerRemoved(_ controller: ViewController) {
self.filterController(controller, animated: false)
}
public func updateModalTransition(_ value: CGFloat, transition: ContainedViewLayoutTransition) {
}
public func updateToInterfaceOrientation(_ orientation: UIInterfaceOrientation) {
/*for record in self._viewControllers {
if let controller = record.controller as? ContainableController {
controller.updateToInterfaceOrientation(orientation)
}
}*/
}
open override func loadView() {
self._displayNode = ASDisplayNode(viewBlock: {
return NavigationControllerView()
}, didLoad: nil)
self.view = self.displayNode.view
self.view.clipsToBounds = true
self.view.autoresizingMask = []
self.controllerView.backgroundColor = self.theme.emptyAreaColor
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
self.navigationBar.prefersLargeTitles = false
}
self.navigationBar.removeFromSuperview()
/*let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
panRecognizer.delegate = self
panRecognizer.delaysTouchesBegan = false
panRecognizer.cancelsTouchesInView = true
self.view.addGestureRecognizer(panRecognizer)*/
}
public func pushViewController(_ controller: ViewController) {
self.pushViewController(controller, completion: {})
}
public func pushViewController(_ controller: ViewController, animated: Bool = true, completion: @escaping () -> Void) {
let navigateAction: () -> Void = { [weak self] in
guard let strongSelf = self else {
return
}
if !controller.hasActiveInput {
//strongSelf.view.endEditing(true)
}
/*strongSelf.scheduleAfterLayout({
guard let strongSelf = self else {
return
}*/
strongSelf.pushViewController(controller, animated: animated)
completion()
//})
}
/*if let lastController = self.viewControllers.last as? ViewController, !lastController.attemptNavigation(navigateAction) {
} else {*/
navigateAction()
//}
}
open override func pushViewController(_ viewController: UIViewController, animated: Bool) {
var controllers = self.viewControllers
controllers.append(viewController)
self.setViewControllers(controllers, animated: animated)
}
public func replaceTopController(_ controller: ViewController, animated: Bool, ready: ValuePromise<Bool>? = nil) {
ready?.set(true)
var controllers = self.viewControllers
controllers.removeLast()
controllers.append(controller)
self.setViewControllers(controllers, animated: animated)
}
public func filterController(_ controller: ViewController, animated: Bool) {
let controllers = self.viewControllers.filter({ $0 !== controller })
if controllers.count != self.viewControllers.count {
self.setViewControllers(controllers, animated: animated)
}
}
public func replaceControllersAndPush(controllers: [UIViewController], controller: ViewController, animated: Bool, options: NavigationAnimationOptions = [], ready: ValuePromise<Bool>? = nil, completion: @escaping () -> Void = {}) {
ready?.set(true)
var controllers = controllers
controllers.append(controller)
self.setViewControllers(controllers, animated: animated)
completion()
}
public func replaceAllButRootController(_ controller: ViewController, animated: Bool, animationOptions: NavigationAnimationOptions = [], ready: ValuePromise<Bool>? = nil, completion: @escaping () -> Void = {}) {
ready?.set(true)
var controllers = self.viewControllers
while controllers.count > 1 {
controllers.removeLast()
}
controllers.append(controller)
self.setViewControllers(controllers, animated: animated)
completion()
}
public func popToRoot(animated: Bool) {
var controllers = self.viewControllers
while controllers.count > 1 {
controllers.removeLast()
}
self.setViewControllers(controllers, animated: animated)
}
override open func popToViewController(_ viewController: UIViewController, animated: Bool) -> [UIViewController]? {
var poppedControllers: [UIViewController] = []
var found = false
var controllers = self.viewControllers
if !controllers.contains(where: { $0 === viewController }) {
return nil
}
while !controllers.isEmpty {
if controllers[controllers.count - 1] === viewController {
found = true
break
}
poppedControllers.insert(controllers[controllers.count - 1], at: 0)
controllers.removeLast()
}
if found {
self.setViewControllers(controllers, animated: animated)
return poppedControllers
} else {
return nil
}
}
open override func popViewController(animated: Bool) -> UIViewController? {
var controller: UIViewController?
var controllers = self.viewControllers
if controllers.count != 0 {
controller = controllers[controllers.count - 1] as UIViewController
controllers.remove(at: controllers.count - 1)
self.setViewControllers(controllers, animated: animated)
}
return controller
}
open override func setViewControllers(_ viewControllers: [UIViewController], animated: Bool) {
self._viewControllers = viewControllers.map { controller in
let controller = controller as! ViewController
controller.navigation_setNavigationController(self)
return controller
}
if let layout = self.validLayout {
self.updateContainers(layout: layout, transition: animated ? .animated(duration: 0.5, curve: .spring) : .immediate)
}
}
override open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
preconditionFailure()
/*if let controller = viewControllerToPresent as? NavigationController {
controller.navigation_setDismiss({ [weak self] in
if let strongSelf = self {
strongSelf.dismiss(animated: false, completion: nil)
}
}, rootController: self.view!.window!.rootViewController)
self._presentedViewController = controller
self.view.endEditing(true)
if let validLayout = self.validLayout {
controller.containerLayoutUpdated(validLayout, transition: .immediate)
}
var ready: Signal<Bool, NoError> = .single(true)
if let controller = controller.topViewController as? ViewController {
ready = controller.ready.get()
|> filter { $0 }
|> take(1)
|> deliverOnMainQueue
}
self.currentPresentDisposable.set(ready.start(next: { [weak self] _ in
if let strongSelf = self {
if flag {
controller.view.frame = strongSelf.view.bounds.offsetBy(dx: 0.0, dy: strongSelf.view.bounds.height)
strongSelf.view.addSubview(controller.view)
UIView.animate(withDuration: 0.3, delay: 0.0, options: UIView.AnimationOptions(rawValue: 7 << 16), animations: {
controller.view.frame = strongSelf.view.bounds
}, completion: { _ in
if let completion = completion {
completion()
}
})
} else {
controller.view.frame = strongSelf.view.bounds
strongSelf.view.addSubview(controller.view)
if let completion = completion {
completion()
}
}
}
}))
} else {
preconditionFailure("NavigationController can't present \(viewControllerToPresent). Only subclasses of NavigationController are allowed.")
}*/
}
override open func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
if let controller = self.presentedViewController {
if flag {
UIView.animate(withDuration: 0.3, delay: 0.0, options: UIView.AnimationOptions(rawValue: 7 << 16), animations: {
controller.view.frame = self.view.bounds.offsetBy(dx: 0.0, dy: self.view.bounds.height)
}, completion: { _ in
controller.view.removeFromSuperview()
self._presentedViewController = nil
if let completion = completion {
completion()
}
})
} else {
controller.view.removeFromSuperview()
self._presentedViewController = nil
if let completion = completion {
completion()
}
}
}
}
public final var currentWindow: WindowHost? {
if let window = self.view.window as? WindowHost {
return window
} else if let superwindow = self.view.window {
for subview in superwindow.subviews {
if let subview = subview as? WindowHost {
return subview
}
}
}
return nil
}
private func scheduleAfterLayout(_ f: @escaping () -> Void) {
(self.view as? UITracingLayerView)?.schedule(layout: {
f()
})
self.view.setNeedsLayout()
}
private func scheduleLayoutTransitionRequest(_ transition: ContainedViewLayoutTransition) {
let requestId = self.scheduledLayoutTransitionRequestId
self.scheduledLayoutTransitionRequestId += 1
self.scheduledLayoutTransitionRequest = (requestId, transition)
(self.view as? UITracingLayerView)?.schedule(layout: { [weak self] in
if let strongSelf = self {
if let (currentRequestId, currentRequestTransition) = strongSelf.scheduledLayoutTransitionRequest, currentRequestId == requestId {
strongSelf.scheduledLayoutTransitionRequest = nil
strongSelf.requestLayout(transition: currentRequestTransition)
}
}
})
self.view.setNeedsLayout()
}
private func requestLayout(transition: ContainedViewLayoutTransition) {
if self.isViewLoaded, let validLayout = self.validLayout {
self.containerLayoutUpdated(validLayout, transition: transition)
}
}
}

View File

@ -0,0 +1,76 @@
import Foundation
import UIKit
import AsyncDisplayKit
import SwiftSignalKit
enum RootNavigationLayout {
case split([ViewController], [ViewController])
case flat([ViewController])
}
struct ModalContainerLayout {
var controllers: [ViewController]
}
struct NavigationLayout {
var root: RootNavigationLayout
var modal: [ModalContainerLayout]
}
func makeNavigationLayout(layout: ContainerViewLayout, controllers: [ViewController]) -> NavigationLayout {
var rootControllers: [ViewController] = []
var modalStack: [ModalContainerLayout] = []
for controller in controllers {
let requiresModal: Bool
var beginsModal: Bool = false
switch controller.navigationPresentation {
case .default:
requiresModal = false
case .master:
requiresModal = false
case .modal:
requiresModal = true
beginsModal = true
case .modalInLargeLayout:
switch layout.metrics.widthClass {
case .compact:
requiresModal = false
case .regular:
requiresModal = true
}
}
if requiresModal {
if beginsModal || modalStack.isEmpty {
modalStack.append(ModalContainerLayout(controllers: [controller]))
} else {
modalStack[modalStack.count - 1].controllers.append(controller)
}
} else if !modalStack.isEmpty {
modalStack[modalStack.count - 1].controllers.append(controller)
} else {
rootControllers.append(controller)
}
}
let rootLayout: RootNavigationLayout
switch layout.metrics.widthClass {
case .compact:
rootLayout = .flat(rootControllers)
case .regular:
let masterControllers = rootControllers.filter {
if case .master = $0.navigationPresentation {
return true
} else {
return false
}
}
let detailControllers = rootControllers.filter {
if case .master = $0.navigationPresentation {
return false
} else {
return true
}
}
rootLayout = .split(masterControllers, detailControllers)
}
return NavigationLayout(root: rootLayout, modal: modalStack)
}

View File

@ -0,0 +1,196 @@
import Foundation
import UIKit
import AsyncDisplayKit
import SwiftSignalKit
final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate {
private var theme: NavigationControllerTheme
private let dim: ASDisplayNode
private let scrollNode: ASScrollNode
let container: NavigationContainer
private(set) var isReady: Bool = false
private(set) var dismissProgress: CGFloat = 0.0
var isReadyUpdated: (() -> Void)?
var updateDismissProgress: ((CGFloat) -> Void)?
var interactivelyDismissed: (() -> Void)?
private var ignoreScrolling = false
private var animator: DisplayLinkAnimator?
init(theme: NavigationControllerTheme, controllerRemoved: @escaping (ViewController) -> Void) {
self.theme = theme
self.dim = ASDisplayNode()
self.dim.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
self.dim.alpha = 0.0
self.scrollNode = ASScrollNode()
self.container = NavigationContainer(controllerRemoved: controllerRemoved)
self.container.clipsToBounds = true
super.init()
self.addSubnode(self.dim)
self.addSubnode(self.scrollNode)
self.scrollNode.addSubnode(self.container)
self.isReady = self.container.isReady
self.container.isReadyUpdated = { [weak self] in
guard let strongSelf = self else {
return
}
if !strongSelf.isReady {
strongSelf.isReady = true
strongSelf.isReadyUpdated?()
}
}
}
deinit {
self.animator?.invalidate()
}
override func didLoad() {
super.didLoad()
self.scrollNode.view.alwaysBounceVertical = false
self.scrollNode.view.alwaysBounceHorizontal = false
self.scrollNode.view.bounces = false
self.scrollNode.view.showsVerticalScrollIndicator = false
self.scrollNode.view.showsHorizontalScrollIndicator = false
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
self.scrollNode.view.contentInsetAdjustmentBehavior = .never
}
self.scrollNode.view.delaysContentTouches = false
self.scrollNode.view.delegate = self
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if self.ignoreScrolling {
return
}
var progress = (self.bounds.height - scrollView.bounds.origin.y) / self.bounds.height
progress = max(0.0, min(1.0, progress))
self.dismissProgress = progress
self.dim.alpha = 1.0 - progress
self.updateDismissProgress?(progress)
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
var progress = (self.bounds.height - scrollView.bounds.origin.y) / self.bounds.height
progress = max(0.0, min(1.0, progress))
self.ignoreScrolling = true
targetContentOffset.pointee = scrollView.contentOffset
scrollView.setContentOffset(scrollView.contentOffset, animated: false)
self.ignoreScrolling = false
self.animator?.invalidate()
let targetOffset: CGFloat
if velocity.y < -0.5 || progress >= 0.5 {
targetOffset = 0.0
} else {
targetOffset = self.bounds.height
}
let velocityFactor: CGFloat = 0.4 / max(1.0, abs(velocity.y))
self.animator = DisplayLinkAnimator(duration: Double(min(0.3, velocityFactor)), from: scrollView.contentOffset.y, to: targetOffset, update: { [weak self] value in
guard let strongSelf = self else {
return
}
strongSelf.scrollNode.view.contentOffset = CGPoint(x: 0.0, y: value)
}, completion: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.animator = nil
if targetOffset == 0.0 {
strongSelf.interactivelyDismissed?()
}
})
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
}
func update(layout: ContainerViewLayout, controllers: [ViewController], transition: ContainedViewLayoutTransition) {
transition.updateFrame(node: self.dim, frame: CGRect(origin: CGPoint(), size: layout.size))
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
self.scrollNode.view.contentSize = CGSize(width: layout.size.width, height: layout.size.height * 2.0)
if !self.scrollNode.view.isDecelerating && !self.scrollNode.view.isDragging && self.animator == nil {
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height), size: layout.size))
}
let containerLayout: ContainerViewLayout
let containerFrame: CGRect
switch layout.metrics.widthClass {
case .compact:
self.dim.isHidden = true
self.container.clipsToBounds = true
self.container.cornerRadius = 10.0
if #available(iOS 11.0, *) {
self.container.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
}
var topInset: CGFloat = 10.0
if let statusBarHeight = layout.statusBarHeight {
topInset += statusBarHeight
}
containerLayout = ContainerViewLayout(size: CGSize(width: layout.size.width, height: layout.size.height - topInset), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(top: 0.0, left: layout.intrinsicInsets.left, bottom: layout.intrinsicInsets.bottom, right: layout.intrinsicInsets.right), safeInsets: UIEdgeInsets(top: 0.0, left: layout.safeInsets.left, bottom: layout.safeInsets.bottom, right: layout.safeInsets.right), statusBarHeight: nil, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver)
containerFrame = CGRect(origin: CGPoint(x: 0.0, y: topInset), size: containerLayout.size)
case .regular:
self.dim.isHidden = false
self.container.clipsToBounds = true
self.container.cornerRadius = 10.0
if #available(iOS 11.0, *) {
self.container.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner]
}
let verticalInset: CGFloat = 44.0
let maxSide = max(layout.size.width, layout.size.height)
let containerSize = CGSize(width: max(layout.size.width - 20.0, floor(maxSide / 2.0)), height: layout.size.height - verticalInset * 2.0)
containerFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - containerSize.width) / 2.0), y: floor((layout.size.height - containerSize.height) / 2.0)), size: containerSize)
var inputHeight: CGFloat?
if let inputHeightValue = layout.inputHeight {
inputHeight = max(0.0, inputHeightValue - (layout.size.height - containerFrame.maxY))
}
containerLayout = ContainerViewLayout(size: containerSize, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver)
}
transition.updateFrame(node: self.container, frame: containerFrame.offsetBy(dx: 0.0, dy: layout.size.height))
self.container.update(layout: containerLayout, canBeClosed: true, controllers: controllers, transition: transition)
}
func animateIn(transition: ContainedViewLayoutTransition) {
transition.updateAlpha(node: self.dim, alpha: 1.0)
transition.animatePositionAdditive(node: self.container, offset: CGPoint(x: 0.0, y: self.bounds.height + self.container.bounds.height / 2.0 - (self.container.position.y - self.bounds.height)))
}
func dismiss(transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) -> ContainedViewLayoutTransition {
for controller in self.container.controllers {
controller.viewWillDisappear(transition.isAnimated)
}
if transition.isAnimated {
let alphaTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
let positionTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
alphaTransition.updateAlpha(node: self.dim, alpha: 0.0, beginWithCurrentState: true)
positionTransition.updatePosition(node: self.container, position: CGPoint(x: self.container.position.x, y: self.bounds.height + self.container.bounds.height / 2.0 + self.bounds.height), beginWithCurrentState: true, completion: { [weak self] _ in
completion()
})
return positionTransition
} else {
for controller in self.container.controllers {
controller.setIgnoreAppearanceMethodInvocations(true)
controller.displayNode.removeFromSupernode()
controller.setIgnoreAppearanceMethodInvocations(false)
controller.viewDidDisappear(transition.isAnimated)
}
completion()
return transition
}
}
}

View File

@ -0,0 +1,113 @@
import Foundation
import UIKit
import AsyncDisplayKit
import SwiftSignalKit
private func generateCornerImage(radius: CGFloat, mirror: Bool) -> UIImage? {
return generateImage(CGSize(width: radius, height: radius), rotatedContext: { size, context in
context.setFillColor(UIColor.black.cgColor)
context.fill(CGRect(origin: CGPoint(), size: size))
context.setBlendMode(.copy)
context.setFillColor(UIColor.clear.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: mirror ? (-radius) : 0.0, y: 0.0), size: CGSize(width: radius * 2.0, height: radius * 2.0)))
})
}
final class NavigationModalFrame: ASDisplayNode {
private let dim: ASDisplayNode
private let topShade: ASDisplayNode
private let leftShade: ASDisplayNode
private let rightShade: ASDisplayNode
private let topLeftCorner: ASImageNode
private let topRightCorner: ASImageNode
private var currentMaxCornerRadius: CGFloat?
private var progress: CGFloat = 0.0
private var validLayout: ContainerViewLayout?
init(theme: NavigationControllerTheme) {
self.dim = ASDisplayNode()
self.dim.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
self.dim.alpha = 0.0
self.topShade = ASDisplayNode()
self.topShade.backgroundColor = .black
self.leftShade = ASDisplayNode()
self.leftShade.backgroundColor = .black
self.rightShade = ASDisplayNode()
self.rightShade.backgroundColor = .black
self.topLeftCorner = ASImageNode()
self.topLeftCorner.displaysAsynchronously = false
self.topLeftCorner.displayWithoutProcessing = true
self.topRightCorner = ASImageNode()
self.topRightCorner.displaysAsynchronously = false
self.topRightCorner.displayWithoutProcessing = true
super.init()
self.addSubnode(self.dim)
self.addSubnode(self.topShade)
self.addSubnode(self.leftShade)
self.addSubnode(self.rightShade)
self.addSubnode(self.topLeftCorner)
self.addSubnode(self.topRightCorner)
}
func update(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
self.validLayout = layout
transition.updateFrame(node: self.dim, frame: CGRect(origin: CGPoint(), size: layout.size))
self.updateShades(layout: layout, progress: 1.0 - self.progress, transition: transition)
}
func animateIn(transition: ContainedViewLayoutTransition) {
transition.updateAlpha(node: self.dim, alpha: 1.0)
if let layout = self.validLayout {
self.updateShades(layout: layout, progress: 0.0, transition: .immediate)
self.updateShades(layout: layout, progress: 1.0, transition: transition)
}
}
func updateDismissal(transition: ContainedViewLayoutTransition, progress: CGFloat, completion: @escaping () -> Void) {
self.progress = progress
transition.updateAlpha(node: self.dim, alpha: 1.0 - progress, completion: { _ in
completion()
})
if let layout = self.validLayout {
self.updateShades(layout: layout, progress: 1.0 - progress, transition: transition)
}
}
private func updateShades(layout: ContainerViewLayout, progress: CGFloat, transition: ContainedViewLayoutTransition) {
let sideInset: CGFloat = 16.0
var topInset: CGFloat = 0.0
if let statusBarHeight = layout.statusBarHeight {
topInset += statusBarHeight
}
let cornerRadius: CGFloat = 8.0
let initialCornerRadius: CGFloat
if !layout.safeInsets.top.isZero {
initialCornerRadius = 40.0
} else {
initialCornerRadius = 0.0
}
if self.currentMaxCornerRadius != cornerRadius {
self.topLeftCorner.image = generateCornerImage(radius: max(initialCornerRadius, cornerRadius), mirror: false)
self.topRightCorner.image = generateCornerImage(radius: max(initialCornerRadius, cornerRadius), mirror: true)
}
let cornerSize = progress * cornerRadius + (1.0 - progress) * initialCornerRadius
transition.updateFrame(node: self.topLeftCorner, frame: CGRect(origin: CGPoint(x: progress * sideInset, y: progress * topInset), size: CGSize(width: cornerSize, height: cornerSize)))
transition.updateFrame(node: self.topRightCorner, frame: CGRect(origin: CGPoint(x: layout.size.width - progress * sideInset - cornerSize, y: progress * topInset), size: CGSize(width: cornerSize, height: cornerSize)))
transition.updateFrame(node: self.topShade, frame: CGRect(origin: CGPoint(x: 0.0, y: (1.0 - progress) * (-topInset)), size: CGSize(width: layout.size.width, height: topInset)))
transition.updateFrame(node: self.leftShade, frame: CGRect(origin: CGPoint(x: (1.0 - progress) * (-sideInset), y: 0.0), size: CGSize(width: sideInset, height: layout.size.height)))
transition.updateFrame(node: self.rightShade, frame: CGRect(origin: CGPoint(x: layout.size.width - sideInset * progress, y: 0.0), size: CGSize(width: sideInset, height: layout.size.height)))
}
}

View File

@ -0,0 +1,43 @@
import Foundation
import UIKit
import AsyncDisplayKit
import SwiftSignalKit
final class NavigationSplitContainer: ASDisplayNode {
private var theme: NavigationControllerTheme
private let masterContainer: NavigationContainer
private let detailContainer: NavigationContainer
private let separator: ASDisplayNode
init(theme: NavigationControllerTheme, controllerRemoved: @escaping (ViewController) -> Void) {
self.theme = theme
self.masterContainer = NavigationContainer(controllerRemoved: controllerRemoved)
self.masterContainer.clipsToBounds = true
self.detailContainer = NavigationContainer(controllerRemoved: controllerRemoved)
self.detailContainer.clipsToBounds = true
self.separator = ASDisplayNode()
self.separator.backgroundColor = theme.navigationBar.separatorColor
super.init()
self.addSubnode(self.masterContainer)
self.addSubnode(self.detailContainer)
self.addSubnode(self.separator)
}
func update(layout: ContainerViewLayout, masterControllers: [ViewController], detailControllers: [ViewController], transition: ContainedViewLayoutTransition) {
let masterWidth = min(max(320.0, floor(layout.size.width / 3.0)), floor(layout.size.width / 2.0))
let detailWidth = layout.size.width - masterWidth
transition.updateFrame(node: self.masterContainer, frame: CGRect(origin: CGPoint(), size: CGSize(width: masterWidth, height: layout.size.height)))
transition.updateFrame(node: self.detailContainer, frame: CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: detailWidth, height: layout.size.height)))
transition.updateFrame(node: self.separator, frame: CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: UIScreenPixel, height: layout.size.height)))
self.masterContainer.update(layout: ContainerViewLayout(size: CGSize(width: masterWidth, height: layout.size.height), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), canBeClosed: false, controllers: masterControllers, transition: transition)
self.detailContainer.update(layout: ContainerViewLayout(size: CGSize(width: detailWidth, height: layout.size.height), metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), canBeClosed: true, controllers: detailControllers, transition: transition)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,8 @@ class ScrollToTopView: UIScrollView, UIScrollViewDelegate {
override init(frame: CGRect) {
super.init(frame: frame)
self.isOpaque = false
self.backgroundColor = .clear
self.delegate = self
self.scrollsToTop = true
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {

View File

@ -11,4 +11,6 @@ public protocol StatusBarHost {
var keyboardView: UIView? { get }
var handleVolumeControl: Signal<Bool, NoError> { get }
func setStatusBarStyle(_ style: UIStatusBarStyle, animated: Bool)
}

View File

@ -278,7 +278,7 @@ class StatusBarManager {
}
self.volumeControlStatusBarNode.isDark = isDark
if let globalStatusBar = globalStatusBar, !forceHiddenBySystemWindows {
/*if let globalStatusBar = globalStatusBar, !forceHiddenBySystemWindows {
let statusBarStyle: UIStatusBarStyle
if forceInCallStatusBarText != nil {
statusBarStyle = .lightContent
@ -298,6 +298,6 @@ class StatusBarManager {
}
} else {
statusBarView.alpha = 0.0
}
}*/
}
}

View File

@ -58,6 +58,8 @@ open class ViewControllerPresentationArguments {
public enum ViewControllerNavigationPresentation {
case `default`
case master
case modal
case modalInLargeLayout
}
@objc open class ViewController: UIViewController, ContainableController {
@ -446,9 +448,9 @@ public enum ViewControllerNavigationPresentation {
override open func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
if let navigationController = self.navigationController as? NavigationController {
navigationController.dismiss(animated: flag, completion: completion)
navigationController.filterController(self, animated: flag)
} else {
super.dismiss(animated: flag, completion: completion)
assertionFailure()
}
}
@ -468,10 +470,10 @@ public enum ViewControllerNavigationPresentation {
public func present(_ controller: ViewController, in context: PresentationContextType, with arguments: Any? = nil, blockInteraction: Bool = false, completion: @escaping () -> Void = {}) {
controller.presentationArguments = arguments
switch context {
case .current:
self.presentationContext.present(controller, on: PresentationSurfaceLevel(rawValue: 0), completion: completion)
case let .window(level):
self.window?.present(controller, on: level, blockInteraction: blockInteraction, completion: completion)
case .current:
self.presentationContext.present(controller, on: PresentationSurfaceLevel(rawValue: 0), completion: completion)
case let .window(level):
self.window?.present(controller, on: level, blockInteraction: blockInteraction, completion: completion)
}
}
@ -507,6 +509,7 @@ public enum ViewControllerNavigationPresentation {
}
open func dismiss(completion: (() -> Void)? = nil) {
(self.navigationController as? NavigationController)?.filterController(self, animated: true)
}
@available(iOSApplicationExtension 9.0, iOS 9.0, *)

View File

@ -692,6 +692,10 @@ public class Window1 {
self._rootController = value
if let rootController = self._rootController {
if let rootController = rootController as? NavigationController {
rootController.statusBarHost = self.statusBarHost
rootController.keyboardManager = self.keyboardManager
}
if !self.windowLayout.size.width.isZero && !self.windowLayout.size.height.isZero {
rootController.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout, deviceMetrics: self.deviceMetrics), transition: .immediate)
}
@ -803,7 +807,7 @@ public class Window1 {
}
}
}
keyboardManager.surfaces = keyboardSurfaces
//keyboardManager.surfaces = keyboardSurfaces
var supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .all)
let orientationToLock: UIInterfaceOrientationMask

View File

@ -62,7 +62,6 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
var currentAccessibilityAreas: [AccessibilityAreaNode] = []
private var previousContentOffset: CGPoint?
private var isDeceleratingBecauseOfDragging = false
private let hiddenMediaDisposable = MetaDisposable()
@ -372,7 +371,6 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
self.scrollNode.view.contentOffset = contentOffset
if didSetScrollOffset {
self.previousContentOffset = contentOffset
self.updateNavigationBar()
if self.currentLayout != nil {
self.setupScrollOffsetOnLayout = false
@ -660,8 +658,6 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.updateVisibleItems(visibleBounds: self.scrollNode.view.bounds)
self.updateNavigationBar()
self.previousContentOffset = self.scrollNode.view.contentOffset
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
@ -694,50 +690,14 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
minBarHeight = 20.0
}
var pageProgress: CGFloat = 0.0
if !self.scrollNode.view.contentSize.height.isZero {
let value = (contentOffset.y + self.scrollNode.view.contentInset.top) / (self.scrollNode.view.contentSize.height - bounds.size.height + self.scrollNode.view.contentInset.top)
pageProgress = max(0.0, min(1.0, value))
}
let delta: CGFloat
if self.setupScrollOffsetOnLayout {
delta = 0.0
} else if let previousContentOffset = self.previousContentOffset {
delta = contentOffset.y - previousContentOffset.y
} else {
delta = 0.0
}
self.previousContentOffset = contentOffset
var transition: ContainedViewLayoutTransition = .immediate
var navigationBarFrame = self.navigationBar.frame
navigationBarFrame.size.width = bounds.size.width
if navigationBarFrame.size.height.isZero {
navigationBarFrame.size.height = maxBarHeight
}
if forceState {
transition = .animated(duration: 0.3, curve: .spring)
let transitionFactor = (navigationBarFrame.size.height - minBarHeight) / (maxBarHeight - minBarHeight)
if contentOffset.y <= -self.scrollNode.view.contentInset.top || transitionFactor > 0.4 {
navigationBarFrame.size.height = maxBarHeight
} else {
navigationBarFrame.size.height = minBarHeight
}
} else {
if contentOffset.y <= -self.scrollNode.view.contentInset.top {
navigationBarFrame.size.height = maxBarHeight
} else {
navigationBarFrame.size.height -= delta
}
navigationBarFrame.size.height = max(minBarHeight, min(maxBarHeight, navigationBarFrame.size.height))
}
if self.setupScrollOffsetOnLayout {
navigationBarFrame.size.height = maxBarHeight
}
navigationBarFrame.size.height = maxBarHeight
let transitionFactor = (navigationBarFrame.size.height - minBarHeight) / (maxBarHeight - minBarHeight)
@ -756,7 +716,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
transition.updateFrame(node: self.navigationBar, frame: navigationBarFrame)
self.navigationBar.updateLayout(size: navigationBarFrame.size, minHeight: minBarHeight, maxHeight: maxBarHeight, topInset: containerLayout.safeInsets.top, leftInset: containerLayout.safeInsets.left, rightInset: containerLayout.safeInsets.right, title: title, pageProgress: pageProgress, transition: transition)
self.navigationBar.updateLayout(size: navigationBarFrame.size, minHeight: minBarHeight, maxHeight: maxBarHeight, topInset: containerLayout.safeInsets.top, leftInset: containerLayout.safeInsets.left, rightInset: containerLayout.safeInsets.right, title: title, pageProgress: 0.0, transition: transition)
transition.animateView {
self.scrollNode.view.scrollIndicatorInsets = UIEdgeInsets(top: navigationBarFrame.size.height, left: 0.0, bottom: containerLayout.intrinsicInsets.bottom, right: 0.0)

View File

@ -505,14 +505,6 @@ open class ItemListController<Entry: ItemListNodeEntry>: ViewController, KeyShor
self.didDisappear?(animated)
}
override open func dismiss(completion: (() -> Void)? = nil) {
if !self.isDismissed {
self.isDismissed = true
(self.displayNode as! ItemListControllerNode<Entry>).animateOut(completion: completion)
self.updateTransitionWhenPresentedAsModal?(0.0, .animated(duration: 0.2, curve: .easeInOut))
}
}
public func frameForItemNode(_ predicate: (ListViewItemNode) -> Bool) -> CGRect? {
var result: CGRect?
(self.displayNode as! ItemListControllerNode<Entry>).listNode.forEachItemNode { itemNode in

View File

@ -508,7 +508,7 @@ open class LegacyController: ViewController, PresentableController {
case .custom:
self.presentingViewController?.dismiss(animated: false, completion: completion)
case .navigation:
break
(self.navigationController as? NavigationController)?.filterController(self, animated: true)
}
}

View File

@ -15,7 +15,8 @@ private func generateClearIcon(color: UIColor) -> UIImage? {
}
public func legacyLocationPickerController(context: AccountContext, selfPeer: Peer, peer: Peer, sendLocation: @escaping (CLLocationCoordinate2D, MapVenue?, String?) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D, Int32) -> Void, theme: PresentationTheme, customLocationPicker: Bool = false, hasLiveLocation: Bool = true, presentationCompleted: @escaping () -> Void = {}) -> ViewController {
let legacyController = LegacyController(presentation: .modal(animateIn: true), theme: theme)
let legacyController = LegacyController(presentation: .navigation, theme: theme)
legacyController.navigationPresentation = .modal
legacyController.presentationCompleted = {
presentationCompleted()
}

View File

@ -1105,6 +1105,7 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
}
let controller = ItemListController(context: context, state: signal)
controller.navigationPresentation = .modal
controller.experimentalSnapScrollToItem = true
dismissImpl = { [weak controller] in
controller?.view.endEditing(true)

View File

@ -600,20 +600,20 @@ public func channelAdminsController(context: AccountContext, peerId: PeerId, loa
}
}
}
presentControllerImpl?(channelAdminController(context: context, peerId: peerId, adminId: peer.id, initialParticipant: participant?.participant, updated: { _ in
}, upgradedToSupergroup: upgradedToSupergroup, transferedOwnership: transferedOwnership), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
pushControllerImpl?(channelAdminController(context: context, peerId: peerId, adminId: peer.id, initialParticipant: participant?.participant, updated: { _ in
}, upgradedToSupergroup: upgradedToSupergroup, transferedOwnership: transferedOwnership))
})
dismissController = { [weak controller] in
controller?.dismiss()
}
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
pushControllerImpl?(controller)
return current
}
})
}, openAdmin: { participant in
presentControllerImpl?(channelAdminController(context: context, peerId: peerId, adminId: participant.peerId, initialParticipant: participant, updated: { _ in
}, upgradedToSupergroup: upgradedToSupergroup, transferedOwnership: transferedOwnership), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
pushControllerImpl?(channelAdminController(context: context, peerId: peerId, adminId: participant.peerId, initialParticipant: participant, updated: { _ in
}, upgradedToSupergroup: upgradedToSupergroup, transferedOwnership: transferedOwnership))
})
let membersAndLoadMoreControl: (Disposable, PeerChannelMemberCategoryControl?)
@ -734,14 +734,14 @@ public func channelAdminsController(context: AccountContext, peerId: PeerId, loa
}
}, openPeer: { _, participant in
if let participant = participant?.participant, case .member = participant {
presentControllerImpl?(channelAdminController(context: context, peerId: peerId, adminId: participant.peerId, initialParticipant: participant, updated: { _ in
pushControllerImpl?(channelAdminController(context: context, peerId: peerId, adminId: participant.peerId, initialParticipant: participant, updated: { _ in
updateState { state in
return state.withUpdatedSearchingMembers(false)
}
}, upgradedToSupergroup: upgradedToSupergroup, transferedOwnership: transferedOwnership), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}, upgradedToSupergroup: upgradedToSupergroup, transferedOwnership: transferedOwnership))
}
}, present: { c, a in
presentControllerImpl?(c, a)
}, pushController: { c in
pushControllerImpl?(c)
})
}

View File

@ -486,8 +486,8 @@ public func channelBlacklistController(context: AccountContext, peerId: PeerId)
if let rendered = rendered, case .member = rendered.participant {
arguments.openPeer(rendered)
}
}, present: { c, a in
presentControllerImpl?(c, a)
}, pushController: { c in
pushControllerImpl?(c)
})
}

View File

@ -403,8 +403,8 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Channel_AddBotErrorHaveRights, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Channel_AddBotAsAdmin, action: {
contactsController?.dismiss()
presentControllerImpl?(channelAdminController(context: context, peerId: peerId, adminId: memberId, initialParticipant: nil, updated: { _ in
}, upgradedToSupergroup: { _, f in f () }, transferedOwnership: { _ in }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
pushControllerImpl?(channelAdminController(context: context, peerId: peerId, adminId: memberId, initialParticipant: nil, updated: { _ in
}, upgradedToSupergroup: { _, f in f () }, transferedOwnership: { _ in }))
})]), nil)
} else {
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Channel_AddBotErrorHaveRights, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
@ -500,8 +500,8 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic) {
pushControllerImpl?(infoController)
}
}, present: { c, a in
presentControllerImpl?(c, a)
}, pushController: { c in
pushControllerImpl?(c)
})
}

View File

@ -296,7 +296,7 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod
private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder, PresentationDateTimeFormat)>
init(context: AccountContext, peerId: PeerId, mode: ChannelMembersSearchMode, filters: [ChannelMembersSearchFilter], searchContext: GroupMembersSearchContext?, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, updateActivity: @escaping (Bool) -> Void, present: @escaping (ViewController, Any?) -> Void) {
init(context: AccountContext, peerId: PeerId, mode: ChannelMembersSearchMode, filters: [ChannelMembersSearchFilter], searchContext: GroupMembersSearchContext?, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, updateActivity: @escaping (Bool) -> Void, pushController: @escaping (ViewController) -> Void) {
self.context = context
self.openPeer = openPeer
self.mode = mode
@ -341,16 +341,16 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod
state.revealedPeerId = nil
return state
}
present(channelAdminController(context: context, peerId: peerId, adminId: participant.peer.id, initialParticipant: participant.participant, updated: { _ in
}, upgradedToSupergroup: { _, f in f() }, transferedOwnership: { _ in }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
pushController(channelAdminController(context: context, peerId: peerId, adminId: participant.peer.id, initialParticipant: participant.participant, updated: { _ in
}, upgradedToSupergroup: { _, f in f() }, transferedOwnership: { _ in }))
}, restrictPeer: { participant in
updateState { state in
var state = state
state.revealedPeerId = nil
return state
}
present(channelBannedMemberController(context: context, peerId: peerId, memberId: participant.peer.id, initialParticipant: participant.participant, updated: { _ in
}, upgradedToSupergroup: { _, f in f() }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
pushController(channelBannedMemberController(context: context, peerId: peerId, memberId: participant.peer.id, initialParticipant: participant.participant, updated: { _ in
}, upgradedToSupergroup: { _, f in f() }))
}, removePeer: { memberId in
updateState { state in
var state = state

View File

@ -47,6 +47,8 @@ final class ChannelMembersSearchController: ViewController {
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
self.navigationPresentation = .modal
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
self.title = self.presentationData.strings.Channel_Members_Title
@ -83,8 +85,8 @@ final class ChannelMembersSearchController: ViewController {
self.controllerNode.requestOpenPeerFromSearch = { [weak self] peer, participant in
self?.openPeer(peer, participant)
}
self.controllerNode.present = { [weak self] c, a in
self?.present(c, in: .window(.root), with: a)
self.controllerNode.pushController = { [weak self] c in
(self?.navigationController as? NavigationController)?.pushViewController(c)
}
self.displayNodeDidLoad()
@ -113,14 +115,6 @@ final class ChannelMembersSearchController: ViewController {
}
}
override func dismiss(completion: (() -> Void)? = nil) {
self.view.endEditing(true)
self.controllerNode.animateOut(completion: { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil)
completion?()
})
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)

View File

@ -108,7 +108,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
var requestActivateSearch: (() -> Void)?
var requestDeactivateSearch: (() -> Void)?
var requestOpenPeerFromSearch: ((Peer, RenderedChannelParticipant?) -> Void)?
var present: ((ViewController, Any?) -> Void)?
var pushController: ((ViewController) -> Void)?
var presentationData: PresentationData
@ -362,8 +362,8 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
self?.requestOpenPeerFromSearch?(peer, participant)
}, updateActivity: { value in
}, present: { [weak self] c, a in
self?.present?(c, a)
}, pushController: { [weak self] c in
self?.pushController?(c)
}), cancel: { [weak self] in
if let requestDeactivateSearch = self?.requestDeactivateSearch {
requestDeactivateSearch()

View File

@ -797,8 +797,8 @@ public func channelPermissionsController(context: AccountContext, peerId origina
upgradedToSupergroupImpl?(upgradedPeerId, f)
}), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}
}, present: { c, a in
presentControllerImpl?(c, a)
}, pushController: { c in
pushControllerImpl?(c)
})
}

View File

@ -811,6 +811,7 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
var addToExistingImpl: (() -> Void)?
var openChatImpl: ((PeerId) -> Void)?
var replaceControllerImpl: ((ViewController) -> Void)?
var pushControllerImpl: ((ViewController) -> Void)?
var presentControllerImpl: ((ViewController, Any?) -> Void)?
var openUrlImpl: ((String) -> Void)?
var openAddressImpl: ((DeviceContactAddressData) -> Void)?
@ -908,7 +909,7 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
return state
}
}, updatePhoneLabel: { id, currentLabel in
presentControllerImpl?(phoneLabelController(context: context, currentLabel: currentLabel, completion: { value in
pushControllerImpl?(phoneLabelController(context: context, currentLabel: currentLabel, completion: { value in
updateState { state in
var state = state
for i in 0 ..< state.phoneNumbers.count {
@ -919,7 +920,7 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
}
return state
}
}), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}))
}, deletePhone: { id in
updateState { state in
var state = state
@ -976,14 +977,14 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}
case .createContact:
presentControllerImpl?(deviceContactInfoController(context: context, subject: .create(peer: subject.peer, contactData: subject.contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
pushControllerImpl?(deviceContactInfoController(context: context, subject: .create(peer: subject.peer, contactData: subject.contactData, isSharing: false, shareViaException: false, completion: { peer, stableId, contactData in
dismissImpl?(false)
if let peer = peer {
} else {
}
}), completed: nil, cancelled: nil), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}), completed: nil, cancelled: nil))
case .addToExisting:
addToExistingImpl?()
case .sendMessage:
@ -1210,6 +1211,7 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
}
let controller = DeviceContactInfoController(context: context, state: signal)
controller.navigationPresentation = .modal
addToExistingImpl = { [weak controller] in
guard let controller = controller else {
return
@ -1226,6 +1228,9 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
replaceControllerImpl = { [weak controller] value in
(controller?.navigationController as? NavigationController)?.replaceTopController(value, animated: true)
}
pushControllerImpl = { [weak controller] c in
(controller?.navigationController as? NavigationController)?.pushViewController(c)
}
presentControllerImpl = { [weak controller] value, presentationArguments in
controller?.present(value, in: .window(.root), with: presentationArguments)
}
@ -1293,7 +1298,8 @@ public func deviceContactInfoController(context: AccountContext, subject: Device
private func addContactToExisting(context: AccountContext, parentController: ViewController, contactData: DeviceContactExtendedData, completion: @escaping (Peer?, DeviceContactStableId, DeviceContactExtendedData) -> Void) {
let contactsController = context.sharedContext.makeContactSelectionController(ContactSelectionControllerParams(context: context, title: { $0.Contacts_Title }, displayDeviceContacts: true))
parentController.present(contactsController, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
contactsController.navigationPresentation = .modal
(parentController.navigationController as? NavigationController)?.pushViewController(contactsController)
let _ = (contactsController.result
|> deliverOnMainQueue).start(next: { peer in
if let peer = peer {

View File

@ -1868,10 +1868,10 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
let _ = (peerView.get()
|> take(1)
|> deliverOnMainQueue).start(next: { peerView in
presentControllerImpl?(channelAdminController(context: context, peerId: peerView.peerId, adminId: participant.peer.id, initialParticipant: participant.participant, updated: { _ in
pushControllerImpl?(channelAdminController(context: context, peerId: peerView.peerId, adminId: participant.peer.id, initialParticipant: participant.participant, updated: { _ in
}, upgradedToSupergroup: { upgradedPeerId, f in
upgradedToSupergroupImpl?(upgradedPeerId, f)
}, transferedOwnership: { _ in }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}, transferedOwnership: { _ in }))
})
}, restrictPeer: { participant in
let _ = (peerView.get()
@ -2257,8 +2257,8 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
if let infoController = context.sharedContext.makePeerInfoController(context: context, peer: peer, mode: .generic) {
arguments.pushController(infoController)
}
}, present: { c, a in
presentControllerImpl?(c, a)
}, pushController: { c in
pushControllerImpl?(c)
})
}

View File

@ -14,20 +14,20 @@ final class ChannelMembersSearchItem: ItemListControllerSearch {
let searchContext: GroupMembersSearchContext?
let cancel: () -> Void
let openPeer: (Peer, RenderedChannelParticipant?) -> Void
let present: (ViewController, Any?) -> Void
let pushController: (ViewController) -> Void
let searchMode: ChannelMembersSearchMode
private var updateActivity: ((Bool) -> Void)?
private var activity: ValuePromise<Bool> = ValuePromise(ignoreRepeated: false)
private let activityDisposable = MetaDisposable()
init(context: AccountContext, peerId: PeerId, searchContext: GroupMembersSearchContext?, searchMode: ChannelMembersSearchMode = .searchMembers, cancel: @escaping () -> Void, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, present: @escaping (ViewController, Any?) -> Void) {
init(context: AccountContext, peerId: PeerId, searchContext: GroupMembersSearchContext?, searchMode: ChannelMembersSearchMode = .searchMembers, cancel: @escaping () -> Void, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, pushController: @escaping (ViewController) -> Void) {
self.context = context
self.peerId = peerId
self.searchContext = searchContext
self.cancel = cancel
self.openPeer = openPeer
self.present = present
self.pushController = pushController
self.searchMode = searchMode
activityDisposable.set((activity.get() |> mapToSignal { value -> Signal<Bool, NoError> in
if value {
@ -73,8 +73,8 @@ final class ChannelMembersSearchItem: ItemListControllerSearch {
func node(current: ItemListControllerSearchNode?, titleContentNode: (NavigationBarContentNode & ItemListControllerSearchNavigationContentNode)?) -> ItemListControllerSearchNode {
return ChannelMembersSearchItemNode(context: self.context, peerId: self.peerId, searchMode: self.searchMode, searchContext: self.searchContext, openPeer: self.openPeer, cancel: self.cancel, updateActivity: { [weak self] value in
self?.activity.set(value)
}, present: { [weak self] c, a in
self?.present(c, a)
}, pushController: { [weak self] c in
self?.pushController(c)
})
}
}
@ -82,10 +82,10 @@ final class ChannelMembersSearchItem: ItemListControllerSearch {
private final class ChannelMembersSearchItemNode: ItemListControllerSearchNode {
private let containerNode: ChannelMembersSearchContainerNode
init(context: AccountContext, peerId: PeerId, searchMode: ChannelMembersSearchMode, searchContext: GroupMembersSearchContext?, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, cancel: @escaping () -> Void, updateActivity: @escaping(Bool) -> Void, present: @escaping (ViewController, Any?) -> Void) {
init(context: AccountContext, peerId: PeerId, searchMode: ChannelMembersSearchMode, searchContext: GroupMembersSearchContext?, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, cancel: @escaping () -> Void, updateActivity: @escaping(Bool) -> Void, pushController: @escaping (ViewController) -> Void) {
self.containerNode = ChannelMembersSearchContainerNode(context: context, peerId: peerId, mode: searchMode, filters: [], searchContext: searchContext, openPeer: { peer, participant in
openPeer(peer, participant)
}, updateActivity: updateActivity, present: present)
}, updateActivity: updateActivity, pushController: pushController)
self.containerNode.cancel = {
cancel()
}

View File

@ -126,6 +126,7 @@ public func phoneLabelController(context: AccountContext, currentLabel: String,
let controller = ItemListController(context: context, state: signal
|> afterDisposed {
})
controller.navigationPresentation = .modal
controller.enableInteractiveDismiss = true
completeImpl = { [weak controller] in

View File

@ -766,7 +766,7 @@ private func getUserPeer(postbox: Postbox, peerId: PeerId) -> Signal<(Peer?, Cac
}
}
public func openAddPersonContactImpl(context: AccountContext, peerId: PeerId, present: @escaping (ViewController, Any?) -> Void) {
public func openAddPersonContactImpl(context: AccountContext, peerId: PeerId, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void) {
let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId)
|> deliverOnMainQueue).start(next: { peer, cachedData in
guard let user = peer as? TelegramUser, let contactData = DeviceContactExtendedData(peer: user) else {
@ -778,7 +778,7 @@ public func openAddPersonContactImpl(context: AccountContext, peerId: PeerId, pr
shareViaException = peerStatusSettings.contains(.addExceptionWhenAddingContact)
}
present(deviceContactInfoController(context: context, subject: .create(peer: user, contactData: contactData, isSharing: true, shareViaException: shareViaException, completion: { peer, stableId, contactData in
pushController(deviceContactInfoController(context: context, subject: .create(peer: user, contactData: contactData, isSharing: true, shareViaException: shareViaException, completion: { peer, stableId, contactData in
if let peer = peer as? TelegramUser {
if let phone = peer.phone, !phone.isEmpty {
}
@ -786,7 +786,7 @@ public func openAddPersonContactImpl(context: AccountContext, peerId: PeerId, pr
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
present(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess(presentationData.strings.AddContact_StatusSuccess(peer.compactDisplayTitle).0, true)), nil)
}
}), completed: nil, cancelled: nil), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}), completed: nil, cancelled: nil))
})
}
@ -916,7 +916,9 @@ public func userInfoController(context: AccountContext, peerId: PeerId, mode: Pe
}, openChat: {
openChatImpl?()
}, addContact: {
openAddPersonContactImpl(context: context, peerId: peerId, present: { c, a in
openAddPersonContactImpl(context: context, peerId: peerId, pushController: { c in
pushControllerImpl?(c)
}, present: { c, a in
presentControllerImpl?(c, a)
})
}, shareContact: {

View File

@ -315,7 +315,7 @@ public func proxySettingsController(context: AccountContext, mode: ProxySettings
}
public func proxySettingsController(accountManager: AccountManager, postbox: Postbox, network: Network, mode: ProxySettingsControllerMode, theme: PresentationTheme, strings: PresentationStrings, updatedPresentationData: Signal<(theme: PresentationTheme, strings: PresentationStrings), NoError>) -> ViewController {
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
var pushControllerImpl: ((ViewController) -> Void)?
var dismissImpl: (() -> Void)?
let stateValue = Atomic(value: ProxySettingsControllerState())
let statePromise = ValuePromise<ProxySettingsControllerState>(stateValue.with { $0 })
@ -342,7 +342,7 @@ public func proxySettingsController(accountManager: AccountManager, postbox: Pos
return current
}).start()
}, addNewServer: {
presentControllerImpl?(proxyServerSettingsController(theme: theme, strings: strings, updatedPresentationData: updatedPresentationData, accountManager: accountManager, postbox: postbox, network: network, currentSettings: nil), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
pushControllerImpl?(proxyServerSettingsController(theme: theme, strings: strings, updatedPresentationData: updatedPresentationData, accountManager: accountManager, postbox: postbox, network: network, currentSettings: nil))
}, activateServer: { server in
let _ = updateProxySettingsInteractively(accountManager: accountManager, { current in
var current = current
@ -355,7 +355,7 @@ public func proxySettingsController(accountManager: AccountManager, postbox: Pos
return current
}).start()
}, editServer: { server in
presentControllerImpl?(proxyServerSettingsController(theme: theme, strings: strings, updatedPresentationData: updatedPresentationData, accountManager: accountManager, postbox: postbox, network: network, currentSettings: server), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
pushControllerImpl?(proxyServerSettingsController(theme: theme, strings: strings, updatedPresentationData: updatedPresentationData, accountManager: accountManager, postbox: postbox, network: network, currentSettings: server))
}, removeServer: { server in
let _ = updateProxySettingsInteractively(accountManager: accountManager, { current in
var current = current
@ -438,8 +438,9 @@ public func proxySettingsController(accountManager: AccountManager, postbox: Pos
}
let controller = ItemListController(theme: theme, strings: strings, updatedPresentationData: updatedPresentationData, state: signal, tabBarItem: nil)
presentControllerImpl = { [weak controller] c, a in
controller?.present(c, in: .window(.root), with: a)
controller.navigationPresentation = .modal
pushControllerImpl = { [weak controller] c in
(controller?.navigationController as? NavigationController)?.pushViewController(c)
}
dismissImpl = { [weak controller] in
controller?.dismiss()

View File

@ -369,6 +369,7 @@ func proxyServerSettingsController(theme: PresentationTheme, strings: Presentati
}
let controller = ItemListController(theme: theme, strings: strings, updatedPresentationData: updatedPresentationData, state: signal, tabBarItem: nil)
controller.navigationPresentation = .modal
presentImpl = { [weak controller] c, d in
controller?.present(c, in: .window(.root), with: d)
}

View File

@ -172,7 +172,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start()
}
}
arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
arguments.pushController(controller)
}))
}
items.append(ActionSheetButtonItem(title: "Via Email", color: .accent, action: { [weak actionSheet] in
@ -225,7 +225,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start()
}
}
arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
arguments.pushController(controller)
}))
}
@ -272,7 +272,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start()
}
}
arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
arguments.pushController(controller)
})
})
case let .sendCriticalLogs(theme):
@ -301,7 +301,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start()
}
}
arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
arguments.pushController(controller)
}))
}

View File

@ -211,7 +211,7 @@ private enum SettingsEntry: ItemListNodeEntry {
})
case let .username(theme, text, address):
return ItemListDisclosureItem(theme: theme, title: text, label: address, sectionId: ItemListSectionId(self.section), style: .blocks, action: {
arguments.presentController(usernameSetupController(context: arguments.context))
arguments.pushController(usernameSetupController(context: arguments.context))
})
case let .addAccount(theme, text):
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .center, sectionId: ItemListSectionId(self.section), style: .blocks, action: {

View File

@ -894,7 +894,7 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode {
})
}
dismissInputImpl?()
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
pushController(controller)
}, updateRevealedPeerId: { peerId in
updateState { current in
return current.withUpdatedRevealedPeerId(peerId)

View File

@ -239,7 +239,7 @@ public func blockedPeersController(context: AccountContext, blockedPeersContext:
strongController.dismiss()
}))
}
presentControllerImpl?(controller, nil)
pushControllerImpl?(controller)
}, removePeer: { memberId in
updateState {
return $0.withUpdatedRemovingPeerId(memberId)

View File

@ -50,6 +50,8 @@ public final class ThemePreviewController: ViewController {
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.previewTheme, presentationStrings: self.presentationData.strings))
self.navigationPresentation = .modal
self.isModalWhenInOverlay = true
let themeName: String
@ -333,13 +335,6 @@ public final class ThemePreviewController: ViewController {
}))
}
override public func dismiss(completion: (() -> Void)? = nil) {
self.controllerNode.animateOut(completion: { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil)
completion?()
})
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)

View File

@ -345,6 +345,7 @@ public func usernameSetupController(context: AccountContext) -> ViewController {
}
let controller = ItemListController(context: context, state: signal)
controller.navigationPresentation = .modal
controller.enableInteractiveDismiss = true
dismissImpl = { [weak controller] in
controller?.view.endEditing(true)

View File

@ -61,6 +61,13 @@ extension PeekControllerTheme {
public extension NavigationControllerTheme {
convenience init(presentationTheme: PresentationTheme) {
self.init(navigationBar: NavigationBarTheme(rootControllerTheme: presentationTheme), emptyAreaColor: presentationTheme.chatList.backgroundColor)
let navigationStatusBar: NavigationStatusBarStyle
switch presentationTheme.rootController.statusBarStyle {
case .black:
navigationStatusBar = .black
case .white:
navigationStatusBar = .white
}
self.init(statusBar: navigationStatusBar, navigationBar: NavigationBarTheme(rootControllerTheme: presentationTheme), emptyAreaColor: presentationTheme.chatList.backgroundColor)
}
}

View File

@ -58,7 +58,15 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
self.theme = theme
self.openUrl = openUrl
super.init(mode: .single, theme: NavigationControllerTheme(navigationBar: AuthorizationSequenceController.navigationBarTheme(theme), emptyAreaColor: .black))
let navigationStatusBar: NavigationStatusBarStyle
switch theme.rootController.statusBarStyle {
case .black:
navigationStatusBar = .black
case .white:
navigationStatusBar = .white
}
super.init(mode: .single, theme: NavigationControllerTheme(statusBar: navigationStatusBar, navigationBar: AuthorizationSequenceController.navigationBarTheme(theme), emptyAreaColor: .black))
self.stateDisposable = (account.postbox.stateView()
|> map { view -> InnerState in

View File

@ -987,7 +987,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}, openTheme: { [weak self] message in
if let strongSelf = self, strongSelf.isNodeLoaded, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) {
strongSelf.chatDisplayNode.dismissInput()
openChatTheme(context: strongSelf.context, message: message, present: { [weak self] c, a in
openChatTheme(context: strongSelf.context, message: message, pushController: { [weak self] c in
(self?.navigationController as? NavigationController)?.pushViewController(c)
}, present: { [weak self] c, a in
self?.present(c, in: .window(.root), with: a, blockInteraction: true)
})
}
@ -5265,7 +5267,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let _ = legacyAssetPicker(context: strongSelf.context, presentationData: strongSelf.presentationData, editingMedia: editingMedia, fileMode: fileMode, peer: peer, saveEditedPhotos: settings.storeEditedPhotos, allowGrouping: true, selectionLimit: selectionLimit).start(next: { generator in
if let strongSelf = self {
let legacyController = LegacyController(presentation: .modal(animateIn: true), theme: strongSelf.presentationData.theme, initialLayout: strongSelf.validLayout)
let legacyController = LegacyController(presentation: .navigation, theme: strongSelf.presentationData.theme, initialLayout: strongSelf.validLayout)
legacyController.navigationPresentation = .modal
legacyController.statusBar.statusBarStyle = strongSelf.presentationData.theme.rootController.statusBarStyle.style
legacyController.controllerLoaded = { [weak legacyController] in
legacyController?.view.disablesInteractiveTransitionGestureRecognizer = true
@ -5290,7 +5293,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
})
}))
strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller)
}
}, presentSelectionLimitExceeded: {
guard let strongSelf = self else {
@ -5330,7 +5333,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
strongSelf.chatDisplayNode.dismissInput()
strongSelf.present(legacyController, in: .window(.root))
legacyController.navigationPresentation = .modal
(strongSelf.navigationController as? NavigationController)?.pushViewController(legacyController)
}
})
})
@ -5385,7 +5389,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
strongSelf.chatDisplayNode.dismissInput()
strongSelf.present(legacyLocationPickerController(context: strongSelf.context, selfPeer: selfPeer, peer: peer, sendLocation: { coordinate, venue, _ in
(strongSelf.navigationController as? NavigationController)?.pushViewController(legacyLocationPickerController(context: strongSelf.context, selfPeer: selfPeer, peer: peer, sendLocation: { coordinate, venue, _ in
guard let strongSelf = self else {
return
}
@ -5422,15 +5426,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
strongSelf.sendMessages([message])
}
}, theme: strongSelf.presentationData.theme, hasLiveLocation: !strongSelf.presentationInterfaceState.isScheduledMessages), in: .window(.root))
}, theme: strongSelf.presentationData.theme, hasLiveLocation: !strongSelf.presentationInterfaceState.isScheduledMessages))
})
}
private func presentContactPicker() {
let contactsController = ContactSelectionControllerImpl(ContactSelectionControllerParams(context: self.context, title: { $0.Contacts_Title }, displayDeviceContacts: true))
contactsController.navigationPresentation = .modal
self.chatDisplayNode.dismissInput()
self.present(contactsController, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
self.controllerNavigationDisposable.set((contactsController.result |> deliverOnMainQueue).start(next: { [weak self] peer in
(self.navigationController as? NavigationController)?.pushViewController(contactsController)
self.controllerNavigationDisposable.set((contactsController.result
|> deliverOnMainQueue).start(next: { [weak self] peer in
if let strongSelf = self, let peer = peer {
let dataSignal: Signal<(Peer?, DeviceContactExtendedData?), NoError>
switch peer {
@ -5488,7 +5494,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil)
strongSelf.sendMessages([message])
} else {
strongSelf.present(strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .filter(peer: peerAndContactData.0, contactId: nil, contactData: contactData, completion: { peer, contactData in
let contactController = strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .filter(peer: peerAndContactData.0, contactId: nil, contactData: contactData, completion: { peer, contactData in
guard let strongSelf = self, !contactData.basicData.phoneNumbers.isEmpty else {
return
}
@ -5506,7 +5512,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil)
strongSelf.sendMessages([message])
}
}), completed: nil, cancelled: nil), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}), completed: nil, cancelled: nil)
(strongSelf.navigationController as? NavigationController)?.pushViewController(contactController)
}
}
}))
@ -5516,7 +5523,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
private func presentPollCreation() {
if case let .peer(peerId) = self.chatLocation {
self.present(createPollController(context: self.context, peerId: peerId, completion: { [weak self] message in
(self.navigationController as? NavigationController)?.pushViewController(createPollController(context: self.context, peerId: peerId, completion: { [weak self] message in
guard let strongSelf = self else {
return
}
@ -5529,7 +5536,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
})
strongSelf.sendMessages([message.withUpdatedReplyToMessageId(replyMessageId)])
}), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}))
}
}
@ -6345,7 +6352,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
self.chatDisplayNode.dismissInput()
self.present(controller, in: .window(.root), blockInteraction: true)
(self.navigationController as? NavigationController)?.pushViewController(controller)
}
private func openPeer(peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer, fromMessage: Message?) {
@ -6462,7 +6469,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
}
self.chatDisplayNode.dismissInput()
self.present(controller, in: .window(.root))
(self.navigationController as? NavigationController)?.pushViewController(controller)
}
default:
break
@ -7698,17 +7705,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
private func openScheduledMessages() {
let controller = ChatControllerImpl(context: self.context, chatLocation: self.chatLocation, subject: .scheduledMessages)
controller.navigationPresentation = .modal
(self.navigationController as? NavigationController)?.pushViewController(controller)
//self.present(controller, in: .window(.root))
}
override public func dismiss(completion: (() -> Void)? = nil) {
if !self.isDismissed {
self.isDismissed = true
self.chatDisplayNode.animateOut(completion: { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil)
})
self.updateTransitionWhenPresentedAsModal?(0.0, .animated(duration: 0.2, curve: .easeInOut))
}
}
}

View File

@ -40,13 +40,15 @@ public class ComposeController: ViewController {
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
self.navigationPresentation = .modal
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
self.title = self.presentationData.strings.Compose_NewMessage
self.isModalWhenInOverlay = true
//self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(cancelPressed))
@ -263,6 +265,6 @@ public class ComposeController: ViewController {
}
@objc private func cancelPressed() {
self.presentingViewController?.dismiss(animated: true, completion: nil)
(self.navigationController as? NavigationController)?.filterController(self, animated: true)
}
}

View File

@ -271,19 +271,6 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
}))
}
override open func dismiss(completion: (() -> Void)? = nil) {
if let presentationArguments = self.presentationArguments as? ViewControllerPresentationArguments {
switch presentationArguments.presentationAnimation {
case .modalSheet:
self.dismissed?()
self.contactsNode.animateOut(completion: completion)
case .none:
self.dismissed?()
completion?()
}
}
}
func dismissSearch() {
self.deactivateSearch()
}

View File

@ -261,12 +261,8 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
}, stopLiveLocation: {
params.context.liveLocationManager?.cancelLiveLocation(peerId: params.message.id.peerId)
}, openUrl: params.openUrl)
if params.modal {
params.present(controller, nil)
} else {
params.navigationController?.pushViewController(controller)
}
controller.navigationPresentation = .modal
params.navigationController?.pushViewController(controller)
return true
case let .stickerPack(reference):
let controller = StickerPackPreviewController(context: params.context, stickerPack: reference, parentNavigationController: params.navigationController)
@ -381,7 +377,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
return false
}
let controller = ThemePreviewController(context: params.context, previewTheme: theme, source: .media(.message(message: MessageReference(params.message), media: media)))
params.present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
params.navigationController?.pushViewController(controller)
}
}
return false
@ -430,6 +426,7 @@ func openChatInstantPage(context: AccountContext, message: Message, sourcePeerTy
}
let pageController = InstantPageController(context: context, webPage: webpage, sourcePeerType: sourcePeerType ?? .channel, anchor: anchor)
pageController.navigationPresentation = .modal
navigationController.pushViewController(pageController)
}
break
@ -459,7 +456,7 @@ func openChatWallpaper(context: AccountContext, message: Message, present: @esca
}
}
func openChatTheme(context: AccountContext, message: Message, present: @escaping (ViewController, Any?) -> Void) {
func openChatTheme(context: AccountContext, message: Message, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void) {
for media in message.media {
if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
let _ = (context.sharedContext.resolveUrl(account: context.account, url: content.url)
@ -474,7 +471,7 @@ func openChatTheme(context: AccountContext, message: Message, present: @escaping
if case let .theme(slug) = resolvedUrl, let file = file {
if let path = context.sharedContext.accountManager.mediaBox.completedResourcePath(file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead), let theme = makePresentationTheme(data: data) {
let controller = ThemePreviewController(context: context, previewTheme: theme, source: .slug(slug, file))
present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
pushController(controller)
}
} else {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }

View File

@ -81,7 +81,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
}
}
dismissInput()
present(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
navigationController?.pushViewController(controller)
case let .channelMessage(peerId, messageId):
openPeer(peerId, .chat(textInputState: nil, subject: .message(messageId)))
case let .stickerPack(name):
@ -203,10 +203,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
continueWithPeer(peerId)
}
}
if let navigationController = navigationController {
context.sharedContext.applicationBindings.dismissNativeController()
(navigationController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
}
navigationController?.pushViewController(controller)
}
case let .wallpaper(parameter):
let presentationData = context.sharedContext.currentPresentationData.with { $0 }

View File

@ -59,6 +59,8 @@ public class PeerMediaCollectionController: TelegramBaseController {
super.init(context: context, navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: self.presentationData.theme).withUpdatedSeparatorColor(self.presentationData.theme.rootController.navigationBar.backgroundColor), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)), mediaAccessoryPanelVisibility: .specific(size: .compact), locationBroadcastPanelSource: .none)
self.navigationPresentation = .modalInLargeLayout
self.title = self.presentationData.strings.SharedMedia_TitleAll
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
@ -105,7 +107,18 @@ public class PeerMediaCollectionController: TelegramBaseController {
return nil
}, addToTransitionSurface: { view in
if let strongSelf = self {
strongSelf.mediaCollectionDisplayNode.view.insertSubview(view, aboveSubview: strongSelf.mediaCollectionDisplayNode.historyNode.view)
var belowSubview: UIView?
if let historyNode = strongSelf.mediaCollectionDisplayNode.historyNode as? ChatHistoryGridNode {
if let lowestSectionNode = historyNode.lowestSectionNode() {
belowSubview = lowestSectionNode.view
}
}
strongSelf.mediaCollectionDisplayNode.historyNode
if let belowSubview = belowSubview {
strongSelf.mediaCollectionDisplayNode.historyNode.view.insertSubview(view, belowSubview: belowSubview)
} else {
strongSelf.mediaCollectionDisplayNode.historyNode.view.addSubview(view)
}
}
}, openUrl: { url in
self?.openUrl(url)
@ -823,7 +836,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
})
}
}
strongSelf.present(controller, in: .window(.root))
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller)
})
}

View File

@ -59,6 +59,9 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
self.navigationPresentation = .modal
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
self.customTitle = params.title
@ -178,7 +181,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.peerSelectionNode.animateIn()
//self.peerSelectionNode.animateIn()
}
override public func viewDidDisappear(_ animated: Bool) {
@ -215,9 +218,4 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
}
}
}
override public func dismiss(completion: (() -> Void)? = nil) {
self.peerSelectionNode.view.endEditing(true)
self.peerSelectionNode.animateOut(completion: completion)
}
}

View File

@ -924,7 +924,9 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}
public func makePeerInfoController(context: AccountContext, peer: Peer, mode: PeerInfoControllerMode) -> ViewController? {
return peerInfoControllerImpl(context: context, peer: peer, mode: mode)
let controller = peerInfoControllerImpl(context: context, peer: peer, mode: mode)
controller?.navigationPresentation = .modalInLargeLayout
return controller
}
public func openExternalUrl(context: AccountContext, urlContext: OpenURLContext, url: String, forceExternal: Bool, presentationData: PresentationData, navigationController: NavigationController?, dismissInput: @escaping () -> Void) {
@ -995,8 +997,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
openAddContactImpl(context: context, firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, label: label, present: present, pushController: pushController, completed: completed)
}
public func openAddPersonContact(context: AccountContext, peerId: PeerId, present: @escaping (ViewController, Any?) -> Void) {
openAddPersonContactImpl(context: context, peerId: peerId, present: present)
public func openAddPersonContact(context: AccountContext, peerId: PeerId, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void) {
openAddPersonContactImpl(context: context, peerId: peerId, pushController: pushController, present: present)
}
public func makeCreateGroupController(context: AccountContext, peerIds: [PeerId], initialTitle: String?, mode: CreateGroupMode, completion: ((PeerId, @escaping () -> Void) -> Void)?) -> ViewController {

View File

@ -472,12 +472,4 @@ public final class WebSearchController: ViewController {
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition)
}
override public func dismiss(completion: (() -> Void)? = nil) {
self.navigationContentNode?.deactivate()
self.controllerNode.animateOut(completion: { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil)
completion?()
})
}
}