Swiftgram/submodules/LocationUI/Sources/LocationAnnotation.swift
2019-11-25 14:09:43 +04:00

285 lines
12 KiB
Swift

import Foundation
import UIKit
import MapKit
import Display
import Postbox
import SyncCore
import TelegramCore
import AvatarNode
import AppBundle
import TelegramPresentationData
import LocationResources
let locationPinReuseIdentifier = "locationPin"
private func generateSmallBackgroundImage(color: UIColor) -> UIImage? {
return generateImage(CGSize(width: 56.0, height: 56.0)) { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setShadow(offset: CGSize(), blur: 4.0, color: UIColor(rgb: 0x000000, alpha: 0.5).cgColor)
context.setFillColor(UIColor.white.cgColor)
context.fillEllipse(in: CGRect(x: 16.0, y: 16.0, width: 24.0, height: 24.0))
context.setShadow(offset: CGSize(), blur: 0.0, color: nil)
context.setFillColor(color.cgColor)
context.fillEllipse(in: CGRect(x: 17.0 + UIScreenPixel, y: 17.0 + UIScreenPixel, width: 22.0 - 2.0 * UIScreenPixel, height: 22.0 - 2.0 * UIScreenPixel))
}
}
class LocationPinAnnotation: NSObject, MKAnnotation {
let account: Account
let theme: PresentationTheme
var coordinate: CLLocationCoordinate2D
let location: TelegramMediaMap
var title: String? = ""
var subtitle: String? = ""
init(account: Account, theme: PresentationTheme, location: TelegramMediaMap) {
self.account = account
self.theme = theme
self.location = location
self.coordinate = location.coordinate
super.init()
}
}
class LocationPinAnnotationLayer: CALayer {
var customZPosition: CGFloat?
override var zPosition: CGFloat {
get {
if let zPosition = self.customZPosition {
return zPosition
} else {
return super.zPosition
}
} set {
super.zPosition = newValue
}
}
}
class LocationPinAnnotationView: MKAnnotationView {
let shadowNode: ASImageNode
let backgroundNode: ASImageNode
let smallNode: ASImageNode
let iconNode: TransformImageNode
let smallIconNode: TransformImageNode
let dotNode: ASImageNode
var avatarNode: AvatarNode?
var appeared = false
var animating = false
override class var layerClass: AnyClass {
return LocationPinAnnotationLayer.self
}
func setZPosition(_ zPosition: CGFloat?) {
if let layer = self.layer as? LocationPinAnnotationLayer {
layer.customZPosition = zPosition
}
}
init(annotation: LocationPinAnnotation) {
self.shadowNode = ASImageNode()
self.shadowNode.image = UIImage(bundleImageName: "Location/PinShadow")
if let image = self.shadowNode.image {
self.shadowNode.bounds = CGRect(origin: CGPoint(), size: image.size)
}
self.backgroundNode = ASImageNode()
self.backgroundNode.image = UIImage(bundleImageName: "Location/PinBackground")
if let image = self.backgroundNode.image {
self.backgroundNode.bounds = CGRect(origin: CGPoint(), size: image.size)
}
self.smallNode = ASImageNode()
self.smallNode.image = UIImage(bundleImageName: "Location/PinSmallBackground")
if let image = self.smallNode.image {
self.smallNode.bounds = CGRect(origin: CGPoint(), size: image.size)
}
self.iconNode = TransformImageNode()
self.iconNode.bounds = CGRect(origin: CGPoint(), size: CGSize(width: 64.0, height: 64.0))
self.smallIconNode = TransformImageNode()
self.smallIconNode.frame = CGRect(origin: CGPoint(x: 15.0, y: 15.0), size: CGSize(width: 26.0, height: 26.0))
self.dotNode = ASImageNode()
self.dotNode.image = generateFilledCircleImage(diameter: 6.0, color: annotation.theme.list.itemAccentColor)
if let image = self.dotNode.image {
self.dotNode.bounds = CGRect(origin: CGPoint(), size: image.size)
}
super.init(annotation: annotation, reuseIdentifier: locationPinReuseIdentifier)
self.addSubnode(self.smallNode)
self.smallNode.addSubnode(self.smallIconNode)
self.addSubnode(self.shadowNode)
self.shadowNode.addSubnode(self.backgroundNode)
self.backgroundNode.addSubnode(self.iconNode)
self.addSubnode(self.dotNode)
self.annotation = annotation
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var annotation: MKAnnotation? {
didSet {
if let annotation = self.annotation as? LocationPinAnnotation {
let venueType = annotation.location.venue?.type ?? ""
let color = venueType.isEmpty ? annotation.theme.list.itemAccentColor : venueIconColor(type: venueType)
self.backgroundNode.image = generateTintedImage(image: UIImage(bundleImageName: "Location/PinBackground"), color: color)
self.iconNode.setSignal(venueIcon(postbox: annotation.account.postbox, type: annotation.location.venue?.type ?? "", background: false))
self.smallIconNode.setSignal(venueIcon(postbox: annotation.account.postbox, type: annotation.location.venue?.type ?? "", background: false))
self.smallNode.image = generateSmallBackgroundImage(color: color)
self.dotNode.image = generateFilledCircleImage(diameter: 6.0, color: color)
self.dotNode.isHidden = false
if !self.isSelected {
self.dotNode.alpha = 0.0
self.shadowNode.isHidden = true
self.smallNode.isHidden = false
}
}
}
}
override func prepareForReuse() {
self.smallNode.isHidden = true
self.backgroundNode.isHidden = false
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if animated {
self.layoutSubviews()
self.animating = true
if selected {
self.shadowNode.position = CGPoint(x: self.shadowNode.position.x, y: self.shadowNode.position.y + self.shadowNode.frame.height / 2.0)
self.shadowNode.anchorPoint = CGPoint(x: 0.5, y: 1.0)
self.shadowNode.isHidden = false
self.shadowNode.transform = CATransform3DMakeScale(0.1, 0.1, 1.0)
UIView.animate(withDuration: 0.35, delay: 0.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.5, options: [], animations: {
self.smallNode.transform = CATransform3DMakeScale(0.001, 0.001, 1.0)
self.shadowNode.transform = CATransform3DIdentity
if self.dotNode.isHidden {
self.smallNode.alpha = 0.0
}
}) { _ in
self.animating = false
self.shadowNode.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.smallNode.isHidden = true
self.smallNode.transform = CATransform3DIdentity
}
self.dotNode.alpha = 1.0
self.dotNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
} else {
self.smallNode.isHidden = false
self.smallNode.transform = CATransform3DMakeScale(0.01, 0.01, 1.0)
self.shadowNode.position = CGPoint(x: self.shadowNode.position.x, y: self.shadowNode.position.y + self.shadowNode.frame.height / 2.0)
self.shadowNode.anchorPoint = CGPoint(x: 0.5, y: 1.0)
UIView.animate(withDuration: 0.35, delay: 0.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.5, options: [], animations: {
self.smallNode.transform = CATransform3DIdentity
self.shadowNode.transform = CATransform3DMakeScale(0.1, 0.1, 1.0)
if self.dotNode.isHidden {
self.smallNode.alpha = 1.0
}
}) { _ in
self.animating = false
self.shadowNode.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.shadowNode.isHidden = true
self.shadowNode.transform = CATransform3DIdentity
}
let previousAlpha = self.dotNode.alpha
self.dotNode.alpha = 0.0
self.dotNode.layer.animateAlpha(from: previousAlpha, to: 0.0, duration: 0.2)
}
} else {
self.smallNode.isHidden = selected
self.shadowNode.isHidden = !selected
self.dotNode.alpha = selected ? 1.0 : 0.0
self.smallNode.alpha = 1.0
self.layoutSubviews()
}
}
override func layoutSubviews() {
super.layoutSubviews()
guard !self.animating else {
return
}
self.dotNode.position = CGPoint()
self.smallNode.position = CGPoint()
self.shadowNode.position = CGPoint(x: UIScreenPixel, y: -36.0)
self.backgroundNode.position = CGPoint(x: self.shadowNode.frame.width / 2.0, y: self.shadowNode.frame.height / 2.0)
self.iconNode.position = CGPoint(x: self.shadowNode.frame.width / 2.0, y: self.shadowNode.frame.height / 2.0 - 5.0)
let smallIconLayout = self.smallIconNode.asyncLayout()
let smallIconApply = smallIconLayout(TransformImageArguments(corners: ImageCorners(), imageSize: self.smallIconNode.bounds.size, boundingSize: self.smallIconNode.bounds.size, intrinsicInsets: UIEdgeInsets()))
smallIconApply()
let iconLayout = self.iconNode.asyncLayout()
let iconApply = iconLayout(TransformImageArguments(corners: ImageCorners(), imageSize: self.iconNode.bounds.size, boundingSize: self.iconNode.bounds.size, intrinsicInsets: UIEdgeInsets()))
iconApply()
if let avatarNode = self.avatarNode {
avatarNode.position = self.isSelected ? CGPoint(x: UIScreenPixel, y: -41.0) : CGPoint()
avatarNode.transform = self.isSelected ? CATransform3DIdentity : CATransform3DMakeScale(0.64, 0.64, 1.0)
}
if !self.appeared {
self.appeared = true
self.smallNode.transform = CATransform3DMakeScale(0.1, 0.1, 1.0)
UIView.animate(withDuration: 0.55, delay: 0.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.5, options: [], animations: {
self.smallNode.transform = CATransform3DIdentity
}) { _ in
}
}
}
func setPeer(account: Account, theme: PresentationTheme, peer: Peer) {
let avatarNode: AvatarNode
if let currentAvatarNode = self.avatarNode {
avatarNode = currentAvatarNode
} else {
avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 24.0))
avatarNode.bounds = CGRect(origin: CGPoint(), size: CGSize(width: 55.0, height: 55.0))
avatarNode.position = CGPoint()
self.avatarNode = avatarNode
self.addSubnode(avatarNode)
}
avatarNode.setPeer(account: account, theme: theme, peer: peer)
}
func setRaised(_ raised: Bool, avatar: Bool, animated: Bool, completion: @escaping () -> Void = {}) {
}
}