Refactoring

This commit is contained in:
Ilya Yelagov
2022-12-08 08:43:31 +04:00
parent 073920c5ec
commit cd09a54fe4
6 changed files with 1226 additions and 9038 deletions

View File

@@ -1,465 +0,0 @@
/*import Foundation
import UIKit
import Display
private let purple = UIColor(rgb: 0x3252ef)
private let pink = UIColor(rgb: 0xe4436c)
private let latePurple = UIColor(rgb: 0x974aa9)
private let latePink = UIColor(rgb: 0xf0436c)
public final class AnimatedCountView: UIView {
let countLabel = AnimatedCountLabel()
// let titleLabel = UILabel()
let subtitleLabel = UILabel()
private let foregroundView = UIView()
private let foregroundGradientLayer = CAGradientLayer()
private let maskingView = UIView()
override init(frame: CGRect = .zero) {
super.init(frame: frame)
self.foregroundGradientLayer.type = .radial
self.foregroundGradientLayer.colors = [pink.cgColor, purple.cgColor, purple.cgColor]
self.foregroundGradientLayer.locations = [0.0, 0.85, 1.0]
self.foregroundGradientLayer.startPoint = CGPoint(x: 1.0, y: 0.0)
self.foregroundGradientLayer.endPoint = CGPoint(x: 0.0, y: 1.0)
self.foregroundView.mask = self.maskingView
self.foregroundView.layer.addSublayer(self.foregroundGradientLayer)
self.addSubview(self.foregroundView)
// self.addSubview(self.titleLabel)
self.addSubview(self.subtitleLabel)
self.maskingView.addSubview(countLabel)
countLabel.clipsToBounds = false
subtitleLabel.textAlignment = .center
// self.backgroundColor = UIColor.white.withAlphaComponent(0.1)
}
override public func layoutSubviews() {
super.layoutSubviews()
self.foregroundView.frame = CGRect(origin: CGPoint.zero, size: bounds.size)// .insetBy(dx: -40, dy: -40)
self.foregroundGradientLayer.frame = CGRect(origin: .zero, size: bounds.size).insetBy(dx: -60, dy: -60)
self.maskingView.frame = CGRect(origin: .zero, size: bounds.size)
countLabel.frame = CGRect(origin: .zero, size: bounds.size)
subtitleLabel.frame = .init(x: bounds.midX - subtitleLabel.intrinsicContentSize.width / 2 - 10, y: subtitleLabel.text == "No viewers" ? bounds.midY - 10 : bounds.height - 6, width: subtitleLabel.intrinsicContentSize.width + 20, height: 20)
}
func update(countString: String, subtitle: String) {
self.setupGradientAnimations()
let text: String = countString// presentationStringsFormattedNumber(Int32(count), ",")
// self.titleNode.attributedText = NSAttributedString(string: "", font: Font.with(size: 23.0, design: .round, weight: .semibold, traits: []), textColor: .white)
// let titleSize = self.titleNode.updateLayout(size)
// self.titleNode.frame = CGRect(x: floor((size.width - titleSize.width) / 2.0), y: 48.0, width: titleSize.width, height: titleSize.height)
if CGFloat(text.count * 40) < bounds.width - 32 {
self.countLabel.attributedText = NSAttributedString(string: text, font: Font.with(size: 60.0, design: .round, weight: .semibold, traits: [.monospacedNumbers]), textColor: .white)
} else {
self.countLabel.attributedText = NSAttributedString(string: text, font: Font.with(size: 54.0, design: .round, weight: .semibold, traits: [.monospacedNumbers]), textColor: .white)
}
// var timerSize = self.timerNode.updateLayout(CGSize(width: size.width + 100.0, height: size.height))
// if timerSize.width > size.width - 32.0 {
// self.timerNode.attributedText = NSAttributedString(string: text, font: Font.with(size: 60.0, design: .round, weight: .semibold, traits: [.monospacedNumbers]), textColor: .white)
// timerSize = self.timerNode.updateLayout(CGSize(width: size.width + 100.0, height: size.height))
// }
// self.timerNode.frame = CGRect(x: floor((size.width - timerSize.width) / 2.0), y: 78.0, width: timerSize.width, height: timerSize.height)
self.subtitleLabel.attributedText = NSAttributedString(string: subtitle, font: Font.with(size: 16.0, design: .round, weight: .semibold, traits: []), textColor: .white)
self.subtitleLabel.isHidden = subtitle.isEmpty
// let subtitleSize = self.subtitleNode.updateLayout(size)
// self.subtitleNode.frame = CGRect(x: floor((size.width - subtitleSize.width) / 2.0), y: 164.0, width: subtitleSize.width, height: subtitleSize.height)
// self.foregroundView.frame = CGRect(origin: CGPoint(), size: size)
// self.setNeedsLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupGradientAnimations() {
if let _ = self.foregroundGradientLayer.animation(forKey: "movement") {
} else {
let previousValue = self.foregroundGradientLayer.startPoint
let newValue = CGPoint(x: CGFloat.random(in: 0.65 ..< 0.85), y: CGFloat.random(in: 0.1 ..< 0.45))
self.foregroundGradientLayer.startPoint = newValue
CATransaction.begin()
let animation = CABasicAnimation(keyPath: "startPoint")
animation.duration = Double.random(in: 0.8 ..< 1.4)
animation.fromValue = previousValue
animation.toValue = newValue
CATransaction.setCompletionBlock { [weak self] in
// if let isCurrentlyInHierarchy = self?.isCurrentlyInHierarchy, isCurrentlyInHierarchy {
self?.setupGradientAnimations()
// }
}
self.foregroundGradientLayer.add(animation, forKey: "movement")
CATransaction.commit()
}
}
}
class AnimatedCharLayer: CATextLayer {
var text: String? {
get {
self.string as? String ?? (self.string as? NSAttributedString)?.string
}
set {
self.string = newValue
}
}
var attributedText: NSAttributedString? {
get {
self.string as? NSAttributedString //?? (self.string as? String).map { NSAttributed.init
}
set {
self.string = newValue
}
}
var layer: CALayer { self }
override init() {
super.init()
self.contentsScale = UIScreen.main.scale
}
override init(layer: Any) {
super.init(layer: layer)
self.contentsScale = UIScreen.main.scale
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class AnimatedCountLabel: UILabel {
override var text: String? {
get {
chars.reduce("") { $0 + ($1.text ?? "") }
}
set {
update(with: newValue ?? "")
}
}
override var attributedText: NSAttributedString? {
get {
let string = NSMutableAttributedString()
for char in chars {
string.append(char.attributedText ?? NSAttributedString())
}
return string
}
set {
udpateAttributed(with: newValue ?? NSAttributedString())
}
}
private var chars = [AnimatedCharLayer]()
private let containerView = UIView()
override init(frame: CGRect = .zero) {
super.init(frame: frame)
containerView.clipsToBounds = false
addSubview(containerView)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var itemWidth: CGFloat { 36 }
var commaWidth: CGFloat { 8 }
var interItemSpacing: CGFloat { 0 }
private func offsetForChar(at index: Int, within characters: [NSAttributedString]? = nil) -> CGFloat {
if let characters {
return characters[0..<index].reduce(0) {
if $1.string == "," {
return $0 + commaWidth + interItemSpacing
}
return $0 + itemWidth + interItemSpacing
}
} else {
return self.chars[0..<index].reduce(0) {
if $1.attributedText?.string == "," {
return $0 + commaWidth + interItemSpacing
}
return $0 + itemWidth + interItemSpacing
}
}
}
override func layoutSubviews() {
super.layoutSubviews()
let countWidth = offsetForChar(at: chars.count) /*chars.reduce(0) {
if $1.attributedText?.string == "," {
return $0 + commaWidth + interItemSpacing
}
return $0 + itemWidth + interItemSpacing
}*/ - interItemSpacing
containerView.frame = .init(x: bounds.midX - countWidth / 2, y: 0, width: countWidth, height: bounds.height)
chars.enumerated().forEach { (index, char) in
let offset = offsetForChar(at: index)
char.frame.origin.x = offset
// char.frame.origin.x = CGFloat(chars.count - 1 - index) * (40 + interItemSpacing)
char.frame.origin.y = 0
}
}
/// Unused
func update(with newString: String) {
/*let itemWidth: CGFloat = 40
let initialDuration: TimeInterval = 0.3
let newChars = Array(newString).map { String($0) }
let currentChars = chars.map { $0.text ?? "X" }
// let currentWidth = itemWidth * CGFloat(currentChars.count)
let newWidth = itemWidth * CGFloat(newChars.count)
let interItemDelay: TimeInterval = 0.15
var changeIndex = 0
var newLayers = [AnimatedCharLayer]()
for index in 0..<min(newChars.count, currentChars.count) {
let newCharIndex = newChars.count - 1 - index
let currCharIndex = currentChars.count - 1 - index
if true || newChars[newCharIndex] != currentChars[currCharIndex] {
animateOut(for: chars[currCharIndex].layer, duration: initialDuration, beginTime: TimeInterval(changeIndex) * interItemDelay)
let newLayer = AnimatedCharLayer()
newLayer.text = newChars[newCharIndex]
newLayer.frame = .init(x: newWidth - CGFloat(index + 1) * itemWidth, y: 100, width: itemWidth, height: 36)
containerView.layer.addSublayer(newLayer)
animateIn(for: newLayer.layer, duration: initialDuration, beginTime: TimeInterval(changeIndex) * interItemDelay)
newLayers.append(newLayer)
changeIndex += 1
} else {
newLayers.append(chars[currCharIndex])
}
}
for index in min(newChars.count, currentChars.count)..<currentChars.count {
let currCharIndex = currentChars.count - 1 - index
// remove unused
animateOut(for: chars[currCharIndex].layer, duration: initialDuration, beginTime: TimeInterval(changeIndex) * interItemDelay)
changeIndex += 1
}
for index in min(newChars.count, currentChars.count)..<newChars.count {
let newCharIndex = newChars.count - 1 - index
let newLayer = AnimatedCharLayer()
newLayer.text = newChars[newCharIndex]
newLayer.frame = .init(x: newWidth - CGFloat(index + 1) * itemWidth, y: 100, width: itemWidth, height: 36)
containerView.layer.addSublayer(newLayer)
animateIn(for: newLayer.layer, duration: initialDuration, beginTime: TimeInterval(changeIndex) * interItemDelay)
newLayers.append(newLayer)
changeIndex += 1
}
chars = newLayers*/
}
func udpateAttributed(with newString: NSAttributedString) {
let interItemSpacing: CGFloat = 0
let separatedStrings = Array(newString.string).map { String($0) }
var range = NSRange(location: 0, length: 0)
var newChars = [NSAttributedString]()
for string in separatedStrings {
range.length = string.count
let attributedString = newString.attributedSubstring(from: range)
newChars.append(attributedString)
range.location += range.length
}
let currentChars = chars.map { $0.attributedText ?? .init() }
let maxAnimationDuration: TimeInterval = 0.5
var numberOfChanges = abs(newChars.count - currentChars.count)
for index in 0..<min(newChars.count, currentChars.count) {
let newCharIndex = newChars.count - 1 - index
let currCharIndex = currentChars.count - 1 - index
if newChars[newCharIndex] != currentChars[currCharIndex] {
numberOfChanges += 1
}
}
let initialDuration: TimeInterval = min(0.25, maxAnimationDuration / Double(numberOfChanges)) /// 0.25
// let currentWidth = itemWidth * CGFloat(currentChars.count)
// let newWidth = itemWidth * CGFloat(newChars.count)
let interItemDelay: TimeInterval = 0.08
var changeIndex = 0
var newLayers = [AnimatedCharLayer]()
for index in 0..<min(newChars.count, currentChars.count) {
let newCharIndex = newChars.count - 1 - index
let currCharIndex = currentChars.count - 1 - index
if true || newChars[newCharIndex] != currentChars[currCharIndex] {
let initialDuration = newChars[newCharIndex] != currentChars[currCharIndex] ? initialDuration : 0
if newChars[newCharIndex] != currentChars[currCharIndex] {
animateOut(for: chars[currCharIndex].layer, duration: initialDuration, beginTime: TimeInterval(changeIndex) * interItemDelay)
} else {
chars[currCharIndex].layer.removeFromSuperlayer()
}
let newLayer = AnimatedCharLayer()
newLayer.attributedText = newChars[newCharIndex]
let offset = offsetForChar(at: newCharIndex, within: newChars)/* newChars[0..<newCharIndex].reduce(0) {
if $1.string == "," {
return $0 + commaWidth + interItemSpacing
}
return $0 + itemWidth + interItemSpacing
}*/
newLayer.frame = .init(x: offset/*CGFloat(newCharIndex) * (40 + interItemSpacing)*/, y: 0, width: itemWidth, height: itemWidth * 1.8)
// newLayer.frame = .init(x: CGFloat(chars.count - 1 - index) * (40 + interItemSpacing), y: 0, width: itemWidth, height: itemWidth * 1.8)
containerView.layer.addSublayer(newLayer)
if newChars[newCharIndex] != currentChars[currCharIndex] {
newLayer.layer.opacity = 0
animateIn(for: newLayer.layer, duration: initialDuration, beginTime: TimeInterval(changeIndex) * interItemDelay)
changeIndex += 1
}
newLayers.append(newLayer)
} else {
newLayers.append(chars[currCharIndex])
}
}
for index in min(newChars.count, currentChars.count)..<currentChars.count {
let currCharIndex = currentChars.count - 1 - index
// remove unused
animateOut(for: chars[currCharIndex].layer, duration: initialDuration, beginTime: TimeInterval(changeIndex) * interItemDelay)
changeIndex += 1
}
for index in min(newChars.count, currentChars.count)..<newChars.count {
let newCharIndex = newChars.count - 1 - index
let newLayer = AnimatedCharLayer()
newLayer.attributedText = newChars[newCharIndex]
let offset = offsetForChar(at: newCharIndex, within: newChars)/*newChars[0..<newCharIndex].reduce(0) {
if $1.string == "," {
return $0 + commaWidth + interItemSpacing
}
return $0 + itemWidth + interItemSpacing
}*/
newLayer.frame = .init(x: offset/*CGFloat(newCharIndex) * (40 + interItemSpacing)*/, y: 0, width: itemWidth, height: itemWidth * 1.8)
containerView.layer.addSublayer(newLayer)
animateIn(for: newLayer.layer, duration: initialDuration, beginTime: TimeInterval(changeIndex) * interItemDelay)
newLayers.append(newLayer)
changeIndex += 1
}
let prevCount = chars.count
chars = newLayers.reversed()
let countWidth = offsetForChar(at: newChars.count, within: newChars) - interItemSpacing/*newChars.reduce(-interItemSpacing) {
if $1.string == "," {
return $0 + commaWidth + interItemSpacing
}
return $0 + itemWidth + interItemSpacing
}*/
if didBegin && prevCount != chars.count {
UIView.animate(withDuration: Double(changeIndex) * initialDuration/*, delay: initialDuration * Double(changeIndex)*/) { [self] in
containerView.frame = .init(x: self.bounds.midX - countWidth / 2, y: 0, width: countWidth, height: self.bounds.height)
// containerView.backgroundColor = .red.withAlphaComponent(0.3)
}
} else {
containerView.frame = .init(x: self.bounds.midX - countWidth / 2, y: 0, width: countWidth, height: self.bounds.height)
didBegin = true
}
// self.backgroundColor = .green.withAlphaComponent(0.2)
self.clipsToBounds = false
}
var didBegin = false
func animateOut(for layer: CALayer, duration: CFTimeInterval, beginTime: CFTimeInterval) {
// let animation = CAKeyframeAnimation()
// animation.keyPath = "opacity"
// animation.values = [layer.presentation()?.value(forKey: "opacity") ?? 1, 0.0]
// animation.keyTimes = [0, 1]
// animation.duration = duration
// animation.beginTime = CACurrentMediaTime() + beginTime
//// animation.isAdditive = true
// animation.isRemovedOnCompletion = false
// animation.fillMode = .backwards
// layer.opacity = 0
// layer.add(animation, forKey: "opacity")
//
//
let opacityInAnimation = CABasicAnimation(keyPath: "opacity")
opacityInAnimation.fromValue = 1
opacityInAnimation.toValue = 0
opacityInAnimation.duration = duration
opacityInAnimation.beginTime = CACurrentMediaTime() + beginTime
DispatchQueue.main.asyncAfter(deadline: .now() + duration + beginTime) {
layer.removeFromSuperlayer()
}
let scaleOutAnimation = CABasicAnimation(keyPath: "transform.scale")
scaleOutAnimation.fromValue = 1 // layer.presentation()?.value(forKey: "transform.scale") ?? 1
scaleOutAnimation.toValue = 0.1
scaleOutAnimation.duration = duration
scaleOutAnimation.beginTime = CACurrentMediaTime() + 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 = CACurrentMediaTime() + beginTime
layer.add(translate, forKey: "translate")
}
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 = [18, -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")
}
}
*/

