mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 09:20:08 +00:00
Add proximity alert tooltip
This commit is contained in:
parent
81426bbbf8
commit
555a37b492
@ -5849,3 +5849,6 @@ Any member of this group will be able to see messages in the channel.";
|
|||||||
"Location.LiveLocationRequired.Title" = "Share Location";
|
"Location.LiveLocationRequired.Title" = "Share Location";
|
||||||
"Location.LiveLocationRequired.Description" = "For the alert to work, please share your live location in this chat.";
|
"Location.LiveLocationRequired.Description" = "For the alert to work, please share your live location in this chat.";
|
||||||
"Location.LiveLocationRequired.ShareLocation" = "Share Location";
|
"Location.LiveLocationRequired.ShareLocation" = "Share Location";
|
||||||
|
|
||||||
|
"Location.ProximityTip" = "Alert when %@ is close";
|
||||||
|
"Location.ProximityGroupTip" = "Alert when any group member is close";
|
||||||
|
|||||||
@ -1291,7 +1291,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
|||||||
|
|
||||||
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 8.0), size: CGSize())
|
let location = CGRect(origin: CGPoint(x: absoluteFrame.midX, y: absoluteFrame.minY - 8.0), size: CGSize())
|
||||||
|
|
||||||
parentController.present(TooltipScreen(text: text, icon: .chatListPress, location: .point(location), shouldDismissOnTouch: { point in
|
parentController.present(TooltipScreen(text: text, icon: .chatListPress, location: .point(location, .bottom), shouldDismissOnTouch: { point in
|
||||||
guard let strongSelf = self, let parentController = strongSelf.parent as? TabBarController else {
|
guard let strongSelf = self, let parentController = strongSelf.parent as? TabBarController else {
|
||||||
return .dismiss(consume: false)
|
return .dismiss(consume: false)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,8 @@ static_library(
|
|||||||
"//submodules/PersistentStringHash:PersistentStringHash",
|
"//submodules/PersistentStringHash:PersistentStringHash",
|
||||||
"//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode",
|
"//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode",
|
||||||
"//submodules/LiveLocationTimerNode:LiveLocationTimerNode",
|
"//submodules/LiveLocationTimerNode:LiveLocationTimerNode",
|
||||||
|
"//submodules/TelegramNotices:TelegramNotices",
|
||||||
|
"//submodules/TooltipUI:TooltipUI",
|
||||||
],
|
],
|
||||||
frameworks = [
|
frameworks = [
|
||||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||||
|
|||||||
@ -37,6 +37,8 @@ swift_library(
|
|||||||
"//submodules/PersistentStringHash:PersistentStringHash",
|
"//submodules/PersistentStringHash:PersistentStringHash",
|
||||||
"//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode",
|
"//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode",
|
||||||
"//submodules/LiveLocationTimerNode:LiveLocationTimerNode",
|
"//submodules/LiveLocationTimerNode:LiveLocationTimerNode",
|
||||||
|
"//submodules/TelegramNotices:TelegramNotices",
|
||||||
|
"//submodules/TooltipUI:TooltipUI",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -210,6 +210,14 @@ final class LocationMapHeaderNode: ASDisplayNode {
|
|||||||
self.shadowNode.image = generateShadowImage(theme: self.presentationData.theme, highlighted: highlighted)
|
self.shadowNode.image = generateShadowImage(theme: self.presentationData.theme, highlighted: highlighted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func proximityButtonFrame() -> CGRect? {
|
||||||
|
if self.notificationButtonNode.alpha > 0.0 {
|
||||||
|
return self.optionsBackgroundNode.view.convert(self.notificationButtonNode.frame, to: self.view)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func infoPressed() {
|
@objc private func infoPressed() {
|
||||||
self.toggleMapModeSelection()
|
self.toggleMapModeSelection()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,8 +47,9 @@ class LocationViewInteraction {
|
|||||||
let sendLiveLocation: (CLLocationCoordinate2D, Int32?) -> Void
|
let sendLiveLocation: (CLLocationCoordinate2D, Int32?) -> Void
|
||||||
let stopLiveLocation: () -> Void
|
let stopLiveLocation: () -> Void
|
||||||
let updateRightBarButton: (LocationViewRightBarButton) -> Void
|
let updateRightBarButton: (LocationViewRightBarButton) -> Void
|
||||||
|
let present: (ViewController) -> Void
|
||||||
|
|
||||||
init(toggleMapModeSelection: @escaping () -> Void, updateMapMode: @escaping (LocationMapMode) -> Void, goToUserLocation: @escaping () -> Void, goToCoordinate: @escaping (CLLocationCoordinate2D) -> Void, requestDirections: @escaping () -> Void, share: @escaping () -> Void, setupProximityNotification: @escaping (Bool, CLLocationCoordinate2D?, MessageId?) -> Void, updateSendActionHighlight: @escaping (Bool) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D, Int32?) -> Void, stopLiveLocation: @escaping () -> Void, updateRightBarButton: @escaping (LocationViewRightBarButton) -> Void) {
|
init(toggleMapModeSelection: @escaping () -> Void, updateMapMode: @escaping (LocationMapMode) -> Void, goToUserLocation: @escaping () -> Void, goToCoordinate: @escaping (CLLocationCoordinate2D) -> Void, requestDirections: @escaping () -> Void, share: @escaping () -> Void, setupProximityNotification: @escaping (Bool, CLLocationCoordinate2D?, MessageId?) -> Void, updateSendActionHighlight: @escaping (Bool) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D, Int32?) -> Void, stopLiveLocation: @escaping () -> Void, updateRightBarButton: @escaping (LocationViewRightBarButton) -> Void, present: @escaping (ViewController) -> Void) {
|
||||||
self.toggleMapModeSelection = toggleMapModeSelection
|
self.toggleMapModeSelection = toggleMapModeSelection
|
||||||
self.updateMapMode = updateMapMode
|
self.updateMapMode = updateMapMode
|
||||||
self.goToUserLocation = goToUserLocation
|
self.goToUserLocation = goToUserLocation
|
||||||
@ -60,6 +61,7 @@ class LocationViewInteraction {
|
|||||||
self.sendLiveLocation = sendLiveLocation
|
self.sendLiveLocation = sendLiveLocation
|
||||||
self.stopLiveLocation = stopLiveLocation
|
self.stopLiveLocation = stopLiveLocation
|
||||||
self.updateRightBarButton = updateRightBarButton
|
self.updateRightBarButton = updateRightBarButton
|
||||||
|
self.present = present
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,6 +282,10 @@ public final class LocationViewController: ViewController {
|
|||||||
strongSelf.rightBarButtonAction = action
|
strongSelf.rightBarButtonAction = action
|
||||||
strongSelf.updateRightBarButton()
|
strongSelf.updateRightBarButton()
|
||||||
}
|
}
|
||||||
|
}, present: { [weak self] c in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.present(c, in: .window(.root))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
self.scrollToTop = { [weak self] in
|
self.scrollToTop = { [weak self] in
|
||||||
|
|||||||
@ -11,11 +11,13 @@ import ItemListUI
|
|||||||
import ItemListVenueItem
|
import ItemListVenueItem
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import TelegramStringFormatting
|
import TelegramStringFormatting
|
||||||
|
import TelegramNotices
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import AppBundle
|
import AppBundle
|
||||||
import CoreLocation
|
import CoreLocation
|
||||||
import Geocoding
|
import Geocoding
|
||||||
import DeviceAccess
|
import DeviceAccess
|
||||||
|
import TooltipUI
|
||||||
|
|
||||||
func getLocation(from message: Message) -> TelegramMediaMap? {
|
func getLocation(from message: Message) -> TelegramMediaMap? {
|
||||||
return message.media.first(where: { $0 is TelegramMediaMap } ) as? TelegramMediaMap
|
return message.media.first(where: { $0 is TelegramMediaMap } ) as? TelegramMediaMap
|
||||||
@ -214,6 +216,8 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
|||||||
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
|
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
|
||||||
private var listOffset: CGFloat?
|
private var listOffset: CGFloat?
|
||||||
|
|
||||||
|
private var displayedProximityAlertTooltip = false
|
||||||
|
|
||||||
init(context: AccountContext, presentationData: PresentationData, subject: Message, interaction: LocationViewInteraction, locationManager: LocationManager) {
|
init(context: AccountContext, presentationData: PresentationData, subject: Message, interaction: LocationViewInteraction, locationManager: LocationManager) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
@ -291,6 +295,8 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
interaction.setupProximityNotification(reset, strongSelf.headerNode.mapNode.currentUserLocation?.coordinate, ownMessageId)
|
interaction.setupProximityNotification(reset, strongSelf.headerNode.mapNode.currentUserLocation?.coordinate, ownMessageId)
|
||||||
|
|
||||||
|
let _ = ApplicationSpecificNotice.incrementLocationProximityAlertTip(accountManager: context.sharedContext.accountManager, count: 4).start()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,6 +418,18 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
|||||||
|
|
||||||
strongSelf.headerNode.updateState(mapMode: state.mapMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: false, proximityNotification: proximityNotification, animated: false)
|
strongSelf.headerNode.updateState(mapMode: state.mapMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: false, proximityNotification: proximityNotification, animated: false)
|
||||||
|
|
||||||
|
if let proximityNotification = proximityNotification, !proximityNotification && !strongSelf.displayedProximityAlertTooltip {
|
||||||
|
strongSelf.displayedProximityAlertTooltip = true
|
||||||
|
|
||||||
|
let _ = (ApplicationSpecificNotice.getLocationProximityAlertTip(accountManager: context.sharedContext.accountManager)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] counter in
|
||||||
|
if let strongSelf = self, counter < 3 {
|
||||||
|
let _ = ApplicationSpecificNotice.incrementLocationProximityAlertTip(accountManager: context.sharedContext.accountManager).start()
|
||||||
|
strongSelf.displayProximityAlertTooltip()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
switch state.selectedLocation {
|
switch state.selectedLocation {
|
||||||
case .initial:
|
case .initial:
|
||||||
if previousState?.selectedLocation != .initial {
|
if previousState?.selectedLocation != .initial {
|
||||||
@ -589,6 +607,30 @@ final class LocationViewControllerNode: ViewControllerTracingNode, CLLocationMan
|
|||||||
self.headerNode.mapNode.showAll()
|
self.headerNode.mapNode.showAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func displayProximityAlertTooltip() {
|
||||||
|
guard let location = self.headerNode.proximityButtonFrame().flatMap({ frame -> CGRect in
|
||||||
|
return self.headerNode.view.convert(frame, to: nil)
|
||||||
|
}) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = (self.context.account.postbox.loadedPeerWithId(self.subject.id.peerId)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var text: String = strongSelf.presentationData.strings.Location_ProximityGroupTip
|
||||||
|
if peer.id.namespace == Namespaces.Peer.CloudUser {
|
||||||
|
text = strongSelf.presentationData.strings.Location_ProximityTip(peer.compactDisplayTitle).0
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.interaction.present(TooltipScreen(text: text, icon: nil, location: .point(location.offsetBy(dx: -9.0, dy: 0.0), .right), displayDuration: .custom(3.0), shouldDismissOnTouch: { _ in
|
||||||
|
return .dismiss(consume: false)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
let isFirstLayout = self.validLayout == nil
|
let isFirstLayout = self.validLayout == nil
|
||||||
self.validLayout = (layout, navigationHeight)
|
self.validLayout = (layout, navigationHeight)
|
||||||
|
|||||||
@ -641,7 +641,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.present?(TooltipScreen(text: self.presentationData.strings.Call_CameraTooltip, style: .light, icon: nil, location: .point(location.offsetBy(dx: 0.0, dy: -14.0)), displayDuration: .custom(5.0), shouldDismissOnTouch: { _ in
|
self.present?(TooltipScreen(text: self.presentationData.strings.Call_CameraTooltip, style: .light, icon: nil, location: .point(location.offsetBy(dx: 0.0, dy: -14.0), .bottom), displayDuration: .custom(5.0), shouldDismissOnTouch: { _ in
|
||||||
return .dismiss(consume: false)
|
return .dismiss(consume: false)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -136,6 +136,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
|
|||||||
case themeChangeTip = 17
|
case themeChangeTip = 17
|
||||||
case callsTabTip = 18
|
case callsTabTip = 18
|
||||||
case chatFolderTips = 19
|
case chatFolderTips = 19
|
||||||
|
case locationProximityAlertTip = 20
|
||||||
|
|
||||||
var key: ValueBoxKey {
|
var key: ValueBoxKey {
|
||||||
let v = ValueBoxKey(length: 4)
|
let v = ValueBoxKey(length: 4)
|
||||||
@ -258,6 +259,10 @@ private struct ApplicationSpecificNoticeKeys {
|
|||||||
static func themeChangeTip() -> NoticeEntryKey {
|
static func themeChangeTip() -> NoticeEntryKey {
|
||||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.themeChangeTip.key)
|
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.themeChangeTip.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func locationProximityAlertTip() -> NoticeEntryKey {
|
||||||
|
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.locationProximityAlertTip.key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ApplicationSpecificNotice {
|
public struct ApplicationSpecificNotice {
|
||||||
@ -662,6 +667,28 @@ public struct ApplicationSpecificNotice {
|
|||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func getLocationProximityAlertTip(accountManager: AccountManager) -> Signal<Int32, NoError> {
|
||||||
|
return accountManager.transaction { transaction -> Int32 in
|
||||||
|
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatMessageOptionsTip()) as? ApplicationSpecificCounterNotice {
|
||||||
|
return value.value
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func incrementLocationProximityAlertTip(accountManager: AccountManager, count: Int32 = 1) -> Signal<Void, NoError> {
|
||||||
|
return accountManager.transaction { transaction -> Void in
|
||||||
|
var currentValue: Int32 = 0
|
||||||
|
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.chatMessageOptionsTip()) as? ApplicationSpecificCounterNotice {
|
||||||
|
currentValue = value.value
|
||||||
|
}
|
||||||
|
currentValue += count
|
||||||
|
|
||||||
|
transaction.setNotice(ApplicationSpecificNoticeKeys.chatMessageOptionsTip(), ApplicationSpecificCounterNotice(value: currentValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static func reset(accountManager: AccountManager) -> Signal<Void, NoError> {
|
public static func reset(accountManager: AccountManager) -> Signal<Void, NoError> {
|
||||||
return accountManager.transaction { transaction -> Void in
|
return accountManager.transaction { transaction -> Void in
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -5774,7 +5774,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
let inputText = self.presentationInterfaceState.interfaceState.effectiveInputState.inputText.string
|
let inputText = self.presentationInterfaceState.interfaceState.effectiveInputState.inputText.string
|
||||||
if !inputText.isEmpty {
|
if !inputText.isEmpty {
|
||||||
if inputText.count > 4 {
|
if inputText.count > 4 {
|
||||||
let _ = (ApplicationSpecificNotice.getChatMessageOptionsTip(accountManager: context.sharedContext.accountManager)
|
let _ = (ApplicationSpecificNotice.getChatMessageOptionsTip(accountManager: self.context.sharedContext.accountManager)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] counter in
|
|> deliverOnMainQueue).start(next: { [weak self] counter in
|
||||||
if let strongSelf = self, counter < 3 {
|
if let strongSelf = self, counter < 3 {
|
||||||
let _ = ApplicationSpecificNotice.incrementChatMessageOptionsTip(accountManager: strongSelf.context.sharedContext.accountManager).start()
|
let _ = ApplicationSpecificNotice.incrementChatMessageOptionsTip(accountManager: strongSelf.context.sharedContext.accountManager).start()
|
||||||
|
|||||||
@ -287,9 +287,15 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
var invertArrow = false
|
var invertArrow = false
|
||||||
switch self.location {
|
switch self.location {
|
||||||
case let .point(rect):
|
case let .point(rect, arrowPosition):
|
||||||
let backgroundWidth = textSize.width + contentInset * 2.0 + animationSize.width + animationSpacing
|
let backgroundWidth = textSize.width + contentInset * 2.0 + animationSize.width + animationSpacing
|
||||||
backgroundFrame = CGRect(origin: CGPoint(x: rect.midX - backgroundWidth / 2.0, y: rect.minY - bottomInset - backgroundHeight), size: CGSize(width: backgroundWidth, height: backgroundHeight))
|
switch arrowPosition {
|
||||||
|
case .bottom:
|
||||||
|
backgroundFrame = CGRect(origin: CGPoint(x: rect.midX - backgroundWidth / 2.0, y: rect.minY - bottomInset - backgroundHeight), size: CGSize(width: backgroundWidth, height: backgroundHeight))
|
||||||
|
case .right:
|
||||||
|
backgroundFrame = CGRect(origin: CGPoint(x: rect.minX - backgroundWidth - bottomInset, y: rect.midY - backgroundHeight / 2.0), size: CGSize(width: backgroundWidth, height: backgroundHeight))
|
||||||
|
}
|
||||||
|
|
||||||
if backgroundFrame.minX < sideInset {
|
if backgroundFrame.minX < sideInset {
|
||||||
backgroundFrame.origin.x = sideInset
|
backgroundFrame.origin.x = sideInset
|
||||||
}
|
}
|
||||||
@ -312,24 +318,35 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
if let effectView = self.effectView {
|
if let effectView = self.effectView {
|
||||||
transition.updateFrame(view: effectView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
transition.updateFrame(view: effectView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
||||||
}
|
}
|
||||||
if let image = self.arrowNode.image, case let .point(rect) = self.location {
|
if let image = self.arrowNode.image, case let .point(rect, arrowPosition) = self.location {
|
||||||
let arrowSize = image.size
|
let arrowSize = image.size
|
||||||
let arrowCenterX = rect.midX
|
let arrowCenterX = rect.midX
|
||||||
|
|
||||||
let arrowFrame: CGRect
|
let arrowFrame: CGRect
|
||||||
|
|
||||||
if invertArrow {
|
switch arrowPosition {
|
||||||
arrowFrame = CGRect(origin: CGPoint(x: floor(arrowCenterX - arrowSize.width / 2.0), y: -arrowSize.height), size: arrowSize)
|
case .bottom:
|
||||||
} else {
|
if invertArrow {
|
||||||
arrowFrame = CGRect(origin: CGPoint(x: floor(arrowCenterX - arrowSize.width / 2.0), y: backgroundFrame.height), size: arrowSize)
|
arrowFrame = CGRect(origin: CGPoint(x: floor(arrowCenterX - arrowSize.width / 2.0), y: -arrowSize.height), size: arrowSize)
|
||||||
|
} else {
|
||||||
|
arrowFrame = CGRect(origin: CGPoint(x: floor(arrowCenterX - arrowSize.width / 2.0), y: backgroundFrame.height), size: arrowSize)
|
||||||
|
}
|
||||||
|
ContainedViewLayoutTransition.immediate.updateTransformScale(node: self.arrowContainer, scale: CGPoint(x: 1.0, y: invertArrow ? -1.0 : 1.0))
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.arrowContainer, frame: arrowFrame.offsetBy(dx: -backgroundFrame.minX, dy: 0.0))
|
||||||
|
|
||||||
|
self.arrowNode.frame = CGRect(origin: CGPoint(), size: arrowSize)
|
||||||
|
self.arrowEffectView?.frame = CGRect(origin: CGPoint(), size: arrowSize)
|
||||||
|
case .right:
|
||||||
|
arrowFrame = CGRect(origin: CGPoint(x: backgroundFrame.width + arrowSize.height, y: rect.midY), size: CGSize(width: arrowSize.height, height: arrowSize.width))
|
||||||
|
|
||||||
|
ContainedViewLayoutTransition.immediate.updateTransformRotation(node: self.arrowContainer, angle: -CGFloat.pi / 2.0)
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.arrowContainer, frame: arrowFrame.offsetBy(dx: 0.0, dy: -backgroundFrame.minY - floorToScreenPixels((backgroundFrame.height - arrowSize.width) / 2.0)))
|
||||||
|
|
||||||
|
self.arrowNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -0.5), size: arrowSize)
|
||||||
|
self.arrowEffectView?.frame = CGRect(origin: CGPoint(x: 0.0, y: -0.5), size: arrowSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
transition.updateFrame(node: self.arrowContainer, frame: arrowFrame.offsetBy(dx: -backgroundFrame.minX, dy: 0.0))
|
|
||||||
|
|
||||||
ContainedViewLayoutTransition.immediate.updateTransformScale(node: self.arrowContainer, scale: CGPoint(x: 1.0, y: invertArrow ? -1.0 : 1.0))
|
|
||||||
|
|
||||||
self.arrowNode.frame = CGRect(origin: CGPoint(), size: arrowFrame.size)
|
|
||||||
self.arrowEffectView?.frame = CGRect(origin: CGPoint(), size: arrowFrame.size)
|
|
||||||
} else {
|
} else {
|
||||||
self.arrowNode.isHidden = true
|
self.arrowNode.isHidden = true
|
||||||
self.arrowEffectView?.isHidden = true
|
self.arrowEffectView?.isHidden = true
|
||||||
@ -375,10 +392,19 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
if let _ = self.validLayout {
|
if let _ = self.validLayout {
|
||||||
self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -13.0 - self.backgroundNode.frame.height), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -13.0 - self.backgroundNode.frame.height), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||||
}
|
}
|
||||||
case .point:
|
case let .point(_, arrowPosition):
|
||||||
self.containerNode.layer.animateSpring(from: NSNumber(value: Float(0.01)), to: NSNumber(value: Float(1.0)), keyPath: "transform.scale", duration: 0.4, damping: 105.0)
|
self.containerNode.layer.animateSpring(from: NSNumber(value: Float(0.01)), to: NSNumber(value: Float(1.0)), keyPath: "transform.scale", duration: 0.4, damping: 105.0)
|
||||||
let arrowY: CGFloat = self.isArrowInverted ? self.arrowContainer.frame.minY : self.arrowContainer.frame.maxY
|
|
||||||
self.containerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: self.arrowContainer.frame.midX - self.containerNode.bounds.width / 2.0, y: arrowY - self.containerNode.bounds.height / 2.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: 0.4, damping: 105.0, additive: true)
|
let startPoint: CGPoint
|
||||||
|
switch arrowPosition {
|
||||||
|
case .bottom:
|
||||||
|
let arrowY: CGFloat = self.isArrowInverted ? self.arrowContainer.frame.minY : self.arrowContainer.frame.maxY
|
||||||
|
startPoint = CGPoint(x: self.arrowContainer.frame.midX - self.containerNode.bounds.width / 2.0, y: arrowY - self.containerNode.bounds.height / 2.0)
|
||||||
|
case .right:
|
||||||
|
startPoint = CGPoint(x: self.arrowContainer.frame.maxX - self.containerNode.bounds.width / 2.0, y: self.arrowContainer.frame.minY - self.containerNode.bounds.height / 2.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.containerNode.layer.animateSpring(from: NSValue(cgPoint: startPoint), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: 0.4, damping: 105.0, additive: true)
|
||||||
self.containerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
self.containerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,14 +433,22 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
|||||||
if let _ = self.validLayout {
|
if let _ = self.validLayout {
|
||||||
self.containerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -13.0 - self.backgroundNode.frame.height), duration: 0.3, removeOnCompletion: false, additive: true)
|
self.containerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -13.0 - self.backgroundNode.frame.height), duration: 0.3, removeOnCompletion: false, additive: true)
|
||||||
}
|
}
|
||||||
case .point:
|
case let .point(_, arrowPosition):
|
||||||
self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||||
completion()
|
completion()
|
||||||
})
|
})
|
||||||
self.containerNode.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false)
|
self.containerNode.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false)
|
||||||
|
|
||||||
let arrowY: CGFloat = self.isArrowInverted ? self.arrowContainer.frame.minY : self.arrowContainer.frame.maxY
|
let targetPoint: CGPoint
|
||||||
self.containerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: self.arrowContainer.frame.midX - self.containerNode.bounds.width / 2.0, y: arrowY - self.containerNode.bounds.height / 2.0), duration: 0.2, removeOnCompletion: false, additive: true)
|
switch arrowPosition {
|
||||||
|
case .bottom:
|
||||||
|
let arrowY: CGFloat = self.isArrowInverted ? self.arrowContainer.frame.minY : self.arrowContainer.frame.maxY
|
||||||
|
targetPoint = CGPoint(x: self.arrowContainer.frame.midX - self.containerNode.bounds.width / 2.0, y: arrowY - self.containerNode.bounds.height / 2.0)
|
||||||
|
case .right:
|
||||||
|
targetPoint = CGPoint(x: self.arrowContainer.frame.maxX - self.containerNode.bounds.width / 2.0, y: self.arrowContainer.frame.minY - self.containerNode.bounds.height / 2.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.containerNode.layer.animatePosition(from: CGPoint(), to: targetPoint, duration: 0.2, removeOnCompletion: false, additive: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,8 +476,13 @@ public final class TooltipScreen: ViewController {
|
|||||||
case dismiss(consume: Bool)
|
case dismiss(consume: Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum ArrowPosition {
|
||||||
|
case bottom
|
||||||
|
case right
|
||||||
|
}
|
||||||
|
|
||||||
public enum Location {
|
public enum Location {
|
||||||
case point(CGRect)
|
case point(CGRect, ArrowPosition)
|
||||||
case top
|
case top
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user