Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
a278e11
Add FavoriteListViewController
DmitrySvetlichny Jun 4, 2026
a024943
Context menu Add new folder
DmitrySvetlichny Jun 4, 2026
f993569
toolbar ui
Vladyslav-lys Jun 4, 2026
81bbb9b
Add actionsButton to Toolbar
DmitrySvetlichny Jun 4, 2026
d4208a6
Add FreeBackupBanner
DmitrySvetlichny Jun 5, 2026
66d7407
Merge branch 'master' into update_ui_my_places_favorite_iOS
Vladyslav-lys Jun 5, 2026
a3f51fc
Add stats
DmitrySvetlichny Jun 5, 2026
8aedcf8
Add sort
DmitrySvetlichny Jun 5, 2026
e52c4dd
Merge branch 'master' of https://github.com/osmandapp/OsmAnd-ios into…
DmitrySvetlichny Jun 5, 2026
8b5e64f
Add image to UIAction
DmitrySvetlichny Jun 5, 2026
45abeab
refactored, new wrapper added
Vladyslav-lys Jun 8, 2026
3ecf984
Merge branch 'master' of https://github.com/osmandapp/OsmAnd-ios into…
DmitrySvetlichny Jun 8, 2026
e0db552
fix in FavoriteSortModeHelper and conflicts
DmitrySvetlichny Jun 8, 2026
63a802f
add folder subtitle
DmitrySvetlichny Jun 8, 2026
93fa251
Merge branch 'master' of https://github.com/osmandapp/OsmAnd-ios into…
DmitrySvetlichny Jun 8, 2026
64529f2
toolbar
Vladyslav-lys Jun 9, 2026
1ca7cbb
Merge branch 'update_ui_my_places_favorite_iOS_toolbar' into update_u…
Vladyslav-lys Jun 9, 2026
0a53b49
Merge branch 'master' into update_ui_my_places_favorite_iOS
Vladyslav-lys Jun 9, 2026
024c7a9
Merge branch 'master' of https://github.com/osmandapp/OsmAnd-ios into…
DmitrySvetlichny Jun 9, 2026
3bc75bf
Add Folder menu
DmitrySvetlichny Jun 9, 2026
79be4fb
Del menuImage
DmitrySvetlichny Jun 9, 2026
2e5baf0
empty states
Vladyslav-lys Jun 9, 2026
41e5a83
resolved conflicts
Vladyslav-lys Jun 9, 2026
c52b500
resolved conflicts
Vladyslav-lys Jun 9, 2026
c821614
Add Folder menu fix
DmitrySvetlichny Jun 9, 2026
d602207
Update FavoriteListViewController.swift
Vladyslav-lys Jun 10, 2026
e4b1fb1
Add selected title
DmitrySvetlichny Jun 10, 2026
732bd07
Add Search
DmitrySvetlichny Jun 10, 2026
bc2e72d
Merge branch 'master' into update_ui_my_places_favorite_iOS
Vladyslav-lys Jun 11, 2026
e1ae24c
Merge branch 'master' into update_ui_my_places_favorite_iOS
Vladyslav-lys Jun 11, 2026
00d0c87
Sort Folders fix
DmitrySvetlichny Jun 11, 2026
5567c9b
Import fix
DmitrySvetlichny Jun 11, 2026
c1297ee
updated favorite point
Vladyslav-lys Jun 11, 2026
3f2b6b8
OAFavoriteFoldersBridge del
DmitrySvetlichny Jun 12, 2026
7a56765
updating list if context menu open fixed
Vladyslav-lys Jun 12, 2026
b05e83f
Merge branch 'master' into update_ui_my_places_favorite_iOS
Vladyslav-lys Jun 12, 2026
4e0dea1
updating toolbar, import fixed, old my places removed
Vladyslav-lys Jun 12, 2026
63d7ae0
setFavoriteGroupVisible fix
DmitrySvetlichny Jun 12, 2026
b474350
refactored
Vladyslav-lys Jun 12, 2026
b2b338f
refactored
Vladyslav-lys Jun 12, 2026
d3f0ec9
Add FavoriteListCell
DmitrySvetlichny Jun 15, 2026
96c6105
refactored
Vladyslav-lys Jun 15, 2026
3697378
Merge branch 'master' into update_ui_my_places_favorite_iOS
Vladyslav-lys Jun 15, 2026
1211301
refactored
Vladyslav-lys Jun 15, 2026
8f667d7
two line name and selection mode context menu fixed
Vladyslav-lys Jun 15, 2026
12210e8
fixed comments
Vladyslav-lys Jun 16, 2026
3ea1892
refactored
Vladyslav-lys Jun 16, 2026
bff4ae1
refactored
Vladyslav-lys Jun 17, 2026
8ec9f8b
Merge branch 'master' into update_ui_my_places_favorite_iOS
Vladyslav-lys Jun 17, 2026
f13c435
refactored
Vladyslav-lys Jun 17, 2026
b11977e
refactored
Vladyslav-lys Jun 17, 2026
8e04af2
fixed scaled font
Vladyslav-lys Jun 18, 2026
6df35f4
Merge branch 'master' into update_ui_my_places_favorite_iOS
Vladyslav-lys Jun 18, 2026
c8d6683
resolved conflicts
Vladyslav-lys Jun 24, 2026
d4afca3
not adding points from subfolders fixed
Vladyslav-lys Jun 26, 2026
34e26ba
expensive distance updates fixed
Vladyslav-lys Jun 26, 2026
a7b2c3a
favoriteDataDidChange not in main thread fixed
Vladyslav-lys Jun 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 74 additions & 10 deletions OsmAnd.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions Resources/Localizations/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,8 @@
"shared_string_subfolders_in" = "Subfolders in";
"shared_string_sorted_by" = "is sorted by:";
"shared_string_nearest" = "Nearest";
"distance_nearest" = "Nearest first";
"distance_farthest" = "Farthest first";
"track_sort_az" = "Name A - Z";
"track_sort_za" = "Name Z - A";
"newest_date_first" = "Newest date first";
Expand Down Expand Up @@ -1009,6 +1011,14 @@
"hud_no_movement" = "No movement";

