Stylizing elements

This commit is contained in:
Ilya Yelagov 2022-11-27 20:20:31 +04:00
parent a23580635f
commit bfd81f6a0b
3 changed files with 110 additions and 169 deletions

View File

@ -19,90 +19,6 @@ import CreateExternalMediaStreamScreen
import HierarchyTrackingLayer
import UndoPanelComponent
final class NavigationBackButtonComponent: Component {
let text: String
let color: UIColor
init(text: String, color: UIColor) {
self.text = text
self.color = color
}
static func ==(lhs: NavigationBackButtonComponent, rhs: NavigationBackButtonComponent) -> Bool {
if lhs.text != rhs.text {
return false
}
if lhs.color != rhs.color {
return false
}
return false
}
public final class View: UIView {
private let arrowView: UIImageView
private let textView: ComponentHostView<Empty>
private var component: NavigationBackButtonComponent?
override init(frame: CGRect) {
self.arrowView = UIImageView()
self.textView = ComponentHostView<Empty>()
super.init(frame: frame)
self.addSubview(self.arrowView)
self.addSubview(self.textView)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(component: NavigationBackButtonComponent, availableSize: CGSize, transition: Transition) -> CGSize {
let spacing: CGFloat = 6.0
let innerArrowInset: CGFloat = -8.0
if self.component?.color != component.color {
self.arrowView.image = NavigationBarTheme.generateBackArrowImage(color: component.color)
}
self.component = component
let textSize = self.textView.update(
transition: .immediate,
component: AnyComponent(Text(
text: component.text,
font: Font.regular(17.0),
color: component.color
)),
environment: {},
containerSize: availableSize
)
var leftInset: CGFloat = 0.0
var size = textSize
if let arrowImage = self.arrowView.image {
size.width += innerArrowInset + arrowImage.size.width + spacing
size.height = max(size.height, arrowImage.size.height)
self.arrowView.frame = CGRect(origin: CGPoint(x: innerArrowInset, y: floor((size.height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
leftInset += innerArrowInset + arrowImage.size.width + spacing
}
self.textView.frame = CGRect(origin: CGPoint(x: leftInset, y: floor((size.height - textSize.height) / 2.0)), size: textSize)
return size
}
}
public func makeView() -> View {
return View(frame: CGRect())
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, transition: transition)
}
}
final class StreamTitleComponent: Component {
let text: String
let isRecording: Bool
@ -137,10 +53,13 @@ final class StreamTitleComponent: Component {
addSubview(label)
label.text = "LIVE"
label.font = .systemFont(ofSize: 10, weight: .medium)
label.font = .systemFont(ofSize: 12, weight: .semibold)
label.textAlignment = .center
layer.addSublayer(stalledAnimatedGradient)
self.clipsToBounds = true
if #available(iOS 13.0, *) {
self.layer.cornerCurve = .continuous
}
toggle(isLive: false)
}
@ -161,15 +80,32 @@ final class StreamTitleComponent: Component {
if isLive {
if !wasLive {
// TODO: animate
wasLive = true
let frame = self.frame
UIView.animate(withDuration: 0.15, animations: {
self.toggle(isLive: true)
self.transform = .init(scaleX: 1.5, y: 1.5)
}, completion: { _ in
UIView.animate(withDuration: 0.15) {
self.transform = .identity
self.frame = frame
}
})
return
}
self.backgroundColor = .systemPink
self.backgroundColor = UIColor(red: 0.82, green: 0.26, blue: 0.37, alpha: 1)
stalledAnimatedGradient.opacity = 0
stalledAnimatedGradient.removeAllAnimations()
} else {
if wasLive {
// TODO: animate
wasLive = false
UIView.animate(withDuration: 0.3) {
self.toggle(isLive: false)
}
return
}
self.backgroundColor = .gray
self.backgroundColor = UIColor(white: 0.36, alpha: 1)
stalledAnimatedGradient.opacity = 1
// stalledAnimatedGradient.add(<#T##anim: CAAnimation##CAAnimation#>, forKey: <#T##String?#>)
}
@ -247,13 +183,13 @@ final class StreamTitleComponent: Component {
indicatorView.removeFromSuperview()
}
}
liveIndicatorView.toggle(isLive: component.isActive)
let sideInset: CGFloat = 20.0
let size = CGSize(width: textSize.width + sideInset * 2.0, height: textSize.height)
let textFrame = CGRect(origin: CGPoint(x: sideInset, y: floor((size.height - textSize.height) / 2.0)), size: textSize)
self.textView.frame = textFrame
liveIndicatorView.frame = CGRect(origin: CGPoint(x: textFrame.maxX + 6.0, y: floorToScreenPixels((size.height - textSize.height) / 2.0) + 1.0), size: .init(width: 40, height: 18))
liveIndicatorView.frame = CGRect(origin: CGPoint(x: textFrame.maxX + 6.0, y: floorToScreenPixels((size.height - textSize.height) / 2.0 - 2) + 1.0), size: .init(width: 40, height: 22))
self.liveIndicatorView.toggle(isLive: component.isActive)
if let indicatorView = self.indicatorView, let image = indicatorView.image {
indicatorView.frame = CGRect(origin: CGPoint(x: liveIndicatorView.frame.maxX + 6.0, y: floorToScreenPixels((size.height - image.size.height) / 2.0) + 1.0), size: image.size)
@ -377,9 +313,10 @@ private final class NavigationBarComponent: CombinedComponent {
}
// let maxCenterInset = max(centerLeftInset, centerRightInset)
let someUndesiredOffset: CGFloat = 16
if let centerItem = centerItem {
context.add(centerItem
.position(CGPoint(x: context.availableSize.width / 2 /*maxCenterInset + (context.availableSize.width - maxCenterInset - maxCenterInset) / 2.0*/, y: context.component.topInset + contentHeight / 2.0))
.position(CGPoint(x: context.availableSize.width / 2 - someUndesiredOffset /*maxCenterInset + (context.availableSize.width - maxCenterInset - maxCenterInset) / 2.0*/, y: context.component.topInset + contentHeight / 2.0))
)
}
@ -785,6 +722,7 @@ public final class _MediaStreamComponent: CombinedComponent {
private(set) var hasVideo: Bool = false
private var stateDisposable: Disposable?
private var infoDisposable: Disposable?
private var connectionDisposable: Disposable?
private(set) var originInfo: OriginInfo?
@ -808,13 +746,14 @@ public final class _MediaStreamComponent: CombinedComponent {
private var isVisibleInHierarchyDisposable: Disposable?
private var scheduledDismissUITimer: SwiftSignalKit.Timer?
var videoStalled: Bool = false
let deactivatePictureInPictureIfVisible = StoredActionSlot(Void.self)
var videoHiddenForPip = false
/// To update videoHiddenForPip
var onExpandedFromPictureInPicture: ((State) -> Void)?
private let infoThrottler = Throttler<Int?>.init(duration: 5, queue: .main)
private let infoThrottler = Throttler<Int>.init(duration: 5, queue: .main)
init(call: PresentationGroupCallImpl) {
self.call = call
@ -845,6 +784,15 @@ public final class _MediaStreamComponent: CombinedComponent {
strongSelf.updated(transition: .immediate)
})
self.connectionDisposable = call.state.start(next: { [weak self] state in
switch state.networkState {
case .connected:
self?.videoStalled = false
default:
self?.videoStalled = true
}
})
let callPeer = call.accountContext.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: call.peerId))
self.infoDisposable = (combineLatest(queue: .mainQueue(), call.state, call.members, callPeer)
@ -855,11 +803,12 @@ public final class _MediaStreamComponent: CombinedComponent {
var updated = false
// TODO: remove debug
Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { _ in
strongSelf.infoThrottler.publish(members.totalCount/*Int.random(in: 0..<10000000)*/) { [weak strongSelf] latestCount in
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in
strongSelf.infoThrottler.publish(/*members.totalCount*/Int.random(in: 0..<10000000)) { [weak strongSelf] latestCount in
guard let strongSelf = strongSelf else { return }
var updated = false
let originInfo = OriginInfo(title: callPeer.debugDisplayTitle, memberCount: members.totalCount)
print(members)
let originInfo = OriginInfo(title: callPeer.debugDisplayTitle, memberCount: latestCount)
if strongSelf.originInfo != originInfo {
strongSelf.originInfo = originInfo
updated = true
@ -927,6 +876,7 @@ public final class _MediaStreamComponent: CombinedComponent {
self.stateDisposable?.dispose()
self.infoDisposable?.dispose()
self.isVisibleInHierarchyDisposable?.dispose()
self.connectionDisposable?.dispose()
}
func toggleDisplayUI() {
@ -1015,11 +965,14 @@ public final class _MediaStreamComponent: CombinedComponent {
}
var isFullscreen = state.isFullscreen
let isLandscape = context.availableSize.width > context.availableSize.height
if let _ = context.state.videoSize {
if let videoSize = context.state.videoSize {
// Always fullscreen in landscape
if /*videoSize.width > videoSize.height &&*/ isLandscape && !isFullscreen {
state.isFullscreen = true
isFullscreen = true
} else if videoSize.width > videoSize.height && !isLandscape && isFullscreen {
state.isFullscreen = false
isFullscreen = false
}
}
@ -1033,6 +986,7 @@ public final class _MediaStreamComponent: CombinedComponent {
// TODO: find out how to get image
peerImage: nil,
isFullscreen: isFullscreen,
videoLoading: context.state.videoStalled,
activatePictureInPicture: activatePictureInPicture,
deactivatePictureInPicture: deactivatePictureInPicture,
bringBackControllerForPictureInPictureDeactivation: { [weak call] completed in
@ -1061,9 +1015,16 @@ public final class _MediaStreamComponent: CombinedComponent {
// let contextView = context.view
if context.state.isPictureInPictureSupported, context.state.hasVideo {
navigationRightItems.append(AnyComponentWithIdentity(id: "pip", component: AnyComponent(Button(
content: AnyComponent(BundleIconComponent(
name: "Media Gallery/PictureInPictureButton",
tintColor: .white
content: AnyComponent(ZStack([
AnyComponentWithIdentity(id: "b", component: AnyComponent(Circle(
fillColor: .white.withAlphaComponent(0.08),
size: CGSize(width: 32.0, height: 32.0)
))),
AnyComponentWithIdentity(id: "a", component: AnyComponent(BundleIconComponent(
name: "Media Gallery/PictureInPictureButton",
tintColor: .white
)))
]
)),
action: {
activatePictureInPicture.invoke(Action {
@ -1084,9 +1045,8 @@ public final class _MediaStreamComponent: CombinedComponent {
AnyComponent(Button(
content: AnyComponent(ZStack([
AnyComponentWithIdentity(id: "b", component: AnyComponent(Circle(
strokeColor: .white,
strokeWidth: 1.5,
size: CGSize(width: 22.0, height: 22.0)
fillColor: .white.withAlphaComponent(0.08),
size: CGSize(width: 32.0, height: 32.0)
))),
AnyComponentWithIdentity(id: "a", component: AnyComponent(LottieAnimationComponent(
animation: LottieAnimationComponent.AnimationItem(
@ -1098,7 +1058,7 @@ public final class _MediaStreamComponent: CombinedComponent {
"Point 3.Group 1.Fill 1": whiteColor,
"Point 1.Group 1.Fill 1": whiteColor
],
size: CGSize(width: 22.0, height: 22.0)
size: CGSize(width: 32.0, height: 32.0)
).tagged(moreAnimationTag))),
])),
action: { [weak call, weak state] in
@ -1322,7 +1282,7 @@ public final class _MediaStreamComponent: CombinedComponent {
})
)*/,
rightItems: navigationRightItems,
centerItem: AnyComponent(StreamTitleComponent(text: state.peerTitle, isRecording: state.recordingStartTimestamp != nil, isActive: call.hasVideo))
centerItem: AnyComponent(StreamTitleComponent(text: state.peerTitle, isRecording: state.recordingStartTimestamp != nil, isActive: context.state.hasVideo))
)
// let navigationBar = navigationBar.update(
@ -1356,7 +1316,7 @@ public final class _MediaStreamComponent: CombinedComponent {
let videoHeight: CGFloat = context.availableSize.width / 16 * 9
let bottomPadding = 40 + environment.safeInsets.bottom
let sheetHeight: CGFloat = isFullscreen ? context.availableSize.height : (44 + videoHeight + 40 + 69 + 32 + 70 + bottomPadding)
let sheetHeight: CGFloat = isFullscreen ? context.availableSize.height : (44 + videoHeight + 40 + 69 + 16 + 32 + 70 + bottomPadding)
let isFullyDragged = context.availableSize.height - sheetHeight + state.dismissOffset < 30
context.add(background
@ -1519,8 +1479,8 @@ public final class _MediaStreamComponent: CombinedComponent {
sheetHeight: max(sheetHeight - context.state.dismissOffset, sheetHeight),
backgroundColor: isFullscreen ? .clear : (isFullyDragged ? fullscreenBackgroundColor : panelBackgroundColor),
bottomPadding: bottomPadding,
participantsCount: // [0, 5, 15, 16, 95, 100, 16042, 942539].randomElement()!
context.state.originInfo?.memberCount ?? 0
participantsCount: context.state.originInfo?.memberCount ?? 0 // Int.random(in: 0...999998)// [0, 5, 15, 16, 95, 100, 16042, 942539].randomElement()!
//
),
availableSize: context.availableSize,
transition: context.transition
@ -1610,6 +1570,7 @@ public final class _MediaStreamComponent: CombinedComponent {
)
context.add(fullScreenOverlayComponent
.position(.init(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2))
.opacity(state.displayUI ? 1 : 0)
)
}

View File

@ -24,6 +24,7 @@ final class _MediaStreamVideoComponent: Component {
let peerImage: Any?
let isFullscreen: Bool
let onVideoSizeRetrieved: (CGSize) -> Void
let videoLoading: Bool
init(
call: PresentationGroupCallImpl,
hasVideo: Bool,
@ -32,6 +33,7 @@ final class _MediaStreamVideoComponent: Component {
peerTitle: String,
peerImage: Any?,
isFullscreen: Bool,
videoLoading: Bool,
activatePictureInPicture: ActionSlot<Action<Void>>,
deactivatePictureInPicture: ActionSlot<Void>,
bringBackControllerForPictureInPictureDeactivation: @escaping (@escaping () -> Void) -> Void,
@ -43,6 +45,7 @@ final class _MediaStreamVideoComponent: Component {
self.isVisible = isVisible
self.isAdmin = isAdmin
self.peerTitle = peerTitle
self.videoLoading = videoLoading
self.activatePictureInPicture = activatePictureInPicture
self.deactivatePictureInPicture = deactivatePictureInPicture
self.bringBackControllerForPictureInPictureDeactivation = bringBackControllerForPictureInPictureDeactivation
@ -100,7 +103,8 @@ final class _MediaStreamVideoComponent: Component {
private var videoPlaceholderView: UIView?
private var noSignalView: ComponentHostView<Empty>?
private let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
private let shimmerOverlayView = CALayer()
private var pictureInPictureController: AVPictureInPictureController?
private var component: _MediaStreamVideoComponent?
@ -181,7 +185,6 @@ final class _MediaStreamVideoComponent: Component {
if let videoView = self.videoRenderingContext.makeView(input: input, blur: false, forceSampleBufferDisplayLayer: true) {
self.videoView = videoView
self.addSubview(videoView)
videoView.alpha = 1
if let sampleBufferVideoView = videoView as? SampleBufferVideoRenderingView {
if #available(iOS 13.0, *) {
sampleBufferVideoView.sampleBufferLayer.preventsDisplaySleepDuringVideoPlayback = true
@ -262,6 +265,8 @@ final class _MediaStreamVideoComponent: Component {
strongSelf.noSignalView?.removeFromSuperview()
strongSelf.noSignalView = nil
let snapshot = strongSelf.videoView?.snapshotView(afterScreenUpdates: true)
strongSelf.addSubview(snapshot ?? UIVisualEffectView(effect: UIBlurEffect(style: .dark)))
state?.updated(transition: .immediate)
}
}
@ -316,7 +321,12 @@ final class _MediaStreamVideoComponent: Component {
videoView.layer.cornerRadius = component.isFullscreen ? 0 : 10
// 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)
@ -354,7 +364,7 @@ final class _MediaStreamVideoComponent: Component {
activityIndicatorTransition = transition.withAnimation(.none)
activityIndicatorView = ComponentHostView<Empty>()
self.activityIndicatorView = activityIndicatorView
self.addSubview(activityIndicatorView)
// self.addSubview(activityIndicatorView)
}
let activityIndicatorSize = activityIndicatorView.update(
@ -431,6 +441,11 @@ final class _MediaStreamVideoComponent: Component {
return availableSize
}
func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
videoView?.alpha = 0
videoBlurView?.alpha = 0
}
public func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
guard let component = self.component else {
completionHandler(false)
@ -455,6 +470,8 @@ final class _MediaStreamVideoComponent: Component {
}
func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
self.videoView?.alpha = 1
self.videoBlurView?.alpha = 1
self.state?.updated(transition: .immediate)
}
}

View File

@ -171,16 +171,16 @@ final class StreamSheetComponent: CombinedComponent {
if let topItem = topItem {
context.add(topItem
.position(CGPoint(x: topItem.size.width / 2.0, y: topOffset + contentHeight / 2.0))
.position(CGPoint(x: topItem.size.width / 2.0, y: topOffset + 32))
)
(context.view as? StreamSheetComponent.View)?.overlayComponentsFrames.append(.init(x: 0, y: topOffset, width: topItem.size.width, height: topItem.size.height))
}
let videoHeight = (availableWidth - 32) / 16 * 9
let sheetHeight = context.component.sheetHeight
let animatedParticipantsVisible = context.component.participantsCount != -1
if true {
context.add(viewerCounter
.position(CGPoint(x: context.availableSize.width / 2, y: topOffset + 50 + videoHeight + 40 + 30))
.position(CGPoint(x: context.availableSize.width / 2, y: topOffset + 50 + videoHeight + (sheetHeight - 69 - videoHeight - 50 - context.component.bottomPadding) / 2 - 12))
.opacity(animatedParticipantsVisible ? 1 : 0)
)
}
@ -215,55 +215,6 @@ private let pink = UIColor(rgb: 0xe4436c)
private let latePurple = UIColor(rgb: 0x974aa9)
private let latePink = UIColor(rgb: 0xf0436c)
final class ViewerCountComponent: Component {
private let count: Int
// private let counterView: VoiceChatTimerNode
static func ==(lhs: ViewerCountComponent, rhs: ViewerCountComponent) -> Bool {
if lhs.count != rhs.count {
return false
}
return true
}
init(count: Int) {
self.count = count
}
public func update(view: UIView, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
/*self.foregroundView.frame = CGRect(origin: CGPoint(), size: size)
self.foregroundGradientLayer.frame = self.foregroundView.bounds
self.maskView.frame = self.foregroundView.bounds
let text: String = presentationStringsFormattedNumber(participants, groupingSeparator)
let subtitle = "listening"
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)
self.timerNode.attributedText = NSAttributedString(string: text, font: Font.with(size: 68.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.subtitleNode.attributedText = NSAttributedString(string: subtitle, font: Font.with(size: 21.0, design: .round, weight: .semibold, traits: []), textColor: .white)
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)
*/
return availableSize
}
}
final class SheetBackgroundComponent: Component {
private let color: UIColor
@ -401,7 +352,7 @@ public final class AnimatedCountView: UIView {
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, width: subtitleLabel.intrinsicContentSize.width + 20, height: 20)
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) {
@ -614,7 +565,6 @@ class AnimatedCountLabel: UILabel {
}
func udpateAttributed(with newString: NSAttributedString) {
let initialDuration: TimeInterval = 0.25
let interItemSpacing: CGFloat = 0
let separatedStrings = Array(newString.string).map { String($0) }
@ -629,10 +579,22 @@ class AnimatedCountLabel: UILabel {
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.15
let interItemDelay: TimeInterval = 0.08
var changeIndex = 0
var newLayers = [AnimatedCharLayer]()
@ -706,7 +668,7 @@ class AnimatedCountLabel: UILabel {
return $0 + itemWidth + interItemSpacing
}
if didBegin && prevCount != chars.count {
UIView.animate(withDuration: 0.3, delay: initialDuration * Double(changeIndex)) { [self] in
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)
}
@ -714,13 +676,14 @@ class AnimatedCountLabel: UILabel {
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.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.value(forKey: "opacity") ?? 1, 0.0]
animation.values = [layer.presentation()?.value(forKey: "opacity") ?? 1, 0.0]
animation.keyTimes = [0, 1]
animation.duration = duration
animation.beginTime = CACurrentMediaTime() + beginTime
@ -735,7 +698,7 @@ class AnimatedCountLabel: UILabel {
layer.removeFromSuperlayer()
}
let scaleOutAnimation = CABasicAnimation(keyPath: "transform.scale")
scaleOutAnimation.fromValue = layer.value(forKey: "transform.scale") ?? 1
scaleOutAnimation.fromValue = layer.presentation()?.value(forKey: "transform.scale") ?? 1
scaleOutAnimation.toValue = 0.1
scaleOutAnimation.duration = duration
scaleOutAnimation.beginTime = CACurrentMediaTime() + beginTime