2023-11-14 11:26:57 +04:00

104 lines
3.3 KiB
Swift

import Foundation
import UIKit
import Display
import ComponentFlow
final class AvatarLayer: SimpleLayer {
struct Params: Equatable {
var size: CGSize
var cornerRadius: CGFloat
var isExpanded: Bool
init(size: CGSize, cornerRadius: CGFloat, isExpanded: Bool) {
self.size = size
self.cornerRadius = cornerRadius
self.isExpanded = isExpanded
}
}
private(set) var params: Params?
private var rasterizedImage: UIImage?
private var isAnimating: Bool = false
var image: UIImage? {
didSet {
if self.image !== oldValue {
self.updateImage()
}
}
}
override init() {
super.init()
self.contentsGravity = .resizeAspectFill
self.masksToBounds = true
}
override init(layer: Any) {
super.init(layer: layer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func updateImage() {
guard let params else {
return
}
if self.isAnimating || params.isExpanded {
self.contents = self.image?.cgImage
} else {
self.contents = self.image.flatMap({ image -> UIImage? in
let imageSize = CGSize(width: min(params.size.width, params.size.height), height: min(params.size.width, params.size.height))
return generateImage(imageSize, contextGenerator: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
if params.cornerRadius == size.width * 0.5 {
context.addEllipse(in: CGRect(origin: CGPoint(), size: size))
} else {
context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: params.cornerRadius).cgPath)
}
context.clip()
if let cgImage = image.cgImage {
context.draw(cgImage, in: CGRect(origin: CGPoint(), size: size))
}
})
})?.cgImage
}
}
func update(size: CGSize, isExpanded: Bool, cornerRadius: CGFloat, transition: Transition) {
let params = Params(size: size, cornerRadius: cornerRadius, isExpanded: isExpanded)
if self.params == params {
return
}
let previousCornerRadius = self.params?.cornerRadius
self.params = params
if previousCornerRadius != params.cornerRadius {
self.masksToBounds = true
self.isAnimating = true
self.updateImage()
if let previousCornerRadius, self.animation(forKey: "cornerRadius") == nil {
self.cornerRadius = previousCornerRadius
}
transition.setCornerRadius(layer: self, cornerRadius: cornerRadius, completion: { [weak self] completed in
guard let self, completed else {
return
}
self.isAnimating = false
self.masksToBounds = false
self.cornerRadius = 0.0
self.updateImage()
})
}
}
}