// Favorites
"shared_string_pinned" = "Pinned";
"delete_groups_confirmation" = "Delete groups?";
"add_to_track" = "Add to track";
"unpin_folder" = "Unpin folder";
"pin_folder" = "Pin folder";
"no_search_results" = "No results";
"favorite_search_empty_state_description" = "Adjust your search or filters to see if that helps";
"favorites_empty_folder_description" = "This folder doesn\’t have any points yet.";
"shared_string_color" = "Color";
"fav_colors" = "Colors";
"shared_string_name" = "Name";
Expand Down Expand Up @@ -1057,6 +1067,16 @@
"fav_point_emoticons_message" = "Favorite renamed to \'%@\' to save the string containing emoticons to a file.";
"access_default_color" = "Default color";
"default_color_descr" = "This color wil be used for all new favorites added to the group.";
"favorites_delete_confirmation_title" = "Delete %ld favorites?";
"items_delete_confirmation_title" = "Delete %ld items?";
"folders_delete_confirmation_title" = "Delete %ld folders?";
"favorites_delete_confirmation_message" = "This action cannot be undone.";
"mixed_delete_confirmation_message" = "This will delete %ld folders and %ld points. This action cannot be undone.";
"empty_state_favourites" = "Add Favorites";
"empty_state_favourites_desc" = "Import Favorites or add them by marking points on the map.";
"tracks_empty_folder" = "Empty folder";
"tracks_empty_folder_description" = "This folder doesn’t have any points yet.";
"delete_favorite_confirmation_title" = "Delete \"%@\"?";

"favorite_friends_category" = "Friends";
"favorite_places_category" = "Places";
Expand Down Expand Up @@ -1676,6 +1696,7 @@
"change_folder" = "Change folder";
"select_folder" = "Select folder";
"select_folder_descr" = "Select folder or add new one";
"add_new_folder" = "Add new folder";
"add_folder" = "Add folder";
"add_smart_folder" = "Add smart folder";
"save_as_smart_folder" = "Save as smart folder";
Expand Down Expand Up @@ -4123,13 +4144,16 @@
"original_icon_description" = "Each point retains its individual icon.";
"original_color_description" = "Each point retains its individual color.";
"original_shape_description" = "Each point retains its individual shape.";
"add_to" = "Add to";

"my_places_no_tracks_title_root" = "You don’t have track files";
"my_places_no_tracks_descr_root" = "You can import, create or record track files using OsmAnd.";
"my_places_no_tracks_title" = "Empty folder";
"my_places_no_tracks_descr" = "This folder doesn’t have any track yet.";
"delete_folder" = "Delete folder";
"favorite_confirm_delete_group" = "Delete \"%1$@\" group and all included points (%2$lu)?";
"root_folder" = "Root folder";
"shared_string_folders" = "Folders";
"all_folders" = "All folders";
"copy_as_new_folder" = "Copy as new folder";
"add_to_a_folder" = "Add to a folder";
Expand Down
68 changes: 68 additions & 0 deletions Sources/Controllers/Cells/BackupBannerCollectionViewCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// BackupBannerCollectionViewCell.swift
// OsmAnd Maps
//
// Created by Vladyslav Lysenko on 17.06.2026.
// Copyright © 2026 OsmAnd. All rights reserved.
//

