Add proximity alert tooltip

This commit is contained in:
Ilya Laktyushin 2020-10-19 10:26:52 +04:00
parent 81426bbbf8
commit 555a37b492
13 changed files with 3166 additions and 3033 deletions

View File

@ -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";

View File

@ -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)
} }

View File

@ -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",

View File

@ -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",

View File

@ -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()
} }

View File

@ -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

View File

@ -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)

View File

@ -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)
})) }))
} }

View File

@ -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
} }

View File

@ -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()

View File

@ -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
} }