Files
Swiftgram/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift
2025-12-30 02:05:00 +08:00

1242 lines
65 KiB
Swift

import Foundation
import UIKit
import Display
import ComponentFlow
import GlassBackgroundComponent
import AsyncDisplayKit
import EdgeEffect
import ComponentDisplayAdapters
public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
public static var defaultSecondaryContentHeight: CGFloat {
return 38.0
}
public static let thinBackArrowImage = generateTintedImage(image: UIImage(bundleImageName: "Navigation/BackArrow"), color: .white)?.withRenderingMode(.alwaysTemplate)
public static let titleFont = Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers])
var presentationData: NavigationBarPresentationData
private var validLayout: (size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool)?
private var requestedLayout: Bool = false
public var requestContainerLayout: ((ContainedViewLayoutTransition) -> Void)?
public var backPressed: () -> () = { }
public var userInfo: Any?
public var makeCustomTransitionNode: ((NavigationBar, Bool) -> CustomNavigationTransitionNode?)?
public var allowsCustomTransition: (() -> Bool)?
public let stripeNode: ASDisplayNode
public let clippingNode: SparseNode
private let buttonsContainerNode: ASDisplayNode
public private(set) var contentNode: NavigationBarContentNode?
public private(set) var secondaryContentNode: ASDisplayNode?
public var secondaryContentNodeDisplayFraction: CGFloat = 1.0
private var itemTitleListenerKey: Int?
private var itemTitleViewListenerKey: Int?
private var itemLeftButtonListenerKey: Int?
private var itemLeftButtonSetEnabledListenerKey: Int?
private var itemRightButtonListenerKey: Int?
private var itemRightButtonsListenerKey: Int?
private var itemBadgeListenerKey: Int?
private var hintAnimateTitleNodeOnNextLayout: Bool = false
private var _item: UINavigationItem?
public var item: UINavigationItem? {
get {
return self._item
} set(value) {
if let previousValue = self._item {
if let itemTitleListenerKey = self.itemTitleListenerKey {
previousValue.removeSetTitleListener(itemTitleListenerKey)
self.itemTitleListenerKey = nil
}
if let itemLeftButtonListenerKey = self.itemLeftButtonListenerKey {
previousValue.removeSetLeftBarButtonItemListener(itemLeftButtonListenerKey)
self.itemLeftButtonListenerKey = nil
}
if let itemLeftButtonSetEnabledListenerKey = self.itemLeftButtonSetEnabledListenerKey {
previousValue.leftBarButtonItem?.removeSetEnabledListener(itemLeftButtonSetEnabledListenerKey)
self.itemLeftButtonSetEnabledListenerKey = nil
}
if let itemRightButtonListenerKey = self.itemRightButtonListenerKey {
previousValue.removeSetRightBarButtonItemListener(itemRightButtonListenerKey)
self.itemRightButtonListenerKey = nil
}
if let itemRightButtonsListenerKey = self.itemRightButtonsListenerKey {
previousValue.removeSetMultipleRightBarButtonItemsListener(itemRightButtonsListenerKey)
self.itemRightButtonsListenerKey = nil
}
if let itemBadgeListenerKey = self.itemBadgeListenerKey {
previousValue.removeSetBadgeListener(itemBadgeListenerKey)
self.itemBadgeListenerKey = nil
}
}
self._item = value
self.leftButtonNodeImpl.view.removeFromSuperview()
self.rightButtonNodeImpl.view.removeFromSuperview()
if let item = value {
self.title = item.title
self.itemTitleListenerKey = item.addSetTitleListener { [weak self] text, animated in
if let strongSelf = self {
let animateIn = animated && (strongSelf.title?.isEmpty ?? true)
strongSelf.title = text
if animateIn {
strongSelf.titleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
}
}
}
let itemTitleView = item.titleView
if self.titleView !== itemTitleView {
if let oldTitleView = self.titleView as? NavigationBarTitleView {
oldTitleView.requestUpdate = nil
}
self.titleView = itemTitleView
if let titleView = self.titleView as? NavigationBarTitleView {
titleView.requestUpdate = { [weak self, weak titleView] transition in
guard let self, let titleView, self.titleView === titleView else {
return
}
if let requestContainerLayout = self.requestContainerLayout {
requestContainerLayout(transition)
} else {
self.requestLayout()
}
}
}
}
self.itemTitleViewListenerKey = item.addSetTitleViewListener { [weak self] itemTitleView in
guard let self else {
return
}
if let oldTitleView = self.titleView as? NavigationBarTitleView {
oldTitleView.requestUpdate = nil
}
self.titleView = itemTitleView
if let titleView = self.titleView as? NavigationBarTitleView {
titleView.requestUpdate = { [weak self, weak titleView] transition in
guard let self, let titleView, self.titleView === titleView else {
return
}
if let requestContainerLayout = self.requestContainerLayout {
requestContainerLayout(transition)
} else {
self.requestLayout()
}
}
}
}
self.itemLeftButtonListenerKey = item.addSetLeftBarButtonItemListener { [weak self] previousItem, _, animated in
if let strongSelf = self {
if let itemLeftButtonSetEnabledListenerKey = strongSelf.itemLeftButtonSetEnabledListenerKey {
previousItem?.removeSetEnabledListener(itemLeftButtonSetEnabledListenerKey)
strongSelf.itemLeftButtonSetEnabledListenerKey = nil
}
strongSelf.updateLeftButton(animated: animated)
strongSelf.invalidateCalculatedLayout()
strongSelf.requestLayout()
}
}
self.itemRightButtonListenerKey = item.addSetRightBarButtonItemListener { [weak self] previousItem, currentItem, animated in
if let strongSelf = self {
strongSelf.updateRightButton(animated: animated)
strongSelf.invalidateCalculatedLayout()
strongSelf.requestLayout()
}
}
self.itemRightButtonsListenerKey = item.addSetMultipleRightBarButtonItemsListener { [weak self] items, animated in
if let strongSelf = self {
strongSelf.updateRightButton(animated: animated)
strongSelf.invalidateCalculatedLayout()
strongSelf.requestLayout()
}
}
self.itemBadgeListenerKey = item.addSetBadgeListener { [weak self] text in
if let strongSelf = self {
strongSelf.updateBadgeText(text: text)
}
}
self.updateBadgeText(text: item.badge)
self.updateLeftButton(animated: false)
self.updateRightButton(animated: false)
} else {
self.title = nil
self.updateLeftButton(animated: false)
self.updateRightButton(animated: false)
}
self.invalidateCalculatedLayout()
self.requestLayout()
}
}
public var customBackButtonText: String?
private var title: String? {
didSet {
if let title = self.title {
self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBarImpl.titleFont, textColor: self.presentationData.theme.primaryTextColor)
self.titleNode.accessibilityLabel = title
if self.titleNode.supernode == nil {
self.buttonsContainerNode.addSubnode(self.titleNode)
}
} else {
self.titleNode.removeFromSupernode()
}
self.updateAccessibilityElements()
self.invalidateCalculatedLayout()
self.requestLayout()
}
}
public private(set) var titleView: UIView? {
didSet {
if let oldValue = oldValue {
oldValue.removeFromSuperview()
}
if let titleView = self.titleView {
if let backgroundContainer = self.backgroundContainer {
backgroundContainer.contentView.addSubview(titleView)
} else {
self.buttonsContainerNode.view.addSubview(titleView)
}
}
self.invalidateCalculatedLayout()
self.requestLayout()
}
}
public var layoutSuspended: Bool = false
private let titleNode: ImmediateTextNode
var previousItemListenerKey: Int?
var previousItemBackListenerKey: Int?
private func updateAccessibilityElements() {
}
override public var accessibilityElements: [Any]? {
get {
var accessibilityElements: [Any] = []
if self.backButtonNodeImpl.view.superview != nil {
addAccessibilityChildren(of: self.backButtonNodeImpl, container: self, to: &accessibilityElements)
}
if self.leftButtonNodeImpl.view.superview != nil {
addAccessibilityChildren(of: self.leftButtonNodeImpl, container: self, to: &accessibilityElements)
}
if self.titleNode.view.superview != nil {
addAccessibilityChildren(of: self.titleNode, container: self, to: &accessibilityElements)
accessibilityElements.append(self.titleNode)
}
if let titleView = self.titleView, titleView.superview != nil {
titleView.accessibilityFrame = UIAccessibility.convertToScreenCoordinates(titleView.bounds, in: titleView)
accessibilityElements.append(titleView)
}
if self.rightButtonNodeImpl.supernode != nil {
addAccessibilityChildren(of: self.rightButtonNodeImpl, container: self, to: &accessibilityElements)
}
if let contentNode = self.contentNode {
addAccessibilityChildren(of: contentNode, container: self, to: &accessibilityElements)
}
if let secondaryContentNode = self.secondaryContentNode {
addAccessibilityChildren(of: secondaryContentNode, container: self, to: &accessibilityElements)
}
return accessibilityElements
} set(value) {
}
}
override public func didLoad() {
super.didLoad()
self.updateAccessibilityElements()
}
public var enableAutomaticBackButton: Bool = true
var _previousItem: NavigationPreviousAction?
public var previousItem: NavigationPreviousAction? {
get {
if !self.enableAutomaticBackButton {
return nil
}
return self._previousItem
} set(value) {
if !self.enableAutomaticBackButton {
self._previousItem = nil
return
}
if self._previousItem != value {
if let previousValue = self._previousItem, case let .item(itemValue) = previousValue {
if let previousItemListenerKey = self.previousItemListenerKey {
itemValue.removeSetTitleListener(previousItemListenerKey)
self.previousItemListenerKey = nil
}
if let previousItemBackListenerKey = self.previousItemBackListenerKey {
itemValue.removeSetBackBarButtonItemListener(previousItemBackListenerKey)
self.previousItemBackListenerKey = nil
}
}
self._previousItem = value
if let previousItem = value {
switch previousItem {
case let .item(itemValue):
self.previousItemListenerKey = itemValue.addSetTitleListener { [weak self] _, _ in
if let strongSelf = self, let previousItem = strongSelf.previousItem, case let .item(itemValue) = previousItem {
if case .glass = strongSelf.presentationData.theme.style {
strongSelf.backButtonNodeImpl.updateManualText("", isBack: true)
} else if let customBackButtonText = strongSelf.customBackButtonText {
strongSelf.backButtonNodeImpl.updateManualText(customBackButtonText, isBack: true)
} else if let backBarButtonItem = itemValue.backBarButtonItem {
strongSelf.backButtonNodeImpl.updateManualText(backBarButtonItem.title ?? "", isBack: true)
} else {
strongSelf.backButtonNodeImpl.updateManualText(itemValue.title ?? "", isBack: true)
}
strongSelf.invalidateCalculatedLayout()
strongSelf.requestLayout()
}
}
self.previousItemBackListenerKey = itemValue.addSetBackBarButtonItemListener { [weak self] _, _, _ in
if let strongSelf = self, let previousItem = strongSelf.previousItem, case let .item(itemValue) = previousItem {
if case .glass = strongSelf.presentationData.theme.style {
strongSelf.backButtonNodeImpl.updateManualText("", isBack: true)
} else if let customBackButtonText = strongSelf.customBackButtonText {
strongSelf.backButtonNodeImpl.updateManualText(customBackButtonText, isBack: true)
} else if let backBarButtonItem = itemValue.backBarButtonItem {
strongSelf.backButtonNodeImpl.updateManualText(backBarButtonItem.title ?? "", isBack: true)
} else {
strongSelf.backButtonNodeImpl.updateManualText(itemValue.title ?? "", isBack: true)
}
strongSelf.invalidateCalculatedLayout()
strongSelf.requestLayout()
}
}
case .close:
break
}
}
self.updateLeftButton(animated: false)
self.invalidateCalculatedLayout()
self.requestLayout()
}
}
}
private func updateBadgeText(text: String?) {
let actualText = text ?? ""
if self.badgeNode.text != actualText {
self.badgeNode.text = actualText
self.badgeNode.isHidden = actualText.isEmpty
self.backButtonNodeImpl.manualAlpha = self.badgeNode.isHidden ? 1.0 : 0.0
self.invalidateCalculatedLayout()
self.requestLayout()
}
}
private func updateLeftButton(animated: Bool) {
if let item = self.item {
var needsLeftButton = false
if let leftBarButtonItem = item.leftBarButtonItem, !leftBarButtonItem.backButtonAppearance {
needsLeftButton = true
} else if let previousItem = self.previousItem, case .close = previousItem {
needsLeftButton = true
}
if needsLeftButton {
if animated {
if self.backButtonNodeImpl.view.superview != nil {
if let snapshotView = self.backButtonNodeImpl.view.snapshotContentTree() {
snapshotView.frame = self.backButtonNodeImpl.frame
self.backButtonNodeImpl.view.superview?.insertSubview(snapshotView, aboveSubview: self.backButtonNodeImpl.view)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
}
if self.backButtonArrow.view.superview != nil {
if let snapshotView = self.backButtonArrow.view.snapshotContentTree() {
snapshotView.frame = self.backButtonArrow.frame
self.backButtonArrow.view.superview?.insertSubview(snapshotView, aboveSubview: self.backButtonArrow.view)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
}
if self.badgeNode.view.superview != nil {
if let snapshotView = self.badgeNode.view.snapshotContentTree() {
snapshotView.frame = self.badgeNode.frame
self.badgeNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.badgeNode.view)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
}
}
self.backButtonNodeImpl.view.removeFromSuperview()
self.backButtonArrow.view.removeFromSuperview()
self.badgeNode.view.removeFromSuperview()
if let leftBarButtonItem = item.leftBarButtonItem {
self.leftButtonNodeImpl.updateItems([], animated: animated)
self.leftButtonNodeImpl.updateItems([leftBarButtonItem], animated: animated)
} else {
self.leftButtonNodeImpl.updateItems([], animated: animated)
self.leftButtonNodeImpl.updateItems([UIBarButtonItem(title: "___close", style: .plain, target: nil, action: nil)], animated: animated)
}
if self.leftButtonNodeImpl.supernode == nil {
if let leftButtonsBackgroundView = self.leftButtonsBackgroundView {
leftButtonsBackgroundView.container.addSubview(self.leftButtonNodeImpl.view)
} else {
self.buttonsContainerNode.view.addSubview(self.leftButtonNodeImpl.view)
}
}
if animated {
self.leftButtonNodeImpl.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
}
} else {
if animated, self.leftButtonNodeImpl.view.superview != nil {
if let snapshotView = self.leftButtonNodeImpl.view.snapshotContentTree() {
snapshotView.frame = self.leftButtonNodeImpl.frame
self.leftButtonNodeImpl.view.superview?.insertSubview(snapshotView, aboveSubview: self.leftButtonNodeImpl.view)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
}
self.leftButtonNodeImpl.view.removeFromSuperview()
var backTitle: String?
if case .glass = self.presentationData.theme.style {
backTitle = ""
} else if let customBackButtonText = self.customBackButtonText {
backTitle = customBackButtonText
} else if let leftBarButtonItem = item.leftBarButtonItem, leftBarButtonItem.backButtonAppearance {
backTitle = leftBarButtonItem.title
} else if let previousItem = self.previousItem {
switch previousItem {
case let .item(itemValue):
if let backBarButtonItem = itemValue.backBarButtonItem {
backTitle = backBarButtonItem.title ?? self.presentationData.strings.back
} else {
backTitle = itemValue.title ?? self.presentationData.strings.back
}
case .close:
backTitle = nil
}
}
if let backTitle {
self.backButtonNodeImpl.updateManualText(backTitle, isBack: true)
if self.backButtonNodeImpl.supernode == nil {
if let leftButtonsBackgroundView = self.leftButtonsBackgroundView {
leftButtonsBackgroundView.container.addSubview(self.backButtonNodeImpl.view)
leftButtonsBackgroundView.container.addSubview(self.backButtonArrow.view)
leftButtonsBackgroundView.container.addSubview(self.badgeNode.view)
} else {
self.buttonsContainerNode.view.addSubview(self.backButtonNodeImpl.view)
self.buttonsContainerNode.view.addSubview(self.backButtonArrow.view)
self.buttonsContainerNode.view.addSubview(self.badgeNode.view)
}
}
if animated {
self.backButtonNodeImpl.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
self.backButtonArrow.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
self.badgeNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
}
} else {
self.backButtonNodeImpl.view.removeFromSuperview()
}
}
} else {
self.leftButtonNodeImpl.view.removeFromSuperview()
self.backButtonNodeImpl.view.removeFromSuperview()
self.backButtonArrow.view.removeFromSuperview()
self.badgeNode.view.removeFromSuperview()
}
self.updateAccessibilityElements()
}
private func updateRightButton(animated: Bool) {
if let item = self.item {
var items: [UIBarButtonItem] = []
if let rightBarButtonItems = item.rightBarButtonItems, !rightBarButtonItems.isEmpty {
items = rightBarButtonItems
} else if let rightBarButtonItem = item.rightBarButtonItem {
items = [rightBarButtonItem]
}
self.rightButtonNodeUpdated = true
if !items.isEmpty {
if self.rightButtonNodeImpl.isEmpty {
self.rightButtonNodeImpl.updateItems(items, animated: false)
} else {
self.rightButtonNodeImpl.updateItems([], animated: animated)
self.rightButtonNodeImpl.updateItems(items, animated: animated)
}
if self.rightButtonNodeImpl.view.superview == nil {
if let rightButtonsBackgroundView = self.rightButtonsBackgroundView {
rightButtonsBackgroundView.container.addSubview(self.rightButtonNodeImpl.view)
} else {
self.buttonsContainerNode.view.addSubview(self.rightButtonNodeImpl.view)
}
}
} else {
if animated, self.rightButtonNodeImpl.view.superview != nil {
if let snapshotView = self.rightButtonNodeImpl.view.snapshotContentTree() {
snapshotView.frame = self.rightButtonNodeImpl.frame
self.rightButtonNodeImpl.view.superview?.insertSubview(snapshotView, aboveSubview: self.rightButtonNodeImpl.view)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
}
self.rightButtonNodeImpl.view.removeFromSuperview()
self.rightButtonNodeImpl.updateItems([], animated: false)
}
} else {
if animated, self.rightButtonNodeImpl.view.superview != nil {
if let snapshotView = self.rightButtonNodeImpl.view.snapshotContentTree() {
snapshotView.frame = self.rightButtonNodeImpl.frame
self.rightButtonNodeImpl.view.superview?.insertSubview(snapshotView, aboveSubview: self.rightButtonNodeImpl.view)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
}
self.rightButtonNodeImpl.view.removeFromSuperview()
self.rightButtonNodeImpl.updateItems([], animated: false)
}
self.updateAccessibilityElements()
}
public let backgroundNode: NavigationBackgroundNode
private var leftButtonsBackgroundView: (background: GlassBackgroundView, container: UIView)?
private var rightButtonsBackgroundView: (background: GlassBackgroundView, container: UIView)?
private let backButtonNodeImpl: NavigationButtonNodeImpl
public var backButtonNode: NavigationButtonNode {
return self.backButtonNodeImpl
}
public let badgeNode: NavigationBarBadgeNode
public let backButtonArrow: ASImageNode
private let leftButtonNodeImpl: NavigationButtonNodeImpl
public var leftButtonNode: NavigationButtonNode {
return self.leftButtonNodeImpl
}
private let rightButtonNodeImpl: NavigationButtonNodeImpl
public var rightButtonNode: NavigationButtonNode {
return self.rightButtonNodeImpl
}
private var rightButtonNodeUpdated: Bool = false
public let additionalContentNode: SparseNode
public func reattachAdditionalContentNode() {
if self.additionalContentNode.supernode !== self {
self.insertSubnode(self.additionalContentNode, aboveSubnode: self.clippingNode)
}
}
public var secondaryContentHeight: CGFloat
private var edgeEffectExtension: CGFloat = 0.0
private var edgeEffectView: EdgeEffectView?
private var backgroundContainer: GlassBackgroundContainerView?
public var backgroundView: UIView {
if let edgeEffectView = self.edgeEffectView {
return edgeEffectView
} else {
return self.backgroundNode.view
}
}
public let customOverBackgroundContentView: UIView
public init(presentationData: NavigationBarPresentationData) {
self.presentationData = presentationData
self.stripeNode = ASDisplayNode()
self.titleNode = ImmediateTextNode()
self.titleNode.isAccessibilityElement = true
self.titleNode.accessibilityTraits = .header
self.backButtonNodeImpl = NavigationButtonNodeImpl(isGlass: presentationData.theme.style == .glass)
if case .glass = presentationData.theme.style {
} else {
self.backButtonNodeImpl.hitTestSlop = UIEdgeInsets(top: 0.0, left: -20.0, bottom: 0.0, right: 0.0)
}
self.badgeNode = NavigationBarBadgeNode(fillColor: self.presentationData.theme.buttonColor, strokeColor: self.presentationData.theme.buttonColor, textColor: self.presentationData.theme.badgeTextColor)
self.badgeNode.isUserInteractionEnabled = false
self.badgeNode.isHidden = true
self.backButtonArrow = ASImageNode()
self.backButtonArrow.displayWithoutProcessing = true
self.backButtonArrow.displaysAsynchronously = false
self.backButtonArrow.isUserInteractionEnabled = false
self.leftButtonNodeImpl = NavigationButtonNodeImpl(isGlass: presentationData.theme.style == .glass)
self.rightButtonNodeImpl = NavigationButtonNodeImpl(isGlass: presentationData.theme.style == .glass)
if case .glass = presentationData.theme.style {
} else {
self.rightButtonNodeImpl.hitTestSlop = UIEdgeInsets(top: -4.0, left: -4.0, bottom: -4.0, right: -10.0)
}
self.clippingNode = SparseNode()
if case .glass = presentationData.theme.style {
} else {
self.clippingNode.clipsToBounds = true
}
self.buttonsContainerNode = SparseNode()
self.buttonsContainerNode.clipsToBounds = true
self.backButtonNodeImpl.color = self.presentationData.theme.buttonColor
self.backButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor
self.leftButtonNodeImpl.color = self.presentationData.theme.buttonColor
self.leftButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor
self.rightButtonNodeImpl.color = self.presentationData.theme.buttonColor
self.rightButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor
self.backButtonArrow.image = presentationData.theme.style == .glass ? generateTintedImage(image: glassBackArrowImage, color: self.presentationData.theme.buttonColor) : navigationBarBackArrowImage(color: self.presentationData.theme.buttonColor)
if let title = self.title {
self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBarImpl.titleFont, textColor: self.presentationData.theme.primaryTextColor)
self.titleNode.accessibilityLabel = title
}
self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor
self.backgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.backgroundColor, enableBlur: self.presentationData.theme.enableBackgroundBlur)
self.additionalContentNode = SparseNode()
self.secondaryContentHeight = NavigationBarImpl.defaultSecondaryContentHeight
self.customOverBackgroundContentView = SparseContainerView()
super.init()
if case .glass = presentationData.theme.style {
let edgeEffectView = EdgeEffectView()
self.edgeEffectView = edgeEffectView
self.view.addSubview(edgeEffectView)
let backgroundContainer = GlassBackgroundContainerView()
self.backgroundContainer = backgroundContainer
self.view.addSubview(backgroundContainer)
backgroundContainer.contentView.addSubview(self.customOverBackgroundContentView)
let leftButtonsBackgroundView: (background: GlassBackgroundView, container: UIView) = (GlassBackgroundView(), UIView())
leftButtonsBackgroundView.background.contentView.addSubview(leftButtonsBackgroundView.container)
self.leftButtonsBackgroundView = leftButtonsBackgroundView
backgroundContainer.contentView.addSubview(leftButtonsBackgroundView.background)
let rightButtonsBackgroundView: (background: GlassBackgroundView, container: UIView) = (GlassBackgroundView(), UIView())
rightButtonsBackgroundView.background.contentView.addSubview(rightButtonsBackgroundView.container)
self.rightButtonsBackgroundView = rightButtonsBackgroundView
backgroundContainer.contentView.addSubview(rightButtonsBackgroundView.background)
} else {
self.addSubnode(self.backgroundNode)
self.view.addSubview(self.customOverBackgroundContentView)
}
self.addSubnode(self.buttonsContainerNode)
self.addSubnode(self.clippingNode)
self.addSubnode(self.additionalContentNode)
self.stripeNode.isLayerBacked = true
self.stripeNode.displaysAsynchronously = false
if case .legacy = presentationData.theme.style {
self.addSubnode(self.stripeNode)
}
self.backgroundColor = nil
self.isOpaque = false
self.titleNode.displaysAsynchronously = false
self.titleNode.maximumNumberOfLines = 1
self.titleNode.truncationType = .end
self.titleNode.isOpaque = false
self.backButtonNodeImpl.highlightChanged = { [weak self] index, highlighted in
if let strongSelf = self, index == 0 {
strongSelf.backButtonArrow.alpha = (highlighted ? 0.4 : 1.0)
strongSelf.badgeNode.alpha = (highlighted ? 0.4 : 1.0)
}
}
self.backButtonNodeImpl.pressed = { [weak self] index in
if let strongSelf = self, index == 0 {
if let leftBarButtonItem = strongSelf.item?.leftBarButtonItem, leftBarButtonItem.backButtonAppearance {
leftBarButtonItem.performActionOnTarget()
} else {
strongSelf.backPressed()
}
}
}
self.leftButtonNodeImpl.pressed = { [weak self] index in
if let item = self?.item {
if index == 0 {
if let leftBarButtonItem = item.leftBarButtonItem {
leftBarButtonItem.performActionOnTarget()
} else if let previousItem = self?.previousItem, case .close = previousItem {
self?.backPressed()
}
}
}
}
self.rightButtonNodeImpl.pressed = { [weak self] index in
if let item = self?.item {
if let rightBarButtonItems = item.rightBarButtonItems, !rightBarButtonItems.isEmpty {
if index < rightBarButtonItems.count {
rightBarButtonItems[index].performActionOnTarget()
}
} else if let rightBarButtonItem = item.rightBarButtonItem {
rightBarButtonItem.performActionOnTarget()
}
}
}
}
public var isBackgroundVisible: Bool {
return self.backgroundNode.alpha == 1.0
}
public func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) {
let alpha = max(0.0, min(1.0, alpha))
transition.updateAlpha(node: self.backgroundNode, alpha: alpha, delay: 0.15)
transition.updateAlpha(node: self.stripeNode, alpha: alpha, delay: 0.15)
}
public func updatePresentationData(_ presentationData: NavigationBarPresentationData, transition: ContainedViewLayoutTransition = .immediate) {
if presentationData.theme !== self.presentationData.theme || presentationData.strings !== self.presentationData.strings {
self.presentationData = presentationData
self.backgroundNode.updateColor(color: self.presentationData.theme.backgroundColor, transition: transition)
self.backButtonNodeImpl.color = self.presentationData.theme.buttonColor
self.backButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor
self.leftButtonNodeImpl.color = self.presentationData.theme.buttonColor
self.leftButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor
self.rightButtonNodeImpl.color = self.presentationData.theme.buttonColor
self.rightButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor
self.backButtonArrow.image = self.presentationData.theme.style == .glass ? generateTintedImage(image: glassBackArrowImage, color: self.presentationData.theme.buttonColor) : navigationBarBackArrowImage(color: self.presentationData.theme.buttonColor)
if let title = self.title {
self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBarImpl.titleFont, textColor: self.presentationData.theme.primaryTextColor)
self.titleNode.accessibilityLabel = title
}
self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor
self.badgeNode.updateTheme(fillColor: self.presentationData.theme.buttonColor, strokeColor: self.presentationData.theme.buttonColor, textColor: self.presentationData.theme.badgeTextColor)
self.updateLeftButton(animated: false)
self.requestLayout()
}
}
private func requestLayout() {
self.requestedLayout = true
self.setNeedsLayout()
}
override public func layout() {
super.layout()
if let validLayout = self.validLayout, self.requestedLayout {
self.requestedLayout = false
self.updateLayout(size: validLayout.size, defaultHeight: validLayout.defaultHeight, additionalTopHeight: validLayout.additionalTopHeight, additionalContentHeight: validLayout.additionalContentHeight, additionalBackgroundHeight: validLayout.additionalBackgroundHeight, additionalCutout: validLayout.additionalCutout, leftInset: validLayout.leftInset, rightInset: validLayout.rightInset, appearsHidden: validLayout.appearsHidden, isLandscape: validLayout.isLandscape, transition: .immediate)
}
}
public func updateLayout(size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool, transition: ContainedViewLayoutTransition) {
if self.layoutSuspended {
return
}
self.validLayout = (size, defaultHeight, additionalTopHeight, additionalContentHeight, additionalBackgroundHeight, additionalCutout, leftInset, rightInset, appearsHidden, isLandscape)
var contentVerticalOrigin = additionalTopHeight
if case .glass = self.presentationData.theme.style {
contentVerticalOrigin += 2.0
}
let backgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight))
if self.backgroundNode.frame != backgroundFrame {
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
self.backgroundNode.update(size: backgroundFrame.size, transition: transition)
}
if let backgroundContainer = self.backgroundContainer {
var backgroundContainerFrame = backgroundFrame
backgroundContainerFrame.size.height += 44.0
transition.updateFrame(view: backgroundContainer, frame: backgroundContainerFrame)
backgroundContainer.update(size: backgroundContainerFrame.size, isDark: self.presentationData.theme.overallDarkAppearance, transition: ComponentTransition(transition))
}
if let edgeEffectView = self.edgeEffectView {
if let edgeEffectColor = self.presentationData.theme.edgeEffectColor, edgeEffectColor.alpha == 0.0 {
edgeEffectView.isHidden = true
} else {
edgeEffectView.isHidden = false
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: -20.0), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight + 20.0 + 20.0))
transition.updatePosition(layer: edgeEffectView.layer, position: edgeEffectFrame.center)
transition.updateBounds(layer: edgeEffectView.layer, bounds: CGRect(origin: CGPoint(), size: edgeEffectFrame.size))
edgeEffectView.update(content: self.presentationData.theme.edgeEffectColor ?? .white, blur: true, rect: CGRect(origin: CGPoint(), size: edgeEffectFrame.size), edge: .top, edgeSize: 50.0, transition: ComponentTransition(transition))
}
}
let apparentAdditionalHeight: CGFloat = self.secondaryContentNode != nil ? (self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction) : 0.0
let leftButtonInset: CGFloat = leftInset + 16.0
let backButtonInset: CGFloat = leftInset + 27.0
transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: size))
transition.updateFrame(node: self.additionalContentNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight)))
transition.updateFrame(node: self.buttonsContainerNode, frame: CGRect(origin: CGPoint(), size: size))
var expansionHeight: CGFloat = 0.0
if let contentNode = self.contentNode {
var contentNodeFrame: CGRect
switch contentNode.mode {
case .replacement:
expansionHeight = contentNode.height - defaultHeight
if case .glass = self.presentationData.theme.style {
contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: contentVerticalOrigin), size: CGSize(width: size.width, height: contentNode.nominalHeight))
} else {
contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height - additionalContentHeight))
}
transition.updateFrame(node: contentNode, frame: contentNodeFrame)
let _ = contentNode.updateLayout(size: contentNodeFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition)
case .expansion:
expansionHeight = contentNode.height
let additionalExpansionHeight: CGFloat = self.secondaryContentNode != nil && appearsHidden ? (self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction) : 0.0
contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - (appearsHidden ? 0.0 : additionalContentHeight) - expansionHeight - apparentAdditionalHeight - additionalExpansionHeight), size: CGSize(width: size.width, height: expansionHeight))
if appearsHidden {
contentNodeFrame.origin.y = size.height - contentNode.height + contentVerticalOrigin
}
if appearsHidden {
if self.secondaryContentNode != nil {
contentNodeFrame.origin.y += self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction
}
}
transition.updateFrame(node: contentNode, frame: contentNodeFrame)
let _ = contentNode.updateLayout(size: contentNodeFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition)
}
}
transition.updateFrame(node: self.stripeNode, frame: CGRect(x: (additionalCutout?.width ?? 0.0), y: size.height + additionalBackgroundHeight, width: size.width - (additionalCutout?.width ?? 0.0), height: UIScreenPixel))
let nominalHeight: CGFloat = 60.0
var leftTitleInset: CGFloat = leftInset
var rightTitleInset: CGFloat = rightInset
var leftButtonsWidth: CGFloat = 0.0
if self.backButtonNodeImpl.view.superview != nil {
let backButtonSize = self.backButtonNodeImpl.updateLayout(constrainedSize: CGSize(width: size.width, height: 44.0), isLandscape: isLandscape, isLeftAligned: true)
leftTitleInset = backButtonSize.width + backButtonInset
if case .glass = self.presentationData.theme.style {
} else {
let topHitTestSlop = (nominalHeight - backButtonSize.height) * 0.5
self.backButtonNodeImpl.hitTestSlop = UIEdgeInsets(top: -topHitTestSlop, left: -27.0, bottom: -topHitTestSlop, right: -8.0)
}
do {
self.backButtonNodeImpl.alpha = 1.0
if case .glass = self.presentationData.theme.style {
} else {
transition.updateFrame(node: self.backButtonNodeImpl, frame: CGRect(origin: CGPoint(x: backButtonInset, y: contentVerticalOrigin + floor((nominalHeight - backButtonSize.height) / 2.0)), size: backButtonSize))
}
self.backButtonArrow.alpha = 1.0
let backButtonArrowFrame: CGRect
if case .glass = self.presentationData.theme.style {
let backButtonArrowSize = CGSize(width: 44.0, height: 44.0)
backButtonArrowFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: backButtonArrowSize)
} else {
let backButtonArrowSize = CGSize(width: 13.0, height: 22.0)
backButtonArrowFrame = CGRect(origin: CGPoint(x: leftInset + 8.0, y: contentVerticalOrigin + floor((nominalHeight - backButtonArrowSize.height) / 2.0)), size: backButtonArrowSize)
}
leftButtonsWidth += backButtonArrowFrame.width
transition.updateFrame(node: self.backButtonArrow, frame: backButtonArrowFrame)
self.badgeNode.alpha = 1.0
}
} else if self.leftButtonNodeImpl.view.superview != nil {
let leftButtonSize = self.leftButtonNodeImpl.updateLayout(constrainedSize: CGSize(width: size.width, height: 44.0), isLandscape: isLandscape, isLeftAligned: true)
leftTitleInset = leftButtonSize.width + leftButtonInset + 1.0
var transition = transition
if self.leftButtonNodeImpl.frame.width.isZero {
transition = .immediate
}
self.leftButtonNodeImpl.alpha = 1.0
if case .glass = self.presentationData.theme.style {
transition.updateFrame(node: self.leftButtonNodeImpl, frame: CGRect(origin: CGPoint(x: leftButtonsWidth, y: floor((44.0 - leftButtonSize.height) / 2.0)), size: leftButtonSize))
} else {
transition.updateFrame(node: self.leftButtonNodeImpl, frame: CGRect(origin: CGPoint(x: leftButtonInset, y: contentVerticalOrigin + floor((nominalHeight - leftButtonSize.height) / 2.0)), size: leftButtonSize))
}
if !self.leftButtonNodeImpl.isEmpty {
leftButtonsWidth += leftButtonSize.width
}
}
let badgeSize = self.badgeNode.measure(CGSize(width: 200.0, height: 100.0))
let backButtonArrowFrame = self.backButtonArrow.frame
if case .glass = self.presentationData.theme.style, self.badgeNode.view.superview != nil, !self.badgeNode.isHidden {
transition.updateFrame(node: self.badgeNode, frame: CGRect(origin: CGPoint(x: leftButtonsWidth - 14.0, y: floor((44.0 - badgeSize.height) * 0.5)), size: badgeSize))
leftButtonsWidth += badgeSize.width - 3.0
} else {
transition.updateFrame(node: self.badgeNode, frame: CGRect(origin: backButtonArrowFrame.origin.offsetBy(dx: 16.0, dy: 2.0), size: badgeSize))
}
var rightButtonsWidth: CGFloat = 0.0
if self.rightButtonNodeImpl.view.superview != nil {
let rightButtonSize = self.rightButtonNodeImpl.updateLayout(constrainedSize: (CGSize(width: size.width, height: 44.0)), isLandscape: isLandscape, isLeftAligned: false)
if !self.rightButtonNodeImpl.isEmpty {
rightButtonsWidth += rightButtonSize.width
}
self.rightButtonNodeImpl.alpha = 1.0
var transition = transition
if self.rightButtonNodeImpl.frame.width.isZero || self.rightButtonNodeUpdated {
transition = .immediate
}
if case .glass = self.presentationData.theme.style {
transition.updateFrame(node: self.rightButtonNodeImpl, frame: CGRect(origin: CGPoint(x: 0.0, y: floor((44.0 - rightButtonSize.height) / 2.0)), size: rightButtonSize))
} else {
rightTitleInset = rightButtonSize.width + leftButtonInset + 4.0
transition.updateFrame(node: self.rightButtonNodeImpl, frame: CGRect(origin: CGPoint(x: size.width - leftButtonInset - rightButtonSize.width, y: contentVerticalOrigin + floor((nominalHeight - rightButtonSize.height) / 2.0)), size: rightButtonSize))
}
}
self.rightButtonNodeUpdated = false
if let leftButtonsBackgroundView = self.leftButtonsBackgroundView {
if leftButtonsWidth != 0.0 {
leftTitleInset = leftInset + 16.0 + leftButtonsWidth + 10.0
}
if self.backButtonNodeImpl.view.superview != nil {
transition.updateFrame(node: self.backButtonNodeImpl, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: leftButtonsWidth, height: 44.0)))
}
let leftButtonsBackgroundFrame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: contentVerticalOrigin + floor((nominalHeight - 44.0) * 0.5)), size: CGSize(width: max(44.0, leftButtonsWidth), height: 44.0))
var leftButtonsBackgroundTransition = ComponentTransition(transition)
if leftButtonsBackgroundView.background.alpha == 0.0 {
leftButtonsBackgroundTransition = .immediate
}
leftButtonsBackgroundTransition.setPosition(view: leftButtonsBackgroundView.background, position: leftButtonsBackgroundFrame.center)
leftButtonsBackgroundTransition.setBounds(view: leftButtonsBackgroundView.background, bounds: CGRect(origin: CGPoint(), size: leftButtonsBackgroundFrame.size))
leftButtonsBackgroundTransition.setFrame(view: leftButtonsBackgroundView.container, frame: CGRect(origin: CGPoint(), size: leftButtonsBackgroundFrame.size))
ComponentTransition(transition).setAlpha(view: leftButtonsBackgroundView.background, alpha: leftButtonsWidth == 0.0 ? 0.0 : 1.0)
leftButtonsBackgroundView.background.update(size: leftButtonsBackgroundFrame.size, cornerRadius: leftButtonsBackgroundFrame.height * 0.5, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, isVisible: leftButtonsWidth != 0.0, transition: leftButtonsBackgroundTransition)
}
if let rightButtonsBackgroundView = self.rightButtonsBackgroundView {
if rightButtonsWidth != 0.0 {
rightTitleInset = rightInset + 16.0 + rightButtonsWidth + 10.0
let rightButtonsBackgroundFrame = CGRect(origin: CGPoint(x: size.width - rightInset - 16.0 - rightButtonsWidth, y: contentVerticalOrigin + floor((nominalHeight - 44.0) * 0.5)), size: CGSize(width: rightButtonsWidth, height: 44.0))
var rightButtonsBackgroundTransition = ComponentTransition(transition)
if rightButtonsBackgroundView.background.isHidden {
rightButtonsBackgroundTransition = .immediate
}
rightButtonsBackgroundView.container.layer.cornerRadius = 44.0 * 0.5
rightButtonsBackgroundTransition.setFrame(view: rightButtonsBackgroundView.background, frame: rightButtonsBackgroundFrame)
if rightButtonsBackgroundView.container.bounds.size != rightButtonsBackgroundFrame.size {
rightButtonsBackgroundView.container.clipsToBounds = true
let rightButtonsBackgroundViewContainer = rightButtonsBackgroundView.container
rightButtonsBackgroundTransition.setFrame(view: rightButtonsBackgroundView.container, frame: CGRect(origin: CGPoint(), size: rightButtonsBackgroundFrame.size), completion: { [weak rightButtonsBackgroundViewContainer] flag in
if flag, let rightButtonsBackgroundViewContainer {
rightButtonsBackgroundViewContainer.clipsToBounds = false
}
})
}
rightButtonsBackgroundView.background.isHidden = false
rightButtonsBackgroundView.background.update(size: rightButtonsBackgroundFrame.size, cornerRadius: rightButtonsBackgroundFrame.height * 0.5, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: rightButtonsBackgroundTransition)
} else {
rightButtonsBackgroundView.background.isHidden = true
}
}
if self.titleNode.view.superview != nil {
let titleSize = self.titleNode.updateLayout(CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight))
do {
var transition = transition
if self.titleNode.frame.width.isZero {
transition = .immediate
}
self.titleNode.alpha = 1.0
let titleOffset: CGFloat = 0.0
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + titleOffset + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize))
}
}
if let titleView = self.titleView {
if let titleView = titleView as? NavigationBarTitleView {
var titleViewTransition = transition
if titleView.frame.isEmpty {
titleViewTransition = .immediate
}
let titleSize = titleView.updateLayout(availableSize: CGSize(width: size.width - leftTitleInset - rightTitleInset, height: nominalHeight), transition: titleViewTransition)
var titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) * 0.5), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize)
if titleFrame.origin.x < leftTitleInset {
titleFrame.origin.x = leftTitleInset + floorToScreenPixels((size.width - leftTitleInset - rightTitleInset - titleFrame.width) * 0.5)
}
titleViewTransition.updateFrame(view: titleView, frame: titleFrame)
} else {
let titleSize = CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight)
let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize)
var titleViewTransition = transition
if titleView.frame.isEmpty {
titleViewTransition = .immediate
titleView.frame = titleFrame
}
titleViewTransition.updateFrame(view: titleView, frame: titleFrame)
}
}
}
public func updateEdgeEffectExtension(value: CGFloat, transition: ContainedViewLayoutTransition) {
if self.edgeEffectExtension == value {
return
}
self.edgeEffectExtension = value
self.applyEdgeEffectExtension(transition: transition)
}
private func applyEdgeEffectExtension(transition: ContainedViewLayoutTransition) {
if let edgeEffectView = self.edgeEffectView {
transition.updateTransform(layer: edgeEffectView.layer, transform: CATransform3DMakeTranslation(0.0, max(0.0, min(20.0, self.edgeEffectExtension)), 0.0))
}
}
public var intrinsicCanTransitionInline: Bool = true
public var passthroughTouches = true
public var canTransitionInline: Bool {
if let contentNode = self.contentNode, case .replacement = contentNode.mode {
return false
} else {
return self.intrinsicCanTransitionInline
}
}
public func contentHeight(defaultHeight: CGFloat) -> CGFloat {
var result: CGFloat = 0.0
if let contentNode = self.contentNode {
switch contentNode.mode {
case .expansion:
result += defaultHeight + contentNode.height
case .replacement:
result += contentNode.height
}
} else {
result += defaultHeight
}
if let _ = self.secondaryContentNode {
result += self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction
}
return result
}
public func setContentNode(_ contentNode: NavigationBarContentNode?, animated: Bool) {
let transition: ComponentTransition = animated ? .easeInOut(duration: 0.2) : .immediate
if self.contentNode !== contentNode {
if let previous = self.contentNode {
if animated {
previous.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self, weak previous] _ in
if let strongSelf = self, let previous = previous {
if previous !== strongSelf.contentNode {
previous.removeFromSupernode()
}
}
})
} else {
previous.removeFromSupernode()
}
}
self.contentNode = contentNode
self.contentNode?.requestContainerLayout = { [weak self] transition in
guard let self else {
return
}
self.requestContainerLayout?(transition)
}
if let contentNode {
contentNode.layer.removeAnimation(forKey: "opacity")
if case .glass = self.presentationData.theme.style {
self.addSubnode(contentNode)
} else {
contentNode.clipsToBounds = true
if self.stripeNode.supernode != nil {
self.insertSubnode(contentNode, belowSubnode: self.stripeNode)
} else {
self.insertSubnode(contentNode, at: 0)
}
}
if animated {
contentNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
if case .replacement = contentNode.mode {
if !self.buttonsContainerNode.alpha.isZero {
self.buttonsContainerNode.alpha = 0.0
if animated {
self.buttonsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
}
}
if let backgroundContainer = self.backgroundContainer, !backgroundContainer.alpha.isZero {
transition.setAlpha(view: backgroundContainer, alpha: 0.0)
}
}
if !self.bounds.size.width.isZero {
self.requestedLayout = true
self.layout()
} else {
self.requestLayout()
}
} else {
if self.buttonsContainerNode.alpha.isZero {
self.buttonsContainerNode.alpha = 1.0
if animated {
self.buttonsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
if let backgroundContainer = self.backgroundContainer, backgroundContainer.alpha.isZero {
transition.setAlpha(view: backgroundContainer, alpha: 1.0)
}
}
}
}
public func setSecondaryContentNode(_ secondaryContentNode: ASDisplayNode?, animated: Bool = false) {
if self.secondaryContentNode !== secondaryContentNode {
if let previous = self.secondaryContentNode, previous.supernode === self.clippingNode {
if animated {
previous.layer.animateAlpha(from: previous.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previous] finished in
if finished {
previous?.removeFromSupernode()
previous?.layer.removeAllAnimations()
}
})
} else {
previous.removeFromSupernode()
}
}
self.secondaryContentNode = secondaryContentNode
if let secondaryContentNode = secondaryContentNode {
self.clippingNode.addSubnode(secondaryContentNode)
if animated {
secondaryContentNode.layer.animateAlpha(from: 0.0, to: secondaryContentNode.alpha, duration: 0.3)
}
}
}
}
public func executeBack() -> Bool {
if self.backButtonNodeImpl.isInHierarchy {
self.backButtonNodeImpl.pressed(0)
} else if self.leftButtonNodeImpl.isInHierarchy {
self.leftButtonNodeImpl.pressed(0)
} else {
self.backButtonNodeImpl.pressed(0)
}
return true
}
public func setHidden(_ hidden: Bool, animated: Bool) {
if let contentNode = self.contentNode, case .replacement = contentNode.mode {
} else {
let targetAlpha: CGFloat = hidden ? 0.0 : 1.0
let previousAlpha = self.buttonsContainerNode.alpha
if previousAlpha != targetAlpha {
self.buttonsContainerNode.alpha = targetAlpha
if animated {
self.buttonsContainerNode.layer.animateAlpha(from: previousAlpha, to: targetAlpha, duration: 0.2)
}
}
}
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let result = self.additionalContentNode.view.hitTest(self.view.convert(point, to: self.additionalContentNode.view), with: event) {
return result
}
guard let result = super.hitTest(point, with: event) else {
if !self.bounds.contains(point) {
if let result = self.customOverBackgroundContentView.hitTest(self.view.convert(point, to: self.customOverBackgroundContentView), with: event) {
if result !== self.backgroundContainer?.contentView {
return result
}
}
}
return nil
}
if self.passthroughTouches && (result == self.view || result == self.buttonsContainerNode.view || result == self.backgroundNode.view || result == self.backgroundNode.backgroundView || result == self.backgroundContainer?.contentView) {
return nil
}
return result
}
}