Swiftgram/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift
2021-05-11 23:31:03 +04:00

156 lines
5.4 KiB
Swift

import Foundation
import UIKit
import AsyncDisplayKit
import Display
import GradientBackground
import TelegramPresentationData
import SyncCore
private let motionAmount: CGFloat = 32.0
public final class WallpaperBackgroundNode: ASDisplayNode {
private let contentNode: ASDisplayNode
private var gradientBackgroundNode: GradientBackgroundNode?
private var validLayout: CGSize?
private var wallpaper: TelegramWallpaper?
private var motionEnabled: Bool = false {
didSet {
if oldValue != self.motionEnabled {
if self.motionEnabled {
let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis)
horizontal.minimumRelativeValue = motionAmount
horizontal.maximumRelativeValue = -motionAmount
let vertical = UIInterpolatingMotionEffect(keyPath: "center.y", type: .tiltAlongVerticalAxis)
vertical.minimumRelativeValue = motionAmount
vertical.maximumRelativeValue = -motionAmount
let group = UIMotionEffectGroup()
group.motionEffects = [horizontal, vertical]
self.contentNode.view.addMotionEffect(group)
} else {
for effect in self.contentNode.view.motionEffects {
self.contentNode.view.removeMotionEffect(effect)
}
}
if !self.frame.isEmpty {
self.updateScale()
}
}
}
}
public var image: UIImage? {
didSet {
self.contentNode.contents = self.image?.cgImage
}
}
public var rotation: CGFloat = 0.0 {
didSet {
var fromValue: CGFloat = 0.0
if let value = (self.layer.value(forKeyPath: "transform.rotation.z") as? NSNumber)?.floatValue {
fromValue = CGFloat(value)
}
self.contentNode.layer.transform = CATransform3DMakeRotation(self.rotation, 0.0, 0.0, 1.0)
self.contentNode.layer.animateRotation(from: fromValue, to: self.rotation, duration: 0.3)
}
}
private var imageContentMode: UIView.ContentMode {
didSet {
self.contentNode.contentMode = self.imageContentMode
}
}
private func updateScale() {
if self.motionEnabled {
let scale = (self.frame.width + motionAmount * 2.0) / self.frame.width
self.contentNode.transform = CATransform3DMakeScale(scale, scale, 1.0)
} else {
self.contentNode.transform = CATransform3DIdentity
}
}
public override init() {
self.imageContentMode = .scaleAspectFill
self.contentNode = ASDisplayNode()
self.contentNode.contentMode = self.imageContentMode
super.init()
self.clipsToBounds = true
self.contentNode.frame = self.bounds
self.addSubnode(self.contentNode)
}
public func update(wallpaper: TelegramWallpaper) {
if self.wallpaper == wallpaper {
return
}
self.wallpaper = wallpaper
if wallpaper.isBuiltin {
if self.gradientBackgroundNode == nil {
let gradientBackgroundNode = createGradientBackgroundNode()
self.gradientBackgroundNode = gradientBackgroundNode
self.addSubnode(gradientBackgroundNode)
}
self.contentNode.isHidden = true
} else {
if let gradientBackgroundNode = self.gradientBackgroundNode {
self.gradientBackgroundNode = nil
gradientBackgroundNode.removeFromSupernode()
}
if case .gradient = wallpaper {
self.imageContentMode = .scaleToFill
} else {
self.imageContentMode = .scaleAspectFill
}
self.motionEnabled = wallpaper.settings?.motion ?? false
self.contentNode.isHidden = false
}
if let size = self.validLayout {
self.updateLayout(size: size, transition: .immediate)
}
}
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
let isFirstLayout = self.validLayout == nil
self.validLayout = size
transition.updatePosition(node: self.contentNode, position: CGPoint(x: size.width / 2.0, y: size.height / 2.0))
transition.updateBounds(node: self.contentNode, bounds: CGRect(origin: CGPoint(), size: size))
if let gradientBackgroundNode = self.gradientBackgroundNode {
transition.updateFrame(node: gradientBackgroundNode, frame: CGRect(origin: CGPoint(), size: size))
gradientBackgroundNode.updateLayout(size: size, transition: transition)
}
if isFirstLayout && !self.frame.isEmpty {
self.updateScale()
}
}
public func animateEvent(transition: ContainedViewLayoutTransition) {
guard let wallpaper = self.wallpaper else {
return
}
switch wallpaper {
case let .builtin(settings):
if !settings.motion {
//return
}
default:
break
}
self.gradientBackgroundNode?.animateEvent(transition: transition)
}
}