protocol BackupBannerCollectionViewCellDelegate: AnyObject {
func didClose()
func didOpenOsmAndCloud()
func backupBannerHeight(_ banner: FreeBackupBanner, fittingWidth: CGFloat) -> CGFloat
}

final class BackupBannerCollectionViewCell: UICollectionViewCell {
weak var delegate: BackupBannerCollectionViewCellDelegate? {
didSet {
updateBannerLayout()
}
}

private var heightConstraint: NSLayoutConstraint?

private lazy var banner: FreeBackupBanner? = {
guard let banner = Bundle.main.loadNibNamed(FreeBackupBanner.reuseIdentifier, owner: self)?.first as? FreeBackupBanner else { return nil }
banner.configure(bannerType: .favorite)
banner.translatesAutoresizingMaskIntoConstraints = false
return banner
}()

override init(frame: CGRect) {
super.init(frame: frame)
setupBanner()
}

required init?(coder: NSCoder) {
super.init(coder: coder)
}

private func setupBanner() {
guard let banner else { return }
contentView.addSubview(banner)

NSLayoutConstraint.activate([
banner.topAnchor.constraint(equalTo: contentView.topAnchor),
banner.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
banner.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
banner.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])

heightConstraint = banner.heightAnchor.constraint(equalToConstant: banner.frame.height)
heightConstraint?.isActive = true
}

private func updateBannerLayout() {
guard let banner else { return }
banner.didOsmAndCloudButtonAction = { [weak self] in
self?.delegate?.didOpenOsmAndCloud()
}
banner.didCloseButtonAction = { [weak self] in
self?.delegate?.didClose()
}

let fittingWidth = contentView.bounds.width > 0.0 ? contentView.bounds.width : bounds.width
let bannerHeight = delegate?.backupBannerHeight(banner, fittingWidth: fittingWidth) ?? banner.frame.height
heightConstraint?.constant = bannerHeight
}
}
21 changes: 21 additions & 0 deletions Sources/Controllers/Cells/EmptyStateCollectionViewCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// EmptyStateCollectionViewCell.swift
// OsmAnd Maps
//
// Created by Vladyslav Lysenko on 09.06.2026.
// Copyright © 2026 OsmAnd. All rights reserved.
//

final class EmptyStateCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var button: UIButton!
@IBOutlet private weak var cellImageView: UIImageView!
@IBOutlet private weak var titleLabel: UILabel!
@IBOutlet private weak var descriptionLabel: UILabel!

func configure(image: UIImage, title: String, description: String) {
cellImageView.image = image
cellImageView.tintColor = .iconColorDefault
titleLabel.text = title
descriptionLabel.text = description
}
}
37 changes: 37 additions & 0 deletions Sources/Controllers/Cells/StatsFooterCollectionViewCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// StatsFooterCollectionViewCell.swift
// OsmAnd Maps
//
// Created by Vladyslav Lysenko on 17.06.2026.
// Copyright © 2026 OsmAnd. All rights reserved.
//