View File

@@ -1,7 +1,6 @@
import Foundation
import UIKit
import ComponentFlow
import ActivityIndicatorComponent
import AccountContext
import AVKit
import MultilineTextComponent
@@ -13,59 +12,24 @@ import SwiftSignalKit
import AvatarNode
import Postbox
typealias MediaStreamVideoComponent = _MediaStreamVideoComponent
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.startAnimation()
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) {
fatalError()
}
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(_:) {
//
// }
}
final class _MediaStreamVideoComponent: Component {
final class MediaStreamVideoComponent: Component {
let call: PresentationGroupCallImpl
let hasVideo: Bool
let isVisible: Bool
@@ -75,7 +39,6 @@ final class _MediaStreamVideoComponent: Component {
let deactivatePictureInPicture: ActionSlot<Void>
let bringBackControllerForPictureInPictureDeactivation: (@escaping () -> Void) -> Void
let pictureInPictureClosed: () -> Void
let peerImage: Any?
let isFullscreen: Bool
let onVideoSizeRetrieved: (CGSize) -> Void
let videoLoading: Bool
@@ -88,7 +51,6 @@ final class _MediaStreamVideoComponent: Component {
isVisible: Bool,
isAdmin: Bool,
peerTitle: String,
peerImage: Any?,
isFullscreen: Bool,
videoLoading: Bool,
callPeer: Peer?,
@@ -112,12 +74,11 @@ final class _MediaStreamVideoComponent: Component {
self.onVideoPlaybackLiveChange = onVideoPlaybackLiveChange
self.callPeer = callPeer
self.peerImage = peerImage
self.isFullscreen = isFullscreen
self.onVideoSizeRetrieved = onVideoSizeRetrieved
}
public static func ==(lhs: _MediaStreamVideoComponent, rhs: _MediaStreamVideoComponent) -> Bool {
public static func ==(lhs: MediaStreamVideoComponent, rhs: MediaStreamVideoComponent) -> Bool {
if lhs.call !== rhs.call {
return false
}
@@ -160,7 +121,6 @@ final class _MediaStreamVideoComponent: Component {
private let blurTintView: UIView
private var videoBlurView: VideoRenderingView?
private var videoView: VideoRenderingView?
private var activityIndicatorView: ComponentHostView<Empty>?
private var loadingView: ComponentHostView<Empty>?
private var videoPlaceholderView: UIView?
@@ -169,7 +129,7 @@ final class _MediaStreamVideoComponent: Component {
private let shimmerOverlayView = CALayer()
private var pictureInPictureController: AVPictureInPictureController?
private var component: _MediaStreamVideoComponent?
private var component: MediaStreamVideoComponent?
private var hadVideo: Bool = false
private var requestedExpansion: Bool = false
@@ -209,9 +169,7 @@ final class _MediaStreamVideoComponent: Component {
}
let maskGradientLayer = CAGradientLayer()
private var wasVisible = true
var shimmer = StandaloneShimmerEffect()
var borderShimmer = StandaloneShimmerEffect()
let shimmerOverlayLayer = CALayer()
let shimmerBorderLayer = CALayer()
let placeholderView = UIImageView()
@@ -230,24 +188,12 @@ final class _MediaStreamVideoComponent: Component {
private func updateVideoStalled(isStalled: Bool) {
if isStalled {
guard let component = self.component else { return }
// let effect = UIBlurEffect(style: .light)
// let intensity: CGFloat = 0.4
// self.loadingBlurView.effect = nil
// self.loadingBlurView.animator.stopAnimation(true)
// self.loadingBlurView.animator = UIViewPropertyAnimator(duration: 1, curve: .linear) { [unowned loadingBlurView] in loadingBlurView.effect = effect }
// self.loadingBlurView.animator.fractionComplete = intensity
// self.loadingBlurView.animator.fractionComplete = 0.4
// self.loadingBlurView.effect = UIBlurEffect(style: .light)
if let frameView = lastFrame[component.call.peerId.id.description] {
frameView.removeFromSuperview()
placeholderView.subviews.forEach { $0.removeFromSuperview() }
placeholderView.addSubview(frameView)
frameView.frame = placeholderView.bounds
// placeholderView.backgroundColor = .green
} else {
// placeholderView.addSubview(avatarPlaceholderView)
// placeholderView.subviews.forEach { $0.removeFromSuperview() }
// placeholderView.backgroundColor = .red
}
if !hadVideo && placeholderView.superview == nil {
@@ -273,19 +219,11 @@ final class _MediaStreamVideoComponent: Component {
loadingBlurView.contentView.layer.addSublayer(shimmerBorderLayer)
}
loadingBlurView.clipsToBounds = true
// 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
let cornerRadius = loadingBlurView.layer.cornerRadius
// shimmerOverlayLayer.opacity = 0.6
shimmerBorderLayer.cornerRadius = cornerRadius // TODO: check isFullScreeen
shimmerBorderLayer.cornerRadius = cornerRadius
shimmerBorderLayer.masksToBounds = true
shimmerBorderLayer.compositingFilter = "overlayBlendMode"// "softLightBlendMode"
shimmerBorderLayer.compositingFilter = "softLightBlendMode"
shimmerBorderLayer.frame = loadingBlurView.bounds
let borderMask = CAShapeLayer()
borderMask.path = CGPath(roundedRect: .init(x: 0, y: 0, width: shimmerBorderLayer.bounds.width, height: shimmerBorderLayer.bounds.height), cornerWidth: cornerRadius, cornerHeight: cornerRadius, transform: nil)
@@ -293,27 +231,13 @@ final class _MediaStreamVideoComponent: Component {
borderMask.strokeColor = UIColor.white.withAlphaComponent(0.7).cgColor
borderMask.lineWidth = 3
shimmerBorderLayer.mask = borderMask
// 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()
// shimmerBorderLayer.removeAllAnimations()
// if shimmerBorderLayer.mask == nil {
borderShimmer = .init()
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 {
loadingBlurView.layer.removeAllAnimations()
let anim = CABasicAnimation(keyPath: "opacity")
anim.duration = 0.5
anim.fromValue = 1
@@ -324,6 +248,7 @@ final class _MediaStreamVideoComponent: Component {
guard self?.videoStalled == false else { return }
self?.loadingBlurView.removeFromSuperview()
self?.placeholderView.removeFromSuperview()
self?.loadingBlurView.layer.removeAllAnimations()
}
loadingBlurView.layer.add(anim, forKey: "opacity")
} else {
@@ -376,7 +301,7 @@ final class _MediaStreamVideoComponent: Component {
frameInputDisposable?.dispose()
}
func update(component: _MediaStreamVideoComponent, availableSize: CGSize, state: State, transition: Transition) -> CGSize {
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]
@@ -422,25 +347,12 @@ final class _MediaStreamVideoComponent: Component {
// TODO: use mapToThrottled (?)
frameInputDisposable = input.start(next: { [weak self] input in
guard let strongSelf = self else { return }
// print("input")
// strongSelf.stallTimer?.invalidate()
// TODO: optimize with throttle
strongSelf.timeLastFrameReceived = CFAbsoluteTimeGetCurrent()
// DispatchQueue.main.async {
// strongSelf.stallTimer = _stallTimer
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
// print(strongSelf.videoStalled)
// if strongSelf.videoStalled {
// strongSelf.stallTimer?.fire()
// }
// RunLoop.main.add(strongSelf.stallTimer!, forMode: .common)
strongSelf.videoLoadingThrottler.publish(false, includingLatest: true) { isStalled in
strongSelf.videoStalled = isStalled
strongSelf.onVideoPlaybackChange(!isStalled)
}
// strongSelf.videoStalled = false
// strongSelf.onVideoPlaybackChange(true)
// }
})
stallTimer = _stallTimer
// RunLoop.main.add(stallTimer!, forMode: .common)
@@ -542,9 +454,6 @@ final class _MediaStreamVideoComponent: Component {
strongSelf.hadVideo = true
strongSelf.activityIndicatorView?.removeFromSuperview()
strongSelf.activityIndicatorView = nil
strongSelf.noSignalTimer?.invalidate()
strongSelf.noSignalTimer = nil
strongSelf.noSignalTimeout = false
@@ -582,11 +491,10 @@ final class _MediaStreamVideoComponent: Component {
let videoSize: CGSize
let videoCornerRadius: CGFloat = component.isFullscreen ? 0 : 10
if let videoView = self.videoView {
// TODO: REMOVE FROM HERE and move to call end (or at least to background)
// if let presentation = videoView.snapshotView(afterScreenUpdates: false) {
if videoView.bounds.size.width > 0,
videoView.alpha > 0,
self.hadVideo,
// TODO: remove from here and move to call end (or at least to background)
let snapshot = videoView.snapshotView(afterScreenUpdates: false) ?? videoView.snapshotView(afterScreenUpdates: true) {
lastFrame[component.call.peerId.id.description] = snapshot// ()!
}
@@ -632,14 +540,6 @@ final class _MediaStreamVideoComponent: Component {
videoView.updateIsEnabled(isVideoVisible)
videoView.clipsToBounds = true
videoView.layer.cornerRadius = videoCornerRadius
// var aspect = videoView.getAspect()
// if aspect <= 0.01 {
// TODO: remove debug
// if component.videoLoading {
// videoView.alpha = 0.5
// } else {
// videoView.alpha = 1
// }
transition.withAnimation(.none).setFrame(view: videoView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - videoSize.width) / 2.0), y: floor((availableSize.height - videoSize.height) / 2.0)), size: videoSize), completion: nil)
@@ -685,31 +585,6 @@ final class _MediaStreamVideoComponent: Component {
// loadingBlurView.removeFromSuperview()
}
if !self.hadVideo {
// TODO: hide fullscreen button without video
let aspect: CGFloat = 16.0 / 9
let videoSize = CGSize(width: aspect * 100.0, height: 100.0).aspectFitted(.init(width: availableSize.width - videoInset * 2, height: availableSize.height))
// loadingpreview.frame = .init(, videoSize)
print(videoSize)
// TODO: remove activity indicator
var activityIndicatorTransition = transition
let activityIndicatorView: ComponentHostView<Empty>
if let current = self.activityIndicatorView {
activityIndicatorView = current
} else {
activityIndicatorTransition = transition.withAnimation(.none)
activityIndicatorView = ComponentHostView<Empty>()
self.activityIndicatorView = activityIndicatorView
// self.addSubview(activityIndicatorView)
}
let activityIndicatorSize = activityIndicatorView.update(
transition: transition,
component: AnyComponent(ActivityIndicatorComponent(color: .white)),
environment: {},
containerSize: CGSize(width: 100.0, height: 100.0)
)
let activityIndicatorFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - activityIndicatorSize.width) / 2.0), y: floor((availableSize.height - activityIndicatorSize.height) / 2.0)), size: activityIndicatorSize)
activityIndicatorTransition.setFrame(view: activityIndicatorView, frame: activityIndicatorFrame, completion: nil)
if self.noSignalTimer == nil {
if #available(iOS 10.0, *) {
@@ -750,7 +625,7 @@ final class _MediaStreamVideoComponent: Component {
environment: {},
containerSize: CGSize(width: availableSize.width - 16.0 * 2.0, height: 1000.0)
)
noSignalTransition.setFrame(view: noSignalView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - noSignalSize.width) / 2.0), y: (availableSize.height - noSignalSize.height) / 2.0/*activityIndicatorFrame.maxY + 24.0*/), size: noSignalSize), completion: nil)
noSignalTransition.setFrame(view: noSignalView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - noSignalSize.width) / 2.0), y: (availableSize.height - noSignalSize.height) / 2.0), size: noSignalSize), completion: nil)
}
}
@@ -825,7 +700,7 @@ final class _MediaStreamVideoComponent: Component {
} else {
self.component?.pictureInPictureClosed()
}
// TODO: extract precise animation or observe window changes
// TODO: extract precise animation timing or observe window changes
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
self.videoView?.alpha = 1
}
@@ -850,23 +725,4 @@ final class _MediaStreamVideoComponent: Component {
}
// TODO: move to appropriate place
var lastFrame: [String: UIView] = [:]
extension UIView {
func snapshot() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(bounds.size, true, UIScreen.main.scale)
guard let currentContext = UIGraphicsGetCurrentContext() else {
UIGraphicsEndImageContext()
return nil
}
layer.render(in: currentContext)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
fileprivate var lastFrame: [String: UIView] = [:]

View File

@@ -8,10 +8,7 @@ import MultilineTextComponent
import Display
final class StreamSheetComponent: CombinedComponent {
// let color: UIColor
// let leftItem: AnyComponent<Empty>?
let topComponent: AnyComponent<Empty>?
// let viewerCounter: AnyComponent<Empty>?
let bottomButtonsRow: AnyComponent<Empty>?
// TODO: sync
let sheetHeight: CGFloat
@@ -24,7 +21,6 @@ final class StreamSheetComponent: CombinedComponent {
let videoHeight: CGFloat
init(
// color: UIColor,
topComponent: AnyComponent<Empty>,
bottomButtonsRow: AnyComponent<Empty>,
topOffset: CGFloat,
@@ -36,9 +32,7 @@ final class StreamSheetComponent: CombinedComponent {
deviceCornerRadius: CGFloat,
videoHeight: CGFloat
) {
// self.leftItem = leftItem
self.topComponent = topComponent
// self.viewerCounter = AnyComponent(ViewerCountComponent(count: 0))
self.bottomButtonsRow = bottomButtonsRow
self.topOffset = topOffset
self.sheetHeight = sheetHeight
@@ -83,7 +77,7 @@ final class StreamSheetComponent: CombinedComponent {
}
return true
}
//
final class View: UIView {
var overlayComponentsFrames = [CGRect]()
@@ -95,7 +89,6 @@ final class StreamSheetComponent: CombinedComponent {
}
func update(component: StreamSheetComponent, availableSize: CGSize, state: State, transition: Transition) -> CGSize {
// self.backgroundColor = .purple.withAlphaComponent(0.6)
return availableSize
}