Swiftgram/TelegramUI/NotificationItemContainerNode.swift
2017-04-18 19:53:47 +03:00

123 lines
4.8 KiB
Swift

import Foundation
import AsyncDisplayKit
import Display
private let backgroundImageWithShadow = generateImage(CGSize(width: 30.0 + 8.0 * 2.0, height: 30.0 + 8.0 + 20.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setShadow(offset: CGSize(width: 0.0, height: -4.0), blur: 40.0, color: UIColor(white: 0.0, alpha: 0.3).cgColor)
context.setFillColor(UIColor.white.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: 8.0, y: 8.0), size: CGSize(width: 30.0, height: 30.0)))
})?.stretchableImage(withLeftCapWidth: 8 + 15, topCapHeight: 8 + 15)
final class NotificationItemContainerNode: ASDisplayNode {
private let backgroundNode: ASImageNode
private var validLayout: ContainerViewLayout?
var item: NotificationItem?
var contentNode: NotificationItemNode? {
didSet {
if self.contentNode !== oldValue {
oldValue?.removeFromSupernode()
}
if let contentNode = self.contentNode {
self.addSubnode(contentNode)
if let validLayout = self.validLayout {
self.updateLayout(layout: validLayout, transition: .immediate)
}
}
}
}
var dismissed: ((NotificationItem) -> Void)?
override init() {
self.backgroundNode = ASImageNode()
self.backgroundNode.displayWithoutProcessing = true
self.backgroundNode.displaysAsynchronously = false
self.backgroundNode.image = backgroundImageWithShadow
super.init()
self.addSubnode(self.backgroundNode)
}
override func didLoad() {
super.didLoad()
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
panRecognizer.delaysTouchesBegan = false
panRecognizer.cancelsTouchesInView = false
self.view.addGestureRecognizer(panRecognizer)
}
func animateIn() {
self.layer.animatePosition(from: CGPoint(x: 0.0, y: -100.0), to: CGPoint(), duration: 0.4, additive: true)
}
func animateOut(completion: @escaping () -> Void) {
self.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -100.0), duration: 0.4, removeOnCompletion: false, additive: true, completion: { _ in
completion()
})
}
func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
self.validLayout = layout
if let contentNode = self.contentNode {
let contentInsets = UIEdgeInsets(top: 8.0, left: 8.0, bottom: 8.0, right: 8.0)
let contentWidth = layout.size.width - contentInsets.left - contentInsets.right
let contentHeight = contentNode.updateLayout(width: contentWidth, transition: transition)
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: 8.0 + contentHeight + 20.0)))
transition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(x: contentInsets.left, y: contentInsets.top), size: CGSize(width: contentWidth, height: contentHeight)))
}
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let contentNode = self.contentNode, contentNode.frame.contains(point) {
return self.view
}
return nil
}
@objc func tapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
if let item = self.item {
item.tapped()
}
}
}
@objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
break
case .changed:
let translation = recognizer.translation(in: self.view)
var bounds = self.bounds
bounds.origin.y = max(0.0, -translation.y)
self.bounds = bounds
case .ended:
self.animateOut(completion: { [weak self] in
if let strongSelf = self, let item = strongSelf.item {
strongSelf.dismissed?(item)
}
})
case .cancelled:
let previousBounds = self.bounds
var bounds = self.bounds
bounds.origin.y = 0.0
self.bounds = bounds
self.layer.animateBounds(from: previousBounds, to: self.bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionEaseInEaseOut)
default:
break
}
}
}