final class StatsFooterCollectionViewCell: UICollectionViewCell {
private static let statsFooterInsets = NSDirectionalEdgeInsets(top: 12.0, leading: 20.0, bottom: 12.0, trailing: 20.0)

lazy var label: UILabel = {
let label = UILabel()
label.font = .preferredFont(forTextStyle: .footnote)
label.adjustsFontForContentSizeCategory = true
label.textColor = .textColorSecondary
label.textAlignment = .center
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()

override init(frame: CGRect) {
super.init(frame: frame)
setupLabel()
}

required init?(coder: NSCoder) {
super.init(coder: coder)
}

private func setupLabel() {
contentView.addSubview(label)

NSLayoutConstraint.activate([label.topAnchor.constraint(equalTo: contentView.topAnchor, constant: Self.statsFooterInsets.top), label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: Self.statsFooterInsets.leading), label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Self.statsFooterInsets.trailing), label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -Self.statsFooterInsets.bottom)])
}
}
104 changes: 104 additions & 0 deletions Sources/Controllers/Cells/Xibs/EmptyStateCollectionViewCell.xib
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="24765" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24743"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="EmptyStateCollectionViewCell" customModule="OsmAnd_Maps" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="417" height="247"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="417" height="247"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalCompressionResistancePriority="751" text="Label" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jGg-TU-lOT">
<rect key="frame" x="30" y="145" width="357" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" name="textColorSecondary"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hVD-cm-iau">
<rect key="frame" x="30" y="120" width="357" height="19"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" name="textColorPrimary"/>
<nil key="highlightedColor"/>
</label>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="OjP-rq-aR6">
<rect key="frame" x="178.66666666666666" y="30" width="60" height="60"/>
<constraints>
<constraint firstAttribute="height" constant="60" id="NdD-pT-nVe"/>
<constraint firstAttribute="width" constant="60" id="SwJ-Ry-OFs"/>
</constraints>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Uv7-Qe-aCC">
<rect key="frame" x="30" y="183" width="357" height="44"/>
<color key="backgroundColor" name="buttonBgColorTertiary"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="44" id="myJ-ng-E4T"/>
</constraints>
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
<color key="tintColor" name="buttonTextColorSecondary"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Button">
<color key="titleColor" name="buttonTextColorSecondary"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="9"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</button>
</subviews>
</view>
<viewLayoutGuide key="safeArea" id="Dhz-Ls-gHt"/>
<color key="backgroundColor" name="groupBg"/>
<constraints>
<constraint firstItem="jGg-TU-lOT" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leadingMargin" constant="10" id="2yn-g4-D2K"/>
<constraint firstItem="jGg-TU-lOT" firstAttribute="top" secondItem="hVD-cm-iau" secondAttribute="bottom" constant="6" id="76z-YL-V4W"/>
<constraint firstAttribute="bottom" secondItem="Uv7-Qe-aCC" secondAttribute="bottom" constant="20" id="7yK-ks-mJh"/>
<constraint firstAttribute="trailingMargin" secondItem="Uv7-Qe-aCC" secondAttribute="trailing" constant="10" id="CUQ-pc-nsa"/>
<constraint firstItem="hVD-cm-iau" firstAttribute="top" secondItem="OjP-rq-aR6" secondAttribute="bottom" constant="30" id="Hqe-sE-7FK"/>
<constraint firstItem="Uv7-Qe-aCC" firstAttribute="top" secondItem="jGg-TU-lOT" secondAttribute="bottom" constant="20" id="PhW-zN-orB"/>
<constraint firstItem="OjP-rq-aR6" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" constant="30" id="VBI-48-zOK"/>
<constraint firstItem="OjP-rq-aR6" firstAttribute="centerX" secondItem="gTV-IL-0wX" secondAttribute="centerX" id="YbB-1I-MqD"/>
<constraint firstItem="Uv7-Qe-aCC" firstAttribute="centerX" secondItem="gTV-IL-0wX" secondAttribute="centerX" id="kBS-xb-YSG"/>
<constraint firstItem="Uv7-Qe-aCC" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leadingMargin" constant="10" id="loF-WJ-qaw"/>
<constraint firstAttribute="trailingMargin" secondItem="hVD-cm-iau" secondAttribute="trailing" constant="10" id="oVM-dg-oTH"/>
<constraint firstItem="hVD-cm-iau" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leadingMargin" constant="10" id="qgJ-31-ZB0"/>
<constraint firstAttribute="trailingMargin" secondItem="jGg-TU-lOT" secondAttribute="trailing" constant="10" id="u0X-pX-MkK"/>
</constraints>
<size key="customSize" width="417" height="247"/>
<connections>
<outlet property="button" destination="Uv7-Qe-aCC" id="yGl-Ch-uRi"/>
<outlet property="cellImageView" destination="OjP-rq-aR6" id="VSP-HZ-OcU"/>
<outlet property="descriptionLabel" destination="jGg-TU-lOT" id="3L1-PE-2Hh"/>
<outlet property="titleLabel" destination="hVD-cm-iau" id="vtI-ng-FdT"/>
</connections>
<point key="canvasLocation" x="280.15267175572518" y="139.08450704225353"/>
</collectionViewCell>
</objects>
<resources>
<namedColor name="buttonBgColorTertiary">
<color red="0.88627450980392153" green="0.85098039215686272" blue="0.94901960784313721" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="buttonTextColorSecondary">
<color red="0.3411764705882353" green="0.078431372549019607" blue="0.80000000000000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="groupBg">
<color red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="textColorPrimary">
<color red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="textColorSecondary">
<color red="0.49019607843137253" green="0.45098039215686275" blue="0.5490196078431373" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>
Loading