mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-08 19:10:53 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
dbc652ed84
@ -35,7 +35,7 @@ public final class AnimationNode : ASDisplayNode {
|
||||
|
||||
self.setViewBlock({
|
||||
if let animationName = animationName, let url = getAppBundle().url(forResource: animationName, withExtension: "json"), let animation = Animation.filepath(url.path) {
|
||||
let view = AnimationView(animation: animation)
|
||||
let view = AnimationView(animation: animation, configuration: LottieConfiguration(renderingEngine: .mainThread, decodingStrategy: .codable))
|
||||
view.animationSpeed = self.speed
|
||||
view.backgroundColor = .clear
|
||||
view.isOpaque = false
|
||||
@ -63,7 +63,7 @@ public final class AnimationNode : ASDisplayNode {
|
||||
|
||||
self.setViewBlock({
|
||||
if let json = try? JSONSerialization.jsonObject(with: animationData, options: []) as? [String: Any], let animation = try? Animation(dictionary: json) {
|
||||
let view = AnimationView(animation: animation)
|
||||
let view = AnimationView(animation: animation, configuration: LottieConfiguration(renderingEngine: .mainThread, decodingStrategy: .codable))
|
||||
view.animationSpeed = self.speed
|
||||
view.backgroundColor = .clear
|
||||
view.isOpaque = false
|
||||
|
@ -138,7 +138,7 @@ public final class LottieAnimationComponent: Component {
|
||||
self.didPlayToCompletion = false
|
||||
|
||||
if let url = getAppBundle().url(forResource: component.animation.name, withExtension: "json"), let animation = Animation.filepath(url.path) {
|
||||
let view = AnimationView(animation: animation)
|
||||
let view = AnimationView(animation: animation, configuration: LottieConfiguration(renderingEngine: .mainThread, decodingStrategy: .codable))
|
||||
switch component.animation.mode {
|
||||
case .still, .animateTransitionFromPrevious:
|
||||
view.loopMode = .playOnce
|
||||
|
@ -112,7 +112,7 @@ private class StickerNode: ASDisplayNode {
|
||||
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 400.0, height: 400.0))
|
||||
|
||||
let pathPrefix = context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(file.resource.id)
|
||||
animationNode.setup(source: AnimatedStickerResourceSource(account: self.context.account, resource: file.resource, isVideo: file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .direct(cachePathPrefix: pathPrefix))
|
||||
animationNode.setup(source: AnimatedStickerResourceSource(account: self.context.account, resource: file.resource, isVideo: file.isVideoSticker), width: Int(fittedDimensions.width * 1.6), height: Int(fittedDimensions.height * 1.6), playbackMode: .loop, mode: .direct(cachePathPrefix: pathPrefix))
|
||||
|
||||
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: context.account.postbox, file: file, small: false, size: fittedDimensions))
|
||||
|
||||
@ -125,7 +125,7 @@ private class StickerNode: ASDisplayNode {
|
||||
let additionalAnimationNode = AnimatedStickerNode()
|
||||
|
||||
let pathPrefix = context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(effect.resource.id)
|
||||
additionalAnimationNode.setup(source: source, width: Int(fittedDimensions.width * 2.0), height: Int(fittedDimensions.height * 2.0), playbackMode: .loop, mode: .direct(cachePathPrefix: pathPrefix))
|
||||
additionalAnimationNode.setup(source: source, width: Int(fittedDimensions.width * 1.4), height: Int(fittedDimensions.height * 1.4), playbackMode: .loop, mode: .direct(cachePathPrefix: pathPrefix))
|
||||
self.additionalAnimationNode = additionalAnimationNode
|
||||
}
|
||||
} else {
|
||||
|
@ -1082,7 +1082,7 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
|
||||
self.addSubnode(additionalAnimationNode)
|
||||
|
||||
if !isLarge, !avatarPeers.isEmpty, let url = getAppBundle().url(forResource: "effectavatar", withExtension: "json"), let composition = Animation.filepath(url.path) {
|
||||
let view = AnimationView(animation: composition)
|
||||
let view = AnimationView(animation: composition, configuration: LottieConfiguration(renderingEngine: .mainThread, decodingStrategy: .codable))
|
||||
view.animationSpeed = 1.0
|
||||
view.backgroundColor = nil
|
||||
view.isOpaque = false
|
||||
|
@ -13,6 +13,7 @@ swift_library(
|
||||
"//submodules/ComponentFlow:ComponentFlow",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/Components/LottieAnimationComponent:LottieAnimationComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -3,16 +3,22 @@ import UIKit
|
||||
import ComponentFlow
|
||||
import AppBundle
|
||||
import Display
|
||||
import LottieAnimationComponent
|
||||
|
||||
public final class AudioTranscriptionPendingIndicatorComponent: Component {
|
||||
public let color: UIColor
|
||||
public let font: UIFont
|
||||
|
||||
public init(color: UIColor) {
|
||||
public init(color: UIColor, font: UIFont) {
|
||||
self.color = color
|
||||
self.font = font
|
||||
}
|
||||
|
||||
public static func ==(lhs: AudioTranscriptionPendingIndicatorComponent, rhs: AudioTranscriptionPendingIndicatorComponent) -> Bool {
|
||||
if lhs.color !== rhs.color {
|
||||
if lhs.color != rhs.color {
|
||||
return false
|
||||
}
|
||||
if lhs.font != rhs.font {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@ -61,6 +67,10 @@ public final class AudioTranscriptionPendingIndicatorComponent: Component {
|
||||
let dotSize: CGFloat = 2.0
|
||||
let spacing: CGFloat = 3.0
|
||||
|
||||
var stringSize = NSAttributedString(string: "...", font: component.font, textColor: .black).boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil).size
|
||||
stringSize.width = ceil(stringSize.width)
|
||||
stringSize.height = ceil(stringSize.height)
|
||||
|
||||
if self.component?.color != component.color {
|
||||
if let dotImage = generateFilledCircleImage(diameter: dotSize, color: component.color) {
|
||||
for dotLayer in self.dotLayers {
|
||||
@ -77,7 +87,7 @@ public final class AudioTranscriptionPendingIndicatorComponent: Component {
|
||||
self.dotLayers[i].frame = CGRect(origin: CGPoint(x: CGFloat(i) * (dotSize + spacing), y: 0.0), size: CGSize(width: dotSize, height: dotSize))
|
||||
}
|
||||
|
||||
return CGSize(width: min(availableSize.width, size.width), height: min(availableSize.height, size.height))
|
||||
return CGSize(width: min(availableSize.width, stringSize.width), height: min(availableSize.height, size.height))
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,3 +99,80 @@ public final class AudioTranscriptionPendingIndicatorComponent: Component {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
public final class AudioTranscriptionPendingLottieIndicatorComponent: Component {
|
||||
public let color: UIColor
|
||||
public let font: UIFont
|
||||
|
||||
public init(color: UIColor, font: UIFont) {
|
||||
self.color = color
|
||||
self.font = font
|
||||
}
|
||||
|
||||
public static func ==(lhs: AudioTranscriptionPendingLottieIndicatorComponent, rhs: AudioTranscriptionPendingLottieIndicatorComponent) -> Bool {
|
||||
if lhs.color != rhs.color {
|
||||
return false
|
||||
}
|
||||
if lhs.font != rhs.font {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
private let animationView: ComponentHostView<Empty>
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.animationView = ComponentHostView<Empty>()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.animationView)
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: AudioTranscriptionPendingLottieIndicatorComponent, availableSize: CGSize, transition: Transition) -> CGSize {
|
||||
let originalSize = CGSize(width: 48.0, height: 66.0)
|
||||
let animationSize = originalSize.aspectFitted(CGSize(width: 15.0, height: 100.0))
|
||||
let _ = self.animationView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(LottieAnimationComponent(
|
||||
animation: LottieAnimationComponent.AnimationItem(
|
||||
name: "animated_text_dots",
|
||||
colors: [
|
||||
"Comp 1.Point 3.Group 1.Fill 1": component.color,
|
||||
"Comp 1.Point 2.Group 1.Fill 1": component.color,
|
||||
"Comp 1.Point 1.Group 1.Fill 1": component.color
|
||||
],
|
||||
mode: .animating(loop: true)
|
||||
),
|
||||
size: animationSize
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: animationSize
|
||||
)
|
||||
|
||||
var stringSize = NSAttributedString(string: "...", font: component.font, textColor: .black).boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil).size
|
||||
stringSize.width = ceil(stringSize.width)
|
||||
stringSize.height = ceil(stringSize.height)
|
||||
|
||||
let size = CGSize(width: min(availableSize.width, stringSize.width), height: min(availableSize.height, 10.0))
|
||||
|
||||
self.animationView.frame = CGRect(origin: CGPoint(x: -2.0, y: size.height - animationSize.height + 4.0 + UIScreenPixel), size: animationSize)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
{"v":"5.8.1","fr":60,"ip":0,"op":60,"w":48,"h":66,"nm":"Comp 2","ddd":0,"assets":[{"id":"comp_0","nm":"Comp 1","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Point 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.7,"y":1},"o":{"x":0.7,"y":0},"t":12,"s":[118.6,256,0],"to":[-0.6,-119,0],"ti":[-1.438,-92.469,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.32,"y":0},"t":32,"s":[255.938,275.469,0],"to":[0,0,0],"ti":[0,0,0]},{"t":42,"s":[255.938,255.969,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-102.4,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-17.673,0],[0,-17.673],[17.673,0],[0,17.673]],"o":[[17.673,0],[0,17.673],[-17.673,0],[0,-17.673]],"v":[[-102.4,-32],[-70.4,0],[-102.4,32],[-134.4,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Point 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.7,"y":1},"o":{"x":0.7,"y":0},"t":0,"s":[256,256,0],"to":[1.75,-110,0],"ti":[-5.25,-93.219,0]},{"i":{"x":0.42,"y":1},"o":{"x":0.3,"y":0},"t":20,"s":[393.25,276.969,0],"to":[0,0,0],"ti":[0,0,0]},{"t":30,"s":[393.25,255.969,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-17.673,0],[0,-17.673],[17.673,0],[0,17.673]],"o":[[17.673,0],[0,17.673],[-17.673,0],[0,-17.673]],"v":[[0,-32],[32,0],[0,32],[-32,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Point 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[393.4,256,0],"to":[0,0,0],"ti":[0,0,0]},{"t":40,"s":[118.562,255.969,0]}],"ix":2,"l":2},"a":{"a":0,"k":[102.4,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-17.673,0],[0,-17.673],[17.673,0],[0,17.673]],"o":[[17.673,0],[0,17.673],[-17.673,0],[0,-17.673]],"v":[[102.4,-32],[134.4,0],[102.4,32],[70.4,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":180,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Comp 1","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[24,48,0],"ix":2,"l":2},"a":{"a":0,"k":[256,256,0],"ix":1,"l":2},"s":{"a":0,"k":[10.8,10.8,100],"ix":6,"l":2}},"ao":0,"w":512,"h":512,"ip":0,"op":180,"st":0,"bm":0}],"markers":[]}
|
@ -654,16 +654,18 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
|
||||
var displayTrailingAnimatedDots = false
|
||||
|
||||
/*#if DEBUG
|
||||
if "".isEmpty {
|
||||
displayTrailingAnimatedDots = true
|
||||
}
|
||||
#endif*/
|
||||
|
||||
if let transcribedText = transcribedText, case .expanded = effectiveAudioTranscriptionState {
|
||||
switch transcribedText {
|
||||
case let .success(text, isPending):
|
||||
textString = NSAttributedString(string: text, font: textFont, textColor: messageTheme.primaryTextColor)
|
||||
|
||||
#if DEBUG
|
||||
var isPending = isPending
|
||||
if "".isEmpty {
|
||||
isPending = true
|
||||
}
|
||||
#endif
|
||||
|
||||
if isPending {
|
||||
let modifiedString = NSMutableAttributedString(attributedString: textString!)
|
||||
modifiedString.append(NSAttributedString(string: "...", font: textFont, textColor: .clear))
|
||||
@ -1025,14 +1027,19 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
strongSelf.transcriptionPendingIndicator = transcriptionPendingIndicator
|
||||
strongSelf.textClippingNode.view.addSubview(transcriptionPendingIndicator)
|
||||
}
|
||||
|
||||
let indicatorComponent: AnyComponent<Empty>
|
||||
indicatorComponent = AnyComponent(AudioTranscriptionPendingLottieIndicatorComponent(color: messageTheme.primaryTextColor, font: textFont))
|
||||
//indicatorComponent = AnyComponent(AudioTranscriptionPendingIndicatorComponent(color: messageTheme.primaryTextColor, font: textFont))
|
||||
|
||||
let indicatorSize = transcriptionPendingIndicator.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(AudioTranscriptionPendingIndicatorComponent(color: messageTheme.primaryTextColor)),
|
||||
component: indicatorComponent,
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
)
|
||||
|
||||
transcriptionPendingIndicator.frame = CGRect(origin: CGPoint(x: strongSelf.textNode.frame.minX + textLayout.trailingLineWidth + 2.0, y: strongSelf.textNode.frame.maxY - indicatorSize.height - 6.0), size: indicatorSize)
|
||||
transcriptionPendingIndicator.frame = CGRect(origin: CGPoint(x: strongSelf.textNode.frame.minX + textLayout.trailingLineWidth - indicatorSize.width + 1.0, y: strongSelf.textNode.frame.maxY - indicatorSize.height - 6.0), size: indicatorSize)
|
||||
} else {
|
||||
if let transcriptionPendingIndicator = strongSelf.transcriptionPendingIndicator {
|
||||
strongSelf.transcriptionPendingIndicator = nil
|
||||
|
@ -11,7 +11,7 @@ final class LockView: UIButton, TGModernConversationInputMicButtonLock {
|
||||
guard let url = getAppBundle().url(forResource: "LockWait", withExtension: "json"), let animation = Animation.filepath(url.path)
|
||||
else { return AnimationView() }
|
||||
|
||||
let view = AnimationView(animation: animation)
|
||||
let view = AnimationView(animation: animation, configuration: LottieConfiguration(renderingEngine: .mainThread, decodingStrategy: .codable))
|
||||
view.loopMode = .autoReverse
|
||||
view.backgroundColor = .clear
|
||||
view.isOpaque = false
|
||||
@ -22,7 +22,7 @@ final class LockView: UIButton, TGModernConversationInputMicButtonLock {
|
||||
guard let url = getAppBundle().url(forResource: "Lock", withExtension: "json"), let animation = Animation.filepath(url.path)
|
||||
else { return AnimationView() }
|
||||
|
||||
let view = AnimationView(animation: animation)
|
||||
let view = AnimationView(animation: animation, configuration: LottieConfiguration(renderingEngine: .mainThread, decodingStrategy: .codable))
|
||||
view.backgroundColor = .clear
|
||||
view.isOpaque = false
|
||||
return view
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Copyright © 2021 Airbnb Inc. All rights reserved.
|
||||
|
||||
import QuartzCore
|
||||
import UIKit
|
||||
|
||||
extension CALayer {
|
||||
|
||||
@ -77,6 +78,12 @@ extension CALayer {
|
||||
let animation = CABasicAnimation(keyPath: property.caLayerKeypath)
|
||||
animation.fromValue = keyframeValue
|
||||
animation.toValue = keyframeValue
|
||||
if #available(iOS 15.0, *) {
|
||||
let maxFps = Float(UIScreen.main.maximumFramesPerSecond)
|
||||
if maxFps > 61.0 {
|
||||
animation.preferredFrameRateRange = CAFrameRateRange(minimum: maxFps, maximum: maxFps, preferred: maxFps)
|
||||
}
|
||||
}
|
||||
return animation
|
||||
}
|
||||
|
||||
@ -134,6 +141,12 @@ extension CALayer {
|
||||
let calculationMode = try self.calculationMode(for: keyframes, context: context)
|
||||
|
||||
let animation = CAKeyframeAnimation(keyPath: property.caLayerKeypath)
|
||||
if #available(iOS 15.0, *) {
|
||||
let maxFps = Float(UIScreen.main.maximumFramesPerSecond)
|
||||
if maxFps > 61.0 {
|
||||
animation.preferredFrameRateRange = CAFrameRateRange(minimum: maxFps, maximum: maxFps, preferred: maxFps)
|
||||
}
|
||||
}
|
||||
|
||||
// Position animations define a `CGPath` curve that should be followed,
|
||||
// instead of animating directly between keyframe point values.
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
import Foundation
|
||||
import QuartzCore
|
||||
import UIKit
|
||||
|
||||
// MARK: - MainThreadAnimationLayer
|
||||
|
||||
@ -112,6 +113,12 @@ final class MainThreadAnimationLayer: CALayer, RootAnimationLayer {
|
||||
let animation = CABasicAnimation(keyPath: event)
|
||||
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
||||
animation.fromValue = presentation()?.currentFrame
|
||||
if #available(iOS 15.0, *) {
|
||||
let maxFps = Float(UIScreen.main.maximumFramesPerSecond)
|
||||
if maxFps > 61.0 {
|
||||
animation.preferredFrameRateRange = CAFrameRateRange(minimum: maxFps, maximum: maxFps, preferred: maxFps)
|
||||
}
|
||||
}
|
||||
return animation
|
||||
}
|
||||
return super.action(forKey: event)
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
import Foundation
|
||||
import QuartzCore
|
||||
import UIKit
|
||||
|
||||
// MARK: - LottieBackgroundBehavior
|
||||
|
||||
@ -393,6 +394,42 @@ final public class AnimationView: AnimationViewBase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var workaroundDisplayLink: CADisplayLink?
|
||||
private var needsWorkaroundDisplayLink: Bool = false {
|
||||
didSet {
|
||||
if self.needsWorkaroundDisplayLink != oldValue {
|
||||
if self.needsWorkaroundDisplayLink {
|
||||
if workaroundDisplayLink == nil {
|
||||
class WorkaroundDisplayLinkTarget {
|
||||
private let f: () -> Void
|
||||
|
||||
init(_ f: @escaping () -> Void) {
|
||||
self.f = f
|
||||
}
|
||||
|
||||
@objc func update() {
|
||||
self.f()
|
||||
}
|
||||
}
|
||||
self.workaroundDisplayLink = CADisplayLink(target: WorkaroundDisplayLinkTarget { [weak self] in
|
||||
let _ = self?.realtimeAnimationProgress
|
||||
}, selector: #selector(WorkaroundDisplayLinkTarget.update))
|
||||
if #available(iOS 15.0, *) {
|
||||
let maxFps = Float(UIScreen.main.maximumFramesPerSecond)
|
||||
self.workaroundDisplayLink?.preferredFrameRateRange = CAFrameRateRange(minimum: maxFps, maximum: maxFps, preferred: maxFps)
|
||||
}
|
||||
self.workaroundDisplayLink?.add(to: .main, forMode: .common)
|
||||
}
|
||||
} else {
|
||||
if let workaroundDisplayLink = self.workaroundDisplayLink {
|
||||
self.workaroundDisplayLink = nil
|
||||
workaroundDisplayLink.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Plays the animation from its current state to the end.
|
||||
///
|
||||
@ -1136,6 +1173,8 @@ final public class AnimationView: AnimationViewBase {
|
||||
animationLayer?.removeAnimation(forKey: activeAnimationName)
|
||||
updateAnimationFrame(pauseFrame)
|
||||
animationContext = nil
|
||||
|
||||
self.needsWorkaroundDisplayLink = false
|
||||
}
|
||||
|
||||
/// Updates an in flight animation.
|
||||
@ -1258,6 +1297,12 @@ final public class AnimationView: AnimationViewBase {
|
||||
layerAnimation.fillMode = CAMediaTimingFillMode.both
|
||||
layerAnimation.repeatCount = loopMode.caAnimationConfiguration.repeatCount
|
||||
layerAnimation.autoreverses = loopMode.caAnimationConfiguration.autoreverses
|
||||
if #available(iOS 15.0, *) {
|
||||
let maxFps = Float(UIScreen.main.maximumFramesPerSecond)
|
||||
if maxFps > 61.0 {
|
||||
layerAnimation.preferredFrameRateRange = CAFrameRateRange(minimum: maxFps, maximum: maxFps, preferred: maxFps)
|
||||
}
|
||||
}
|
||||
|
||||
layerAnimation.isRemovedOnCompletion = false
|
||||
if timeOffset != 0 {
|
||||
@ -1269,6 +1314,7 @@ final public class AnimationView: AnimationViewBase {
|
||||
animationContext.closure.animationKey = activeAnimationName
|
||||
|
||||
animationlayer.add(layerAnimation, forKey: activeAnimationName)
|
||||
self.needsWorkaroundDisplayLink = true
|
||||
updateRasterizationState()
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ public final class CompatibleAnimationView: UIView {
|
||||
|
||||
@objc
|
||||
public init(compatibleAnimation: CompatibleAnimation) {
|
||||
animationView = AnimationView(animation: compatibleAnimation.animation)
|
||||
animationView = AnimationView(animation: compatibleAnimation.animation, configuration: LottieConfiguration(renderingEngine: .mainThread, decodingStrategy: .codable))
|
||||
self.compatibleAnimation = compatibleAnimation
|
||||
super.init(frame: .zero)
|
||||
commonInit()
|
||||
|
Loading…
x
Reference in New Issue
Block a user