2024-06-12 23:04:04 +04:00

139 lines
6.1 KiB
Swift

import Foundation
import UIKit
import Display
import ComponentFlow
import TelegramPresentationData
final class WarpView: UIView {
private final class WarpPartView: UIView {
let cloneView: PortalView
init?(contentView: PortalSourceView) {
guard let cloneView = PortalView(matchPosition: false) else {
return nil
}
self.cloneView = cloneView
super.init(frame: CGRect())
self.layer.anchorPoint = CGPoint(x: 0.5, y: 0.0)
self.clipsToBounds = true
self.addSubview(cloneView.view)
contentView.addPortal(view: cloneView)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(containerSize: CGSize, rect: CGRect, transition: ComponentTransition) {
transition.setFrame(view: self.cloneView.view, frame: CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: CGSize(width: containerSize.width, height: containerSize.height)))
}
}
let contentView: PortalSourceView
private let clippingView: UIView
private var warpViews: [WarpPartView] = []
private let warpMaskContainer: UIView
private let warpMaskGradientLayer: SimpleGradientLayer
override init(frame: CGRect) {
self.contentView = PortalSourceView()
self.clippingView = UIView()
self.warpMaskContainer = UIView()
self.warpMaskGradientLayer = SimpleGradientLayer()
self.warpMaskContainer.layer.mask = self.warpMaskGradientLayer
super.init(frame: frame)
self.clippingView.addSubview(self.contentView)
self.clippingView.clipsToBounds = true
self.addSubview(self.clippingView)
self.addSubview(self.warpMaskContainer)
for _ in 0 ..< 8 {
if let warpView = WarpPartView(contentView: self.contentView) {
self.warpViews.append(warpView)
self.warpMaskContainer.addSubview(warpView)
}
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func update(size: CGSize, topInset: CGFloat, warpHeight: CGFloat, theme: PresentationTheme, transition: ComponentTransition) {
transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: size))
let allItemsHeight = warpHeight * 0.5
for i in 0 ..< self.warpViews.count {
let itemHeight = warpHeight / CGFloat(self.warpViews.count)
let itemFraction = CGFloat(i + 1) / CGFloat(self.warpViews.count)
let _ = itemHeight
let da = CGFloat.pi * 0.5 / CGFloat(self.warpViews.count)
let alpha = CGFloat.pi * 0.5 - itemFraction * CGFloat.pi * 0.5
let endPoint = CGPoint(x: cos(alpha), y: sin(alpha))
let prevAngle = alpha + da
let prevPt = CGPoint(x: cos(prevAngle), y: sin(prevAngle))
var angle: CGFloat
angle = -atan2(endPoint.y - prevPt.y, endPoint.x - prevPt.x)
let itemLengthVector = CGPoint(x: endPoint.x - prevPt.x, y: endPoint.y - prevPt.y)
let itemLength = sqrt(itemLengthVector.x * itemLengthVector.x + itemLengthVector.y * itemLengthVector.y) * warpHeight * 0.5
let _ = itemLength
var transform: CATransform3D
transform = CATransform3DIdentity
transform.m34 = 1.0 / 240.0
transform = CATransform3DTranslate(transform, 0.0, prevPt.x * allItemsHeight, (1.0 - prevPt.y) * allItemsHeight)
transform = CATransform3DRotate(transform, angle, 1.0, 0.0, 0.0)
let positionY = size.height - allItemsHeight + 4.0 + CGFloat(i) * itemLength
let rect = CGRect(origin: CGPoint(x: 0.0, y: positionY), size: CGSize(width: size.width, height: itemLength))
transition.setPosition(view: self.warpViews[i], position: CGPoint(x: rect.midX, y: 4.0))
transition.setBounds(view: self.warpViews[i], bounds: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: itemLength)))
transition.setTransform(view: self.warpViews[i], transform: transform)
self.warpViews[i].update(containerSize: size, rect: rect, transition: transition)
}
let clippingTopInset: CGFloat = topInset
let frame = CGRect(origin: CGPoint(x: 0.0, y: clippingTopInset), size: CGSize(width: size.width, height: -clippingTopInset + size.height - 21.0))
transition.setPosition(view: self.clippingView, position: frame.center)
transition.setBounds(view: self.clippingView, bounds: CGRect(origin: CGPoint(x: 0.0, y: clippingTopInset), size: frame.size))
self.clippingView.clipsToBounds = true
transition.setFrame(view: self.warpMaskContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: size.height - allItemsHeight), size: CGSize(width: size.width, height: allItemsHeight)))
var locations: [NSNumber] = []
var colors: [CGColor] = []
let numStops = 6
for i in 0 ..< numStops {
let step = CGFloat(i) / CGFloat(numStops - 1)
locations.append(step as NSNumber)
colors.append(UIColor.black.withAlphaComponent(1.0 - step * step).cgColor)
}
let gradientHeight: CGFloat = 6.0
self.warpMaskGradientLayer.startPoint = CGPoint(x: 0.0, y: (allItemsHeight - gradientHeight) / allItemsHeight)
self.warpMaskGradientLayer.endPoint = CGPoint(x: 0.0, y: 1.0)
self.warpMaskGradientLayer.locations = locations
self.warpMaskGradientLayer.colors = colors
self.warpMaskGradientLayer.type = .axial
transition.setFrame(layer: self.warpMaskGradientLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: allItemsHeight)))
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
return self.contentView.hitTest(point, with: event)
}
}