mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Various improvements
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import TelegramPresentationData
|
||||
|
||||
private final class ShimmerEffectView: UIView {
|
||||
private var currentBackgroundColor: UIColor?
|
||||
private var currentForegroundColor: UIColor?
|
||||
private let imageContainerView: UIView
|
||||
private let imageView: UIImageView
|
||||
|
||||
private var absoluteLocation: (CGRect, CGSize)?
|
||||
private var shouldBeAnimating = false
|
||||
|
||||
override init(frame: CGRect = .zero) {
|
||||
self.imageContainerView = UIView()
|
||||
self.imageContainerView.isUserInteractionEnabled = false
|
||||
|
||||
self.imageView = UIImageView()
|
||||
self.imageView.isUserInteractionEnabled = false
|
||||
self.imageView.contentMode = .scaleToFill
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.isUserInteractionEnabled = false
|
||||
self.clipsToBounds = true
|
||||
|
||||
self.imageContainerView.addSubview(self.imageView)
|
||||
self.addSubview(self.imageContainerView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
override func didMoveToWindow() {
|
||||
super.didMoveToWindow()
|
||||
updateAnimation()
|
||||
}
|
||||
|
||||
func update(backgroundColor: UIColor, foregroundColor: UIColor) {
|
||||
if let currentBackgroundColor = self.currentBackgroundColor, currentBackgroundColor.argb == backgroundColor.argb,
|
||||
let currentForegroundColor = self.currentForegroundColor, currentForegroundColor.argb == foregroundColor.argb {
|
||||
return
|
||||
}
|
||||
self.currentBackgroundColor = backgroundColor
|
||||
self.currentForegroundColor = foregroundColor
|
||||
|
||||
self.imageView.image = generateImage(CGSize(width: 4.0, height: 320.0), opaque: true, scale: 1.0, rotatedContext: { size, context in
|
||||
context.setFillColor(backgroundColor.cgColor)
|
||||
context.fill(CGRect(origin: .zero, size: size))
|
||||
|
||||
context.clip(to: CGRect(origin: .zero, size: size))
|
||||
|
||||
let transparentColor = foregroundColor.withAlphaComponent(0.0).cgColor
|
||||
let peakColor = foregroundColor.cgColor
|
||||
|
||||
var locations: [CGFloat] = [0.0, 0.5, 1.0]
|
||||
let colors: [CGColor] = [transparentColor, peakColor, transparentColor]
|
||||
|
||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
|
||||
|
||||
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: [])
|
||||
})
|
||||
}
|
||||
|
||||
func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
||||
if let absoluteLocation, absoluteLocation.0 == rect && absoluteLocation.1 == containerSize {
|
||||
return
|
||||
}
|
||||
let sizeUpdated = self.absoluteLocation?.1 != containerSize
|
||||
let frameUpdated = self.absoluteLocation?.0 != rect
|
||||
self.absoluteLocation = (rect, containerSize)
|
||||
|
||||
if sizeUpdated, shouldBeAnimating {
|
||||
self.imageView.layer.removeAnimation(forKey: "shimmer")
|
||||
self.addImageAnimation()
|
||||
}
|
||||
|
||||
if frameUpdated {
|
||||
self.imageContainerView.frame = CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: containerSize)
|
||||
}
|
||||
|
||||
self.updateAnimation()
|
||||
}
|
||||
|
||||
private func updateAnimation() {
|
||||
let inHierarchy = (self.window != nil)
|
||||
let shouldAnimate = inHierarchy && (self.absoluteLocation != nil)
|
||||
if shouldAnimate != self.shouldBeAnimating {
|
||||
self.shouldBeAnimating = shouldAnimate
|
||||
if shouldAnimate {
|
||||
self.addImageAnimation()
|
||||
} else {
|
||||
self.imageView.layer.removeAnimation(forKey: "shimmer")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func addImageAnimation() {
|
||||
guard let containerSize = self.absoluteLocation?.1 else { return }
|
||||
let gradientHeight: CGFloat = 250.0
|
||||
self.imageView.frame = CGRect(origin: CGPoint(x: 0.0, y: -gradientHeight),
|
||||
size: CGSize(width: containerSize.width, height: gradientHeight))
|
||||
|
||||
let anim = CABasicAnimation(keyPath: "position.y")
|
||||
anim.fromValue = 0.0
|
||||
anim.toValue = (containerSize.height + gradientHeight)
|
||||
anim.duration = 1.3
|
||||
anim.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
||||
anim.fillMode = .removed
|
||||
anim.isRemovedOnCompletion = true
|
||||
anim.repeatCount = .infinity
|
||||
anim.beginTime = CACurrentMediaTime() + 1.0
|
||||
anim.isAdditive = true
|
||||
|
||||
self.imageView.layer.add(anim, forKey: "shimmer")
|
||||
}
|
||||
}
|
||||
|
||||
public final class GiftLoadingShimmerView: UIView {
|
||||
private let backgroundView = UIView()
|
||||
private let effectView = ShimmerEffectView()
|
||||
private let maskImageView = UIImageView()
|
||||
|
||||
private var currentParams: (size: CGSize, theme: PresentationTheme, showFilters: Bool)?
|
||||
|
||||
public override init(frame: CGRect = .zero) {
|
||||
super.init(frame: frame)
|
||||
self.isUserInteractionEnabled = false
|
||||
self.backgroundColor = .clear
|
||||
|
||||
self.addSubview(self.backgroundView)
|
||||
self.addSubview(self.effectView)
|
||||
self.addSubview(self.maskImageView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) { fatalError() }
|
||||
|
||||
public func update(size: CGSize, theme: PresentationTheme, showFilters: Bool = false, isPlain: Bool = false, transition: ContainedViewLayoutTransition) {
|
||||
let backgroundColor = isPlain ? theme.list.itemBlocksBackgroundColor : theme.list.blocksBackgroundColor
|
||||
let color = theme.list.itemSecondaryTextColor.mixedWith(theme.list.blocksBackgroundColor, alpha: 0.85)
|
||||
|
||||
if self.currentParams?.size != size || self.currentParams?.theme !== theme || self.currentParams?.showFilters != showFilters {
|
||||
self.currentParams = (size, theme, showFilters)
|
||||
|
||||
self.backgroundView.backgroundColor = color
|
||||
|
||||
self.maskImageView.image = generateImage(size, rotatedContext: { size, context in
|
||||
context.setFillColor(backgroundColor.cgColor)
|
||||
context.fill(CGRect(origin: .zero, size: size))
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
|
||||
if showFilters {
|
||||
let filterSpacing: CGFloat = 6.0
|
||||
let filterWidth = (size.width - sideInset * 2.0 - filterSpacing * 3.0) / 4.0
|
||||
for i in 0 ..< 4 {
|
||||
let rect = CGRect(origin: CGPoint(x: sideInset + (filterWidth + filterSpacing) * CGFloat(i), y: 0.0),
|
||||
size: CGSize(width: filterWidth, height: 28.0))
|
||||
context.addPath(CGPath(roundedRect: rect, cornerWidth: 14.0, cornerHeight: 14.0, transform: nil))
|
||||
}
|
||||
}
|
||||
|
||||
var currentY: CGFloat = 39.0 + 7.0
|
||||
var rowIndex: Int = 0
|
||||
|
||||
let optionSpacing: CGFloat = 10.0
|
||||
let optionWidth = (size.width - sideInset * 2.0 - optionSpacing * 2.0) / 3.0
|
||||
let itemSize = CGSize(width: optionWidth, height: 154.0)
|
||||
|
||||
context.setBlendMode(.copy)
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
|
||||
while currentY < size.height {
|
||||
for i in 0 ..< 3 {
|
||||
let itemOrigin = CGPoint(
|
||||
x: sideInset + CGFloat(i) * (itemSize.width + optionSpacing),
|
||||
y: 39.0 + 9.0 + CGFloat(rowIndex) * (itemSize.height + optionSpacing)
|
||||
)
|
||||
context.addPath(CGPath(roundedRect: CGRect(origin: itemOrigin, size: itemSize),
|
||||
cornerWidth: 10.0, cornerHeight: 10.0, transform: nil))
|
||||
}
|
||||
currentY += itemSize.height
|
||||
rowIndex += 1
|
||||
}
|
||||
context.fillPath()
|
||||
})
|
||||
|
||||
self.effectView.update(backgroundColor: color, foregroundColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4))
|
||||
self.effectView.updateAbsoluteRect(CGRect(origin: .zero, size: size), within: size)
|
||||
}
|
||||
|
||||
transition.updateFrame(view: self.backgroundView, frame: CGRect(origin: .zero, size: size))
|
||||
transition.updateFrame(view: self.maskImageView, frame: CGRect(origin: .zero, size: size))
|
||||
transition.updateFrame(view: self.effectView, frame: CGRect(origin: .zero, size: size))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user