[ios] Fix track selection point updates on every new selection

On the every new tap on the `Track` or during the `Elevation chart` dragging, the track `Active point` will be updated now. It allows to keep the current selected track point coordinates up to date and fix the bug when the `route to/route from` buttons use only the initial coordinates.
Key changes:
1. the `Active point` and `My position` points are moved from the `Elevation profile` to the `PlacePageTrackData` because this properties are related to the whole track. Not only chart. The chart is only one of the consumers of this data updates.
2. The subscription to the active point updates is moved from the `Elevation profile` to the `PlacePagePresenter`. The reason - see 1.
2. The callback `onActivePointChanged` is added to notify that the active point is updated
3. When the callback is triggered the `PlacePageTrackData` fetches the new coordinates from the core and saves it. This coordinates are used by the `route to/from` buttons.

Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
This commit is contained in:
Kiryl Kaveryn
2025-06-17 21:22:47 +04:00
committed by Konstantin Pastbin
parent 927299f4a9
commit aec82794ac
13 changed files with 126 additions and 65 deletions

View File

@@ -6,7 +6,8 @@ NS_ASSUME_NONNULL_BEGIN
@interface PlacePageTrackData (Core)
- (instancetype)initWithTrack:(Track const &)track;
- (instancetype)initWithTrack:(Track const &)track
onActivePointChanged:(MWMVoidBlock)onActivePointChangedHandler;
@end

View File

@@ -12,8 +12,15 @@ NS_ASSUME_NONNULL_BEGIN
@property(nonatomic, readonly) MWMMarkGroupID groupId;
@property(nonatomic, readwrite, nonnull) TrackInfo * trackInfo;
@property(nonatomic, readwrite, nullable) ElevationProfileData * elevationProfileData;
@property(nonatomic, readonly) double activePoint;
@property(nonatomic, readonly) double myPosition;
@property(nonatomic) MWMVoidBlock onActivePointChangedHandler;
- (instancetype)initWithTrackInfo:(TrackInfo * _Nonnull)trackInfo elevationInfo:(ElevationProfileData * _Nullable)elevationInfo;
- (instancetype)initWithTrackInfo:(TrackInfo *)trackInfo
elevationInfo:(ElevationProfileData * _Nullable)elevationInfo
onActivePointChanged:(MWMVoidBlock)onActivePointChangedHandler;
- (void)updateActivePointDistance:(double)distance;
@end

View File

