forked from organicmaps/organicmaps
Compare commits
12 commits
master
...
ios/route-
Author | SHA1 | Date | |
---|---|---|---|
b07a645d86 | |||
a52b51cb58 | |||
07f90e3df3 | |||
d714c975b1 | |||
a6b4d2712c | |||
695cec4f1a | |||
d0d84a5dd5 | |||
2ebf6a5397 | |||
f05e5e5ed6 | |||
af8a07d430 | |||
fd5feb2727 | |||
2d3c5b25a5 |
90 changed files with 1281 additions and 2684 deletions
|
@ -167,8 +167,9 @@ Please join our beta program, suggest your features, and report bugs:
|
|||
|
||||
- **Rate us on the [App Store](https://apps.apple.com/app/organic-maps/id1567437057)
|
||||
and [Google Play](https://play.google.com/store/apps/details?id=app.organicmaps)**.
|
||||
- **Star us on Forgejo**.
|
||||
- Report bugs or issues to [the issue tracker](https://git.omaps.dev/organicmaps/organicmaps/issues).
|
||||
- **Star us on GitHub**.
|
||||
- Report bugs or issues to [the issue tracker](https://github.com/organicmaps/organicmaps/issues).
|
||||
- [Discuss](https://github.com/organicmaps/organicmaps/discussions/categories/ideas) ideas or propose feature requests.
|
||||
- Subscribe to our [Telegram Channel](https://t.me/OrganicMapsApp) or to the [[matrix] space](https://matrix.to/#/#organicmaps:matrix.org) for updates.
|
||||
- Join our [Telegram Group](https://t.me/OrganicMaps) to discuss with other users.
|
||||
- Присоединяйтесь к нашей [русскоязычной группе в Telegram](https://t.me/OrganicMapsRu) для обратной связи и помощи.
|
||||
|
@ -178,7 +179,7 @@ and [Google Play](https://play.google.com/store/apps/details?id=app.organicmaps)
|
|||
- Follow our updates in
|
||||
[Mastodon](https://fosstodon.org/@organicmaps),
|
||||
[Facebook](https://facebook.com/OrganicMaps),
|
||||
[X (Twitter)](https://x.com/OrganicMapsApp),
|
||||
[Twitter](https://twitter.com/OrganicMapsApp),
|
||||
[Instagram](https://instagram.com/organicmaps.app/).
|
||||
- Güncellemelerimizi [Instagram](https://instagram.com/organicmapstr/) üzerinden takip edin.
|
||||
|
||||
|
|
|
@ -144,8 +144,4 @@
|
|||
<string name="stop_without_saving">Parar ensin guardar</string>
|
||||
<string name="continue_recording">Siguir grabando</string>
|
||||
<string name="stop_track_recording">Parar grabación del trayeutu</string>
|
||||
<string name="downloader_no_space_message">Desanicia los datos imprecisos</string>
|
||||
<string name="editor_osm_history">El to historial d’ediciones</string>
|
||||
<string name="m">m</string>
|
||||
<string name="download_resources_continue">Dir al mapa</string>
|
||||
</resources>
|
||||
|
|
|
@ -186,7 +186,7 @@
|
|||
<!-- resource for context menu -->
|
||||
<string name="edit">Edita</string>
|
||||
<!-- Warning message when doing search around current position -->
|
||||
<string name="unknown_current_position">Encara no s’ha determinat la vostra ubicació</string>
|
||||
<string name="unknown_current_position">Encara no s\'ha pogut determinar la vostra geolocalització</string>
|
||||
<!-- Alert message that we can't run Map Storage settings due to some reasons. -->
|
||||
<string name="cant_change_this_setting">La configuració de l\'emmagatzematge del mapes està desactivada.</string>
|
||||
<!-- Alert message that downloading is in progress. -->
|
||||
|
@ -528,7 +528,7 @@
|
|||
<!-- Information about OSM at the top of the editing page -->
|
||||
<string name="editor_about_osm">Les vostres edicions es pengen a la base de dades pública <a href="https://wiki.openstreetmap.org/wiki/Ca:About">OpenStreetMap</a>. Si us plau, no afegiu informació personal o amb drets d\'autor.</string>
|
||||
<string name="editor_more_about_osm">Més sobre l\'OpenStreetMap</string>
|
||||
<string name="editor_osm_history">El vostre historial d’edicions</string>
|
||||
<string name="editor_osm_history">El teu historial d\'edició</string>
|
||||
<string name="editor_osm_notes">Notes de les vostres dades del mapa</string>
|
||||
<string name="editor_operator">Operador</string>
|
||||
<!-- To indicate the operator of ATMs, bicycle rentals, electric vehicle charging stations... -->
|
||||
|
@ -604,7 +604,7 @@
|
|||
<string name="mobile_data_option_ask">Demana-ho sempre</string>
|
||||
<string name="traffic_update_maps_text">Per a mostrar les dades de trànsit, els maps han d\'estar actualitzats.</string>
|
||||
<string name="big_font">Augmenta la mida de lletra del mapa</string>
|
||||
<string name="traffic_update_app">Actualitzeu l’Organic Maps</string>
|
||||
<string name="traffic_update_app">Actualitzeu l\'Organic Maps</string>
|
||||
<!-- "traffic" as in "road congestion" -->
|
||||
<string name="traffic_data_unavailable">No hi ha dades de trànsit disponibles</string>
|
||||
<string name="enable_logging">Activa el registre</string>
|
||||
|
@ -832,7 +832,7 @@
|
|||
<!-- Displayed on the phone screen. Android Auto connected -->
|
||||
<string name="aa_connected_title">Us heu connectat a Android Auto</string>
|
||||
<!-- Displayed on the phone screen. Button to display maps on the phone screen instead of a car -->
|
||||
<string name="car_continue_on_the_phone">Passa al telèfon</string>
|
||||
<string name="car_continue_on_the_phone">Continua al telèfon</string>
|
||||
<!-- Displayed on the Android Auto or CarPlay screen. Button to display maps on the car screen instead of a phone. Must be no more than 18 symbols! -->
|
||||
<string name="car_continue_in_the_car">A la pantalla del cotxe</string>
|
||||
<!-- Ask user to grant location permissions -->
|
||||
|
@ -903,7 +903,4 @@
|
|||
<string name="mb">MB</string>
|
||||
<string name="gb">GB</string>
|
||||
<string name="login_osm">Inicia sessió a l’OpenStreetMap</string>
|
||||
<string name="twitter">X (Twitter)</string>
|
||||
<string name="wikimedia_commons">Wikimedia Commons</string>
|
||||
<string name="matrix">[Matrix]</string>
|
||||
</resources>
|
||||
|
|
|
@ -444,7 +444,7 @@
|
|||
<string name="type.self_service.partially">Autoservei parcial</string>
|
||||
<string name="type.self_service.no">Sense autoservei</string>
|
||||
<!-- https://wiki.openstreetmap.org/wiki/Key:social_facility -->
|
||||
<string name="type.amenity.social_facility">Servei social</string>
|
||||
<string name="type.amenity.social_facility">Equipament Social</string>
|
||||
<!-- https://wiki.openstreetmap.org/wiki/Tag:emergency=emergency_ward_entrance -->
|
||||
<string name="type.emergency.emergency_ward_entrance">Entrada d\'urgències</string>
|
||||
<!-- https://wiki.openstreetmap.org/wiki/Tag:amenity=dojo -->
|
||||
|
@ -850,66 +850,4 @@
|
|||
<string name="type.leisure.track">Sender</string>
|
||||
<string name="type.leisure.track.area">Sender</string>
|
||||
<string name="type.leisure.water_park">Parc aquàtic</string>
|
||||
<string name="type.sport.baseball">Beisbol</string>
|
||||
<string name="type.sport.basketball">Basquetbol</string>
|
||||
<string name="type.sport.beachvolleyball">Voleibol de platja</string>
|
||||
<string name="type.sport.gymnastics">Gimnàstica</string>
|
||||
<string name="type.sport.multi">Diversos esports</string>
|
||||
<string name="type.cuisine.local">Local</string>
|
||||
<string name="type.boundary.administrative.2">Frontera nacional</string>
|
||||
<string name="type.cuisine.filipino">Filipina</string>
|
||||
<string name="type.cuisine.hungarian">Hongaresa</string>
|
||||
<string name="type.cuisine.japanese">Japonesa</string>
|
||||
<string name="type.cuisine.lebanese">Libanesa</string>
|
||||
<string name="type.cuisine.lao">Laosiana</string>
|
||||
<string name="type.cuisine.korean">Coreana</string>
|
||||
<string name="type.cuisine.moroccan">Marroquina</string>
|
||||
<string name="type.cuisine.regional">Regional</string>
|
||||
<string name="type.landuse.forest.deciduous">Bosc de caducifòlies</string>
|
||||
<string name="type.power.substation">Subestació</string>
|
||||
<string name="type.cuisine.portuguese">Portuguesa</string>
|
||||
<string name="type.cuisine.polish">Polonesa</string>
|
||||
<string name="type.cuisine.french">Francesa</string>
|
||||
<string name="type.cuisine.russian">Rusa</string>
|
||||
<string name="type.landuse.forest.coniferous">Bosc de coníferes</string>
|
||||
<string name="type.landuse.field">Camp</string>
|
||||
<string name="type.man_made.works">Fàbrica</string>
|
||||
<string name="type.cuisine.georgian">Georgiana</string>
|
||||
<string name="type.cuisine.german">Alemanya</string>
|
||||
<string name="type.cuisine.greek">Grega</string>
|
||||
<string name="type.healthcare.blood_donation">Centre de donació de sang</string>
|
||||
<string name="type.military.bunker">Búnquer</string>
|
||||
<string name="type.natural.grassland">Herbassar</string>
|
||||
<string name="type.natural.cliff">Penya-segat</string>
|
||||
<string name="type.natural.earth_bank">Talús de terra</string>
|
||||
<string name="type.natural.land">Terreny</string>
|
||||
<string name="type.natural.meadow">Prat de dall</string>
|
||||
<string name="type.natural.heath">Landa</string>
|
||||
<string name="type.amenity.vending_machine.condoms">Dispensador de preservatius</string>
|
||||
<string name="type.cuisine.balkan">Balcànica</string>
|
||||
<string name="type.cuisine.ice_cream">Gelat</string>
|
||||
<string name="type.cuisine.indian">Índia</string>
|
||||
<string name="type.cuisine.grill">Graellada</string>
|
||||
<string name="type.healthcare.psychotherapist">Psicoterapeuta</string>
|
||||
<string name="type.healthcare.podiatrist">Podòleg</string>
|
||||
<string name="type.natural.coastline">Costa</string>
|
||||
<string name="type.natural.water.lock">Resclosa</string>
|
||||
<string name="type.natural.water.pond">Estany</string>
|
||||
<string name="type.natural.water.reservoir">Embassament</string>
|
||||
<string name="type.natural.strait">Estret</string>
|
||||
<string name="type.natural.orchard">Verger</string>
|
||||
<string name="type.noexit">Atzucac</string>
|
||||
<string name="type.natural.wetland">Zona humida</string>
|
||||
<string name="type.natural.wetland.bog">Torbera</string>
|
||||
<string name="type.natural.wetland.marsh">Aiguamoll</string>
|
||||
<string name="type.natural.tree_row">Fila d’arbres</string>
|
||||
<string name="type.natural.vineyard">Vinyar</string>
|
||||
<string name="type.highway.pedestrian">Carrer de vianants</string>
|
||||
<string name="type.highway.pedestrian.area">Àrea de vianants</string>
|
||||
<string name="type.landuse.reservoir">Embassament</string>
|
||||
<string name="type.landuse.orchard">Verger</string>
|
||||
<string name="type.landuse.meadow">Prat de dall</string>
|
||||
<string name="type.highway.pedestrian.tunnel">Túnel de vianants</string>
|
||||
<string name="type.cuisine.turkish">Turca</string>
|
||||
<string name="type.highway.pedestrian.bridge">Pont de vianants</string>
|
||||
</resources>
|
||||
|
|
|
@ -875,7 +875,7 @@
|
|||
<!-- Prompt for stopping a track recording. -->
|
||||
<string name="stop_track_recording">Aufnahme des Tracks stoppen</string>
|
||||
<!-- Title for the "Stop Without Saving" action for the alert when saving a track recording. -->
|
||||
<string name="stop_without_saving">Ohne Speichern beenden</string>
|
||||
<string name="stop_without_saving">Anhalten ohne zu speichern</string>
|
||||
<!-- Title for the "Stop Without Saving" action for the alert when saving a track recording. -->
|
||||
<string name="continue_recording">Aufnahme fortsetzen</string>
|
||||
<!-- Title for the alert when saving a track recording. -->
|
||||
|
|
|
@ -248,7 +248,7 @@
|
|||
<string name="type.craft.winery">Kellerei</string>
|
||||
<string name="type.craft.tailor">Schneider</string>
|
||||
<string name="type.cuisine.african">Afrikanisch</string>
|
||||
<string name="type.cuisine.american">US-amerikanisch</string>
|
||||
<string name="type.cuisine.american">Amerikanisch</string>
|
||||
<string name="type.cuisine.arab">Arabisch</string>
|
||||
<string name="type.cuisine.argentinian">Argentinisch</string>
|
||||
<string name="type.cuisine.asian">Asiatisch</string>
|
||||
|
|
|
@ -523,7 +523,7 @@
|
|||
<!-- Information about OSM at the top of the editing page -->
|
||||
<string name="editor_about_osm">Sus ediciones se cargan en la base de datos pública <a href="https://wiki.openstreetmap.org/wiki/ES:Acerca_de_OpenStreetMap">OpenStreetMap</a>. Por favor, no añada información personal o protegida por derechos de autor.</string>
|
||||
<string name="editor_more_about_osm">Más acerca de OpenStreetMap</string>
|
||||
<string name="editor_osm_history">Su historial de ediciones</string>
|
||||
<string name="editor_osm_history">Mi historial de edición</string>
|
||||
<string name="editor_osm_notes">Mis notas de datos cartográficos</string>
|
||||
<string name="editor_operator">Operador</string>
|
||||
<!-- To indicate the operator of ATMs, bicycle rentals, electric vehicle charging stations... -->
|
||||
|
@ -830,7 +830,7 @@
|
|||
<!-- Displayed on the phone screen. Android Auto connected -->
|
||||
<string name="aa_connected_title">Se ha conectado a Android Auto</string>
|
||||
<!-- Displayed on the phone screen. Button to display maps on the phone screen instead of a car -->
|
||||
<string name="car_continue_on_the_phone">Pasar al teléfono</string>
|
||||
<string name="car_continue_on_the_phone">Continuar en el teléfono</string>
|
||||
<!-- Displayed on the Android Auto or CarPlay screen. Button to display maps on the car screen instead of a phone. Must be no more than 18 symbols! -->
|
||||
<string name="car_continue_in_the_car">A la pantalla del coche</string>
|
||||
<!-- Ask user to grant location permissions -->
|
||||
|
|
|
@ -368,7 +368,7 @@
|
|||
<string name="type.healthcare.blood_donation">Centro de donación de sangre</string>
|
||||
<string name="type.healthcare.optometrist">Optometría</string>
|
||||
<string name="type.healthcare.podiatrist">Podología</string>
|
||||
<string name="type.healthcare.psychotherapist">Psicoterapeuta</string>
|
||||
<string name="type.healthcare.psychotherapist">Psicoterapia</string>
|
||||
<string name="type.healthcare.sample_collection">Muestreo</string>
|
||||
<string name="type.healthcare.speech_therapist">Logopedia</string>
|
||||
<!-- SECTION: Types: Roads -->
|
||||
|
@ -708,7 +708,7 @@
|
|||
<string name="type.natural.cape">Cabo</string>
|
||||
<string name="type.natural.cave_entrance">Cueva</string>
|
||||
<string name="type.natural.cliff">Acantilado</string>
|
||||
<string name="type.natural.earth_bank">Talud de tierra</string>
|
||||
<string name="type.natural.earth_bank">Acantilado</string>
|
||||
<string name="type.man_made.embankment">Terraplén</string>
|
||||
<string name="type.natural.coastline">Costa</string>
|
||||
<string name="type.natural.desert">Desierto</string>
|
||||
|
@ -718,7 +718,7 @@
|
|||
<string name="type.natural.heath">Brezal</string>
|
||||
<string name="type.natural.hot_spring">Aguas termales</string>
|
||||
<string name="type.natural.water.lake">Lago</string>
|
||||
<string name="type.natural.water.lock">Esclusa</string>
|
||||
<string name="type.natural.water.lock">Cámara de bloqueo</string>
|
||||
<string name="type.natural.water.pond">Estanque</string>
|
||||
<string name="type.natural.water.reservoir">Embalse</string>
|
||||
<string name="type.natural.water.basin">Cuenca</string>
|
||||
|
|
|
@ -117,6 +117,7 @@
|
|||
<string name="measurement_units">Unités de mesure</string>
|
||||
<!-- Detailed description of Measurement Units settings button -->
|
||||
<string name="measurement_units_summary">Choisir entre miles et kilomètres</string>
|
||||
|
||||
<!-- SECTION: Search categories -->
|
||||
<!-- Search category for cafes, bars, restaurants; any changes should be duplicated in categories.txt @category_eat! -->
|
||||
<string name="category_eat">Un endroit pour manger</string>
|
||||
|
@ -164,6 +165,7 @@
|
|||
<string name="category_water">Eau</string>
|
||||
<!-- Search category for RV facilities; any changes should be duplicated in categories.txt @category_rv! -->
|
||||
<string name="category_rv">Aménagements pour camping-car</string>
|
||||
|
||||
<!-- SECTION: Other translations -->
|
||||
<!-- Notes field in Bookmarks view -->
|
||||
<string name="description">Notes</string>
|
||||
|
@ -368,9 +370,10 @@
|
|||
<string name="gray">Gris</string>
|
||||
<!-- blue gray color -->
|
||||
<string name="blue_gray">Gris-bleu</string>
|
||||
|
||||
<!-- SECTION: Routing dialogs strings -->
|
||||
<string name="dialog_routing_disclaimer_title">Lorsque vous suivez l\'itinéraire, gardez à l\'esprit les points suivants :</string>
|
||||
<string name="dialog_routing_disclaimer_priority">— Les conditions de circulation, le code de la route et les panneaux de signalisation ont la priorité sur l\'appareil de navigation ;</string>
|
||||
<string name="dialog_routing_disclaimer_priority">— Les conditions de circulation, le code de la route et les panneaux de signalisation ont la priorité sur l\'appareil de navigation ;</string>
|
||||
<string name="dialog_routing_disclaimer_precision">— La carte peut être imprécise et l\'itinéraire proposé n\'est pas forcément le plus direct pour arriver à destination ;</string>
|
||||
<string name="dialog_routing_disclaimer_recommendations">— L\'itinéraire proposé doit être considéré comme une simple recommandation ;</string>
|
||||
<string name="dialog_routing_disclaimer_borders">— Faites attention aux itinéraires traversant des zones frontalières : les itinéraires générés par l\'application peuvent parfois franchir des frontières étatiques dans des zones interdites ;</string>
|
||||
|
@ -397,6 +400,7 @@
|
|||
<string name="not_now">Pas maintenant</string>
|
||||
<string name="dialog_routing_download_and_build_cross_route">Voulez-vous télécharger la carte et créer un itinéraire plus direct s\'étendant sur plus d\'une carte ?</string>
|
||||
<string name="dialog_routing_download_cross_route">Téléchargez des cartes pour créer un itinéraire plus direct sortant des limites de cette carte.</string>
|
||||
|
||||
<!-- SECTION: Strings for downloading map from search -->
|
||||
<string name="search_without_internet_advertisement">Pour commencer à rechercher et à créer des itinéraires, veuillez télécharger la carte. Après cela, vous n\'aurez plus besoin d\'une connexion Internet.</string>
|
||||
<string name="search_select_map">Sélectionner la carte</string>
|
||||
|
@ -539,7 +543,7 @@
|
|||
<string name="editor_osm_notes">Vos notes sur les données cartographiques</string>
|
||||
<string name="editor_operator">Opérateur</string>
|
||||
<!-- To indicate the operator of ATMs, bicycle rentals, electric vehicle charging stations... -->
|
||||
<string name="operator">Opérateur : %s</string>
|
||||
<string name="operator">Opérateur : %s</string>
|
||||
<string name="editor_category_unsuitable_title">Tu ne trouves pas de catégorie appropriée ?</string>
|
||||
<string name="editor_category_unsuitable_text">Organic Maps ne permet d\'ajouter que des catégories de points simples, c\'est-à-dire pas de villes, de routes, de lacs, de contours de bâtiments, etc. Merci d\'ajouter ces catégories directement sur <a href="https://www.openstreetmap.org">OpenStreetMap.org</a>. Consulte notre <a href="https://organicmaps.app/faq/editing/advanced-map-editing">guide</a> pour obtenir des instructions détaillées étape par étape.</string>
|
||||
<string name="downloader_no_downloaded_maps_title">Vous n\'avez téléchargé aucune carte</string>
|
||||
|
@ -617,9 +621,9 @@
|
|||
<!-- Settings: "Send general feedback" button -->
|
||||
<string name="feedback_general">Feedback général</string>
|
||||
<string name="prefs_languages_information">Nous utilisons le système TTS pour les instructions vocales. De nombreux appareils Android utilisent Google TTS, vous pouvez le télécharger ou le mettre à jour depuis Google Play (https://play.google.com/store/apps/details?id=com.google.android.tts)</string>
|
||||
<string name="prefs_languages_information_off">Pour certaines langues, il vous faudra installer un autre logiciel de synthèse vocale ou un pack de langue supplémentaire depuis l’app store (Google Play, Galaxy Store, App Gallery, FDroid). \nOuvrez les paramètres de votre appareil → Langue et saisie → Reconnaissance vocale → Saisie vocale. \nIci, vous pouvez gérer les paramètres pour la synthèse vocale (par exemple, télécharger un pack de langue pour une utilisation en mode hors ligne) et sélectionner un autre moteur de saisie vocale.</string>
|
||||
<string name="prefs_languages_information_off">Pour certaines langues, il vous faudra installer un autre logiciel de synthèse vocale ou un pack de langue supplémentaire depuis l’app store (Google Play, Galaxy Store, App Gallery, FDroid). Ouvrez les paramètres de votre appareil → Langue et saisie → Reconnaissance vocale → Saisie vocale. Ici, vous pouvez gérer les paramètres pour la synthèse vocale (par exemple, télécharger un pack de langue pour une utilisation en mode hors ligne) et sélectionner un autre moteur de saisie vocale.</string>
|
||||
<string name="prefs_languages_information_off_link">Pour plus d’informations, veuillez consulter ce guide.</string>
|
||||
<string name="transliteration_title">Translittératisé en alphabet latin</string>
|
||||
<string name="transliteration_title">Translittération en latin</string>
|
||||
<string name="learn_more">En savoir plus</string>
|
||||
<!-- Subway exits for public transport marks on the map -->
|
||||
<string name="core_exit">Sortie</string>
|
||||
|
@ -634,7 +638,7 @@
|
|||
<!-- Alert to ask user relogin to OpenStreetMap with OAuth2 flow after OAuth1 authentication is deprecated. -->
|
||||
<string name="alert_reauth_message">Connecte-toi à OpenStreetMap pour télécharger automatiquement toutes tes modifications de cartes. En savoir plus <a href="https://github.com/organicmaps/organicmaps/issues/6144">ici</a>.</string>
|
||||
<string name="dialog_error_storage_title">Problème d\'accès au stockage</string>
|
||||
<string name="dialog_error_storage_message">Le stockage externe n\'est pas disponible. La carte SD a probablement été enlevée, endommagée ou le système est en lecture seule. Veuillez vérifier votre carte SD ou nous contacter via support@organicmaps.app</string>
|
||||
<string name="dialog_error_storage_message">Le stockage externe n\'est pas disponible. La carte SD a probablement été enlevée, endommagée ou le système est en lecture seule. Veuillez vérifier votre carte SD ou nous contacter via support\@organicmaps.app</string>
|
||||
<string name="setting_emulate_bad_storage">Émuler le mauvais stockage</string>
|
||||
<string name="core_entrance">Entrée</string>
|
||||
<string name="error_enter_correct_name">Veuillez entrer un nom correct</string>
|
||||
|
@ -752,6 +756,7 @@
|
|||
<string name="moreyear_ago_sorttype">Il y a plus d\'un an</string>
|
||||
<string name="near_me_sorttype">Près de moi</string>
|
||||
<string name="others_sorttype">Autres</string>
|
||||
|
||||
<!-- SECTION: Bookmark types used for sorting -->
|
||||
<string name="food_places">Aliments</string>
|
||||
<string name="tourist_places">Choses à voir</string>
|
||||
|
@ -844,7 +849,7 @@
|
|||
<!-- Notification title for permission request from AA. -->
|
||||
<string name="aa_request_permission_notification">Cette application a besoin de ta permission</string>
|
||||
<!-- The text in the activity for location permission request. -->
|
||||
<string name="aa_request_permission_activity_text">Organic Maps dans Android Auto a besoin des permissions de localisation pour fonctionner efficacement</string>
|
||||
<string name="aa_request_permission_activity_text">Organic Maps dans Android Auto a besoin d\'une autorisation de localisation pour fonctionner efficacement</string>
|
||||
<!-- Grant Permissions button. -->
|
||||
<string name="aa_grant_permissions">Accorder l\'accès</string>
|
||||
<!-- Outdoors/hiking map style (activity) name in the Styles and Layers dialog -->
|
||||
|
@ -895,20 +900,4 @@
|
|||
<string name="uri_open_location_failed">Aucune application installée ne permet d\'ouvrir l\'emplacement</string>
|
||||
<!-- preference string for using auto theme only in navigation mode -->
|
||||
<string name="nav_auto">Auto dans la navigation</string>
|
||||
<string name="telegram">Telegram</string>
|
||||
<string name="twitter">X (Twitter)</string>
|
||||
<string name="instagram">Instagram</string>
|
||||
<string name="editor_line_social_network">LINE</string>
|
||||
<string name="vk">VK</string>
|
||||
<string name="github">GitHub</string>
|
||||
<string name="mastodon">Mastodon</string>
|
||||
<string name="facebook">Facebook</string>
|
||||
<string name="matrix">[Matrix]</string>
|
||||
<string name="tts_info_link">https://organicmaps.app/fr/faq/voice/synth%C3%A8se-vocale-tts-sur-android/</string>
|
||||
<string name="telegram_url">https://t.me/OrganicMapsApp</string>
|
||||
<string name="wikimedia_commons">Wikimedia Commons</string>
|
||||
<string name="comma_separated_pair">%1$s, %2$s</string>
|
||||
<string name="instagram_url">https://www.instagram.com/organicmaps.app</string>
|
||||
<string name="openstreetmap">OpenStreetMap</string>
|
||||
<string name="navigation_stop_button">Stop</string>
|
||||
</resources>
|
||||
|
|
|
@ -1307,7 +1307,4 @@
|
|||
<string name="type.amenity.dojo">Dojo</string>
|
||||
<!-- https://wiki.openstreetmap.org/wiki/Tag:leisure=sports_hall -->
|
||||
<string name="type.leisure.sports_hall">Salle de sport</string>
|
||||
<string name="type.aeroway.terminal">Terminal</string>
|
||||
<string name="type.power.pole">Pylône électrique</string>
|
||||
<string name="type.man_made.utility_pole">Pylônes (Télécommunication, lampadaire)</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,348 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="name">Nafn</string>
|
||||
<string name="address">Heimilisfang</string>
|
||||
<string name="list">Listi</string>
|
||||
<string name="settings">Stillingar</string>
|
||||
<string name="category_fuel">Gas</string>
|
||||
<string name="category_bank">Banki</string>
|
||||
<string name="category_pharmacy">Lyfjabúð</string>
|
||||
<string name="category_hospital">Sjúkrahús</string>
|
||||
<string name="category_police">Lögreglustöð</string>
|
||||
<string name="category_wifi">WiFi</string>
|
||||
<string name="category_recycling">Endurvinnsla</string>
|
||||
<string name="category_water">Vatn</string>
|
||||
<string name="edit">Breyta</string>
|
||||
<string name="prefs_group_general">Almennar stillingar</string>
|
||||
<string name="prefs_group_information">Upplýsingar</string>
|
||||
<string name="off">Slökkt</string>
|
||||
<string name="on">Kveikt</string>
|
||||
<string name="instagram">Instagram</string>
|
||||
<string name="openstreetmap">OpenStreetMap</string>
|
||||
<string name="feedback">Umsagnir</string>
|
||||
<string name="help">Hjálp</string>
|
||||
<string name="copyright">Höfundarréttur</string>
|
||||
<string name="downloader_downloaded_subtitle">Niðurhalað</string>
|
||||
<string name="downloader_status_maps">Landakort</string>
|
||||
<string name="downloader_delete_map">Eyða korti</string>
|
||||
<string name="downloader_update_map">Uppfæra kort</string>
|
||||
<string name="save">Vista</string>
|
||||
<string name="red">Rautt</string>
|
||||
<string name="yellow">Gult</string>
|
||||
<string name="blue">Blátt</string>
|
||||
<string name="purple">Purpurablátt</string>
|
||||
<string name="orange">Appelsínugult</string>
|
||||
<string name="pink">Bleikt</string>
|
||||
<string name="hide">Fela</string>
|
||||
<string name="categories">Flokkar</string>
|
||||
<string name="history">Breytingaskrá</string>
|
||||
<string name="read_in_wikipedia">Wikipedia</string>
|
||||
<string name="p2p_start">Upphaf</string>
|
||||
<string name="next_button">Næsta</string>
|
||||
<string name="editor_time_from">Frá</string>
|
||||
<string name="editor_time_to">Til</string>
|
||||
<string name="editor_time_open">Opið</string>
|
||||
<string name="editor_report_problem_send_button">Senda</string>
|
||||
<string name="daily">Daglega</string>
|
||||
<string name="twentyfour_seven">24/7</string>
|
||||
<string name="day_off_today">Lokað í dag</string>
|
||||
<string name="today">Í dag</string>
|
||||
<string name="opens_in">Opnar eftir %s</string>
|
||||
<string name="closes_in">Lokar eftir %s</string>
|
||||
<string name="closed">Lokað</string>
|
||||
<string name="login">Skrá inn</string>
|
||||
<string name="login_osm">Skrá inn á OpenStreetMap</string>
|
||||
<string name="password">Lykilorð</string>
|
||||
<string name="street">Gata</string>
|
||||
<string name="details">Nánar</string>
|
||||
<string name="building">Bygging</string>
|
||||
<string name="cuisine">Eldhús</string>
|
||||
<string name="editor_edit_place_category_title">Flokkur</string>
|
||||
<string name="downloader_of">%1$d af %2$d</string>
|
||||
<string name="editor_operator">Rekstraraðili</string>
|
||||
<string name="m">m</string>
|
||||
<string name="km">km</string>
|
||||
<string name="kilometers_per_hour">km/klst</string>
|
||||
<string name="mi">míl</string>
|
||||
<string name="ft">ft</string>
|
||||
<string name="miles_per_hour">mi/klst</string>
|
||||
<string name="hour">klst</string>
|
||||
<string name="minute">mín</string>
|
||||
<string name="placepage_more_button">Meira</string>
|
||||
<string name="editor_remove_place_button">Eyða</string>
|
||||
<string name="navigation_stop_button">Stöðva</string>
|
||||
<string name="learn_more">Kanna nánar</string>
|
||||
<string name="restore">Endurheimta</string>
|
||||
<plurals name="tracks">
|
||||
<item quantity="one">%d ferill</item>
|
||||
<item quantity="other">%d ferlar</item>
|
||||
</plurals>
|
||||
<string name="privacy">Gagnaleynd</string>
|
||||
<string name="speedcams_alert_title">Hraðamyndavélar</string>
|
||||
<string name="power_managment_setting_never">Aldrei</string>
|
||||
<string name="power_managment_setting_manual_max">Alltaf</string>
|
||||
<string name="no_available">Nei</string>
|
||||
<string name="comma_separated_pair">%1$s, %2$s</string>
|
||||
<string name="volume">Hljóðstyrkur</string>
|
||||
<string name="ok">Í lagi</string>
|
||||
<string name="by_default">Sjálfgefið</string>
|
||||
<string name="food_places">Matur</string>
|
||||
<string name="buildings">Byggingar</string>
|
||||
<string name="parkings">Bílastæði</string>
|
||||
<string name="medicine">Lækningar</string>
|
||||
<string name="elevation_profile_ascent">Hækkun</string>
|
||||
<string name="elevation_profile_descent">Lækkun</string>
|
||||
<string name="elevation_profile_time">Tími:</string>
|
||||
<string name="change_map_locale">Tungumál korts</string>
|
||||
<string name="outdoor_seating">Sæti utandyra</string>
|
||||
<string name="gb">GB</string>
|
||||
<string name="miles">Mílur</string>
|
||||
<string name="core_my_position">Staðsetning mín</string>
|
||||
<string name="delete">Eyða</string>
|
||||
<string name="later">Seinna</string>
|
||||
<string name="downloading">Sæki…</string>
|
||||
<string name="kilometres">Kílómetrar</string>
|
||||
<string name="mb">MB</string>
|
||||
<string name="close">Loka</string>
|
||||
<string name="download">Sækja</string>
|
||||
<string name="pause">Bið</string>
|
||||
<string name="continue_button">Halda áfram</string>
|
||||
<string name="core_my_places">Staðirnir mínir</string>
|
||||
<string name="back">Til baka</string>
|
||||
<string name="cancel">Hætta við</string>
|
||||
<string name="search">Leita</string>
|
||||
<string name="measurement_units">Mælieiningar</string>
|
||||
<string name="category_transport">Samgöngur</string>
|
||||
<string name="category_hotel">Hótel</string>
|
||||
<string name="category_entertainment">Afþreying</string>
|
||||
<string name="category_atm">Hraðbanki</string>
|
||||
<string name="share">Deila</string>
|
||||
<string name="email">Netfang</string>
|
||||
<string name="done">Lokið</string>
|
||||
<string name="tracks_title">Ferlar</string>
|
||||
<string name="length">Lengd</string>
|
||||
<string name="prefs_group_route">Leiðsögn</string>
|
||||
<string name="placepage_distance">Vegalengd</string>
|
||||
<string name="menu">Valmynd</string>
|
||||
<string name="website">Vefsvæði</string>
|
||||
<string name="facebook">Facebook</string>
|
||||
<string name="downloader_download_map">Sækja kort</string>
|
||||
<string name="downloader_retry">Reyna aftur</string>
|
||||
<string name="green">Grænt</string>
|
||||
<string name="brown">Brúnt</string>
|
||||
<string name="show">Sýna</string>
|
||||
<string name="day">d</string>
|
||||
<string name="core_exit">Hætta</string>
|
||||
<string name="core_entrance">Inngangur</string>
|
||||
<string name="bookmark_lists_hide_all">Fela allt</string>
|
||||
<string name="bookmark_lists_show_all">Sýna allt</string>
|
||||
<string name="button_layer_traffic">Umferð</string>
|
||||
<string name="button_layer_subway">Neðanjarðarlest</string>
|
||||
<string name="not_shared">Einka</string>
|
||||
<string name="yes_available">Já</string>
|
||||
<string name="by_type">Eftir gerð</string>
|
||||
<string name="by_date">Eftir dagsetningu</string>
|
||||
<string name="others_sorttype">Annað</string>
|
||||
<string name="elevation_profile_difficulty">Erfiðleikastig</string>
|
||||
<string name="zoom_in">Renna að</string>
|
||||
<string name="zoom_out">Renna frá</string>
|
||||
<string name="copied_to_clipboard">Afritað á klippispjald: %s</string>
|
||||
<string name="share_my_location">Deila staðsetningu minni</string>
|
||||
<string name="data_version">OpenStreetMap-gögn: %s</string>
|
||||
<string name="pref_zoom_title">Aðdráttarhnappar</string>
|
||||
<string name="pref_map_style_title">Næturhamur</string>
|
||||
<string name="pref_zoom_summary">Birta á kortinu</string>
|
||||
<string name="auto">Sjálfvirkt</string>
|
||||
<string name="pref_map_3d_title">Fjarvíddarsýn</string>
|
||||
<string name="search_show_on_map">Skoða á korti</string>
|
||||
<string name="news">Fréttir</string>
|
||||
<string name="github">GitHub</string>
|
||||
<string name="matrix">[Matrix]</string>
|
||||
<string name="mastodon">Mastodon</string>
|
||||
<string name="twitter">X (Twitter)</string>
|
||||
<string name="vk">VK</string>
|
||||
<string name="editor_line_social_network">LINE</string>
|
||||
<string name="rate_the_app">Gefðu forritinu einkunn</string>
|
||||
<string name="downloader_update_all_button">Uppfæra allt</string>
|
||||
<string name="downloader_cancel_all">Hætta við allt</string>
|
||||
<string name="downloader_download_all_button">Sækja allt</string>
|
||||
<string name="pref_display_kayak_title">Hóteltenglar frá Kayak.com</string>
|
||||
<string name="routing_not_enough_space">Ekki nægilegt pláss</string>
|
||||
<string name="enable_location_services">Virkjaðu staðsetningarþjónustur</string>
|
||||
<string name="deep_purple">Dökkpurpurablátt</string>
|
||||
<string name="light_blue">Ljósblátt</string>
|
||||
<string name="cyan">Grænblátt (cyan)</string>
|
||||
<string name="teal">Blágrænt</string>
|
||||
<string name="dialog_routing_system_error">Kerfisvilla</string>
|
||||
<string name="dialog_routing_try_again">Endilega reyndu aftur</string>
|
||||
<string name="not_now">Ekki núna</string>
|
||||
<string name="search_select_map">Veldu kort</string>
|
||||
<string name="search_not_found">Úbbs, engar niðurstöður fundust.</string>
|
||||
<string name="search_history_title">Leitarferill</string>
|
||||
<string name="wikimedia_commons">Wikimedia Commons</string>
|
||||
<string name="p2p_your_location">Staðsetning þín</string>
|
||||
<string name="p2p_from_here">Leið frá</string>
|
||||
<string name="editor_time_close">Lokað</string>
|
||||
<string name="editor_time_advanced">Ítarlegri hamur</string>
|
||||
<string name="editor_time_simple">Einfaldur hamur</string>
|
||||
<string name="editor_correct_mistake">Leiðrétta mistök</string>
|
||||
<string name="editor_add_select_location">Veldu staðsetningu</string>
|
||||
<string name="editor_report_problem_title">Vandamál</string>
|
||||
<string name="editor_report_problem_no_place_title">Þessi staður er ekki til</string>
|
||||
<string name="day_off">Lokað</string>
|
||||
<string name="no_osm_account">Ertu ekki með OpenStreetMap-aðgang?</string>
|
||||
<string name="not_signed_in">Ekki skráð/ur inn</string>
|
||||
<string name="forgot_password">Gleymdirðu lykilorðinu?</string>
|
||||
<string name="logout">Skrá út</string>
|
||||
<string name="edit_place">Breyta stað</string>
|
||||
<string name="postal_code">Póstnúmer</string>
|
||||
<string name="email_or_username">Tölvupóstur eða notandanafn</string>
|
||||
<string name="editor_add_phone">Bæta við símanúmeri</string>
|
||||
<string name="level">Hæð</string>
|
||||
<string name="editor_edit_place_name_hint">Nafn staðarins</string>
|
||||
<string name="editor_report_problem_other_title">Annað vandamál</string>
|
||||
<string name="editor_zip_code">Póstnúmer</string>
|
||||
<string name="error_enter_correct_zip_code">Settu inn gilt póstnúmer</string>
|
||||
<string name="operator">Rekstraraðili: %s</string>
|
||||
<string name="editor_comment_hint">Athugasemd…</string>
|
||||
<string name="editor_reset_edits_button">Henda</string>
|
||||
<string name="mobile_data_option_always">Alltaf nota</string>
|
||||
<string name="bookmark_lists">Listar</string>
|
||||
<plurals name="bookmarks_places">
|
||||
<item quantity="one">%d bókamerki</item>
|
||||
<item quantity="other">%d bókamerki</item>
|
||||
</plurals>
|
||||
<string name="bookmarks_create_new_group">Búa til nýjan lista</string>
|
||||
<string name="bookmarks_new_list_hint">Nýr listi</string>
|
||||
<string name="phone_number">Símanúmer</string>
|
||||
<string name="privacy_policy">Meðferð persónuupplýsinga</string>
|
||||
<string name="terms_of_use">Notkunarskilmálar</string>
|
||||
<string name="layers_title">Stílar landakorta og þekjur</string>
|
||||
<string name="export_file">Flytja út KMZ</string>
|
||||
<string name="export_file_gpx">Flytja út GPX</string>
|
||||
<string name="avoid_tolls">Forðast gjaldskyldu</string>
|
||||
<string name="avoid_unpaved">Forðast vegi með óbundnu slitlagi</string>
|
||||
<string name="avoid_ferry">Forðast ferjur</string>
|
||||
<string name="avoid_motorways">Forðast hraðbrautir</string>
|
||||
<string name="unpaved_road">Óbundið slitlag</string>
|
||||
<string name="ferry_crossing">Þverun með ferju</string>
|
||||
<string name="yes">Já</string>
|
||||
<string name="no">Nei</string>
|
||||
<string name="trip_finished">Þú hefur náð á áfangastað!</string>
|
||||
<string name="sort">Raða…</string>
|
||||
<string name="sort_bookmarks">Raða bókamerkjum</string>
|
||||
<string name="by_distance">Eftir vegalengd</string>
|
||||
<string name="by_name">Eftir nafni</string>
|
||||
<string name="week_ago_sorttype">Fyrir viku síðan</string>
|
||||
<string name="month_ago_sorttype">Fyrir mánuði síðan</string>
|
||||
<string name="moremonth_ago_sorttype">Fyrir meira en mánuði síðan</string>
|
||||
<string name="moreyear_ago_sorttype">Fyrir meira en ári síðan</string>
|
||||
<string name="tourist_places">Skoðunarverðir staðir</string>
|
||||
<string name="animals">Dýr</string>
|
||||
<string name="shops">Verslanir</string>
|
||||
<string name="fuel_places">Bensínstöðvar</string>
|
||||
<string name="button_layer_isolines">Hæðarlínur</string>
|
||||
<string name="elevation_profile_distance">Fjarl.:</string>
|
||||
<string name="downloader_loading_ios">Sæki</string>
|
||||
<string name="bookmarks_error_title_list_name_already_taken">Þetta nafn er þegar í notkun</string>
|
||||
<string name="bookmarks_error_message_list_name_already_taken">Veldu eitthvað annað heiti</string>
|
||||
<string name="please_wait">Bíddu aðeins…</string>
|
||||
<string name="category_desc_more">…meira</string>
|
||||
<string name="delete_list">Eyða lista</string>
|
||||
<string name="public_access">Opinber aðgangur</string>
|
||||
<string name="limited_access">Takmarkaður aðgangur</string>
|
||||
<string name="place_description_title">Lýsing á stað</string>
|
||||
<string name="unable_to_calc_alert_title">Tókst ekki að reikna leið</string>
|
||||
<string name="near_me_sorttype">Nálægt mér</string>
|
||||
<string name="museums">Söfn</string>
|
||||
<string name="parks">Almenningsgarðar</string>
|
||||
<string name="swim_places">Sund</string>
|
||||
<string name="mountains">Fjöll</string>
|
||||
<string name="hotels">Hótel</string>
|
||||
<string name="money">Peningar</string>
|
||||
<string name="search_in_the_list">Leita í listanum</string>
|
||||
<string name="religious_places">Trúarlegir staðir</string>
|
||||
<string name="download_map_title">Sækja heims-yfirlitskortið</string>
|
||||
<string name="already_donated">Hef þegar styrkt verkefnið</string>
|
||||
<string name="remind_me_later">Áminna mig síðar</string>
|
||||
<string name="support_organic_maps">Styddu við Organic Maps</string>
|
||||
<string name="how_to_support_us">Styddu við verkefnið</string>
|
||||
<string name="osm_log_out_confirmation">Ertu viss um að þú viljir skrá þig út af OpenStreetMap-aðgangnum þínum?</string>
|
||||
<string name="telegram">Telegram</string>
|
||||
<string name="faq">Algengar spurningar (FAQ)</string>
|
||||
<string name="donate">Styrkja</string>
|
||||
<string name="report_a_bug">Tilkynna um villu</string>
|
||||
<string name="downloader_queued">Í biðröð</string>
|
||||
<string name="downloader_near_me_subtitle">Nálægt mér</string>
|
||||
<string name="downloader_downloading">Sæki:</string>
|
||||
<string name="downloader_delete_map_while_routing_dialog">Til að eyða korti skaltu stöðva leiðsögn.</string>
|
||||
<string name="lime">Límónugrænt</string>
|
||||
<string name="deep_orange">Dimmappelsínugult</string>
|
||||
<string name="gray">Grátt</string>
|
||||
<string name="autodownload">Sækja kort sjálfvirkt</string>
|
||||
<string name="add_language">Bæta við tungumáli</string>
|
||||
<string name="house_number">Bygging númer</string>
|
||||
<string name="blue_gray">Blágrátt</string>
|
||||
<string name="p2p_to_here">Leið til</string>
|
||||
<string name="editor_report_problem_under_construction_title">Lokað vegna viðhalds</string>
|
||||
<string name="editor_report_problem_duplicate_place_title">Tvítekinn staður</string>
|
||||
<string name="social_media">Samfélagsmiðlar</string>
|
||||
<string name="add_street">Bæta við götu</string>
|
||||
<string name="empty_street_name_error">Settu inn götuheiti</string>
|
||||
<string name="choose_language">Veldu tungumál</string>
|
||||
<string name="choose_street">Veldu götu</string>
|
||||
<string name="level_value_generic">Hæð: %s</string>
|
||||
<string name="editor_login_error_dialog">Villa við innskráningu.</string>
|
||||
<string name="editor_profile_changes">Staðfestar breytingar</string>
|
||||
<string name="accept">Samþykkja</string>
|
||||
<string name="decline">Hafna</string>
|
||||
<string name="mobile_data_option_never">Aldrei nota</string>
|
||||
<string name="mobile_data_option_ask">Alltaf spyrja</string>
|
||||
<string name="download_maps">Sækja kort</string>
|
||||
<string name="download_has_failed">Mistókst að sækja. Ýttu til að reyna aftur.</string>
|
||||
<string name="search_map">Leita á korti</string>
|
||||
<string name="try_again">Reyndu aftur</string>
|
||||
<string name="about_menu_title">Um Organic Maps</string>
|
||||
<string name="about_headline">Ókeypis fyrir alla, unnið af alúð</string>
|
||||
<string name="location_settings">Staðsetningarstillingar</string>
|
||||
<string name="download_resources_continue">Fara á kort</string>
|
||||
<string name="download_country_ask">Sækja %s?</string>
|
||||
<string name="update_country_ask">Uppfæra %s?</string>
|
||||
<string name="add_new_set">Bæta við nýjum lista</string>
|
||||
<string name="maps_storage">Vista landakort í</string>
|
||||
<string name="power_managment_setting_auto">Þegar lítil rafhleðsla er eftir</string>
|
||||
<string name="define_to_avoid_btn">Skilgreindu vegi sem á að forðast</string>
|
||||
<string name="toll_road">Vegur með gjaldskyldu</string>
|
||||
<string name="capacity">Afkastageta: %s</string>
|
||||
<string name="network">Þjónustuaðili: %s</string>
|
||||
<string name="elevation_profile_min_elevation">Lágm.hæð</string>
|
||||
<string name="elevation_profile_max_elevation">Hám.hæð</string>
|
||||
<string name="isolines_toast_zooms_1_10">Renndu að til að skoða hæðarlínur</string>
|
||||
<string name="splash_subtitle">Kortagögn frá OpenStreetMap</string>
|
||||
<string name="telegram_url">https://t.me/OrganicMapsApp</string>
|
||||
<string name="button_layer_outdoor">Utandyra</string>
|
||||
<string name="website_menu">Tengill í valmynd</string>
|
||||
<string name="view_menu">Skoða valmynd</string>
|
||||
<string name="self_service">Sjálfsafgreiðsla</string>
|
||||
<string name="limited_accuracy">Takmörkuð nákvæmni</string>
|
||||
<string name="zoom_to_country">Birta á kortinu</string>
|
||||
<string name="country_status_download_failed">Mistókst að sækja</string>
|
||||
<string name="pref_tts_speedcams_auto">Áminna ef farið er of hratt</string>
|
||||
<string name="pref_tts_speedcams_always">Alltaf aðvara</string>
|
||||
<string name="pref_tts_speedcams_never">Aldrei aðvara</string>
|
||||
<string name="power_managment_title">Orkusparnaður</string>
|
||||
<string name="select_list">Veldu lista</string>
|
||||
<string name="transit_not_found">Leiðsögn í neðanjarðarlestum er ekki tiltæk í augnablikinu á þessu svæði</string>
|
||||
<string name="disk_error_title">Villa á diski</string>
|
||||
<string name="connection_failure">Tenging brást</string>
|
||||
<string name="disconnect_usb_cable_title">Aftengja USB-kapal</string>
|
||||
<string name="enable_keep_screen_on">Halda kveiktu á skjánum</string>
|
||||
<string name="enable_show_on_lock_screen">Birta á læsiskjánum</string>
|
||||
<string name="instagram_url">https://www.instagram.com/organicmaps.app</string>
|
||||
<string name="tts_info_link">https://organicmaps.app/faq/text-to-speech-android-tts/</string>
|
||||
<string name="translated_om_site_url">https://organicmaps.app/</string>
|
||||
<string name="osm_wiki_about_url">https://wiki.openstreetmap.org/wiki/About_OpenStreetMap</string>
|
||||
<string name="browser_not_available">Vafri er ekki tiltækur</string>
|
||||
<string name="open_in_app">Opna í öðru forriti</string>
|
||||
<string name="select_option">Veldu valkost</string>
|
||||
</resources>
|
File diff suppressed because it is too large
Load diff
|
@ -525,7 +525,7 @@
|
|||
<string name="operator">Operators: %s</string>
|
||||
<string name="editor_category_unsuitable_title">Vai neatrodat atbilstošu kategoriju?</string>
|
||||
<string name="editor_category_unsuitable_text">„Organic Maps“ ļauj pievienot tikai vienkāršu punktu kategorijas, proti, nevarat pievienot pilsētas, ceļus, ezerus, ēku aprises u.tml. Šādas kategorijas lūdzam pievienot tiešā veidā <a href="https://www.openstreetmap.org">OpenStreetMap.org</a>. Lai uzzinātu, kā to izdarīt, sekojiet norādēm <a href="https://organicmaps.app/faq/editing/advanced-map-editing">rokasgrāmatā</a>.</string>
|
||||
<string name="downloader_no_downloaded_maps_title">Ierīcē nav lejupielādētu karšu</string>
|
||||
<string name="downloader_no_downloaded_maps_title">Ierīcē nav lejupielādētas kartes</string>
|
||||
<string name="downloader_no_downloaded_maps_message">Lai darbotos nesaistes navigācija un meklēšana, lejupielādējiet kartes.</string>
|
||||
<string name="current_location_unknown_error_title">Pašreizējā vieta nav zināma.</string>
|
||||
<!-- abbreviation for meters -->
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<string name="delete">Удалить</string>
|
||||
<string name="download_maps">Загрузить карты</string>
|
||||
<!-- Settings/Downloader - info for country when download fails -->
|
||||
<string name="download_has_failed">Ошибка загрузки. Нажмите, чтобы повторить попытку.</string>
|
||||
<string name="download_has_failed">Ошибка загрузки. Нажмите, чтобы повторить попытку</string>
|
||||
<!-- Settings/Downloader - info for country which started downloading -->
|
||||
<string name="downloading">Загружается…</string>
|
||||
<!-- Choose measurement on first launch alert - choose metric system button -->
|
||||
|
|
|
@ -79,3 +79,4 @@
|
|||
#import "MWMSearchSuggestionCell.h"
|
||||
#import "MWMSearch.h"
|
||||
#import "SearchResult.h"
|
||||
#import "RoutePreview.h"
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
extension CALayer {
|
||||
func setCorner(radius: CGFloat,
|
||||
corners: CACornerMask? = nil) {
|
||||
cornerRadius = radius
|
||||
if let corners {
|
||||
maskedCorners = corners
|
||||
func setCornerRadius(_ cornerRadius: CornerRadius,
|
||||
maskedCorners: CACornerMask? = nil) {
|
||||
self.cornerRadius = cornerRadius.value
|
||||
if let maskedCorners {
|
||||
self.maskedCorners = maskedCorners
|
||||
}
|
||||
if #available(iOS 13.0, *) {
|
||||
cornerCurve = .continuous
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CACornerMask {
|
||||
static var all: CACornerMask {
|
||||
return [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ extension UIView {
|
|||
snapshot.layer.contents = contents
|
||||
snapshot.layer.bounds = layer.bounds
|
||||
}
|
||||
snapshot.layer.setCorner(radius: layer.cornerRadius)
|
||||
snapshot.layer.setCornerRadius(.custom(layer.cornerRadius))
|
||||
snapshot.layer.masksToBounds = layer.masksToBounds
|
||||
snapshot.contentMode = contentMode
|
||||
snapshot.transform = transform
|
||||
|
|
|
@ -8,7 +8,7 @@ final class AlertPresentationController: DimmedModalPresentationController {
|
|||
|
||||
override func presentationTransitionWillBegin() {
|
||||
super.presentationTransitionWillBegin()
|
||||
presentedViewController.view.layer.setCorner(radius: 12)
|
||||
presentedViewController.view.layer.setCornerRadius(.modalSheet)
|
||||
presentedViewController.view.clipsToBounds = true
|
||||
guard let containerView = containerView, let presentedView = presentedView else { return }
|
||||
containerView.addSubview(presentedView)
|
||||
|
|
|
@ -17,7 +17,7 @@ override var frameOfPresentedViewInContainerView: CGRect {
|
|||
|
||||
override func presentationTransitionWillBegin() {
|
||||
super.presentationTransitionWillBegin()
|
||||
presentedViewController.view.layer.setCorner(radius: 8)
|
||||
presentedViewController.view.layer.setCornerRadius(.buttonDefault)
|
||||
presentedViewController.view.clipsToBounds = true
|
||||
guard let containerView = containerView, let presentedView = presentedView else { return }
|
||||
containerView.addSubview(presentedView)
|
||||
|
|
|
@ -16,7 +16,7 @@ final class PromoBookingPresentationController: DimmedModalPresentationControlle
|
|||
|
||||
override func presentationTransitionWillBegin() {
|
||||
super.presentationTransitionWillBegin()
|
||||
presentedViewController.view.layer.setCorner(radius: 8)
|
||||
presentedViewController.view.layer.setCornerRadius(.buttonDefault)
|
||||
presentedViewController.view.clipsToBounds = true
|
||||
guard let containerView = containerView, let presentedView = presentedView else { return }
|
||||
containerView.addSubview(presentedView)
|
||||
|
|
|
@ -22,7 +22,7 @@ final class Toast: NSObject {
|
|||
}
|
||||
|
||||
private init(_ text: String) {
|
||||
blurView.layer.setCorner(radius: 8)
|
||||
blurView.layer.setCornerRadius(.buttonDefault)
|
||||
blurView.clipsToBounds = true
|
||||
blurView.alpha = 0
|
||||
|
||||
|
|
|
@ -14,6 +14,5 @@
|
|||
- (void)processMyPositionStateModeEvent:(MWMMyPositionMode)mode;
|
||||
|
||||
+ (void)updateAvailableArea:(CGRect)frame;
|
||||
+ (CGRect)getAvailableArea;
|
||||
|
||||
@end
|
||||
|
|
|
@ -58,8 +58,6 @@ NSString * const kUDDidShowLongTapToShowSideButtonsToast = @"kUDDidShowLongTapTo
|
|||
|
||||
+ (void)updateAvailableArea:(CGRect)frame { [[self buttons].sideView updateAvailableArea:frame]; }
|
||||
|
||||
+ (CGRect)getAvailableArea { return [self buttons].sideView.getAvailableArea; }
|
||||
|
||||
- (void)zoomIn
|
||||
{
|
||||
GetFramework().Scale(Framework::SCALE_MAG, true);
|
||||
|
|
|
@ -8,6 +8,5 @@
|
|||
- (void)setHidden:(BOOL)hidden animated:(BOOL)animated;
|
||||
|
||||
- (void)updateAvailableArea:(CGRect)frame;
|
||||
- (CGRect)getAvailableArea;
|
||||
|
||||
@end
|
||||
|
|
|
@ -144,10 +144,6 @@ CGFloat const kButtonsBottomOffset = 6;
|
|||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (CGRect)getAvailableArea {
|
||||
return self.availableArea;
|
||||
}
|
||||
|
||||
- (CGFloat)availableHeight {
|
||||
return self.availableArea.size.height - kButtonsTopOffset - kButtonsBottomOffset;
|
||||
}
|
||||
|
|
|
@ -195,6 +195,8 @@ NSArray<UIImage *> *imagesWithName(NSString *name) {
|
|||
if (CGRectEqualToRect(controller.availableArea, frame))
|
||||
return;
|
||||
controller.availableArea = frame;
|
||||
BOOL isHidden = frame.origin.y + frame.size.height < controller.view.origin.y + controller.view.height + kTopOffset;
|
||||
[MapViewController.sharedController.controlsManager setTrafficButtonHidden:isHidden];
|
||||
[controller refreshLayout];
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ typedef NS_ENUM(NSUInteger, MWMNavigationDashboardState) {
|
|||
MWMNavigationDashboardStateNavigation
|
||||
};
|
||||
|
||||
@class NavigationDashboardView;
|
||||
|
||||
@interface MWMNavigationDashboardManager : NSObject
|
||||
|
||||
+ (nonnull MWMNavigationDashboardManager *)sharedManager;
|
||||
|
|
|
@ -1,40 +1,24 @@
|
|||
#import "MWMNavigationDashboardManager.h"
|
||||
#import "MWMMapViewControlsManager.h"
|
||||
#import "MWMNavigationInfoView.h"
|
||||
#import "MWMRoutePreview.h"
|
||||
#import "MWMSearch.h"
|
||||
#import "MapViewController.h"
|
||||
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
namespace {
|
||||
NSString *const kRoutePreviewIPhoneXibName = @"MWMiPhoneRoutePreview";
|
||||
NSString *const kNavigationInfoViewXibName = @"MWMNavigationInfoView";
|
||||
NSString *const kNavigationControlViewXibName = @"NavigationControlView";
|
||||
} // namespace
|
||||
#import "NavigationDashboardView.h"
|
||||
|
||||
@interface MWMMapViewControlsManager ()
|
||||
|
||||
@property(nonatomic) MWMNavigationDashboardManager *navigationManager;
|
||||
@property(nonatomic) MWMNavigationDashboardManager * navigationManager;
|
||||
|
||||
@end
|
||||
|
||||
@interface MWMNavigationDashboardManager () <SearchOnMapManagerObserver, MWMRoutePreviewDelegate>
|
||||
|
||||
@property(copy, nonatomic) NSDictionary *etaAttributes;
|
||||
@property(copy, nonatomic) NSDictionary *etaSecondaryAttributes;
|
||||
@property(copy, nonatomic) NSString *errorMessage;
|
||||
@property(nonatomic) IBOutlet MWMBaseRoutePreviewStatus *baseRoutePreviewStatus;
|
||||
@property(nonatomic) IBOutlet MWMNavigationControlView *navigationControlView;
|
||||
@property(nonatomic) IBOutlet MWMNavigationInfoView *navigationInfoView;
|
||||
@property(nonatomic) IBOutlet MWMRoutePreview *routePreview;
|
||||
@property(nonatomic) IBOutlet MWMTransportRoutePreviewStatus *transportRoutePreviewStatus;
|
||||
@property(nonatomic) IBOutletCollection(MWMRouteStartButton) NSArray *goButtons;
|
||||
@property(nonatomic) MWMNavigationDashboardEntity *entity;
|
||||
@property(nonatomic) MWMRouteManagerTransitioningManager *routeManagerTransitioningManager;
|
||||
@property(weak, nonatomic) IBOutlet UIButton *showRouteManagerButton;
|
||||
@property(weak, nonatomic) IBOutlet UIView *goButtonsContainer;
|
||||
@property(weak, nonatomic) UIView *ownerView;
|
||||
@property(copy, nonatomic) NSDictionary * etaAttributes;
|
||||
@property(copy, nonatomic) NSDictionary * etaSecondaryAttributes;
|
||||
@property(copy, nonatomic) NSString * errorMessage;
|
||||
@property(copy, nonatomic) MWMNavigationDashboardEntity * entity;
|
||||
|
||||
@property(nonatomic, readonly) NavigationDashboardView * _Nonnull navigationDashboardView;
|
||||
@property(weak, nonatomic) UIView * ownerView;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -48,6 +32,8 @@ NSString *const kNavigationControlViewXibName = @"NavigationControlView";
|
|||
self = [super init];
|
||||
if (self) {
|
||||
_ownerView = view;
|
||||
_navigationDashboardView = [[NavigationDashboardView alloc] initWithOwnerView:view];
|
||||
_navigationDashboardView.delegate = self;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -56,215 +42,51 @@ NSString *const kNavigationControlViewXibName = @"NavigationControlView";
|
|||
return [[MapViewController sharedController] searchManager];
|
||||
}
|
||||
|
||||
- (void)loadPreviewWithStatusBoxes {
|
||||
[NSBundle.mainBundle loadNibNamed:kRoutePreviewIPhoneXibName owner:self options:nil];
|
||||
auto ownerView = self.ownerView;
|
||||
_baseRoutePreviewStatus.ownerView = ownerView;
|
||||
_transportRoutePreviewStatus.ownerView = ownerView;
|
||||
}
|
||||
|
||||
#pragma mark - MWMRoutePreview
|
||||
|
||||
- (void)setRouteBuilderProgress:(CGFloat)progress {
|
||||
[self.routePreview router:[MWMRouter type] setProgress:progress / 100.];
|
||||
[self.navigationDashboardView setRouteBuilderProgress:[MWMRouter type] progress:progress / 100.];
|
||||
}
|
||||
|
||||
#pragma mark - MWMNavigationGo
|
||||
|
||||
- (IBAction)routingStartTouchUpInside {
|
||||
[MWMRouter startRouting];
|
||||
}
|
||||
- (void)updateGoButtonTitle {
|
||||
NSString *title = L(@"p2p_start");
|
||||
|
||||
for (MWMRouteStartButton *button in self.goButtons)
|
||||
[button setTitle:title forState:UIControlStateNormal];
|
||||
}
|
||||
#pragma mark - On route updates
|
||||
|
||||
- (void)onNavigationInfoUpdated {
|
||||
auto entity = self.entity;
|
||||
if (!entity.isValid)
|
||||
return;
|
||||
[_navigationInfoView onNavigationInfoUpdated:entity];
|
||||
bool const isPublicTransport = [MWMRouter type] == MWMRouterTypePublicTransport;
|
||||
bool const isRuler = [MWMRouter type] == MWMRouterTypeRuler;
|
||||
if (isPublicTransport || isRuler)
|
||||
[_transportRoutePreviewStatus onNavigationInfoUpdated:entity prependDistance:isRuler];
|
||||
else
|
||||
[_baseRoutePreviewStatus onNavigationInfoUpdated:entity];
|
||||
[_navigationControlView onNavigationInfoUpdated:entity];
|
||||
[self.navigationDashboardView onNavigationInfoUpdated:entity];
|
||||
}
|
||||
|
||||
#pragma mark - On route updates
|
||||
|
||||
- (void)onRoutePrepare {
|
||||
self.state = MWMNavigationDashboardStatePrepare;
|
||||
self.routePreview.drivingOptionsState = MWMDrivingOptionsStateNone;
|
||||
[self.navigationDashboardView setDrivingOptionState:MWMDrivingOptionsStateNone];
|
||||
}
|
||||
|
||||
- (void)onRoutePlanning {
|
||||
self.state = MWMNavigationDashboardStatePlanning;
|
||||
self.routePreview.drivingOptionsState = MWMDrivingOptionsStateNone;
|
||||
[self.navigationDashboardView setDrivingOptionState:MWMDrivingOptionsStateNone];
|
||||
}
|
||||
|
||||
- (void)onRouteError:(NSString *)error {
|
||||
self.errorMessage = error;
|
||||
self.state = MWMNavigationDashboardStateError;
|
||||
self.routePreview.drivingOptionsState =
|
||||
[MWMRouter hasActiveDrivingOptions] ? MWMDrivingOptionsStateChange : MWMDrivingOptionsStateNone;
|
||||
[self.navigationDashboardView setDrivingOptionState:[MWMRouter hasActiveDrivingOptions] ? MWMDrivingOptionsStateChange : MWMDrivingOptionsStateNone];
|
||||
}
|
||||
|
||||
- (void)onRouteReady:(BOOL)hasWarnings {
|
||||
if (self.state != MWMNavigationDashboardStateNavigation)
|
||||
self.state = MWMNavigationDashboardStateReady;
|
||||
if ([MWMRouter hasActiveDrivingOptions]) {
|
||||
self.routePreview.drivingOptionsState = MWMDrivingOptionsStateChange;
|
||||
[self.navigationDashboardView setDrivingOptionState:MWMDrivingOptionsStateChange];
|
||||
} else {
|
||||
self.routePreview.drivingOptionsState = hasWarnings ? MWMDrivingOptionsStateDefine : MWMDrivingOptionsStateNone;
|
||||
[self.navigationDashboardView setDrivingOptionState:hasWarnings ? MWMDrivingOptionsStateDefine : MWMDrivingOptionsStateNone];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onRoutePointsUpdated {
|
||||
if (self.state == MWMNavigationDashboardStateHidden)
|
||||
self.state = MWMNavigationDashboardStatePrepare;
|
||||
[self.navigationInfoView updateToastView];
|
||||
[self.navigationDashboardView onRoutePointsUpdated];
|
||||
}
|
||||
|
||||
#pragma mark - State changes
|
||||
|
||||
- (void)stateHidden {
|
||||
self.routePreview = nil;
|
||||
self.navigationInfoView.state = MWMNavigationInfoViewStateHidden;
|
||||
self.navigationInfoView = nil;
|
||||
_navigationControlView.isVisible = NO;
|
||||
_navigationControlView = nil;
|
||||
[self.baseRoutePreviewStatus hide];
|
||||
[_transportRoutePreviewStatus hide];
|
||||
_transportRoutePreviewStatus = nil;
|
||||
}
|
||||
|
||||
- (void)statePrepare {
|
||||
self.navigationInfoView.state = MWMNavigationInfoViewStatePrepare;
|
||||
auto routePreview = self.routePreview;
|
||||
[routePreview addToView:self.ownerView];
|
||||
[routePreview statePrepare];
|
||||
[routePreview selectRouter:[MWMRouter type]];
|
||||
[self updateGoButtonTitle];
|
||||
[self.baseRoutePreviewStatus hide];
|
||||
[_transportRoutePreviewStatus hide];
|
||||
for (MWMRouteStartButton *button in self.goButtons)
|
||||
[button statePrepare];
|
||||
}
|
||||
|
||||
- (void)statePlanning {
|
||||
[self statePrepare];
|
||||
[self.routePreview router:[MWMRouter type] setState:MWMCircularProgressStateSpinner];
|
||||
[self setRouteBuilderProgress:0.];
|
||||
}
|
||||
|
||||
- (void)stateError {
|
||||
if (_state == MWMNavigationDashboardStateReady)
|
||||
return;
|
||||
|
||||
NSAssert(_state == MWMNavigationDashboardStatePlanning, @"Invalid state change (error)");
|
||||
auto routePreview = self.routePreview;
|
||||
[routePreview router:[MWMRouter type] setState:MWMCircularProgressStateFailed];
|
||||
[self updateGoButtonTitle];
|
||||
[self.baseRoutePreviewStatus showErrorWithMessage:self.errorMessage];
|
||||
for (MWMRouteStartButton *button in self.goButtons)
|
||||
[button stateError];
|
||||
}
|
||||
|
||||
- (void)stateReady {
|
||||
// TODO: Here assert sometimes fires with _state = MWMNavigationDashboardStateReady, if app was stopped while navigating and then restarted.
|
||||
// Also in ruler mode when new point is added by single tap on the map state MWMNavigationDashboardStatePlanning is skipped and we get _state = MWMNavigationDashboardStateReady.
|
||||
NSAssert(_state == MWMNavigationDashboardStatePlanning || _state == MWMNavigationDashboardStateReady, @"Invalid state change (ready)");
|
||||
[self setRouteBuilderProgress:100.];
|
||||
[self updateGoButtonTitle];
|
||||
bool const isTransport = ([MWMRouter type] == MWMRouterTypePublicTransport);
|
||||
bool const isRuler = ([MWMRouter type] == MWMRouterTypeRuler);
|
||||
if (isTransport || isRuler)
|
||||
[self.transportRoutePreviewStatus showReady];
|
||||
else
|
||||
[self.baseRoutePreviewStatus showReady];
|
||||
self.goButtonsContainer.hidden = isTransport || isRuler;
|
||||
for (MWMRouteStartButton *button in self.goButtons)
|
||||
{
|
||||
if (isRuler)
|
||||
[button stateHidden];
|
||||
else
|
||||
[button stateReady];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onRouteStart {
|
||||
self.state = MWMNavigationDashboardStateNavigation;
|
||||
}
|
||||
- (void)onRouteStop {
|
||||
self.state = MWMNavigationDashboardStateHidden;
|
||||
}
|
||||
- (void)stateNavigation {
|
||||
self.routePreview = nil;
|
||||
self.navigationInfoView.state = MWMNavigationInfoViewStateNavigation;
|
||||
self.navigationControlView.isVisible = YES;
|
||||
[self.baseRoutePreviewStatus hide];
|
||||
[_transportRoutePreviewStatus hide];
|
||||
_transportRoutePreviewStatus = nil;
|
||||
[self onNavigationInfoUpdated];
|
||||
}
|
||||
|
||||
#pragma mark - MWMRoutePreviewStatus
|
||||
|
||||
- (IBAction)showRouteManager {
|
||||
auto routeManagerViewModel = [[MWMRouteManagerViewModel alloc] init];
|
||||
auto routeManager = [[MWMRouteManagerViewController alloc] initWithViewModel:routeManagerViewModel];
|
||||
routeManager.modalPresentationStyle = UIModalPresentationCustom;
|
||||
|
||||
self.routeManagerTransitioningManager = [[MWMRouteManagerTransitioningManager alloc] init];
|
||||
routeManager.transitioningDelegate = self.routeManagerTransitioningManager;
|
||||
|
||||
[[MapViewController sharedController] presentViewController:routeManager animated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - MWMNavigationControlView
|
||||
|
||||
- (IBAction)ttsButtonAction {
|
||||
BOOL const isEnabled = [MWMTextToSpeech tts].active;
|
||||
[MWMTextToSpeech tts].active = !isEnabled;
|
||||
}
|
||||
|
||||
- (IBAction)settingsButtonAction {
|
||||
[[MapViewController sharedController] openSettings];
|
||||
}
|
||||
|
||||
- (IBAction)stopRoutingButtonAction {
|
||||
[MWMSearch clear];
|
||||
[MWMRouter stopRouting];
|
||||
[self.searchManager close];
|
||||
}
|
||||
|
||||
#pragma mark - SearchOnMapManagerObserver
|
||||
|
||||
- (void)searchManagerWithDidChangeState:(SearchOnMapState)state {
|
||||
switch (state) {
|
||||
case SearchOnMapStateClosed:
|
||||
[self.navigationInfoView setSearchState:NavigationSearchState::MinimizedNormal animated:YES];
|
||||
break;
|
||||
case SearchOnMapStateHidden:
|
||||
case SearchOnMapStateSearching:
|
||||
[self setMapSearch];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Available area
|
||||
|
||||
+ (void)updateNavigationInfoAvailableArea:(CGRect)frame {
|
||||
[[self sharedManager] updateNavigationInfoAvailableArea:frame];
|
||||
}
|
||||
|
||||
- (void)updateNavigationInfoAvailableArea:(CGRect)frame {
|
||||
_navigationInfoView.availableArea = frame;
|
||||
}
|
||||
#pragma mark - Properties
|
||||
|
||||
- (void)setState:(MWMNavigationDashboardState)state {
|
||||
|
@ -299,64 +121,91 @@ NSString *const kNavigationControlViewXibName = @"NavigationControlView";
|
|||
BottomTabBarViewController.controller.isHidden = state != MWMNavigationDashboardStateHidden;
|
||||
}
|
||||
|
||||
@synthesize routePreview = _routePreview;
|
||||
- (MWMRoutePreview *)routePreview {
|
||||
if (!_routePreview)
|
||||
[self loadPreviewWithStatusBoxes];
|
||||
return _routePreview;
|
||||
}
|
||||
|
||||
- (void)setRoutePreview:(MWMRoutePreview *)routePreview {
|
||||
if (routePreview == _routePreview)
|
||||
return;
|
||||
[_routePreview remove];
|
||||
_routePreview = routePreview;
|
||||
_routePreview.delegate = self;
|
||||
}
|
||||
|
||||
- (MWMBaseRoutePreviewStatus *)baseRoutePreviewStatus {
|
||||
if (!_baseRoutePreviewStatus)
|
||||
[self loadPreviewWithStatusBoxes];
|
||||
return _baseRoutePreviewStatus;
|
||||
}
|
||||
|
||||
- (MWMTransportRoutePreviewStatus *)transportRoutePreviewStatus {
|
||||
if (!_transportRoutePreviewStatus)
|
||||
[self loadPreviewWithStatusBoxes];
|
||||
return _transportRoutePreviewStatus;
|
||||
}
|
||||
|
||||
- (MWMNavigationInfoView *)navigationInfoView {
|
||||
if (!_navigationInfoView) {
|
||||
[NSBundle.mainBundle loadNibNamed:kNavigationInfoViewXibName owner:self options:nil];
|
||||
_navigationInfoView.state = MWMNavigationInfoViewStateHidden;
|
||||
_navigationInfoView.ownerView = self.ownerView;
|
||||
}
|
||||
return _navigationInfoView;
|
||||
}
|
||||
|
||||
- (MWMNavigationControlView *)navigationControlView {
|
||||
if (!_navigationControlView) {
|
||||
[NSBundle.mainBundle loadNibNamed:kNavigationControlViewXibName owner:self options:nil];
|
||||
_navigationControlView.ownerView = self.ownerView;
|
||||
}
|
||||
return _navigationControlView;
|
||||
}
|
||||
|
||||
- (MWMNavigationDashboardEntity *)entity {
|
||||
if (!_entity)
|
||||
_entity = [[MWMNavigationDashboardEntity alloc] init];
|
||||
return _entity;
|
||||
}
|
||||
|
||||
- (void)setMapSearch {
|
||||
[_navigationInfoView setMapSearch];
|
||||
#pragma mark - State changes
|
||||
|
||||
- (void)stateHidden {
|
||||
[self.navigationDashboardView setHidden];
|
||||
}
|
||||
|
||||
- (void)statePrepare {
|
||||
[self.navigationDashboardView statePrepare];
|
||||
}
|
||||
|
||||
- (void)statePlanning {
|
||||
[self statePrepare];
|
||||
[self.navigationDashboardView statePlanning];
|
||||
[self setRouteBuilderProgress:0.];
|
||||
}
|
||||
|
||||
- (void)stateError {
|
||||
if (_state == MWMNavigationDashboardStateReady)
|
||||
return;
|
||||
NSAssert(_state == MWMNavigationDashboardStatePlanning, @"Invalid state change (error)");
|
||||
[self.navigationDashboardView stateError:self.errorMessage];
|
||||
}
|
||||
|
||||
- (void)stateReady {
|
||||
// TODO: Here assert sometimes fires with _state = MWMNavigationDashboardStateReady, if app was stopped while navigating and then restarted.
|
||||
// Also in ruler mode when new point is added by single tap on the map state MWMNavigationDashboardStatePlanning is skipped and we get _state = MWMNavigationDashboardStateReady.
|
||||
NSAssert(_state == MWMNavigationDashboardStatePlanning || _state == MWMNavigationDashboardStateReady, @"Invalid state change (ready)");
|
||||
[self setRouteBuilderProgress:100.];
|
||||
[self.navigationDashboardView stateReady];
|
||||
}
|
||||
|
||||
- (void)onRouteStart {
|
||||
self.state = MWMNavigationDashboardStateNavigation;
|
||||
}
|
||||
- (void)onRouteStop {
|
||||
self.state = MWMNavigationDashboardStateHidden;
|
||||
}
|
||||
|
||||
- (void)stateNavigation {
|
||||
[self.navigationDashboardView stateNavigation];
|
||||
[self onNavigationInfoUpdated];
|
||||
}
|
||||
|
||||
#pragma mark - MWMRoutePreviewDelegate
|
||||
|
||||
- (void)routingStartButtonDidTap {
|
||||
[MWMRouter startRouting];
|
||||
}
|
||||
|
||||
- (void)routePreviewDidPressDrivingOptions:(MWMRoutePreview *)routePreview {
|
||||
[[MapViewController sharedController] openDrivingOptions];
|
||||
}
|
||||
|
||||
- (void)ttsButtonDidTap {
|
||||
BOOL const isEnabled = [MWMTextToSpeech tts].active;
|
||||
[MWMTextToSpeech tts].active = !isEnabled;
|
||||
}
|
||||
|
||||
- (void)settingsButtonDidTap {
|
||||
[[MapViewController sharedController] openSettings];
|
||||
}
|
||||
|
||||
- (void)stopRoutingButtonDidTap {
|
||||
[MWMSearch clear];
|
||||
[MWMRouter stopRouting];
|
||||
[self.searchManager close];
|
||||
}
|
||||
|
||||
#pragma mark - SearchOnMapManagerObserver
|
||||
|
||||
- (void)searchManagerWithDidChangeState:(SearchOnMapState)state {
|
||||
[self.navigationDashboardView searchManagerWithDidChangeState:state];
|
||||
}
|
||||
|
||||
#pragma mark - Available area
|
||||
|
||||
+ (void)updateNavigationInfoAvailableArea:(CGRect)frame {
|
||||
[[self sharedManager].navigationDashboardView updateNavigationInfoAvailableArea:frame];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,31 +1,8 @@
|
|||
#import "MWMCircularProgressState.h"
|
||||
#import "MWMRouterType.h"
|
||||
#import "RoutePreviewView.h"
|
||||
|
||||
typedef NS_ENUM(NSInteger, MWMDrivingOptionsState) {
|
||||
MWMDrivingOptionsStateNone,
|
||||
MWMDrivingOptionsStateDefine,
|
||||
MWMDrivingOptionsStateChange
|
||||
};
|
||||
|
||||
@class MWMRoutePreview;
|
||||
|
||||
@protocol MWMRoutePreviewDelegate
|
||||
|
||||
- (void)routePreviewDidPressDrivingOptions:(MWMRoutePreview *)routePreview;
|
||||
|
||||
@end
|
||||
|
||||
@interface MWMRoutePreview : UIView
|
||||
@interface MWMRoutePreview : UIView <RoutePreviewView>
|
||||
|
||||
@property(nonatomic) MWMDrivingOptionsState drivingOptionsState;
|
||||
@property(weak, nonatomic) id<MWMRoutePreviewDelegate> delegate;
|
||||
|
||||
- (void)addToView:(UIView *)superview;
|
||||
- (void)remove;
|
||||
|
||||
- (void)statePrepare;
|
||||
- (void)selectRouter:(MWMRouterType)routerType;
|
||||
- (void)router:(MWMRouterType)routerType setState:(MWMCircularProgressState)state;
|
||||
- (void)router:(MWMRouterType)routerType setProgress:(CGFloat)progress;
|
||||
|
||||
@end
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
#import "RoutePreviewView.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class MWMNavigationDashboardEntity;
|
||||
|
||||
@interface NavigationDashboardView : NSObject
|
||||
|
||||
@property(weak, nonatomic) id<MWMRoutePreviewDelegate> delegate;
|
||||
|
||||
- (instancetype)initWithOwnerView:(UIView *)ownerView;
|
||||
|
||||
- (void)loadPreview;
|
||||
- (void)onNavigationInfoUpdated:(MWMNavigationDashboardEntity *)entity;
|
||||
- (void)setDrivingOptionState:(MWMDrivingOptionsState)state;
|
||||
- (void)searchManagerWithDidChangeState:(SearchOnMapState)state;
|
||||
- (void)updateNavigationInfoAvailableArea:(CGRect)frame;
|
||||
- (void)setRouteBuilderProgress:(MWMRouterType)router progress:(CGFloat)progress;
|
||||
|
||||
- (void)setHidden;
|
||||
- (void)statePrepare;
|
||||
- (void)statePlanning;
|
||||
- (void)stateError:(NSString *_Nonnull)errorMessage;
|
||||
- (void)stateReady;
|
||||
- (void)onRouteStart;
|
||||
- (void)onRouteStop;
|
||||
- (void)onRoutePointsUpdated;
|
||||
- (void)stateNavigation;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,245 @@
|
|||
#import "NavigationDashboardView.h"
|
||||
#import "MWMNavigationDashboardManager.h"
|
||||
#import "MWMMapViewControlsManager.h"
|
||||
#import "MWMNavigationInfoView.h"
|
||||
#import "MWMRoutePreview.h"
|
||||
#import "MWMSearch.h"
|
||||
#import "MapViewController.h"
|
||||
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
NSString *kRoutePreviewIPhoneXibName = @"MWMiPhoneRoutePreview";
|
||||
NSString *kNavigationInfoViewXibName = @"MWMNavigationInfoView";
|
||||
NSString *kNavigationControlViewXibName = @"NavigationControlView";
|
||||
|
||||
@interface NavigationDashboardView()
|
||||
|
||||
@property(nonatomic) IBOutlet MWMBaseRoutePreviewStatus *baseRoutePreviewStatus;
|
||||
@property(nonatomic) IBOutlet MWMNavigationControlView *navigationControlView;
|
||||
@property(nonatomic) IBOutlet MWMNavigationInfoView *navigationInfoView;
|
||||
@property(nonatomic) IBOutlet MWMRoutePreview *routePreview;
|
||||
@property(nonatomic) IBOutlet MWMTransportRoutePreviewStatus *transportRoutePreviewStatus;
|
||||
@property(nonatomic) IBOutletCollection(MWMRouteStartButton) NSArray *goButtons;
|
||||
@property(nonatomic) MWMRouteManagerTransitioningManager *routeManagerTransitioningManager;
|
||||
@property(weak, nonatomic) IBOutlet UIButton *showRouteManagerButton;
|
||||
@property(weak, nonatomic) IBOutlet UIView *goButtonsContainer;
|
||||
@property(weak, nonatomic) UIView *ownerView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NavigationDashboardView
|
||||
|
||||
- (instancetype)initWithOwnerView:(UIView *)ownerView {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.ownerView = ownerView;
|
||||
[self loadPreview];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)loadPreview {
|
||||
[NSBundle.mainBundle loadNibNamed:kRoutePreviewIPhoneXibName owner:self options:nil];
|
||||
auto const ownerView = self.ownerView;
|
||||
self.baseRoutePreviewStatus.ownerView = ownerView;
|
||||
self.transportRoutePreviewStatus.ownerView = ownerView;
|
||||
}
|
||||
|
||||
- (void)setRouteBuilderProgress:(MWMRouterType)router progress:(CGFloat)progress {
|
||||
[self.routePreview router:router setProgress:progress];
|
||||
}
|
||||
|
||||
- (void)onNavigationInfoUpdated:(MWMNavigationDashboardEntity *)entity {
|
||||
[_navigationInfoView onNavigationInfoUpdated:entity];
|
||||
bool const isPublicTransport = ([MWMRouter type] == MWMRouterTypePublicTransport);
|
||||
bool const isRuler = ([MWMRouter type] == MWMRouterTypeRuler);
|
||||
if (isPublicTransport || isRuler)
|
||||
[_transportRoutePreviewStatus onNavigationInfoUpdated:entity prependDistance:isRuler];
|
||||
else
|
||||
[_baseRoutePreviewStatus onNavigationInfoUpdated:entity];
|
||||
[_navigationControlView onNavigationInfoUpdated:entity];
|
||||
}
|
||||
|
||||
- (void)setDrivingOptionState:(MWMDrivingOptionsState)state {
|
||||
self.routePreview.drivingOptionsState = state;
|
||||
}
|
||||
|
||||
- (void)onRoutePointsUpdated {
|
||||
[self.navigationInfoView updateToastView];
|
||||
}
|
||||
|
||||
- (void)updateGoButtonTitle {
|
||||
NSString *title = L(@"p2p_start");
|
||||
|
||||
for (MWMRouteStartButton *button in self.goButtons)
|
||||
[button setTitle:title forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
#pragma mark - State changes
|
||||
|
||||
- (void)setHidden {
|
||||
self.routePreview = nil;
|
||||
self.navigationInfoView.state = MWMNavigationInfoViewStateHidden;
|
||||
self.navigationInfoView = nil;
|
||||
_navigationControlView.isVisible = NO;
|
||||
_navigationControlView = nil;
|
||||
[self.baseRoutePreviewStatus hide];
|
||||
[_transportRoutePreviewStatus hide];
|
||||
_transportRoutePreviewStatus = nil;
|
||||
}
|
||||
|
||||
- (void)statePrepare {
|
||||
self.navigationInfoView.state = MWMNavigationInfoViewStatePrepare;
|
||||
[self.routePreview addToView:self.ownerView];
|
||||
[self.routePreview statePrepare];
|
||||
[self.routePreview selectRouter:[MWMRouter type]];
|
||||
[self updateGoButtonTitle];
|
||||
[self.baseRoutePreviewStatus hide];
|
||||
[_transportRoutePreviewStatus hide];
|
||||
for (MWMRouteStartButton *button in self.goButtons)
|
||||
[button statePrepare];
|
||||
}
|
||||
|
||||
- (void)statePlanning {
|
||||
// [self statePrepare];
|
||||
[self.routePreview router:[MWMRouter type] setState:MWMCircularProgressStateSpinner];
|
||||
}
|
||||
|
||||
- (void)stateError:(NSString *_Nonnull)errorMessage {
|
||||
[self.routePreview router:[MWMRouter type] setState:MWMCircularProgressStateFailed];
|
||||
[self updateGoButtonTitle];
|
||||
[self.baseRoutePreviewStatus showErrorWithMessage:errorMessage];
|
||||
for (MWMRouteStartButton *button in self.goButtons)
|
||||
[button stateError];
|
||||
}
|
||||
|
||||
- (void)stateReady {
|
||||
[self updateGoButtonTitle];
|
||||
bool const isTransport = ([MWMRouter type] == MWMRouterTypePublicTransport);
|
||||
bool const isRuler = ([MWMRouter type] == MWMRouterTypeRuler);
|
||||
if (isTransport || isRuler)
|
||||
[self.transportRoutePreviewStatus showReady];
|
||||
else
|
||||
[self.baseRoutePreviewStatus showReady];
|
||||
self.goButtonsContainer.hidden = isTransport || isRuler;
|
||||
for (MWMRouteStartButton *button in self.goButtons)
|
||||
{
|
||||
if (isRuler)
|
||||
[button stateHidden];
|
||||
else
|
||||
[button stateReady];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onRouteStart {
|
||||
}
|
||||
|
||||
- (void)onRouteStop {
|
||||
}
|
||||
|
||||
- (void)stateNavigation {
|
||||
self.routePreview = nil;
|
||||
self.navigationInfoView.state = MWMNavigationInfoViewStateNavigation;
|
||||
self.navigationControlView.isVisible = YES;
|
||||
[self.baseRoutePreviewStatus hide];
|
||||
[_transportRoutePreviewStatus hide];
|
||||
_transportRoutePreviewStatus = nil;
|
||||
}
|
||||
|
||||
#pragma mark - MWMRoutePreviewStatus
|
||||
|
||||
- (IBAction)showRouteManager {
|
||||
MWMRouteManagerViewModel * routeManagerViewModel = [[MWMRouteManagerViewModel alloc] init];
|
||||
MWMRouteManagerViewController * routeManager = [[MWMRouteManagerViewController alloc] initWithViewModel:routeManagerViewModel];
|
||||
routeManager.modalPresentationStyle = UIModalPresentationCustom;
|
||||
|
||||
self.routeManagerTransitioningManager = [[MWMRouteManagerTransitioningManager alloc] init];
|
||||
routeManager.transitioningDelegate = self.routeManagerTransitioningManager;
|
||||
|
||||
[[MapViewController sharedController] presentViewController:routeManager animated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
@synthesize routePreview = _routePreview;
|
||||
- (MWMRoutePreview *)routePreview {
|
||||
if (!_routePreview)
|
||||
[self loadPreview];
|
||||
return _routePreview;
|
||||
}
|
||||
|
||||
- (void)setRoutePreview:(MWMRoutePreview *)routePreview {
|
||||
if (routePreview == _routePreview)
|
||||
return;
|
||||
[_routePreview remove];
|
||||
_routePreview = routePreview;
|
||||
_routePreview.delegate = self.delegate;
|
||||
}
|
||||
|
||||
- (MWMBaseRoutePreviewStatus *)baseRoutePreviewStatus {
|
||||
if (!_baseRoutePreviewStatus)
|
||||
[self loadPreview];
|
||||
return _baseRoutePreviewStatus;
|
||||
}
|
||||
|
||||
- (MWMTransportRoutePreviewStatus *)transportRoutePreviewStatus {
|
||||
if (!_transportRoutePreviewStatus)
|
||||
[self loadPreview];
|
||||
return _transportRoutePreviewStatus;
|
||||
}
|
||||
|
||||
- (MWMNavigationInfoView *)navigationInfoView {
|
||||
if (!_navigationInfoView) {
|
||||
[NSBundle.mainBundle loadNibNamed:kNavigationInfoViewXibName owner:self options:nil];
|
||||
_navigationInfoView.state = MWMNavigationInfoViewStateHidden;
|
||||
_navigationInfoView.ownerView = self.ownerView;
|
||||
}
|
||||
return _navigationInfoView;
|
||||
}
|
||||
|
||||
- (MWMNavigationControlView *)navigationControlView {
|
||||
if (!_navigationControlView) {
|
||||
[NSBundle.mainBundle loadNibNamed:kNavigationControlViewXibName owner:self options:nil];
|
||||
_navigationControlView.ownerView = self.ownerView;
|
||||
}
|
||||
return _navigationControlView;
|
||||
}
|
||||
|
||||
#pragma mark - Button tap Actions
|
||||
|
||||
- (IBAction)ttsButtonAction {
|
||||
[self.delegate ttsButtonDidTap];
|
||||
}
|
||||
|
||||
- (IBAction)settingsButtonAction {
|
||||
[self.delegate settingsButtonDidTap];
|
||||
}
|
||||
|
||||
- (IBAction)stopRoutingButtonAction {
|
||||
[self.delegate stopRoutingButtonDidTap];
|
||||
}
|
||||
|
||||
- (IBAction)routingStartTouchUpInside {
|
||||
[self.delegate routingStartButtonDidTap];
|
||||
}
|
||||
|
||||
#pragma mark - SearchOnMapManagerObserver
|
||||
|
||||
- (void)searchManagerWithDidChangeState:(SearchOnMapState)state {
|
||||
switch (state) {
|
||||
case SearchOnMapStateClosed:
|
||||
[self.navigationInfoView setSearchState:NavigationSearchState::MinimizedNormal animated:YES];
|
||||
break;
|
||||
case SearchOnMapStateHidden:
|
||||
case SearchOnMapStateSearching:
|
||||
[self.navigationInfoView setMapSearch];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Available area
|
||||
|
||||
- (void)updateNavigationInfoAvailableArea:(CGRect)frame {
|
||||
_navigationInfoView.availableArea = frame;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,6 +1,5 @@
|
|||
final class TransportRuler: TransportTransitCell {
|
||||
enum Config {
|
||||
static let backgroundCornerRadius: CGFloat = 4
|
||||
static var backgroundColor: UIColor { return UIColor.blackOpaque() }
|
||||
static var imageColor: UIColor { return UIColor.blackSecondaryText() }
|
||||
static var labelTextColor: UIColor { return .black }
|
||||
|
@ -10,7 +9,7 @@ final class TransportRuler: TransportTransitCell {
|
|||
|
||||
@IBOutlet private weak var background: UIView! {
|
||||
didSet {
|
||||
background.layer.setCorner(radius: Config.backgroundCornerRadius)
|
||||
background.layer.setCornerRadius(.buttonSmall)
|
||||
background.backgroundColor = Config.backgroundColor
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
final class TransportTransitPedestrian: TransportTransitCell {
|
||||
enum Config {
|
||||
static let backgroundCornerRadius: CGFloat = 4
|
||||
static var backgroundColor: UIColor { return UIColor.blackOpaque() }
|
||||
static var imageColor: UIColor { return UIColor.blackSecondaryText() }
|
||||
}
|
||||
|
||||
@IBOutlet private weak var background: UIView! {
|
||||
didSet {
|
||||
background.layer.setCorner(radius: Config.backgroundCornerRadius)
|
||||
background.layer.setCornerRadius(.buttonSmall)
|
||||
background.backgroundColor = Config.backgroundColor
|
||||
}
|
||||
}
|
||||
|
||||
@IBOutlet private weak var image: UIImageView! {
|
||||
didSet {
|
||||
image.image = #imageLiteral(resourceName: "ic_walk")
|
||||
image.image = UIImage(resource: .icWalk)
|
||||
image.tintColor = Config.imageColor
|
||||
image.contentMode = .scaleAspectFit
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
final class TransportTransitTrain: TransportTransitCell {
|
||||
enum Config {
|
||||
static let backgroundCornerRadius: CGFloat = 4
|
||||
static var labelTextColor: UIColor { return .white }
|
||||
static let labelTextFont = UIFont.bold12()
|
||||
static let labelTrailing: CGFloat = 4
|
||||
|
@ -8,7 +7,7 @@ final class TransportTransitTrain: TransportTransitCell {
|
|||
|
||||
@IBOutlet private weak var background: UIView! {
|
||||
didSet {
|
||||
background.layer.setCorner(radius: Config.backgroundCornerRadius)
|
||||
background.layer.setCornerRadius(.buttonSmall)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
#import "MWMCircularProgressState.h"
|
||||
#import "MWMRouterType.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, MWMDrivingOptionsState) {
|
||||
MWMDrivingOptionsStateNone,
|
||||
MWMDrivingOptionsStateDefine,
|
||||
MWMDrivingOptionsStateChange
|
||||
};
|
||||
|
||||
@protocol MWMRoutePreviewDelegate;
|
||||
|
||||
@protocol RoutePreviewView <NSObject>
|
||||
|
||||
@property(nonatomic) MWMDrivingOptionsState drivingOptionsState;
|
||||
@property(weak, nonatomic) id<MWMRoutePreviewDelegate> delegate;
|
||||
|
||||
- (void)addToView:(UIView *)superview;
|
||||
- (void)remove;
|
||||
|
||||
- (void)statePrepare;
|
||||
- (void)selectRouter:(MWMRouterType)routerType;
|
||||
- (void)router:(MWMRouterType)routerType setState:(MWMCircularProgressState)state;
|
||||
- (void)router:(MWMRouterType)routerType setProgress:(CGFloat)progress;
|
||||
|
||||
@end
|
||||
|
||||
@protocol MWMRoutePreviewDelegate <NSObject>
|
||||
|
||||
- (void)routePreviewDidPressDrivingOptions:(id<RoutePreviewView>)routePreview;
|
||||
- (void)ttsButtonDidTap;
|
||||
- (void)settingsButtonDidTap;
|
||||
- (void)stopRoutingButtonDidTap;
|
||||
- (void)routingStartButtonDidTap;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -7,6 +7,11 @@
|
|||
@class MWMMapDownloadDialog;
|
||||
@class BookmarksCoordinator;
|
||||
@class SearchOnMapManager;
|
||||
@class SideButtonsArea;
|
||||
@class WidgetsArea;
|
||||
@class TrafficButtonArea;
|
||||
@class PlacePageArea;
|
||||
|
||||
@protocol MWMLocationModeListener;
|
||||
|
||||
@interface MapViewController : MWMViewController
|
||||
|
@ -52,5 +57,11 @@
|
|||
@property(nonatomic) MWMMyPositionMode currentPositionMode;
|
||||
@property(strong, nonatomic) IBOutlet EAGLView * _Nonnull mapView;
|
||||
@property(strong, nonatomic) IBOutlet UIView * _Nonnull controlsView;
|
||||
@property(nonatomic) UIView * _Nonnull searchContainer;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet SideButtonsArea * sideButtonsArea;
|
||||
@property (weak, nonatomic) IBOutlet WidgetsArea * widgetsArea;
|
||||
@property (weak, nonatomic) IBOutlet TrafficButtonArea * trafficButtonArea;
|
||||
@property (weak, nonatomic) IBOutlet PlacePageArea * placePageArea;
|
||||
|
||||
@end
|
||||
|
|
|
@ -148,34 +148,48 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
|||
|
||||
- (void)setupPlacePageContainer {
|
||||
self.placePageContainer = [[TouchTransparentView alloc] initWithFrame:self.view.bounds];
|
||||
self.placePageContainer.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self.view addSubview:self.placePageContainer];
|
||||
[self.view bringSubviewToFront:self.placePageContainer];
|
||||
|
||||
self.placePageContainer.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
self.placePageLeadingConstraint = [self.placePageContainer.leadingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor constant:kPlacePageLeadingOffset];
|
||||
self.placePageLeadingConstraint.active = YES;
|
||||
if (IPAD)
|
||||
self.placePageLeadingConstraint.priority = UILayoutPriorityDefaultLow;
|
||||
|
||||
self.placePageWidthConstraint = [self.placePageContainer.widthAnchor constraintEqualToConstant:0];
|
||||
self.placePageWidthConstraint = [self.placePageContainer.widthAnchor constraintEqualToConstant:kPlacePageCompactWidth];
|
||||
self.placePageTrailingConstraint = [self.placePageContainer.trailingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.trailingAnchor];
|
||||
|
||||
[self.placePageContainer.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor].active = YES;
|
||||
if (IPAD) {
|
||||
self.placePageLeadingConstraint.priority = UILayoutPriorityDefaultLow;
|
||||
[self.placePageContainer.bottomAnchor constraintLessThanOrEqualToAnchor:self.view.bottomAnchor].active = YES;
|
||||
}
|
||||
else {
|
||||
[self.placePageContainer.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;
|
||||
}
|
||||
NSLayoutConstraint * topConstraint = [self.placePageContainer.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor];
|
||||
|
||||
NSLayoutConstraint * bottomConstraint;
|
||||
if (IPAD)
|
||||
bottomConstraint = [self.placePageContainer.bottomAnchor constraintLessThanOrEqualToAnchor:self.view.bottomAnchor];
|
||||
else
|
||||
bottomConstraint = [self.placePageContainer.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor];
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[
|
||||
self.placePageLeadingConstraint,
|
||||
topConstraint,
|
||||
bottomConstraint,
|
||||
]];
|
||||
|
||||
[self updatePlacePageContainerConstraints];
|
||||
}
|
||||
|
||||
- (void)setupSearchContainer {
|
||||
if (self.searchContainer != nil)
|
||||
return;
|
||||
self.searchContainer = [[TouchTransparentView alloc] initWithFrame:self.view.bounds];
|
||||
[self.view addSubview:self.searchContainer];
|
||||
[self.view bringSubviewToFront:self.searchContainer];
|
||||
self.searchContainer.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
}
|
||||
|
||||
- (void)updatePlacePageContainerConstraints {
|
||||
const BOOL isLimitedWidth = IPAD || self.traitCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact;
|
||||
[self.placePageWidthConstraint setConstant:kPlacePageCompactWidth];
|
||||
|
||||
if (IPAD && self.searchViewContainer != nil) {
|
||||
NSLayoutConstraint * leadingToSearchConstraint = [self.placePageContainer.leadingAnchor constraintEqualToAnchor:self.searchViewContainer.trailingAnchor constant:kPlacePageLeadingOffset];
|
||||
if (IPAD && self.searchViewAvailableArea != nil) {
|
||||
NSLayoutConstraint * leadingToSearchConstraint = [self.placePageContainer.leadingAnchor constraintEqualToAnchor:self.searchViewAvailableArea.trailingAnchor constant:kPlacePageLeadingOffset];
|
||||
leadingToSearchConstraint.priority = UILayoutPriorityDefaultHigh;
|
||||
leadingToSearchConstraint.active = isLimitedWidth;
|
||||
}
|
||||
|
@ -259,9 +273,6 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
|||
return;
|
||||
}
|
||||
|
||||
if (self.searchManager.isSearching && type == df::TouchEvent::TOUCH_MOVE)
|
||||
[self.searchManager setMapIsDragging];
|
||||
|
||||
NSArray *allTouches = [[event allTouches] allObjects];
|
||||
if ([allTouches count] < 1)
|
||||
return;
|
||||
|
@ -273,6 +284,10 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
|||
UITouch *touch = [allTouches objectAtIndex:0];
|
||||
CGPoint const pt = [touch locationInView:v];
|
||||
|
||||
// Check if the tap is inside searchView)
|
||||
if (self.searchManager.isSearching && type == df::TouchEvent::TOUCH_MOVE && !CGRectContainsPoint(self.searchViewAvailableArea.frame, pt))
|
||||
[self.searchManager setMapIsDragging];
|
||||
|
||||
e.SetTouchType(type);
|
||||
|
||||
df::Touch t0;
|
||||
|
@ -372,6 +387,7 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
|||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
[self setupPlacePageContainer];
|
||||
[self setupSearchContainer];
|
||||
|
||||
if (@available(iOS 14.0, *))
|
||||
[self setupTrackPadGestureRecognizers];
|
||||
|
@ -726,12 +742,12 @@ NSString *const kSettingsSegue = @"Map2Settings";
|
|||
|
||||
- (SearchOnMapManager *)searchManager {
|
||||
if (!_searchManager)
|
||||
_searchManager = [[SearchOnMapManager alloc] initWithNavigationController:self.navigationController];
|
||||
_searchManager = [[SearchOnMapManager alloc] init];
|
||||
return _searchManager;
|
||||
}
|
||||
|
||||
- (UIView * _Nullable)searchViewContainer {
|
||||
return self.searchManager.viewController.view;
|
||||
- (UIView * _Nullable)searchViewAvailableArea {
|
||||
return self.searchManager.viewController.availableAreaView;
|
||||
}
|
||||
|
||||
- (BOOL)hasNavigationBar {
|
||||
|
|
|
@ -61,7 +61,7 @@ using Observers = NSHashTable<Observer>;
|
|||
- (void)searchEverywhere {
|
||||
self.lastSearchTimestamp += 1;
|
||||
NSUInteger const timestamp = self.lastSearchTimestamp;
|
||||
|
||||
|
||||
search::EverywhereSearchParams params{
|
||||
m_query, m_locale, {} /* default timeout */, m_isCategory,
|
||||
// m_onResults
|
||||
|
@ -156,6 +156,7 @@ using Observers = NSHashTable<Observer>;
|
|||
|
||||
+ (void)showResultAtIndex:(NSUInteger)index {
|
||||
auto const & result = [MWMSearch manager]->m_everywhereResults[index];
|
||||
GetFramework().StopLocationFollow();
|
||||
GetFramework().SelectSearchResult(result, true);
|
||||
}
|
||||
|
||||
|
@ -168,8 +169,13 @@ using Observers = NSHashTable<Observer>;
|
|||
|
||||
+ (void)showEverywhereSearchResultsOnMap {
|
||||
MWMSearch * manager = [MWMSearch manager];
|
||||
if (![MWMRouter isRoutingActive])
|
||||
GetFramework().ShowSearchResults(manager->m_everywhereResults);
|
||||
if (![MWMRouter isRoutingActive]) {
|
||||
auto const & results = manager->m_everywhereResults;
|
||||
if (results.GetCount() == 1)
|
||||
[self showResultAtIndex:0];
|
||||
else
|
||||
GetFramework().ShowSearchResults(manager->m_everywhereResults);
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)showViewportSearchResultsOnMap {
|
||||
|
|
|
@ -4,6 +4,7 @@ class Style: ExpressibleByDictionaryLiteral {
|
|||
case borderColor
|
||||
case borderWidth
|
||||
case cornerRadius
|
||||
case maskedCorners
|
||||
case shadowColor
|
||||
case shadowOpacity
|
||||
case shadowOffset
|
||||
|
@ -115,11 +116,16 @@ extension Style {
|
|||
set { params[.borderWidth] = newValue }
|
||||
}
|
||||
|
||||
var cornerRadius: CGFloat? {
|
||||
get { return self[.cornerRadius] as? CGFloat }
|
||||
var cornerRadius: CornerRadius? {
|
||||
get { return self[.cornerRadius] as? CornerRadius }
|
||||
set { params[.cornerRadius] = newValue }
|
||||
}
|
||||
|
||||
var maskedCorners: CACornerMask? {
|
||||
get { return self[.maskedCorners] as? CACornerMask }
|
||||
set { params[.maskedCorners] = newValue }
|
||||
}
|
||||
|
||||
var shadowColor: UIColor? {
|
||||
get { return self[.shadowColor] as? UIColor }
|
||||
set { params[.shadowColor] = newValue }
|
||||
|
|
21
iphone/Maps/Core/Theme/CornerRadius.swift
Normal file
21
iphone/Maps/Core/Theme/CornerRadius.swift
Normal file
|
@ -0,0 +1,21 @@
|
|||
enum CornerRadius {
|
||||
case modalSheet
|
||||
case buttonDefault
|
||||
case buttonDefaultSmall
|
||||
case buttonSmall
|
||||
case grabber
|
||||
case custom(CGFloat)
|
||||
}
|
||||
|
||||
extension CornerRadius {
|
||||
var value: CGFloat {
|
||||
switch self {
|
||||
case .modalSheet: return 12
|
||||
case .buttonDefault: return 8
|
||||
case .buttonDefaultSmall: return 6
|
||||
case .buttonSmall: return 4
|
||||
case .grabber: return 2.5
|
||||
case .custom(let value): return value
|
||||
}
|
||||
}
|
||||
}
|
|
@ -59,6 +59,9 @@ enum GlobalStyleSheet: String, CaseIterable {
|
|||
case white = "MWMWhite"
|
||||
case datePickerView = "DatePickerView"
|
||||
case valueStepperView = "ValueStepperView"
|
||||
case grabber
|
||||
case modalSheetBackground
|
||||
case modalSheetContent
|
||||
}
|
||||
|
||||
extension GlobalStyleSheet: IStyleSheet {
|
||||
|
@ -176,7 +179,7 @@ extension GlobalStyleSheet: IStyleSheet {
|
|||
s.backgroundColor = colors.tabBarButtonBackground
|
||||
s.tintColor = colors.blackSecondaryText
|
||||
s.coloring = MWMButtonColoring.black
|
||||
s.cornerRadius = 8
|
||||
s.cornerRadius = .buttonDefault
|
||||
s.shadowColor = UIColor(0,0,0,alpha20)
|
||||
s.shadowOpacity = 1
|
||||
s.shadowOffset = CGSize(width: 0, height: 1)
|
||||
|
@ -184,7 +187,7 @@ extension GlobalStyleSheet: IStyleSheet {
|
|||
}
|
||||
case .trackRecordingWidgetButton:
|
||||
return .addFrom(Self.bottomTabBarButton) { s in
|
||||
s.cornerRadius = 23
|
||||
s.cornerRadius = .custom(23)
|
||||
}
|
||||
case .blackOpaqueBackground:
|
||||
return .add { s in
|
||||
|
@ -232,7 +235,7 @@ extension GlobalStyleSheet: IStyleSheet {
|
|||
}
|
||||
case .dialogView:
|
||||
return .add { s in
|
||||
s.cornerRadius = 8
|
||||
s.cornerRadius = .buttonDefault
|
||||
s.shadowRadius = 2
|
||||
s.shadowColor = UIColor(0,0,0,alpha26)
|
||||
s.shadowOpacity = 1
|
||||
|
@ -242,7 +245,7 @@ extension GlobalStyleSheet: IStyleSheet {
|
|||
}
|
||||
case .alertView:
|
||||
return .add { s in
|
||||
s.cornerRadius = 12
|
||||
s.cornerRadius = .modalSheet
|
||||
s.shadowRadius = 6
|
||||
s.shadowColor = UIColor(0,0,0,alpha20)
|
||||
s.shadowOpacity = 1
|
||||
|
@ -273,7 +276,7 @@ extension GlobalStyleSheet: IStyleSheet {
|
|||
case .flatNormalButton:
|
||||
return .add { s in
|
||||
s.font = fonts.medium14
|
||||
s.cornerRadius = 8
|
||||
s.cornerRadius = .buttonDefault
|
||||
s.clip = true
|
||||
s.fontColor = colors.whitePrimaryText
|
||||
s.backgroundColor = colors.linkBlue
|
||||
|
@ -288,7 +291,7 @@ extension GlobalStyleSheet: IStyleSheet {
|
|||
case .flatNormalTransButton:
|
||||
return .add { s in
|
||||
s.font = fonts.medium14
|
||||
s.cornerRadius = 8
|
||||
s.cornerRadius = .buttonDefault
|
||||
s.clip = true
|
||||
s.fontColor = colors.linkBlue
|
||||
s.backgroundColor = colors.clear
|
||||
|
@ -330,7 +333,7 @@ extension GlobalStyleSheet: IStyleSheet {
|
|||
case .flatRedButton:
|
||||
return .add { s in
|
||||
s.font = fonts.medium14
|
||||
s.cornerRadius = 8
|
||||
s.cornerRadius = .buttonDefault
|
||||
s.fontColor = colors.whitePrimaryText
|
||||
s.backgroundColor = colors.buttonRed
|
||||
s.fontColorHighlighted = colors.buttonRedHighlighted
|
||||
|
@ -346,7 +349,7 @@ extension GlobalStyleSheet: IStyleSheet {
|
|||
return .add { s in
|
||||
s.font = fonts.regular14
|
||||
s.fontColor = colors.linkBlue
|
||||
s.cornerRadius = 8
|
||||
s.cornerRadius = .buttonDefault
|
||||
s.borderColor = colors.linkBlue
|
||||
s.borderWidth = 1
|
||||
s.fontColorHighlighted = colors.linkBlueHighlighted
|
||||
|
@ -358,7 +361,7 @@ extension GlobalStyleSheet: IStyleSheet {
|
|||
s.fontColor = colors.linkBlue
|
||||
s.fontColorHighlighted = colors.white
|
||||
s.borderColor = colors.linkBlue
|
||||
s.cornerRadius = 8
|
||||
s.cornerRadius = .buttonDefault
|
||||
s.borderWidth = 1
|
||||
s.backgroundColor = colors.clear
|
||||
s.backgroundColorHighlighted = colors.linkBlue
|
||||
|
@ -429,6 +432,26 @@ extension GlobalStyleSheet: IStyleSheet {
|
|||
s.fontColor = colors.blackPrimaryText
|
||||
s.coloring = MWMButtonColoring.blue
|
||||
}
|
||||
case .grabber:
|
||||
return .addFrom(Self.background) { s in
|
||||
s.cornerRadius = .grabber
|
||||
}
|
||||
case .modalSheetBackground:
|
||||
return .add { s in
|
||||
s.backgroundColor = colors.white
|
||||
s.shadowColor = UIColor.black
|
||||
s.shadowOffset = CGSize(width: 0, height: 1)
|
||||
s.shadowOpacity = 0.3
|
||||
s.shadowRadius = 6
|
||||
s.cornerRadius = .modalSheet
|
||||
s.clip = false
|
||||
s.maskedCorners = isIPad ? [] : [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
}
|
||||
case .modalSheetContent:
|
||||
return .addFrom(Self.modalSheetBackground) { s in
|
||||
s.backgroundColor = colors.clear
|
||||
s.clip = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ extension MapStyleSheet: IStyleSheet {
|
|||
s.backgroundColor = colors.clear
|
||||
s.borderColor = colors.clear
|
||||
s.borderWidth = 0
|
||||
s.cornerRadius = 6
|
||||
s.cornerRadius = .buttonDefaultSmall
|
||||
}
|
||||
case .mapMenuButtonEnabled:
|
||||
return .add { s in
|
||||
|
@ -37,7 +37,7 @@ extension MapStyleSheet: IStyleSheet {
|
|||
s.backgroundColor = colors.linkBlue
|
||||
s.borderColor = colors.linkBlue
|
||||
s.borderWidth = 2
|
||||
s.cornerRadius = 6
|
||||
s.cornerRadius = .buttonDefaultSmall
|
||||
}
|
||||
case .mapStreetNameBackgroundView:
|
||||
return .add { s in
|
||||
|
@ -90,7 +90,7 @@ extension MapStyleSheet: IStyleSheet {
|
|||
case .mapFirstTurnView:
|
||||
return .add { s in
|
||||
s.backgroundColor = colors.linkBlue
|
||||
s.cornerRadius = 4
|
||||
s.cornerRadius = .buttonSmall
|
||||
s.shadowRadius = 2
|
||||
s.shadowColor = colors.blackHintText
|
||||
s.shadowOpacity = 1
|
||||
|
@ -104,7 +104,7 @@ extension MapStyleSheet: IStyleSheet {
|
|||
return .add { s in
|
||||
s.shadowOffset = CGSize(width: 0, height: 3)
|
||||
s.shadowRadius = 6
|
||||
s.cornerRadius = 4
|
||||
s.cornerRadius = .buttonSmall
|
||||
s.shadowOpacity = 1
|
||||
s.backgroundColor = colors.white
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ extension PlacePageStyleSheet: IStyleSheet {
|
|||
case .ppTitlePopularView:
|
||||
return .add { s in
|
||||
s.backgroundColor = colors.linkBlueHighlighted
|
||||
s.cornerRadius = 10
|
||||
s.cornerRadius = .custom(10)
|
||||
}
|
||||
case .ppActionBarTitle:
|
||||
return .add { s in
|
||||
|
@ -45,7 +45,7 @@ extension PlacePageStyleSheet: IStyleSheet {
|
|||
case .ppElevationProfileDescriptionCell:
|
||||
return .add { s in
|
||||
s.backgroundColor = colors.blackOpaque
|
||||
s.cornerRadius = 6
|
||||
s.cornerRadius = .buttonDefault
|
||||
}
|
||||
case .ppElevationProfileExtendedDifficulty:
|
||||
return .add { s in
|
||||
|
@ -110,7 +110,7 @@ extension PlacePageStyleSheet: IStyleSheet {
|
|||
case .ppHeaderView:
|
||||
return .add { s in
|
||||
s.backgroundColor = colors.white
|
||||
s.cornerRadius = 10
|
||||
s.cornerRadius = .modalSheet
|
||||
s.clip = true
|
||||
}
|
||||
case .ppNavigationShadowView:
|
||||
|
@ -123,19 +123,15 @@ extension PlacePageStyleSheet: IStyleSheet {
|
|||
s.clip = false
|
||||
}
|
||||
case .ppBackgroundView:
|
||||
return .add { s in
|
||||
return .addFrom(GlobalStyleSheet.modalSheetBackground) { s in
|
||||
s.backgroundColor = colors.pressBackground
|
||||
s.cornerRadius = 10
|
||||
s.shadowColor = UIColor.black
|
||||
s.shadowOffset = CGSize(width: 0, height: 1)
|
||||
s.shadowOpacity = 0.6
|
||||
s.shadowRadius = 2
|
||||
s.maskedCorners = isIPad ? CACornerMask.all : [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
s.clip = false
|
||||
}
|
||||
case .ppView:
|
||||
return .add { s in
|
||||
s.backgroundColor = colors.clear
|
||||
s.cornerRadius = 10
|
||||
s.cornerRadius = .modalSheet
|
||||
s.clip = true
|
||||
}
|
||||
case .ppHeaderCircleIcon:
|
||||
|
|
|
@ -31,7 +31,7 @@ class UISearchBarRenderer: UIViewRenderer {
|
|||
} else {
|
||||
control.setSearchFieldBackgroundImage(UIImage(), for: .normal)
|
||||
}
|
||||
searchTextField.layer.setCorner(radius: 8)
|
||||
searchTextField.layer.setCornerRadius(.buttonDefault)
|
||||
searchTextField.layer.masksToBounds = true
|
||||
// Placeholder color
|
||||
if let placeholder = searchTextField.placeholder {
|
||||
|
|
|
@ -20,7 +20,7 @@ extension UITextField {
|
|||
class UITextFieldRenderer {
|
||||
class func render(_ control: UITextField, style: Style) {
|
||||
if let cornerRadius = style.cornerRadius {
|
||||
control.layer.setCorner(radius: cornerRadius)
|
||||
control.layer.setCornerRadius(cornerRadius)
|
||||
control.clipsToBounds = true
|
||||
}
|
||||
control.borderStyle = .none
|
||||
|
|
|
@ -46,7 +46,10 @@ class UIViewRenderer {
|
|||
control.layer.borderWidth = borderWidth
|
||||
}
|
||||
if let cornerRadius = style.cornerRadius {
|
||||
control.layer.cornerRadius = cornerRadius
|
||||
control.layer.cornerRadius = cornerRadius.value
|
||||
}
|
||||
if let maskedCorners = style.maskedCorners {
|
||||
control.layer.maskedCorners = maskedCorners
|
||||
}
|
||||
if let clip = style.clip {
|
||||
control.clipsToBounds = clip
|
||||
|
|
|
@ -1,107 +1,27 @@
|
|||
enum SearchStyleSheet: String, CaseIterable {
|
||||
case searchHeader
|
||||
case searchInstallButton = "SearchInstallButton"
|
||||
case searchBanner = "SearchBanner"
|
||||
case searchClosedBackground = "SearchClosedBackground"
|
||||
case searchCancelButton
|
||||
case searchPopularView = "SearchPopularView"
|
||||
case searchSideAvailableMarker = "SearchSideAvaliableMarker"
|
||||
case searchBarView = "SearchBarView"
|
||||
case searchActionBarView = "SearchActionBarView"
|
||||
case searchActionBarButton = "SearchActionBarButton"
|
||||
case searchSearchTextField = "SearchSearchTextField"
|
||||
case searchSearchTextFieldIcon = "SearchSearchTextFieldIcon"
|
||||
case searchDatePickerField = "SearchDatePickerField"
|
||||
case searchCellAvailable = "SearchCellAvaliable"
|
||||
}
|
||||
|
||||
extension SearchStyleSheet: IStyleSheet {
|
||||
func styleResolverFor(colors: IColors, fonts: IFonts) -> Theme.StyleResolver {
|
||||
switch self {
|
||||
case .searchHeader:
|
||||
return .add { s in
|
||||
s.backgroundColor = colors.primary
|
||||
iPhoneSpecific {
|
||||
s.shadowColor = UIColor.black
|
||||
s.shadowOffset = CGSize(width: 0, height: 1)
|
||||
s.shadowOpacity = 0.5
|
||||
s.shadowRadius = 3
|
||||
s.cornerRadius = 10
|
||||
}
|
||||
}
|
||||
case .searchInstallButton:
|
||||
return .add { s in
|
||||
s.cornerRadius = 10
|
||||
s.clip = true
|
||||
s.font = fonts.medium12
|
||||
s.fontColor = colors.blackSecondaryText
|
||||
s.backgroundColor = colors.searchPromoBackground
|
||||
}
|
||||
case .searchBanner:
|
||||
return .add { s in
|
||||
s.backgroundColor = colors.searchPromoBackground
|
||||
}
|
||||
case .searchClosedBackground:
|
||||
return .add { s in
|
||||
s.cornerRadius = 4
|
||||
s.backgroundColor = colors.blackHintText
|
||||
}
|
||||
case .searchPopularView:
|
||||
return .add { s in
|
||||
s.cornerRadius = 10
|
||||
s.cornerRadius = .custom(10)
|
||||
s.backgroundColor = colors.linkBlueHighlighted
|
||||
}
|
||||
case .searchSideAvailableMarker:
|
||||
return .add { s in
|
||||
s.backgroundColor = colors.ratingGreen
|
||||
}
|
||||
case .searchBarView:
|
||||
case .searchCancelButton:
|
||||
return .add { s in
|
||||
s.backgroundColor = colors.primary
|
||||
s.shadowRadius = 2
|
||||
s.shadowColor = UIColor(0, 0, 0, alpha26)
|
||||
s.shadowOpacity = 1
|
||||
s.shadowOffset = CGSize.zero
|
||||
}
|
||||
case .searchActionBarView:
|
||||
return .add { s in
|
||||
s.backgroundColor = colors.linkBlue
|
||||
s.cornerRadius = 20
|
||||
s.shadowRadius = 1
|
||||
s.shadowColor = UIColor(0, 0, 0, 0.24)
|
||||
s.shadowOffset = CGSize(width: 0, height: 2)
|
||||
s.shadowOpacity = 1
|
||||
}
|
||||
case .searchActionBarButton:
|
||||
return .add { s in
|
||||
s.backgroundColor = colors.clear
|
||||
s.fontColor = colors.whitePrimaryText
|
||||
s.font = fonts.semibold14
|
||||
s.coloring = .whiteText
|
||||
}
|
||||
case .searchSearchTextField:
|
||||
return .add { s in
|
||||
s.fontColor = colors.blackPrimaryText
|
||||
s.backgroundColor = colors.white
|
||||
s.tintColor = colors.blackSecondaryText
|
||||
s.cornerRadius = 8.0
|
||||
s.barTintColor = colors.primary
|
||||
}
|
||||
case .searchSearchTextFieldIcon:
|
||||
return .add { s in
|
||||
s.tintColor = colors.blackSecondaryText
|
||||
s.coloring = MWMButtonColoring.black
|
||||
s.color = colors.blackSecondaryText
|
||||
}
|
||||
case .searchDatePickerField:
|
||||
return .add { s in
|
||||
s.backgroundColor = colors.white
|
||||
s.cornerRadius = 4
|
||||
s.borderColor = colors.solidDividers
|
||||
s.borderWidth = 1
|
||||
}
|
||||
case .searchCellAvailable:
|
||||
return .addFrom(GlobalStyleSheet.tableCell) { s in
|
||||
s.backgroundColor = colors.transparentGreen
|
||||
s.fontColorHighlighted = colors.whitePrimaryTextHighlighted
|
||||
s.font = fonts.regular17
|
||||
s.backgroundColor = .clear
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,5 +82,3 @@
|
|||
"kilometers_per_hour" = "km/h";
|
||||
"stop_without_saving" = "Parar ensin guardar";
|
||||
"location_services_disabled_header" = "Los servicios d’ubicación tán desactivaos";
|
||||
"downloader_no_space_message" = "Desanicia los datos imprecisos";
|
||||
"m" = "m";
|
||||
|
|
|
@ -169,7 +169,7 @@
|
|||
"edit" = "Edita";
|
||||
|
||||
/* Warning message when doing search around current position */
|
||||
"unknown_current_position" = "Encara no s’ha determinat la vostra ubicació";
|
||||
"unknown_current_position" = "Encara no s'ha pogut determinar la vostra geolocalització";
|
||||
|
||||
/* Subject for emailed bookmark */
|
||||
"bookmark_share_email_subject" = "Ep, mireu el meu marcador a l'Organic Maps!";
|
||||
|
@ -973,7 +973,7 @@
|
|||
"car_used_on_the_car_screen" = "Ara esteu utilitzant els Organic Maps a la pantalla del cotxe";
|
||||
|
||||
/* Displayed on the phone screen. Button to display maps on the phone screen instead of a car */
|
||||
"car_continue_on_the_phone" = "Passa al telèfon";
|
||||
"car_continue_on_the_phone" = "Continua al telèfon";
|
||||
|
||||
/* Displayed on the Android Auto or CarPlay screen. Button to display maps on the car screen instead of a phone. Must be no more than 18 symbols! */
|
||||
"car_continue_in_the_car" = "A la pantalla del cotxe";
|
||||
|
|
|
@ -172,7 +172,7 @@
|
|||
"type.amenity.vending_machine" = "Màquina expenedora";
|
||||
"type.amenity.vending_machine.cigarettes" = "Dispensador de cigarrets";
|
||||
"type.amenity.vending_machine.coffee" = "Dispensador de cafè";
|
||||
"type.amenity.vending_machine.condoms" = "Dispensador de preservatius";
|
||||
"type.amenity.vending_machine.condoms" = "Condoms Dispenser";
|
||||
"type.amenity.vending_machine.drinks" = "Dispensador de begudes";
|
||||
"type.amenity.vending_machine.food" = "Dispensador d’aliments";
|
||||
"type.amenity.vending_machine.newspapers" = "Dispensador de periòdics";
|
||||
|
@ -215,7 +215,7 @@
|
|||
"type.boundary.administrative" = "Frontera administrativa";
|
||||
|
||||
/* Borders between countries. */
|
||||
"type.boundary.administrative.2" = "Frontera nacional";
|
||||
"type.boundary.administrative.2" = "National Border";
|
||||
|
||||
/* Country's primary subdivision borders, e.g. between regions, provinces, states.. */
|
||||
"type.boundary.administrative.3" = "Frontera regional";
|
||||
|
@ -274,7 +274,7 @@
|
|||
"type.cuisine.asian" = "Asiàtica";
|
||||
"type.cuisine.austrian" = "Austríaca";
|
||||
"type.cuisine.bagel" = "Bagel";
|
||||
"type.cuisine.balkan" = "Balcànica";
|
||||
"type.cuisine.balkan" = "Balkan";
|
||||
"type.cuisine.barbecue" = "Barbecue";
|
||||
"type.cuisine.bavarian" = "Bavarian";
|
||||
"type.cuisine.beef_bowl" = "Beef Bowl";
|
||||
|
@ -295,37 +295,37 @@
|
|||
"type.cuisine.diner" = "Diner";
|
||||
"type.cuisine.donut" = "Donut";
|
||||
"type.cuisine.ethiopian" = "Ethiopian";
|
||||
"type.cuisine.filipino" = "Filipina";
|
||||
"type.cuisine.filipino" = "Filipino";
|
||||
"type.cuisine.fine_dining" = "Fine Dining";
|
||||
"type.cuisine.fish" = "Fish";
|
||||
"type.cuisine.fish_and_chips" = "Fish and Chips";
|
||||
"type.cuisine.french" = "Francesa";
|
||||
"type.cuisine.french" = "French";
|
||||
"type.cuisine.friture" = "Friture";
|
||||
"type.cuisine.georgian" = "Georgiana";
|
||||
"type.cuisine.german" = "Alemanya";
|
||||
"type.cuisine.greek" = "Grega";
|
||||
"type.cuisine.grill" = "Graellada";
|
||||
"type.cuisine.georgian" = "Georgian";
|
||||
"type.cuisine.german" = "German";
|
||||
"type.cuisine.greek" = "Greek";
|
||||
"type.cuisine.grill" = "Grill";
|
||||
"type.cuisine.heuriger" = "Heuriger";
|
||||
"type.cuisine.hotdog" = "Hotdog";
|
||||
"type.cuisine.hungarian" = "Hongaresa";
|
||||
"type.cuisine.ice_cream" = "Gelat";
|
||||
"type.cuisine.indian" = "Índia";
|
||||
"type.cuisine.hungarian" = "Hungarian";
|
||||
"type.cuisine.ice_cream" = "Ice Cream";
|
||||
"type.cuisine.indian" = "Indian";
|
||||
"type.cuisine.indonesian" = "Indonesian";
|
||||
"type.cuisine.international" = "Internacional";
|
||||
"type.cuisine.irish" = "Irish";
|
||||
"type.cuisine.italian" = "Italian";
|
||||
"type.cuisine.italian_pizza" = "Italian, Pizza";
|
||||
"type.cuisine.japanese" = "Japonesa";
|
||||
"type.cuisine.japanese" = "Japanese";
|
||||
"type.cuisine.kebab" = "Kebab";
|
||||
"type.cuisine.korean" = "Coreana";
|
||||
"type.cuisine.lao" = "Laosiana";
|
||||
"type.cuisine.lebanese" = "Libanesa";
|
||||
"type.cuisine.korean" = "Korean";
|
||||
"type.cuisine.lao" = "Lao";
|
||||
"type.cuisine.lebanese" = "Lebanese";
|
||||
"type.cuisine.local" = "Local";
|
||||
"type.cuisine.malagasy" = "Malagasy";
|
||||
"type.cuisine.malaysian" = "Malaysian";
|
||||
"type.cuisine.mediterranean" = "Mediterranean";
|
||||
"type.cuisine.mexican" = "Mexicana";
|
||||
"type.cuisine.moroccan" = "Marroquina";
|
||||
"type.cuisine.moroccan" = "Moroccan";
|
||||
"type.cuisine.noodles" = "Fideus xinesos";
|
||||
"type.cuisine.oriental" = "East Asian";
|
||||
"type.cuisine.pancake" = "Pancake";
|
||||
|
@ -333,11 +333,11 @@
|
|||
"type.cuisine.persian" = "Persian";
|
||||
"type.cuisine.peruvian" = "Peruvian";
|
||||
"type.cuisine.pizza" = "Pizza";
|
||||
"type.cuisine.polish" = "Polonesa";
|
||||
"type.cuisine.portuguese" = "Portuguesa";
|
||||
"type.cuisine.polish" = "Polish";
|
||||
"type.cuisine.portuguese" = "Portuguese";
|
||||
"type.cuisine.ramen" = "Ramen";
|
||||
"type.cuisine.regional" = "Regional";
|
||||
"type.cuisine.russian" = "Rusa";
|
||||
"type.cuisine.russian" = "Russian";
|
||||
"type.cuisine.sandwich" = "Sandwich";
|
||||
"type.cuisine.sausage" = "Sausage";
|
||||
"type.cuisine.savory_pancakes" = "Savory Pancakes";
|
||||
|
@ -349,7 +349,7 @@
|
|||
"type.cuisine.tapas" = "Tapas";
|
||||
"type.cuisine.tea" = "Te";
|
||||
"type.cuisine.thai" = "Thai";
|
||||
"type.cuisine.turkish" = "Turca";
|
||||
"type.cuisine.turkish" = "Turkish";
|
||||
"type.cuisine.vegan" = "Vegan";
|
||||
"type.cuisine.vegetarian" = "Vegetarian";
|
||||
"type.cuisine.vietnamese" = "Vietnamese";
|
||||
|
@ -377,10 +377,10 @@
|
|||
"type.healthcare.physiotherapist" = "Fisioterapeuta";
|
||||
"type.healthcare.alternative" = "Alternative Medicine";
|
||||
"type.healthcare.audiologist" = "Audiologist";
|
||||
"type.healthcare.blood_donation" = "Centre de donació de sang";
|
||||
"type.healthcare.blood_donation" = "Blood Donation Center";
|
||||
"type.healthcare.optometrist" = "Optometrist";
|
||||
"type.healthcare.podiatrist" = "Podòleg";
|
||||
"type.healthcare.psychotherapist" = "Psicoterapeuta";
|
||||
"type.healthcare.podiatrist" = "Podiatrist";
|
||||
"type.healthcare.psychotherapist" = "Psychotherapist";
|
||||
"type.healthcare.sample_collection" = "Sample Collection Centre";
|
||||
"type.healthcare.speech_therapist" = "Logopedics";
|
||||
"type.highway" = "Highway";
|
||||
|
@ -459,14 +459,14 @@
|
|||
|
||||
/* These translations are used for all type.highway.*.tunnel. */
|
||||
"type.highway.path.tunnel" = "Túnel";
|
||||
"type.highway.pedestrian" = "Carrer de vianants";
|
||||
"type.highway.pedestrian.area" = "Àrea de vianants";
|
||||
"type.highway.pedestrian" = "Pedestrian Street";
|
||||
"type.highway.pedestrian.area" = "Pedestrian Street";
|
||||
|
||||
/* These translations are used for all type.highway.*.bridge. */
|
||||
"type.highway.pedestrian.bridge" = "Pont de vianants";
|
||||
"type.highway.pedestrian.bridge" = "Pedestrian Bridge";
|
||||
|
||||
/* These translations are used for all type.highway.*.tunnel. */
|
||||
"type.highway.pedestrian.tunnel" = "Túnel de vianants";
|
||||
"type.highway.pedestrian.tunnel" = "Pedestrian Tunnel";
|
||||
"type.highway.primary" = "Carretera primària";
|
||||
|
||||
/* These translations are used for all type.highway.*.bridge. */
|
||||
|
@ -678,11 +678,11 @@
|
|||
"type.landuse.education" = "Educational Facility";
|
||||
"type.landuse.farmland" = "Farmland";
|
||||
"type.landuse.farmyard" = "Farmyard";
|
||||
"type.landuse.field" = "Camp";
|
||||
"type.landuse.field" = "Field";
|
||||
"type.landuse.flowerbed" = "Llit de flors";
|
||||
"type.landuse.forest" = "Bosc";
|
||||
"type.landuse.forest.coniferous" = "Bosc de coníferes";
|
||||
"type.landuse.forest.deciduous" = "Bosc de caducifòlies";
|
||||
"type.landuse.forest.coniferous" = "Coniferous Forest";
|
||||
"type.landuse.forest.deciduous" = "Deciduous Forest";
|
||||
"type.landuse.forest.mixed" = "Mixed-Leaf Forest";
|
||||
"type.landuse.garages" = "Garatges";
|
||||
"type.landuse.grass" = "Grass";
|
||||
|
@ -690,13 +690,13 @@
|
|||
"type.landuse.greenhouse_horticulture" = "Hivernacle";
|
||||
"type.landuse.industrial" = "Àrea industrial";
|
||||
"type.landuse.landfill" = "Abocador";
|
||||
"type.landuse.meadow" = "Prat de dall";
|
||||
"type.landuse.meadow" = "Meadow";
|
||||
"type.landuse.military" = "Military Area";
|
||||
"type.landuse.orchard" = "Verger";
|
||||
"type.landuse.orchard" = "Orchard";
|
||||
"type.landuse.quarry" = "Pedrera";
|
||||
"type.landuse.railway" = "Railway Premises";
|
||||
"type.landuse.recreation_ground" = "Recreation Ground";
|
||||
"type.landuse.reservoir" = "Embassament";
|
||||
"type.landuse.reservoir" = "Reservoir";
|
||||
"type.landuse.residential" = "Residential Area";
|
||||
"type.landuse.retail" = "Retail Area";
|
||||
"type.landuse.salt_pond" = "Salt Pond";
|
||||
|
@ -772,11 +772,11 @@
|
|||
"type.man_made.water_well" = "Pou d’aigua";
|
||||
"type.man_made.water_well.drinking_water_no" = "Pou d’aigua";
|
||||
"type.man_made.windmill" = "Molí de vent";
|
||||
"type.man_made.works" = "Fàbrica";
|
||||
"type.man_made.works" = "Industrial Works";
|
||||
"type.mapswithme" = "mapswithme";
|
||||
"type.mapswithme.grid" = "mapswithme-grid";
|
||||
"type.military" = "Military";
|
||||
"type.military.bunker" = "Búnquer";
|
||||
"type.military.bunker" = "Bunker";
|
||||
"type.mountain_pass" = "Mountain Pass";
|
||||
"type.natural" = "Nature";
|
||||
|
||||
|
@ -794,40 +794,40 @@
|
|||
"type.natural.beach.gravel" = "Gravel Beach";
|
||||
"type.natural.cape" = "Cape";
|
||||
"type.natural.cave_entrance" = "Cave Entrance";
|
||||
"type.natural.cliff" = "Penya-segat";
|
||||
"type.natural.earth_bank" = "Talús de terra";
|
||||
"type.natural.cliff" = "Cliff";
|
||||
"type.natural.earth_bank" = "Earth Bank";
|
||||
"type.man_made.embankment" = "Terraplè";
|
||||
"type.natural.coastline" = "Costa";
|
||||
"type.natural.coastline" = "Coastline";
|
||||
"type.natural.desert" = "Desert";
|
||||
"type.natural.geyser" = "Guèiser";
|
||||
"type.natural.glacier" = "Glacier";
|
||||
"type.natural.grassland" = "Herbassar";
|
||||
"type.natural.heath" = "Landa";
|
||||
"type.natural.grassland" = "Grassland";
|
||||
"type.natural.heath" = "Heath";
|
||||
"type.natural.hot_spring" = "Font termal";
|
||||
"type.natural.water.lake" = "Lake";
|
||||
"type.natural.water.lock" = "Resclosa";
|
||||
"type.natural.water.pond" = "Estany";
|
||||
"type.natural.water.reservoir" = "Embassament";
|
||||
"type.natural.water.lock" = "Lock Chamber";
|
||||
"type.natural.water.pond" = "Pond";
|
||||
"type.natural.water.reservoir" = "Reservoir";
|
||||
"type.natural.water.basin" = "Basin";
|
||||
"type.natural.water.river" = "Riu";
|
||||
"type.natural.land" = "Terreny";
|
||||
"type.natural.meadow" = "Prat de dall";
|
||||
"type.natural.orchard" = "Verger";
|
||||
"type.natural.land" = "Land";
|
||||
"type.natural.meadow" = "Meadow";
|
||||
"type.natural.orchard" = "Orchard";
|
||||
"type.natural.peak" = "Peak";
|
||||
"type.natural.saddle" = "Mountain Saddle";
|
||||
"type.natural.rock" = "Rock";
|
||||
"type.natural.scrub" = "Scrub";
|
||||
"type.natural.spring" = "Natural Spring";
|
||||
"type.natural.spring.drinking_water_no" = "Natural Spring";
|
||||
"type.natural.strait" = "Estret";
|
||||
"type.natural.tree_row" = "Fila d’arbres";
|
||||
"type.natural.vineyard" = "Vinyar";
|
||||
"type.natural.strait" = "Strait";
|
||||
"type.natural.tree_row" = "Tree Row";
|
||||
"type.natural.vineyard" = "Vineyard";
|
||||
"type.natural.volcano" = "Volcà";
|
||||
"type.natural.water" = "Aigua";
|
||||
"type.natural.wetland" = "Zona humida";
|
||||
"type.natural.wetland.bog" = "Torbera";
|
||||
"type.natural.wetland.marsh" = "Aiguamoll";
|
||||
"type.noexit" = "Atzucac";
|
||||
"type.natural.wetland" = "Wetland";
|
||||
"type.natural.wetland.bog" = "Bog";
|
||||
"type.natural.wetland.marsh" = "Marsh";
|
||||
"type.noexit" = "Dead End";
|
||||
"type.office" = "Office";
|
||||
"type.office.company" = "Company Office";
|
||||
"type.office.estate_agent" = "Estate Agent";
|
||||
|
@ -892,7 +892,7 @@
|
|||
"type.power.plant.solar" = "Central d'energia solar";
|
||||
"type.power.plant.wind" = "Central eòlica";
|
||||
"type.power.station" = "Power Station";
|
||||
"type.power.substation" = "Subestació";
|
||||
"type.power.substation" = "Substation";
|
||||
|
||||
/* A tower or pylon carrying high voltage electricity cables. */
|
||||
"type.power.tower" = "Power Tower";
|
||||
|
@ -1333,18 +1333,18 @@
|
|||
"type.sport.archery" = "Archery";
|
||||
"type.sport.athletics" = "Atletisme";
|
||||
"type.sport.australian_football" = "Australian Football";
|
||||
"type.sport.baseball" = "Beisbol";
|
||||
"type.sport.basketball" = "Basquetbol";
|
||||
"type.sport.beachvolleyball" = "Voleibol de platja";
|
||||
"type.sport.baseball" = "Baseball";
|
||||
"type.sport.basketball" = "Basketball";
|
||||
"type.sport.beachvolleyball" = "Beach Volleyball";
|
||||
"type.sport.bowls" = "Bowls";
|
||||
"type.sport.chess" = "Escacs";
|
||||
"type.sport.cricket" = "Cricket";
|
||||
"type.sport.curling" = "Curling";
|
||||
"type.sport.equestrian" = "Esports eqüestres";
|
||||
"type.sport.golf" = "Golf";
|
||||
"type.sport.gymnastics" = "Gimnàstica";
|
||||
"type.sport.gymnastics" = "Gymnastics";
|
||||
"type.sport.handball" = "Handball";
|
||||
"type.sport.multi" = "Diversos esports";
|
||||
"type.sport.multi" = "Various Sports";
|
||||
|
||||
/* Used to tag a scuba diving site. */
|
||||
"type.sport.scuba_diving" = "Scuba Diving Site";
|
||||
|
@ -1474,7 +1474,7 @@
|
|||
"type.self_service.no" = "Sense autoservei";
|
||||
|
||||
/* https://wiki.openstreetmap.org/wiki/Key:social_facility */
|
||||
"type.amenity.social_facility" = "Servei social";
|
||||
"type.amenity.social_facility" = "Equipament Social";
|
||||
|
||||
/* https://wiki.openstreetmap.org/wiki/Tag:emergency=emergency_ward_entrance */
|
||||
"type.emergency.emergency_ward_entrance" = "Entrada d'urgències";
|
||||
|
|
|
@ -1058,7 +1058,7 @@
|
|||
"stop_track_recording" = "Aufnahme des Tracks stoppen";
|
||||
|
||||
/* Title for the "Stop Without Saving" action for the alert when saving a track recording. */
|
||||
"stop_without_saving" = "Ohne Speichern beenden";
|
||||
"stop_without_saving" = "Anhalten ohne zu speichern";
|
||||
|
||||
/* Title for the "Stop Without Saving" action for the alert when saving a track recording. */
|
||||
"continue_recording" = "Aufnahme fortsetzen";
|
||||
|
|
|
@ -268,7 +268,7 @@
|
|||
"type.craft.winery" = "Kellerei";
|
||||
"type.craft.tailor" = "Schneider";
|
||||
"type.cuisine.african" = "Afrikanisch";
|
||||
"type.cuisine.american" = "US-amerikanisch";
|
||||
"type.cuisine.american" = "Amerikanisch";
|
||||
"type.cuisine.arab" = "Arabisch";
|
||||
"type.cuisine.argentinian" = "Argentinisch";
|
||||
"type.cuisine.asian" = "Asiatisch";
|
||||
|
|
|
@ -973,7 +973,7 @@
|
|||
"car_used_on_the_car_screen" = "Ahora usa Organic Maps en la pantalla del coche";
|
||||
|
||||
/* Displayed on the phone screen. Button to display maps on the phone screen instead of a car */
|
||||
"car_continue_on_the_phone" = "Pasar al teléfono";
|
||||
"car_continue_on_the_phone" = "Continuar en el teléfono";
|
||||
|
||||
/* Displayed on the Android Auto or CarPlay screen. Button to display maps on the car screen instead of a phone. Must be no more than 18 symbols! */
|
||||
"car_continue_in_the_car" = "A la pantalla del coche";
|
||||
|
|
|
@ -380,7 +380,7 @@
|
|||
"type.healthcare.blood_donation" = "Centro de donación de sangre";
|
||||
"type.healthcare.optometrist" = "Optometría";
|
||||
"type.healthcare.podiatrist" = "Podología";
|
||||
"type.healthcare.psychotherapist" = "Psicoterapeuta";
|
||||
"type.healthcare.psychotherapist" = "Psicoterapia";
|
||||
"type.healthcare.sample_collection" = "Muestreo";
|
||||
"type.healthcare.speech_therapist" = "Logopedia";
|
||||
"type.highway" = "Carretera";
|
||||
|
@ -795,7 +795,7 @@
|
|||
"type.natural.cape" = "Cabo";
|
||||
"type.natural.cave_entrance" = "Cueva";
|
||||
"type.natural.cliff" = "Acantilado";
|
||||
"type.natural.earth_bank" = "Talud de tierra";
|
||||
"type.natural.earth_bank" = "Acantilado";
|
||||
"type.man_made.embankment" = "Terraplén";
|
||||
"type.natural.coastline" = "Costa";
|
||||
"type.natural.desert" = "Desierto";
|
||||
|
@ -805,7 +805,7 @@
|
|||
"type.natural.heath" = "Brezal";
|
||||
"type.natural.hot_spring" = "Aguas termales";
|
||||
"type.natural.water.lake" = "Lago";
|
||||
"type.natural.water.lock" = "Esclusa";
|
||||
"type.natural.water.lock" = "Cámara de bloqueo";
|
||||
"type.natural.water.pond" = "Estanque";
|
||||
"type.natural.water.reservoir" = "Embalse";
|
||||
"type.natural.water.basin" = "Cuenca";
|
||||
|
|
|
@ -411,7 +411,7 @@
|
|||
/* blue gray color */
|
||||
"blue_gray" = "Gris-bleu";
|
||||
"dialog_routing_disclaimer_title" = "Lorsque vous suivez l'itinéraire, gardez à l'esprit les points suivants :";
|
||||
"dialog_routing_disclaimer_priority" = "— Les conditions de circulation, le code de la route et les panneaux de signalisation ont la priorité sur l'appareil de navigation ;";
|
||||
"dialog_routing_disclaimer_priority" = "— Les conditions de circulation, le code de la route et les panneaux de signalisation ont la priorité sur l'appareil de navigation ;";
|
||||
"dialog_routing_disclaimer_precision" = "— La carte peut être imprécise et l'itinéraire proposé n'est pas forcément le plus direct pour arriver à destination ;";
|
||||
"dialog_routing_disclaimer_recommendations" = "— L'itinéraire proposé doit être considéré comme une simple recommandation ;";
|
||||
"dialog_routing_disclaimer_borders" = "— Faites attention aux itinéraires traversant des zones frontalières : les itinéraires générés par l'application peuvent parfois franchir des frontières étatiques dans des zones interdites ;";
|
||||
|
@ -598,7 +598,7 @@
|
|||
"editor_operator" = "Opérateur";
|
||||
|
||||
/* To indicate the operator of ATMs, bicycle rentals, electric vehicle charging stations... */
|
||||
"operator" = "Opérateur : %@";
|
||||
"operator" = "Opérateur : %@";
|
||||
"editor_category_unsuitable_title" = "Tu ne trouves pas de catégorie appropriée ?";
|
||||
"editor_category_unsuitable_text" = "Organic Maps ne permet d'ajouter que des catégories de points simples, c'est-à-dire pas de villes, de routes, de lacs, de contours de bâtiments, etc. Merci d'ajouter ces catégories directement sur <a href=\"https://www.openstreetmap.org\">OpenStreetMap.org</a>. Consulte notre <a href=\"https://organicmaps.app/faq/editing/advanced-map-editing\">guide</a> pour obtenir des instructions détaillées étape par étape.";
|
||||
"downloader_no_downloaded_maps_title" = "Vous n'avez téléchargé aucune carte";
|
||||
|
@ -709,7 +709,7 @@
|
|||
"traffic_data_unavailable" = "Les données de circulation ne sont pas disponibles";
|
||||
"enable_logging" = "Activer le journal";
|
||||
"log_file_size" = "Taille du fichier journal : %@";
|
||||
"transliteration_title" = "Translittératisé en alphabet latin";
|
||||
"transliteration_title" = "Translittération en latin";
|
||||
|
||||
/* Subway exits for public transport marks on the map */
|
||||
"core_exit" = "Sortie";
|
||||
|
|
|
@ -12,14 +12,10 @@
|
|||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>zero</key>
|
||||
<string>marque-page</string>
|
||||
<key>one</key>
|
||||
<string>marque-page</string>
|
||||
<key>many</key>
|
||||
<string>marque-pages</string>
|
||||
<string>%d signet</string>
|
||||
<key>other</key>
|
||||
<string>marque-pages</string>
|
||||
<string>%d signets</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>bookmarks_detect_message</key>
|
||||
|
@ -32,12 +28,8 @@
|
|||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>zero</key>
|
||||
<string>%d fichier ont été trouvé. Vous n'en verrez pas après la conversion.</string>
|
||||
<key>one</key>
|
||||
<string>%d fichier a été trouvé. Vous le verrez après la conversion.</string>
|
||||
<key>many</key>
|
||||
<string>%d fichiers ont été trouvés. Vous les verrez après la conversion.</string>
|
||||
<key>other</key>
|
||||
<string>%d fichiers ont été trouvés. Vous les verrez après la conversion.</string>
|
||||
</dict>
|
||||
|
@ -52,14 +44,8 @@
|
|||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>zero</key>
|
||||
<string>%d voie</string>
|
||||
<key>one</key>
|
||||
<string>%d voie</string>
|
||||
<key>many</key>
|
||||
<string>%d voies</string>
|
||||
<key>other</key>
|
||||
<string>%d voies</string>
|
||||
<string>%d trace</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
|
|
|
@ -898,10 +898,10 @@
|
|||
"type.power.tower" = "Pylône électrique";
|
||||
|
||||
/* A single pole supporting minor power lines. */
|
||||
"type.power.pole" = "Pylône électrique";
|
||||
"type.power.pole" = "Power Pole";
|
||||
|
||||
/* A single pole supporting various public utilities, such as lighting or telephony. */
|
||||
"type.man_made.utility_pole" = "Pylônes (Télécommunication, lampadaire)";
|
||||
"type.man_made.utility_pole" = "Utility Pole";
|
||||
"type.psurface" = "psurface";
|
||||
"type.psurface.paved_bad" = "psurface-paved_bad";
|
||||
"type.psurface.paved_good" = "psurface-paved_good";
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>tracks</key>
|
||||
<dict>
|
||||
<key>value</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>other</key>
|
||||
<string>%d tracks?</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,107 +0,0 @@
|
|||
"type.healthcare.laboratory" = "Læknisfræðirannsóknastofa";
|
||||
"type.barrier.kissing_gate" = "Vippuhlið";
|
||||
"type.waterway.ditch" = "Drenskurður";
|
||||
"type.boundary.administrative.3" = "Héraðsmörk";
|
||||
"type.recycling.paper" = "Pappír";
|
||||
"type.aeroway.aerodrome" = "Flugvöllur";
|
||||
"type.barrier.swing_gate" = "Sveifluhlið";
|
||||
"type.aerialway.cable_car" = "Kláfur";
|
||||
"type.aerialway.chair_lift" = "Stólalyfta";
|
||||
"type.aerialway.drag_lift" = "Toglyfta";
|
||||
"type.aerialway.gondola" = "Eggjalyfta";
|
||||
"type.aerialway.mixed_lift" = "Blönduð gerð víralyftu";
|
||||
"type.aerialway.station" = "Lyftustöð";
|
||||
"type.aeroway" = "Stoðkerfisnet flugsamgangna";
|
||||
"type.aeroway.aerodrome.international" = "Alþjóðaflugvöllur";
|
||||
"type.aeroway.apron" = "Flughlað";
|
||||
"type.aeroway.gate" = "Hlið";
|
||||
"type.amenity.place_of_worship.hindu" = "Hindú-musteri";
|
||||
"type.amenity.place_of_worship.jewish" = "Sýnagóga";
|
||||
"type.amenity.place_of_worship.muslim" = "Moska";
|
||||
"type.amenity.place_of_worship.shinto" = "Shinto-skrín";
|
||||
"type.amenity.police" = "Lögreglustöð";
|
||||
"type.amenity.post_box" = "Pósthólf";
|
||||
"type.amenity.pub" = "Krá";
|
||||
"type.amenity.public_bookcase" = "Bókaskipti";
|
||||
"type.amenity.recycling.centre" = "Endurvinnslustöð";
|
||||
"type.amenity.recycling" = "Endurvinnslugámur";
|
||||
"type.amenity.recycling.container" = "Endurvinnslugámur";
|
||||
"type.recycling.batteries" = "Rafgeymar";
|
||||
"type.recycling.clothes" = "Fatnaður";
|
||||
"type.recycling.glass_bottles" = "Glerflöskur";
|
||||
"type.recycling.plastic_bottles" = "Plastflöskur";
|
||||
"type.recycling.scrap_metal" = "Brotajárn";
|
||||
"type.recycling.cardboard" = "Bylgjupappi";
|
||||
"type.recycling.cans" = "Dósir";
|
||||
"type.recycling.shoes" = "Skór";
|
||||
"type.recycling.cartons" = "Pappi";
|
||||
"type.amenity.restaurant" = "Veitingastaður";
|
||||
"type.amenity.school" = "Skólaföt";
|
||||
"type.amenity.shelter" = "Skýli";
|
||||
"type.amenity.shelter.public_transport" = "Skýli";
|
||||
"type.amenity.university" = "Háskóli";
|
||||
"type.amenity.vending_machine" = "Sjálfsali";
|
||||
"type.amenity.vending_machine.cigarettes" = "Tóbakssjálfsali";
|
||||
"type.amenity.vending_machine.coffee" = "Kaffisjálfsali";
|
||||
"type.amenity.vending_machine.condoms" = "Smokkasjálfsali";
|
||||
"type.amenity.vending_machine.drinks" = "Drykkjasjálfsali";
|
||||
"type.amenity.vending_machine.food" = "Matvörusjálfsali";
|
||||
"type.amenity.vending_machine.parking_tickets" = "Stöðumælir";
|
||||
"type.amenity.vending_machine.public_transport_tickets" = "Miðasjálfsali";
|
||||
"type.amenity.vending_machine.sweets" = "Sælgætissjálfsali";
|
||||
"type.amenity.vending_machine.excrement_bags" = "Saurpokasjálfsali";
|
||||
"type.barrier.hedge" = "Limgerði";
|
||||
"type.barrier.lift_gate" = "Liftuhlið";
|
||||
"type.barrier.stile" = "Hliðstigi";
|
||||
"type.barrier.turnstile" = "Snúningshlið";
|
||||
"type.barrier.toll_booth" = "Tollverðir";
|
||||
"type.barrier.wall" = "Veggur";
|
||||
"type.boundary" = "Mörk";
|
||||
"type.boundary.administrative" = "Stjórnsýslumörk";
|
||||
"type.boundary.administrative.2" = "Landamæri";
|
||||
"type.boundary.administrative.4" = "Héraðsmörk";
|
||||
"type.boundary.national_park" = "Þjóðgarður";
|
||||
"type.boundary.aboriginal_lands" = "Frumbyggjaland";
|
||||
"type.boundary.protected_area" = "Verndarsvæði";
|
||||
"type.boundary.protected_area.1" = "Verndarsvæði";
|
||||
"type.boundary.protected_area.2" = "Verndarsvæði";
|
||||
"type.boundary.protected_area.3" = "Verndarsvæði";
|
||||
"type.boundary.protected_area.4" = "Verndarsvæði";
|
||||
"type.boundary.protected_area.5" = "Verndarsvæði";
|
||||
"type.boundary.protected_area.6" = "Verndarsvæði";
|
||||
"type.building" = "Bygging";
|
||||
"type.building.address" = "Heimilisfang";
|
||||
"type.building.has_parts" = "Bygging";
|
||||
"type.building_part" = "Bygging";
|
||||
"type.building.garage" = "Bílskúr";
|
||||
"type.amenity.parcel_locker" = "Bögglaskápur";
|
||||
"type.amenity.vehicle_inspection" = "Bifreiðaskoðun";
|
||||
"type.amenity.vending_machine.fuel" = "Eldsneytisdæla";
|
||||
"type.amenity.veterinary" = "Dýralæknir";
|
||||
"type.amenity.waste_disposal" = "Ruslgámur";
|
||||
"type.amenity.waste_transfer_station" = "Millifærslustöð fyrir úrgang";
|
||||
"type.amenity.water_point" = "Vatnspóstur";
|
||||
"type.amenity.water_point.drinking_water_no" = "Vatnspóstur";
|
||||
"type.barrier" = "Hindrun";
|
||||
"type.barrier.block" = "Reitur";
|
||||
"type.barrier.bollard" = "Polli";
|
||||
"type.barrier.border_control" = "Landamæraeftirlit";
|
||||
"type.barrier.chain" = "Keðja";
|
||||
"type.barrier.city_wall" = "Borgarmúr";
|
||||
"type.barrier.cycle_barrier" = "Hjólahindrun";
|
||||
"type.natural.water.wastewater" = "Óhreinsað vatn";
|
||||
"type.barrier.entrance" = "Inngangur";
|
||||
"type.barrier.fence" = "Girðing";
|
||||
"type.man_made.silo" = "Síló";
|
||||
"type.tourism.information.visitor_centre" = "Gestastofa";
|
||||
"type.tourism.viewpoint" = "Útsýnisstaður";
|
||||
"type.amenity.waste_basket" = "Rusl";
|
||||
"type.cuisine.noodles" = "Núðlur";
|
||||
"type.recycling.plastic" = "Plast";
|
||||
"type.shop.chemist" = "Lyfsali";
|
||||
"type.barrier.gate" = "Hlið";
|
||||
"type.amenity.vending_machine.newspapers" = "Dagblaðasjálfsali";
|
||||
"type.barrier.retaining_wall" = "Stoðveggur";
|
||||
"type.amenity.post_office" = "Pósthús";
|
||||
"type.amenity.prison" = "Fangelsi";
|
||||
"type.aerialway" = "Víralyfta";
|
|
@ -601,7 +601,7 @@
|
|||
"operator" = "Operators: %@";
|
||||
"editor_category_unsuitable_title" = "Vai neatrodat atbilstošu kategoriju?";
|
||||
"editor_category_unsuitable_text" = "„Organic Maps“ ļauj pievienot tikai vienkāršu punktu kategorijas, proti, nevarat pievienot pilsētas, ceļus, ezerus, ēku aprises u.tml. Šādas kategorijas lūdzam pievienot tiešā veidā <a href=\"https://www.openstreetmap.org\">OpenStreetMap.org</a>. Lai uzzinātu, kā to izdarīt, sekojiet norādēm <a href=\"https://organicmaps.app/faq/editing/advanced-map-editing\">rokasgrāmatā</a>.";
|
||||
"downloader_no_downloaded_maps_title" = "Ierīcē nav lejupielādētu karšu";
|
||||
"downloader_no_downloaded_maps_title" = "Ierīcē nav lejupielādētas kartes";
|
||||
"downloader_no_downloaded_maps_message" = "Lai darbotos nesaistes navigācija un meklēšana, lejupielādējiet kartes.";
|
||||
"current_location_unknown_error_title" = "Pašreizējā vieta nav zināma.";
|
||||
"current_location_unknown_error_message" = "Nosakot jūsu atrašanās vietu, radās kļūda. Pārleicinieties, ka ierīce darbojas korekti un mēģiniet vēlreiz.";
|
||||
|
|
|
@ -491,20 +491,17 @@
|
|||
ED4DC7782CAEDECC0029B338 /* ProductButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4DC7732CAEDECC0029B338 /* ProductButton.swift */; };
|
||||
ED4DC7792CAEDECC0029B338 /* ProductsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4DC7742CAEDECC0029B338 /* ProductsViewController.swift */; };
|
||||
ED5BAF4B2D688F5B0088D7B1 /* SearchOnMapHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5BAF4A2D688F5A0088D7B1 /* SearchOnMapHeaderView.swift */; };
|
||||
ED5E02142D8B17B600A5CC7B /* ModalPresentationStepsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED5E02132D8B17B600A5CC7B /* ModalPresentationStepsController.swift */; };
|
||||
ED5E02522D92E33300A5CC7B /* NavigationDashboardView.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED5E02512D92E33300A5CC7B /* NavigationDashboardView.mm */; };
|
||||
ED63CEB92BDF8F9D006155C4 /* SettingsTableViewiCloudSwitchCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED63CEB62BDF8F9C006155C4 /* SettingsTableViewiCloudSwitchCell.swift */; };
|
||||
ED70D55C2D5396F300738C1E /* SearchResult.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED70D55A2D5396F300738C1E /* SearchResult.mm */; };
|
||||
ED70D5892D539A2500738C1E /* SearchOnMapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED70D5872D539A2500738C1E /* SearchOnMapViewController.swift */; };
|
||||
ED70D58A2D539A2500738C1E /* SearchOnMapModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED70D5852D539A2500738C1E /* SearchOnMapModels.swift */; };
|
||||
ED70D58B2D539A2500738C1E /* SearchOnMapModalTransitionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED70D57E2D539A2500738C1E /* SearchOnMapModalTransitionManager.swift */; };
|
||||
ED70D58C2D539A2500738C1E /* SearchOnMapPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED70D5862D539A2500738C1E /* SearchOnMapPresenter.swift */; };
|
||||
ED70D58D2D539A2500738C1E /* ModalScreenPresentationStep.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED70D57C2D539A2500738C1E /* ModalScreenPresentationStep.swift */; };
|
||||
ED70D58E2D539A2500738C1E /* SideMenuPresentationAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED70D5802D539A2500738C1E /* SideMenuPresentationAnimator.swift */; };
|
||||
ED70D58D2D539A2500738C1E /* ModalPresentationStep.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED70D57C2D539A2500738C1E /* ModalPresentationStep.swift */; };
|
||||
ED70D58F2D539A2500738C1E /* SearchOnMapInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED70D5832D539A2500738C1E /* SearchOnMapInteractor.swift */; };
|
||||
ED70D5902D539A2500738C1E /* SearchOnMapModalPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED70D57D2D539A2500738C1E /* SearchOnMapModalPresentationController.swift */; };
|
||||
ED70D5912D539A2500738C1E /* MapPassthroughView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED70D57B2D539A2500738C1E /* MapPassthroughView.swift */; };
|
||||
ED70D5922D539A2500738C1E /* PlaceholderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED70D5822D539A2500738C1E /* PlaceholderView.swift */; };
|
||||
ED70D5932D539A2500738C1E /* SearchOnMapManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED70D5842D539A2500738C1E /* SearchOnMapManager.swift */; };
|
||||
ED70D5942D539A2500738C1E /* SideMenuDismissalAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED70D57F2D539A2500738C1E /* SideMenuDismissalAnimator.swift */; };
|
||||
ED77556E2C2C490B0051E656 /* UIAlertController+openInAppActionSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED77556D2C2C490B0051E656 /* UIAlertController+openInAppActionSheet.swift */; };
|
||||
ED79A5AB2BD7AA9C00952D1F /* LoadingOverlayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED79A5AA2BD7AA9C00952D1F /* LoadingOverlayViewController.swift */; };
|
||||
ED79A5AD2BD7BA0F00952D1F /* UIApplication+LoadingOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED79A5AC2BD7BA0F00952D1F /* UIApplication+LoadingOverlay.swift */; };
|
||||
|
@ -526,6 +523,9 @@
|
|||
ED9857082C4ED02D00694F6C /* MailComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED9857072C4ED02D00694F6C /* MailComposer.swift */; };
|
||||
ED9966802B94FBC20083CE55 /* ColorPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED99667D2B94FBC20083CE55 /* ColorPicker.swift */; };
|
||||
EDA1EAA42CC7ECAD00DBDCAA /* ElevationProfileFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA1EAA32CC7ECAD00DBDCAA /* ElevationProfileFormatter.swift */; };
|
||||
EDB71D8C2D8474A0004A6A7F /* CornerRadius.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDB71D8B2D8474A0004A6A7F /* CornerRadius.swift */; };
|
||||
EDB71E002D8B0338004A6A7F /* ModalPresentationAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDB71DFF2D8B0338004A6A7F /* ModalPresentationAnimator.swift */; };
|
||||
EDB71E042D8B0943004A6A7F /* SearchOnMapAreaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDB71E032D8B0943004A6A7F /* SearchOnMapAreaView.swift */; };
|
||||
EDBD68072B625724005DD151 /* LocationServicesDisabledAlert.xib in Resources */ = {isa = PBXBuildFile; fileRef = EDBD68062B625724005DD151 /* LocationServicesDisabledAlert.xib */; };
|
||||
EDBD680B2B62572E005DD151 /* LocationServicesDisabledAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDBD680A2B62572E005DD151 /* LocationServicesDisabledAlert.swift */; };
|
||||
EDC3573B2B7B5029001AE9CA /* CALayer+SetCorner.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC3573A2B7B5029001AE9CA /* CALayer+SetCorner.swift */; };
|
||||
|
@ -1390,6 +1390,10 @@
|
|||
A630D205207CAA3A00976DEA /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||
A630D206207CAA5800976DEA /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||
AA1C7E3D269A2DD600BAADF2 /* EditTrackViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditTrackViewController.swift; sourceTree = "<group>"; };
|
||||
AC4209FF2D79BCEC00A64AA9 /* af */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = af; path = af.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
AC420A002D79BCED00A64AA9 /* af */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = af; path = af.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
AC420A012D79BCED00A64AA9 /* af */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = af; path = af.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
AC420A022D79BCEE00A64AA9 /* af */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = af; path = af.lproj/LocalizableTypes.strings; sourceTree = "<group>"; };
|
||||
AC420A082D79BDDA00A64AA9 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
AC420A092D79BDDA00A64AA9 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
AC420A0A2D79BDDB00A64AA9 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = lt; path = lt.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
|
@ -1398,10 +1402,6 @@
|
|||
AC420A142D79C2EC00A64AA9 /* mt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mt; path = mt.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
AC420A152D79C2EC00A64AA9 /* mt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = mt; path = mt.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
AC420A162D79C2ED00A64AA9 /* mt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mt; path = mt.lproj/LocalizableTypes.strings; sourceTree = "<group>"; };
|
||||
AC4209FF2D79BCEC00A64AA9 /* af */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = af; path = af.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
AC420A002D79BCED00A64AA9 /* af */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = af; path = af.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
AC420A012D79BCED00A64AA9 /* af */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = af; path = af.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
AC420A022D79BCEE00A64AA9 /* af */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = af; path = af.lproj/LocalizableTypes.strings; sourceTree = "<group>"; };
|
||||
AC79C8912A65AB9500594C24 /* UIColor+hexString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+hexString.swift"; sourceTree = "<group>"; };
|
||||
B33D21AE20DAF9F000BAD749 /* Toast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toast.swift; sourceTree = "<group>"; };
|
||||
B3E3B4FC20D463B700DA8C13 /* BMCCategoriesHeader.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BMCCategoriesHeader.xib; sourceTree = "<group>"; };
|
||||
|
@ -1467,17 +1467,16 @@
|
|||
ED4DC7742CAEDECC0029B338 /* ProductsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductsViewController.swift; sourceTree = "<group>"; };
|
||||
ED4DC7752CAEDECC0029B338 /* ProductsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductsViewModel.swift; sourceTree = "<group>"; };
|
||||
ED5BAF4A2D688F5A0088D7B1 /* SearchOnMapHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchOnMapHeaderView.swift; sourceTree = "<group>"; };
|
||||
ED5E02132D8B17B600A5CC7B /* ModalPresentationStepsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalPresentationStepsController.swift; sourceTree = "<group>"; };
|
||||
ED5E024F2D92AC9300A5CC7B /* RoutePreviewView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RoutePreviewView.h; sourceTree = "<group>"; };
|
||||
ED5E02502D92E33300A5CC7B /* NavigationDashboardView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NavigationDashboardView.h; sourceTree = "<group>"; };
|
||||
ED5E02512D92E33300A5CC7B /* NavigationDashboardView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NavigationDashboardView.mm; sourceTree = "<group>"; };
|
||||
ED63CEB62BDF8F9C006155C4 /* SettingsTableViewiCloudSwitchCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsTableViewiCloudSwitchCell.swift; sourceTree = "<group>"; };
|
||||
ED70D5582D5396F300738C1E /* SearchItemType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SearchItemType.h; sourceTree = "<group>"; };
|
||||
ED70D5592D5396F300738C1E /* SearchResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SearchResult.h; sourceTree = "<group>"; };
|
||||
ED70D55A2D5396F300738C1E /* SearchResult.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SearchResult.mm; sourceTree = "<group>"; };
|
||||
ED70D55B2D5396F300738C1E /* SearchResult+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SearchResult+Core.h"; sourceTree = "<group>"; };
|
||||
ED70D57B2D539A2500738C1E /* MapPassthroughView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapPassthroughView.swift; sourceTree = "<group>"; };
|
||||
ED70D57C2D539A2500738C1E /* ModalScreenPresentationStep.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalScreenPresentationStep.swift; sourceTree = "<group>"; };
|
||||
ED70D57D2D539A2500738C1E /* SearchOnMapModalPresentationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchOnMapModalPresentationController.swift; sourceTree = "<group>"; };
|
||||
ED70D57E2D539A2500738C1E /* SearchOnMapModalTransitionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchOnMapModalTransitionManager.swift; sourceTree = "<group>"; };
|
||||
ED70D57F2D539A2500738C1E /* SideMenuDismissalAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenuDismissalAnimator.swift; sourceTree = "<group>"; };
|
||||
ED70D5802D539A2500738C1E /* SideMenuPresentationAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenuPresentationAnimator.swift; sourceTree = "<group>"; };
|
||||
ED70D57C2D539A2500738C1E /* ModalPresentationStep.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalPresentationStep.swift; sourceTree = "<group>"; };
|
||||
ED70D5822D539A2500738C1E /* PlaceholderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderView.swift; sourceTree = "<group>"; };
|
||||
ED70D5832D539A2500738C1E /* SearchOnMapInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchOnMapInteractor.swift; sourceTree = "<group>"; };
|
||||
ED70D5842D539A2500738C1E /* SearchOnMapManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchOnMapManager.swift; sourceTree = "<group>"; };
|
||||
|
@ -1548,6 +1547,9 @@
|
|||
ED9857072C4ED02D00694F6C /* MailComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailComposer.swift; sourceTree = "<group>"; };
|
||||
ED99667D2B94FBC20083CE55 /* ColorPicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorPicker.swift; sourceTree = "<group>"; };
|
||||
EDA1EAA32CC7ECAD00DBDCAA /* ElevationProfileFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElevationProfileFormatter.swift; sourceTree = "<group>"; };
|
||||
EDB71D8B2D8474A0004A6A7F /* CornerRadius.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CornerRadius.swift; sourceTree = "<group>"; };
|
||||
EDB71DFF2D8B0338004A6A7F /* ModalPresentationAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalPresentationAnimator.swift; sourceTree = "<group>"; };
|
||||
EDB71E032D8B0943004A6A7F /* SearchOnMapAreaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchOnMapAreaView.swift; sourceTree = "<group>"; };
|
||||
EDBD68062B625724005DD151 /* LocationServicesDisabledAlert.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = LocationServicesDisabledAlert.xib; sourceTree = "<group>"; };
|
||||
EDBD680A2B62572E005DD151 /* LocationServicesDisabledAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationServicesDisabledAlert.swift; sourceTree = "<group>"; };
|
||||
EDC3573A2B7B5029001AE9CA /* CALayer+SetCorner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CALayer+SetCorner.swift"; sourceTree = "<group>"; };
|
||||
|
@ -2552,9 +2554,12 @@
|
|||
34AB65FE1FC5AA320078E451 /* MWMiPhoneRoutePreview.xib */,
|
||||
34AB65FF1FC5AA320078E451 /* MWMRoutePreview.h */,
|
||||
34AB65FD1FC5AA320078E451 /* MWMRoutePreview.mm */,
|
||||
ED5E024F2D92AC9300A5CC7B /* RoutePreviewView.h */,
|
||||
34AB65D71FC5AA320078E451 /* RouteManager */,
|
||||
34AB65EC1FC5AA320078E451 /* RoutePreviewStatus */,
|
||||
34AB65D51FC5AA320078E451 /* RouteStartButton.swift */,
|
||||
ED5E02502D92E33300A5CC7B /* NavigationDashboardView.h */,
|
||||
ED5E02512D92E33300A5CC7B /* NavigationDashboardView.mm */,
|
||||
);
|
||||
path = RoutePreview;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3008,6 +3013,7 @@
|
|||
993DF0CE23F6BDB000AC231A /* MainTheme.swift */,
|
||||
ED914AB72D351DF000973C45 /* StyleApplicable.swift */,
|
||||
EDCA7CDE2D317DF9003366CE /* StyleSheet.swift */,
|
||||
EDB71D8B2D8474A0004A6A7F /* CornerRadius.swift */,
|
||||
993DF10123F6BDB100AC231A /* GlobalStyleSheet.swift */,
|
||||
99A906F223FA95AB0005872B /* PlacePageStyleSheet.swift */,
|
||||
99F8B4C523B644A6009FF0B4 /* MapStyleSheet.swift */,
|
||||
|
@ -3291,12 +3297,9 @@
|
|||
ED70D5812D539A2500738C1E /* Presentation */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ED70D57B2D539A2500738C1E /* MapPassthroughView.swift */,
|
||||
ED70D57C2D539A2500738C1E /* ModalScreenPresentationStep.swift */,
|
||||
ED70D57D2D539A2500738C1E /* SearchOnMapModalPresentationController.swift */,
|
||||
ED70D57E2D539A2500738C1E /* SearchOnMapModalTransitionManager.swift */,
|
||||
ED70D57F2D539A2500738C1E /* SideMenuDismissalAnimator.swift */,
|
||||
ED70D5802D539A2500738C1E /* SideMenuPresentationAnimator.swift */,
|
||||
ED5E02132D8B17B600A5CC7B /* ModalPresentationStepsController.swift */,
|
||||
EDB71DFF2D8B0338004A6A7F /* ModalPresentationAnimator.swift */,
|
||||
ED70D57C2D539A2500738C1E /* ModalPresentationStep.swift */,
|
||||
);
|
||||
path = Presentation;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3312,6 +3315,7 @@
|
|||
ED70D5862D539A2500738C1E /* SearchOnMapPresenter.swift */,
|
||||
ED70D5872D539A2500738C1E /* SearchOnMapViewController.swift */,
|
||||
ED5BAF4A2D688F5A0088D7B1 /* SearchOnMapHeaderView.swift */,
|
||||
EDB71E032D8B0943004A6A7F /* SearchOnMapAreaView.swift */,
|
||||
);
|
||||
path = SearchOnMap;
|
||||
sourceTree = "<group>";
|
||||
|
@ -4585,6 +4589,7 @@
|
|||
F653CE191C71F62700A453F1 /* MWMAddPlaceNavigationBar.mm in Sources */,
|
||||
340475621E081A4600C92850 /* MWMNetworkPolicy+UI.m in Sources */,
|
||||
F6E2FEE51E097BA00083EBEC /* MWMSearchNoResults.m in Sources */,
|
||||
ED5E02142D8B17B600A5CC7B /* ModalPresentationStepsController.swift in Sources */,
|
||||
F6E2FF631E097BA00083EBEC /* MWMTTSLanguageViewController.mm in Sources */,
|
||||
4715273524907F8200E91BBA /* BookmarkColorViewController.swift in Sources */,
|
||||
47E3C7292111E614008B3B27 /* FadeInAnimatedTransitioning.swift in Sources */,
|
||||
|
@ -4634,6 +4639,7 @@
|
|||
993DF12223F6BDB100AC231A /* UINavigationItemRenderer.swift in Sources */,
|
||||
993DF12B23F6BDB100AC231A /* StyleManager.swift in Sources */,
|
||||
ED43B8BD2C12063500D07BAA /* DocumentPicker.swift in Sources */,
|
||||
ED5E02522D92E33300A5CC7B /* NavigationDashboardView.mm in Sources */,
|
||||
470E1674252AD7F2002D201A /* BookmarksListInfoViewController.swift in Sources */,
|
||||
47B9065521C7FA400079C85E /* NSString+MD5.m in Sources */,
|
||||
ED5BAF4B2D688F5B0088D7B1 /* SearchOnMapHeaderView.swift in Sources */,
|
||||
|
@ -4664,6 +4670,7 @@
|
|||
99AAEA74244DA5ED0039D110 /* BottomMenuPresentationController.swift in Sources */,
|
||||
99514BB823E82B450085D3A7 /* ElevationProfilePresenter.swift in Sources */,
|
||||
34C9BD031C6DB693000DC38D /* MWMTableViewController.m in Sources */,
|
||||
EDB71E002D8B0338004A6A7F /* ModalPresentationAnimator.swift in Sources */,
|
||||
F6E2FD8C1E097BA00083EBEC /* MWMNoMapsView.m in Sources */,
|
||||
34D3B0361E389D05004100F9 /* MWMEditorSelectTableViewCell.m in Sources */,
|
||||
990128562449A82500C72B10 /* BottomTabBarView.swift in Sources */,
|
||||
|
@ -4729,6 +4736,7 @@
|
|||
34D3AFEA1E378AF1004100F9 /* UINib+Init.swift in Sources */,
|
||||
34AB663E1FC5AA330078E451 /* RouteManagerTransitioning.swift in Sources */,
|
||||
993DF0CB23F6BD0600AC231A /* ElevationDetailsRouter.swift in Sources */,
|
||||
EDB71D8C2D8474A0004A6A7F /* CornerRadius.swift in Sources */,
|
||||
47CA68FC250F99E500671019 /* BookmarksListCellStrategy.swift in Sources */,
|
||||
34AB662F1FC5AA330078E451 /* RouteManagerPresentationController.swift in Sources */,
|
||||
993F5508237C622700545511 /* DeepLinkRouteStrategyAdapter.mm in Sources */,
|
||||
|
@ -4817,16 +4825,11 @@
|
|||
3404755C1E081A4600C92850 /* MWMLocationManager.mm in Sources */,
|
||||
ED70D5892D539A2500738C1E /* SearchOnMapViewController.swift in Sources */,
|
||||
ED70D58A2D539A2500738C1E /* SearchOnMapModels.swift in Sources */,
|
||||
ED70D58B2D539A2500738C1E /* SearchOnMapModalTransitionManager.swift in Sources */,
|
||||
ED70D58C2D539A2500738C1E /* SearchOnMapPresenter.swift in Sources */,
|
||||
ED70D58D2D539A2500738C1E /* ModalScreenPresentationStep.swift in Sources */,
|
||||
ED70D58E2D539A2500738C1E /* SideMenuPresentationAnimator.swift in Sources */,
|
||||
ED70D58D2D539A2500738C1E /* ModalPresentationStep.swift in Sources */,
|
||||
ED70D58F2D539A2500738C1E /* SearchOnMapInteractor.swift in Sources */,
|
||||
ED70D5902D539A2500738C1E /* SearchOnMapModalPresentationController.swift in Sources */,
|
||||
ED70D5912D539A2500738C1E /* MapPassthroughView.swift in Sources */,
|
||||
ED70D5922D539A2500738C1E /* PlaceholderView.swift in Sources */,
|
||||
ED70D5932D539A2500738C1E /* SearchOnMapManager.swift in Sources */,
|
||||
ED70D5942D539A2500738C1E /* SideMenuDismissalAnimator.swift in Sources */,
|
||||
3454D7BC1E07F045004AF2AD /* CLLocation+Mercator.mm in Sources */,
|
||||
47E3C7272111E5A8008B3B27 /* AlertPresentationController.swift in Sources */,
|
||||
CDCA27812243F59800167D87 /* CarPlayRouter.swift in Sources */,
|
||||
|
@ -4846,6 +4849,7 @@
|
|||
34AB66381FC5AA330078E451 /* RouteManagerCell.swift in Sources */,
|
||||
ED1263AB2B6F99F900AD99F3 /* UIView+AddSeparator.swift in Sources */,
|
||||
CD4A1F132305872700F2A6B6 /* PromoBookingPresentationController.swift in Sources */,
|
||||
EDB71E042D8B0943004A6A7F /* SearchOnMapAreaView.swift in Sources */,
|
||||
3472B5D3200F501500DC6CD5 /* BackgroundFetchTaskFrameworkType.swift in Sources */,
|
||||
47E460AD240D737D00385B45 /* OpeinigHoursLocalization.swift in Sources */,
|
||||
99F9A0E52462CA0E00AE21E0 /* DownloadAllView.swift in Sources */,
|
||||
|
|
|
@ -12,8 +12,7 @@ final class SearchOnMapTests: XCTestCase {
|
|||
override func setUp() {
|
||||
super.setUp()
|
||||
searchManager = SearchManagerMock.self
|
||||
presenter = SearchOnMapPresenter(transitionManager: SearchOnMapModalTransitionManager(),
|
||||
isRouting: false,
|
||||
presenter = SearchOnMapPresenter(isRouting: false,
|
||||
didChangeState: { [weak self] in self?.currentState = $0 })
|
||||
interactor = SearchOnMapInteractor(presenter: presenter, searchManager: searchManager)
|
||||
view = SearchOnMapViewMock()
|
||||
|
@ -131,8 +130,13 @@ final class SearchOnMapTests: XCTestCase {
|
|||
searchManager.results = results
|
||||
|
||||
interactor.handle(.didSelectResult(results[0], withSearchText: searchText))
|
||||
XCTAssertEqual(currentState, .hidden)
|
||||
XCTAssertEqual(view.viewModel.presentationStep, .hidden)
|
||||
if isIPad {
|
||||
XCTAssertEqual(currentState, .searching)
|
||||
XCTAssertEqual(view.viewModel.presentationStep, .fullScreen)
|
||||
} else {
|
||||
XCTAssertEqual(currentState, .hidden)
|
||||
XCTAssertEqual(view.viewModel.presentationStep, .hidden)
|
||||
}
|
||||
}
|
||||
|
||||
func test_GivenSearchIsActive_WhenSelectPlaceOnMap_ThenHideSearch() {
|
||||
|
@ -159,8 +163,13 @@ final class SearchOnMapTests: XCTestCase {
|
|||
searchManager.results = results
|
||||
|
||||
interactor.handle(.didSelectResult(results[0], withSearchText: searchText))
|
||||
XCTAssertEqual(currentState, .hidden)
|
||||
XCTAssertEqual(view.viewModel.presentationStep, .hidden)
|
||||
if isIPad {
|
||||
XCTAssertEqual(currentState, .searching)
|
||||
XCTAssertEqual(view.viewModel.presentationStep, .fullScreen)
|
||||
} else {
|
||||
XCTAssertEqual(currentState, .hidden)
|
||||
XCTAssertEqual(view.viewModel.presentationStep, .hidden)
|
||||
}
|
||||
|
||||
interactor.handle(.didDeselectPlaceOnMap)
|
||||
XCTAssertEqual(currentState, .searching)
|
||||
|
@ -222,6 +231,10 @@ private class SearchOnMapViewMock: SearchOnMapView {
|
|||
func render(_ viewModel: SearchOnMap.ViewModel) {
|
||||
self.viewModel = viewModel
|
||||
}
|
||||
func close() {
|
||||
}
|
||||
func show() {
|
||||
}
|
||||
}
|
||||
|
||||
private class SearchManagerMock: SearchManager {
|
||||
|
|
|
@ -101,7 +101,10 @@ class AvailableArea: UIView {
|
|||
}
|
||||
|
||||
func addConstraints(otherView: UIView, directions: MWMAvailableAreaAffectDirections) {
|
||||
precondition(!directions.isEmpty)
|
||||
guard !directions.isEmpty else {
|
||||
LOG(.warning, "Attempt to add empty affecting directions from \(otherView) to \(self)")
|
||||
return
|
||||
}
|
||||
let add = { (sa: NSLayoutConstraint.Attribute, oa: NSLayoutConstraint.Attribute, rel: NSLayoutConstraint.Relation) in
|
||||
let c = NSLayoutConstraint(item: self, attribute: sa, relatedBy: rel, toItem: otherView, attribute: oa, multiplier: 1, constant: 0)
|
||||
c.priority = UILayoutPriority.defaultHigh
|
||||
|
|
|
@ -25,7 +25,7 @@ class BottomMenuViewController: MWMViewController {
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
tableView.layer.setCorner(radius: 8, corners: [.layerMinXMinYCorner, .layerMaxXMinYCorner])
|
||||
tableView.layer.setCornerRadius(.buttonDefault, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner])
|
||||
tableView.sectionFooterHeight = 0
|
||||
|
||||
tableView.dataSource = presenter
|
||||
|
|
|
@ -22,7 +22,7 @@ final class ProductButton: UIButton {
|
|||
titleLabel?.allowsDefaultTighteningForTruncation = true
|
||||
titleLabel?.adjustsFontSizeToFitWidth = true
|
||||
titleLabel?.minimumScaleFactor = 0.5
|
||||
layer.setCorner(radius: 5.0)
|
||||
layer.setCornerRadius(.buttonDefaultSmall)
|
||||
layer.masksToBounds = true
|
||||
addTarget(self, action: #selector(buttonDidTap), for: .touchUpInside)
|
||||
}
|
||||
|
|
|
@ -153,7 +153,6 @@ final class PlacePageScrollView: UIScrollView {
|
|||
|
||||
private func setupView() {
|
||||
let bgView = UIView()
|
||||
bgView.setStyle(.ppBackgroundView)
|
||||
stackView.insertSubview(bgView, at: 0)
|
||||
bgView.alignToSuperview()
|
||||
|
||||
|
@ -163,7 +162,7 @@ final class PlacePageScrollView: UIScrollView {
|
|||
stackView.backgroundColor = .clear
|
||||
|
||||
let cornersToMask: CACornerMask = alternativeSizeClass(iPhone: [], iPad: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner])
|
||||
actionBarContainerView.layer.setCorner(radius: 16, corners: cornersToMask)
|
||||
actionBarContainerView.layer.setCornerRadius(.modalSheet, maskedCorners: cornersToMask)
|
||||
actionBarContainerView.layer.masksToBounds = true
|
||||
|
||||
// See https://github.com/organicmaps/organicmaps/issues/6917 for the details.
|
||||
|
|
|
@ -43,7 +43,7 @@ class DifficultyView: UIView {
|
|||
for _ in 0..<difficultyLevelCount {
|
||||
let view = UIView()
|
||||
stackView.addArrangedSubview(view)
|
||||
view.layer.setCorner(radius: bulletSize.height / 2)
|
||||
view.layer.setCornerRadius(.custom(bulletSize.height / 2))
|
||||
views.append(view)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,8 +115,8 @@ final class PlaceholderView: UIView {
|
|||
|
||||
// MARK: - ModallyPresentedViewController
|
||||
extension PlaceholderView: ModallyPresentedViewController {
|
||||
func translationYDidUpdate(_ translationY: CGFloat) {
|
||||
self.containerModalYTranslation = translationY
|
||||
func presentationFrameDidChange(_ frame: CGRect) {
|
||||
self.containerModalYTranslation = frame.origin.y
|
||||
reloadConstraints()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
/// A transparent view that allows touch events to pass through to the MapViewController's view.
|
||||
///
|
||||
/// This view is used to enable interaction with the underlying map while still maintaining a
|
||||
/// transparent overlay. It does not block touch events but forwards them to the specified `passingView`.
|
||||
|
||||
final class MapPassthroughView: UIView {
|
||||
private weak var passingView: UIView?
|
||||
|
||||
init(passingView: UIView) {
|
||||
self.passingView = passingView
|
||||
super.init(frame: passingView.bounds)
|
||||
self.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
self.alpha = 0
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
guard let passingView else { return nil }
|
||||
let pointInPassthroughView = passingView.convert(point, from: self)
|
||||
super.hitTest(point, with: event)
|
||||
if passingView.bounds.contains(pointInPassthroughView) {
|
||||
return MapViewController.shared()?.view.hitTest(point, with: event)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
enum PresentationStepChangeAnimation {
|
||||
case none
|
||||
case slide
|
||||
case slideAndBounce
|
||||
}
|
||||
|
||||
final class ModalPresentationAnimator {
|
||||
|
||||
private enum Constants {
|
||||
static let animationDuration: TimeInterval = kDefaultAnimationDuration
|
||||
static let springDamping: CGFloat = 0.85
|
||||
static let springVelocity: CGFloat = 0.2
|
||||
}
|
||||
|
||||
static func animate(with stepAnimation: PresentationStepChangeAnimation = .slide,
|
||||
animations: @escaping (() -> Void),
|
||||
completion: ((Bool) -> Void)?) {
|
||||
switch stepAnimation {
|
||||
case .none:
|
||||
animations()
|
||||
completion?(true)
|
||||
case .slide:
|
||||
UIView.animate(withDuration: Constants.animationDuration,
|
||||
delay: 0,
|
||||
options: .curveEaseOut,
|
||||
animations: animations,
|
||||
completion: completion)
|
||||
case .slideAndBounce:
|
||||
UIView.animate(withDuration: Constants.animationDuration,
|
||||
delay: 0,
|
||||
usingSpringWithDamping: Constants.springDamping,
|
||||
initialSpringVelocity: Constants.springVelocity,
|
||||
options: .curveLinear,
|
||||
animations: animations,
|
||||
completion: completion)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
enum ModalScreenPresentationStep {
|
||||
enum ModalPresentationStep: Int, CaseIterable {
|
||||
case fullScreen
|
||||
case halfScreen
|
||||
case compact
|
||||
case hidden
|
||||
}
|
||||
|
||||
extension ModalScreenPresentationStep {
|
||||
extension ModalPresentationStep {
|
||||
private enum Constants {
|
||||
static let iPadWidth: CGFloat = 350
|
||||
static let compactHeightOffset: CGFloat = 120
|
||||
|
@ -14,7 +14,7 @@ extension ModalScreenPresentationStep {
|
|||
static let landscapeTopInset: CGFloat = 10
|
||||
}
|
||||
|
||||
var upper: ModalScreenPresentationStep {
|
||||
var upper: ModalPresentationStep {
|
||||
switch self {
|
||||
case .fullScreen:
|
||||
return .fullScreen
|
||||
|
@ -27,7 +27,7 @@ extension ModalScreenPresentationStep {
|
|||
}
|
||||
}
|
||||
|
||||
var lower: ModalScreenPresentationStep {
|
||||
var lower: ModalPresentationStep {
|
||||
switch self {
|
||||
case .fullScreen:
|
||||
return .halfScreen
|
||||
|
@ -40,18 +40,22 @@ extension ModalScreenPresentationStep {
|
|||
}
|
||||
}
|
||||
|
||||
var first: ModalScreenPresentationStep {
|
||||
var first: ModalPresentationStep {
|
||||
.fullScreen
|
||||
}
|
||||
|
||||
var last: ModalScreenPresentationStep {
|
||||
var last: ModalPresentationStep {
|
||||
.compact
|
||||
}
|
||||
|
||||
func frame(for viewController: UIViewController, in containerView: UIView) -> CGRect {
|
||||
func frame(for presentedView: UIView, in containerViewController: UIViewController) -> CGRect {
|
||||
let isIPad = UIDevice.current.userInterfaceIdiom == .pad
|
||||
let containerSize = containerView.bounds.size
|
||||
let safeAreaInsets = containerView.safeAreaInsets
|
||||
var containerSize = containerViewController.view.bounds.size
|
||||
if containerSize == .zero {
|
||||
containerSize = UIScreen.main.bounds.size
|
||||
}
|
||||
let safeAreaInsets = containerViewController.view.safeAreaInsets
|
||||
let traitCollection = containerViewController.traitCollection
|
||||
var frame = CGRect(origin: .zero, size: containerSize)
|
||||
|
||||
if isIPad {
|
||||
|
@ -65,7 +69,7 @@ extension ModalScreenPresentationStep {
|
|||
return frame
|
||||
}
|
||||
|
||||
let isPortraitOrientation = viewController.traitCollection.verticalSizeClass == .regular
|
||||
let isPortraitOrientation = traitCollection.verticalSizeClass == .regular
|
||||
if isPortraitOrientation {
|
||||
switch self {
|
||||
case .fullScreen:
|
|
@ -0,0 +1,118 @@
|
|||
final class ModalPresentationStepsController {
|
||||
|
||||
enum StepUpdate {
|
||||
case didClose
|
||||
case didUpdateFrame(CGRect)
|
||||
case didUpdateStep(ModalPresentationStep)
|
||||
}
|
||||
|
||||
fileprivate enum Constants {
|
||||
static let slowSwipeVelocity: CGFloat = 500
|
||||
static let fastSwipeDownVelocity: CGFloat = 4000
|
||||
static let fastSwipeUpVelocity: CGFloat = 3000
|
||||
static let translationThreshold: CGFloat = 50
|
||||
}
|
||||
|
||||
private weak var presentedView: UIView?
|
||||
private weak var containerViewController: UIViewController?
|
||||
|
||||
private var initialTranslationY: CGFloat = .zero
|
||||
|
||||
private(set) var currentStep: ModalPresentationStep = .fullScreen
|
||||
private(set) var maxAvailableFrame: CGRect = .zero
|
||||
|
||||
var currentFrame: CGRect { frame(for: currentStep) }
|
||||
var hiddenFrame: CGRect { frame(for: .hidden) }
|
||||
|
||||
var didUpdateHandler: ((StepUpdate) -> Void)?
|
||||
|
||||
func set(presentedView: UIView, containerViewController: UIViewController) {
|
||||
self.presentedView = presentedView
|
||||
self.containerViewController = containerViewController
|
||||
}
|
||||
|
||||
func setInitialState() {
|
||||
setStep(.hidden, animation: .none)
|
||||
}
|
||||
|
||||
func close(completion: (() -> Void)? = nil) {
|
||||
setStep(.hidden, animation: .slide, completion: completion)
|
||||
}
|
||||
|
||||
func updateMaxAvailableFrame() {
|
||||
maxAvailableFrame = frame(for: .fullScreen)
|
||||
}
|
||||
|
||||
func handlePan(_ gesture: UIPanGestureRecognizer) {
|
||||
guard let presentedView else { return }
|
||||
let translation = gesture.translation(in: presentedView)
|
||||
let velocity = gesture.velocity(in: presentedView)
|
||||
var currentFrame = presentedView.frame
|
||||
|
||||
switch gesture.state {
|
||||
case .began:
|
||||
initialTranslationY = presentedView.frame.origin.y
|
||||
case .changed:
|
||||
let newY = max(max(initialTranslationY + translation.y, 0), maxAvailableFrame.origin.y)
|
||||
currentFrame.origin.y = newY
|
||||
presentedView.frame = currentFrame
|
||||
didUpdateHandler?(.didUpdateFrame(currentFrame))
|
||||
case .ended:
|
||||
let nextStep: ModalPresentationStep
|
||||
if velocity.y > Constants.fastSwipeDownVelocity {
|
||||
didUpdateHandler?(.didClose)
|
||||
return
|
||||
} else if velocity.y < -Constants.fastSwipeUpVelocity {
|
||||
nextStep = .fullScreen
|
||||
} else if velocity.y > Constants.slowSwipeVelocity || translation.y > Constants.translationThreshold {
|
||||
if currentStep == .compact {
|
||||
didUpdateHandler?(.didClose)
|
||||
return
|
||||
}
|
||||
nextStep = currentStep.lower
|
||||
} else if velocity.y < -Constants.slowSwipeVelocity || translation.y < -Constants.translationThreshold {
|
||||
nextStep = currentStep.upper
|
||||
} else {
|
||||
nextStep = currentStep
|
||||
}
|
||||
|
||||
let animation: PresentationStepChangeAnimation = abs(velocity.y) > Constants.slowSwipeVelocity ? .slideAndBounce : .slide
|
||||
setStep(nextStep, animation: animation, notifyAboutStepUpdate: true)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func setStep(_ step: ModalPresentationStep,
|
||||
completion: (() -> Void)? = nil) {
|
||||
guard currentStep != step else { return }
|
||||
setStep(step, animation: .slide, notifyAboutStepUpdate: false, completion: completion)
|
||||
}
|
||||
|
||||
private func setStep(_ step: ModalPresentationStep,
|
||||
animation: PresentationStepChangeAnimation,
|
||||
notifyAboutStepUpdate: Bool = true,
|
||||
completion: (() -> Void)? = nil) {
|
||||
guard let presentedView else { return }
|
||||
currentStep = step
|
||||
updateMaxAvailableFrame()
|
||||
|
||||
let frame = frame(for: step)
|
||||
didUpdateHandler?(.didUpdateFrame(frame))
|
||||
|
||||
ModalPresentationAnimator.animate(with: animation) {
|
||||
presentedView.frame = frame
|
||||
} completion: { [weak self] _ in
|
||||
guard let self else { return }
|
||||
if notifyAboutStepUpdate {
|
||||
self.didUpdateHandler?(.didUpdateStep(step))
|
||||
}
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
|
||||
private func frame(for step: ModalPresentationStep) -> CGRect {
|
||||
guard let presentedView, let containerViewController else { return .zero }
|
||||
return step.frame(for: presentedView, in: containerViewController)
|
||||
}
|
||||
}
|
|
@ -1,226 +0,0 @@
|
|||
protocol ModallyPresentedViewController {
|
||||
func translationYDidUpdate(_ translationY: CGFloat)
|
||||
}
|
||||
|
||||
protocol SearchOnMapModalPresentationView: AnyObject {
|
||||
func setPresentationStep(_ step: ModalScreenPresentationStep)
|
||||
func close()
|
||||
}
|
||||
|
||||
final class SearchOnMapModalPresentationController: UIPresentationController {
|
||||
|
||||
private enum StepChangeAnimation {
|
||||
case slide
|
||||
case slideAndBounce
|
||||
}
|
||||
|
||||
private enum Constants {
|
||||
static let animationDuration: TimeInterval = kDefaultAnimationDuration
|
||||
static let springDamping: CGFloat = 0.85
|
||||
static let springVelocity: CGFloat = 0.2
|
||||
static let iPhoneCornerRadius: CGFloat = 10
|
||||
static let slowSwipeVelocity: CGFloat = 500
|
||||
static let fastSwipeDownVelocity: CGFloat = 4000
|
||||
static let fastSwipeUpVelocity: CGFloat = 3000
|
||||
static let translationThreshold: CGFloat = 50
|
||||
static let panGestureThreshold: CGFloat = 5
|
||||
}
|
||||
|
||||
private var initialTranslationY: CGFloat = 0
|
||||
private weak var interactor: SearchOnMapInteractor? {
|
||||
(presentedViewController as? SearchOnMapViewController)?.interactor
|
||||
}
|
||||
// TODO: replace with set of steps passed from the outside
|
||||
private var presentationStep: ModalScreenPresentationStep = .fullScreen
|
||||
private var internalScrollViewContentOffset: CGFloat = 0
|
||||
private var maxAvailableFrameOfPresentedView: CGRect = .zero
|
||||
|
||||
// MARK: - Init
|
||||
override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
|
||||
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
|
||||
|
||||
iPhoneSpecific {
|
||||
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
|
||||
panGestureRecognizer.delegate = self
|
||||
presentedViewController.view.addGestureRecognizer(panGestureRecognizer)
|
||||
if let presentedViewController = presentedViewController as? SearchOnMapView {
|
||||
presentedViewController.scrollViewDelegate = self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Lifecycle
|
||||
override func containerViewWillLayoutSubviews() {
|
||||
super.containerViewWillLayoutSubviews()
|
||||
presentedView?.frame = frameOfPresentedViewInContainerView
|
||||
}
|
||||
|
||||
override func presentationTransitionWillBegin() {
|
||||
guard let containerView else { return }
|
||||
containerView.backgroundColor = .clear
|
||||
let passThroughView = MapPassthroughView(passingView: containerView)
|
||||
containerView.addSubview(passThroughView)
|
||||
}
|
||||
|
||||
override func presentationTransitionDidEnd(_ completed: Bool) {
|
||||
translationYDidUpdate(presentedView?.frame.origin.y ?? 0)
|
||||
}
|
||||
|
||||
override func dismissalTransitionDidEnd(_ completed: Bool) {
|
||||
super.dismissalTransitionDidEnd(completed)
|
||||
if completed {
|
||||
presentedView?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
super.traitCollectionDidChange(previousTraitCollection)
|
||||
updateMaxAvailableFrameOfPresentedView()
|
||||
}
|
||||
|
||||
// MARK: - Layout
|
||||
override var frameOfPresentedViewInContainerView: CGRect {
|
||||
guard let containerView else { return .zero }
|
||||
let frame = presentationStep.frame(for: presentedViewController, in: containerView)
|
||||
updateMaxAvailableFrameOfPresentedView()
|
||||
return frame
|
||||
}
|
||||
|
||||
private func updateMaxAvailableFrameOfPresentedView() {
|
||||
guard let containerView else { return }
|
||||
maxAvailableFrameOfPresentedView = ModalScreenPresentationStep.fullScreen.frame(for: presentedViewController, in: containerView)
|
||||
}
|
||||
|
||||
private func updateSideButtonsAvailableArea(_ newY: CGFloat) {
|
||||
iPhoneSpecific {
|
||||
guard presentedViewController.traitCollection.verticalSizeClass != .compact else { return }
|
||||
var sideButtonsAvailableArea = MWMSideButtons.getAvailableArea()
|
||||
sideButtonsAvailableArea.size.height = newY - sideButtonsAvailableArea.origin.y
|
||||
MWMSideButtons.updateAvailableArea(sideButtonsAvailableArea)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Pan gesture handling
|
||||
@objc private func handlePan(_ gesture: UIPanGestureRecognizer) {
|
||||
guard let presentedView, maxAvailableFrameOfPresentedView != .zero else { return }
|
||||
interactor?.handle(.didStartDraggingSearch)
|
||||
|
||||
let translation = gesture.translation(in: presentedView)
|
||||
let velocity = gesture.velocity(in: presentedView)
|
||||
|
||||
switch gesture.state {
|
||||
case .began:
|
||||
initialTranslationY = presentedView.frame.origin.y
|
||||
case .changed:
|
||||
let newY = max(max(initialTranslationY + translation.y, 0), maxAvailableFrameOfPresentedView.origin.y)
|
||||
presentedView.frame.origin.y = newY
|
||||
updateSideButtonsAvailableArea(newY)
|
||||
translationYDidUpdate(newY)
|
||||
case .ended:
|
||||
let nextStep: ModalScreenPresentationStep
|
||||
if velocity.y > Constants.fastSwipeDownVelocity {
|
||||
interactor?.handle(.closeSearch)
|
||||
return
|
||||
} else if velocity.y < -Constants.fastSwipeUpVelocity {
|
||||
nextStep = .fullScreen // fast swipe up
|
||||
} else if velocity.y > Constants.slowSwipeVelocity || translation.y > Constants.translationThreshold {
|
||||
if presentationStep == .compact {
|
||||
interactor?.handle(.closeSearch)
|
||||
return
|
||||
}
|
||||
nextStep = presentationStep.lower // regular swipe down
|
||||
} else if velocity.y < -Constants.slowSwipeVelocity || translation.y < -Constants.translationThreshold {
|
||||
nextStep = presentationStep.upper // regular swipe up
|
||||
} else {
|
||||
// TODO: swipe to closest step on the big translation
|
||||
nextStep = presentationStep
|
||||
}
|
||||
let animation: StepChangeAnimation = abs(velocity.y) > Constants.slowSwipeVelocity ? .slideAndBounce : .slide
|
||||
animateTo(nextStep, animation: animation)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func animateTo(_ presentationStep: ModalScreenPresentationStep, animation: StepChangeAnimation = .slide) {
|
||||
guard let presentedView, let containerView else { return }
|
||||
self.presentationStep = presentationStep
|
||||
interactor?.handle(.didUpdatePresentationStep(presentationStep))
|
||||
|
||||
let updatedFrame = presentationStep.frame(for: presentedViewController, in: containerView)
|
||||
let targetYTranslation = updatedFrame.origin.y
|
||||
|
||||
switch animation {
|
||||
case .slide:
|
||||
UIView.animate(withDuration: Constants.animationDuration,
|
||||
delay: 0,
|
||||
options: .curveEaseOut,
|
||||
animations: { [weak self] in
|
||||
presentedView.frame = updatedFrame
|
||||
self?.translationYDidUpdate(targetYTranslation)
|
||||
self?.updateSideButtonsAvailableArea(targetYTranslation)
|
||||
})
|
||||
case .slideAndBounce:
|
||||
UIView.animate(withDuration: Constants.animationDuration,
|
||||
delay: 0,
|
||||
usingSpringWithDamping: Constants.springDamping,
|
||||
initialSpringVelocity: Constants.springVelocity,
|
||||
options: .curveLinear,
|
||||
animations: { [weak self] in
|
||||
presentedView.frame = updatedFrame
|
||||
self?.translationYDidUpdate(targetYTranslation)
|
||||
self?.updateSideButtonsAvailableArea(targetYTranslation)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SearchOnMapModalPresentationView
|
||||
extension SearchOnMapModalPresentationController: SearchOnMapModalPresentationView {
|
||||
func setPresentationStep(_ step: ModalScreenPresentationStep) {
|
||||
guard presentationStep != step else { return }
|
||||
animateTo(step)
|
||||
}
|
||||
|
||||
func close() {
|
||||
guard let containerView else { return }
|
||||
updateSideButtonsAvailableArea(containerView.frame.height)
|
||||
presentedViewController.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ModallyPresentedViewController
|
||||
extension SearchOnMapModalPresentationController: ModallyPresentedViewController {
|
||||
func translationYDidUpdate(_ translationY: CGFloat) {
|
||||
iPhoneSpecific {
|
||||
(presentedViewController as? SearchOnMapViewController)?.translationYDidUpdate(translationY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIGestureRecognizerDelegate
|
||||
extension SearchOnMapModalPresentationController: UIGestureRecognizerDelegate {
|
||||
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
true
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
// threshold is used to soften transition from the internal scroll zero content offset
|
||||
internalScrollViewContentOffset < Constants.panGestureThreshold
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SearchOnMapScrollViewDelegate
|
||||
extension SearchOnMapModalPresentationController: SearchOnMapScrollViewDelegate {
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
guard let presentedView else { return }
|
||||
let hasReachedTheTop = Int(presentedView.frame.origin.y) > Int(maxAvailableFrameOfPresentedView.origin.y)
|
||||
let hasZeroContentOffset = internalScrollViewContentOffset == 0
|
||||
if hasReachedTheTop && hasZeroContentOffset {
|
||||
// prevent the internal scroll view scrolling
|
||||
scrollView.contentOffset.y = internalScrollViewContentOffset
|
||||
return
|
||||
}
|
||||
internalScrollViewContentOffset = scrollView.contentOffset.y
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
@objc
|
||||
final class SearchOnMapModalTransitionManager: NSObject, UIViewControllerTransitioningDelegate {
|
||||
|
||||
weak var presentationController: SearchOnMapModalPresentationView?
|
||||
|
||||
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> (any UIViewControllerAnimatedTransitioning)? {
|
||||
isIPad ? SideMenuPresentationAnimator() : nil
|
||||
}
|
||||
|
||||
func animationController(forDismissed dismissed: UIViewController) -> (any UIViewControllerAnimatedTransitioning)? {
|
||||
isIPad ? SideMenuDismissalAnimator() : nil
|
||||
}
|
||||
|
||||
func presentationController(forPresented presented: UIViewController,
|
||||
presenting: UIViewController?,
|
||||
source: UIViewController) -> UIPresentationController? {
|
||||
let presentationController = SearchOnMapModalPresentationController(presentedViewController: presented, presenting: presenting)
|
||||
self.presentationController = presentationController
|
||||
return presentationController
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
final class SideMenuDismissalAnimator: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
return kDefaultAnimationDuration / 2
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
guard let fromVC = transitionContext.viewController(forKey: .from) else { return }
|
||||
let initialFrame = transitionContext.initialFrame(for: fromVC)
|
||||
let targetFrame = initialFrame.offsetBy(dx: -initialFrame.width, dy: 0)
|
||||
|
||||
UIView.animate(withDuration: transitionDuration(using: transitionContext),
|
||||
delay: .zero,
|
||||
options: .curveEaseIn,
|
||||
animations: {
|
||||
fromVC.view.frame = targetFrame
|
||||
},
|
||||
completion: {
|
||||
fromVC.view.removeFromSuperview()
|
||||
transitionContext.completeTransition($0)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
final class SideMenuPresentationAnimator: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
return kDefaultAnimationDuration / 2
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
guard let toVC = transitionContext.viewController(forKey: .to) else { return }
|
||||
let containerView = transitionContext.containerView
|
||||
let finalFrame = transitionContext.finalFrame(for: toVC)
|
||||
let originFrame = finalFrame.offsetBy(dx: -finalFrame.width, dy: 0)
|
||||
containerView.addSubview(toVC.view)
|
||||
toVC.view.frame = originFrame
|
||||
toVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
|
||||
UIView.animate(withDuration: transitionDuration(using: transitionContext),
|
||||
delay: .zero,
|
||||
options: .curveEaseOut,
|
||||
animations: {
|
||||
toVC.view.frame = finalFrame
|
||||
},
|
||||
completion: {
|
||||
transitionContext.completeTransition($0)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
final class SearchOnMapAreaView: UIView {
|
||||
override var sideButtonsAreaAffectDirections: MWMAvailableAreaAffectDirections {
|
||||
alternative(iPhone: .bottom, iPad: [])
|
||||
}
|
||||
|
||||
override var trafficButtonAreaAffectDirections: MWMAvailableAreaAffectDirections {
|
||||
alternative(iPhone: .bottom, iPad: [])
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
protocol SearchOnMapHeaderViewDelegate: UISearchBarDelegate {
|
||||
func cancelButtonDidTap()
|
||||
func grabberDidTap()
|
||||
}
|
||||
|
||||
final class SearchOnMapHeaderView: UIView {
|
||||
|
@ -10,15 +11,19 @@ final class SearchOnMapHeaderView: UIView {
|
|||
}
|
||||
|
||||
private enum Constants {
|
||||
static let searchBarHeight: CGFloat = 36
|
||||
static let searchBarInsets: UIEdgeInsets = UIEdgeInsets(top: 8, left: 10, bottom: 10, right: 0)
|
||||
static let grabberHeight: CGFloat = 5
|
||||
static let grabberWidth: CGFloat = 36
|
||||
static let grabberTopMargin: CGFloat = 5
|
||||
static let cancelButtonInsets: UIEdgeInsets = UIEdgeInsets(top: 0, left: 6, bottom: 0, right: 8)
|
||||
static let cancelButtonInsets: UIEdgeInsets = UIEdgeInsets(top: 0, left: 6, bottom: 0, right: 16)
|
||||
}
|
||||
|
||||
private let grabberView = UIView()
|
||||
private let grabberTapHandlerView = UIView()
|
||||
private let searchBar = UISearchBar()
|
||||
private let cancelButton = UIButton()
|
||||
private let cancelContainer = UIView()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
@ -32,22 +37,29 @@ final class SearchOnMapHeaderView: UIView {
|
|||
}
|
||||
|
||||
private func setupView() {
|
||||
setStyle(.searchHeader)
|
||||
layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
setStyle(.primaryBackground)
|
||||
|
||||
setupGrabberView()
|
||||
setupGrabberTapHandlerView()
|
||||
setupSearchBar()
|
||||
setupCancelButton()
|
||||
}
|
||||
|
||||
private func setupGrabberView() {
|
||||
grabberView.setStyle(.background)
|
||||
grabberView.layer.setCorner(radius: Constants.grabberHeight / 2)
|
||||
grabberView.setStyle(.grabber)
|
||||
iPadSpecific { [weak self] in
|
||||
self?.grabberView.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
private func setupGrabberTapHandlerView() {
|
||||
grabberTapHandlerView.backgroundColor = .clear
|
||||
iPhoneSpecific {
|
||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(grabberDidTap))
|
||||
grabberTapHandlerView.addGestureRecognizer(tapGesture)
|
||||
}
|
||||
}
|
||||
|
||||
private func setupSearchBar() {
|
||||
searchBar.placeholder = L("search")
|
||||
searchBar.showsCancelButton = false
|
||||
|
@ -59,19 +71,24 @@ final class SearchOnMapHeaderView: UIView {
|
|||
}
|
||||
|
||||
private func setupCancelButton() {
|
||||
cancelButton.tintColor = .whitePrimaryText()
|
||||
cancelButton.setStyle(.clearBackground)
|
||||
cancelContainer.setStyle(.primaryBackground)
|
||||
cancelButton.setStyle(.searchCancelButton)
|
||||
cancelButton.setTitle(L("cancel"), for: .normal)
|
||||
cancelButton.addTarget(self, action: #selector(cancelButtonTapped), for: .touchUpInside)
|
||||
cancelButton.addTarget(self, action: #selector(cancelButtonDidTap), for: .touchUpInside)
|
||||
}
|
||||
|
||||
private func layoutView() {
|
||||
addSubview(grabberView)
|
||||
addSubview(grabberTapHandlerView)
|
||||
addSubview(searchBar)
|
||||
addSubview(cancelButton)
|
||||
addSubview(cancelContainer)
|
||||
cancelContainer.addSubview(cancelButton)
|
||||
|
||||
grabberView.translatesAutoresizingMaskIntoConstraints = false
|
||||
grabberTapHandlerView.translatesAutoresizingMaskIntoConstraints = false
|
||||
grabberTapHandlerView.setContentHuggingPriority(.defaultLow, for: .vertical)
|
||||
searchBar.translatesAutoresizingMaskIntoConstraints = false
|
||||
cancelContainer.translatesAutoresizingMaskIntoConstraints = false
|
||||
cancelButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
|
@ -80,18 +97,33 @@ final class SearchOnMapHeaderView: UIView {
|
|||
grabberView.widthAnchor.constraint(equalToConstant: Constants.grabberWidth),
|
||||
grabberView.heightAnchor.constraint(equalToConstant: Constants.grabberHeight),
|
||||
|
||||
searchBar.topAnchor.constraint(equalTo: grabberView.bottomAnchor),
|
||||
searchBar.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
searchBar.trailingAnchor.constraint(equalTo: cancelButton.leadingAnchor, constant: -Constants.cancelButtonInsets.left),
|
||||
grabberTapHandlerView.topAnchor.constraint(equalTo: grabberView.bottomAnchor),
|
||||
grabberTapHandlerView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
grabberTapHandlerView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
grabberTapHandlerView.bottomAnchor.constraint(equalTo: searchBar.topAnchor),
|
||||
|
||||
cancelButton.centerYAnchor.constraint(equalTo: searchBar.centerYAnchor),
|
||||
cancelButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -Constants.cancelButtonInsets.right),
|
||||
searchBar.topAnchor.constraint(equalTo: grabberView.bottomAnchor, constant: Constants.searchBarInsets.top),
|
||||
searchBar.leadingAnchor.constraint(equalTo: leadingAnchor, constant: Constants.searchBarInsets.left),
|
||||
searchBar.trailingAnchor.constraint(equalTo: cancelContainer.leadingAnchor),
|
||||
searchBar.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -Constants.searchBarInsets.bottom),
|
||||
searchBar.heightAnchor.constraint(equalToConstant: Constants.searchBarHeight),
|
||||
|
||||
bottomAnchor.constraint(equalTo: searchBar.bottomAnchor)
|
||||
cancelContainer.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
cancelContainer.topAnchor.constraint(equalTo: searchBar.topAnchor),
|
||||
cancelContainer.bottomAnchor.constraint(equalTo: searchBar.bottomAnchor),
|
||||
|
||||
cancelButton.topAnchor.constraint(equalTo: cancelContainer.topAnchor),
|
||||
cancelButton.leadingAnchor.constraint(equalTo: cancelContainer.leadingAnchor, constant: Constants.cancelButtonInsets.left),
|
||||
cancelButton.trailingAnchor.constraint(equalTo: cancelContainer.trailingAnchor, constant: -Constants.cancelButtonInsets.right),
|
||||
cancelButton.bottomAnchor.constraint(equalTo: cancelContainer.bottomAnchor),
|
||||
])
|
||||
}
|
||||
|
||||
@objc private func cancelButtonTapped() {
|
||||
@objc private func grabberDidTap() {
|
||||
delegate?.grabberDidTap()
|
||||
}
|
||||
|
||||
@objc private func cancelButtonDidTap() {
|
||||
delegate?.cancelButtonDidTap()
|
||||
}
|
||||
|
||||
|
|
|
@ -70,12 +70,12 @@ final class SearchOnMapInteractor: NSObject {
|
|||
searchManager.saveQuery(searchText.text,
|
||||
forInputLocale: searchText.locale)
|
||||
showResultsOnMap = true
|
||||
searchManager.showEverywhereSearchResultsOnMap()
|
||||
return .showOnTheMap
|
||||
}
|
||||
|
||||
private func processTypedText(_ searchText: SearchOnMap.SearchText) -> SearchOnMap.Response {
|
||||
isUpdatesDisabled = false
|
||||
showResultsOnMap = true
|
||||
searchManager.searchQuery(searchText.text,
|
||||
forInputLocale: searchText.locale,
|
||||
withCategory: false)
|
||||
|
|
|
@ -19,16 +19,14 @@ protocol SearchOnMapManagerObserver: AnyObject {
|
|||
|
||||
@objcMembers
|
||||
final class SearchOnMapManager: NSObject {
|
||||
private let navigationController: UINavigationController
|
||||
private weak var interactor: SearchOnMapInteractor?
|
||||
private var interactor: SearchOnMapInteractor? { viewController?.interactor }
|
||||
private let observers = ListenerContainer<SearchOnMapManagerObserver>()
|
||||
|
||||
// MARK: - Public properties
|
||||
weak var viewController: UIViewController?
|
||||
weak var viewController: SearchOnMapViewController?
|
||||
var isSearching: Bool { viewController != nil }
|
||||
|
||||
init(navigationController: UINavigationController = MapViewController.shared()!.navigationController!) {
|
||||
self.navigationController = navigationController
|
||||
override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
@ -38,10 +36,9 @@ final class SearchOnMapManager: NSObject {
|
|||
return
|
||||
}
|
||||
FrameworkHelper.deactivateMapSelection()
|
||||
let viewController = buildViewController(isRouting: isRouting)
|
||||
let viewController = SearchOnMapViewControllerBuilder.build(isRouting: isRouting,
|
||||
didChangeState: notifyObservers)
|
||||
self.viewController = viewController
|
||||
self.interactor = viewController.interactor
|
||||
navigationController.present(viewController, animated: true)
|
||||
}
|
||||
|
||||
func hide() {
|
||||
|
@ -77,20 +74,20 @@ final class SearchOnMapManager: NSObject {
|
|||
observers.removeListener(observer)
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
private func buildViewController(isRouting: Bool) -> SearchOnMapViewController {
|
||||
let transitioningManager = SearchOnMapModalTransitionManager()
|
||||
let presenter = SearchOnMapPresenter(transitionManager: transitioningManager,
|
||||
isRouting: isRouting,
|
||||
didChangeState: { [weak self] state in
|
||||
guard let self else { return }
|
||||
self.observers.forEach { observer in observer.searchManager(didChangeState: state) }
|
||||
})
|
||||
private func notifyObservers(_ state: SearchOnMapState) {
|
||||
observers.forEach { observer in observer.searchManager(didChangeState: state) }
|
||||
}
|
||||
}
|
||||
|
||||
private struct SearchOnMapViewControllerBuilder {
|
||||
static func build(isRouting: Bool, didChangeState: @escaping ((SearchOnMapState) -> Void)) -> SearchOnMapViewController {
|
||||
let viewController = SearchOnMapViewController()
|
||||
let presenter = SearchOnMapPresenter(isRouting: isRouting,
|
||||
didChangeState: didChangeState)
|
||||
let interactor = SearchOnMapInteractor(presenter: presenter)
|
||||
let viewController = SearchOnMapViewController(interactor: interactor)
|
||||
presenter.view = viewController
|
||||
viewController.modalPresentationStyle = .custom
|
||||
viewController.transitioningDelegate = transitioningManager
|
||||
viewController.interactor = interactor
|
||||
viewController.show()
|
||||
return viewController
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
enum SearchOnMap {
|
||||
struct ViewModel: Equatable {
|
||||
enum ContentState: Equatable {
|
||||
enum Content: Equatable {
|
||||
case historyAndCategory
|
||||
case results(SearchResults)
|
||||
case noResults
|
||||
|
@ -10,8 +10,8 @@ enum SearchOnMap {
|
|||
var isTyping: Bool
|
||||
var skipSuggestions: Bool
|
||||
var searchingText: String?
|
||||
var contentState: ContentState
|
||||
var presentationStep: ModalScreenPresentationStep
|
||||
var contentState: Content
|
||||
var presentationStep: ModalPresentationStep
|
||||
}
|
||||
|
||||
struct SearchResults: Equatable {
|
||||
|
@ -54,7 +54,7 @@ enum SearchOnMap {
|
|||
case clearButtonDidTap
|
||||
case didSelectPlaceOnMap
|
||||
case didDeselectPlaceOnMap
|
||||
case didUpdatePresentationStep(ModalScreenPresentationStep)
|
||||
case didUpdatePresentationStep(ModalPresentationStep)
|
||||
}
|
||||
|
||||
enum Response: Equatable {
|
||||
|
@ -67,7 +67,7 @@ enum SearchOnMap {
|
|||
case clearSearch
|
||||
case setSearchScreenHidden(Bool)
|
||||
case setSearchScreenCompact
|
||||
case updatePresentationStep(ModalScreenPresentationStep)
|
||||
case updatePresentationStep(ModalPresentationStep)
|
||||
case close
|
||||
case none
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ final class SearchOnMapPresenter {
|
|||
typealias ViewModel = SearchOnMap.ViewModel
|
||||
|
||||
weak var view: SearchOnMapView?
|
||||
weak var presentationView: SearchOnMapModalPresentationView? { transitionManager.presentationController }
|
||||
|
||||
private var searchState: SearchOnMapState = .searching {
|
||||
didSet {
|
||||
|
@ -12,13 +11,11 @@ final class SearchOnMapPresenter {
|
|||
}
|
||||
}
|
||||
|
||||
private let transitionManager: SearchOnMapModalTransitionManager
|
||||
private var viewModel: ViewModel = .initial
|
||||
private var isRouting: Bool
|
||||
private var didChangeState: ((SearchOnMapState) -> Void)?
|
||||
|
||||
init(transitionManager: SearchOnMapModalTransitionManager, isRouting: Bool, didChangeState: ((SearchOnMapState) -> Void)?) {
|
||||
self.transitionManager = transitionManager
|
||||
init(isRouting: Bool, didChangeState: ((SearchOnMapState) -> Void)?) {
|
||||
self.isRouting = isRouting
|
||||
self.didChangeState = didChangeState
|
||||
didChangeState?(searchState)
|
||||
|
@ -28,8 +25,8 @@ final class SearchOnMapPresenter {
|
|||
guard response != .none else { return }
|
||||
|
||||
if response == .close {
|
||||
view?.close()
|
||||
searchState = .closed
|
||||
presentationView?.close()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -43,7 +40,6 @@ final class SearchOnMapPresenter {
|
|||
viewModel = newViewModel
|
||||
view?.render(newViewModel)
|
||||
searchState = newViewModel.presentationStep.searchState
|
||||
presentationView?.setPresentationStep(newViewModel.presentationStep)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,6 +93,9 @@ final class SearchOnMapPresenter {
|
|||
viewModel.isTyping = false
|
||||
viewModel.presentationStep = .compact
|
||||
case .updatePresentationStep(let step):
|
||||
if step == .hidden {
|
||||
viewModel.isTyping = false
|
||||
}
|
||||
viewModel.presentationStep = step
|
||||
case .close, .none:
|
||||
break
|
||||
|
@ -105,7 +104,7 @@ final class SearchOnMapPresenter {
|
|||
}
|
||||
}
|
||||
|
||||
private extension ModalScreenPresentationStep {
|
||||
private extension ModalPresentationStep {
|
||||
var searchState: SearchOnMapState {
|
||||
switch self {
|
||||
case .fullScreen, .halfScreen, .compact:
|
||||
|
|
|
@ -1,52 +1,73 @@
|
|||
protocol SearchOnMapView: AnyObject {
|
||||
var scrollViewDelegate: SearchOnMapScrollViewDelegate? { get set }
|
||||
|
||||
func render(_ viewModel: SearchOnMap.ViewModel)
|
||||
func show()
|
||||
func close()
|
||||
}
|
||||
|
||||
@objc
|
||||
protocol SearchOnMapScrollViewDelegate: AnyObject {
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView)
|
||||
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)
|
||||
}
|
||||
|
||||
@objc
|
||||
protocol ModallyPresentedViewController: AnyObject {
|
||||
@objc func presentationFrameDidChange(_ frame: CGRect)
|
||||
}
|
||||
|
||||
final class SearchOnMapViewController: UIViewController {
|
||||
typealias ViewModel = SearchOnMap.ViewModel
|
||||
typealias ContentState = SearchOnMap.ViewModel.ContentState
|
||||
typealias Content = SearchOnMap.ViewModel.Content
|
||||
typealias SearchText = SearchOnMap.SearchText
|
||||
|
||||
fileprivate enum Constants {
|
||||
static let categoriesHeight: CGFloat = 100
|
||||
static let filtersHeight: CGFloat = 50
|
||||
static let keyboardAnimationDuration: CGFloat = 0.3
|
||||
static let cancelButtonInsets: UIEdgeInsets = UIEdgeInsets(top: 0, left: 6, bottom: 0, right: 8)
|
||||
static let estimatedRowHeight: CGFloat = 80
|
||||
static let panGestureThreshold: CGFloat = 5
|
||||
static let dimAlpha: CGFloat = 0.3
|
||||
static let dimViewThreshold: CGFloat = 50
|
||||
}
|
||||
|
||||
let interactor: SearchOnMapInteractor
|
||||
weak var scrollViewDelegate: SearchOnMapScrollViewDelegate?
|
||||
var interactor: SearchOnMapInteractor?
|
||||
|
||||
private var searchResults = SearchOnMap.SearchResults([])
|
||||
|
||||
// MARK: - UI Elements
|
||||
@objc let availableAreaView = SearchOnMapAreaView()
|
||||
private let contentView = UIView()
|
||||
private let headerView = SearchOnMapHeaderView()
|
||||
private let containerView = UIView()
|
||||
private let searchResultsView = UIView()
|
||||
private let resultsTableView = UITableView()
|
||||
private let historyAndCategoryTabViewController = SearchTabViewController()
|
||||
// TODO: implement filters
|
||||
private let filtersCollectionView: UICollectionView = {
|
||||
let layout = UICollectionViewFlowLayout()
|
||||
layout.scrollDirection = .horizontal
|
||||
return UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||||
}()
|
||||
private var searchingActivityView = PlaceholderView(hasActivityIndicator: true)
|
||||
private var containerModalYTranslation: CGFloat = 0
|
||||
private var searchNoResultsView = PlaceholderView(title: L("search_not_found"),
|
||||
subtitle: L("search_not_found_query"))
|
||||
private var dimView: UIView?
|
||||
|
||||
private var internalScrollViewContentOffset: CGFloat = .zero
|
||||
private let presentationStepsController = ModalPresentationStepsController()
|
||||
private var searchResults = SearchOnMap.SearchResults([])
|
||||
|
||||
// MARK: - Init
|
||||
init(interactor: SearchOnMapInteractor) {
|
||||
self.interactor = interactor
|
||||
init() {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
configureModalPresentation()
|
||||
}
|
||||
|
||||
private func configureModalPresentation() {
|
||||
guard let mapViewController = MapViewController.shared() else {
|
||||
fatalError("MapViewController is not available")
|
||||
}
|
||||
presentationStepsController.set(presentedView: availableAreaView, containerViewController: self)
|
||||
presentationStepsController.didUpdateHandler = presentationUpdateHandler
|
||||
|
||||
mapViewController.searchContainer.addSubview(view)
|
||||
mapViewController.addChild(self)
|
||||
view.frame = mapViewController.searchContainer.bounds
|
||||
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
didMove(toParent: mapViewController)
|
||||
|
||||
let affectedAreaViews = [
|
||||
mapViewController.sideButtonsArea,
|
||||
mapViewController.trafficButtonArea,
|
||||
]
|
||||
affectedAreaViews.forEach { $0?.addAffectingView(availableAreaView) }
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
|
@ -54,16 +75,16 @@ final class SearchOnMapViewController: UIViewController {
|
|||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
// MARK: - Lifecycle
|
||||
override func loadView() {
|
||||
view = TouchTransparentView()
|
||||
}
|
||||
|
||||
// MARK: - Lifecycle
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setupViews()
|
||||
layoutViews()
|
||||
interactor.handle(.openSearch)
|
||||
presentationStepsController.setInitialState()
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
|
@ -71,30 +92,58 @@ final class SearchOnMapViewController: UIViewController {
|
|||
headerView.setIsSearching(false)
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
private func setupViews() {
|
||||
view.setStyle(.clearBackground)
|
||||
setupTapGestureRecognizer()
|
||||
setupHeaderView()
|
||||
setupContainerView()
|
||||
setupResultsTableView()
|
||||
setupHistoryAndCategoryTabView()
|
||||
setupResultsTableView()
|
||||
setupFiltersCollectionView()
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
super.traitCollectionDidChange(previousTraitCollection)
|
||||
updateFrameOfPresentedViewInContainerView()
|
||||
updateDimView(for: availableAreaView.frame)
|
||||
}
|
||||
|
||||
private func setupTapGestureRecognizer() {
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: any UIViewControllerTransitionCoordinator) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
if #available(iOS 14.0, *), ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
updateFrameOfPresentedViewInContainerView()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
private func setupViews() {
|
||||
availableAreaView.setStyleAndApply(.modalSheetBackground)
|
||||
contentView.setStyleAndApply(.modalSheetContent)
|
||||
|
||||
setupGestureRecognizers()
|
||||
setupDimView()
|
||||
setupHeaderView()
|
||||
setupSearchResultsView()
|
||||
setupResultsTableView()
|
||||
setupHistoryAndCategoryTabView()
|
||||
}
|
||||
|
||||
private func setupDimView() {
|
||||
iPhoneSpecific {
|
||||
dimView = UIView()
|
||||
dimView?.backgroundColor = .black
|
||||
dimView?.frame = view.bounds
|
||||
}
|
||||
}
|
||||
|
||||
private func setupGestureRecognizers() {
|
||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapOutside))
|
||||
tapGesture.cancelsTouchesInView = false
|
||||
view.addGestureRecognizer(tapGesture)
|
||||
contentView.addGestureRecognizer(tapGesture)
|
||||
|
||||
iPhoneSpecific {
|
||||
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
|
||||
panGestureRecognizer.delegate = self
|
||||
contentView.addGestureRecognizer(panGestureRecognizer)
|
||||
}
|
||||
}
|
||||
|
||||
private func setupHeaderView() {
|
||||
headerView.delegate = self
|
||||
}
|
||||
|
||||
private func setupContainerView() {
|
||||
containerView.setStyle(.background)
|
||||
private func setupSearchResultsView() {
|
||||
searchResultsView.setStyle(.background)
|
||||
}
|
||||
|
||||
private func setupResultsTableView() {
|
||||
|
@ -112,91 +161,141 @@ final class SearchOnMapViewController: UIViewController {
|
|||
historyAndCategoryTabViewController.delegate = self
|
||||
}
|
||||
|
||||
// TODO: (KK) Implement filters collection viewe
|
||||
private func setupFiltersCollectionView() {
|
||||
filtersCollectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "FilterCell")
|
||||
filtersCollectionView.dataSource = self
|
||||
}
|
||||
|
||||
private func layoutViews() {
|
||||
view.addSubview(headerView)
|
||||
view.addSubview(containerView)
|
||||
if let dimView {
|
||||
view.addSubview(dimView)
|
||||
dimView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
}
|
||||
view.addSubview(availableAreaView)
|
||||
availableAreaView.addSubview(contentView)
|
||||
contentView.addSubview(headerView)
|
||||
contentView.addSubview(searchResultsView)
|
||||
|
||||
contentView.translatesAutoresizingMaskIntoConstraints = false
|
||||
headerView.translatesAutoresizingMaskIntoConstraints = false
|
||||
containerView.translatesAutoresizingMaskIntoConstraints = false
|
||||
searchResultsView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
headerView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
headerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
headerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
contentView.topAnchor.constraint(equalTo: availableAreaView.topAnchor),
|
||||
contentView.leadingAnchor.constraint(equalTo: availableAreaView.leadingAnchor),
|
||||
contentView.trailingAnchor.constraint(equalTo: availableAreaView.trailingAnchor),
|
||||
contentView.bottomAnchor.constraint(equalTo: availableAreaView.bottomAnchor),
|
||||
|
||||
containerView.topAnchor.constraint(equalTo: headerView.bottomAnchor),
|
||||
containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
containerView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
headerView.topAnchor.constraint(equalTo: contentView.topAnchor),
|
||||
headerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
|
||||
headerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
||||
|
||||
searchResultsView.topAnchor.constraint(equalTo: headerView.bottomAnchor),
|
||||
searchResultsView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
|
||||
searchResultsView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
||||
searchResultsView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
|
||||
])
|
||||
|
||||
layoutResultsView()
|
||||
layoutHistoryAndCategoryTabView()
|
||||
layoutSearchNoResultsView()
|
||||
layoutSearchingView()
|
||||
updateFrameOfPresentedViewInContainerView()
|
||||
}
|
||||
|
||||
private func layoutResultsView() {
|
||||
containerView.addSubview(resultsTableView)
|
||||
searchResultsView.addSubview(resultsTableView)
|
||||
resultsTableView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
resultsTableView.topAnchor.constraint(equalTo: containerView.topAnchor),
|
||||
resultsTableView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
||||
resultsTableView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
|
||||
resultsTableView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
|
||||
resultsTableView.topAnchor.constraint(equalTo: searchResultsView.topAnchor),
|
||||
resultsTableView.leadingAnchor.constraint(equalTo: searchResultsView.leadingAnchor),
|
||||
resultsTableView.trailingAnchor.constraint(equalTo: searchResultsView.trailingAnchor),
|
||||
resultsTableView.bottomAnchor.constraint(equalTo: searchResultsView.bottomAnchor)
|
||||
])
|
||||
}
|
||||
|
||||
private func layoutHistoryAndCategoryTabView() {
|
||||
containerView.addSubview(historyAndCategoryTabViewController.view)
|
||||
searchResultsView.addSubview(historyAndCategoryTabViewController.view)
|
||||
historyAndCategoryTabViewController.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
historyAndCategoryTabViewController.view.topAnchor.constraint(equalTo: containerView.topAnchor),
|
||||
historyAndCategoryTabViewController.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
||||
historyAndCategoryTabViewController.view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
|
||||
historyAndCategoryTabViewController.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
|
||||
historyAndCategoryTabViewController.view.topAnchor.constraint(equalTo: searchResultsView.topAnchor),
|
||||
historyAndCategoryTabViewController.view.leadingAnchor.constraint(equalTo: searchResultsView.leadingAnchor),
|
||||
historyAndCategoryTabViewController.view.trailingAnchor.constraint(equalTo: searchResultsView.trailingAnchor),
|
||||
historyAndCategoryTabViewController.view.bottomAnchor.constraint(equalTo: searchResultsView.bottomAnchor)
|
||||
])
|
||||
}
|
||||
|
||||
private func layoutSearchNoResultsView() {
|
||||
searchNoResultsView.translatesAutoresizingMaskIntoConstraints = false
|
||||
containerView.addSubview(searchNoResultsView)
|
||||
searchResultsView.addSubview(searchNoResultsView)
|
||||
NSLayoutConstraint.activate([
|
||||
searchNoResultsView.topAnchor.constraint(equalTo: containerView.topAnchor),
|
||||
searchNoResultsView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
||||
searchNoResultsView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
|
||||
searchNoResultsView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
|
||||
searchNoResultsView.topAnchor.constraint(equalTo: searchResultsView.topAnchor),
|
||||
searchNoResultsView.leadingAnchor.constraint(equalTo: searchResultsView.leadingAnchor),
|
||||
searchNoResultsView.trailingAnchor.constraint(equalTo: searchResultsView.trailingAnchor),
|
||||
searchNoResultsView.bottomAnchor.constraint(equalTo: searchResultsView.bottomAnchor)
|
||||
])
|
||||
}
|
||||
|
||||
private func layoutSearchingView() {
|
||||
containerView.insertSubview(searchingActivityView, at: 0)
|
||||
searchResultsView.insertSubview(searchingActivityView, at: 0)
|
||||
searchingActivityView.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
searchingActivityView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
||||
searchingActivityView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
|
||||
searchingActivityView.topAnchor.constraint(equalTo: containerView.topAnchor),
|
||||
searchingActivityView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
|
||||
searchingActivityView.leadingAnchor.constraint(equalTo: searchResultsView.leadingAnchor),
|
||||
searchingActivityView.trailingAnchor.constraint(equalTo: searchResultsView.trailingAnchor),
|
||||
searchingActivityView.topAnchor.constraint(equalTo: searchResultsView.topAnchor),
|
||||
searchingActivityView.bottomAnchor.constraint(equalTo: searchResultsView.bottomAnchor)
|
||||
])
|
||||
}
|
||||
|
||||
// MARK: - Handle Button Actions
|
||||
@objc private func handleTapOutside(_ gesture: UITapGestureRecognizer) {
|
||||
// MARK: - Handle Presentation Steps
|
||||
private func updateFrameOfPresentedViewInContainerView() {
|
||||
presentationStepsController.updateMaxAvailableFrame()
|
||||
availableAreaView.frame = presentationStepsController.currentFrame
|
||||
view.layoutIfNeeded()
|
||||
}
|
||||
|
||||
@objc
|
||||
private func handleTapOutside(_ gesture: UITapGestureRecognizer) {
|
||||
let location = gesture.location(in: view)
|
||||
if resultsTableView.frame.contains(location) && searchResults.isEmpty {
|
||||
headerView.setIsSearching(false)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Handle State Updates
|
||||
private func setContent(_ content: ContentState) {
|
||||
@objc
|
||||
private func handlePan(_ gesture: UIPanGestureRecognizer) {
|
||||
interactor?.handle(.didStartDraggingSearch)
|
||||
presentationStepsController.handlePan(gesture)
|
||||
}
|
||||
|
||||
private var presentationUpdateHandler: (ModalPresentationStepsController.StepUpdate) -> Void {
|
||||
{ [weak self] update in
|
||||
guard let self else { return }
|
||||
switch update {
|
||||
case .didClose:
|
||||
self.interactor?.handle(.closeSearch)
|
||||
case .didUpdateFrame(let frame):
|
||||
self.presentationFrameDidChange(frame)
|
||||
self.updateDimView(for: frame)
|
||||
case .didUpdateStep(let step):
|
||||
self.interactor?.handle(.didUpdatePresentationStep(step))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateDimView(for frame: CGRect) {
|
||||
guard let dimView else { return }
|
||||
let currentTop = frame.origin.y
|
||||
let maxTop = presentationStepsController.maxAvailableFrame.origin.y
|
||||
let alpha = (1 - (currentTop - maxTop) / Constants.dimViewThreshold) * Constants.dimAlpha
|
||||
let isCloseToTop = currentTop - maxTop < Constants.dimViewThreshold
|
||||
let isPortrait = UIApplication.shared.statusBarOrientation.isPortrait
|
||||
let shouldDim = isCloseToTop && isPortrait
|
||||
UIView.animate(withDuration: kDefaultAnimationDuration / 2) {
|
||||
dimView.alpha = shouldDim ? alpha : 0
|
||||
dimView.isHidden = !shouldDim
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Handle Content Updates
|
||||
private func setContent(_ content: Content) {
|
||||
switch content {
|
||||
case .historyAndCategory:
|
||||
historyAndCategoryTabViewController.reloadSearchHistory()
|
||||
|
@ -214,7 +313,7 @@ final class SearchOnMapViewController: UIViewController {
|
|||
showView(viewToShow(for: content))
|
||||
}
|
||||
|
||||
private func viewToShow(for content: ContentState) -> UIView {
|
||||
private func viewToShow(for content: Content) -> UIView {
|
||||
switch content {
|
||||
case .historyAndCategory:
|
||||
return historyAndCategoryTabViewController.view
|
||||
|
@ -232,24 +331,26 @@ final class SearchOnMapViewController: UIViewController {
|
|||
historyAndCategoryTabViewController.view,
|
||||
searchNoResultsView,
|
||||
searchingActivityView].filter { $0 != view }
|
||||
UIView.transition(with: containerView,
|
||||
duration: kDefaultAnimationDuration / 2,
|
||||
options: [.transitionCrossDissolve, .curveEaseInOut], animations: {
|
||||
viewsToHide.forEach { viewToHide in
|
||||
view.isHidden = false
|
||||
view.alpha = 1
|
||||
viewToHide.isHidden = true
|
||||
viewToHide.alpha = 0
|
||||
}
|
||||
})
|
||||
UIView.animate(withDuration: kDefaultAnimationDuration / 2,
|
||||
delay: 0,
|
||||
options: .curveEaseInOut,
|
||||
animations: {
|
||||
viewsToHide.forEach { $0.alpha = 0 }
|
||||
view.alpha = 1
|
||||
}) { _ in
|
||||
viewsToHide.forEach { $0.isHidden = true }
|
||||
view.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
private func setIsSearching(_ isSearching: Bool) {
|
||||
headerView.setIsSearching(isSearching)
|
||||
}
|
||||
|
||||
private func replaceSearchText(with text: String) {
|
||||
headerView.setSearchText(text)
|
||||
private func setSearchText(_ text: String?) {
|
||||
if let text {
|
||||
headerView.setSearchText(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,20 +359,33 @@ extension SearchOnMapViewController: SearchOnMapView {
|
|||
func render(_ viewModel: ViewModel) {
|
||||
setContent(viewModel.contentState)
|
||||
setIsSearching(viewModel.isTyping)
|
||||
if let searchingText = viewModel.searchingText {
|
||||
replaceSearchText(with: searchingText)
|
||||
setSearchText(viewModel.searchingText)
|
||||
presentationStepsController.setStep(viewModel.presentationStep)
|
||||
}
|
||||
|
||||
func show() {
|
||||
interactor?.handle(.openSearch)
|
||||
}
|
||||
|
||||
func close() {
|
||||
headerView.setIsSearching(false)
|
||||
updateDimView(for: presentationStepsController.hiddenFrame)
|
||||
willMove(toParent: nil)
|
||||
presentationStepsController.close { [weak self] in
|
||||
self?.view.removeFromSuperview()
|
||||
self?.removeFromParent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ModallyPresentedViewController
|
||||
extension SearchOnMapViewController: ModallyPresentedViewController {
|
||||
func translationYDidUpdate(_ translationY: CGFloat) {
|
||||
self.containerModalYTranslation = translationY
|
||||
func presentationFrameDidChange(_ frame: CGRect) {
|
||||
let translationY = frame.origin.y
|
||||
resultsTableView.contentInset.bottom = translationY
|
||||
historyAndCategoryTabViewController.translationYDidUpdate(translationY)
|
||||
searchNoResultsView.translationYDidUpdate(translationY)
|
||||
searchingActivityView.translationYDidUpdate(translationY)
|
||||
historyAndCategoryTabViewController.presentationFrameDidChange(frame)
|
||||
searchNoResultsView.presentationFrameDidChange(frame)
|
||||
searchingActivityView.presentationFrameDidChange(frame)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -303,60 +417,82 @@ extension SearchOnMapViewController: UITableViewDataSource {
|
|||
extension SearchOnMapViewController: UITableViewDelegate {
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let result = searchResults[indexPath.row]
|
||||
interactor.handle(.didSelectResult(result, withSearchText: headerView.searchText))
|
||||
interactor?.handle(.didSelectResult(result, withSearchText: headerView.searchText))
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
}
|
||||
|
||||
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||
interactor.handle(.didStartDraggingSearch)
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
scrollViewDelegate?.scrollViewDidScroll(scrollView)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UICollectionViewDataSource
|
||||
extension SearchOnMapViewController: UICollectionViewDataSource {
|
||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
// TODO: remove search from here
|
||||
Int(Search.resultsCount())
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FilterCell", for: indexPath)
|
||||
return cell
|
||||
interactor?.handle(.didStartDraggingSearch)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SearchOnMapHeaderViewDelegate
|
||||
extension SearchOnMapViewController: SearchOnMapHeaderViewDelegate {
|
||||
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
|
||||
interactor.handle(.didStartTyping)
|
||||
interactor?.handle(.didStartTyping)
|
||||
}
|
||||
|
||||
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
|
||||
guard !searchText.isEmpty else {
|
||||
interactor.handle(.clearButtonDidTap)
|
||||
interactor?.handle(.clearButtonDidTap)
|
||||
return
|
||||
}
|
||||
interactor.handle(.didType(SearchText(searchText, locale: searchBar.textInputMode?.primaryLanguage)))
|
||||
interactor?.handle(.didType(SearchText(searchText, locale: searchBar.textInputMode?.primaryLanguage)))
|
||||
}
|
||||
|
||||
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
|
||||
guard let searchText = searchBar.text, !searchText.isEmpty else { return }
|
||||
interactor.handle(.searchButtonDidTap(SearchText(searchText, locale: searchBar.textInputMode?.primaryLanguage)))
|
||||
interactor?.handle(.searchButtonDidTap(SearchText(searchText, locale: searchBar.textInputMode?.primaryLanguage)))
|
||||
}
|
||||
|
||||
func cancelButtonDidTap() {
|
||||
interactor.handle(.closeSearch)
|
||||
interactor?.handle(.closeSearch)
|
||||
}
|
||||
|
||||
func grabberDidTap() {
|
||||
interactor?.handle(.didUpdatePresentationStep(.fullScreen))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SearchTabViewControllerDelegate
|
||||
extension SearchOnMapViewController: SearchTabViewControllerDelegate {
|
||||
func searchTabController(_ viewController: SearchTabViewController, didSearch text: String, withCategory: Bool) {
|
||||
interactor.handle(.didSelectText(SearchText(text, locale: nil), isCategory: withCategory))
|
||||
interactor?.handle(.didSelectText(SearchText(text, locale: nil), isCategory: withCategory))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIGestureRecognizerDelegate
|
||||
extension SearchOnMapViewController: UIGestureRecognizerDelegate {
|
||||
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
true
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UIPanGestureRecognizer {
|
||||
// threshold is used to soften transition from the internal scroll zero content offset
|
||||
return internalScrollViewContentOffset < Constants.panGestureThreshold
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SearchOnMapScrollViewDelegate
|
||||
extension SearchOnMapViewController: SearchOnMapScrollViewDelegate {
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
let hasReachedTheTop = Int(availableAreaView.frame.origin.y) > Int(presentationStepsController.maxAvailableFrame.origin.y)
|
||||
let hasZeroContentOffset = internalScrollViewContentOffset == 0
|
||||
if hasReachedTheTop && hasZeroContentOffset {
|
||||
// prevent the internal scroll view scrolling
|
||||
scrollView.contentOffset.y = internalScrollViewContentOffset
|
||||
return
|
||||
}
|
||||
internalScrollViewContentOffset = scrollView.contentOffset.y
|
||||
}
|
||||
|
||||
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
|
||||
// lock internal scroll view when the user fast scrolls screen to the top
|
||||
if internalScrollViewContentOffset == 0 {
|
||||
targetContentOffset.pointee = .zero
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,10 @@ final class SearchCategoriesViewController: MWMTableViewController {
|
|||
delegate?.scrollViewDidScroll(scrollView)
|
||||
}
|
||||
|
||||
override func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
|
||||
delegate?.scrollViewWillEndDragging(scrollView, withVelocity: velocity, targetContentOffset: targetContentOffset)
|
||||
}
|
||||
|
||||
func category(at indexPath: IndexPath) -> String {
|
||||
let index = indexPath.row
|
||||
return categories[index]
|
||||
|
@ -52,8 +56,8 @@ final class SearchCategoriesViewController: MWMTableViewController {
|
|||
}
|
||||
|
||||
extension SearchCategoriesViewController: ModallyPresentedViewController {
|
||||
func translationYDidUpdate(_ translationY: CGFloat) {
|
||||
func presentationFrameDidChange(_ frame: CGRect) {
|
||||
guard isViewLoaded else { return }
|
||||
tableView.contentInset.bottom = translationY + view.safeAreaInsets.bottom
|
||||
tableView.contentInset.bottom = frame.origin.y + view.safeAreaInsets.bottom
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,9 +122,9 @@ extension SearchHistoryViewController: UITableViewDelegate {
|
|||
}
|
||||
|
||||
extension SearchHistoryViewController: ModallyPresentedViewController {
|
||||
func translationYDidUpdate(_ translationY: CGFloat) {
|
||||
func presentationFrameDidChange(_ frame: CGRect) {
|
||||
guard isViewLoaded else { return }
|
||||
tableView.contentInset.bottom = translationY
|
||||
emptyHistoryView.translationYDidUpdate(translationY)
|
||||
tableView.contentInset.bottom = frame.origin.y
|
||||
emptyHistoryView.presentationFrameDidChange(frame)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,8 +54,8 @@ final class SearchTabViewController: TabViewController {
|
|||
}
|
||||
|
||||
extension SearchTabViewController: ModallyPresentedViewController {
|
||||
func translationYDidUpdate(_ translationY: CGFloat) {
|
||||
viewControllers.forEach { ($0 as? ModallyPresentedViewController)?.translationYDidUpdate(translationY) }
|
||||
func presentationFrameDidChange(_ frame: CGRect) {
|
||||
viewControllers.forEach { ($0 as? ModallyPresentedViewController)?.presentationFrameDidChange(frame) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,10 @@ extension SearchTabViewController: SearchOnMapScrollViewDelegate {
|
|||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
delegate?.scrollViewDidScroll(scrollView)
|
||||
}
|
||||
|
||||
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
|
||||
delegate?.scrollViewWillEndDragging(scrollView, withVelocity: velocity, targetContentOffset: targetContentOffset)
|
||||
}
|
||||
}
|
||||
|
||||
extension SearchTabViewController: SearchCategoriesViewControllerDelegate {
|
||||
|
|
|
@ -194,11 +194,15 @@
|
|||
<outlet property="carplayPlaceholderView" destination="ixC-IZ-Pvs" id="3rZ-Kn-VBS"/>
|
||||
<outlet property="controlsView" destination="rL1-9E-4b7" id="sfV-7X-WlR"/>
|
||||
<outlet property="mapView" destination="aPn-pa-nCx" id="tCi-LW-1ll"/>
|
||||
<outlet property="placePageArea" destination="awj-9E-eBS" id="nDP-as-zc2"/>
|
||||
<outlet property="placePageAreaKeyboard" destination="PFs-sL-oVA" id="O3P-ia-ZlX"/>
|
||||
<outlet property="sideButtonsArea" destination="xJx-UU-IdV" id="Qug-gg-Za8"/>
|
||||
<outlet property="sideButtonsAreaBottom" destination="VfU-Zk-8IU" id="MvP-Ki-4wP"/>
|
||||
<outlet property="sideButtonsAreaKeyboard" destination="SDX-4J-Jz5" id="kv9-zX-hbD"/>
|
||||
<outlet property="trafficButtonArea" destination="QKu-4A-UgP" id="uJI-rT-zGt"/>
|
||||
<outlet property="visibleAreaBottom" destination="OE7-Qb-J0v" id="isp-aT-LtA"/>
|
||||
<outlet property="visibleAreaKeyboard" destination="YUs-MJ-9w8" id="UJP-KT-2uK"/>
|
||||
<outlet property="widgetsArea" destination="NI8-tV-i2B" id="xU3-51-vHe"/>
|
||||
<segue destination="Lfa-Zp-orR" kind="custom" identifier="Map2EditorSegue" customClass="MWMSegue" id="OEF-kR-jKi"/>
|
||||
<segue destination="QlF-CJ-cEG" kind="custom" identifier="MapToCategorySelectorSegue" customClass="MWMSegue" id="4Cc-99-mlN"/>
|
||||
<segue destination="5Wc-fy-NOW" kind="custom" identifier="Map2OsmLogin" customClass="MWMSegue" id="7YC-t5-0WN"/>
|
||||
|
|
Loading…
Add table
Reference in a new issue