TM-SGNL-iOS/Signal/ConversationView/CellViews/CVWallpaperBlurView.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

137 lines
3.6 KiB
Swift

//
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import SignalServiceKit
public import SignalUI
public class CVWallpaperBlurView: ManualLayoutViewWithLayer {
private var isPreview = false
private weak var provider: WallpaperBlurProvider?
private let imageView = CVImageView()
private let maskLayer = CAShapeLayer()
private var state: WallpaperBlurState?
private var maskCornerRadius: CGFloat = 0
init() {
super.init(name: "CVWallpaperBlurView")
self.clipsToBounds = true
self.layer.zPosition = -1
imageView.contentMode = .scaleAspectFill
imageView.layer.mask = maskLayer
imageView.layer.masksToBounds = true
addSubview(imageView)
owsAssertDebug(self.layer.delegate === self)
maskLayer.disableAnimationsWithDelegate()
addLayoutBlock { [weak self] _ in
self?.applyLayout()
}
}
public func applyLayout() {
UIView.performWithoutAnimation {
imageView.frame = imageViewFrame
maskLayer.frame = imageView.bounds
let maskPath = UIBezierPath(roundedRect: maskFrame, cornerRadius: maskCornerRadius)
maskLayer.path = maskPath.cgPath
}
}
public func configureForPreview(maskCornerRadius: CGFloat) {
resetContentAndConfiguration()
self.isPreview = true
self.maskCornerRadius = maskCornerRadius
updateIfNecessary()
}
public func configure(provider: WallpaperBlurProvider,
maskCornerRadius: CGFloat) {
resetContentAndConfiguration()
self.isPreview = false
// TODO: Observe provider changes.
self.provider = provider
self.maskCornerRadius = maskCornerRadius
updateIfNecessary()
}
public func updateIfNecessary() {
guard !isPreview else {
self.backgroundColor = Theme.backgroundColor
imageView.isHidden = true
return
}
guard let provider = provider else {
owsFailDebug("Missing provider.")
resetContentAndConfiguration()
return
}
guard let state = provider.wallpaperBlurState else {
resetContent()
return
}
guard state.id != self.state?.id else {
ensurePositioning()
return
}
self.state = state
imageView.image = state.image
imageView.isHidden = false
ensurePositioning()
}
private var imageViewFrame: CGRect = .zero
private var maskFrame: CGRect = .zero
private func ensurePositioning() {
guard !isPreview else {
return
}
guard let state = self.state else {
resetContent()
return
}
let referenceView = state.referenceView
self.imageViewFrame = self.convert(referenceView.bounds, from: referenceView)
self.maskFrame = referenceView.convert(self.bounds, from: self)
applyLayout()
}
private func resetContent() {
backgroundColor = nil
imageView.image = nil
imageView.isHidden = false
imageViewFrame = .zero
maskFrame = .zero
state = nil
}
public func resetContentAndConfiguration() {
isPreview = false
provider = nil
maskCornerRadius = 0
resetContent()
}
// MARK: - CALayerDelegate
public override func action(for layer: CALayer, forKey event: String) -> CAAction? {
// Disable all implicit CALayer animations.
NSNull()
}
}