From aafc704c7182ef8dffc2c210bcc638df6c7ca0f2 Mon Sep 17 00:00:00 2001 From: Ilya Yelagov Date: Wed, 7 Dec 2022 06:18:14 +0400 Subject: [PATCH] Adding blurred "last frame" and tweaking counter animation --- .../Components/AnimatedCounterView.swift | 165 ++++++++++-------- .../MediaStreamVideoComponent.swift | 50 ++++-- 2 files changed, 131 insertions(+), 84 deletions(-) diff --git a/submodules/TelegramCallsUI/Sources/Components/AnimatedCounterView.swift b/submodules/TelegramCallsUI/Sources/Components/AnimatedCounterView.swift index e57fa01fb3..71fa96d44a 100644 --- a/submodules/TelegramCallsUI/Sources/Components/AnimatedCounterView.swift +++ b/submodules/TelegramCallsUI/Sources/Components/AnimatedCounterView.swift @@ -391,82 +391,101 @@ class AnimatedCountLabel: UILabel { // layer.add(animation, forKey: "opacity") // // - let beginTimeOffset: CFTimeInterval = /*beginTime == .zero ? 0 :*/ /*CFTimeInterval(DispatchTime.now().uptimeNanoseconds / 1000000000)*/ layer.convertTime(CACurrentMediaTime(), to: nil) - - let opacityInAnimation = CABasicAnimation(keyPath: "opacity") - opacityInAnimation.fromValue = 1 - opacityInAnimation.toValue = 0 -// opacityInAnimation.duration = duration -// opacityInAnimation.beginTime = beginTimeOffset + beginTime -// opacityInAnimation.completion = { _ in -// layer.removeFromSuperlayer() -// } -// layer.add(opacityInAnimation, forKey: "opacity") - -// let timer = Timer.scheduledTimer(withTimeInterval: duration + beginTime, repeats: false) { timer in - DispatchQueue.main.asyncAfter(deadline: .now() + duration * 0.95 + beginTime) { -// DispatchQueue.main.async { -// layer.backgroundColor = UIColor.red.withAlphaComponent(0.3).cgColor -// } -// DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - layer.removeFromSuperlayer() -// } -// timer.invalidate() - } -// RunLoop.current.add(timer, forMode: .common) - - let scaleOutAnimation = CABasicAnimation(keyPath: "transform.scale") - scaleOutAnimation.fromValue = 1 // layer.presentation()?.value(forKey: "transform.scale") ?? 1 - scaleOutAnimation.toValue = 0.0 -// scaleOutAnimation.duration = duration -// scaleOutAnimation.beginTime = beginTimeOffset + beginTime -// layer.add(scaleOutAnimation, forKey: "scaleout") - - let translate = CABasicAnimation(keyPath: "transform.translation") - translate.fromValue = CGPoint.zero - translate.toValue = CGPoint(x: 0, y: -layer.bounds.height * 0.3)// -layer.bounds.height + 3.0) -// translate.duration = duration -// translate.beginTime = beginTimeOffset + beginTime -// layer.add(translate, forKey: "translate") - - let group = CAAnimationGroup() - group.animations = [opacityInAnimation, scaleOutAnimation, translate] - group.duration = duration - group.beginTime = beginTimeOffset + beginTime - layer.add(group, forKey: "out") + let beginTimeOffset: CFTimeInterval = 0/*beginTime == .zero ? 0 :*/ // CFTimeInterval(DispatchTime.now().uptimeNanoseconds / 1000000000) /*layer.convertTime(*/// CACurrentMediaTime()//, to: nil) + DispatchQueue.main.asyncAfter(deadline: .now() + beginTime) { + let currentTime = CFTimeInterval(DispatchTime.now().uptimeNanoseconds / 1000000000) + let beginTime: CFTimeInterval = 0 + print("[DIFF-out] \(currentTime - beginTimeOffset)") + let opacityInAnimation = CABasicAnimation(keyPath: "opacity") + opacityInAnimation.fromValue = 1 + opacityInAnimation.toValue = 0 + opacityInAnimation.fillMode = .forwards + opacityInAnimation.isRemovedOnCompletion = false + // opacityInAnimation.duration = duration + // opacityInAnimation.beginTime = beginTimeOffset + beginTime + // opacityInAnimation.completion = { _ in + // layer.removeFromSuperlayer() + // } + // layer.add(opacityInAnimation, forKey: "opacity") + + // let timer = Timer.scheduledTimer(withTimeInterval: duration + beginTime, repeats: false) { timer in + // DispatchQueue.main.asyncAfter(deadline: .now() + duration + beginTime) { + // DispatchQueue.main.async { + // layer.backgroundColor = UIColor.red.withAlphaComponent(0.3).cgColor + // } + // DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + // layer.removeFromSuperlayer() + // } + // timer.invalidate() + // } + // RunLoop.current.add(timer, forMode: .common) + + let scaleOutAnimation = CABasicAnimation(keyPath: "transform.scale") + scaleOutAnimation.fromValue = 1 // layer.presentation()?.value(forKey: "transform.scale") ?? 1 + scaleOutAnimation.toValue = 0.0 + // scaleOutAnimation.duration = duration + // scaleOutAnimation.beginTime = beginTimeOffset + beginTime + // layer.add(scaleOutAnimation, forKey: "scaleout") + + let translate = CABasicAnimation(keyPath: "transform.translation") + translate.fromValue = CGPoint.zero + translate.toValue = CGPoint(x: 0, y: -layer.bounds.height * 0.3)// -layer.bounds.height + 3.0) + // translate.duration = duration + // translate.beginTime = beginTimeOffset + beginTime + // layer.add(translate, forKey: "translate") + + let group = CAAnimationGroup() + group.animations = [opacityInAnimation, scaleOutAnimation, translate] + group.duration = duration + group.beginTime = beginTimeOffset + beginTime + group.fillMode = .forwards + group.isRemovedOnCompletion = false + group.completion = { _ in + layer.removeFromSuperlayer() + } + // layer.opacity = 0 + layer.add(group, forKey: "out") + } } func animateIn(for newLayer: CALayer, duration: CFTimeInterval, beginTime: CFTimeInterval) { - newLayer.opacity = 0 - // newLayer.backgroundColor = UIColor.red.cgColor - let opacityInAnimation = CABasicAnimation(keyPath: "opacity") - opacityInAnimation.fromValue = 0 - opacityInAnimation.toValue = 1 - opacityInAnimation.duration = duration - opacityInAnimation.beginTime = CACurrentMediaTime() + beginTime -// opacityInAnimation.isAdditive = true - opacityInAnimation.fillMode = .backwards - newLayer.opacity = 1 - newLayer.add(opacityInAnimation, forKey: "opacity") -// newLayer.opacity = 1 - - let scaleOutAnimation = CABasicAnimation(keyPath: "transform.scale") - scaleOutAnimation.fromValue = 0 - scaleOutAnimation.toValue = 1 - scaleOutAnimation.duration = duration - scaleOutAnimation.beginTime = CACurrentMediaTime() + beginTime -// scaleOutAnimation.isAdditive = true - newLayer.add(scaleOutAnimation, forKey: "scalein") - - let animation = CAKeyframeAnimation() - animation.keyPath = "position.y" - animation.values = [20, -6, 0] - animation.keyTimes = [0, 0.64, 1] - animation.timingFunction = CAMediaTimingFunction.init(name: .easeInEaseOut) - animation.duration = duration / 0.64 - animation.beginTime = CACurrentMediaTime() + beginTime - animation.isAdditive = true - newLayer.add(animation, forKey: "pos") + let beginTimeOffset: CFTimeInterval = 0// CFTimeInterval(DispatchTime.now().uptimeNanoseconds / 1000000000)// CACurrentMediaTime() + DispatchQueue.main.asyncAfter(deadline: .now() + beginTime) { + let currentTime = CFTimeInterval(DispatchTime.now().uptimeNanoseconds / 1000000000) + let beginTime: CFTimeInterval = 0 + print("[DIFF-in] \(currentTime - beginTimeOffset)") + newLayer.opacity = 0 + // newLayer.backgroundColor = UIColor.red.cgColor + + let opacityInAnimation = CABasicAnimation(keyPath: "opacity") + opacityInAnimation.fromValue = 0 + opacityInAnimation.toValue = 1 + opacityInAnimation.duration = duration + opacityInAnimation.beginTime = beginTimeOffset + beginTime + // opacityInAnimation.isAdditive = true + opacityInAnimation.fillMode = .backwards + newLayer.opacity = 1 + newLayer.add(opacityInAnimation, forKey: "opacity") + // newLayer.opacity = 1 + + let scaleOutAnimation = CABasicAnimation(keyPath: "transform.scale") + scaleOutAnimation.fromValue = 0 + scaleOutAnimation.toValue = 1 + scaleOutAnimation.duration = duration + scaleOutAnimation.beginTime = beginTimeOffset + beginTime + // scaleOutAnimation.isAdditive = true + newLayer.add(scaleOutAnimation, forKey: "scalein") + + let animation = CAKeyframeAnimation() + animation.keyPath = "position.y" + animation.values = [20, -6, 0] + animation.keyTimes = [0, 0.64, 1] + animation.timingFunction = CAMediaTimingFunction.init(name: .easeInEaseOut) + animation.duration = duration / 0.64 + animation.beginTime = beginTimeOffset + beginTime + animation.isAdditive = true + newLayer.add(animation, forKey: "pos") + } } } diff --git a/submodules/TelegramCallsUI/Sources/Components/MediaStreamVideoComponent.swift b/submodules/TelegramCallsUI/Sources/Components/MediaStreamVideoComponent.swift index 6566f3c2e8..beb759e362 100644 --- a/submodules/TelegramCallsUI/Sources/Components/MediaStreamVideoComponent.swift +++ b/submodules/TelegramCallsUI/Sources/Components/MediaStreamVideoComponent.swift @@ -11,6 +11,28 @@ import ShimmerEffect import TelegramCore typealias MediaStreamVideoComponent = _MediaStreamVideoComponent +class CustomIntensityVisualEffectView: UIVisualEffectView { + + /// Create visual effect view with given effect and its intensity + /// + /// - Parameters: + /// - effect: visual effect, eg UIBlurEffect(style: .dark) + /// - intensity: custom intensity from 0.0 (no effect) to 1.0 (full effect) using linear scale + init(effect: UIVisualEffect, intensity: CGFloat) { + super.init(effect: nil) + animator = UIViewPropertyAnimator(duration: 1, curve: .linear) { [unowned self] in self.effect = effect } + animator.fractionComplete = intensity + } + + required init?(coder aDecoder: NSCoder) { + fatalError() + } + + // MARK: Private + private var animator: UIViewPropertyAnimator! + +} + final class _MediaStreamVideoComponent: Component { let call: PresentationGroupCallImpl let hasVideo: Bool @@ -103,7 +125,7 @@ final class _MediaStreamVideoComponent: Component { private var videoPlaceholderView: UIView? private var noSignalView: ComponentHostView? - private let loadingBlurView = UIVisualEffectView(effect: UIBlurEffect(style: .light)) + private let loadingBlurView = CustomIntensityVisualEffectView(effect: UIBlurEffect(style: .light), intensity: 0.4) private let shimmerOverlayView = CALayer() private var pictureInPictureController: AVPictureInPictureController? @@ -155,23 +177,24 @@ final class _MediaStreamVideoComponent: Component { func update(component: _MediaStreamVideoComponent, availableSize: CGSize, state: State, transition: Transition) -> CGSize { self.state = state - if component.videoLoading && placeholderView.superview == nil { - addSubview(placeholderView) - } - placeholderView.alpha = 0.7 +// placeholderView.alpha = 0.7 // placeholderView.image = lastFrame[component.call.peerId.id.description] if let frame = lastFrame[component.call.peerId.id.description] { + placeholderView.subviews.forEach { $0.removeFromSuperview() } placeholderView.addSubview(frame) frame.frame = placeholderView.bounds - placeholderView.backgroundColor = .green +// placeholderView.backgroundColor = .green } else { - placeholderView.subviews.forEach { $0.removeFromSuperview() } - placeholderView.backgroundColor = .red +// placeholderView.subviews.forEach { $0.removeFromSuperview() } +// placeholderView.backgroundColor = .red } placeholderView.backgroundColor = .red if component.videoLoading { + if placeholderView.superview == nil { + addSubview(placeholderView) + } if loadingBlurView.superview == nil { -// addSubview(loadingBlurView) + addSubview(loadingBlurView) } if shimmerOverlayLayer.superlayer == nil { loadingBlurView.layer.addSublayer(shimmerOverlayLayer) @@ -194,8 +217,14 @@ final class _MediaStreamVideoComponent: Component { shimmerBorderLayer.mask = borderMask borderShimmer.layer = borderMask borderShimmer.testUpdate(background: .clear, foreground: .white) + loadingBlurView.alpha = 1 } else { - loadingBlurView.removeFromSuperview() + UIView.animate(withDuration: 0.2, animations: { + self.loadingBlurView.alpha = 0 + }, completion: { _ in + self.loadingBlurView.removeFromSuperview() + }) + placeholderView.removeFromSuperview() } if component.hasVideo, self.videoView == nil { @@ -216,7 +245,6 @@ final class _MediaStreamVideoComponent: Component { if let videoView = self.videoRenderingContext.makeView(input: input, blur: false, forceSampleBufferDisplayLayer: true) { self.videoView = videoView - self.placeholderView.removeFromSuperview() self.addSubview(videoView) videoView.alpha = 0 UIView.animate(withDuration: 0.3) {