mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
149 lines
7.7 KiB
Swift
149 lines
7.7 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import AsyncDisplayKit
|
|
import SwiftSignalKit
|
|
|
|
private enum CornerType {
|
|
case topLeft
|
|
case topRight
|
|
case bottomLeft
|
|
case bottomRight
|
|
}
|
|
|
|
private func generateCornerImage(radius: CGFloat, type: CornerType) -> UIImage? {
|
|
return generateImage(CGSize(width: radius, height: radius), rotatedContext: { size, context in
|
|
context.setFillColor(UIColor.black.cgColor)
|
|
context.fill(CGRect(origin: CGPoint(), size: size))
|
|
context.setBlendMode(.copy)
|
|
context.setFillColor(UIColor.clear.cgColor)
|
|
|
|
UIGraphicsPushContext(context)
|
|
let origin: CGPoint
|
|
switch type {
|
|
case .topLeft:
|
|
origin = CGPoint()
|
|
case .topRight:
|
|
origin = CGPoint(x: -radius, y: 0.0)
|
|
case .bottomLeft:
|
|
origin = CGPoint(x: 0.0, y: -radius)
|
|
case .bottomRight:
|
|
origin = CGPoint(x: -radius, y: -radius)
|
|
}
|
|
UIBezierPath(roundedRect: CGRect(origin: origin, size: CGSize(width: radius * 2.0, height: radius * 2.0)), cornerRadius: radius).fill()
|
|
UIGraphicsPopContext()
|
|
})
|
|
}
|
|
|
|
public final class NavigationModalFrame: ASDisplayNode {
|
|
private let topShade: ASDisplayNode
|
|
private let leftShade: ASDisplayNode
|
|
private let rightShade: ASDisplayNode
|
|
private let bottomShade: ASDisplayNode
|
|
private let topLeftCorner: ASImageNode
|
|
private let topRightCorner: ASImageNode
|
|
private let bottomLeftCorner: ASImageNode
|
|
private let bottomRightCorner: ASImageNode
|
|
|
|
private var currentMaxCornerRadius: CGFloat?
|
|
|
|
private var progress: CGFloat = 1.0
|
|
private var additionalProgress: CGFloat = 0.0
|
|
private var validLayout: ContainerViewLayout?
|
|
|
|
override public init() {
|
|
self.topShade = ASDisplayNode()
|
|
self.topShade.backgroundColor = .black
|
|
self.leftShade = ASDisplayNode()
|
|
self.leftShade.backgroundColor = .black
|
|
self.rightShade = ASDisplayNode()
|
|
self.rightShade.backgroundColor = .black
|
|
self.bottomShade = ASDisplayNode()
|
|
self.bottomShade.backgroundColor = .black
|
|
|
|
self.topLeftCorner = ASImageNode()
|
|
self.topLeftCorner.displaysAsynchronously = false
|
|
self.topRightCorner = ASImageNode()
|
|
self.topRightCorner.displaysAsynchronously = false
|
|
self.bottomLeftCorner = ASImageNode()
|
|
self.bottomLeftCorner.displaysAsynchronously = false
|
|
self.bottomRightCorner = ASImageNode()
|
|
self.bottomRightCorner.displaysAsynchronously = false
|
|
|
|
super.init()
|
|
|
|
self.addSubnode(self.topShade)
|
|
self.addSubnode(self.leftShade)
|
|
self.addSubnode(self.rightShade)
|
|
self.addSubnode(self.bottomShade)
|
|
self.addSubnode(self.topLeftCorner)
|
|
self.addSubnode(self.topRightCorner)
|
|
self.addSubnode(self.bottomLeftCorner)
|
|
self.addSubnode(self.bottomRightCorner)
|
|
}
|
|
|
|
public func update(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
|
self.validLayout = layout
|
|
|
|
self.updateShades(layout: layout, progress: 1.0 - self.progress, additionalProgress: self.additionalProgress, transition: transition, completion: {})
|
|
}
|
|
|
|
public func updateDismissal(transition: ContainedViewLayoutTransition, progress: CGFloat, additionalProgress: CGFloat, completion: @escaping () -> Void) {
|
|
self.progress = progress
|
|
self.additionalProgress = additionalProgress
|
|
|
|
if let layout = self.validLayout {
|
|
self.updateShades(layout: layout, progress: 1.0 - progress, additionalProgress: additionalProgress, transition: transition, completion: completion)
|
|
} else {
|
|
completion()
|
|
}
|
|
}
|
|
|
|
private func updateShades(layout: ContainerViewLayout, progress: CGFloat, additionalProgress: CGFloat, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) {
|
|
let sideInset: CGFloat = 16.0
|
|
var topInset: CGFloat = 0.0
|
|
if let statusBarHeight = layout.statusBarHeight {
|
|
topInset += statusBarHeight
|
|
}
|
|
let additionalTopInset: CGFloat = 10.0
|
|
|
|
let contentScale = (layout.size.width - sideInset * 2.0) / layout.size.width
|
|
let bottomInset: CGFloat = layout.size.height - contentScale * layout.size.height - topInset
|
|
|
|
let cornerRadius: CGFloat = 9.0
|
|
let initialCornerRadius: CGFloat
|
|
if !layout.safeInsets.top.isZero {
|
|
initialCornerRadius = layout.deviceMetrics.screenCornerRadius
|
|
} else {
|
|
initialCornerRadius = 0.0
|
|
}
|
|
if self.currentMaxCornerRadius != cornerRadius {
|
|
self.topLeftCorner.image = generateCornerImage(radius: max(initialCornerRadius, cornerRadius), type: .topLeft)
|
|
self.topRightCorner.image = generateCornerImage(radius: max(initialCornerRadius, cornerRadius), type: .topRight)
|
|
self.bottomLeftCorner.image = generateCornerImage(radius: max(initialCornerRadius, cornerRadius), type: .bottomLeft)
|
|
self.bottomRightCorner.image = generateCornerImage(radius: max(initialCornerRadius, cornerRadius), type: .bottomRight)
|
|
}
|
|
|
|
let cornerSize = progress * cornerRadius + (1.0 - progress) * initialCornerRadius
|
|
let cornerSideOffset: CGFloat = progress * sideInset + additionalProgress * sideInset
|
|
let cornerTopOffset: CGFloat = progress * topInset + additionalProgress * additionalTopInset
|
|
let cornerBottomOffset: CGFloat = progress * bottomInset
|
|
transition.updateFrame(node: self.topLeftCorner, frame: CGRect(origin: CGPoint(x: cornerSideOffset, y: cornerTopOffset), size: CGSize(width: cornerSize, height: cornerSize)), beginWithCurrentState: true)
|
|
transition.updateFrame(node: self.topRightCorner, frame: CGRect(origin: CGPoint(x: layout.size.width - cornerSideOffset - cornerSize, y: cornerTopOffset), size: CGSize(width: cornerSize, height: cornerSize)), beginWithCurrentState: true)
|
|
transition.updateFrame(node: self.bottomLeftCorner, frame: CGRect(origin: CGPoint(x: cornerSideOffset, y: layout.size.height - cornerBottomOffset - cornerSize), size: CGSize(width: cornerSize, height: cornerSize)), beginWithCurrentState: true)
|
|
transition.updateFrame(node: self.bottomRightCorner, frame: CGRect(origin: CGPoint(x: layout.size.width - cornerSideOffset - cornerSize, y: layout.size.height - cornerBottomOffset - cornerSize), size: CGSize(width: cornerSize, height: cornerSize)), beginWithCurrentState: true)
|
|
|
|
let topShadeOffset: CGFloat = progress * topInset + additionalProgress * additionalTopInset
|
|
let bottomShadeOffset: CGFloat = progress * bottomInset
|
|
let leftShadeOffset: CGFloat = progress * sideInset + additionalProgress * sideInset
|
|
let rightShadeWidth: CGFloat = progress * sideInset + additionalProgress * sideInset
|
|
let rightShadeOffset: CGFloat = layout.size.width - rightShadeWidth
|
|
|
|
transition.updateFrame(node: self.topShade, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: topShadeOffset)), beginWithCurrentState: true)
|
|
transition.updateFrame(node: self.bottomShade, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - bottomShadeOffset), size: CGSize(width: layout.size.width, height: bottomShadeOffset)))
|
|
transition.updateFrame(node: self.leftShade, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: leftShadeOffset, height: layout.size.height)), beginWithCurrentState: true)
|
|
transition.updateFrame(node: self.rightShade, frame: CGRect(origin: CGPoint(x: rightShadeOffset, y: 0.0), size: CGSize(width: rightShadeWidth, height: layout.size.height)), beginWithCurrentState: true, completion: { _ in
|
|
completion()
|
|
})
|
|
}
|
|
}
|