Swiftgram/submodules/Display/Display/TooltipController.swift
Peter 8f5a4f7dc1 Add 'submodules/Display/' from commit '7bd11013ea936e3d49d937550d599f5816d32560'
git-subtree-dir: submodules/Display
git-subtree-mainline: 9bc996374ffdad37aef175427db72731c9551dcf
git-subtree-split: 7bd11013ea936e3d49d937550d599f5816d32560
2019-06-11 18:44:37 +01:00

173 lines
5.5 KiB
Swift

import Foundation
import UIKit
import AsyncDisplayKit
import SwiftSignalKit
public enum TooltipControllerContent: Equatable {
case text(String)
case iconAndText(UIImage, String)
var text: String {
switch self {
case let .text(text), let .iconAndText(_, text):
return text
}
}
var image: UIImage? {
if case let .iconAndText(image, _) = self {
return image
}
return nil
}
}
private enum SourceAndRect {
case node(() -> (ASDisplayNode, CGRect)?)
case view(() -> (UIView, CGRect)?)
func globalRect() -> CGRect? {
switch self {
case let .node(node):
if let (sourceNode, sourceRect) = node() {
return sourceNode.view.convert(sourceRect, to: nil)
}
case let .view(view):
if let (sourceView, sourceRect) = view() {
return sourceView.convert(sourceRect, to: nil)
}
}
return nil
}
}
public final class TooltipControllerPresentationArguments {
fileprivate let sourceAndRect: SourceAndRect
public init(sourceNodeAndRect: @escaping () -> (ASDisplayNode, CGRect)?) {
self.sourceAndRect = .node(sourceNodeAndRect)
}
public init(sourceViewAndRect: @escaping () -> (UIView, CGRect)?) {
self.sourceAndRect = .view(sourceViewAndRect)
}
}
public final class TooltipController: ViewController {
private var controllerNode: TooltipControllerNode {
return self.displayNode as! TooltipControllerNode
}
public var content: TooltipControllerContent {
didSet {
if self.content != oldValue {
if self.isNodeLoaded {
self.controllerNode.updateText(self.content.text, transition: .animated(duration: 0.25, curve: .easeInOut))
if self.timeoutTimer != nil {
self.timeoutTimer?.invalidate()
self.timeoutTimer = nil
self.beginTimeout()
}
}
}
}
}
private let timeout: Double
private let dismissByTapOutside: Bool
private let dismissImmediatelyOnLayoutUpdate: Bool
private var timeoutTimer: SwiftSignalKit.Timer?
private var layout: ContainerViewLayout?
public var dismissed: (() -> Void)?
public init(content: TooltipControllerContent, timeout: Double = 2.0, dismissByTapOutside: Bool = false, dismissImmediatelyOnLayoutUpdate: Bool = false) {
self.content = content
self.timeout = timeout
self.dismissByTapOutside = dismissByTapOutside
self.dismissImmediatelyOnLayoutUpdate = dismissImmediatelyOnLayoutUpdate
super.init(navigationBarPresentationData: nil)
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.timeoutTimer?.invalidate()
}
public override func loadDisplayNode() {
self.displayNode = TooltipControllerNode(content: self.content, dismiss: { [weak self] in
self?.dismiss()
}, dismissByTapOutside: self.dismissByTapOutside)
self.displayNodeDidLoad()
}
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.controllerNode.animateIn()
self.beginTimeout()
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
if self.layout != nil && self.layout! != layout {
if self.dismissImmediatelyOnLayoutUpdate {
self.dismissImmediately()
} else {
self.dismiss()
}
} else {
self.layout = layout
if let presentationArguments = self.presentationArguments as? TooltipControllerPresentationArguments, let sourceRect = presentationArguments.sourceAndRect.globalRect() {
self.controllerNode.sourceRect = sourceRect
} else {
self.controllerNode.sourceRect = nil
}
self.controllerNode.containerLayoutUpdated(layout, transition: transition)
}
}
public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.controllerNode.animateIn()
self.beginTimeout()
}
private func beginTimeout() {
if self.timeoutTimer == nil {
let timeoutTimer = SwiftSignalKit.Timer(timeout: self.timeout, repeat: false, completion: { [weak self] in
if let strongSelf = self {
strongSelf.dismissed?()
strongSelf.controllerNode.animateOut {
self?.presentingViewController?.dismiss(animated: false)
}
}
}, queue: Queue.mainQueue())
self.timeoutTimer = timeoutTimer
timeoutTimer.start()
}
}
override public func dismiss(completion: (() -> Void)? = nil) {
self.dismissed?()
self.controllerNode.animateOut { [weak self] in
self?.presentingViewController?.dismiss(animated: false)
completion?()
}
}
public func dismissImmediately() {
self.dismissed?()
self.presentingViewController?.dismiss(animated: false)
}
}