mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Support updated API
This commit is contained in:
454
submodules/ChatListUI/Sources/ChatListContainerItemNode.swift
Normal file
454
submodules/ChatListUI/Sources/ChatListContainerItemNode.swift
Normal file
@@ -0,0 +1,454 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import AccountContext
|
||||
import TelegramPresentationData
|
||||
import SwiftSignalKit
|
||||
import AnimationCache
|
||||
import MultiAnimationRenderer
|
||||
import TelegramCore
|
||||
import Postbox
|
||||
import ChatListHeaderComponent
|
||||
import ActionPanelComponent
|
||||
import ChatFolderLinkPreviewScreen
|
||||
|
||||
final class ChatListContainerItemNode: ASDisplayNode {
|
||||
private final class TopPanelItem {
|
||||
let view = ComponentView<Empty>()
|
||||
var size: CGSize?
|
||||
|
||||
init() {
|
||||
}
|
||||
}
|
||||
|
||||
private let context: AccountContext
|
||||
private weak var controller: ChatListControllerImpl?
|
||||
private let location: ChatListControllerLocation
|
||||
private let animationCache: AnimationCache
|
||||
private let animationRenderer: MultiAnimationRenderer
|
||||
private var presentationData: PresentationData
|
||||
private let becameEmpty: (ChatListFilter?) -> Void
|
||||
private let emptyAction: (ChatListFilter?) -> Void
|
||||
private let secondaryEmptyAction: () -> Void
|
||||
private let openArchiveSettings: () -> Void
|
||||
private let isInlineMode: Bool
|
||||
|
||||
private var floatingHeaderOffset: CGFloat?
|
||||
|
||||
private(set) var emptyNode: ChatListEmptyNode?
|
||||
var emptyShimmerEffectNode: ChatListShimmerNode?
|
||||
private var shimmerNodeOffset: CGFloat = 0.0
|
||||
let listNode: ChatListNode
|
||||
|
||||
private var topPanel: TopPanelItem?
|
||||
|
||||
private var pollFilterUpdatesDisposable: Disposable?
|
||||
private var chatFilterUpdatesDisposable: Disposable?
|
||||
private var peerDataDisposable: Disposable?
|
||||
|
||||
private var chatFolderUpdates: ChatFolderUpdates?
|
||||
|
||||
private var canReportPeer: Bool = false
|
||||
|
||||
private(set) var validLayout: (size: CGSize, insets: UIEdgeInsets, visualNavigationHeight: CGFloat, originalNavigationHeight: CGFloat, inlineNavigationLocation: ChatListControllerLocation?, inlineNavigationTransitionFraction: CGFloat, storiesInset: CGFloat)?
|
||||
private var scrollingOffset: (navigationHeight: CGFloat, offset: CGFloat)?
|
||||
|
||||
init(context: AccountContext, controller: ChatListControllerImpl?, location: ChatListControllerLocation, filter: ChatListFilter?, chatListMode: ChatListNodeMode, previewing: Bool, isInlineMode: Bool, controlsHistoryPreload: Bool, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, becameEmpty: @escaping (ChatListFilter?) -> Void, emptyAction: @escaping (ChatListFilter?) -> Void, secondaryEmptyAction: @escaping () -> Void, openArchiveSettings: @escaping () -> Void, autoSetReady: Bool) {
|
||||
self.context = context
|
||||
self.controller = controller
|
||||
self.location = location
|
||||
self.animationCache = animationCache
|
||||
self.animationRenderer = animationRenderer
|
||||
self.presentationData = presentationData
|
||||
self.becameEmpty = becameEmpty
|
||||
self.emptyAction = emptyAction
|
||||
self.secondaryEmptyAction = secondaryEmptyAction
|
||||
self.openArchiveSettings = openArchiveSettings
|
||||
self.isInlineMode = isInlineMode
|
||||
|
||||
self.listNode = ChatListNode(context: context, location: location, chatListFilter: filter, previewing: previewing, fillPreloadItems: controlsHistoryPreload, mode: chatListMode, theme: presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: animationCache, animationRenderer: animationRenderer, disableAnimations: true, isInlineMode: isInlineMode, autoSetReady: autoSetReady)
|
||||
|
||||
if let controller, case .chatList(groupId: .root) = controller.location {
|
||||
self.listNode.scrollHeightTopInset = ChatListNavigationBar.searchScrollHeight + ChatListNavigationBar.storiesScrollHeight
|
||||
}
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.listNode)
|
||||
|
||||
self.listNode.isEmptyUpdated = { [weak self] isEmptyState, _, transition in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
var needsShimmerNode = false
|
||||
var shimmerNodeOffset: CGFloat = 0.0
|
||||
|
||||
var needsEmptyNode = false
|
||||
var hasOnlyArchive = false
|
||||
var hasOnlyGeneralThread = false
|
||||
var isLoading = false
|
||||
|
||||
switch isEmptyState {
|
||||
case let .empty(isLoadingValue, hasArchiveInfo):
|
||||
if hasArchiveInfo {
|
||||
shimmerNodeOffset = 253.0
|
||||
}
|
||||
if isLoadingValue {
|
||||
needsShimmerNode = true
|
||||
needsEmptyNode = false
|
||||
isLoading = isLoadingValue
|
||||
} else {
|
||||
needsEmptyNode = true
|
||||
}
|
||||
if !isLoadingValue {
|
||||
strongSelf.becameEmpty(filter)
|
||||
}
|
||||
case let .notEmpty(_, onlyHasArchiveValue, onlyGeneralThreadValue):
|
||||
needsEmptyNode = onlyHasArchiveValue || onlyGeneralThreadValue
|
||||
hasOnlyArchive = onlyHasArchiveValue
|
||||
hasOnlyGeneralThread = onlyGeneralThreadValue
|
||||
}
|
||||
|
||||
if needsEmptyNode {
|
||||
if let currentNode = strongSelf.emptyNode {
|
||||
currentNode.updateIsLoading(isLoading)
|
||||
} else {
|
||||
let subject: ChatListEmptyNode.Subject
|
||||
if let filter = filter {
|
||||
var showEdit = true
|
||||
if case let .filter(_, _, _, data) = filter {
|
||||
if data.excludeRead && data.includePeers.peers.isEmpty && data.includePeers.pinnedPeers.isEmpty {
|
||||
showEdit = false
|
||||
}
|
||||
}
|
||||
subject = .filter(showEdit: showEdit)
|
||||
} else {
|
||||
if case .forum = location {
|
||||
subject = .forum(hasGeneral: hasOnlyGeneralThread)
|
||||
} else {
|
||||
if case .chatList(groupId: .archive) = location {
|
||||
subject = .archive
|
||||
} else {
|
||||
subject = .chats(hasArchive: hasOnlyArchive)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let emptyNode = ChatListEmptyNode(context: context, subject: subject, isLoading: isLoading, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, action: {
|
||||
self?.emptyAction(filter)
|
||||
}, secondaryAction: {
|
||||
self?.secondaryEmptyAction()
|
||||
}, openArchiveSettings: {
|
||||
self?.openArchiveSettings()
|
||||
})
|
||||
strongSelf.emptyNode = emptyNode
|
||||
strongSelf.listNode.addSubnode(emptyNode)
|
||||
if let (size, insets, _, _, _, _, _) = strongSelf.validLayout {
|
||||
let emptyNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))
|
||||
emptyNode.frame = emptyNodeFrame
|
||||
emptyNode.updateLayout(size: size, insets: insets, transition: .immediate)
|
||||
|
||||
if let scrollingOffset = strongSelf.scrollingOffset {
|
||||
emptyNode.updateScrollingOffset(navigationHeight: scrollingOffset.navigationHeight, offset: scrollingOffset.offset, transition: .immediate)
|
||||
}
|
||||
}
|
||||
emptyNode.alpha = 0.0
|
||||
transition.updateAlpha(node: emptyNode, alpha: 1.0)
|
||||
}
|
||||
} else if let emptyNode = strongSelf.emptyNode {
|
||||
strongSelf.emptyNode = nil
|
||||
transition.updateAlpha(node: emptyNode, alpha: 0.0, completion: { [weak emptyNode] _ in
|
||||
emptyNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
if needsShimmerNode {
|
||||
strongSelf.shimmerNodeOffset = shimmerNodeOffset
|
||||
if strongSelf.emptyShimmerEffectNode == nil {
|
||||
let emptyShimmerEffectNode = ChatListShimmerNode()
|
||||
strongSelf.emptyShimmerEffectNode = emptyShimmerEffectNode
|
||||
strongSelf.insertSubnode(emptyShimmerEffectNode, belowSubnode: strongSelf.listNode)
|
||||
if let (size, insets, _, _, _, _, _) = strongSelf.validLayout, let offset = strongSelf.floatingHeaderOffset {
|
||||
strongSelf.layoutEmptyShimmerEffectNode(node: emptyShimmerEffectNode, size: size, insets: insets, verticalOffset: offset + strongSelf.shimmerNodeOffset, transition: .immediate)
|
||||
}
|
||||
}
|
||||
} else if let emptyShimmerEffectNode = strongSelf.emptyShimmerEffectNode {
|
||||
strongSelf.emptyShimmerEffectNode = nil
|
||||
let emptyNodeTransition = transition.isAnimated ? transition : .animated(duration: 0.3, curve: .easeInOut)
|
||||
emptyNodeTransition.updateAlpha(node: emptyShimmerEffectNode, alpha: 0.0, completion: { [weak emptyShimmerEffectNode] _ in
|
||||
emptyShimmerEffectNode?.removeFromSupernode()
|
||||
})
|
||||
strongSelf.listNode.alpha = 0.0
|
||||
emptyNodeTransition.updateAlpha(node: strongSelf.listNode, alpha: 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
self.listNode.updateFloatingHeaderOffset = { [weak self] offset, transition in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.floatingHeaderOffset = offset
|
||||
if let (size, insets, _, _, _, _, _) = strongSelf.validLayout, let emptyShimmerEffectNode = strongSelf.emptyShimmerEffectNode {
|
||||
strongSelf.layoutEmptyShimmerEffectNode(node: emptyShimmerEffectNode, size: size, insets: insets, verticalOffset: offset + strongSelf.shimmerNodeOffset, transition: transition)
|
||||
}
|
||||
strongSelf.layoutAdditionalPanels(transition: transition)
|
||||
}
|
||||
|
||||
if let filter, case let .filter(id, _, _, data) = filter, data.isShared {
|
||||
self.pollFilterUpdatesDisposable = self.context.engine.peers.pollChatFolderUpdates(folderId: id).start()
|
||||
self.chatFilterUpdatesDisposable = (self.context.engine.peers.subscribedChatFolderUpdates(folderId: id)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
var update = false
|
||||
if let result, result.availableChatsToJoin != 0 {
|
||||
if self.chatFolderUpdates?.availableChatsToJoin != result.availableChatsToJoin {
|
||||
update = true
|
||||
}
|
||||
self.chatFolderUpdates = result
|
||||
} else {
|
||||
if self.chatFolderUpdates != nil {
|
||||
self.chatFolderUpdates = nil
|
||||
update = true
|
||||
}
|
||||
}
|
||||
if update {
|
||||
if let (size, insets, visualNavigationHeight, originalNavigationHeight, inlineNavigationLocation, inlineNavigationTransitionFraction, storiesInset) = self.validLayout {
|
||||
self.updateLayout(size: size, insets: insets, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: inlineNavigationTransitionFraction, storiesInset: storiesInset, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if case let .forum(peerId) = location {
|
||||
self.peerDataDisposable = (context.engine.data.subscribe(
|
||||
TelegramEngine.EngineData.Item.Peer.StatusSettings(id: peerId)
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] statusSettings in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
var canReportPeer = false
|
||||
if let statusSettings, statusSettings.flags.contains(.canReport) {
|
||||
canReportPeer = true
|
||||
}
|
||||
if self.canReportPeer != canReportPeer {
|
||||
self.canReportPeer = canReportPeer
|
||||
if let (size, insets, visualNavigationHeight, originalNavigationHeight, inlineNavigationLocation, inlineNavigationTransitionFraction, storiesInset) = self.validLayout {
|
||||
self.updateLayout(size: size, insets: insets, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: inlineNavigationTransitionFraction, storiesInset: storiesInset, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.pollFilterUpdatesDisposable?.dispose()
|
||||
self.chatFilterUpdatesDisposable?.dispose()
|
||||
self.peerDataDisposable?.dispose()
|
||||
}
|
||||
|
||||
private func layoutEmptyShimmerEffectNode(node: ChatListShimmerNode, size: CGSize, insets: UIEdgeInsets, verticalOffset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
node.update(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, size: size, isInlineMode: self.isInlineMode, presentationData: self.presentationData, transition: .immediate)
|
||||
transition.updateFrameAdditive(node: node, frame: CGRect(origin: CGPoint(x: 0.0, y: verticalOffset), size: size))
|
||||
}
|
||||
|
||||
private func layoutAdditionalPanels(transition: ContainedViewLayoutTransition) {
|
||||
guard let (size, insets, visualNavigationHeight, _, _, _, _) = self.validLayout, let offset = self.floatingHeaderOffset else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = size
|
||||
let _ = insets
|
||||
|
||||
if let topPanel = self.topPanel, let topPanelSize = topPanel.size {
|
||||
let minY: CGFloat = visualNavigationHeight - 44.0 + topPanelSize.height
|
||||
|
||||
if let topPanelView = topPanel.view.view {
|
||||
var animateIn = false
|
||||
var topPanelTransition = transition
|
||||
if topPanelView.bounds.isEmpty {
|
||||
topPanelTransition = .immediate
|
||||
animateIn = true
|
||||
}
|
||||
topPanelTransition.updateFrame(view: topPanelView, frame: CGRect(origin: CGPoint(x: 0.0, y: max(minY, offset - topPanelSize.height)), size: topPanelSize))
|
||||
if animateIn {
|
||||
transition.animatePositionAdditive(layer: topPanelView.layer, offset: CGPoint(x: 0.0, y: -topPanelView.bounds.height))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updatePresentationData(_ presentationData: PresentationData) {
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.listNode.accessibilityPageScrolledString = { row, count in
|
||||
return presentationData.strings.VoiceOver_ScrollStatus(row, count).string
|
||||
}
|
||||
|
||||
self.listNode.updateThemeAndStrings(theme: presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true)
|
||||
|
||||
self.emptyNode?.updateThemeAndStrings(theme: presentationData.theme, strings: presentationData.strings)
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, visualNavigationHeight: CGFloat, originalNavigationHeight: CGFloat, inlineNavigationLocation: ChatListControllerLocation?, inlineNavigationTransitionFraction: CGFloat, storiesInset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, insets, visualNavigationHeight, originalNavigationHeight, inlineNavigationLocation, inlineNavigationTransitionFraction, storiesInset)
|
||||
|
||||
var listInsets = insets
|
||||
var additionalTopInset: CGFloat = 0.0
|
||||
|
||||
if let chatFolderUpdates = self.chatFolderUpdates {
|
||||
let topPanel: TopPanelItem
|
||||
var topPanelTransition = Transition(transition)
|
||||
if let current = self.topPanel {
|
||||
topPanel = current
|
||||
} else {
|
||||
topPanelTransition = .immediate
|
||||
topPanel = TopPanelItem()
|
||||
self.topPanel = topPanel
|
||||
}
|
||||
|
||||
let title: String = self.presentationData.strings.ChatList_PanelNewChatsAvailable(Int32(chatFolderUpdates.availableChatsToJoin))
|
||||
|
||||
let topPanelHeight: CGFloat = 44.0
|
||||
|
||||
let _ = topPanel.view.update(
|
||||
transition: topPanelTransition,
|
||||
component: AnyComponent(ActionPanelComponent(
|
||||
theme: self.presentationData.theme,
|
||||
title: title,
|
||||
color: .accent,
|
||||
action: { [weak self] in
|
||||
guard let self, let chatFolderUpdates = self.chatFolderUpdates else {
|
||||
return
|
||||
}
|
||||
|
||||
self.listNode.push?(ChatFolderLinkPreviewScreen(context: self.context, subject: .updates(chatFolderUpdates), contents: chatFolderUpdates.chatFolderLinkContents))
|
||||
},
|
||||
dismissAction: { [weak self] in
|
||||
guard let self, let chatFolderUpdates = self.chatFolderUpdates else {
|
||||
return
|
||||
}
|
||||
let _ = self.context.engine.peers.hideChatFolderUpdates(folderId: chatFolderUpdates.folderId).start()
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: size.width, height: topPanelHeight)
|
||||
)
|
||||
if let topPanelView = topPanel.view.view {
|
||||
if topPanelView.superview == nil {
|
||||
self.view.addSubview(topPanelView)
|
||||
}
|
||||
}
|
||||
|
||||
topPanel.size = CGSize(width: size.width, height: topPanelHeight)
|
||||
listInsets.top += topPanelHeight
|
||||
additionalTopInset += topPanelHeight
|
||||
} else if self.canReportPeer {
|
||||
let topPanel: TopPanelItem
|
||||
var topPanelTransition = Transition(transition)
|
||||
if let current = self.topPanel {
|
||||
topPanel = current
|
||||
} else {
|
||||
topPanelTransition = .immediate
|
||||
topPanel = TopPanelItem()
|
||||
self.topPanel = topPanel
|
||||
}
|
||||
|
||||
let title: String = self.presentationData.strings.Conversation_ReportSpamAndLeave
|
||||
|
||||
let topPanelHeight: CGFloat = 44.0
|
||||
|
||||
let _ = topPanel.view.update(
|
||||
transition: topPanelTransition,
|
||||
component: AnyComponent(ActionPanelComponent(
|
||||
theme: self.presentationData.theme,
|
||||
title: title,
|
||||
color: .destructive,
|
||||
action: { [weak self] in
|
||||
guard let self, case let .forum(peerId) = self.location else {
|
||||
return
|
||||
}
|
||||
|
||||
let actionSheet = ActionSheetController(presentationData: self.presentationData)
|
||||
actionSheet.setItemGroups([
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetTextItem(title: self.presentationData.strings.Conversation_ReportSpamGroupConfirmation),
|
||||
ActionSheetButtonItem(title: self.presentationData.strings.Conversation_ReportSpamAndLeave, color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
if let self {
|
||||
self.controller?.setInlineChatList(location: nil)
|
||||
let _ = self.context.engine.peers.removePeerChat(peerId: peerId, reportChatSpam: true).start()
|
||||
}
|
||||
})
|
||||
]),
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])
|
||||
])
|
||||
self.listNode.present?(actionSheet)
|
||||
},
|
||||
dismissAction: { [weak self] in
|
||||
guard let self, case let .forum(peerId) = self.location else {
|
||||
return
|
||||
}
|
||||
let _ = self.context.engine.peers.dismissPeerStatusOptions(peerId: peerId).start()
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: size.width, height: topPanelHeight)
|
||||
)
|
||||
if let topPanelView = topPanel.view.view {
|
||||
if topPanelView.superview == nil {
|
||||
self.view.addSubview(topPanelView)
|
||||
}
|
||||
}
|
||||
|
||||
topPanel.size = CGSize(width: size.width, height: topPanelHeight)
|
||||
listInsets.top += topPanelHeight
|
||||
additionalTopInset += topPanelHeight
|
||||
} else {
|
||||
if let topPanel = self.topPanel {
|
||||
self.topPanel = nil
|
||||
if let topPanelView = topPanel.view.view {
|
||||
transition.updatePosition(layer: topPanelView.layer, position: CGPoint(x: topPanelView.layer.position.x, y: topPanelView.layer.position.y - topPanelView.layer.bounds.height), completion: { [weak topPanelView] _ in
|
||||
topPanelView?.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
|
||||
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: size, insets: listInsets, duration: duration, curve: curve)
|
||||
|
||||
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.listNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, visibleTopInset: visualNavigationHeight + additionalTopInset, originalTopInset: originalNavigationHeight + additionalTopInset, storiesInset: storiesInset, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: inlineNavigationTransitionFraction)
|
||||
|
||||
if let emptyNode = self.emptyNode {
|
||||
let emptyNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))
|
||||
transition.updateFrame(node: emptyNode, frame: emptyNodeFrame)
|
||||
emptyNode.updateLayout(size: emptyNodeFrame.size, insets: listInsets, transition: transition)
|
||||
|
||||
if let scrollingOffset = self.scrollingOffset {
|
||||
emptyNode.updateScrollingOffset(navigationHeight: scrollingOffset.navigationHeight, offset: scrollingOffset.offset, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
self.layoutAdditionalPanels(transition: transition)
|
||||
}
|
||||
|
||||
func updateScrollingOffset(navigationHeight: CGFloat, offset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.scrollingOffset = (navigationHeight, offset)
|
||||
|
||||
if let emptyNode = self.emptyNode {
|
||||
emptyNode.updateScrollingOffset(navigationHeight: navigationHeight, offset: offset, transition: transition)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user