mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-02 00:17:02 +00:00
Merge branch 'navigation'
This commit is contained in:
commit
afe71368b0
10
BUCK
10
BUCK
@ -45,6 +45,7 @@ resource_dependencies = [
|
||||
"//submodules/TelegramUI:TelegramUIResources",
|
||||
"//:AppResources",
|
||||
"//:AppStringResources",
|
||||
"//:InfoPlistStringResources",
|
||||
"//:AppIntentVocabularyResources",
|
||||
"//:Icons",
|
||||
"//:AdditionalIcons",
|
||||
@ -80,6 +81,15 @@ apple_resource(
|
||||
visibility = ["PUBLIC"],
|
||||
)
|
||||
|
||||
apple_resource(
|
||||
name = "InfoPlistStringResources",
|
||||
files = [],
|
||||
variants = glob([
|
||||
"Telegram-iOS/*.lproj/InfoPlist.strings",
|
||||
]),
|
||||
visibility = ["PUBLIC"],
|
||||
)
|
||||
|
||||
apple_asset_catalog(
|
||||
name = "Icons",
|
||||
dirs = [
|
||||
|
@ -114,7 +114,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, source: ChatC
|
||||
if let (group, index) = groupAndIndex {
|
||||
if !isSavedMessages {
|
||||
let isArchived = group == Namespaces.PeerGroup.archive
|
||||
items.append(.action(ContextMenuActionItem(text: isArchived ? strings.ChatList_Context_Archive : strings.ChatList_Context_Unarchive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isArchived ? "Chat/Context Menu/Unarchive" : "Chat/Context Menu/Archive"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||
items.append(.action(ContextMenuActionItem(text: isArchived ? strings.ChatList_Context_Unarchive : strings.ChatList_Context_Archive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isArchived ? "Chat/Context Menu/Unarchive" : "Chat/Context Menu/Archive"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||
if isArchived {
|
||||
let _ = (context.account.postbox.transaction { transaction -> Void in
|
||||
updatePeerGroupIdInteractively(transaction: transaction, peerId: peerId, groupId: .root)
|
||||
|
@ -509,6 +509,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
if let strongSelf = self {
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
let chatListController = ChatListControllerImpl(context: strongSelf.context, groupId: groupId, controlsHistoryPreload: false, enableDebugActions: false)
|
||||
chatListController.navigationPresentation = .master
|
||||
navigationController.pushViewController(chatListController)
|
||||
strongSelf.chatListDisplayNode.chatListNode.clearHighlightAnimated(true)
|
||||
}
|
||||
@ -664,6 +665,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
switch item.content {
|
||||
case let .groupReference(groupReference):
|
||||
let chatListController = ChatListControllerImpl(context: strongSelf.context, groupId: groupReference.groupId, controlsHistoryPreload: false, enableDebugActions: false)
|
||||
chatListController.navigationPresentation = .master
|
||||
let contextController = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupReference.groupId, chatListController: strongSelf), reactionItems: [], gesture: gesture)
|
||||
strongSelf.presentInGlobalOverlay(contextController)
|
||||
case let .peer(peer):
|
||||
@ -1094,6 +1096,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
}
|
||||
case let .groupReference(groupId, _, _, _, _):
|
||||
let chatListController = ChatListControllerImpl(context: self.context, groupId: groupId, controlsHistoryPreload: false, enableDebugActions: false)
|
||||
chatListController.navigationPresentation = .master
|
||||
chatListController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
|
||||
return (chatListController, sourceRect)
|
||||
}
|
||||
|
@ -281,7 +281,8 @@ public class ContactsController: ViewController {
|
||||
}
|
||||
let presentPeersNearby = {
|
||||
let controller = strongSelf.context.sharedContext.makePeersNearbyController(context: strongSelf.context)
|
||||
(strongSelf.navigationController as? NavigationController)?.replaceAllButRootController(controller, animated: true, completion: { [weak self] in
|
||||
controller.navigationPresentation = .master
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller, animated: true, completion: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true)
|
||||
}
|
||||
@ -294,6 +295,7 @@ public class ContactsController: ViewController {
|
||||
default:
|
||||
let controller = PermissionController(context: strongSelf.context, splashScreen: false)
|
||||
controller.setState(.permission(.nearbyLocation(status: PermissionRequestStatus(accessType: status))), animated: false)
|
||||
controller.navigationPresentation = .master
|
||||
controller.proceed = { result in
|
||||
if result {
|
||||
presentPeersNearby()
|
||||
|
@ -10,6 +10,7 @@ private enum ContextItemNode {
|
||||
}
|
||||
|
||||
final class ContextActionsContainerNode: ASDisplayNode {
|
||||
private var effectView: UIVisualEffectView?
|
||||
private var itemNodes: [ContextItemNode]
|
||||
|
||||
init(theme: PresentationTheme, items: [ContextMenuItem], getController: @escaping () -> ContextController?, actionSelected: @escaping (ContextMenuActionResult) -> Void) {
|
||||
@ -51,8 +52,28 @@ final class ContextActionsContainerNode: ASDisplayNode {
|
||||
})
|
||||
}
|
||||
|
||||
func updateLayout(constrainedWidth: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
let minActionsWidth = min(constrainedWidth, max(250.0, floor(constrainedWidth / 3.0)))
|
||||
func updateLayout(widthClass: ContainerViewLayoutSizeClass, constrainedWidth: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
var minActionsWidth: CGFloat = 250.0
|
||||
switch widthClass {
|
||||
case .compact:
|
||||
minActionsWidth = max(minActionsWidth, floor(constrainedWidth / 3.0))
|
||||
if let effectView = self.effectView {
|
||||
self.effectView = nil
|
||||
effectView.removeFromSuperview()
|
||||
}
|
||||
case .regular:
|
||||
if self.effectView == nil {
|
||||
let effectView: UIVisualEffectView
|
||||
if #available(iOS 10.0, *) {
|
||||
effectView = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
|
||||
} else {
|
||||
effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
|
||||
}
|
||||
self.effectView = effectView
|
||||
self.view.insertSubview(effectView, at: 0)
|
||||
}
|
||||
}
|
||||
minActionsWidth = min(minActionsWidth, constrainedWidth)
|
||||
let separatorHeight: CGFloat = 8.0
|
||||
|
||||
var maxWidth: CGFloat = 0.0
|
||||
@ -111,7 +132,11 @@ final class ContextActionsContainerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
return CGSize(width: maxWidth, height: verticalOffset)
|
||||
let size = CGSize(width: maxWidth, height: verticalOffset)
|
||||
if let effectView = self.effectView {
|
||||
transition.updateFrame(view: effectView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func updateTheme(theme: PresentationTheme) {
|
||||
|
@ -27,9 +27,11 @@ public final class ContextExtractedContentNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
final class ContextControllerContentNode: ASDisplayNode {
|
||||
let sourceNode: ASDisplayNode
|
||||
let controller: ViewController
|
||||
|
||||
init(controller: ViewController) {
|
||||
init(sourceNode: ASDisplayNode, controller: ViewController) {
|
||||
self.sourceNode = sourceNode
|
||||
self.controller = controller
|
||||
|
||||
super.init()
|
||||
|
@ -80,6 +80,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
private var propertyAnimator: AnyObject?
|
||||
private var displayLinkAnimator: DisplayLinkAnimator?
|
||||
private let dimNode: ASDisplayNode
|
||||
private let withoutBlurDimNode: ASDisplayNode
|
||||
private let dismissNode: ASDisplayNode
|
||||
|
||||
private let clippingNode: ASDisplayNode
|
||||
@ -132,6 +133,10 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
self.dimNode.backgroundColor = theme.contextMenu.dimColor
|
||||
self.dimNode.alpha = 0.0
|
||||
|
||||
self.withoutBlurDimNode = ASDisplayNode()
|
||||
self.withoutBlurDimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
|
||||
self.withoutBlurDimNode.alpha = 0.0
|
||||
|
||||
self.dismissNode = ASDisplayNode()
|
||||
|
||||
self.clippingNode = ASDisplayNode()
|
||||
@ -164,8 +169,8 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
|
||||
self.scrollNode.view.delegate = self
|
||||
|
||||
self.view.addSubview(self.effectView)
|
||||
self.addSubnode(self.dimNode)
|
||||
self.addSubnode(self.withoutBlurDimNode)
|
||||
|
||||
self.addSubnode(self.clippingNode)
|
||||
|
||||
@ -437,14 +442,14 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
self.originalProjectedContentViewFrame = (convertFrame(takenViewInfo.contentContainingNode.frame, from: parentSupernode.view, to: self.view), convertFrame(takenViewInfo.contentContainingNode.contentRect, from: takenViewInfo.contentContainingNode.view, to: self.view))
|
||||
}
|
||||
case let .controller(source):
|
||||
let contentParentNode = ContextControllerContentNode(controller: source.controller)
|
||||
self.contentContainerNode.contentNode = .controller(contentParentNode)
|
||||
self.contentContainerNode.clipsToBounds = true
|
||||
self.contentContainerNode.cornerRadius = 14.0
|
||||
self.contentContainerNode.addSubnode(contentParentNode)
|
||||
|
||||
let transitionInfo = source.transitionInfo()
|
||||
if let transitionInfo = transitionInfo, let (sourceNode, sourceNodeRect) = transitionInfo.sourceNode() {
|
||||
let contentParentNode = ContextControllerContentNode(sourceNode: sourceNode, controller: source.controller)
|
||||
self.contentContainerNode.contentNode = .controller(contentParentNode)
|
||||
self.contentContainerNode.clipsToBounds = true
|
||||
self.contentContainerNode.cornerRadius = 14.0
|
||||
self.contentContainerNode.addSubnode(contentParentNode)
|
||||
|
||||
let projectedFrame = convertFrame(sourceNodeRect, from: sourceNode.view, to: self.view)
|
||||
self.originalProjectedContentViewFrame = (projectedFrame, projectedFrame)
|
||||
}
|
||||
@ -483,8 +488,13 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
self.updateLayout(layout: validLayout, transition: .immediate, previousActionsContainerNode: nil)
|
||||
}
|
||||
|
||||
self.dimNode.alpha = 1.0
|
||||
self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor)
|
||||
if !self.dimNode.isHidden {
|
||||
self.dimNode.alpha = 1.0
|
||||
self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor)
|
||||
} else {
|
||||
self.withoutBlurDimNode.alpha = 1.0
|
||||
self.withoutBlurDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor)
|
||||
}
|
||||
|
||||
if #available(iOS 10.0, *) {
|
||||
if let propertyAnimator = self.propertyAnimator {
|
||||
@ -662,7 +672,12 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
})
|
||||
}
|
||||
|
||||
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false)
|
||||
if !self.dimNode.isHidden {
|
||||
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false)
|
||||
} else {
|
||||
self.withoutBlurDimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2 * animationDurationFactor, removeOnCompletion: false, completion: { _ in
|
||||
completedActionsNode = true
|
||||
intermediateCompletion()
|
||||
@ -799,7 +814,11 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
})
|
||||
}
|
||||
|
||||
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false)
|
||||
if !self.dimNode.isHidden {
|
||||
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false)
|
||||
} else {
|
||||
self.withoutBlurDimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false)
|
||||
}
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2 * animationDurationFactor, removeOnCompletion: false, completion: { _ in
|
||||
completedActionsNode = true
|
||||
intermediateCompletion()
|
||||
@ -872,10 +891,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
strongSelf.reactionContextNode = nil
|
||||
reactionCompleted = true
|
||||
intermediateCompletion()
|
||||
/*strongSelf.animateOut(result: .default, completion: {
|
||||
reactionCompleted = true
|
||||
intermediateCompletion()
|
||||
})*/
|
||||
})
|
||||
}
|
||||
|
||||
@ -939,6 +954,31 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
|
||||
transition.updateFrame(view: self.effectView, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
transition.updateFrame(node: self.withoutBlurDimNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
|
||||
switch layout.metrics.widthClass {
|
||||
case .compact:
|
||||
if self.effectView.superview == nil {
|
||||
self.view.insertSubview(self.effectView, at: 0)
|
||||
if #available(iOS 10.0, *) {
|
||||
if let propertyAnimator = self.propertyAnimator {
|
||||
let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator
|
||||
propertyAnimator?.stopAnimation(true)
|
||||
}
|
||||
}
|
||||
self.effectView.effect = makeCustomZoomBlurEffect()
|
||||
self.dimNode.alpha = 1.0
|
||||
}
|
||||
self.dimNode.isHidden = false
|
||||
self.withoutBlurDimNode.isHidden = true
|
||||
case .regular:
|
||||
if self.effectView.superview != nil {
|
||||
self.effectView.removeFromSuperview()
|
||||
self.withoutBlurDimNode.alpha = 1.0
|
||||
}
|
||||
self.dimNode.isHidden = true
|
||||
self.withoutBlurDimNode.isHidden = false
|
||||
}
|
||||
transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
|
||||
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
@ -958,7 +998,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
let isInitialLayout = self.actionsContainerNode.frame.size.width.isZero
|
||||
let previousContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
|
||||
|
||||
let actionsSize = self.actionsContainerNode.updateLayout(constrainedWidth: layout.size.width - actionsSideInset * 2.0, transition: actionsContainerTransition)
|
||||
let actionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, constrainedWidth: layout.size.width - actionsSideInset * 2.0, transition: actionsContainerTransition)
|
||||
let contentSize = originalProjectedContentViewFrame.1.size
|
||||
self.contentContainerNode.updateLayout(size: contentSize, scaledSize: contentSize, transition: transition)
|
||||
|
||||
@ -1006,65 +1046,105 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}
|
||||
}
|
||||
case let .controller(contentParentNode):
|
||||
let contentActionsSpacing: CGFloat = actionsSideInset
|
||||
let topEdge = max(contentTopInset, self.contentAreaInScreenSpace?.minY ?? 0.0)
|
||||
let projectedFrame = convertFrame(contentParentNode.sourceNode.bounds, from: contentParentNode.sourceNode.view, to: self.view)
|
||||
self.originalProjectedContentViewFrame = (projectedFrame, projectedFrame)
|
||||
|
||||
//contentParentNode.updateLayout(size: layout.size, transition: transition)
|
||||
|
||||
let isInitialLayout = self.actionsContainerNode.frame.size.width.isZero
|
||||
let previousContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
|
||||
|
||||
let actionsSize = self.actionsContainerNode.updateLayout(constrainedWidth: layout.size.width - actionsSideInset * 2.0, transition: actionsContainerTransition)
|
||||
let contentScale = (layout.size.width - actionsSideInset * 2.0) / layout.size.width
|
||||
let contentUnscaledSize: CGSize
|
||||
if !contentParentNode.controller.preferredContentSize.width.isZero {
|
||||
contentUnscaledSize = contentParentNode.controller.preferredContentSize
|
||||
} else {
|
||||
let proposedContentHeight = layout.size.height - topEdge - contentActionsSpacing - actionsSize.height - layout.intrinsicInsets.bottom - actionsBottomInset
|
||||
contentUnscaledSize = CGSize(width: layout.size.width, height: max(400.0, proposedContentHeight))
|
||||
}
|
||||
let contentSize = CGSize(width: floor(contentUnscaledSize.width * contentScale), height: floor(contentUnscaledSize.height * contentScale))
|
||||
|
||||
self.contentContainerNode.updateLayout(size: contentUnscaledSize, scaledSize: contentSize, transition: transition)
|
||||
|
||||
let maximumActionsFrameOrigin = max(60.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - actionsSize.height)
|
||||
var originalActionsFrame = CGRect(origin: CGPoint(x: actionsSideInset, y: min(maximumActionsFrameOrigin, floor((layout.size.height - contentActionsSpacing - contentSize.height) / 2.0) + contentSize.height + contentActionsSpacing)), size: actionsSize)
|
||||
var originalContentFrame = CGRect(origin: CGPoint(x: actionsSideInset, y: originalActionsFrame.minY - contentActionsSpacing - contentSize.height), size: contentSize)
|
||||
if originalContentFrame.minY < topEdge {
|
||||
let requiredOffset = topEdge - originalContentFrame.minY
|
||||
let availableOffset = max(0.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - originalActionsFrame.maxY)
|
||||
let offset = min(requiredOffset, availableOffset)
|
||||
originalActionsFrame = originalActionsFrame.offsetBy(dx: 0.0, dy: offset)
|
||||
originalContentFrame = originalContentFrame.offsetBy(dx: 0.0, dy: offset)
|
||||
}
|
||||
|
||||
let contentHeight = max(layout.size.height, max(layout.size.height, originalActionsFrame.maxY + actionsBottomInset) - originalContentFrame.minY + contentTopInset)
|
||||
|
||||
let scrollContentSize = CGSize(width: layout.size.width, height: contentHeight)
|
||||
if self.scrollNode.view.contentSize != scrollContentSize {
|
||||
self.scrollNode.view.contentSize = scrollContentSize
|
||||
}
|
||||
|
||||
let overflowOffset = min(0.0, originalContentFrame.minY - contentTopInset)
|
||||
|
||||
let contentContainerFrame = originalContentFrame
|
||||
transition.updateFrame(node: self.contentContainerNode, frame: contentContainerFrame.offsetBy(dx: 0.0, dy: -overflowOffset))
|
||||
actionsContainerTransition.updateFrame(node: self.actionsContainerNode, frame: originalActionsFrame.offsetBy(dx: 0.0, dy: -overflowOffset))
|
||||
|
||||
if isInitialLayout {
|
||||
self.scrollNode.view.contentOffset = CGPoint(x: 0.0, y: -overflowOffset)
|
||||
let currentContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
|
||||
if overflowOffset < 0.0 {
|
||||
transition.animateOffsetAdditive(node: self.scrollNode, offset: currentContainerFrame.minY - previousContainerFrame.minY)
|
||||
if let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
|
||||
let contentActionsSpacing: CGFloat = actionsSideInset
|
||||
let topEdge = max(contentTopInset, self.contentAreaInScreenSpace?.minY ?? 0.0)
|
||||
|
||||
let isInitialLayout = self.actionsContainerNode.frame.size.width.isZero
|
||||
let previousContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
|
||||
|
||||
let actionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, constrainedWidth: layout.size.width - actionsSideInset * 2.0, transition: actionsContainerTransition)
|
||||
let contentScale = (layout.size.width - actionsSideInset * 2.0) / layout.size.width
|
||||
var contentUnscaledSize: CGSize
|
||||
if case .compact = layout.metrics.widthClass {
|
||||
let proposedContentHeight = layout.size.height - topEdge - contentActionsSpacing - actionsSize.height - layout.intrinsicInsets.bottom - actionsBottomInset
|
||||
contentUnscaledSize = CGSize(width: layout.size.width, height: max(400.0, proposedContentHeight))
|
||||
|
||||
if let preferredSize = contentParentNode.controller.preferredContentSizeForLayout(ContainerViewLayout(size: contentUnscaledSize, metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false)) {
|
||||
contentUnscaledSize = preferredSize
|
||||
}
|
||||
} else {
|
||||
let proposedContentHeight = layout.size.height - topEdge - contentActionsSpacing - actionsSize.height - layout.intrinsicInsets.bottom - actionsBottomInset
|
||||
contentUnscaledSize = CGSize(width: min(layout.size.width, 340.0), height: min(568.0, proposedContentHeight))
|
||||
|
||||
if let preferredSize = contentParentNode.controller.preferredContentSizeForLayout(ContainerViewLayout(size: contentUnscaledSize, metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false)) {
|
||||
contentUnscaledSize = preferredSize
|
||||
}
|
||||
}
|
||||
let contentSize = CGSize(width: floor(contentUnscaledSize.width * contentScale), height: floor(contentUnscaledSize.height * contentScale))
|
||||
|
||||
self.contentContainerNode.updateLayout(size: contentUnscaledSize, scaledSize: contentSize, transition: transition)
|
||||
|
||||
let maximumActionsFrameOrigin = max(60.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - actionsSize.height)
|
||||
var originalActionsFrame: CGRect
|
||||
var originalContentFrame: CGRect
|
||||
var contentHeight: CGFloat
|
||||
if case .compact = layout.metrics.widthClass {
|
||||
originalActionsFrame = CGRect(origin: CGPoint(x: actionsSideInset, y: min(maximumActionsFrameOrigin, floor((layout.size.height - contentActionsSpacing - contentSize.height) / 2.0) + contentSize.height + contentActionsSpacing)), size: actionsSize)
|
||||
originalContentFrame = CGRect(origin: CGPoint(x: actionsSideInset, y: originalActionsFrame.minY - contentActionsSpacing - contentSize.height), size: contentSize)
|
||||
if originalContentFrame.minY < topEdge {
|
||||
let requiredOffset = topEdge - originalContentFrame.minY
|
||||
let availableOffset = max(0.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - originalActionsFrame.maxY)
|
||||
let offset = min(requiredOffset, availableOffset)
|
||||
originalActionsFrame = originalActionsFrame.offsetBy(dx: 0.0, dy: offset)
|
||||
originalContentFrame = originalContentFrame.offsetBy(dx: 0.0, dy: offset)
|
||||
}
|
||||
contentHeight = max(layout.size.height, max(layout.size.height, originalActionsFrame.maxY + actionsBottomInset) - originalContentFrame.minY + contentTopInset)
|
||||
} else {
|
||||
originalContentFrame = CGRect(origin: CGPoint(x: floor(originalProjectedContentViewFrame.1.midX - contentSize.width / 2.0), y: floor(originalProjectedContentViewFrame.1.midY - contentSize.height / 2.0)), size: contentSize)
|
||||
originalContentFrame.origin.x = min(originalContentFrame.origin.x, layout.size.width - actionsSideInset - contentSize.width)
|
||||
originalContentFrame.origin.x = max(originalContentFrame.origin.x, actionsSideInset)
|
||||
originalContentFrame.origin.y = min(originalContentFrame.origin.y, layout.size.height - layout.intrinsicInsets.bottom - actionsSideInset - contentSize.height)
|
||||
originalContentFrame.origin.y = max(originalContentFrame.origin.y, contentTopInset)
|
||||
if originalContentFrame.maxX <= layout.size.width - actionsSideInset - actionsSize.width - contentActionsSpacing {
|
||||
originalActionsFrame = CGRect(origin: CGPoint(x: originalContentFrame.maxX + contentActionsSpacing, y: originalContentFrame.minY), size: actionsSize)
|
||||
if originalActionsFrame.maxX > layout.size.width - actionsSideInset {
|
||||
let offset = originalActionsFrame.maxX - (layout.size.width - actionsSideInset)
|
||||
originalActionsFrame.origin.x -= offset
|
||||
originalContentFrame.origin.x -= offset
|
||||
}
|
||||
} else {
|
||||
originalActionsFrame = CGRect(origin: CGPoint(x: originalContentFrame.minX - contentActionsSpacing - actionsSize.width, y: originalContentFrame.minY), size: actionsSize)
|
||||
if originalActionsFrame.minX < actionsSideInset {
|
||||
let offset = actionsSideInset - originalActionsFrame.minX
|
||||
originalActionsFrame.origin.x += offset
|
||||
originalContentFrame.origin.x += offset
|
||||
}
|
||||
}
|
||||
contentHeight = layout.size.height
|
||||
contentHeight = max(contentHeight, originalActionsFrame.maxY + actionsBottomInset)
|
||||
contentHeight = max(contentHeight, originalContentFrame.maxY + actionsBottomInset)
|
||||
}
|
||||
|
||||
let scrollContentSize = CGSize(width: layout.size.width, height: contentHeight)
|
||||
if self.scrollNode.view.contentSize != scrollContentSize {
|
||||
self.scrollNode.view.contentSize = scrollContentSize
|
||||
}
|
||||
|
||||
let overflowOffset = min(0.0, originalContentFrame.minY - contentTopInset)
|
||||
|
||||
let contentContainerFrame = originalContentFrame
|
||||
transition.updateFrame(node: self.contentContainerNode, frame: contentContainerFrame.offsetBy(dx: 0.0, dy: -overflowOffset))
|
||||
actionsContainerTransition.updateFrame(node: self.actionsContainerNode, frame: originalActionsFrame.offsetBy(dx: 0.0, dy: -overflowOffset))
|
||||
|
||||
if isInitialLayout {
|
||||
self.scrollNode.view.contentOffset = CGPoint(x: 0.0, y: -overflowOffset)
|
||||
let currentContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
|
||||
if overflowOffset < 0.0 {
|
||||
transition.animateOffsetAdditive(node: self.scrollNode, offset: currentContainerFrame.minY - previousContainerFrame.minY)
|
||||
}
|
||||
}
|
||||
|
||||
let absoluteContentRect = contentContainerFrame.offsetBy(dx: 0.0, dy: -self.scrollNode.view.contentOffset.y)
|
||||
|
||||
if let reactionContextNode = self.reactionContextNode {
|
||||
let insets = layout.insets(options: [.statusBar])
|
||||
transition.updateFrame(node: reactionContextNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
reactionContextNode.updateLayout(size: layout.size, insets: insets, anchorRect: CGRect(origin: CGPoint(x: absoluteContentRect.minX, y: absoluteContentRect.minY), size: contentSize), transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
let absoluteContentRect = contentContainerFrame.offsetBy(dx: 0.0, dy: -self.scrollNode.view.contentOffset.y)
|
||||
|
||||
if let reactionContextNode = self.reactionContextNode {
|
||||
let insets = layout.insets(options: [.statusBar])
|
||||
transition.updateFrame(node: reactionContextNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
reactionContextNode.updateLayout(size: layout.size, insets: insets, anchorRect: CGRect(origin: CGPoint(x: absoluteContentRect.minX, y: absoluteContentRect.minY), size: contentSize), transition: transition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,21 +23,17 @@ public final class ContextControllerSourceNode: ASDisplayNode {
|
||||
guard let strongSelf = self, !strongSelf.bounds.width.isZero else {
|
||||
return
|
||||
}
|
||||
let minScale: CGFloat = (strongSelf.bounds.width - 20.0) / strongSelf.bounds.width
|
||||
let minScale: CGFloat = (strongSelf.bounds.width - 10.0) / strongSelf.bounds.width
|
||||
let currentScale = 1.0 * (1.0 - progress) + minScale * progress
|
||||
switch update {
|
||||
case .update:
|
||||
strongSelf.layer.sublayerTransform = CATransform3DMakeScale(currentScale, currentScale, 1.0)
|
||||
case .begin:
|
||||
strongSelf.layer.sublayerTransform = CATransform3DMakeScale(currentScale, currentScale, 1.0)
|
||||
/*let previousProgress: CGFloat = 0.0
|
||||
let previousScale = 1.0 * (1.0 - previousProgress) + minScale * previousProgress
|
||||
strongSelf.layer.sublayerTransform = CATransform3DMakeScale(currentScale, currentScale, 1.0)
|
||||
strongSelf.layer.animate(from: previousScale as NSNumber, to: currentScale as NSNumber, keyPath: "sublayerTransform.scale", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.3, additive: false)*/
|
||||
case let .ended(previousProgress):
|
||||
let previousScale = 1.0 * (1.0 - previousProgress) + minScale * previousProgress
|
||||
strongSelf.layer.sublayerTransform = CATransform3DMakeScale(currentScale, currentScale, 1.0)
|
||||
strongSelf.layer.animate(from: previousScale as NSNumber, to: currentScale as NSNumber, keyPath: "sublayerTransform.scale", timingFunction: kCAMediaTimingFunctionSpring, duration: 0.3)
|
||||
strongSelf.layer.animateSpring(from: previousScale as NSNumber, to: currentScale as NSNumber, keyPath: "sublayerTransform.scale", duration: 0.5, delay: 0.0, initialVelocity: 0.0, damping: 90.0)
|
||||
}
|
||||
}
|
||||
contextGesture.activated = { [weak self] gesture in
|
||||
|
@ -40,6 +40,7 @@ private func cancelParentGestures(view: UIView) {
|
||||
public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDelegate {
|
||||
private var currentProgress: CGFloat = 0.0
|
||||
private var delayTimer: Timer?
|
||||
private var animator: DisplayLinkAnimator?
|
||||
private var isValidated: Bool = false
|
||||
|
||||
public var activationProgress: ((CGFloat, ContextGestureTransition) -> Void)?
|
||||
@ -62,6 +63,8 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
|
||||
self.isValidated = false
|
||||
self.externalUpdated = nil
|
||||
self.externalEnded = nil
|
||||
self.animator?.invalidate()
|
||||
self.animator = nil
|
||||
}
|
||||
|
||||
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
@ -80,6 +83,33 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
|
||||
return
|
||||
}
|
||||
strongSelf.isValidated = true
|
||||
if strongSelf.animator == nil {
|
||||
strongSelf.animator = DisplayLinkAnimator(duration: 0.3, from: 0.0, to: 1.0, update: { value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if strongSelf.isValidated {
|
||||
strongSelf.currentProgress = value
|
||||
strongSelf.activationProgress?(value, .update)
|
||||
}
|
||||
}, completion: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
switch strongSelf.state {
|
||||
case .possible:
|
||||
strongSelf.delayTimer?.invalidate()
|
||||
strongSelf.animator?.invalidate()
|
||||
strongSelf.activated?(strongSelf)
|
||||
if let view = strongSelf.view?.superview {
|
||||
cancelParentGestures(view: view)
|
||||
}
|
||||
strongSelf.state = .began
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
strongSelf.activationProgress?(strongSelf.currentProgress, .begin)
|
||||
}, selector: #selector(TimerTargetWrapper.timerEvent), userInfo: nil, repeats: false)
|
||||
self.delayTimer = delayTimer
|
||||
@ -91,7 +121,7 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
|
||||
super.touchesMoved(touches, with: event)
|
||||
|
||||
if let touch = touches.first {
|
||||
if #available(iOS 9.0, *) {
|
||||
/*if #available(iOS 9.0, *) {
|
||||
let maxForce: CGFloat = max(2.5, min(3.0, touch.maximumPossibleForce))
|
||||
let progress = touch.force / maxForce
|
||||
self.currentProgress = progress
|
||||
@ -111,7 +141,7 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
self.externalUpdated?(self.view, touch.location(in: self.view))
|
||||
}
|
||||
@ -131,6 +161,7 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
|
||||
}
|
||||
|
||||
self.delayTimer?.invalidate()
|
||||
self.animator?.invalidate()
|
||||
|
||||
self.state = .failed
|
||||
}
|
||||
@ -145,6 +176,7 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
|
||||
}
|
||||
|
||||
self.delayTimer?.invalidate()
|
||||
self.animator?.invalidate()
|
||||
|
||||
self.state = .failed
|
||||
}
|
||||
@ -154,6 +186,7 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
|
||||
self.activationProgress?(0.0, .ended(self.currentProgress))
|
||||
|
||||
self.delayTimer?.invalidate()
|
||||
self.animator?.invalidate()
|
||||
self.state = .failed
|
||||
}
|
||||
}
|
||||
@ -163,6 +196,7 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
|
||||
let previousProgress = self.currentProgress
|
||||
self.currentProgress = 0.0
|
||||
self.delayTimer?.invalidate()
|
||||
self.animator?.invalidate()
|
||||
self.isValidated = false
|
||||
self.activationProgress?(0.0, .ended(previousProgress))
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ load("//Config:buck_rule_macros.bzl", "framework")
|
||||
framework(
|
||||
name = "Display",
|
||||
srcs = glob([
|
||||
"Display/*.swift",
|
||||
"Display/*.m",
|
||||
"Display/**/*.swift",
|
||||
"Display/**/*.m",
|
||||
]),
|
||||
headers = glob([
|
||||
"Display/*.h",
|
||||
|
@ -22,6 +22,7 @@ public protocol ContainableController: class {
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition)
|
||||
func updateToInterfaceOrientation(_ orientation: UIInterfaceOrientation)
|
||||
func updateModalTransition(_ value: CGFloat, transition: ContainedViewLayoutTransition)
|
||||
func preferredContentSizeForLayout(_ layout: ContainerViewLayout) -> CGSize?
|
||||
|
||||
func viewWillAppear(_ animated: Bool)
|
||||
func viewWillDisappear(_ animated: Bool)
|
||||
|
@ -44,7 +44,12 @@ public final class DisplayLinkAnimator {
|
||||
self.displayLink.invalidate()
|
||||
}
|
||||
|
||||
@objc func tick() {
|
||||
public func invalidate() {
|
||||
self.displayLink.isPaused = true
|
||||
self.displayLink.invalidate()
|
||||
}
|
||||
|
||||
@objc private func tick() {
|
||||
if self.completed {
|
||||
return
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ private final class NavigationControllerView: UITracingLayerView {
|
||||
var inTransition = false
|
||||
|
||||
let sharedStatusBar: StatusBar
|
||||
let masterContainerView: NavigationControllerContainerView
|
||||
let containerView: NavigationControllerContainerView
|
||||
let separatorView: UIView
|
||||
var navigationBackgroundView: UIView?
|
||||
@ -51,24 +52,15 @@ private final class NavigationControllerView: UITracingLayerView {
|
||||
var masterDetailsBlackout: ASDisplayNode?
|
||||
var topControllerNode: ASDisplayNode?
|
||||
|
||||
/*override var accessibilityElements: [Any]? {
|
||||
get {
|
||||
var accessibilityElements: [Any] = []
|
||||
if let topControllerNode = self.topControllerNode {
|
||||
addAccessibilityChildren(of: topControllerNode, container: self, to: &accessibilityElements)
|
||||
}
|
||||
return accessibilityElements
|
||||
} set(value) {
|
||||
}
|
||||
}*/
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.masterContainerView = NavigationControllerContainerView()
|
||||
self.containerView = NavigationControllerContainerView()
|
||||
self.separatorView = UIView()
|
||||
self.sharedStatusBar = StatusBar()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.masterContainerView)
|
||||
self.addSubview(self.containerView)
|
||||
}
|
||||
|
||||
@ -149,6 +141,7 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
private var scheduledLayoutTransitionRequestId: Int = 0
|
||||
private var scheduledLayoutTransitionRequest: (Int, ContainedViewLayoutTransition)?
|
||||
|
||||
private var masterTransitionCoordinator: NavigationTransitionCoordinator?
|
||||
private var navigationTransitionCoordinator: NavigationTransitionCoordinator?
|
||||
|
||||
private var currentPushDisposable = MetaDisposable()
|
||||
@ -235,18 +228,16 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
self.controllerView.separatorView.backgroundColor = theme.navigationBar.separatorColor
|
||||
self.controllerView.navigationBackgroundView?.backgroundColor = theme.navigationBar.backgroundColor
|
||||
self.controllerView.navigationSeparatorView?.backgroundColor = theme.navigationBar.separatorColor
|
||||
// if let emptyDetailView = self.controllerView.emptyDetailView {
|
||||
// emptyDetailView.image = theme.emptyDetailIcon
|
||||
// if let image = theme.emptyDetailIcon {
|
||||
// emptyDetailView.frame = CGRect(origin: CGPoint(x: floor(emptyDetailView.center.x - image.size.width / 2.0), y: floor(emptyDetailView.center.y - image.size.height / 2.0)), size: image.size)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
private var previouslyLaidOutMasterController: UIViewController?
|
||||
private var previouslyLaidOutTopController: UIViewController?
|
||||
|
||||
open func preferredContentSizeForLayout(_ layout: ContainerViewLayout) -> CGSize? {
|
||||
return nil
|
||||
}
|
||||
|
||||
private func layoutConfiguration(for layout: ContainerViewLayout) -> ControllerLayoutConfiguration {
|
||||
switch self.mode {
|
||||
case .single:
|
||||
@ -292,7 +283,7 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
|
||||
switch layoutConfiguration {
|
||||
case .masterDetail:
|
||||
self.viewControllers.first?.view.clipsToBounds = true
|
||||
self.controllerView.masterContainerView.clipsToBounds = true
|
||||
self.controllerView.containerView.clipsToBounds = true
|
||||
let masterData = layoutDataForConfiguration(layoutConfiguration, layout: layout, index: 0)
|
||||
firstControllerFrameAndLayout = masterData
|
||||
@ -419,6 +410,7 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
})
|
||||
}
|
||||
}
|
||||
self.controllerView.masterContainerView.clipsToBounds = false
|
||||
self.controllerView.containerView.clipsToBounds = false
|
||||
lastControllerFrameAndLayout = layoutDataForConfiguration(layoutConfiguration, layout: layout, index: 1)
|
||||
transition.updateFrame(view: self.controllerView.separatorView, frame: CGRect(origin: CGPoint(x: -UIScreenPixel, y: 0.0), size: CGSize(width: UIScreenPixel, height: layout.size.height)), completion: { [weak self] completed in
|
||||
@ -427,6 +419,9 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
}
|
||||
})
|
||||
}
|
||||
if let firstControllerFrameAndLayout = firstControllerFrameAndLayout {
|
||||
transition.updateFrame(view: self.controllerView.masterContainerView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: firstControllerFrameAndLayout.0.size))
|
||||
}
|
||||
transition.updateFrame(view: self.controllerView.containerView, frame: CGRect(origin: CGPoint(x: firstControllerFrameAndLayout?.0.maxX ?? 0.0, y: 0.0), size: lastControllerFrameAndLayout.0.size))
|
||||
|
||||
switch layoutConfiguration {
|
||||
@ -451,11 +446,28 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
}
|
||||
|
||||
var controllersAndFrames: [(Bool, ControllerRecord, ContainerViewLayout)] = []
|
||||
var masterRange: ClosedRange<Int>?
|
||||
if case .masterDetail = layoutConfiguration {
|
||||
for i in 0 ..< self._viewControllers.count {
|
||||
if let controller = self._viewControllers[i].controller as? ViewController {
|
||||
switch controller.navigationPresentation {
|
||||
case .default:
|
||||
break
|
||||
case .master:
|
||||
if let masterRangeValue = masterRange {
|
||||
masterRange = masterRangeValue.lowerBound ... i
|
||||
} else {
|
||||
masterRange = i ... i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for i in 0 ..< self._viewControllers.count {
|
||||
if let controller = self._viewControllers[i].controller as? ViewController {
|
||||
if i == 0 {
|
||||
controller.navigationBar?.previousItem = nil
|
||||
} else if case .masterDetail = layoutConfiguration, i == 1 {
|
||||
} else if let masterRange = masterRange, i == masterRange.upperBound + 1 {
|
||||
controller.navigationBar?.previousItem = .close
|
||||
} else {
|
||||
controller.navigationBar?.previousItem = .item(self.viewControllers[i - 1].navigationItem)
|
||||
@ -468,13 +480,25 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
}
|
||||
self.viewControllers[i].navigation_setNavigationController(self)
|
||||
|
||||
if i == 0, let (_, layout) = firstControllerFrameAndLayout {
|
||||
if let (_, layout) = firstControllerFrameAndLayout, let controller = self._viewControllers[i].controller as? ViewController, case .master = controller.navigationPresentation {
|
||||
controllersAndFrames.append((true, self._viewControllers[i], layout))
|
||||
} else if i == self._viewControllers.count - 1 {
|
||||
controllersAndFrames.append((false, self._viewControllers[i], lastControllerFrameAndLayout.1))
|
||||
}
|
||||
}
|
||||
|
||||
while controllersAndFrames.count >= 2 {
|
||||
if controllersAndFrames[0].0 {
|
||||
if controllersAndFrames[1].0 {
|
||||
controllersAndFrames.remove(at: 0)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var masterController: UIViewController?
|
||||
var appearingMasterController: ControllerRecord?
|
||||
var appearingDetailController: ControllerRecord?
|
||||
@ -506,10 +530,10 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
} else {
|
||||
appearingDetailController = record
|
||||
}
|
||||
} else if record.controller.view.superview !== (isMaster ? self.controllerView : self.controllerView.containerView) {
|
||||
} else if record.controller.view.superview !== (isMaster ? self.controllerView.masterContainerView : self.controllerView.containerView) {
|
||||
record.controller.setIgnoreAppearanceMethodInvocations(true)
|
||||
if isMaster {
|
||||
self.controllerView.insertSubview(record.controller.view, at: 0)
|
||||
self.controllerView.masterContainerView.addSubview(record.controller.view)
|
||||
} else {
|
||||
self.controllerView.containerView.addSubview(record.controller.view)
|
||||
}
|
||||
@ -535,8 +559,76 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
}
|
||||
|
||||
var animatedAppearingDetailController = false
|
||||
var animatedAppearingMasterController = false
|
||||
|
||||
if let previousController = self.previouslyLaidOutTopController, !controllersAndFrames.contains(where: { $0.1.controller === previousController }), previousController.view.superview != nil {
|
||||
if let previousController = self.previouslyLaidOutMasterController, !controllersAndFrames.contains(where: { $0.1.controller === previousController }), previousController.view.superview != nil {
|
||||
if transition.isAnimated, let record = appearingMasterController {
|
||||
animatedAppearingMasterController = true
|
||||
|
||||
previousController.viewWillDisappear(true)
|
||||
record.controller.viewWillAppear(true)
|
||||
record.controller.setIgnoreAppearanceMethodInvocations(true)
|
||||
|
||||
if let controller = record.controller as? ViewController, !controller.hasActiveInput {
|
||||
let (_, controllerLayout) = self.layoutDataForConfiguration(self.layoutConfiguration(for: layout), layout: layout, index: 0)
|
||||
|
||||
let appliedLayout = controllerLayout.withUpdatedInputHeight(controller.hasActiveInput ? controllerLayout.inputHeight : nil)
|
||||
controller.containerLayoutUpdated(appliedLayout, transition: .immediate)
|
||||
}
|
||||
self.controllerView.masterContainerView.addSubview(record.controller.view)
|
||||
record.controller.setIgnoreAppearanceMethodInvocations(false)
|
||||
|
||||
if let _ = previousControllers.firstIndex(where: { $0.controller === record.controller }) {
|
||||
let masterTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self.controllerView.masterContainerView, topView: previousController.view, topNavigationBar: (previousController as? ViewController)?.navigationBar, bottomView: record.controller.view, bottomNavigationBar: (record.controller as? ViewController)?.navigationBar)
|
||||
self.masterTransitionCoordinator = masterTransitionCoordinator
|
||||
|
||||
self.controllerView.inTransition = true
|
||||
masterTransitionCoordinator.animateCompletion(0.0, completion: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.masterTransitionCoordinator = nil
|
||||
strongSelf.controllerView.inTransition = false
|
||||
|
||||
record.controller.viewDidAppear(true)
|
||||
|
||||
previousController.setIgnoreAppearanceMethodInvocations(true)
|
||||
previousController.view.removeFromSuperview()
|
||||
previousController.setIgnoreAppearanceMethodInvocations(false)
|
||||
previousController.viewDidDisappear(true)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if let index = self._viewControllers.firstIndex(where: { $0.controller === previousController }) {
|
||||
self._viewControllers[index].transition = .appearance
|
||||
}
|
||||
let masterTransitionCoordinator = NavigationTransitionCoordinator(transition: .Push, container: self.controllerView.masterContainerView, topView: record.controller.view, topNavigationBar: (record.controller as? ViewController)?.navigationBar, bottomView: previousController.view, bottomNavigationBar: (previousController as? ViewController)?.navigationBar)
|
||||
self.masterTransitionCoordinator = masterTransitionCoordinator
|
||||
|
||||
self.controllerView.inTransition = true
|
||||
masterTransitionCoordinator.animateCompletion(0.0, completion: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
if let index = strongSelf._viewControllers.firstIndex(where: { $0.controller === previousController }) {
|
||||
strongSelf._viewControllers[index].transition = .none
|
||||
}
|
||||
strongSelf.masterTransitionCoordinator = nil
|
||||
strongSelf.controllerView.inTransition = false
|
||||
|
||||
record.controller.viewDidAppear(true)
|
||||
|
||||
previousController.setIgnoreAppearanceMethodInvocations(true)
|
||||
previousController.view.removeFromSuperview()
|
||||
previousController.setIgnoreAppearanceMethodInvocations(false)
|
||||
previousController.viewDidDisappear(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
previousController.viewWillDisappear(false)
|
||||
previousController.view.removeFromSuperview()
|
||||
previousController.viewDidDisappear(false)
|
||||
}
|
||||
}
|
||||
|
||||
if let previousController = self.previouslyLaidOutTopController, previousController !== self.previouslyLaidOutMasterController, !controllersAndFrames.contains(where: { $0.1.controller === previousController }), previousController.view.superview != nil {
|
||||
if transition.isAnimated, let record = appearingDetailController {
|
||||
animatedAppearingDetailController = true
|
||||
|
||||
@ -554,7 +646,6 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
record.controller.setIgnoreAppearanceMethodInvocations(false)
|
||||
|
||||
if let _ = previousControllers.firstIndex(where: { $0.controller === record.controller }) {
|
||||
//previousControllers[index].transition = .appearance
|
||||
let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self.controllerView.containerView, topView: previousController.view, topNavigationBar: (previousController as? ViewController)?.navigationBar, bottomView: record.controller.view, bottomNavigationBar: (record.controller as? ViewController)?.navigationBar)
|
||||
self.navigationTransitionCoordinator = navigationTransitionCoordinator
|
||||
|
||||
@ -615,10 +706,10 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
}
|
||||
}
|
||||
|
||||
if let record = appearingMasterController, let firstControllerFrameAndLayout = firstControllerFrameAndLayout {
|
||||
if !animatedAppearingMasterController, let record = appearingMasterController, let firstControllerFrameAndLayout = firstControllerFrameAndLayout {
|
||||
record.controller.viewWillAppear(false)
|
||||
record.controller.setIgnoreAppearanceMethodInvocations(true)
|
||||
self.controllerView.insertSubview(record.controller.view, belowSubview: self.controllerView.containerView)
|
||||
self.controllerView.masterContainerView.addSubview(record.controller.view)
|
||||
record.controller.setIgnoreAppearanceMethodInvocations(false)
|
||||
record.controller.viewDidAppear(false)
|
||||
if let controller = record.controller as? ViewController {
|
||||
@ -799,127 +890,281 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
|
||||
@objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
guard let layout = self.validLayout else {
|
||||
case .began:
|
||||
guard let layout = self.validLayout else {
|
||||
return
|
||||
}
|
||||
var beginMasterGesture = false
|
||||
var beginDetailGesture = false
|
||||
|
||||
let masterControllers: [ControllerRecord]
|
||||
let detailControllers: [ControllerRecord]
|
||||
|
||||
switch self.layoutConfiguration(for: layout) {
|
||||
case .masterDetail:
|
||||
masterControllers = self._viewControllers.filter({ record in
|
||||
if let controller = record.controller as? ViewController {
|
||||
if case .master = controller.navigationPresentation {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
detailControllers = self._viewControllers.filter({ record in
|
||||
if let controller = record.controller as? ViewController {
|
||||
if case .master = controller.navigationPresentation {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
let locationInMaster = recognizer.location(in: self.controllerView.masterContainerView)
|
||||
let locationInDetail = recognizer.location(in: self.controllerView.containerView)
|
||||
|
||||
if self.controllerView.masterContainerView.bounds.contains(locationInMaster) {
|
||||
beginMasterGesture = masterControllers.count >= 2
|
||||
}
|
||||
if self.controllerView.containerView.bounds.contains(locationInDetail) {
|
||||
beginDetailGesture = detailControllers.count >= 2
|
||||
}
|
||||
case .single:
|
||||
beginDetailGesture = self._viewControllers.count >= 2
|
||||
masterControllers = []
|
||||
detailControllers = self._viewControllers
|
||||
}
|
||||
|
||||
if beginMasterGesture {
|
||||
guard self.masterTransitionCoordinator == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
let topController = masterControllers[masterControllers.count - 1].controller
|
||||
let bottomController = masterControllers[masterControllers.count - 2].controller
|
||||
|
||||
if let topController = topController as? ViewController {
|
||||
if !topController.attemptNavigation({ [weak self, weak topController] in
|
||||
if let topController = topController {
|
||||
self?.filterController(topController, animated: true)
|
||||
}
|
||||
}) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
topController.viewWillDisappear(true)
|
||||
let topView = topController.view!
|
||||
if let bottomController = bottomController as? ViewController {
|
||||
let (_, controllerLayout) = self.layoutDataForConfiguration(self.layoutConfiguration(for: layout), layout: layout, index: 0)
|
||||
|
||||
let appliedLayout = controllerLayout.withUpdatedInputHeight(bottomController.hasActiveInput ? controllerLayout.inputHeight : nil)
|
||||
bottomController.containerLayoutUpdated(appliedLayout, transition: .immediate)
|
||||
}
|
||||
bottomController.viewWillAppear(true)
|
||||
let bottomView = bottomController.view!
|
||||
|
||||
let masterTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self.controllerView.masterContainerView, topView: topView, topNavigationBar: (topController as? ViewController)?.navigationBar, bottomView: bottomView, bottomNavigationBar: (bottomController as? ViewController)?.navigationBar, didUpdateProgress: { [weak self, weak topController, weak bottomController] progress, transition in
|
||||
if let strongSelf = self, let topController = topController, let bottomController = bottomController {
|
||||
(bottomController as? ViewController)?.updateNavigationCustomData((topController as? ViewController)?.customData, progress: 1.0 - progress, transition: transition)
|
||||
}
|
||||
})
|
||||
if let bottomController = bottomController as? ViewController {
|
||||
bottomController.displayNode.recursivelyEnsureDisplaySynchronously(true)
|
||||
}
|
||||
self.masterTransitionCoordinator = masterTransitionCoordinator
|
||||
}
|
||||
if beginDetailGesture {
|
||||
guard self.navigationTransitionCoordinator == nil else {
|
||||
return
|
||||
}
|
||||
let beginGesture: Bool
|
||||
switch self.layoutConfiguration(for: layout) {
|
||||
case .masterDetail:
|
||||
let location = recognizer.location(in: self.controllerView.containerView)
|
||||
if self.controllerView.containerView.bounds.contains(location) {
|
||||
beginGesture = self._viewControllers.count >= 3
|
||||
} else {
|
||||
beginGesture = false
|
||||
}
|
||||
case .single:
|
||||
beginGesture = self._viewControllers.count >= 2
|
||||
let topController = detailControllers[detailControllers.count - 1].controller
|
||||
let bottomController = detailControllers[detailControllers.count - 2].controller
|
||||
|
||||
if let topController = topController as? ViewController {
|
||||
if !topController.attemptNavigation({ [weak self] in
|
||||
let _ = self?.popViewController(animated: true)
|
||||
}) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if beginGesture {
|
||||
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
|
||||
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
|
||||
topController.viewWillDisappear(true)
|
||||
let topView = topController.view!
|
||||
if let bottomController = bottomController as? ViewController {
|
||||
let (_, controllerLayout) = self.layoutDataForConfiguration(self.layoutConfiguration(for: layout), layout: layout, index: 1)
|
||||
|
||||
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!
|
||||
if let bottomController = bottomController as? ViewController {
|
||||
let (_, controllerLayout) = self.layoutDataForConfiguration(self.layoutConfiguration(for: layout), layout: layout, index: self.viewControllers.count - 2)
|
||||
|
||||
let appliedLayout = controllerLayout.withUpdatedInputHeight(bottomController.hasActiveInput ? controllerLayout.inputHeight : nil)
|
||||
bottomController.containerLayoutUpdated(appliedLayout, transition: .immediate)
|
||||
}
|
||||
bottomController.viewWillAppear(true)
|
||||
let bottomView = bottomController.view!
|
||||
|
||||
let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self.controllerView.containerView, topView: topView, topNavigationBar: (topController as? ViewController)?.navigationBar, bottomView: bottomView, bottomNavigationBar: (bottomController as? ViewController)?.navigationBar, didUpdateProgress: { [weak self] progress, transition in
|
||||
if let strongSelf = self {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
if let bottomController = bottomController as? ViewController {
|
||||
bottomController.displayNode.recursivelyEnsureDisplaySynchronously(true)
|
||||
}
|
||||
self.navigationTransitionCoordinator = navigationTransitionCoordinator
|
||||
let appliedLayout = controllerLayout.withUpdatedInputHeight(bottomController.hasActiveInput ? controllerLayout.inputHeight : nil)
|
||||
bottomController.containerLayoutUpdated(appliedLayout, transition: .immediate)
|
||||
}
|
||||
case .changed:
|
||||
if let navigationTransitionCoordinator = self.navigationTransitionCoordinator, !navigationTransitionCoordinator.animatingCompletion {
|
||||
bottomController.viewWillAppear(true)
|
||||
let bottomView = bottomController.view!
|
||||
|
||||
let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self.controllerView.containerView, topView: topView, topNavigationBar: (topController as? ViewController)?.navigationBar, bottomView: bottomView, bottomNavigationBar: (bottomController as? ViewController)?.navigationBar, didUpdateProgress: { [weak self, weak topController, weak bottomController] progress, transition in
|
||||
if let strongSelf = self, let topController = topController, let bottomController = bottomController {
|
||||
(bottomController as? ViewController)?.updateNavigationCustomData((topController as? ViewController)?.customData, progress: 1.0 - progress, transition: transition)
|
||||
}
|
||||
})
|
||||
if let bottomController = bottomController as? ViewController {
|
||||
bottomController.displayNode.recursivelyEnsureDisplaySynchronously(true)
|
||||
}
|
||||
self.navigationTransitionCoordinator = navigationTransitionCoordinator
|
||||
}
|
||||
case .changed:
|
||||
if let layout = self.validLayout {
|
||||
if let masterTransitionCoordinator = self.masterTransitionCoordinator, !masterTransitionCoordinator.animatingCompletion {
|
||||
let (_, controllerLayout) = self.layoutDataForConfiguration(self.layoutConfiguration(for: layout), layout: layout, index: 0)
|
||||
let translation = recognizer.translation(in: self.view).x
|
||||
let progress = max(0.0, min(1.0, translation / self.view.frame.width))
|
||||
let progress = max(0.0, min(1.0, translation / controllerLayout.size.width))
|
||||
masterTransitionCoordinator.progress = progress
|
||||
}
|
||||
if let navigationTransitionCoordinator = self.navigationTransitionCoordinator, !navigationTransitionCoordinator.animatingCompletion {
|
||||
let (_, controllerLayout) = self.layoutDataForConfiguration(self.layoutConfiguration(for: layout), layout: layout, index: 1)
|
||||
let translation = recognizer.translation(in: self.view).x
|
||||
let progress = max(0.0, min(1.0, translation / controllerLayout.size.width))
|
||||
navigationTransitionCoordinator.progress = progress
|
||||
}
|
||||
case .ended:
|
||||
if let navigationTransitionCoordinator = self.navigationTransitionCoordinator, !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: {
|
||||
(self.view as! NavigationControllerView).inTransition = false
|
||||
|
||||
self.navigationTransitionCoordinator = nil
|
||||
|
||||
if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
|
||||
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
|
||||
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
|
||||
|
||||
topController.setIgnoreAppearanceMethodInvocations(true)
|
||||
bottomController.setIgnoreAppearanceMethodInvocations(true)
|
||||
let _ = self.popViewController(animated: false)
|
||||
topController.setIgnoreAppearanceMethodInvocations(false)
|
||||
bottomController.setIgnoreAppearanceMethodInvocations(false)
|
||||
|
||||
topController.viewDidDisappear(true)
|
||||
bottomController.viewDidAppear(true)
|
||||
}
|
||||
case .ended:
|
||||
if let masterTransitionCoordinator = self.masterTransitionCoordinator, !masterTransitionCoordinator.animatingCompletion {
|
||||
let velocity = recognizer.velocity(in: self.view).x
|
||||
|
||||
let masterControllers = self._viewControllers.filter({ record in
|
||||
if let controller = record.controller as? ViewController, case .master = controller.navigationPresentation {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
if velocity > 1000 || masterTransitionCoordinator.progress > 0.2 {
|
||||
(self.view as! NavigationControllerView).inTransition = true
|
||||
masterTransitionCoordinator.animateCompletion(velocity, completion: {
|
||||
(self.view as! NavigationControllerView).inTransition = false
|
||||
self.masterTransitionCoordinator = nil
|
||||
|
||||
let masterControllers = self._viewControllers.filter({ record in
|
||||
if let controller = record.controller as? ViewController, case .master = controller.navigationPresentation {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
} else {
|
||||
if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
|
||||
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
|
||||
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
|
||||
|
||||
if masterControllers.count >= 2 && self.masterTransitionCoordinator == nil {
|
||||
let topController = masterControllers[masterControllers.count - 1].controller
|
||||
let bottomController = masterControllers[masterControllers.count - 2].controller
|
||||
|
||||
topController.viewWillAppear(true)
|
||||
bottomController.viewWillDisappear(true)
|
||||
topController.setIgnoreAppearanceMethodInvocations(true)
|
||||
bottomController.setIgnoreAppearanceMethodInvocations(true)
|
||||
if let topController = topController as? ViewController {
|
||||
self.filterController(topController, animated: false)
|
||||
}
|
||||
topController.setIgnoreAppearanceMethodInvocations(false)
|
||||
bottomController.setIgnoreAppearanceMethodInvocations(false)
|
||||
|
||||
topController.viewDidDisappear(true)
|
||||
bottomController.viewDidAppear(true)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if masterControllers.count >= 2 && masterControllers == nil {
|
||||
let topController = masterControllers[masterControllers.count - 1].controller
|
||||
let bottomController = masterControllers[masterControllers.count - 2].controller
|
||||
|
||||
topController.viewWillAppear(true)
|
||||
bottomController.viewWillDisappear(true)
|
||||
}
|
||||
|
||||
(self.view as! NavigationControllerView).inTransition = true
|
||||
masterTransitionCoordinator.animateCancel({
|
||||
(self.view as! NavigationControllerView).inTransition = false
|
||||
self.masterTransitionCoordinator = nil
|
||||
|
||||
let masterControllers = self._viewControllers.filter({ record in
|
||||
if let controller = record.controller as? ViewController, case .master = controller.navigationPresentation {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
if masterControllers.count >= 2 && self.masterTransitionCoordinator == nil {
|
||||
let topController = masterControllers[masterControllers.count - 1].controller
|
||||
let bottomController = masterControllers[masterControllers.count - 2].controller
|
||||
|
||||
topController.viewDidAppear(true)
|
||||
bottomController.viewDidDisappear(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
if let layout = self.validLayout, let navigationTransitionCoordinator = self.navigationTransitionCoordinator, !navigationTransitionCoordinator.animatingCompletion {
|
||||
let velocity = recognizer.velocity(in: self.view).x
|
||||
|
||||
let detailControllers: [ControllerRecord]
|
||||
switch self.layoutConfiguration(for: layout) {
|
||||
case .masterDetail:
|
||||
detailControllers = self._viewControllers.filter({ record in
|
||||
if let controller = record.controller as? ViewController {
|
||||
if case .master = controller.navigationPresentation {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
case .single:
|
||||
detailControllers = self._viewControllers
|
||||
}
|
||||
|
||||
if velocity > 1000 || navigationTransitionCoordinator.progress > 0.2 {
|
||||
(self.view as! NavigationControllerView).inTransition = true
|
||||
navigationTransitionCoordinator.animateCompletion(velocity, completion: {
|
||||
(self.view as! NavigationControllerView).inTransition = false
|
||||
|
||||
let detailControllers: [ControllerRecord]
|
||||
switch self.layoutConfiguration(for: layout) {
|
||||
case .masterDetail:
|
||||
detailControllers = self._viewControllers.filter({ record in
|
||||
if let controller = record.controller as? ViewController {
|
||||
if case .master = controller.navigationPresentation {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
case .single:
|
||||
detailControllers = self._viewControllers
|
||||
}
|
||||
|
||||
(self.view as! NavigationControllerView).inTransition = true
|
||||
navigationTransitionCoordinator.animateCancel({
|
||||
(self.view as! NavigationControllerView).inTransition = false
|
||||
self.navigationTransitionCoordinator = nil
|
||||
self.navigationTransitionCoordinator = nil
|
||||
|
||||
if detailControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
|
||||
let topController = detailControllers[detailControllers.count - 1].controller
|
||||
let bottomController = detailControllers[detailControllers.count - 2].controller
|
||||
|
||||
if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
|
||||
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
|
||||
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
|
||||
|
||||
topController.viewDidAppear(true)
|
||||
bottomController.viewDidDisappear(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
case .cancelled:
|
||||
if let navigationTransitionCoordinator = self.navigationTransitionCoordinator, !navigationTransitionCoordinator.animatingCompletion {
|
||||
if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
|
||||
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
|
||||
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
|
||||
topController.setIgnoreAppearanceMethodInvocations(true)
|
||||
bottomController.setIgnoreAppearanceMethodInvocations(true)
|
||||
self.filterController(topController as! ViewController, animated: false)
|
||||
topController.setIgnoreAppearanceMethodInvocations(false)
|
||||
bottomController.setIgnoreAppearanceMethodInvocations(false)
|
||||
|
||||
topController.viewDidDisappear(true)
|
||||
bottomController.viewDidAppear(true)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if detailControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
|
||||
let topController = detailControllers[detailControllers.count - 1].controller
|
||||
let bottomController = detailControllers[detailControllers.count - 2].controller
|
||||
|
||||
topController.viewWillAppear(true)
|
||||
bottomController.viewWillDisappear(true)
|
||||
@ -930,17 +1175,133 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
(self.view as! NavigationControllerView).inTransition = false
|
||||
self.navigationTransitionCoordinator = nil
|
||||
|
||||
if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
|
||||
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
|
||||
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
|
||||
let detailControllers: [ControllerRecord]
|
||||
switch self.layoutConfiguration(for: layout) {
|
||||
case .masterDetail:
|
||||
detailControllers = self._viewControllers.filter({ record in
|
||||
if let controller = record.controller as? ViewController {
|
||||
if case .master = controller.navigationPresentation {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
case .single:
|
||||
detailControllers = self._viewControllers
|
||||
}
|
||||
|
||||
if detailControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
|
||||
let topController = detailControllers[detailControllers.count - 1].controller
|
||||
let bottomController = detailControllers[detailControllers.count - 2].controller
|
||||
|
||||
topController.viewDidAppear(true)
|
||||
bottomController.viewDidDisappear(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
case .cancelled:
|
||||
if let masterTransitionCoordinator = self.masterTransitionCoordinator, !masterTransitionCoordinator.animatingCompletion {
|
||||
let masterControllers = self._viewControllers.filter({ record in
|
||||
if let controller = record.controller as? ViewController, case .master = controller.navigationPresentation {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
if masterControllers.count >= 2 && self.masterTransitionCoordinator == nil {
|
||||
let topController = masterControllers[masterControllers.count - 1].controller
|
||||
let bottomController = masterControllers[masterControllers.count - 2].controller
|
||||
|
||||
topController.viewWillAppear(true)
|
||||
bottomController.viewWillDisappear(true)
|
||||
}
|
||||
|
||||
(self.view as! NavigationControllerView).inTransition = true
|
||||
masterTransitionCoordinator.animateCancel({
|
||||
(self.view as! NavigationControllerView).inTransition = false
|
||||
self.masterTransitionCoordinator = nil
|
||||
|
||||
let masterControllers = self._viewControllers.filter({ record in
|
||||
if let controller = record.controller as? ViewController, case .master = controller.navigationPresentation {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
if masterControllers.count >= 2 && self.masterTransitionCoordinator == nil {
|
||||
let topController = masterControllers[masterControllers.count - 1].controller
|
||||
let bottomController = masterControllers[masterControllers.count - 2].controller
|
||||
|
||||
topController.viewDidAppear(true)
|
||||
bottomController.viewDidDisappear(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
if let layout = self.validLayout, let navigationTransitionCoordinator = self.navigationTransitionCoordinator, !navigationTransitionCoordinator.animatingCompletion {
|
||||
let detailControllers: [ControllerRecord]
|
||||
switch self.layoutConfiguration(for: layout) {
|
||||
case .masterDetail:
|
||||
detailControllers = self._viewControllers.filter({ record in
|
||||
if let controller = record.controller as? ViewController {
|
||||
if case .master = controller.navigationPresentation {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
case .single:
|
||||
detailControllers = self._viewControllers
|
||||
}
|
||||
|
||||
if detailControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
|
||||
let topController = detailControllers[detailControllers.count - 1].controller
|
||||
let bottomController = detailControllers[detailControllers.count - 2].controller
|
||||
|
||||
topController.viewWillAppear(true)
|
||||
bottomController.viewWillDisappear(true)
|
||||
}
|
||||
|
||||
(self.view as! NavigationControllerView).inTransition = true
|
||||
navigationTransitionCoordinator.animateCancel({
|
||||
(self.view as! NavigationControllerView).inTransition = false
|
||||
self.navigationTransitionCoordinator = nil
|
||||
|
||||
let detailControllers: [ControllerRecord]
|
||||
switch self.layoutConfiguration(for: layout) {
|
||||
case .masterDetail:
|
||||
detailControllers = self._viewControllers.filter({ record in
|
||||
if let controller = record.controller as? ViewController {
|
||||
if case .master = controller.navigationPresentation {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
case .single:
|
||||
detailControllers = self._viewControllers
|
||||
}
|
||||
|
||||
if detailControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
|
||||
let topController = detailControllers[detailControllers.count - 1].controller
|
||||
let bottomController = detailControllers[detailControllers.count - 2].controller
|
||||
|
||||
topController.viewDidAppear(true)
|
||||
bottomController.viewDidDisappear(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -1002,7 +1363,30 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
self.currentPushDisposable.set(nil)
|
||||
|
||||
var controllers = self.viewControllers
|
||||
controllers.append(viewController)
|
||||
if let controller = viewController as? ViewController {
|
||||
switch controller.navigationPresentation {
|
||||
case .default:
|
||||
controllers.append(viewController)
|
||||
case .master:
|
||||
var i = 0
|
||||
loop: while i < controllers.count {
|
||||
if let currentController = controllers[i] as? ViewController {
|
||||
switch currentController.navigationPresentation {
|
||||
case .master:
|
||||
break
|
||||
case .default:
|
||||
break loop
|
||||
}
|
||||
} else {
|
||||
break loop
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
controllers.insert(viewController, at: i)
|
||||
}
|
||||
} else {
|
||||
controllers.append(viewController)
|
||||
}
|
||||
self.setViewControllers(controllers, animated: animated)
|
||||
}
|
||||
|
||||
|
@ -55,6 +55,11 @@ open class ViewControllerPresentationArguments {
|
||||
}
|
||||
}
|
||||
|
||||
public enum ViewControllerNavigationPresentation {
|
||||
case `default`
|
||||
case master
|
||||
}
|
||||
|
||||
@objc open class ViewController: UIViewController, ContainableController {
|
||||
private var validLayout: ContainerViewLayout?
|
||||
public var currentlyAppliedLayout: ContainerViewLayout? {
|
||||
@ -117,6 +122,8 @@ open class ViewControllerPresentationArguments {
|
||||
return self.preferNavigationUIHidden
|
||||
}
|
||||
|
||||
open var navigationPresentation: ViewControllerNavigationPresentation = .default
|
||||
|
||||
public var presentationArguments: Any?
|
||||
|
||||
public var tabBarItemDebugTapAction: (() -> Void)?
|
||||
@ -236,6 +243,10 @@ open class ViewControllerPresentationArguments {
|
||||
return true
|
||||
}
|
||||
|
||||
open func preferredContentSizeForLayout(_ layout: ContainerViewLayout) -> CGSize? {
|
||||
return nil
|
||||
}
|
||||
|
||||
private func updateScrollToTopView() {
|
||||
if self.scrollToTop != nil {
|
||||
if let displayNode = self._displayNode , self.scrollToTopView == nil {
|
||||
@ -267,9 +278,20 @@ open class ViewControllerPresentationArguments {
|
||||
|
||||
self.navigationBar?.backPressed = { [weak self] in
|
||||
if let strongSelf = self, strongSelf.attemptNavigation({
|
||||
self?.navigationController?.popViewController(animated: true)
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
navigationController.filterController(strongSelf, animated: true)
|
||||
} else {
|
||||
strongSelf.navigationController?.popViewController(animated: true)
|
||||
}
|
||||
}) {
|
||||
strongSelf.navigationController?.popViewController(animated: true)
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
navigationController.filterController(strongSelf, animated: true)
|
||||
} else {
|
||||
strongSelf.navigationController?.popViewController(animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.navigationBar?.requestContainerLayout = { [weak self] transition in
|
||||
|
@ -945,6 +945,14 @@ public class GalleryController: ViewController {
|
||||
self.accountInUseDisposable.set(nil)
|
||||
}
|
||||
|
||||
override public func preferredContentSizeForLayout(_ layout: ContainerViewLayout) -> CGSize? {
|
||||
if let centralItemNode = self.galleryNode.pager.centralItemNode(), let itemSize = centralItemNode.contentSize() {
|
||||
return itemSize.aspectFitted(layout.size)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
@ -955,7 +963,7 @@ public class GalleryController: ViewController {
|
||||
self.adjustedForInitialPreviewingLayout = true
|
||||
self.galleryNode.setControlsHidden(true, animated: false)
|
||||
if let centralItemNode = self.galleryNode.pager.centralItemNode(), let itemSize = centralItemNode.contentSize() {
|
||||
self.preferredContentSize = itemSize.aspectFitted(self.view.bounds.size)
|
||||
self.preferredContentSize = itemSize.aspectFitted(layout.size)
|
||||
self.containerLayoutUpdated(ContainerViewLayout(size: self.preferredContentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
@ -63,8 +63,6 @@ public final class TelegramRootController: NavigationController {
|
||||
if previousTheme !== presentationData.theme {
|
||||
strongSelf.rootTabController?.updateTheme(navigationBarPresentationData: NavigationBarPresentationData(presentationData: presentationData), theme: TabBarControllerTheme(rootControllerTheme: presentationData.theme))
|
||||
strongSelf.rootTabController?.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -81,6 +79,7 @@ public final class TelegramRootController: NavigationController {
|
||||
|
||||
public func addRootControllers(showCallsTab: Bool) {
|
||||
let tabBarController = TabBarController(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), theme: TabBarControllerTheme(rootControllerTheme: self.presentationData.theme))
|
||||
tabBarController.navigationPresentation = .master
|
||||
let chatListController = self.context.sharedContext.makeChatListController(context: self.context, groupId: .root, controlsHistoryPreload: true, hideNetworkActivityStatus: false, enableDebugActions: !GlobalExperimentalSettings.isAppStoreBuild)
|
||||
if let sharedContext = self.context.sharedContext as? SharedAccountContextImpl {
|
||||
chatListController.tabBarItem.badgeValue = sharedContext.switchingData.chatListBadge
|
||||
|
Loading…
x
Reference in New Issue
Block a user