Adding avatar placeholder and tweaking blur animation

This commit is contained in:
Ilya Yelagov 2022-12-07 20:02:12 +04:00
parent c65ad5ad97
commit c28d2bc888
3 changed files with 157 additions and 65 deletions

View File

@ -19,6 +19,8 @@ import CreateExternalMediaStreamScreen
import HierarchyTrackingLayer
import UndoPanelComponent
import AvatarNode
final class StreamTitleComponent: Component {
let text: String
let isRecording: Bool
@ -1034,6 +1036,7 @@ public final class _MediaStreamComponent: CombinedComponent {
peerImage: nil,
isFullscreen: isFullscreen,
videoLoading: context.state.videoStalled,
callPeer: context.state.chatPeer,
activatePictureInPicture: activatePictureInPicture,
deactivatePictureInPicture: deactivatePictureInPicture,
bringBackControllerForPictureInPictureDeactivation: { [weak call] completed in

View File

@ -10,6 +10,8 @@ import ShimmerEffect
import TelegramCore
import SwiftSignalKit
import AvatarNode
import Postbox
typealias MediaStreamVideoComponent = _MediaStreamVideoComponent
@ -17,7 +19,24 @@ class CustomIntensityVisualEffectView: UIVisualEffectView {
init(effect: UIVisualEffect, intensity: CGFloat) {
super.init(effect: nil)
animator = UIViewPropertyAnimator(duration: 1, curve: .linear) { [unowned self] in self.effect = effect }
self.animator?.startAnimation()
self.animator?.pauseAnimation()
animator.fractionComplete = intensity
animator.pausesOnCompletion = true
// subviews.forEach {
// if $0.backgroundColor != nil {
// $0.backgroundColor = $0.backgroundColor?.withAlphaComponent(0.5)
// }
// }
}
override func didMoveToSuperview() {
super.didMoveToSuperview()
// let effect = self.effect
// self.effect = nil
// animator = UIViewPropertyAnimator(duration: 1, curve: .linear) { [unowned self] in self.effect = effect }
// animator.fractionComplete = 0.1// intensity
// animator.pausesOnCompletion = true
}
required init?(coder aDecoder: NSCoder) {
@ -26,24 +45,24 @@ class CustomIntensityVisualEffectView: UIVisualEffectView {
var animator: UIViewPropertyAnimator!
private var displayLink: CADisplayLink?
func setIntensity(_ intensity: CGFloat, animated: Bool) {
self.displayLink?.invalidate()
let displaylink = CADisplayLink(
target: self,
selector: #selector(displayLinkStep)
)
self.displayLink = displaylink
displaylink.add(
to: .current,
forMode: RunLoop.Mode.default
)
}
@objc func displayLinkStep() {
}
// private var displayLink: CADisplayLink?
//
// func setIntensity(_ intensity: CGFloat, animated: Bool) {
// self.displayLink?.invalidate()
// let displaylink = CADisplayLink(
// target: self,
// selector: #selector(displayLinkStep)
// )
// self.displayLink = displaylink
// displaylink.add(
// to: .current,
// forMode: RunLoop.Mode.default
// )
// }
//
// @objc func displayLinkStep(_:) {
//
// }
}
final class _MediaStreamVideoComponent: Component {
@ -60,6 +79,8 @@ final class _MediaStreamVideoComponent: Component {
let isFullscreen: Bool
let onVideoSizeRetrieved: (CGSize) -> Void
let videoLoading: Bool
let callPeer: Peer?
init(
call: PresentationGroupCallImpl,
hasVideo: Bool,
@ -69,6 +90,7 @@ final class _MediaStreamVideoComponent: Component {
peerImage: Any?,
isFullscreen: Bool,
videoLoading: Bool,
callPeer: Peer?,
activatePictureInPicture: ActionSlot<Action<Void>>,
deactivatePictureInPicture: ActionSlot<Void>,
bringBackControllerForPictureInPictureDeactivation: @escaping (@escaping () -> Void) -> Void,
@ -86,6 +108,7 @@ final class _MediaStreamVideoComponent: Component {
self.bringBackControllerForPictureInPictureDeactivation = bringBackControllerForPictureInPictureDeactivation
self.pictureInPictureClosed = pictureInPictureClosed
self.callPeer = callPeer
self.peerImage = peerImage
self.isFullscreen = isFullscreen
self.onVideoSizeRetrieved = onVideoSizeRetrieved
@ -216,6 +239,7 @@ final class _MediaStreamVideoComponent: Component {
frame.frame = placeholderView.bounds
// placeholderView.backgroundColor = .green
} else {
// placeholderView.addSubview(avatarPlaceholderView)
// placeholderView.subviews.forEach { $0.removeFromSuperview() }
// placeholderView.backgroundColor = .red
}
@ -225,63 +249,128 @@ final class _MediaStreamVideoComponent: Component {
}
if loadingBlurView.superview == nil {
addSubview(loadingBlurView)
let anim = CABasicAnimation(keyPath: "opacity")
anim.duration = 0.5
anim.fromValue = 0
anim.toValue = 1
anim.fillMode = .forwards
anim.isRemovedOnCompletion = false
loadingBlurView.layer.add(anim, forKey: "opacity")
}
if shimmerOverlayLayer.superlayer == nil {
loadingBlurView.layer.addSublayer(shimmerOverlayLayer)
loadingBlurView.layer.addSublayer(shimmerBorderLayer)
if shimmerBorderLayer.superlayer == nil {
// loadingBlurView.contentView.layer.addSublayer(shimmerOverlayLayer)
loadingBlurView.contentView.layer.addSublayer(shimmerBorderLayer)
}
loadingBlurView.clipsToBounds = true
shimmer = .init()
shimmer.layer = shimmerOverlayLayer
shimmerOverlayView.compositingFilter = "softLightBlendMode"
shimmer.testUpdate(background: .clear, foreground: .white.withAlphaComponent(0.4))
if shimmerOverlayLayer.mask == nil {
shimmer = .init()
shimmer.layer = shimmerOverlayLayer
shimmerOverlayView.compositingFilter = "softLightBlendMode"
shimmer.testUpdate(background: .clear, foreground: .white.withAlphaComponent(0.4))
}
loadingBlurView.layer.cornerRadius = 10
shimmerOverlayLayer.opacity = 0.6
shimmerBorderLayer.cornerRadius = 10
shimmerBorderLayer.masksToBounds = true
shimmerBorderLayer.compositingFilter = "softLightBlendMode"
shimmerBorderLayer.frame = loadingBlurView.bounds
let borderMask = CAShapeLayer()
borderMask.path = CGPath(roundedRect: .init(x: 0, y: 0, width: loadingBlurView.bounds.width, height: loadingBlurView.bounds.height), cornerWidth: 10, cornerHeight: 10, transform: nil)
borderMask.fillColor = UIColor.clear.cgColor
borderMask.strokeColor = UIColor.white.cgColor
borderMask.lineWidth = 4
borderMask.path = CGPath(roundedRect: .init(x: 0, y: 0, width: shimmerBorderLayer.bounds.width, height: shimmerBorderLayer.bounds.height), cornerWidth: 10, cornerHeight: 10, transform: nil)
borderMask.fillColor = UIColor.white.withAlphaComponent(0.4).cgColor
borderMask.strokeColor = UIColor.white.withAlphaComponent(0.8).cgColor
borderMask.lineWidth = 2
borderMask.frame = shimmerBorderLayer.bounds
// let testBorder = CAShapeLayer()
// testBorder.path = CGPath(roundedRect: .init(x: 0, y: 0, width: shimmerBorderLayer.bounds.width, height: shimmerBorderLayer.bounds.height), cornerWidth: 10, cornerHeight: 10, transform: nil)
// testBorder.fillColor = UIColor.white.withAlphaComponent(0.2).cgColor
// testBorder.strokeColor = UIColor.white.cgColor
// testBorder.lineWidth = 4
// testBorder.frame = shimmerBorderLayer.bounds
// let borderMask = CALayer()
borderShimmer = .init()
shimmerBorderLayer.mask = borderMask
borderShimmer.layer = shimmerBorderLayer
borderShimmer.testUpdate(background: .clear, foreground: .white)
// shimmerBorderLayer.removeAllAnimations()
// if shimmerBorderLayer.mask == nil {
borderShimmer = .init()
shimmerBorderLayer.mask = borderMask
borderShimmer.layer = shimmerBorderLayer
shimmerBorderLayer.backgroundColor = UIColor.clear.cgColor
// shimmerBorderLayer.backgroundColor = UIColor.green.withAlphaComponent(0.4).cgColor
borderShimmer.testUpdate(background: .clear, foreground: .white)
// }
loadingBlurView.alpha = 1
} else {
if hadVideo {
self.loadingBlurView.removeFromSuperview()
placeholderView.removeFromSuperview()
} else {
// Accounting for delay in first frame received
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [self] in
guard !self.videoStalled else { return }
UIView.transition(with: self, duration: 0.2, animations: {
// self.loadingBlurView.animator.fractionComplete = 0
// self.loadingBlurView.effect = nil
self.loadingBlurView.alpha = 0
}, completion: { _ in
self.loadingBlurView.removeFromSuperview()
})
loadingBlurView.layer.removeAllAnimations()
let anim = CABasicAnimation(keyPath: "opacity")
anim.duration = 0.5
anim.fromValue = 1
anim.toValue = 0
anim.fillMode = .forwards
anim.isRemovedOnCompletion = false
anim.completion = { [self] _ in
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [self] in
loadingBlurView.removeFromSuperview()
// loadingBlurView = .init(effect: UIBlurEffect(style: .light), intensity: 0.4)
placeholderView.removeFromSuperview()
}
loadingBlurView.layer.add(anim, forKey: "opacity")
} else {
// Accounting for delay in first frame received
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [self] in
guard !self.videoStalled else { return }
// TODO: animate blur intesity with UIPropertyAnimator
loadingBlurView.layer.removeAllAnimations()
let anim = CABasicAnimation(keyPath: "opacity")
anim.duration = 0.5
anim.fromValue = 1
anim.toValue = 0
anim.fillMode = .forwards
anim.isRemovedOnCompletion = false
anim.completion = { _ in
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [self] in
self.loadingBlurView.removeFromSuperview()
self.placeholderView.removeFromSuperview()
}
loadingBlurView.layer.add(anim, forKey: "opacity")
// UIView.transition(with: self, duration: 0.2, animations: {
//// self.loadingBlurView.animator.fractionComplete = 0
//// self.loadingBlurView.effect = nil
//// self.loadingBlurView.alpha = 0
// }, completion: { _ in
// self.loadingBlurView = .init(effect: UIBlurEffect(style: .light), intensity: 0.4)
// })
}
}
// loadingBlurView.backgroundColor = .yellow.withAlphaComponent(0.4)
}
}
var stallTimer: Foundation.Timer?
let fullScreenBackgroundPlaceholder = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
var avatarDisposable: Disposable?
var didBeginLoadingAvatar = false
// let avatarPlaceholderView = UIImageView()
func update(component: _MediaStreamVideoComponent, availableSize: CGSize, state: State, transition: Transition) -> CGSize {
self.state = state
// placeholderView.alpha = 0.7
// placeholderView.image = lastFrame[component.call.peerId.id.description]
self.component = component
if let peer = component.callPeer, !didBeginLoadingAvatar {
didBeginLoadingAvatar = true
avatarDisposable = peerAvatarCompleteImage(account: component.call.account, peer: EnginePeer(peer), size: CGSize(width: 250.0, height: 250.0), round: false, font: Font.regular(16.0), drawLetters: false, fullSize: false, blurred: true).start(next: { [weak self] image in
DispatchQueue.main.async {
self?.placeholderView.contentMode = .scaleAspectFill
self?.placeholderView.image = image
}
})
}
if component.videoLoading || self.videoStalled {
updateVideoStalled(isStalled: true)
/*if let frame = lastFrame[component.call.peerId.id.description] {
@ -359,9 +448,9 @@ final class _MediaStreamVideoComponent: Component {
frameInputDisposable = input.start(next: { [weak self] input in
guard let strongSelf = self else { return }
print("input")
strongSelf.stallTimer?.invalidate()
// TODO: optimize with throttle
DispatchQueue.main.async {
strongSelf.stallTimer?.invalidate()
strongSelf.stallTimer = _stallTimer
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
// print(strongSelf.videoStalled)
@ -488,7 +577,7 @@ final class _MediaStreamVideoComponent: Component {
fullScreenBackgroundPlaceholder.removeFromSuperview()
} else if component.isFullscreen {
if fullScreenBackgroundPlaceholder.superview == nil {
insertSubview(fullScreenBackgroundPlaceholder, at: 0)
// insertSubview(fullScreenBackgroundPlaceholder, at: 0)
}
fullScreenBackgroundPlaceholder.frame = self.bounds
}
@ -584,10 +673,10 @@ final class _MediaStreamVideoComponent: Component {
placeholderView.frame = loadingBlurView.frame
placeholderView.layer.cornerRadius = 10
placeholderView.clipsToBounds = true
// avatarPlaceholderView.frame = placeholderView.bounds
shimmerOverlayLayer.frame = loadingBlurView.bounds
shimmerBorderLayer.frame = loadingBlurView.bounds
shimmerBorderLayer.mask?.frame = loadingBlurView.bounds
// shimmerBorderLayer.mask?.frame = loadingBlurView.bounds
if component.isFullscreen {
loadingBlurView.removeFromSuperview()
}

View File

@ -36,12 +36,12 @@ import DeviceAccess
//let panelBackgroundColor = UIColor(rgb: 0x1c1c1e)
//let secondaryPanelBackgroundColor = UIColor(rgb: 0x2c2c2e)
//let fullscreenBackgroundColor = UIColor(rgb: 0x000000)
private let smallButtonSize = CGSize(width: 36.0, height: 36.0)
private let sideButtonSize = CGSize(width: 56.0, height: 56.0)
private let topPanelHeight: CGFloat = 63.0
//private let smallButtonSize = CGSize(width: 36.0, height: 36.0)
//private let sideButtonSize = CGSize(width: 56.0, height: 56.0)
//private let topPanelHeight: CGFloat = 63.0
//let bottomAreaHeight: CGFloat = 206.0
private let fullscreenBottomAreaHeight: CGFloat = 80.0
private let bottomGradientHeight: CGFloat = 70.0
//private let fullscreenBottomAreaHeight: CGFloat = 80.0
//private let bottomGradientHeight: CGFloat = 70.0
/*func decorationCornersImage(top: Bool, bottom: Bool, dark: Bool) -> UIImage? {
if !top && !bottom {
@ -1853,16 +1853,16 @@ public final class MediaStreamingControllerImpl: ViewController, VoiceChatContro
self.contentContainer.addSubnode(self.backgroundNode)
self.contentContainer.addSubnode(self.listContainer)
self.contentContainer.addSubnode(self.topPanelNode)
self.listContainer.addSubnode(self.listNode)
self.listContainer.addSubnode(self.leftBorderNode)
self.listContainer.addSubnode(self.rightBorderNode)
self.listContainer.addSubnode(self.bottomCornersNode)
// self.contentContainer.addSubnode(self.listContainer)
// self.contentContainer.addSubnode(self.topPanelNode)
// self.listContainer.addSubnode(self.listNode)
// self.listContainer.addSubnode(self.leftBorderNode)
// self.listContainer.addSubnode(self.rightBorderNode)
// self.listContainer.addSubnode(self.bottomCornersNode)
self.listContainer.addSubnode(self.topCornersNode)
self.contentContainer.addSubnode(self.bottomGradientNode)
self.contentContainer.addSubnode(self.bottomPanelBackgroundNode)
// self.contentContainer.addSubnode(self.participantsNode)
// self.contentContainer.addSubnode(self.bottomPanelBackgroundNode)
self.contentContainer.addSubnode(self.participantsNode)
self.contentContainer.addSubnode(self.tileGridNode)
self.contentContainer.addSubnode(self.mainStageContainerNode)
self.contentContainer.addSubnode(self.transitionContainerNode)
@ -4010,7 +4010,7 @@ public final class MediaStreamingControllerImpl: ViewController, VoiceChatContro
let participantsFrame = CGRect(x: 0.0, y: bottomCornersFrame.maxY - 100.0, width: size.width, height: 216.0)
transition.updateFrame(node: self.participantsNode, frame: participantsFrame)
self.participantsNode.update(size: participantsFrame.size, participants: self.currentTotalCount, groupingSeparator: self.presentationData.dateTimeFormat.groupingSeparator, transition: .immediate)
self.participantsNode.update(size: participantsFrame.size, participants: Int32.random(in: 0..<999999999)/*self.currentTotalCount*/, groupingSeparator: self.presentationData.dateTimeFormat.groupingSeparator, transition: .immediate)
}
private var decorationsAreDark: Bool?