import Foundation import UIKit import AsyncDisplayKit import Display import CallScreen import MetalEngine final class VoiceBlobView: UIView { //private let mediumBlob: BlobView //private let bigBlob: BlobView private let blobsLayer: CallBlobsLayer private let maxLevel: CGFloat private var displayLinkAnimator: ConstantDisplayLinkAnimator? private var audioLevel: CGFloat = 0.0 var presentationAudioLevel: CGFloat = 0.0 var scaleUpdated: ((CGFloat) -> Void)? { didSet { //self.bigBlob.scaleUpdated = self.scaleUpdated } } private(set) var isAnimating = false public typealias BlobRange = (min: CGFloat, max: CGFloat) private let hierarchyTrackingNode: HierarchyTrackingNode private var isCurrentlyInHierarchy = true init( frame: CGRect, maxLevel: CGFloat, mediumBlobRange: BlobRange, bigBlobRange: BlobRange ) { var updateInHierarchy: ((Bool) -> Void)? self.hierarchyTrackingNode = HierarchyTrackingNode({ value in updateInHierarchy?(value) }) self.maxLevel = maxLevel /*self.mediumBlob = BlobView( pointsCount: 8, minRandomness: 1, maxRandomness: 1, minSpeed: 0.9, maxSpeed: 4.0, minScale: mediumBlobRange.min, maxScale: mediumBlobRange.max ) self.bigBlob = BlobView( pointsCount: 8, minRandomness: 1, maxRandomness: 1, minSpeed: 1.0, maxSpeed: 4.4, minScale: bigBlobRange.min, maxScale: bigBlobRange.max )*/ self.blobsLayer = CallBlobsLayer() super.init(frame: frame) self.addSubnode(self.hierarchyTrackingNode) //self.addSubview(self.bigBlob) //self.addSubview(self.mediumBlob) self.layer.addSublayer(self.blobsLayer) self.displayLinkAnimator = ConstantDisplayLinkAnimator() { [weak self] in guard let strongSelf = self else { return } if !strongSelf.isCurrentlyInHierarchy { return } strongSelf.presentationAudioLevel = strongSelf.presentationAudioLevel * 0.9 + strongSelf.audioLevel * 0.1 strongSelf.updateAudioLevel() //strongSelf.mediumBlob.level = strongSelf.presentationAudioLevel //strongSelf.bigBlob.level = strongSelf.presentationAudioLevel } updateInHierarchy = { [weak self] value in if let strongSelf = self { strongSelf.isCurrentlyInHierarchy = value } } } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } public func setColor(_ color: UIColor) { //self.mediumBlob.setColor(color.withAlphaComponent(0.5)) //self.bigBlob.setColor(color.withAlphaComponent(0.21)) } public func updateLevel(_ level: CGFloat, immediately: Bool) { let normalizedLevel = min(1, max(level / maxLevel, 0)) //self.mediumBlob.updateSpeedLevel(to: normalizedLevel) //self.bigBlob.updateSpeedLevel(to: normalizedLevel) self.audioLevel = normalizedLevel if immediately { self.presentationAudioLevel = normalizedLevel } } private func updateAudioLevel() { let additionalAvatarScale = CGFloat(max(0.0, min(self.presentationAudioLevel * 18.0, 5.0)) * 0.05) let blobAmplificationFactor: CGFloat = 2.0 let blobScale = 1.0 + additionalAvatarScale * blobAmplificationFactor self.blobsLayer.transform = CATransform3DMakeScale(blobScale, blobScale, 1.0) self.scaleUpdated?(blobScale) } public func startAnimating() { guard !self.isAnimating else { return } self.isAnimating = true self.updateBlobsState() self.displayLinkAnimator?.isPaused = false } public func stopAnimating() { self.stopAnimating(duration: 0.15) } public func stopAnimating(duration: Double) { guard isAnimating else { return } self.isAnimating = false self.updateBlobsState() self.displayLinkAnimator?.isPaused = true } private func updateBlobsState() { /*if self.isAnimating { if self.mediumBlob.frame.size != .zero { self.mediumBlob.startAnimating() self.bigBlob.startAnimating() } } else { self.mediumBlob.stopAnimating() self.bigBlob.stopAnimating() }*/ } override public func layoutSubviews() { super.layoutSubviews() //self.mediumBlob.frame = bounds //self.bigBlob.frame = bounds let blobsFrame = bounds.insetBy(dx: floor(bounds.width * 0.12), dy: floor(bounds.height * 0.12)) self.blobsLayer.position = blobsFrame.center self.blobsLayer.bounds = CGRect(origin: CGPoint(), size: blobsFrame.size) self.updateBlobsState() } }