@@ -2,33 +2,52 @@
#import "ElevationProfileData+Core.h"
#import "TrackInfo+Core.h"
@interface PlacePageTrackData ()
@property(nonatomic, readwrite) double activePoint;
@end
@implementation PlacePageTrackData
- (nonnull instancetype)initWithTrackInfo:(TrackInfo *)trackInfo elevationInfo:(ElevationProfileData * _Nullable)elevationInfo {
- (instancetype)initWithTrackInfo:(TrackInfo *)trackInfo
elevationInfo:(ElevationProfileData * _Nullable)elevationInfo
onActivePointChanged:(MWMVoidBlock)onActivePointChangedHandler {
self = [super init];
if (self) {
_trackInfo = trackInfo;
_elevationProfileData = elevationInfo;
_onActivePointChangedHandler = onActivePointChangedHandler;
}
return self;
}
- (void)updateActivePointDistance:(double)distance {
self.activePoint = distance;
if (self.onActivePointChangedHandler)
self.onActivePointChangedHandler();
}
@end
@implementation PlacePageTrackData (Core)
- (instancetype)initWithTrack:(Track const &)track {
- (instancetype)initWithTrack:(Track const &)track
onActivePointChanged:(MWMVoidBlock)onActivePointChangedHandler {
self = [super init];
if (self) {
_trackId = track.GetData().m_id;
_trackInfo = [[TrackInfo alloc] initWithTrackStatistics:track.GetStatistics()];
auto const & bm = GetFramework().GetBookmarkManager();
_activePoint = bm.GetElevationActivePoint(_trackId);
_myPosition = bm.GetElevationMyPosition(_trackId);
_onActivePointChangedHandler = onActivePointChangedHandler;
auto const & elevationInfo = track.GetElevationInfo();
if (track.HasAltitudes() && elevationInfo.has_value()) {
auto const & bm = GetFramework().GetBookmarkManager();
_elevationProfileData = [[ElevationProfileData alloc] initWithTrackId:_trackId
elevationInfo:elevationInfo.value()
activePoint:bm.GetElevationActivePoint(_trackId)
myPosition:bm.GetElevationMyPosition(_trackId)];
elevationInfo:elevationInfo.value()];
}
}
return self;

View File

@@ -8,9 +8,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface ElevationProfileData (Core)
- (instancetype)initWithTrackId:(MWMTrackID)trackId
elevationInfo:(ElevationInfo const &)elevationInfo
activePoint:(double)activePoint
myPosition:(double)myPosition;
elevationInfo:(ElevationInfo const &)elevationInfo;
- (instancetype)initWithElevationInfo:(ElevationInfo const &)elevationInfo;
@end

View File

@@ -16,8 +16,6 @@ typedef NS_ENUM(NSInteger, ElevationDifficulty) {
@property(nonatomic, readonly) BOOL isTrackRecording;
@property(nonatomic, readonly) ElevationDifficulty difficulty;
@property(nonatomic, readonly) NSArray<ElevationHeightPoint *> * points;
@property(nonatomic, readonly) double activePoint;
@property(nonatomic, readonly) double myPosition;
@end

View File

@@ -23,16 +23,12 @@ static ElevationDifficulty convertDifficulty(uint8_t difficulty) {
@implementation ElevationProfileData (Core)
- (instancetype)initWithTrackId:(MWMTrackID)trackId
elevationInfo:(ElevationInfo const &)elevationInfo
activePoint:(double)activePoint
myPosition:(double)myPosition {
elevationInfo:(ElevationInfo const &)elevationInfo {
self = [super init];
if (self) {
_trackId = trackId;
_difficulty = convertDifficulty(elevationInfo.GetDifficulty());
_points = [ElevationProfileData pointsFromElevationInfo:elevationInfo];
_activePoint = activePoint;
_myPosition = myPosition;
_isTrackRecording = false;
}
return self;

View File

@@ -32,6 +32,9 @@ static PlacePageRoadType convertRoadType(RoadWarningMarkType roadType) {
std::vector<std::string> m_rawTypes;
}
@property(nonatomic, readwrite) PlacePagePreviewData *previewData;
@property(nonatomic, readwrite) CLLocationCoordinate2D locationCoordinate;
@end
@implementation PlacePageData
@@ -67,7 +70,10 @@ static PlacePageRoadType convertRoadType(RoadWarningMarkType roadType) {
if (rawData().IsTrack()) {
_objectType = PlacePageObjectTypeTrack;
auto const & track = GetFramework().GetBookmarkManager().GetTrack(rawData().GetTrackId());
_trackData = [[PlacePageTrackData alloc] initWithTrack:*track];
__weak auto weakSelf = self;
_trackData = [[PlacePageTrackData alloc] initWithTrack:*track onActivePointChanged:^(void) {
[weakSelf handleActiveTrackSelectionPointChanged];
}];
_isPreviewPlus = track->HasAltitudes();
}
_previewData = [[PlacePagePreviewData alloc] initWithRawData:rawData()];
@@ -91,7 +97,12 @@ static PlacePageRoadType convertRoadType(RoadWarningMarkType roadType) {
_objectType = PlacePageObjectTypeTrackRecording;
_roadType = PlacePageRoadTypeNone;
_previewData = [[PlacePagePreviewData alloc] initWithTrackInfo:trackInfo];
_trackData = [[PlacePageTrackData alloc] initWithTrackInfo:trackInfo elevationInfo:elevationInfo];
__weak auto weakSelf = self;
_trackData = [[PlacePageTrackData alloc] initWithTrackInfo:trackInfo
elevationInfo:elevationInfo
onActivePointChanged:^(void) {
[weakSelf handleActiveTrackSelectionPointChanged];
}];
}
return self;
}
@@ -104,6 +115,15 @@ static PlacePageRoadType convertRoadType(RoadWarningMarkType roadType) {
self.onTrackRecordingProgressUpdate();
}
- (void)handleActiveTrackSelectionPointChanged {
if (!self || !rawData().IsTrack())
return;
auto const & trackInfo = GetFramework().GetBookmarkManager().GetTrackSelectionInfo(rawData().GetTrackId());
auto latlon = mercator::ToLatLon(trackInfo.m_trackPoint);
_locationCoordinate = CLLocationCoordinate2DMake(latlon.m_lat, latlon.m_lon);
self.previewData = [[PlacePagePreviewData alloc] initWithRawData:rawData()];
}
- (void)dealloc {
if (self.mapNodeAttributes != nil) {
[[MWMStorage sharedStorage] removeObserver:self];

View File

@@ -1,13 +1,11 @@
import CoreApi
class ElevationProfileBuilder {
static func build(trackInfo: TrackInfo,
elevationProfileData: ElevationProfileData?,
static func build(trackData: PlacePageTrackData,
delegate: ElevationProfileViewControllerDelegate?) -> ElevationProfileViewController {
let viewController = ElevationProfileViewController();
let presenter = ElevationProfilePresenter(view: viewController,
trackInfo: trackInfo,
profileData: elevationProfileData,
trackData: trackData,
delegate: delegate)
viewController.presenter = presenter
return viewController

View File

@@ -1,10 +1,14 @@
import Chart
import CoreApi
protocol ElevationProfilePresenterProtocol: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func configure()
func update(trackInfo: TrackInfo, profileData: ElevationProfileData?)
protocol TrackActivePointPresenter: AnyObject {
func updateActivePoint(_ distance: Double)
func updateMyPosition(_ distance: Double)
}
protocol ElevationProfilePresenterProtocol: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, TrackActivePointPresenter {
func configure()
func update(with trackData: PlacePageTrackData)
func onDifficultyButtonPressed()
func onSelectedPointChanged(_ point: CGFloat)
}
@@ -22,8 +26,7 @@ fileprivate struct DescriptionsViewModel {
final class ElevationProfilePresenter: NSObject {
private weak var view: ElevationProfileViewProtocol?
private var trackInfo: TrackInfo
private var profileData: ElevationProfileData?
private var trackData: PlacePageTrackData
private let delegate: ElevationProfileViewControllerDelegate?
private let bookmarkManager: BookmarksManager = .shared()
@@ -33,19 +36,17 @@ final class ElevationProfilePresenter: NSObject {
private let formatter: ElevationProfileFormatter
init(view: ElevationProfileViewProtocol,
trackInfo: TrackInfo,
profileData: ElevationProfileData?,
trackData: PlacePageTrackData,
formatter: ElevationProfileFormatter = ElevationProfileFormatter(),
delegate: ElevationProfileViewControllerDelegate?) {
self.view = view
self.delegate = delegate
self.formatter = formatter
self.trackInfo = trackInfo
self.profileData = profileData
if let profileData {
self.trackData = trackData
if let profileData = trackData.elevationProfileData {
self.chartData = ElevationProfileChartData(profileData)
}
self.descriptionModels = Self.descriptionModels(for: trackInfo)
self.descriptionModels = Self.descriptionModels(for: trackData.trackInfo)
}
private static func descriptionModels(for trackInfo: TrackInfo) -> [DescriptionsViewModel] {
@@ -56,30 +57,37 @@ final class ElevationProfilePresenter: NSObject {
DescriptionsViewModel(title: L("elevation_profile_min_elevation"), value: trackInfo.minElevation, imageName: "ic_em_min_attitude_24")
]
}
deinit {
bookmarkManager.resetElevationActivePointChanged()
bookmarkManager.resetElevationMyPositionChanged()
}
}
extension ElevationProfilePresenter: ElevationProfilePresenterProtocol {
func update(trackInfo: TrackInfo, profileData: ElevationProfileData?) {
self.profileData = profileData
if let profileData {
func update(with trackData: PlacePageTrackData) {
self.trackData = trackData
if let profileData = trackData.elevationProfileData {
self.chartData = ElevationProfileChartData(profileData)
} else {
self.chartData = nil
}
descriptionModels = Self.descriptionModels(for: trackInfo)
descriptionModels = Self.descriptionModels(for: trackData.trackInfo)
configure()
}
func updateActivePoint(_ distance: Double) {
guard let view, !view.isChartViewInfoHidden else { return }
view.setActivePoint(distance)
}
func updateMyPosition(_ distance: Double) {
guard let view, !view.isChartViewInfoHidden else { return }
view.setMyPosition(distance)
}
func configure() {
view?.isChartViewHidden = false
let kMinPointsToDraw = 3
guard let profileData, let chartData, chartData.points.count >= kMinPointsToDraw else {
guard let profileData = trackData.elevationProfileData,
let chartData,
chartData.points.count >= kMinPointsToDraw else {
view?.userInteractionEnabled = false
return
}
@@ -93,14 +101,8 @@ extension ElevationProfilePresenter: ElevationProfilePresenterProtocol {
return
}
view?.setActivePoint(profileData.activePoint)
view?.setMyPosition(profileData.myPosition)
bookmarkManager.setElevationActivePointChanged(profileData.trackId) { [weak self] distance in
self?.view?.setActivePoint(distance)
}
bookmarkManager.setElevationMyPositionChanged(profileData.trackId) { [weak self] distance in
self?.view?.setMyPosition(distance)
}
view?.setActivePoint(trackData.activePoint)
view?.setMyPosition(trackData.myPosition)
}
func onDifficultyButtonPressed() {

View File

@@ -13,7 +13,9 @@
case .POI, .bookmark:
layout = PlacePageCommonLayout(interactor: interactor, storyboard: storyboard, data: data)
case .track:
layout = PlacePageTrackLayout(interactor: interactor, storyboard: storyboard, data: data)
let trackLayout = PlacePageTrackLayout(interactor: interactor, storyboard: storyboard, data: data)
interactor.trackActivePointPresenter = trackLayout.elevationMapViewController?.presenter
layout = trackLayout
case .trackRecording:
layout = PlacePageTrackRecordingLayout(interactor: interactor, storyboard: storyboard, data: data)
@unknown default:
@@ -38,7 +40,9 @@
case .POI, .bookmark:
layout = PlacePageCommonLayout(interactor: interactor, storyboard: storyboard, data: data)
case .track:
layout = PlacePageTrackLayout(interactor: interactor, storyboard: storyboard, data: data)
let trackLayout = PlacePageTrackLayout(interactor: interactor, storyboard: storyboard, data: data)
interactor.trackActivePointPresenter = trackLayout.elevationMapViewController?.presenter
layout = trackLayout
case .trackRecording:
layout = PlacePageTrackRecordingLayout(interactor: interactor, storyboard: storyboard, data: data)
@unknown default:

View File

@@ -7,6 +7,8 @@ class PlacePageInteractor: NSObject {
var presenter: PlacePagePresenterProtocol?
weak var viewController: UIViewController?
weak var mapViewController: MapViewController?
weak var trackActivePointPresenter: TrackActivePointPresenter?
private let bookmarksManager = BookmarksManager.shared()
private var placePageData: PlacePageData
private var viewWillAppearIsCalledForTheFirstTime = false
@@ -17,10 +19,12 @@ class PlacePageInteractor: NSObject {
self.mapViewController = mapViewController
super.init()
addToBookmarksManagerObserverList()
subscribeOnTrackActivePointUpdates()
}
deinit {
removeFromBookmarksManagerObserverList()
unsubscribeFromTrackActivePointUpdates()
}
private func updatePlacePageIfNeeded() {
@@ -49,6 +53,23 @@ class PlacePageInteractor: NSObject {
}
}
private func subscribeOnTrackActivePointUpdates() {
guard placePageData.objectType == .track, let trackData = placePageData.trackData else { return }
bookmarksManager.setElevationActivePointChanged(trackData.trackId) { [weak self] distance in
self?.trackActivePointPresenter?.updateActivePoint(distance)
trackData.updateActivePointDistance(distance)
}
bookmarksManager.setElevationMyPositionChanged(trackData.trackId) { [weak self] distance in
self?.trackActivePointPresenter?.updateMyPosition(distance)
}
}
private func unsubscribeFromTrackActivePointUpdates() {
guard placePageData.objectType == .track, let trackData = placePageData.trackData else { return }
bookmarksManager.resetElevationActivePointChanged()
bookmarksManager.resetElevationMyPositionChanged()
}
private func addToBookmarksManagerObserverList() {
bookmarksManager.add(self)
}
@@ -321,7 +342,8 @@ extension PlacePageInteractor: ElevationProfileViewControllerDelegate {
func updateMapPoint(_ point: CLLocationCoordinate2D, distance: Double) {
guard let trackData = placePageData.trackData, trackData.elevationProfileData?.isTrackRecording == false else { return }
BookmarksManager.shared().setElevationActivePoint(point, distance: distance, trackId: trackData.trackId)
bookmarksManager.setElevationActivePoint(point, distance: distance, trackId: trackData.trackId)
placePageData.trackData?.updateActivePointDistance(distance)
}
}

View File

@@ -43,13 +43,10 @@ class PlacePageTrackLayout: IPlacePageLayout {
}()
lazy var elevationMapViewController: ElevationProfileViewController? = {
guard trackData.trackInfo.hasElevationInfo,
let elevationProfileData = trackData.elevationProfileData else {
guard trackData.trackInfo.hasElevationInfo, trackData.elevationProfileData != nil else {
return nil
}
return ElevationProfileBuilder.build(trackInfo: trackData.trackInfo,
elevationProfileData: elevationProfileData,
delegate: interactor)
return ElevationProfileBuilder.build(trackData: trackData, delegate: interactor)
}()
lazy var actionBarViewController: ActionBarViewController = {

View File

@@ -39,8 +39,7 @@ final class PlacePageTrackRecordingLayout: IPlacePageLayout {
guard let trackData = placePageData.trackData else {
return nil
}
return ElevationProfileBuilder.build(trackInfo: trackData.trackInfo,
elevationProfileData: trackData.elevationProfileData,
return ElevationProfileBuilder.build(trackData: trackData,
delegate: interactor)
}()
@@ -86,9 +85,9 @@ final class PlacePageTrackRecordingLayout: IPlacePageLayout {
private extension PlacePageTrackRecordingLayout {
func updateTrackRecordingRelatedSections() {
guard let elevationProfileViewController, let trackInfo = placePageData.trackData?.trackInfo else { return }
guard let elevationProfileViewController, let trackData = placePageData.trackData else { return }
headerViewController.setTitle(placePageData.previewData.title, secondaryTitle: nil)
elevationProfileViewController.presenter?.update(trackInfo: trackInfo, profileData: placePageData.trackData?.elevationProfileData)
elevationProfileViewController.presenter?.update(with: trackData)
presenter?.layoutIfNeeded()
}
}