mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
269 lines
11 KiB
Swift
269 lines
11 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import AsyncDisplayKit
|
|
import ContextUI
|
|
import TelegramPresentationData
|
|
import Display
|
|
|
|
enum PeerInfoHeaderNavigationButtonKey {
|
|
case back
|
|
case edit
|
|
case done
|
|
case cancel
|
|
case select
|
|
case selectionDone
|
|
case search
|
|
case editPhoto
|
|
case editVideo
|
|
case more
|
|
case qrCode
|
|
case moreToSearch
|
|
case postStory
|
|
}
|
|
|
|
final class PeerInfoHeaderNavigationButton: HighlightableButtonNode {
|
|
let containerNode: ContextControllerSourceNode
|
|
let contextSourceNode: ContextReferenceContentNode
|
|
private let textNode: ImmediateTextNode
|
|
private let iconNode: ASImageNode
|
|
private let backIconLayer: SimpleShapeLayer
|
|
private let backgroundNode: NavigationBackgroundNode
|
|
|
|
private var key: PeerInfoHeaderNavigationButtonKey?
|
|
|
|
private var contentsColor: UIColor = .white
|
|
private var canBeExpanded: Bool = false
|
|
|
|
var action: ((ASDisplayNode, ContextGesture?) -> Void)?
|
|
|
|
init() {
|
|
self.contextSourceNode = ContextReferenceContentNode()
|
|
self.containerNode = ContextControllerSourceNode()
|
|
self.containerNode.animateScale = false
|
|
|
|
self.textNode = ImmediateTextNode()
|
|
|
|
self.iconNode = ASImageNode()
|
|
self.iconNode.displaysAsynchronously = false
|
|
self.iconNode.displayWithoutProcessing = true
|
|
|
|
self.backIconLayer = SimpleShapeLayer()
|
|
self.backIconLayer.lineWidth = 3.0
|
|
self.backIconLayer.lineCap = .round
|
|
self.backIconLayer.lineJoin = .round
|
|
self.backIconLayer.strokeColor = UIColor.white.cgColor
|
|
self.backIconLayer.fillColor = nil
|
|
self.backIconLayer.isHidden = true
|
|
self.backIconLayer.path = try? convertSvgPath("M10.5,2 L1.5,11 L10.5,20 ")
|
|
|
|
self.backgroundNode = NavigationBackgroundNode(color: .clear, enableBlur: true)
|
|
|
|
super.init(pointerStyle: .insetRectangle(-8.0, 2.0))
|
|
|
|
self.isAccessibilityElement = true
|
|
self.accessibilityTraits = .button
|
|
|
|
self.containerNode.addSubnode(self.contextSourceNode)
|
|
self.contextSourceNode.addSubnode(self.backgroundNode)
|
|
self.contextSourceNode.addSubnode(self.textNode)
|
|
self.contextSourceNode.addSubnode(self.iconNode)
|
|
self.contextSourceNode.layer.addSublayer(self.backIconLayer)
|
|
|
|
self.addSubnode(self.containerNode)
|
|
|
|
self.containerNode.activated = { [weak self] gesture, _ in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
strongSelf.action?(strongSelf.contextSourceNode, gesture)
|
|
}
|
|
|
|
self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside)
|
|
}
|
|
|
|
@objc private func pressed() {
|
|
self.action?(self.contextSourceNode, nil)
|
|
}
|
|
|
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
|
var boundingRect = self.bounds
|
|
if self.textNode.alpha != 0.0 {
|
|
boundingRect = boundingRect.union(self.textNode.frame)
|
|
}
|
|
boundingRect = boundingRect.insetBy(dx: -8.0, dy: -4.0)
|
|
if boundingRect.contains(point) {
|
|
return super.hitTest(self.bounds.center, with: event)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func updateContentsColor(backgroundColor: UIColor, contentsColor: UIColor, canBeExpanded: Bool, transition: ContainedViewLayoutTransition) {
|
|
self.contentsColor = contentsColor
|
|
self.canBeExpanded = canBeExpanded
|
|
|
|
self.backgroundNode.updateColor(color: backgroundColor, transition: transition)
|
|
|
|
transition.updateTintColor(layer: self.textNode.layer, color: self.contentsColor)
|
|
transition.updateTintColor(layer: self.iconNode.layer, color: self.contentsColor)
|
|
transition.updateStrokeColor(layer: self.backIconLayer, strokeColor: self.contentsColor)
|
|
|
|
switch self.key {
|
|
case .back:
|
|
transition.updateAlpha(layer: self.textNode.layer, alpha: canBeExpanded ? 1.0 : 0.0)
|
|
transition.updateTransformScale(node: self.textNode, scale: canBeExpanded ? 1.0 : 0.001)
|
|
|
|
var iconTransform = CATransform3DIdentity
|
|
iconTransform = CATransform3DScale(iconTransform, canBeExpanded ? 1.0 : 0.8, canBeExpanded ? 1.0 : 0.8, 1.0)
|
|
iconTransform = CATransform3DTranslate(iconTransform, canBeExpanded ? -7.0 : 0.0, 0.0, 0.0)
|
|
transition.updateTransform(node: self.iconNode, transform: CATransform3DGetAffineTransform(iconTransform))
|
|
|
|
transition.updateTransform(layer: self.backIconLayer, transform: CATransform3DGetAffineTransform(iconTransform))
|
|
transition.updateLineWidth(layer: self.backIconLayer, lineWidth: canBeExpanded ? 3.0 : 2.075)
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
func update(key: PeerInfoHeaderNavigationButtonKey, presentationData: PresentationData, height: CGFloat) -> CGSize {
|
|
let transition: ContainedViewLayoutTransition = .immediate
|
|
|
|
var iconOffset = CGPoint()
|
|
switch key {
|
|
case .back:
|
|
iconOffset = CGPoint(x: -1.0, y: 0.0)
|
|
default:
|
|
break
|
|
}
|
|
|
|
let textSize: CGSize
|
|
if self.key != key {
|
|
self.key = key
|
|
|
|
let text: String
|
|
var accessibilityText: String
|
|
var icon: UIImage?
|
|
var isBold = false
|
|
var isGestureEnabled = false
|
|
switch key {
|
|
case .back:
|
|
text = presentationData.strings.Common_Back
|
|
accessibilityText = presentationData.strings.Common_Back
|
|
icon = NavigationBar.backArrowImage(color: .white)
|
|
case .edit:
|
|
text = presentationData.strings.Common_Edit
|
|
accessibilityText = text
|
|
case .cancel:
|
|
text = presentationData.strings.Common_Cancel
|
|
accessibilityText = text
|
|
isBold = false
|
|
case .done, .selectionDone:
|
|
text = presentationData.strings.Common_Done
|
|
accessibilityText = text
|
|
isBold = true
|
|
case .select:
|
|
text = presentationData.strings.Common_Select
|
|
accessibilityText = text
|
|
case .search:
|
|
text = ""
|
|
accessibilityText = presentationData.strings.Common_Search
|
|
icon = nil// PresentationResourcesRootController.navigationCompactSearchIcon(presentationData.theme)
|
|
case .editPhoto:
|
|
text = presentationData.strings.Settings_EditPhoto
|
|
accessibilityText = text
|
|
case .editVideo:
|
|
text = presentationData.strings.Settings_EditVideo
|
|
accessibilityText = text
|
|
case .more:
|
|
text = ""
|
|
accessibilityText = presentationData.strings.Common_More
|
|
icon = nil// PresentationResourcesRootController.navigationMoreCircledIcon(presentationData.theme)
|
|
isGestureEnabled = true
|
|
case .qrCode:
|
|
text = ""
|
|
accessibilityText = presentationData.strings.PeerInfo_QRCode_Title
|
|
icon = PresentationResourcesRootController.navigationQrCodeIcon(presentationData.theme)
|
|
case .moreToSearch:
|
|
text = ""
|
|
accessibilityText = ""
|
|
case .postStory:
|
|
text = ""
|
|
accessibilityText = presentationData.strings.Story_Privacy_PostStory
|
|
icon = PresentationResourcesRootController.navigationPostStoryIcon(presentationData.theme)
|
|
}
|
|
self.accessibilityLabel = accessibilityText
|
|
self.containerNode.isGestureEnabled = isGestureEnabled
|
|
|
|
let font: UIFont = isBold ? Font.semibold(17.0) : Font.regular(17.0)
|
|
|
|
self.textNode.attributedText = NSAttributedString(string: text, font: font, textColor: .white)
|
|
transition.updateTintColor(layer: self.textNode.layer, color: self.contentsColor)
|
|
self.iconNode.image = icon
|
|
transition.updateTintColor(layer: self.iconNode.layer, color: self.contentsColor)
|
|
|
|
self.iconNode.isHidden = false
|
|
|
|
textSize = self.textNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude))
|
|
} else {
|
|
textSize = self.textNode.bounds.size
|
|
}
|
|
|
|
let inset: CGFloat = 0.0
|
|
var textInset: CGFloat = 0.0
|
|
switch key {
|
|
case .back:
|
|
textInset += 11.0
|
|
default:
|
|
break
|
|
}
|
|
|
|
let resultSize: CGSize
|
|
|
|
let textFrame = CGRect(origin: CGPoint(x: inset + textInset, y: floor((height - textSize.height) / 2.0)), size: textSize)
|
|
self.textNode.position = textFrame.center
|
|
self.textNode.bounds = CGRect(origin: CGPoint(), size: textFrame.size)
|
|
|
|
if let image = self.iconNode.image {
|
|
let iconFrame = CGRect(origin: CGPoint(x: inset, y: floor((height - image.size.height) / 2.0)), size: image.size).offsetBy(dx: iconOffset.x, dy: iconOffset.y)
|
|
self.iconNode.position = iconFrame.center
|
|
self.iconNode.bounds = CGRect(origin: CGPoint(), size: iconFrame.size)
|
|
|
|
if case .back = key {
|
|
self.backIconLayer.position = iconFrame.center
|
|
self.backIconLayer.bounds = CGRect(origin: CGPoint(), size: iconFrame.size)
|
|
|
|
self.iconNode.isHidden = true
|
|
self.backIconLayer.isHidden = false
|
|
} else {
|
|
self.iconNode.isHidden = false
|
|
self.backIconLayer.isHidden = true
|
|
}
|
|
|
|
let size = CGSize(width: image.size.width + inset * 2.0, height: height)
|
|
self.containerNode.frame = CGRect(origin: CGPoint(), size: size)
|
|
self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: size)
|
|
resultSize = size
|
|
} else {
|
|
let size = CGSize(width: textSize.width + inset * 2.0, height: height)
|
|
self.containerNode.frame = CGRect(origin: CGPoint(), size: size)
|
|
self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: size)
|
|
resultSize = size
|
|
}
|
|
|
|
let diameter: CGFloat = 32.0
|
|
let backgroundWidth: CGFloat
|
|
if self.iconNode.image != nil {
|
|
backgroundWidth = diameter
|
|
} else {
|
|
backgroundWidth = max(diameter, resultSize.width + 12.0 * 2.0)
|
|
}
|
|
let backgroundFrame = CGRect(origin: CGPoint(x: floor((resultSize.width - backgroundWidth) * 0.5), y: floor((resultSize.height - diameter) * 0.5)), size: CGSize(width: backgroundWidth, height: diameter))
|
|
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
|
|
self.backgroundNode.update(size: backgroundFrame.size, cornerRadius: diameter * 0.5, transition: transition)
|
|
|
|
self.hitTestSlop = UIEdgeInsets(top: -2.0, left: -12.0, bottom: -2.0, right: -12.0)
|
|
|
|
return resultSize
|
|
}
|
|
}
|