mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-02 00:17:02 +00:00
Adding avatar placeholder and tweaking blur animation
This commit is contained in:
parent
c65ad5ad97
commit
c28d2bc888
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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?
|
||||
|
Loading…
x
Reference in New Issue
Block a user