From 71a5da08d4cd265e62b8520df0e24d055e4b77a2 Mon Sep 17 00:00:00 2001 From: Alexander Zimin Date: Mon, 24 Jun 2019 00:19:21 +0300 Subject: [PATCH] Added 3d touch preview to contacts screen and contacts search screen --- TelegramUI/ContactsController.swift | 80 +++++++++++++++++++- TelegramUI/ContactsControllerNode.swift | 29 ++++++- TelegramUI/ContactsPeerItem.swift | 3 +- TelegramUI/ContactsSearchContainerNode.swift | 2 +- 4 files changed, 107 insertions(+), 7 deletions(-) diff --git a/TelegramUI/ContactsController.swift b/TelegramUI/ContactsController.swift index ee22c9f9..e69851cc 100644 --- a/TelegramUI/ContactsController.swift +++ b/TelegramUI/ContactsController.swift @@ -50,6 +50,8 @@ private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBa } public class ContactsController: ViewController { + private var validLayout: ContainerViewLayout? + private let context: AccountContext private var contactsNode: ContactsControllerNode { @@ -182,7 +184,7 @@ public class ContactsController: ViewController { } override public func loadDisplayNode() { - self.displayNode = ContactsControllerNode(context: self.context, sortOrder: sortOrderPromise.get() |> distinctUntilChanged, present: { [weak self] c, a in + self.displayNode = ContactsControllerNode(context: self.context, sortOrder: sortOrderPromise.get() |> distinctUntilChanged, controller: self, present: { [weak self] c, a in self?.present(c, in: .window(.root), with: a) }) self._ready.set(self.contactsNode.contactListNode.ready) @@ -302,6 +304,8 @@ public class ContactsController: ViewController { override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { super.containerLayoutUpdated(layout, transition: transition) + + self.validLayout = layout self.contactsNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationInsetHeight, actualNavigationBarHeight: self.navigationHeight, transition: transition) } @@ -388,4 +392,78 @@ public class ContactsController: ViewController { } }) } + + func previewingController(from sourceView: UIView, for location: CGPoint) -> (UIViewController, CGRect)? { + guard let layout = self.validLayout, case .compact = layout.metrics.widthClass else { + return nil + } + + let boundsSize = self.view.bounds.size + let contentSize: CGSize + if let metrics = DeviceMetrics.forScreenSize(layout.size) { + contentSize = metrics.previewingContentSize(inLandscape: boundsSize.width > boundsSize.height) + } else { + contentSize = boundsSize + } + + var selectedNode: ContactsPeerItemNode? + + if let searchController = self.contactsNode.searchDisplayController { + guard let contentNode = searchController.contentNode as? ContactsSearchContainerNode else { + return nil + } + + let listLocation = self.view.convert(location, to: contentNode.listNode.view) + + contentNode.listNode.forEachItemNode { itemNode in + if let itemNode = itemNode as? ContactsPeerItemNode, itemNode.frame.contains(listLocation), !itemNode.isDisplayingRevealedOptions { + selectedNode = itemNode + } + } + } else { + let listLocation = self.view.convert(location, to: self.contactsNode.contactListNode.listNode.view) + + self.contactsNode.contactListNode.listNode.forEachItemNode { itemNode in + if let itemNode = itemNode as? ContactsPeerItemNode, itemNode.frame.contains(listLocation), !itemNode.isDisplayingRevealedOptions { + selectedNode = itemNode + } + } + } + + if let selectedNode = selectedNode, let peer = selectedNode.item?.peer { + var sourceRect = selectedNode.view.superview!.convert(selectedNode.frame, to: sourceView) + sourceRect.size.height -= UIScreenPixel + switch peer { + case let .peer(peer, _): + guard let peer = peer else { + return nil + } + if peer.id.namespace != Namespaces.Peer.SecretChat { + let chatController = ChatController(context: self.context, chatLocation: .peer(peer.id), mode: .standard(previewing: true)) + chatController.canReadHistory.set(false) + chatController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, standardInputHeight: 216.0, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate) + return (chatController, sourceRect) + } else { + return nil + } + case .deviceContact: + return nil + } + } else { + return nil + } + } + + func previewingCommit(_ viewControllerToCommit: UIViewController) { + if let viewControllerToCommit = viewControllerToCommit as? ViewController { + if let chatController = viewControllerToCommit as? ChatController { + chatController.canReadHistory.set(true) + chatController.updatePresentationMode(.standard(previewing: false)) + if let navigationController = self.navigationController as? NavigationController { + navigateToChatController(navigationController: navigationController, chatController: chatController, context: self.context, chatLocation: chatController.chatLocation, animated: false) + self.contactsNode.contactListNode.listNode.clearHighlightAnimated(true) + } + } + } + } } diff --git a/TelegramUI/ContactsControllerNode.swift b/TelegramUI/ContactsControllerNode.swift index 6e23be2b..e3d50ff9 100644 --- a/TelegramUI/ContactsControllerNode.swift +++ b/TelegramUI/ContactsControllerNode.swift @@ -5,11 +5,25 @@ import Postbox import TelegramCore import SwiftSignalKit +private final class ContactsControllerNodeView: UITracingLayerView, PreviewingHostView { + var previewingDelegate: PreviewingHostViewDelegate? { + return PreviewingHostViewDelegate(controllerForLocation: { [weak self] sourceView, point in + return self?.controller?.previewingController(from: sourceView, for: point) + }, commitController: { [weak self] controller in + self?.controller?.previewingCommit(controller) + }) + } + + weak var controller: ContactsController? +} + final class ContactsControllerNode: ASDisplayNode { let contactListNode: ContactListNode private let context: AccountContext - private var searchDisplayController: SearchDisplayController? + var searchDisplayController: SearchDisplayController? + + weak var controller: ContactsController? private var containerLayout: (ContainerViewLayout, CGFloat)? @@ -23,8 +37,9 @@ final class ContactsControllerNode: ASDisplayNode { private var presentationData: PresentationData private var presentationDataDisposable: Disposable? - init(context: AccountContext, sortOrder: Signal, present: @escaping (ViewController, Any?) -> Void) { + init(context: AccountContext, sortOrder: Signal, controller: ContactsController, present: @escaping (ViewController, Any?) -> Void) { self.context = context + self.controller = controller self.presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -50,9 +65,9 @@ final class ContactsControllerNode: ASDisplayNode { self.contactListNode = ContactListNode(context: context, presentation: presentation, displaySortOptions: true) super.init() - + self.setViewBlock({ - return UITracingLayerView() + return ContactsControllerNodeView() }) self.backgroundColor = self.presentationData.theme.chatList.backgroundColor @@ -101,6 +116,12 @@ final class ContactsControllerNode: ASDisplayNode { }) } } + + override func didLoad() { + super.didLoad() + + (self.view as? ContactsControllerNodeView)?.controller = self.controller + } deinit { self.presentationDataDisposable?.dispose() diff --git a/TelegramUI/ContactsPeerItem.swift b/TelegramUI/ContactsPeerItem.swift index 2d390ca6..942ec24c 100644 --- a/TelegramUI/ContactsPeerItem.swift +++ b/TelegramUI/ContactsPeerItem.swift @@ -313,7 +313,8 @@ class ContactsPeerItemNode: ItemListRevealOptionsItemNode { return nil } } - private var item: ContactsPeerItem? { + + var item: ContactsPeerItem? { return self.layoutParams?.0 } diff --git a/TelegramUI/ContactsSearchContainerNode.swift b/TelegramUI/ContactsSearchContainerNode.swift index 58f3a227..7fa402cc 100644 --- a/TelegramUI/ContactsSearchContainerNode.swift +++ b/TelegramUI/ContactsSearchContainerNode.swift @@ -127,7 +127,7 @@ final class ContactsSearchContainerNode: SearchDisplayControllerContentNode { private let openPeer: (ContactListPeer) -> Void private let dimNode: ASDisplayNode - private let listNode: ListView + let listNode: ListView private let searchQuery = Promise() private let searchDisposable = MetaDisposable()