mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
301 lines
12 KiB
Swift
301 lines
12 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import Display
|
|
import ComponentFlow
|
|
import RoundedRectWithTailPath
|
|
|
|
private final class FlashColorComponent: Component {
|
|
let tint: CameraState.FlashTint?
|
|
let isSelected: Bool
|
|
let action: () -> Void
|
|
|
|
init(
|
|
tint: CameraState.FlashTint?,
|
|
isSelected: Bool,
|
|
action: @escaping () -> Void
|
|
) {
|
|
self.tint = tint
|
|
self.isSelected = isSelected
|
|
self.action = action
|
|
}
|
|
|
|
static func == (lhs: FlashColorComponent, rhs: FlashColorComponent) -> Bool {
|
|
return lhs.tint == rhs.tint && lhs.isSelected == rhs.isSelected
|
|
}
|
|
|
|
final class View: UIButton {
|
|
private var component: FlashColorComponent?
|
|
|
|
private var contentView: UIView
|
|
|
|
private let circleLayer: SimpleShapeLayer
|
|
private var ringLayer: CALayer?
|
|
private var iconLayer: CALayer?
|
|
|
|
private var currentIsHighlighted: Bool = false {
|
|
didSet {
|
|
if self.currentIsHighlighted != oldValue {
|
|
self.contentView.alpha = self.currentIsHighlighted ? 0.6 : 1.0
|
|
}
|
|
}
|
|
}
|
|
|
|
override init(frame: CGRect) {
|
|
self.contentView = UIView(frame: CGRect(origin: .zero, size: frame.size))
|
|
self.contentView.isUserInteractionEnabled = false
|
|
self.circleLayer = SimpleShapeLayer()
|
|
|
|
super.init(frame: frame)
|
|
|
|
self.addSubview(self.contentView)
|
|
self.contentView.layer.addSublayer(self.circleLayer)
|
|
|
|
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
@objc private func pressed() {
|
|
self.component?.action()
|
|
}
|
|
|
|
override public func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
|
self.currentIsHighlighted = true
|
|
|
|
return super.beginTracking(touch, with: event)
|
|
}
|
|
|
|
override public func endTracking(_ touch: UITouch?, with event: UIEvent?) {
|
|
self.currentIsHighlighted = false
|
|
|
|
super.endTracking(touch, with: event)
|
|
}
|
|
|
|
override public func cancelTracking(with event: UIEvent?) {
|
|
self.currentIsHighlighted = false
|
|
|
|
super.cancelTracking(with: event)
|
|
}
|
|
|
|
func update(component: FlashColorComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
|
self.component = component
|
|
let contentSize = CGSize(width: 30.0, height: 30.0)
|
|
self.contentView.frame = CGRect(origin: .zero, size: contentSize)
|
|
|
|
let bounds = CGRect(origin: .zero, size: contentSize)
|
|
self.layer.allowsGroupOpacity = true
|
|
self.contentView.layer.allowsGroupOpacity = true
|
|
|
|
self.circleLayer.frame = bounds
|
|
if self.ringLayer == nil {
|
|
let ringLayer = SimpleLayer()
|
|
ringLayer.backgroundColor = UIColor.clear.cgColor
|
|
ringLayer.cornerRadius = contentSize.width / 2.0
|
|
ringLayer.borderWidth = 1.0 + UIScreenPixel
|
|
ringLayer.frame = CGRect(origin: .zero, size: contentSize)
|
|
self.contentView.layer.insertSublayer(ringLayer, at: 0)
|
|
self.ringLayer = ringLayer
|
|
}
|
|
|
|
if component.isSelected {
|
|
transition.setShapeLayerPath(layer: self.circleLayer, path: CGPath(ellipseIn: bounds.insetBy(dx: 3.0 - UIScreenPixel, dy: 3.0 - UIScreenPixel), transform: nil))
|
|
} else {
|
|
transition.setShapeLayerPath(layer: self.circleLayer, path: CGPath(ellipseIn: bounds, transform: nil))
|
|
}
|
|
|
|
if let color = component.tint?.color {
|
|
self.circleLayer.fillColor = color.cgColor
|
|
self.ringLayer?.borderColor = color.cgColor
|
|
} else {
|
|
if self.iconLayer == nil {
|
|
let iconLayer = SimpleLayer()
|
|
iconLayer.contents = UIImage(bundleImageName: "Camera/FlashOffIcon")?.cgImage
|
|
iconLayer.contentsGravity = .resizeAspect
|
|
iconLayer.frame = bounds.insetBy(dx: -4.0, dy: -4.0)
|
|
self.contentView.layer.addSublayer(iconLayer)
|
|
self.iconLayer = iconLayer
|
|
}
|
|
|
|
self.circleLayer.fillColor = UIColor(rgb: 0xffffff, alpha: 0.1).cgColor
|
|
self.ringLayer?.borderColor = UIColor.clear.cgColor
|
|
}
|
|
|
|
return contentSize
|
|
}
|
|
}
|
|
|
|
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, state: state, environment: environment, transition: transition)
|
|
}
|
|
}
|
|
|
|
final class FlashTintControlComponent: Component {
|
|
let position: CGPoint
|
|
let tint: CameraState.FlashTint
|
|
let update: (CameraState.FlashTint?) -> Void
|
|
let dismiss: () -> Void
|
|
|
|
init(
|
|
position: CGPoint,
|
|
tint: CameraState.FlashTint,
|
|
update: @escaping (CameraState.FlashTint?) -> Void,
|
|
dismiss: @escaping () -> Void
|
|
) {
|
|
self.position = position
|
|
self.tint = tint
|
|
self.update = update
|
|
self.dismiss = dismiss
|
|
}
|
|
|
|
static func == (lhs: FlashTintControlComponent, rhs: FlashTintControlComponent) -> Bool {
|
|
return lhs.position == rhs.position && lhs.tint == rhs.tint
|
|
}
|
|
|
|
final class View: UIButton {
|
|
private var component: FlashTintControlComponent?
|
|
|
|
private let dismissView = UIView()
|
|
private let containerView = UIView()
|
|
private let effectView: UIVisualEffectView
|
|
private let maskLayer = CAShapeLayer()
|
|
private let swatches = ComponentView<Empty>()
|
|
|
|
override init(frame: CGRect) {
|
|
self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
|
|
|
|
super.init(frame: frame)
|
|
|
|
self.containerView.layer.anchorPoint = CGPoint(x: 0.8, y: 0.0)
|
|
|
|
self.addSubview(self.dismissView)
|
|
self.addSubview(self.containerView)
|
|
self.containerView.addSubview(self.effectView)
|
|
|
|
self.dismissView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dismissTapped)))
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
@objc private func dismissTapped() {
|
|
self.component?.dismiss()
|
|
}
|
|
|
|
func update(component: FlashTintControlComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
|
let isFirstTime = self.component == nil
|
|
self.component = component
|
|
|
|
let size = CGSize(width: 184.0, height: 92.0)
|
|
if isFirstTime {
|
|
self.maskLayer.path = generateRoundedRectWithTailPath(rectSize: size, cornerRadius: 10.0, tailSize: CGSize(width: 18, height: 7.0), tailRadius: 1.0, tailPosition: 0.8, transformTail: false).cgPath
|
|
self.maskLayer.frame = CGRect(origin: .zero, size: CGSize(width: size.width, height: size.height + 7.0))
|
|
self.effectView.layer.mask = self.maskLayer
|
|
}
|
|
|
|
let swatchesSize = self.swatches.update(
|
|
transition: transition,
|
|
component: AnyComponent(
|
|
HStack(
|
|
[
|
|
AnyComponentWithIdentity(
|
|
id: "off",
|
|
component: AnyComponent(
|
|
FlashColorComponent(
|
|
tint: nil,
|
|
isSelected: false,
|
|
action: {
|
|
component.update(nil)
|
|
component.dismiss()
|
|
}
|
|
)
|
|
)
|
|
),
|
|
AnyComponentWithIdentity(
|
|
id: "white",
|
|
component: AnyComponent(
|
|
FlashColorComponent(
|
|
tint: .white,
|
|
isSelected: component.tint == .white,
|
|
action: {
|
|
component.update(.white)
|
|
}
|
|
)
|
|
)
|
|
),
|
|
AnyComponentWithIdentity(
|
|
id: "yellow",
|
|
component: AnyComponent(
|
|
FlashColorComponent(
|
|
tint: .yellow,
|
|
isSelected: component.tint == .yellow,
|
|
action: {
|
|
component.update(.yellow)
|
|
}
|
|
)
|
|
)
|
|
),
|
|
AnyComponentWithIdentity(
|
|
id: "blue",
|
|
component: AnyComponent(
|
|
FlashColorComponent(
|
|
tint: .blue,
|
|
isSelected: component.tint == .blue,
|
|
action: {
|
|
component.update(.blue)
|
|
}
|
|
)
|
|
)
|
|
)
|
|
],
|
|
spacing: 16.0
|
|
)
|
|
),
|
|
environment: {},
|
|
containerSize: availableSize
|
|
)
|
|
if let view = self.swatches.view {
|
|
if view.superview == nil {
|
|
self.containerView.addSubview(view)
|
|
}
|
|
view.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - swatchesSize.width) / 2.0), y: 8.0), size: swatchesSize)
|
|
}
|
|
|
|
self.dismissView.frame = CGRect(origin: .zero, size: availableSize)
|
|
|
|
self.containerView.bounds = CGRect(origin: .zero, size: size)
|
|
self.containerView.center = component.position
|
|
|
|
self.effectView.frame = CGRect(origin: CGPoint(x: 0.0, y: -7.0), size: CGSize(width: size.width, height: size.height + 7.0))
|
|
|
|
if isFirstTime {
|
|
self.containerView.layer.animateScale(from: 0.0, to: 1.0, duration: 0.35, timingFunction: kCAMediaTimingFunctionSpring)
|
|
self.containerView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
|
}
|
|
|
|
return availableSize
|
|
}
|
|
|
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
|
if !self.containerView.frame.contains(point) {
|
|
return self.dismissView
|
|
}
|
|
return super.hitTest(point, with: event)
|
|
}
|
|
}
|
|
|
|
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, state: state, environment: environment, transition: transition)
|
|
}
|
|
}
|