mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Glass
This commit is contained in:
parent
202274073f
commit
d3a3ea008d
8 changed files with 218 additions and 87 deletions
|
|
@ -750,7 +750,7 @@ public final class CallListController: TelegramBaseController {
|
|||
})
|
||||
}
|
||||
|
||||
override public func tabBarItemContextAction(sourceNode: ContextExtractedContentContainingNode, gesture: ContextGesture) {
|
||||
override public func tabBarItemContextAction(sourceView: ContextExtractedContentContainingView, gesture: ContextGesture) {
|
||||
var items: [ContextMenuItem] = []
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Calls_StartNewCall, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor)
|
||||
|
|
@ -775,30 +775,27 @@ public final class CallListController: TelegramBaseController {
|
|||
})
|
||||
})))
|
||||
|
||||
let controller = ContextController(presentationData: self.presentationData, source: .extracted(CallListTabBarContextExtractedContentSource(controller: self, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
|
||||
let controller = ContextController(presentationData: self.presentationData, source: .reference(CallListTabBarContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
|
||||
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
|
||||
}
|
||||
}
|
||||
|
||||
private final class CallListTabBarContextExtractedContentSource: ContextExtractedContentSource {
|
||||
private final class CallListTabBarContextReferenceContentSource: ContextReferenceContentSource {
|
||||
let keepInPlace: Bool = true
|
||||
let ignoreContentTouches: Bool = true
|
||||
let blurBackground: Bool = true
|
||||
let actionsHorizontalAlignment: ContextActionsHorizontalAlignment = .center
|
||||
|
||||
private let controller: ViewController
|
||||
private let sourceNode: ContextExtractedContentContainingNode
|
||||
private let sourceView: ContextExtractedContentContainingView
|
||||
|
||||
init(controller: ViewController, sourceNode: ContextExtractedContentContainingNode) {
|
||||
init(controller: ViewController, sourceView: ContextExtractedContentContainingView) {
|
||||
self.controller = controller
|
||||
self.sourceNode = sourceNode
|
||||
self.sourceView = sourceView
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
func transitionInfo() -> ContextControllerReferenceViewInfo? {
|
||||
return ContextControllerReferenceViewInfo(
|
||||
referenceView: self.sourceView.contentView,
|
||||
contentAreaInScreenSpace: UIScreen.main.bounds,
|
||||
actionsPosition: .top
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6061,7 +6061,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||
self.donePressed()
|
||||
}
|
||||
|
||||
override public func tabBarItemContextAction(sourceNode: ContextExtractedContentContainingNode, gesture: ContextGesture) {
|
||||
override public func tabBarItemContextAction(sourceView: ContextExtractedContentContainingView, gesture: ContextGesture) {
|
||||
let _ = (combineLatest(queue: .mainQueue(),
|
||||
self.context.engine.peers.currentChatListFilters(),
|
||||
chatListFilterItems(context: self.context)
|
||||
|
|
@ -6178,7 +6178,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||
}
|
||||
}
|
||||
|
||||
let controller = ContextController(context: strongSelf.context, presentationData: strongSelf.presentationData, source: .extracted(ChatListTabBarContextExtractedContentSource(controller: strongSelf, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
|
||||
let controller = ContextController(context: strongSelf.context, presentationData: strongSelf.presentationData, source: .reference(ChatListTabBarContextReferenceContentSource(controller: strongSelf, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
|
||||
strongSelf.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
|
||||
})
|
||||
}
|
||||
|
|
@ -6435,26 +6435,24 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||
}
|
||||
}
|
||||
|
||||
private final class ChatListTabBarContextExtractedContentSource: ContextExtractedContentSource {
|
||||
private final class ChatListTabBarContextReferenceContentSource: ContextReferenceContentSource {
|
||||
let keepInPlace: Bool = true
|
||||
let ignoreContentTouches: Bool = true
|
||||
let blurBackground: Bool = true
|
||||
let actionsHorizontalAlignment: ContextActionsHorizontalAlignment = .center
|
||||
|
||||
private let controller: ChatListController
|
||||
private let sourceNode: ContextExtractedContentContainingNode
|
||||
private let sourceView: ContextExtractedContentContainingView
|
||||
|
||||
init(controller: ChatListController, sourceNode: ContextExtractedContentContainingNode) {
|
||||
init(controller: ChatListController, sourceView: ContextExtractedContentContainingView) {
|
||||
self.controller = controller
|
||||
self.sourceNode = sourceNode
|
||||
self.sourceView = sourceView
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
func transitionInfo() -> ContextControllerReferenceViewInfo? {
|
||||
return ContextControllerReferenceViewInfo(
|
||||
referenceView: self.sourceView.contentView,
|
||||
contentAreaInScreenSpace: UIScreen.main.bounds,
|
||||
actionsPosition: .top
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -774,7 +774,7 @@ public class ContactsController: ViewController {
|
|||
})
|
||||
}
|
||||
|
||||
override public func tabBarItemContextAction(sourceNode: ContextExtractedContentContainingNode, gesture: ContextGesture) {
|
||||
override public func tabBarItemContextAction(sourceView: ContextExtractedContentContainingView, gesture: ContextGesture) {
|
||||
var items: [ContextMenuItem] = []
|
||||
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Contacts_AddContact, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor)
|
||||
|
|
@ -787,31 +787,28 @@ public class ContactsController: ViewController {
|
|||
})
|
||||
})))
|
||||
|
||||
let controller = ContextController(presentationData: self.presentationData, source: .extracted(ContactsTabBarContextExtractedContentSource(controller: self, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
|
||||
let controller = ContextController(presentationData: self.presentationData, source: .reference(ContactsTabBarContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
|
||||
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
|
||||
}
|
||||
}
|
||||
|
||||
private final class ContactsTabBarContextExtractedContentSource: ContextExtractedContentSource {
|
||||
private final class ContactsTabBarContextReferenceContentSource: ContextReferenceContentSource {
|
||||
let keepInPlace: Bool = true
|
||||
let ignoreContentTouches: Bool = true
|
||||
let blurBackground: Bool = true
|
||||
let actionsHorizontalAlignment: ContextActionsHorizontalAlignment = .center
|
||||
|
||||
private let controller: ViewController
|
||||
private let sourceNode: ContextExtractedContentContainingNode
|
||||
private let sourceView: ContextExtractedContentContainingView
|
||||
|
||||
init(controller: ViewController, sourceNode: ContextExtractedContentContainingNode) {
|
||||
init(controller: ViewController, sourceView: ContextExtractedContentContainingView) {
|
||||
self.controller = controller
|
||||
self.sourceNode = sourceNode
|
||||
self.sourceView = sourceView
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
func transitionInfo() -> ContextControllerReferenceViewInfo? {
|
||||
return ContextControllerReferenceViewInfo(
|
||||
referenceView: self.sourceView.contentView,
|
||||
contentAreaInScreenSpace: UIScreen.main.bounds,
|
||||
actionsPosition: .top
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -697,7 +697,7 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject {
|
|||
|
||||
open var tabBarItemContextActionType: TabBarItemContextActionType = .none
|
||||
|
||||
open func tabBarItemContextAction(sourceNode: ContextExtractedContentContainingNode, gesture: ContextGesture) {
|
||||
open func tabBarItemContextAction(sourceView: ContextExtractedContentContainingView, gesture: ContextGesture) {
|
||||
}
|
||||
|
||||
open func tabBarDisabledAction() {
|
||||
|
|
|
|||
|
|
@ -49,9 +49,8 @@ final class TabBarControllerNode: ASDisplayNode {
|
|||
|
||||
private var theme: PresentationTheme
|
||||
private let itemSelected: (Int, Bool, [ASDisplayNode]) -> Void
|
||||
private let contextAction: (Int, ContextExtractedContentContainingNode, ContextGesture) -> Void
|
||||
private let contextAction: (Int, ContextExtractedContentContainingView, ContextGesture) -> Void
|
||||
|
||||
private let tabBarNode: TabBarNode
|
||||
private let tabBarView = ComponentView<Empty>()
|
||||
|
||||
private let disabledOverlayNode: ASDisplayNode
|
||||
|
|
@ -62,7 +61,7 @@ final class TabBarControllerNode: ASDisplayNode {
|
|||
private(set) var tabBarItems: [TabBarNodeItem] = []
|
||||
private(set) var selectedIndex: Int = 0
|
||||
|
||||
var currentControllerNode: ASDisplayNode?
|
||||
private(set) var currentControllerNode: ASDisplayNode?
|
||||
|
||||
private var layoutResult: LayoutResult?
|
||||
private var isUpdateRequested: Bool = false
|
||||
|
|
@ -81,6 +80,9 @@ final class TabBarControllerNode: ASDisplayNode {
|
|||
} else {
|
||||
self.insertSubnode(currentControllerNode, at: 0)
|
||||
}
|
||||
if let tabBarView = self.tabBarView.view {
|
||||
self.view.bringSubviewToFront(tabBarView)
|
||||
}
|
||||
}
|
||||
|
||||
return { [weak self, weak previousNode] in
|
||||
|
|
@ -90,11 +92,10 @@ final class TabBarControllerNode: ASDisplayNode {
|
|||
}
|
||||
}
|
||||
|
||||
init(theme: PresentationTheme, itemSelected: @escaping (Int, Bool, [ASDisplayNode]) -> Void, contextAction: @escaping (Int, ContextExtractedContentContainingNode, ContextGesture) -> Void, swipeAction: @escaping (Int, TabBarItemSwipeDirection) -> Void, toolbarActionSelected: @escaping (ToolbarActionOption) -> Void, disabledPressed: @escaping () -> Void) {
|
||||
init(theme: PresentationTheme, itemSelected: @escaping (Int, Bool, [ASDisplayNode]) -> Void, contextAction: @escaping (Int, ContextExtractedContentContainingView, ContextGesture) -> Void, swipeAction: @escaping (Int, TabBarItemSwipeDirection) -> Void, toolbarActionSelected: @escaping (ToolbarActionOption) -> Void, disabledPressed: @escaping () -> Void) {
|
||||
self.theme = theme
|
||||
self.itemSelected = itemSelected
|
||||
self.contextAction = contextAction
|
||||
self.tabBarNode = TabBarNode(theme: theme, itemSelected: itemSelected, contextAction: contextAction, swipeAction: swipeAction)
|
||||
self.disabledOverlayNode = ASDisplayNode()
|
||||
self.disabledOverlayNode.backgroundColor = theme.rootController.tabBar.backgroundColor.withAlphaComponent(0.5)
|
||||
self.disabledOverlayNode.alpha = 0.0
|
||||
|
|
@ -141,7 +142,6 @@ final class TabBarControllerNode: ASDisplayNode {
|
|||
self.theme = theme
|
||||
self.backgroundColor = theme.list.plainBackgroundColor
|
||||
|
||||
self.tabBarNode.updateTheme(theme)
|
||||
self.disabledOverlayNode.backgroundColor = theme.rootController.tabBar.backgroundColor.withAlphaComponent(0.5)
|
||||
self.toolbarNode?.updateTheme(ToolbarTheme(theme: theme))
|
||||
self.requestUpdate()
|
||||
|
|
@ -210,6 +210,14 @@ final class TabBarControllerNode: ASDisplayNode {
|
|||
if let index = self.tabBarItems.firstIndex(where: { AnyHashable(ObjectIdentifier($0.item)) == itemId }) {
|
||||
self.itemSelected(index, isLongTap, [])
|
||||
}
|
||||
},
|
||||
contextAction: { [weak self] gesture, sourceView in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let index = self.tabBarItems.firstIndex(where: { AnyHashable(ObjectIdentifier($0.item)) == itemId }) {
|
||||
self.contextAction(index, sourceView, gesture)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
|
|
@ -227,9 +235,6 @@ final class TabBarControllerNode: ASDisplayNode {
|
|||
transition.updateFrame(view: tabBarComponentView, frame: tabBarFrame)
|
||||
}
|
||||
|
||||
//transition.updateFrame(node: self.tabBarNode, frame: tabBarFrame)
|
||||
//self.tabBarNode.updateLayout(size: params.layout.size, leftInset: params.layout.safeInsets.left, rightInset: params.layout.safeInsets.right, additionalSideInsets: params.layout.additionalInsets, bottomInset: bottomInset, transition: transition)
|
||||
|
||||
transition.updateFrame(node: self.disabledOverlayNode, frame: tabBarFrame)
|
||||
|
||||
if let toolbar = params.toolbar {
|
||||
|
|
@ -263,11 +268,20 @@ final class TabBarControllerNode: ASDisplayNode {
|
|||
}
|
||||
|
||||
func frameForControllerTab(at index: Int) -> CGRect? {
|
||||
return self.tabBarNode.frameForControllerTab(at: index).flatMap { self.tabBarNode.view.convert($0, to: self.view) }
|
||||
guard let tabBarView = self.tabBarView.view as? TabBarComponent.View else {
|
||||
return nil
|
||||
}
|
||||
guard let itemFrame = tabBarView.frameForItem(at: index) else {
|
||||
return nil
|
||||
}
|
||||
return self.view.convert(itemFrame, from: tabBarView)
|
||||
}
|
||||
|
||||
func isPointInsideContentArea(point: CGPoint) -> Bool {
|
||||
if point.y < self.tabBarNode.frame.minY {
|
||||
guard let tabBarView = self.tabBarView.view else {
|
||||
return false
|
||||
}
|
||||
if point.y < tabBarView.frame.minY {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -275,13 +289,11 @@ final class TabBarControllerNode: ASDisplayNode {
|
|||
|
||||
func updateTabBarItems(items: [TabBarNodeItem]) {
|
||||
self.tabBarItems = items
|
||||
self.tabBarNode.tabBarItems = items
|
||||
self.requestUpdate()
|
||||
}
|
||||
|
||||
func updateSelectedIndex(index: Int) {
|
||||
self.selectedIndex = index
|
||||
self.tabBarNode.selectedIndex = index
|
||||
self.isChangingSelectedIndex = true
|
||||
self.requestUpdate()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,12 +217,12 @@ open class TabBarControllerImpl: ViewController, TabBarController {
|
|||
}
|
||||
}))
|
||||
}
|
||||
}, contextAction: { [weak self] index, node, gesture in
|
||||
}, contextAction: { [weak self] index, view, gesture in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if index >= 0 && index < strongSelf.tabBarControllerNode.tabBarItems.count {
|
||||
strongSelf.controllers[index].tabBarItemContextAction(sourceNode: node, gesture: gesture)
|
||||
strongSelf.controllers[index].tabBarItemContextAction(sourceView: view, gesture: gesture)
|
||||
}
|
||||
}, swipeAction: { [weak self] index, direction in
|
||||
guard let strongSelf = self else {
|
||||
|
|
|
|||
|
|
@ -13943,7 +13943,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||
self.controllerNode.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: transition)
|
||||
}
|
||||
|
||||
override public func tabBarItemContextAction(sourceNode: ContextExtractedContentContainingNode, gesture: ContextGesture) {
|
||||
override public func tabBarItemContextAction(sourceView: ContextExtractedContentContainingView, gesture: ContextGesture) {
|
||||
guard let (maybePrimary, other) = self.accountsAndPeersValue, let primary = maybePrimary else {
|
||||
return
|
||||
}
|
||||
|
|
@ -13994,7 +13994,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||
})))*/
|
||||
}
|
||||
|
||||
let controller = ContextController(presentationData: self.presentationData, source: .extracted(SettingsTabBarContextExtractedContentSource(controller: self, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
|
||||
let controller = ContextController(presentationData: self.presentationData, source: .reference(SettingsTabBarContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture)
|
||||
self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller)
|
||||
}
|
||||
|
||||
|
|
@ -14123,26 +14123,23 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||
}
|
||||
}
|
||||
|
||||
private final class SettingsTabBarContextExtractedContentSource: ContextExtractedContentSource {
|
||||
private final class SettingsTabBarContextReferenceContentSource: ContextReferenceContentSource {
|
||||
let keepInPlace: Bool = true
|
||||
let ignoreContentTouches: Bool = true
|
||||
let blurBackground: Bool = true
|
||||
let actionsHorizontalAlignment: ContextActionsHorizontalAlignment = .center
|
||||
|
||||
private let controller: ViewController
|
||||
private let sourceNode: ContextExtractedContentContainingNode
|
||||
private let sourceView: ContextExtractedContentContainingView
|
||||
|
||||
init(controller: ViewController, sourceNode: ContextExtractedContentContainingNode) {
|
||||
init(controller: ViewController, sourceView: ContextExtractedContentContainingView) {
|
||||
self.controller = controller
|
||||
self.sourceNode = sourceNode
|
||||
self.sourceView = sourceView
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
func transitionInfo() -> ContextControllerReferenceViewInfo? {
|
||||
return ContextControllerReferenceViewInfo(
|
||||
referenceView: self.sourceView.contentView,
|
||||
contentAreaInScreenSpace: UIScreen.main.bounds,
|
||||
actionsPosition: .top
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,14 +15,16 @@ public final class TabBarComponent: Component {
|
|||
public final class Item: Equatable {
|
||||
public let item: UITabBarItem
|
||||
public let action: (Bool) -> Void
|
||||
public let contextAction: ((ContextGesture, ContextExtractedContentContainingView) -> Void)?
|
||||
|
||||
fileprivate var id: AnyHashable {
|
||||
return AnyHashable(ObjectIdentifier(self.item))
|
||||
}
|
||||
|
||||
public init(item: UITabBarItem, action: @escaping (Bool) -> Void) {
|
||||
public init(item: UITabBarItem, action: @escaping (Bool) -> Void, contextAction: ((ContextGesture, ContextExtractedContentContainingView) -> Void)?) {
|
||||
self.item = item
|
||||
self.action = action
|
||||
self.contextAction = contextAction
|
||||
}
|
||||
|
||||
public static func ==(lhs: Item, rhs: Item) -> Bool {
|
||||
|
|
@ -32,6 +34,9 @@ public final class TabBarComponent: Component {
|
|||
if lhs.item !== rhs.item {
|
||||
return false
|
||||
}
|
||||
if (lhs.contextAction == nil) != (rhs.contextAction == nil) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -66,11 +71,14 @@ public final class TabBarComponent: Component {
|
|||
public final class View: UIView, UITabBarDelegate, UIGestureRecognizerDelegate {
|
||||
private let backgroundView: GlassBackgroundView
|
||||
private let selectionView: GlassBackgroundView.ContentImageView
|
||||
private let contextGestureContainerView: ContextControllerSourceView
|
||||
private let nativeTabBar: UITabBar?
|
||||
|
||||
private var itemViews: [AnyHashable: ComponentView<Empty>] = [:]
|
||||
private var selectedItemViews: [AnyHashable: ComponentView<Empty>] = [:]
|
||||
|
||||
private var itemWithActiveContextGesture: AnyHashable?
|
||||
|
||||
private var component: TabBarComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
|
|
@ -78,6 +86,9 @@ public final class TabBarComponent: Component {
|
|||
self.backgroundView = GlassBackgroundView(frame: CGRect())
|
||||
self.selectionView = GlassBackgroundView.ContentImageView()
|
||||
|
||||
self.contextGestureContainerView = ContextControllerSourceView()
|
||||
self.contextGestureContainerView.isGestureEnabled = true
|
||||
|
||||
if #available(iOS 26.0, *) {
|
||||
self.nativeTabBar = UITabBar()
|
||||
} else {
|
||||
|
|
@ -86,16 +97,110 @@ public final class TabBarComponent: Component {
|
|||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.contextGestureContainerView)
|
||||
|
||||
if let nativeTabBar = self.nativeTabBar {
|
||||
self.addSubview(nativeTabBar)
|
||||
self.contextGestureContainerView.addSubview(nativeTabBar)
|
||||
nativeTabBar.delegate = self
|
||||
let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.onLongPressGesture(_:)))
|
||||
/*let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.onLongPressGesture(_:)))
|
||||
longPressGesture.delegate = self
|
||||
self.addGestureRecognizer(longPressGesture)
|
||||
self.addGestureRecognizer(longPressGesture)*/
|
||||
} else {
|
||||
self.addSubview(self.backgroundView)
|
||||
self.contextGestureContainerView.addSubview(self.backgroundView)
|
||||
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:))))
|
||||
}
|
||||
|
||||
self.contextGestureContainerView.shouldBegin = { [weak self] point in
|
||||
guard let self, let component = self.component else {
|
||||
return false
|
||||
}
|
||||
for (id, itemView) in self.itemViews {
|
||||
if let itemView = itemView.view {
|
||||
if self.convert(itemView.bounds, from: itemView).contains(point) {
|
||||
guard let item = component.items.first(where: { $0.id == id }) else {
|
||||
return false
|
||||
}
|
||||
if item.contextAction == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
self.itemWithActiveContextGesture = id
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
/*self.contextGestureContainerView.customActivationProgress = { [weak self] progress, update in
|
||||
guard let self, let itemWithActiveContextGesture = self.itemWithActiveContextGesture else {
|
||||
return
|
||||
}
|
||||
guard let itemView = self.itemViews[itemWithActiveContextGesture]?.view else {
|
||||
return
|
||||
}
|
||||
let scaleSide = itemView.bounds.width
|
||||
let minScale: CGFloat = max(0.7, (scaleSide - 15.0) / scaleSide)
|
||||
let currentScale = 1.0 * (1.0 - progress) + minScale * progress
|
||||
|
||||
switch update {
|
||||
case .update:
|
||||
let sublayerTransform = CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0)
|
||||
itemView.layer.sublayerTransform = sublayerTransform
|
||||
case .begin:
|
||||
let sublayerTransform = CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0)
|
||||
itemView.layer.sublayerTransform = sublayerTransform
|
||||
case .ended:
|
||||
let sublayerTransform = CATransform3DScale(CATransform3DIdentity, currentScale, currentScale, 1.0)
|
||||
let previousTransform = itemView.layer.sublayerTransform
|
||||
itemView.layer.sublayerTransform = sublayerTransform
|
||||
|
||||
itemView.layer.animate(from: NSValue(caTransform3D: previousTransform), to: NSValue(caTransform3D: sublayerTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2)
|
||||
}
|
||||
}*/
|
||||
self.contextGestureContainerView.activated = { [weak self] gesture, _ in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
guard let itemWithActiveContextGesture = self.itemWithActiveContextGesture else {
|
||||
return
|
||||
}
|
||||
|
||||
var itemView: ItemComponent.View?
|
||||
if self.nativeTabBar != nil {
|
||||
itemView = self.selectedItemViews[itemWithActiveContextGesture]?.view as? ItemComponent.View
|
||||
} else {
|
||||
itemView = self.itemViews[itemWithActiveContextGesture]?.view as? ItemComponent.View
|
||||
}
|
||||
|
||||
guard let itemView else {
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let nativeTabBar = self.nativeTabBar {
|
||||
func cancelGestures(view: UIView) {
|
||||
for recognizer in view.gestureRecognizers ?? [] {
|
||||
if NSStringFromClass(type(of: recognizer)).contains("sSelectionGestureRecognizer") {
|
||||
recognizer.state = .cancelled
|
||||
}
|
||||
}
|
||||
for subview in view.subviews {
|
||||
cancelGestures(view: subview)
|
||||
}
|
||||
}
|
||||
|
||||
cancelGestures(view: nativeTabBar)
|
||||
}
|
||||
}
|
||||
|
||||
guard let item = component.items.first(where: { $0.id == itemWithActiveContextGesture }) else {
|
||||
return
|
||||
}
|
||||
item.contextAction?(gesture, itemView.contextContainerView)
|
||||
}
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
|
|
@ -175,6 +280,19 @@ public final class TabBarComponent: Component {
|
|||
return super.hitTest(point, with: event)
|
||||
}
|
||||
|
||||
public func frameForItem(at index: Int) -> CGRect? {
|
||||
guard let component = self.component else {
|
||||
return nil
|
||||
}
|
||||
if index < 0 || index >= component.items.count {
|
||||
return nil
|
||||
}
|
||||
guard let itemView = self.itemViews[component.items[index].id]?.view else {
|
||||
return nil
|
||||
}
|
||||
return self.convert(itemView.bounds, from: itemView)
|
||||
}
|
||||
|
||||
func update(component: TabBarComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
let innerInset: CGFloat = 3.0
|
||||
|
||||
|
|
@ -304,7 +422,7 @@ public final class TabBarComponent: Component {
|
|||
itemContainer.addSubview(selectedItemComponentView)
|
||||
}
|
||||
} else {
|
||||
self.addSubview(itemComponentView)
|
||||
self.contextGestureContainerView.addSubview(itemComponentView)
|
||||
}
|
||||
}
|
||||
if self.nativeTabBar != nil {
|
||||
|
|
@ -363,6 +481,8 @@ public final class TabBarComponent: Component {
|
|||
transition.setFrame(view: nativeTabBar, frame: CGRect(origin: CGPoint(x: floor((size.width - nativeTabBar.bounds.width) * 0.5), y: 0.0), size: nativeTabBar.bounds.size))
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.contextGestureContainerView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
|
@ -401,6 +521,8 @@ private final class ItemComponent: Component {
|
|||
}
|
||||
|
||||
final class View: UIView {
|
||||
let contextContainerView: ContextExtractedContentContainingView
|
||||
|
||||
private var imageIcon: ComponentView<Empty>?
|
||||
private var animationIcon: ComponentView<Empty>?
|
||||
private let title = ComponentView<Empty>()
|
||||
|
|
@ -414,7 +536,11 @@ private final class ItemComponent: Component {
|
|||
private var setBadgeListener: Int?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.contextContainerView = ContextExtractedContentContainingView()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.contextContainerView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
|
@ -512,9 +638,9 @@ private final class ItemComponent: Component {
|
|||
if let animationIconView = animationIcon.view {
|
||||
if animationIconView.superview == nil {
|
||||
if let badgeView = self.badge?.view {
|
||||
self.insertSubview(animationIconView, belowSubview: badgeView)
|
||||
self.contextContainerView.contentView.insertSubview(animationIconView, belowSubview: badgeView)
|
||||
} else {
|
||||
self.addSubview(animationIconView)
|
||||
self.contextContainerView.contentView.addSubview(animationIconView)
|
||||
}
|
||||
}
|
||||
iconTransition.setFrame(view: animationIconView, frame: iconFrame)
|
||||
|
|
@ -549,9 +675,9 @@ private final class ItemComponent: Component {
|
|||
if let imageIconView = imageIcon.view {
|
||||
if imageIconView.superview == nil {
|
||||
if let badgeView = self.badge?.view {
|
||||
self.insertSubview(imageIconView, belowSubview: badgeView)
|
||||
self.contextContainerView.contentView.insertSubview(imageIconView, belowSubview: badgeView)
|
||||
} else {
|
||||
self.addSubview(imageIconView)
|
||||
self.contextContainerView.contentView.addSubview(imageIconView)
|
||||
}
|
||||
}
|
||||
iconTransition.setFrame(view: imageIconView, frame: iconFrame)
|
||||
|
|
@ -569,7 +695,7 @@ private final class ItemComponent: Component {
|
|||
let titleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) * 0.5), y: availableSize.height - 9.0 - titleSize.height), size: titleSize)
|
||||
if let titleView = self.title.view {
|
||||
if titleView.superview == nil {
|
||||
self.addSubview(titleView)
|
||||
self.contextContainerView.contentView.addSubview(titleView)
|
||||
}
|
||||
titleView.frame = titleFrame
|
||||
}
|
||||
|
|
@ -600,7 +726,7 @@ private final class ItemComponent: Component {
|
|||
let badgeFrame = CGRect(origin: CGPoint(x: floor(availableSize.width / 2.0) + contentWidth - badgeSize.width - 5.0, y: -1.0), size: badgeSize)
|
||||
if let badgeView = badge.view {
|
||||
if badgeView.superview == nil {
|
||||
self.addSubview(badgeView)
|
||||
self.contextContainerView.contentView.addSubview(badgeView)
|
||||
}
|
||||
badgeTransition.setFrame(view: badgeView, frame: badgeFrame)
|
||||
}
|
||||
|
|
@ -609,6 +735,10 @@ private final class ItemComponent: Component {
|
|||
badge.view?.removeFromSuperview()
|
||||
}
|
||||
|
||||
transition.setFrame(view: self.contextContainerView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
transition.setFrame(view: self.contextContainerView.contentView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
self.contextContainerView.contentRect = CGRect(origin: CGPoint(), size: availableSize)
|
||||
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue