From 1cbe25e5bc6ca9dd0662b029d45c6ff2b15f5fe4 Mon Sep 17 00:00:00 2001 From: Kiryl Kaveryn <kirylkaveryn@gmail.com> Date: Thu, 13 Feb 2025 17:18:18 +0400 Subject: [PATCH 01/12] [ios] fix route planning reloading ui bug The bottom route status view sometimes jumps from the top to the bottom during the 1st rendering. It happens because the view's frame is not calculated on the isVisible because the view is not init/deinit multiple times during the route building state changing. The bottom view now only changes its visibility and constraints, not the init/deinit on every update. Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com> --- .../NavigationDashboard/MWMNavigationDashboardManager.mm | 8 +++----- .../RoutePreviewStatus/BaseRoutePreviewStatus.swift | 9 +++------ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/iphone/Maps/Classes/CustomViews/NavigationDashboard/MWMNavigationDashboardManager.mm b/iphone/Maps/Classes/CustomViews/NavigationDashboard/MWMNavigationDashboardManager.mm index 42551ba1dc..43c06fb061 100644 --- a/iphone/Maps/Classes/CustomViews/NavigationDashboard/MWMNavigationDashboardManager.mm +++ b/iphone/Maps/Classes/CustomViews/NavigationDashboard/MWMNavigationDashboardManager.mm @@ -134,8 +134,7 @@ NSString *const kNavigationControlViewXibName = @"NavigationControlView"; self.navigationInfoView = nil; _navigationControlView.isVisible = NO; _navigationControlView = nil; - [_baseRoutePreviewStatus hide]; - _baseRoutePreviewStatus = nil; + [self.baseRoutePreviewStatus hide]; [_transportRoutePreviewStatus hide]; _transportRoutePreviewStatus = nil; } @@ -147,7 +146,7 @@ NSString *const kNavigationControlViewXibName = @"NavigationControlView"; [routePreview statePrepare]; [routePreview selectRouter:[MWMRouter type]]; [self updateGoButtonTitle]; - [_baseRoutePreviewStatus hide]; + [self.baseRoutePreviewStatus hide]; [_transportRoutePreviewStatus hide]; for (MWMRouteStartButton *button in self.goButtons) [button statePrepare]; @@ -204,8 +203,7 @@ NSString *const kNavigationControlViewXibName = @"NavigationControlView"; self.routePreview = nil; self.navigationInfoView.state = MWMNavigationInfoViewStateNavigation; self.navigationControlView.isVisible = YES; - [_baseRoutePreviewStatus hide]; - _baseRoutePreviewStatus = nil; + [self.baseRoutePreviewStatus hide]; [_transportRoutePreviewStatus hide]; _transportRoutePreviewStatus = nil; [self onNavigationInfoUpdated]; diff --git a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/RoutePreviewStatus/BaseRoutePreviewStatus.swift b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/RoutePreviewStatus/BaseRoutePreviewStatus.swift index 73cd7a798f..d01a7897c1 100644 --- a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/RoutePreviewStatus/BaseRoutePreviewStatus.swift +++ b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/RoutePreview/RoutePreviewStatus/BaseRoutePreviewStatus.swift @@ -51,12 +51,8 @@ final class BaseRoutePreviewStatus: SolidTouchView { private var isVisible = false { didSet { - guard isVisible != oldValue else { return } - if isVisible { - addView() - } else { - self.removeFromSuperview() - } + addView() + isHidden = !isVisible } } @@ -68,6 +64,7 @@ final class BaseRoutePreviewStatus: SolidTouchView { leadingAnchor.constraint(equalTo: lg.leadingAnchor).isActive = true trailingAnchor.constraint(equalTo: lg.trailingAnchor).isActive = true bottomAnchor.constraint(equalTo: lg.bottomAnchor).isActive = true + ownerView.layoutIfNeeded() } private func updateHeight() { -- 2.45.3 From cd832b94b6604801e04cef05ecae2983ded31b04 Mon Sep 17 00:00:00 2001 From: Alexey Krasilnikov <alexey@krasilnikov.me> Date: Tue, 18 Feb 2025 17:32:31 +0300 Subject: [PATCH 02/12] Add foreground service to indicate about ongoing downloads Signed-off-by: Alexey Krasilnikov <alexey@krasilnikov.me> --- android/app/src/main/AndroidManifest.xml | 8 + .../DownloadResourcesLegacyActivity.java | 2 +- .../java/app/organicmaps/MwmApplication.java | 45 ++---- .../screens/download/DownloaderScreen.java | 2 +- .../downloader/DownloaderAdapter.java | 4 +- .../downloader/DownloaderNotifier.java | 90 ++++++++--- .../downloader/DownloaderService.java | 142 ++++++++++++++++++ .../ExpandRetryConfirmationListener.java | 10 +- .../organicmaps/downloader/MapManager.java | 39 ++++- .../downloader/OnmapDownloader.java | 7 +- ...tryFailedDownloadConfirmationListener.java | 22 --- .../routing/RoutingMapsDownloadFragment.java | 3 +- 12 files changed, 274 insertions(+), 100 deletions(-) create mode 100644 android/app/src/main/java/app/organicmaps/downloader/DownloaderService.java delete mode 100644 android/app/src/main/java/app/organicmaps/downloader/RetryFailedDownloadConfirmationListener.java diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 42bf79fe7b..86bdadc494 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -37,6 +37,7 @@ <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/> <!-- Android 13 (API level 33) and higher supports a runtime permission for sending non-exempt (including Foreground @@ -489,6 +490,13 @@ android:stopWithTask="false" /> + <service + android:name=".downloader.DownloaderService" + android:foregroundServiceType="dataSync" + android:exported="false" + android:enabled="true" + android:stopWithTask="false"/> + <provider android:name="androidx.core.content.FileProvider" android:authorities="${FILE_PROVIDER_PLACEHOLDER}" diff --git a/android/app/src/main/java/app/organicmaps/DownloadResourcesLegacyActivity.java b/android/app/src/main/java/app/organicmaps/DownloadResourcesLegacyActivity.java index 3be0c88c4c..2eb49fc6d3 100644 --- a/android/app/src/main/java/app/organicmaps/DownloadResourcesLegacyActivity.java +++ b/android/app/src/main/java/app/organicmaps/DownloadResourcesLegacyActivity.java @@ -390,7 +390,7 @@ public class DownloadResourcesLegacyActivity extends BaseMwmFragmentActivity mProgress.setProgressCompat(0, true); mCountryDownloadListenerSlot = MapManager.nativeSubscribe(mCountryDownloadListener); - MapManager.nativeDownload(mCurrentCountry); + MapManager.startDownload(mCurrentCountry); setAction(PROCEED_TO_MAP); } else diff --git a/android/app/src/main/java/app/organicmaps/MwmApplication.java b/android/app/src/main/java/app/organicmaps/MwmApplication.java index 725a0bca2d..7170508325 100644 --- a/android/app/src/main/java/app/organicmaps/MwmApplication.java +++ b/android/app/src/main/java/app/organicmaps/MwmApplication.java @@ -16,13 +16,14 @@ import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.ProcessLifecycleOwner; +import java.io.IOException; +import java.lang.ref.WeakReference; + import app.organicmaps.background.OsmUploadWork; -import app.organicmaps.downloader.Android7RootCertificateWorkaround; -import app.organicmaps.downloader.DownloaderNotifier; import app.organicmaps.bookmarks.data.BookmarkManager; import app.organicmaps.display.DisplayManager; -import app.organicmaps.downloader.CountryItem; -import app.organicmaps.downloader.MapManager; +import app.organicmaps.downloader.Android7RootCertificateWorkaround; +import app.organicmaps.downloader.DownloaderNotifier; import app.organicmaps.location.LocationHelper; import app.organicmaps.location.LocationState; import app.organicmaps.location.SensorHelper; @@ -46,10 +47,6 @@ import app.organicmaps.util.Utils; import app.organicmaps.util.log.Logger; import app.organicmaps.util.log.LogsManager; -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.util.List; - public class MwmApplication extends Application implements Application.ActivityLifecycleCallbacks { @NonNull @@ -78,9 +75,6 @@ public class MwmApplication extends Application implements Application.ActivityL private volatile boolean mFrameworkInitialized; private volatile boolean mPlatformInitialized; - @NonNull - private final MapManager.StorageCallback mStorageCallbacks = new StorageCallbackImpl(); - @Nullable private WeakReference<Activity> mTopActivity; @@ -127,6 +121,9 @@ public class MwmApplication extends Application implements Application.ActivityL return (MwmApplication) context.getApplicationContext(); } + @NonNull + public static MwmApplication sInstance; + @NonNull public static SharedPreferences prefs(@NonNull Context context) { @@ -138,6 +135,9 @@ public class MwmApplication extends Application implements Application.ActivityL { super.onCreate(); Logger.i(TAG, "Initializing application"); + + sInstance = this; + LogsManager.INSTANCE.initFileLogging(this); Android7RootCertificateWorkaround.initializeIfNeeded(this); @@ -230,8 +230,6 @@ public class MwmApplication extends Application implements Application.ActivityL nativeInitFramework(onComplete); - MapManager.nativeSubscribe(mStorageCallbacks); - initNativeStrings(); ThemeSwitcher.INSTANCE.initialize(this); SearchEngine.INSTANCE.initialize(); @@ -363,25 +361,4 @@ public class MwmApplication extends Application implements Application.ActivityL mLocationHelper.stop(); } } - - private class StorageCallbackImpl implements MapManager.StorageCallback - { - @Override - public void onStatusChanged(List<MapManager.StorageCallbackData> data) - { - for (MapManager.StorageCallbackData item : data) - if (item.isLeafNode && item.newStatus == CountryItem.STATUS_FAILED) - { - if (MapManager.nativeIsAutoretryFailed()) - { - DownloaderNotifier.notifyDownloadFailed(MwmApplication.this, item.countryId); - } - - return; - } - } - - @Override - public void onProgress(String countryId, long localSize, long remoteSize) {} - } } diff --git a/android/app/src/main/java/app/organicmaps/car/screens/download/DownloaderScreen.java b/android/app/src/main/java/app/organicmaps/car/screens/download/DownloaderScreen.java index b65924aedf..1eabac4711 100644 --- a/android/app/src/main/java/app/organicmaps/car/screens/download/DownloaderScreen.java +++ b/android/app/src/main/java/app/organicmaps/car/screens/download/DownloaderScreen.java @@ -109,7 +109,7 @@ class DownloaderScreen extends BaseScreen for (final var item : mMissingMaps.entrySet()) { item.getValue().update(); - MapManager.nativeDownload(item.getKey()); + MapManager.startDownload(item.getKey()); } } diff --git a/android/app/src/main/java/app/organicmaps/downloader/DownloaderAdapter.java b/android/app/src/main/java/app/organicmaps/downloader/DownloaderAdapter.java index 69083e34f2..6b59f3ac4f 100644 --- a/android/app/src/main/java/app/organicmaps/downloader/DownloaderAdapter.java +++ b/android/app/src/main/java/app/organicmaps/downloader/DownloaderAdapter.java @@ -386,9 +386,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde } case CountryItem.STATUS_FAILED -> { - RetryFailedDownloadConfirmationListener listener = - new RetryFailedDownloadConfirmationListener(mActivity.getApplication()); - MapManager.warn3gAndRetry(mActivity, mItem.id, listener); + MapManager.warn3gAndRetry(mActivity, mItem.id, null); } case CountryItem.STATUS_UPDATABLE -> MapManager.warnOn3gUpdate(mActivity, mItem.id, () -> MapManager.nativeUpdate(mItem.id)); diff --git a/android/app/src/main/java/app/organicmaps/downloader/DownloaderNotifier.java b/android/app/src/main/java/app/organicmaps/downloader/DownloaderNotifier.java index 1b70eec974..c01bf08425 100644 --- a/android/app/src/main/java/app/organicmaps/downloader/DownloaderNotifier.java +++ b/android/app/src/main/java/app/organicmaps/downloader/DownloaderNotifier.java @@ -22,12 +22,20 @@ import app.organicmaps.R; import app.organicmaps.util.StringUtils; import app.organicmaps.util.log.Logger; -public abstract class DownloaderNotifier +public class DownloaderNotifier { private static final String TAG = DownloaderNotifier.class.getSimpleName(); private static final String CHANNEL_ID = "downloader"; - private static final int NOTIFICATION_ID = 1; + public static final int NOTIFICATION_ID = 1; + + private final Context mContext; + private NotificationCompat.Builder mProgressNotificationBuilder; + + public DownloaderNotifier(Context context) + { + mContext = context; + } public static void createNotificationChannel(@NonNull Context context) { @@ -42,49 +50,93 @@ public abstract class DownloaderNotifier notificationManager.createNotificationChannel(channel); } - public static void notifyDownloadFailed(@NonNull Context context, @Nullable String countryId) + public void notifyDownloadFailed(@Nullable String countryId) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && - ContextCompat.checkSelfPermission(context, POST_NOTIFICATIONS) != PERMISSION_GRANTED) + ContextCompat.checkSelfPermission(mContext, POST_NOTIFICATIONS) != PERMISSION_GRANTED) { Logger.w(TAG, "Permission POST_NOTIFICATIONS is not granted, skipping notification"); return; } - final String title = context.getString(R.string.app_name); + final String title = mContext.getString(R.string.app_name); final String countryName = MapManager.nativeGetName(countryId); - final String content = context.getString(R.string.download_country_failed, countryName); + final String content = mContext.getString(R.string.download_country_failed, countryName); - final int FLAG_IMMUTABLE = Build.VERSION.SDK_INT < Build.VERSION_CODES.M ? 0 : PendingIntent.FLAG_IMMUTABLE; - final Intent contentIntent = MwmActivity.createShowMapIntent(context, countryId); - contentIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - final PendingIntent contentPendingIntent = PendingIntent.getActivity(context, 0, contentIntent, - PendingIntent.FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE); + var contentPendingIntent = getNotificationPendingIntent(countryId); - final Notification notification = new NotificationCompat.Builder(context, CHANNEL_ID) + final Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID) .setAutoCancel(true) .setCategory(NotificationCompat.CATEGORY_ERROR) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setSmallIcon(R.drawable.ic_splash) - .setColor(ContextCompat.getColor(context, R.color.notification)) + .setColor(ContextCompat.getColor(mContext, R.color.notification)) .setContentTitle(title) .setContentText(content) .setShowWhen(true) - .setTicker(getTicker(context, title, content)) + .setTicker(getTicker(mContext, title, content)) .setContentIntent(contentPendingIntent) .setOnlyAlertOnce(true) .build(); Logger.i(TAG, "Notifying about failed map download"); - final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); + final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(mContext); notificationManager.notify(NOTIFICATION_ID, notification); } - static void cancelNotification(@NonNull Context context) + public void notifyProgress() { + notifyProgress(null, 0, 0); + } + + public void notifyProgress(@Nullable String countryId, int maxProgress, int progress) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + ContextCompat.checkSelfPermission(mContext, POST_NOTIFICATIONS) != PERMISSION_GRANTED) + { + Logger.w(TAG, "Permission POST_NOTIFICATIONS is not granted, skipping notification"); + return; + } + + NotificationManagerCompat.from(mContext).notify(NOTIFICATION_ID, buildProgressNotification(countryId, maxProgress, progress)); + } + + @NonNull + public Notification buildProgressNotification() { - Logger.i(TAG, "Cancelling notification about failed map download"); - final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); - notificationManager.cancel(NOTIFICATION_ID); + return buildProgressNotification(null, 0, 0); + } + + @NonNull + public Notification buildProgressNotification(@Nullable String countryId, int maxProgress, int progress) + { + var builder = startNotification(countryId); + + builder.setProgress(maxProgress, progress, maxProgress == 0); + builder.setContentText("Download in progress"); + + return builder.build(); + } + + @NonNull + private NotificationCompat.Builder startNotification(@Nullable String countryId) + { + final String title = mContext.getString(R.string.app_name); + + return new NotificationCompat.Builder(mContext, CHANNEL_ID) + .setAutoCancel(true) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setSmallIcon(R.drawable.ic_splash) + .setColor(ContextCompat.getColor(mContext, R.color.notification)) + .setShowWhen(true) + .setContentTitle(title) + .setContentIntent(getNotificationPendingIntent(countryId)); + } + + @NonNull + private PendingIntent getNotificationPendingIntent(@Nullable String countryId) { + final int FLAG_IMMUTABLE = Build.VERSION.SDK_INT < Build.VERSION_CODES.M ? 0 : PendingIntent.FLAG_IMMUTABLE; + final Intent contentIntent = MwmActivity.createShowMapIntent(mContext, countryId); + contentIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return PendingIntent.getActivity(mContext, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE); } @NonNull diff --git a/android/app/src/main/java/app/organicmaps/downloader/DownloaderService.java b/android/app/src/main/java/app/organicmaps/downloader/DownloaderService.java new file mode 100644 index 0000000000..ee4fcd7550 --- /dev/null +++ b/android/app/src/main/java/app/organicmaps/downloader/DownloaderService.java @@ -0,0 +1,142 @@ +package app.organicmaps.downloader; + +import static android.Manifest.permission.POST_NOTIFICATIONS; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import android.app.ForegroundServiceStartNotAllowedException; +import android.app.Service; +import android.content.Intent; +import android.os.Build; +import android.os.IBinder; + +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; + +import java.util.List; + +import app.organicmaps.MwmApplication; +import app.organicmaps.util.log.Logger; + +public class DownloaderService extends Service implements MapManager.StorageCallback +{ + private static final String TAG = DownloaderService.class.getSimpleName(); + + private final DownloaderNotifier mNotifier = new DownloaderNotifier(this); + private int mSubscriptionSlot; + + @Override + public void onCreate() + { + super.onCreate(); + + Logger.i(TAG); + + mSubscriptionSlot = MapManager.nativeSubscribe(this); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + Logger.i(TAG, "Downloading: " + MapManager.nativeIsDownloading()); + + var notification = mNotifier.buildProgressNotification(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + { + try + { + startForeground(DownloaderNotifier.NOTIFICATION_ID, notification); + } catch (ForegroundServiceStartNotAllowedException e) + { + Logger.e(TAG, "Oops! ForegroundService is not allowed", e); + } + } else + { + startForeground(DownloaderNotifier.NOTIFICATION_ID, notification); + } + + return START_NOT_STICKY; + } + + @Nullable + @Override + public IBinder onBind(Intent intent) + { + return null; + } + + @Override + public void onStatusChanged(List<MapManager.StorageCallbackData> data) + { + var isDownloading = MapManager.nativeIsDownloading(); + var hasFailed = hasDownloadFailed(data); + + Logger.i(TAG, "Downloading: " + isDownloading + " failure: " + hasFailed); + + if (!isDownloading) + { + if (hasFailed) + { + // Detach service from the notification to keep after the service is stopped. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + { + stopForeground(Service.STOP_FOREGROUND_DETACH); + } else + { + stopForeground(false); + } + } + stopSelf(); + } + } + + @Override + public void onProgress(String countryId, long localSize, long remoteSize) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + ContextCompat.checkSelfPermission(this, POST_NOTIFICATIONS) != PERMISSION_GRANTED) + { + Logger.w(TAG, "Permission POST_NOTIFICATIONS is not granted, skipping notification"); + return; + } + + // TODO: How to calculate progress? + mNotifier.notifyProgress(); + } + + @Override + public void onDestroy() + { + super.onDestroy(); + + Logger.i(TAG, "onDestroy"); + + MapManager.nativeUnsubscribe(mSubscriptionSlot); + } + + /** + * Start the foreground service to keep the user informed about the status of region downloads. + */ + public static void startForegroundService() + { + Logger.i(TAG); + var context = MwmApplication.sInstance; + ContextCompat.startForegroundService(context, new Intent(context, DownloaderService.class)); + } + + private boolean hasDownloadFailed(List<MapManager.StorageCallbackData> data) + { + for (MapManager.StorageCallbackData item : data) + { + if (item.isLeafNode && item.newStatus == CountryItem.STATUS_FAILED) + { + if (MapManager.nativeIsAutoretryFailed()) + { + mNotifier.notifyDownloadFailed(item.countryId); + return true; + } + } + } + + return false; + } +} diff --git a/android/app/src/main/java/app/organicmaps/downloader/ExpandRetryConfirmationListener.java b/android/app/src/main/java/app/organicmaps/downloader/ExpandRetryConfirmationListener.java index ec5e90140a..c931f04bee 100644 --- a/android/app/src/main/java/app/organicmaps/downloader/ExpandRetryConfirmationListener.java +++ b/android/app/src/main/java/app/organicmaps/downloader/ExpandRetryConfirmationListener.java @@ -1,28 +1,22 @@ package app.organicmaps.downloader; -import android.app.Application; - -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import app.organicmaps.util.Utils; -class ExpandRetryConfirmationListener extends RetryFailedDownloadConfirmationListener +class ExpandRetryConfirmationListener implements Runnable { @Nullable private final Utils.Proc<Boolean> mDialogClickListener; - ExpandRetryConfirmationListener(@NonNull Application app, - @Nullable Utils.Proc<Boolean> dialogClickListener) + ExpandRetryConfirmationListener(@Nullable Utils.Proc<Boolean> dialogClickListener) { - super(app); mDialogClickListener = dialogClickListener; } @Override public void run() { - super.run(); if (mDialogClickListener == null) return; mDialogClickListener.invoke(true); diff --git a/android/app/src/main/java/app/organicmaps/downloader/MapManager.java b/android/app/src/main/java/app/organicmaps/downloader/MapManager.java index 8348d5ef6d..9cb715baa0 100644 --- a/android/app/src/main/java/app/organicmaps/downloader/MapManager.java +++ b/android/app/src/main/java/app/organicmaps/downloader/MapManager.java @@ -106,8 +106,8 @@ public final class MapManager }) .setPositiveButton(R.string.downloader_retry, (dialog, which) -> { Application app = activity.getApplication(); - RetryFailedDownloadConfirmationListener listener - = new ExpandRetryConfirmationListener(app, dialogClickListener); + ExpandRetryConfirmationListener listener + = new ExpandRetryConfirmationListener(dialogClickListener); warn3gAndRetry(activity, errorData.countryId, listener); }).create(); dlg.setCanceledOnTouchOutside(false); @@ -208,7 +208,7 @@ public final class MapManager return warnOn3g(activity, countryId, () -> { if (onAcceptListener != null) onAcceptListener.run(); - nativeDownload(countryId); + startDownload(countryId); }); } @@ -217,10 +217,37 @@ public final class MapManager return warnOn3g(activity, countryId, () -> { if (onAcceptListener != null) onAcceptListener.run(); - nativeRetry(countryId); + retryDownload(countryId); }); } + /** + * Enqueues failed items under given {@code root} node in downloader. + */ + public static void retryDownload(@NonNull String countryId) { + DownloaderService.startForegroundService(); + nativeRetry(countryId); + } + + /** + * Enqueues the given list of nodes and its children in downloader. + */ + public static void startDownload(String... countries) { + DownloaderService.startForegroundService(); + for (var countryId : countries) + { + nativeDownload(countryId); + } + } + + /** + * Enqueues given {@code root} node and its children in downloader. + */ + public static void startDownload(@NonNull String countryId) { + DownloaderService.startForegroundService(); + nativeDownload(countryId); + } + /** * Retrieves ID of root node. */ @@ -314,12 +341,12 @@ public final class MapManager /** * Enqueues given {@code root} node and its children in downloader. */ - public static native void nativeDownload(String root); + private static native void nativeDownload(String root); /** * Enqueues failed items under given {@code root} node in downloader. */ - public static native void nativeRetry(String root); + private static native void nativeRetry(String root); /** * Enqueues given {@code root} node with its children in downloader. diff --git a/android/app/src/main/java/app/organicmaps/downloader/OnmapDownloader.java b/android/app/src/main/java/app/organicmaps/downloader/OnmapDownloader.java index f637a2d49f..b3503a453f 100644 --- a/android/app/src/main/java/app/organicmaps/downloader/OnmapDownloader.java +++ b/android/app/src/main/java/app/organicmaps/downloader/OnmapDownloader.java @@ -166,7 +166,7 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener if (TextUtils.equals(mCurrentCountry.id, country) && MapManager.nativeHasSpaceToDownloadCountry(country)) { - MapManager.nativeDownload(mCurrentCountry.id); + MapManager.startDownload(mCurrentCountry.id); } } } @@ -210,12 +210,11 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener boolean retry = (mCurrentCountry.status == CountryItem.STATUS_FAILED); if (retry) { - DownloaderNotifier.cancelNotification(mActivity.getApplicationContext()); - MapManager.nativeRetry(mCurrentCountry.id); + MapManager.retryDownload(mCurrentCountry.id); } else { - MapManager.nativeDownload(mCurrentCountry.id); + MapManager.startDownload(mCurrentCountry.id); mActivity.requestPostNotificationsPermission(); } })); diff --git a/android/app/src/main/java/app/organicmaps/downloader/RetryFailedDownloadConfirmationListener.java b/android/app/src/main/java/app/organicmaps/downloader/RetryFailedDownloadConfirmationListener.java deleted file mode 100644 index ecc252d2ec..0000000000 --- a/android/app/src/main/java/app/organicmaps/downloader/RetryFailedDownloadConfirmationListener.java +++ /dev/null @@ -1,22 +0,0 @@ -package app.organicmaps.downloader; - -import android.app.Application; - -import androidx.annotation.NonNull; - -public class RetryFailedDownloadConfirmationListener implements Runnable -{ - @NonNull - private final Application mApplication; - - RetryFailedDownloadConfirmationListener(@NonNull Application application) - { - mApplication = application; - } - - @Override - public void run() - { - DownloaderNotifier.cancelNotification(mApplication); - } -} diff --git a/android/app/src/main/java/app/organicmaps/routing/RoutingMapsDownloadFragment.java b/android/app/src/main/java/app/organicmaps/routing/RoutingMapsDownloadFragment.java index 8af14f5494..976fe7a9db 100644 --- a/android/app/src/main/java/app/organicmaps/routing/RoutingMapsDownloadFragment.java +++ b/android/app/src/main/java/app/organicmaps/routing/RoutingMapsDownloadFragment.java @@ -42,8 +42,7 @@ public class RoutingMapsDownloadFragment extends BaseRoutingErrorDialogFragment mMapsArray[i] = item.id; } - for (String map : mMaps) - MapManager.nativeDownload(map); + MapManager.startDownload(mMapsArray); } private View setupFrame(View frame) -- 2.45.3 From 54bdf5a9f9b03e7edc7e7503a1470ab817547276 Mon Sep 17 00:00:00 2001 From: Kiryl Kaveryn <kirylkaveryn@gmail.com> Date: Wed, 22 Jan 2025 20:55:59 +0400 Subject: [PATCH 03/12] [ios] add `getResults` to the MWMSearch to fetch the full results list Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com> --- iphone/Maps/Core/Search/MWMSearch.h | 1 + iphone/Maps/Core/Search/MWMSearch.mm | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/iphone/Maps/Core/Search/MWMSearch.h b/iphone/Maps/Core/Search/MWMSearch.h index 5b3e9cc345..2026d65619 100644 --- a/iphone/Maps/Core/Search/MWMSearch.h +++ b/iphone/Maps/Core/Search/MWMSearch.h @@ -19,6 +19,7 @@ NS_SWIFT_NAME(Search) + (SearchItemType)resultTypeWithRow:(NSUInteger)row; + (NSUInteger)containerIndexWithRow:(NSUInteger)row; + (SearchResult *)resultWithContainerIndex:(NSUInteger)index; ++ (NSArray<SearchResult *> *)getResults; + (void)clear; diff --git a/iphone/Maps/Core/Search/MWMSearch.mm b/iphone/Maps/Core/Search/MWMSearch.mm index f46f3b1a94..0f0c9fdba4 100644 --- a/iphone/Maps/Core/Search/MWMSearch.mm +++ b/iphone/Maps/Core/Search/MWMSearch.mm @@ -175,6 +175,15 @@ using Observers = NSHashTable<Observer>; return result; } ++ (NSArray<SearchResult *> *)getResults { + NSMutableArray<SearchResult *> * results = [[NSMutableArray alloc] initWithCapacity:MWMSearch.resultsCount]; + for (NSUInteger i = 0; i < MWMSearch.resultsCount; ++i) { + SearchResult * result = [MWMSearch resultWithContainerIndex:i]; + [results addObject:result]; + } + return [results copy]; +} + + (SearchItemType)resultTypeWithRow:(NSUInteger)row { auto itemsIndex = [MWMSearch manager].itemsIndex; return [itemsIndex resultTypeWithRow:row]; -- 2.45.3 From 56da7da8eaac6202c47c0c239334bd6569b7112a Mon Sep 17 00:00:00 2001 From: Kiryl Kaveryn <kirylkaveryn@gmail.com> Date: Wed, 5 Feb 2025 17:01:56 +0400 Subject: [PATCH 04/12] [ios] show current searching results on the map Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com> --- iphone/Maps/Core/Search/MWMSearch.h | 2 ++ iphone/Maps/Core/Search/MWMSearch.mm | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/iphone/Maps/Core/Search/MWMSearch.h b/iphone/Maps/Core/Search/MWMSearch.h index 2026d65619..225a5446ff 100644 --- a/iphone/Maps/Core/Search/MWMSearch.h +++ b/iphone/Maps/Core/Search/MWMSearch.h @@ -15,6 +15,8 @@ NS_SWIFT_NAME(Search) + (void)searchQuery:(NSString *)query forInputLocale:(NSString *)inputLocale withCategory:(BOOL)isCategory; + (void)showResultAtIndex:(NSUInteger)index; ++ (void)showEverywhereSearchResultsOnMap; ++ (void)showViewportSearchResultsOnMap; + (SearchItemType)resultTypeWithRow:(NSUInteger)row; + (NSUInteger)containerIndexWithRow:(NSUInteger)row; diff --git a/iphone/Maps/Core/Search/MWMSearch.mm b/iphone/Maps/Core/Search/MWMSearch.mm index 0f0c9fdba4..d640523c01 100644 --- a/iphone/Maps/Core/Search/MWMSearch.mm +++ b/iphone/Maps/Core/Search/MWMSearch.mm @@ -175,6 +175,18 @@ using Observers = NSHashTable<Observer>; return result; } ++ (void)showEverywhereSearchResultsOnMap { + MWMSearch *manager = [MWMSearch manager]; + if (![MWMRouter isRoutingActive]) + GetFramework().ShowSearchResults(manager->m_everywhereResults); +} + ++ (void)showViewportSearchResultsOnMap { + MWMSearch *manager = [MWMSearch manager]; + if (![MWMRouter isRoutingActive]) + GetFramework().ShowSearchResults(manager->m_viewportResults); +} + + (NSArray<SearchResult *> *)getResults { NSMutableArray<SearchResult *> * results = [[NSMutableArray alloc] initWithCapacity:MWMSearch.resultsCount]; for (NSUInteger i = 0; i < MWMSearch.resultsCount; ++i) { -- 2.45.3 From 8a1ef62698fc7977ea9399f339ee97211aedc7b1 Mon Sep 17 00:00:00 2001 From: Kiryl Kaveryn <kirylkaveryn@gmail.com> Date: Wed, 5 Feb 2025 15:34:45 +0400 Subject: [PATCH 05/12] [ios] rename `IPAD` func to `isIPad` and make internal visibility it may be used in different situations Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com> --- iphone/Maps/Common/Common.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/iphone/Maps/Common/Common.swift b/iphone/Maps/Common/Common.swift index abe3f3e044..757d309d3b 100644 --- a/iphone/Maps/Common/Common.swift +++ b/iphone/Maps/Common/Common.swift @@ -1,19 +1,19 @@ import Foundation -private func IPAD() -> Bool { return UI_USER_INTERFACE_IDIOM() == .pad } +var isIPad: Bool { return UI_USER_INTERFACE_IDIOM() == .pad } func L(_ key: String) -> String { return NSLocalizedString(key, comment: "") } -func alternative<T>(iPhone: T, iPad: T) -> T { return IPAD() ? iPad : iPhone } +func alternative<T>(iPhone: T, iPad: T) -> T { return isIPad ? iPad : iPhone } func iPadSpecific(_ f: () -> Void) { - if IPAD() { + if isIPad { f() } } func iPhoneSpecific(_ f: () -> Void) { - if !IPAD() { + if !isIPad { f() } } -- 2.45.3 From 2b6f6d2aa27cbf50b43702d8a1153ae6f76637b0 Mon Sep 17 00:00:00 2001 From: Kiryl Kaveryn <kirylkaveryn@gmail.com> Date: Tue, 4 Feb 2025 18:29:48 +0400 Subject: [PATCH 06/12] [ios] prevent search marks hiding when the search result is selected Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com> --- iphone/Maps/Core/Search/MWMSearch.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iphone/Maps/Core/Search/MWMSearch.mm b/iphone/Maps/Core/Search/MWMSearch.mm index d640523c01..d4709d1401 100644 --- a/iphone/Maps/Core/Search/MWMSearch.mm +++ b/iphone/Maps/Core/Search/MWMSearch.mm @@ -165,7 +165,7 @@ using Observers = NSHashTable<Observer>; + (void)showResultAtIndex:(NSUInteger)index { auto const & result = [MWMSearch manager]->m_everywhereResults[index]; - GetFramework().ShowSearchResult(result); + GetFramework().SelectSearchResult(result, true); } + (SearchResult *)resultWithContainerIndex:(NSUInteger)index { -- 2.45.3 From 38fb23e338f12189d78f19c132dff8e73ae516ae Mon Sep 17 00:00:00 2001 From: Kiryl Kaveryn <kirylkaveryn@gmail.com> Date: Wed, 5 Feb 2025 17:16:40 +0400 Subject: [PATCH 07/12] [ios] get the current Side buttons available size This size is used to update the side buttons during the modally search screen dragging Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com> --- .../CustomViews/MapViewControls/SideButtons/MWMSideButtons.h | 1 + .../CustomViews/MapViewControls/SideButtons/MWMSideButtons.mm | 3 +++ .../MapViewControls/SideButtons/MWMSideButtonsView.h | 1 + .../MapViewControls/SideButtons/MWMSideButtonsView.mm | 4 ++++ 4 files changed, 9 insertions(+) diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/SideButtons/MWMSideButtons.h b/iphone/Maps/Classes/CustomViews/MapViewControls/SideButtons/MWMSideButtons.h index adc10907c8..c5fb5b49cd 100644 --- a/iphone/Maps/Classes/CustomViews/MapViewControls/SideButtons/MWMSideButtons.h +++ b/iphone/Maps/Classes/CustomViews/MapViewControls/SideButtons/MWMSideButtons.h @@ -14,5 +14,6 @@ - (void)processMyPositionStateModeEvent:(MWMMyPositionMode)mode; + (void)updateAvailableArea:(CGRect)frame; ++ (CGRect)getAvailableArea; @end diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/SideButtons/MWMSideButtons.mm b/iphone/Maps/Classes/CustomViews/MapViewControls/SideButtons/MWMSideButtons.mm index 98b340272a..1eab6ad453 100644 --- a/iphone/Maps/Classes/CustomViews/MapViewControls/SideButtons/MWMSideButtons.mm +++ b/iphone/Maps/Classes/CustomViews/MapViewControls/SideButtons/MWMSideButtons.mm @@ -57,6 +57,9 @@ 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); diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/SideButtons/MWMSideButtonsView.h b/iphone/Maps/Classes/CustomViews/MapViewControls/SideButtons/MWMSideButtonsView.h index dee00bda0a..aaffc37534 100644 --- a/iphone/Maps/Classes/CustomViews/MapViewControls/SideButtons/MWMSideButtonsView.h +++ b/iphone/Maps/Classes/CustomViews/MapViewControls/SideButtons/MWMSideButtonsView.h @@ -8,5 +8,6 @@ - (void)setHidden:(BOOL)hidden animated:(BOOL)animated; - (void)updateAvailableArea:(CGRect)frame; +- (CGRect)getAvailableArea; @end diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/SideButtons/MWMSideButtonsView.mm b/iphone/Maps/Classes/CustomViews/MapViewControls/SideButtons/MWMSideButtonsView.mm index bfea45c39b..ba8dd8ee50 100644 --- a/iphone/Maps/Classes/CustomViews/MapViewControls/SideButtons/MWMSideButtonsView.mm +++ b/iphone/Maps/Classes/CustomViews/MapViewControls/SideButtons/MWMSideButtonsView.mm @@ -144,6 +144,10 @@ CGFloat const kButtonsBottomOffset = 6; [self setNeedsLayout]; } +- (CGRect)getAvailableArea { + return self.availableArea; +} + - (CGFloat)availableHeight { return self.availableArea.size.height - kButtonsTopOffset - kButtonsBottomOffset; } -- 2.45.3 From 85c0e654a1136b22efea22e5b20962541a04a882 Mon Sep 17 00:00:00 2001 From: Kiryl Kaveryn <kirylkaveryn@gmail.com> Date: Tue, 28 Jan 2025 14:59:13 +0400 Subject: [PATCH 08/12] [ios] add `searchHeader` style Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com> --- iphone/Maps/Core/Theme/SearchStyleSheet.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/iphone/Maps/Core/Theme/SearchStyleSheet.swift b/iphone/Maps/Core/Theme/SearchStyleSheet.swift index a1c1a24297..061e48e4d0 100644 --- a/iphone/Maps/Core/Theme/SearchStyleSheet.swift +++ b/iphone/Maps/Core/Theme/SearchStyleSheet.swift @@ -1,4 +1,5 @@ enum SearchStyleSheet: String, CaseIterable { + case searchHeader case searchInstallButton = "SearchInstallButton" case searchBanner = "SearchBanner" case searchClosedBackground = "SearchClosedBackground" @@ -16,6 +17,17 @@ enum SearchStyleSheet: String, CaseIterable { 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 -- 2.45.3 From 3a000ec0d7bbd65ee1b137eaf441ca3c4a913d26 Mon Sep 17 00:00:00 2001 From: Kiryl Kaveryn <kirylkaveryn@gmail.com> Date: Wed, 5 Feb 2025 17:15:20 +0400 Subject: [PATCH 09/12] [ios] implement modal search screen SearchOnMap Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com> --- iphone/Maps/Core/Search/MWMSearch.h | 15 +- iphone/Maps/Core/Search/MWMSearch.mm | 6 +- iphone/Maps/Maps.xcodeproj/project.pbxproj | 92 ++++- .../SearchOnMapTests/SearchOnMapTests.swift | 262 +++++++++++++ .../Search/SearchOnMap/PlaceholderView.swift | 122 ++++++ .../Presentation/MapPassthroughView.swift | 30 ++ .../ModalScreenPresentationStep.swift | 94 +++++ ...archOnMapModalPresentationController.swift | 226 +++++++++++ .../SearchOnMapModalTransitionManager.swift | 21 + .../SideMenuDismissalAnimator.swift | 22 ++ .../SideMenuPresentationAnimator.swift | 25 ++ .../SearchOnMap/SearchOnMapHeaderView.swift | 113 ++++++ .../SearchOnMap/SearchOnMapInteractor.swift | 165 ++++++++ .../SearchOnMap/SearchOnMapManager.swift | 96 +++++ .../SearchOnMap/SearchOnMapModels.swift | 94 +++++ .../SearchOnMap/SearchOnMapPresenter.swift | 117 ++++++ .../SearchOnMapViewController.swift | 362 ++++++++++++++++++ .../SearchCategoriesViewController.swift | 13 +- .../SearchHistoryViewController.swift | 112 ++++-- .../SearchHistoryViewController.xib | 56 --- .../Search/Tabs/SearchTabViewController.swift | 18 +- 21 files changed, 1956 insertions(+), 105 deletions(-) create mode 100644 iphone/Maps/Tests/UI/SearchOnMapTests/SearchOnMapTests.swift create mode 100644 iphone/Maps/UI/Search/SearchOnMap/PlaceholderView.swift create mode 100644 iphone/Maps/UI/Search/SearchOnMap/Presentation/MapPassthroughView.swift create mode 100644 iphone/Maps/UI/Search/SearchOnMap/Presentation/ModalScreenPresentationStep.swift create mode 100644 iphone/Maps/UI/Search/SearchOnMap/Presentation/SearchOnMapModalPresentationController.swift create mode 100644 iphone/Maps/UI/Search/SearchOnMap/Presentation/SearchOnMapModalTransitionManager.swift create mode 100644 iphone/Maps/UI/Search/SearchOnMap/Presentation/SideMenuDismissalAnimator.swift create mode 100644 iphone/Maps/UI/Search/SearchOnMap/Presentation/SideMenuPresentationAnimator.swift create mode 100644 iphone/Maps/UI/Search/SearchOnMap/SearchOnMapHeaderView.swift create mode 100644 iphone/Maps/UI/Search/SearchOnMap/SearchOnMapInteractor.swift create mode 100644 iphone/Maps/UI/Search/SearchOnMap/SearchOnMapManager.swift create mode 100644 iphone/Maps/UI/Search/SearchOnMap/SearchOnMapModels.swift create mode 100644 iphone/Maps/UI/Search/SearchOnMap/SearchOnMapPresenter.swift create mode 100644 iphone/Maps/UI/Search/SearchOnMap/SearchOnMapViewController.swift delete mode 100644 iphone/Maps/UI/Search/Tabs/HistoryTab/SearchHistoryViewController.xib diff --git a/iphone/Maps/Core/Search/MWMSearch.h b/iphone/Maps/Core/Search/MWMSearch.h index 225a5446ff..130d0c4b24 100644 --- a/iphone/Maps/Core/Search/MWMSearch.h +++ b/iphone/Maps/Core/Search/MWMSearch.h @@ -5,9 +5,7 @@ NS_ASSUME_NONNULL_BEGIN @class SearchResult; -NS_SWIFT_NAME(Search) -@interface MWMSearch : NSObject - +@protocol SearchManager + (void)addObserver:(id<MWMSearchObserver>)observer; + (void)removeObserver:(id<MWMSearchObserver>)observer; @@ -18,12 +16,17 @@ NS_SWIFT_NAME(Search) + (void)showEverywhereSearchResultsOnMap; + (void)showViewportSearchResultsOnMap; -+ (SearchItemType)resultTypeWithRow:(NSUInteger)row; -+ (NSUInteger)containerIndexWithRow:(NSUInteger)row; -+ (SearchResult *)resultWithContainerIndex:(NSUInteger)index; + (NSArray<SearchResult *> *)getResults; + (void)clear; +@end + +NS_SWIFT_NAME(Search) +@interface MWMSearch : NSObject<SearchManager> + ++ (SearchItemType)resultTypeWithRow:(NSUInteger)row; ++ (NSUInteger)containerIndexWithRow:(NSUInteger)row; ++ (SearchResult *)resultWithContainerIndex:(NSUInteger)index; + (void)setSearchOnMap:(BOOL)searchOnMap; diff --git a/iphone/Maps/Core/Search/MWMSearch.mm b/iphone/Maps/Core/Search/MWMSearch.mm index d4709d1401..88dca14f27 100644 --- a/iphone/Maps/Core/Search/MWMSearch.mm +++ b/iphone/Maps/Core/Search/MWMSearch.mm @@ -176,15 +176,15 @@ using Observers = NSHashTable<Observer>; } + (void)showEverywhereSearchResultsOnMap { - MWMSearch *manager = [MWMSearch manager]; + MWMSearch * manager = [MWMSearch manager]; if (![MWMRouter isRoutingActive]) GetFramework().ShowSearchResults(manager->m_everywhereResults); } + (void)showViewportSearchResultsOnMap { - MWMSearch *manager = [MWMSearch manager]; + MWMSearch * manager = [MWMSearch manager]; if (![MWMRouter isRoutingActive]) - GetFramework().ShowSearchResults(manager->m_viewportResults); + [manager processViewportChangedEvent]; } + (NSArray<SearchResult *> *)getResults { diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index ec78d74d37..bfc090f5f6 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -493,8 +493,21 @@ ED4DC7772CAEDECC0029B338 /* ProductsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4DC7752CAEDECC0029B338 /* ProductsViewModel.swift */; }; 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 */; }; 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 */; }; + 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 */; }; @@ -506,6 +519,7 @@ ED79A5D82BDF8D6100952D1F /* LocalDirectoryMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED79A5D02BDF8D6100952D1F /* LocalDirectoryMonitor.swift */; }; ED7CCC4F2C1362E300E2A737 /* FileType.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED7CCC4E2C1362E300E2A737 /* FileType.swift */; }; ED808D0F2C38407800D52585 /* CircleImageButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED808D0E2C38407800D52585 /* CircleImageButton.swift */; }; + ED810EC52D566E9B00ECDE2C /* SearchOnMapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED810EC42D566E9B00ECDE2C /* SearchOnMapTests.swift */; }; ED8270F02C2071A3005966DA /* SettingsTableViewDetailedSwitchCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED8270EF2C2071A3005966DA /* SettingsTableViewDetailedSwitchCell.swift */; }; ED83880F2D54DEB3002A0536 /* UIImage+FilledWithColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED83880E2D54DEA4002A0536 /* UIImage+FilledWithColor.swift */; }; ED914AB22D35063A00973C45 /* TextColorStyleSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED914AB12D35063A00973C45 /* TextColorStyleSheet.swift */; }; @@ -523,7 +537,6 @@ EDC4E3612C5E2576009286A2 /* RecentlyDeletedCategoriesViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC4E3412C5D1BD3009286A2 /* RecentlyDeletedCategoriesViewModelTests.swift */; }; EDC4E3692C5E6F5B009286A2 /* MockRecentlyDeletedCategoriesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC4E3402C5D1BD3009286A2 /* MockRecentlyDeletedCategoriesManager.swift */; }; EDCA7CDF2D317DF9003366CE /* StyleSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDCA7CDE2D317DF9003366CE /* StyleSheet.swift */; }; - EDDE060E2D6CAEAF000C328A /* SearchHistoryViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = EDDE060D2D6CAEAF000C328A /* SearchHistoryViewController.xib */; }; EDE243DD2B6D2E640057369B /* AboutController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDE243D52B6CF3980057369B /* AboutController.swift */; }; EDE243E52B6D3F400057369B /* OSMView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDE243E42B6D3F400057369B /* OSMView.swift */; }; EDE243E72B6D55610057369B /* InfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDE243E02B6D3EA00057369B /* InfoView.swift */; }; @@ -1455,11 +1468,24 @@ ED4DC7732CAEDECC0029B338 /* ProductButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductButton.swift; sourceTree = "<group>"; }; 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>"; }; 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>"; }; + 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>"; }; + ED70D5852D539A2500738C1E /* SearchOnMapModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchOnMapModels.swift; sourceTree = "<group>"; }; + ED70D5862D539A2500738C1E /* SearchOnMapPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchOnMapPresenter.swift; sourceTree = "<group>"; }; + ED70D5872D539A2500738C1E /* SearchOnMapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchOnMapViewController.swift; sourceTree = "<group>"; }; ED77556D2C2C490B0051E656 /* UIAlertController+openInAppActionSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAlertController+openInAppActionSheet.swift"; sourceTree = "<group>"; }; ED79A5AA2BD7AA9C00952D1F /* LoadingOverlayViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingOverlayViewController.swift; sourceTree = "<group>"; }; ED79A5AC2BD7BA0F00952D1F /* UIApplication+LoadingOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+LoadingOverlay.swift"; sourceTree = "<group>"; }; @@ -1471,6 +1497,7 @@ ED79A5D02BDF8D6100952D1F /* LocalDirectoryMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalDirectoryMonitor.swift; sourceTree = "<group>"; }; ED7CCC4E2C1362E300E2A737 /* FileType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileType.swift; sourceTree = "<group>"; }; ED808D0E2C38407800D52585 /* CircleImageButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleImageButton.swift; sourceTree = "<group>"; }; + ED810EC42D566E9B00ECDE2C /* SearchOnMapTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchOnMapTests.swift; sourceTree = "<group>"; }; ED8270EF2C2071A3005966DA /* SettingsTableViewDetailedSwitchCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTableViewDetailedSwitchCell.swift; sourceTree = "<group>"; }; ED83880E2D54DEA4002A0536 /* UIImage+FilledWithColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+FilledWithColor.swift"; sourceTree = "<group>"; }; ED914AB12D35063A00973C45 /* TextColorStyleSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextColorStyleSheet.swift; sourceTree = "<group>"; }; @@ -1488,7 +1515,6 @@ EDC4E3482C5D1BEF009286A2 /* RecentlyDeletedCategoriesViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentlyDeletedCategoriesViewModel.swift; sourceTree = "<group>"; }; EDC4E3492C5D1BEF009286A2 /* RecentlyDeletedTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecentlyDeletedTableViewCell.swift; sourceTree = "<group>"; }; EDCA7CDE2D317DF9003366CE /* StyleSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StyleSheet.swift; sourceTree = "<group>"; }; - EDDE060D2D6CAEAF000C328A /* SearchHistoryViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SearchHistoryViewController.xib; sourceTree = "<group>"; }; EDE243D52B6CF3980057369B /* AboutController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutController.swift; sourceTree = "<group>"; }; EDE243E02B6D3EA00057369B /* InfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoView.swift; sourceTree = "<group>"; }; EDE243E42B6D3F400057369B /* OSMView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSMView.swift; sourceTree = "<group>"; }; @@ -3180,6 +3206,7 @@ ED1ADA312BC6B19E0029209F /* Tests */ = { isa = PBXGroup; children = ( + ED810EC02D566E6F00ECDE2C /* UI */, EDC4E3442C5D1BD3009286A2 /* Bookmarks */, 4B4153B82BF970B800EE4B02 /* Classes */, 4B4153B62BF9709100EE4B02 /* Core */, @@ -3231,6 +3258,34 @@ path = Products; sourceTree = "<group>"; }; + ED70D5812D539A2500738C1E /* Presentation */ = { + isa = PBXGroup; + children = ( + ED70D57B2D539A2500738C1E /* MapPassthroughView.swift */, + ED70D57C2D539A2500738C1E /* ModalScreenPresentationStep.swift */, + ED70D57D2D539A2500738C1E /* SearchOnMapModalPresentationController.swift */, + ED70D57E2D539A2500738C1E /* SearchOnMapModalTransitionManager.swift */, + ED70D57F2D539A2500738C1E /* SideMenuDismissalAnimator.swift */, + ED70D5802D539A2500738C1E /* SideMenuPresentationAnimator.swift */, + ); + path = Presentation; + sourceTree = "<group>"; + }; + ED70D5882D539A2500738C1E /* SearchOnMap */ = { + isa = PBXGroup; + children = ( + ED70D5812D539A2500738C1E /* Presentation */, + ED70D5822D539A2500738C1E /* PlaceholderView.swift */, + ED70D5832D539A2500738C1E /* SearchOnMapInteractor.swift */, + ED70D5842D539A2500738C1E /* SearchOnMapManager.swift */, + ED70D5852D539A2500738C1E /* SearchOnMapModels.swift */, + ED70D5862D539A2500738C1E /* SearchOnMapPresenter.swift */, + ED70D5872D539A2500738C1E /* SearchOnMapViewController.swift */, + ED5BAF4A2D688F5A0088D7B1 /* SearchOnMapHeaderView.swift */, + ); + path = SearchOnMap; + sourceTree = "<group>"; + }; ED79A5A92BD7AA7500952D1F /* LoadingOverlay */ = { isa = PBXGroup; children = ( @@ -3253,6 +3308,22 @@ path = iCloud; sourceTree = "<group>"; }; + ED810EC02D566E6F00ECDE2C /* UI */ = { + isa = PBXGroup; + children = ( + ED810EC32D566E7600ECDE2C /* SearchOnMapTests */, + ); + path = UI; + sourceTree = "<group>"; + }; + ED810EC32D566E7600ECDE2C /* SearchOnMapTests */ = { + isa = PBXGroup; + children = ( + ED810EC42D566E9B00ECDE2C /* SearchOnMapTests.swift */, + ); + path = SearchOnMapTests; + sourceTree = "<group>"; + }; ED9857022C4ECFFC00694F6C /* MailComposer */ = { isa = PBXGroup; children = ( @@ -3822,6 +3893,7 @@ F6E2FCE11E097B9F0083EBEC /* Search */ = { isa = PBXGroup; children = ( + ED70D5882D539A2500738C1E /* SearchOnMap */, F6E2FCF41E097B9F0083EBEC /* MWMSearchContentView.h */, F6E2FCF51E097B9F0083EBEC /* MWMSearchContentView.m */, F6E2FCF81E097B9F0083EBEC /* MWMSearchManager+Layout.h */, @@ -3868,7 +3940,6 @@ F6E2FD0F1E097B9F0083EBEC /* HistoryTab */ = { isa = PBXGroup; children = ( - EDDE060D2D6CAEAF000C328A /* SearchHistoryViewController.xib */, 337F98B721D3D67E00C8AC27 /* SearchHistoryCell.swift */, 337F98B321D3C9F200C8AC27 /* SearchHistoryViewController.swift */, ); @@ -4266,7 +4337,6 @@ 6741A9991BF340DE002C974C /* MWMAlertViewController.xib in Resources */, 4501B1942077C35A001B9173 /* resources-xxxhdpi_light in Resources */, 3467CEB7202C6FA900D3C670 /* BMCNotificationsCell.xib in Resources */, - EDDE060E2D6CAEAF000C328A /* SearchHistoryViewController.xib in Resources */, 4761BE2B252D3DB900EE2DE4 /* SubgroupCell.xib in Resources */, 99F9A0E72462CA1700AE21E0 /* DownloadAllView.xib in Resources */, 349D1AD51E2E325B004A2006 /* BottomMenuItemCell.xib in Resources */, @@ -4556,6 +4626,7 @@ ED43B8BD2C12063500D07BAA /* DocumentPicker.swift in Sources */, 470E1674252AD7F2002D201A /* BookmarksListInfoViewController.swift in Sources */, 47B9065521C7FA400079C85E /* NSString+MD5.m in Sources */, + ED5BAF4B2D688F5B0088D7B1 /* SearchOnMapHeaderView.swift in Sources */, CDB4D5022231412900104869 /* SettingsTemplateBuilder.swift in Sources */, 993DF10A23F6BDB100AC231A /* UISwitchRenderer.swift in Sources */, 99C9642C2428C0F700E41723 /* PlacePageHeaderBuilder.swift in Sources */, @@ -4735,6 +4806,18 @@ CD9AD96C2281B56900EC174A /* CPViewPortState.swift in Sources */, EDE243DD2B6D2E640057369B /* AboutController.swift in Sources */, 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 */, + 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 */, @@ -4881,6 +4964,7 @@ EDF838BE2C00B9D0007E4E67 /* LocalDirectoryMonitorDelegateMock.swift in Sources */, EDC4E3692C5E6F5B009286A2 /* MockRecentlyDeletedCategoriesManager.swift in Sources */, EDF838BF2C00B9D0007E4E67 /* SynchronizationStateManagerTests.swift in Sources */, + ED810EC52D566E9B00ECDE2C /* SearchOnMapTests.swift in Sources */, 4B83AE4B2C2E642100B0C3BC /* TTSTesterTest.m in Sources */, EDF838C22C00B9D6007E4E67 /* MetadataItemStubs.swift in Sources */, EDC4E3612C5E2576009286A2 /* RecentlyDeletedCategoriesViewModelTests.swift in Sources */, diff --git a/iphone/Maps/Tests/UI/SearchOnMapTests/SearchOnMapTests.swift b/iphone/Maps/Tests/UI/SearchOnMapTests/SearchOnMapTests.swift new file mode 100644 index 0000000000..1b23176dea --- /dev/null +++ b/iphone/Maps/Tests/UI/SearchOnMapTests/SearchOnMapTests.swift @@ -0,0 +1,262 @@ +import XCTest +@testable import Organic_Maps__Debug_ + +final class SearchOnMapTests: XCTestCase { + + private var presenter: SearchOnMapPresenter! + private var interactor: SearchOnMapInteractor! + private var view: SearchOnMapViewMock! + private var searchManager: SearchManagerMock.Type! + private var currentState: SearchOnMapState = .searching + + override func setUp() { + super.setUp() + searchManager = SearchManagerMock.self + presenter = SearchOnMapPresenter(transitionManager: SearchOnMapModalTransitionManager(), + isRouting: false, + didChangeState: { [weak self] in self?.currentState = $0 }) + interactor = SearchOnMapInteractor(presenter: presenter, searchManager: searchManager) + view = SearchOnMapViewMock() + presenter.view = view + } + + override func tearDown() { + presenter = nil + interactor = nil + view = nil + searchManager.results = .empty + searchManager = nil + super.tearDown() + } + + func test_GivenViewIsLoading_WhenViewLoads_ThenShowsHistoryAndCategory() { + interactor.handle(.openSearch) + + XCTAssertEqual(currentState, .searching) + XCTAssertEqual(view.viewModel.presentationStep, .fullScreen) + XCTAssertEqual(view.viewModel.contentState, .historyAndCategory) + XCTAssertEqual(view.viewModel.searchingText, nil) + XCTAssertEqual(view.viewModel.isTyping, true) + } + + func test_GivenInitialState_WhenSelectCategory_ThenUpdateSearchResultsAndShowMap() { + interactor.handle(.openSearch) + + let searchText = SearchOnMap.SearchText("category") + interactor.handle(.didSelectText(searchText, isCategory: true)) + + XCTAssertEqual(view.viewModel.presentationStep, .halfScreen) + XCTAssertEqual(view.viewModel.contentState, .searching) + XCTAssertEqual(view.viewModel.searchingText, searchText.text) + XCTAssertEqual(view.viewModel.isTyping, false) + + let results = SearchResult.stubResults() + searchManager.results = results + + XCTAssertEqual(currentState, .searching) + XCTAssertEqual(view.viewModel.presentationStep, .halfScreen) + XCTAssertEqual(view.viewModel.contentState, .results(results)) + XCTAssertEqual(view.viewModel.searchingText, nil) + XCTAssertEqual(view.viewModel.isTyping, false) + } + + func test_GivenInitialState_WhenTypeText_ThenUpdateSearchResults() { + interactor.handle(.openSearch) + + let searchText = SearchOnMap.SearchText("text") + interactor.handle(.didType(searchText)) + + XCTAssertEqual(view.viewModel.presentationStep, .fullScreen) + XCTAssertEqual(view.viewModel.contentState, .searching) + XCTAssertEqual(view.viewModel.searchingText, nil) + XCTAssertEqual(view.viewModel.isTyping, true) + + let results = SearchResult.stubResults() + searchManager.results = results + + XCTAssertEqual(currentState, .searching) + XCTAssertEqual(view.viewModel.presentationStep, .fullScreen) + XCTAssertEqual(view.viewModel.contentState, .results(results)) + XCTAssertEqual(view.viewModel.searchingText, nil) + XCTAssertEqual(view.viewModel.isTyping, true) + } + + func test_GivenInitialState_WhenTapSearch_ThenUpdateSearchResultsAndShowMap() { + interactor.handle(.openSearch) + + let searchText = SearchOnMap.SearchText("text") + interactor.handle(.didType(searchText)) + + let results = SearchResult.stubResults() + searchManager.results = results + + XCTAssertEqual(view.viewModel.presentationStep, .fullScreen) + XCTAssertEqual(view.viewModel.contentState, .results(results)) + XCTAssertEqual(view.viewModel.searchingText, nil) + XCTAssertEqual(view.viewModel.isTyping, true) + + interactor.handle(.searchButtonDidTap(searchText)) + + XCTAssertEqual(currentState, .searching) + XCTAssertEqual(view.viewModel.presentationStep, .halfScreen) + XCTAssertEqual(view.viewModel.contentState, .results(results)) + XCTAssertEqual(view.viewModel.searchingText, nil) + XCTAssertEqual(view.viewModel.isTyping, false) + } + + func test_GivenSearchIsOpened_WhenMapIsDragged_ThenCollapseSearchScreen() { + interactor.handle(.openSearch) + XCTAssertEqual(view.viewModel.presentationStep, .fullScreen) + + interactor.handle(.didStartDraggingMap) + XCTAssertEqual(view.viewModel.presentationStep, .compact) + } + + func test_GivenSearchIsOpened_WhenModalPresentationScreenIsDragged_ThenDisableTyping() { + interactor.handle(.openSearch) + XCTAssertEqual(view.viewModel.isTyping, true) + + interactor.handle(.didStartDraggingSearch) + XCTAssertEqual(view.viewModel.isTyping, false) + } + + func test_GivenResultsOnScreen_WhenSelectResult_ThenHideSearch() { + interactor.handle(.openSearch) + XCTAssertEqual(view.viewModel.isTyping, true) + + let searchText = SearchOnMap.SearchText("text") + interactor.handle(.didType(searchText)) + + let results = SearchResult.stubResults() + searchManager.results = results + + interactor.handle(.didSelectResult(results[0], atIndex: 0, withSearchText: searchText)) + XCTAssertEqual(currentState, .hidden) + XCTAssertEqual(view.viewModel.presentationStep, .hidden) + } + + func test_GivenSearchIsActive_WhenSelectPlaceOnMap_ThenHideSearch() { + interactor.handle(.openSearch) + XCTAssertEqual(view.viewModel.presentationStep, .fullScreen) + + interactor.handle(.didSelectPlaceOnMap) + + if isIPad { + XCTAssertNotEqual(view.viewModel.presentationStep, .hidden) + } else { + XCTAssertEqual(view.viewModel.presentationStep, .hidden) + } + } + + func test_GivenSearchIsHidden_WhenPPDeselected_ThenShowSearch() { + interactor.handle(.openSearch) + XCTAssertEqual(view.viewModel.isTyping, true) + + let searchText = SearchOnMap.SearchText("text") + interactor.handle(.didType(searchText)) + + let results = SearchResult.stubResults() + searchManager.results = results + + interactor.handle(.didSelectResult(results[0], atIndex: 0, withSearchText: searchText)) + XCTAssertEqual(currentState, .hidden) + XCTAssertEqual(view.viewModel.presentationStep, .hidden) + + interactor.handle(.didDeselectPlaceOnMap) + XCTAssertEqual(currentState, .searching) + XCTAssertEqual(view.viewModel.presentationStep, .halfScreen) + } + + func test_GivenSearchIsOpen_WhenCloseSearch_ThenHideSearch() { + interactor.handle(.openSearch) + XCTAssertEqual(view.viewModel.presentationStep, .fullScreen) + + interactor.handle(.closeSearch) + XCTAssertEqual(currentState, .closed) + } + + func test_GivenSearchHasText_WhenClearSearch_ThenShowHistoryAndCategory() { + interactor.handle(.openSearch) + + let searchText = SearchOnMap.SearchText("text") + interactor.handle(.didType(searchText)) + + interactor.handle(.clearButtonDidTap) + XCTAssertEqual(view.viewModel.presentationStep, .fullScreen) + XCTAssertEqual(view.viewModel.contentState, .historyAndCategory) + XCTAssertEqual(view.viewModel.searchingText, "") + XCTAssertEqual(view.viewModel.isTyping, true) + } + + func test_GivenSearchExecuted_WhenNoResults_ThenShowNoResults() { + interactor.handle(.openSearch) + + let searchText = SearchOnMap.SearchText("text") + interactor.handle(.didType(searchText)) + + searchManager.results = SearchOnMap.SearchResults([]) + interactor.onSearchCompleted() + + XCTAssertEqual(view.viewModel.contentState, .noResults) + } + + func test_GivenSearchIsActive_WhenSelectSuggestion_ThenSearchAgain() { + interactor.handle(.openSearch) + + let searchText = SearchOnMap.SearchText("old search") + interactor.handle(.didType(searchText)) + + let suggestion = SearchResult(titleText: "", type: .suggestion, suggestion: "suggestion") + interactor.handle(.didSelectResult(suggestion, atIndex: 0, withSearchText: searchText)) + + XCTAssertEqual(view.viewModel.searchingText, "suggestion") + XCTAssertEqual(view.viewModel.contentState, .searching) + } +} + +// MARK: - Mocks + +private class SearchOnMapViewMock: SearchOnMapView { + var viewModel: SearchOnMap.ViewModel = .initial + var scrollViewDelegate: (any SearchOnMapScrollViewDelegate)? + func render(_ viewModel: SearchOnMap.ViewModel) { + self.viewModel = viewModel + } +} + +private class SearchManagerMock: SearchManager { + static var observers = ListenerContainer<MWMSearchObserver>() + static var results = SearchOnMap.SearchResults.empty { + didSet { + observers.forEach { observer in + observer.onSearchCompleted?() + } + } + } + + static func add(_ observer: any MWMSearchObserver) { + self.observers.addListener(observer) + } + + static func remove(_ observer: any MWMSearchObserver) { + self.observers.removeListener(observer) + } + + static func saveQuery(_ query: String, forInputLocale inputLocale: String) {} + static func searchQuery(_ query: String, forInputLocale inputLocale: String, withCategory isCategory: Bool) {} + static func showResult(at index: UInt) {} + static func showEverywhereSearchResultsOnMap() {} + static func showViewportSearchResultsOnMap() {} + static func clear() {} + static func getResults() -> [SearchResult] { results.results } +} + +private extension SearchResult { + static func stubResults() -> SearchOnMap.SearchResults { + SearchOnMap.SearchResults([ + SearchResult(), + SearchResult(), + SearchResult() + ]) + } +} diff --git a/iphone/Maps/UI/Search/SearchOnMap/PlaceholderView.swift b/iphone/Maps/UI/Search/SearchOnMap/PlaceholderView.swift new file mode 100644 index 0000000000..9bf57b97c3 --- /dev/null +++ b/iphone/Maps/UI/Search/SearchOnMap/PlaceholderView.swift @@ -0,0 +1,122 @@ +final class PlaceholderView: UIView { + + private let activityIndicator: UIActivityIndicatorView? + private let titleLabel = UILabel() + private let subtitleLabel = UILabel() + private let stackView = UIStackView() + private var keyboardHeight: CGFloat = 0 + private var centerYConstraint: NSLayoutConstraint! + private var containerModalYTranslation: CGFloat = 0 + private let minOffsetFromTheKeyboardTop: CGFloat = 20 + private let maxOffsetFromTheTop: CGFloat = 100 + + init(title: String? = nil, subtitle: String? = nil, hasActivityIndicator: Bool = false) { + self.activityIndicator = hasActivityIndicator ? UIActivityIndicatorView() : nil + super.init(frame: .zero) + setupView(title: title, subtitle: subtitle) + layoutView() + setupKeyboardObservers() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + private func setupKeyboardObservers() { + NotificationCenter.default.addObserver(self, + selector: #selector(keyboardWillShow(_:)), + name: UIResponder.keyboardWillShowNotification, + object: nil) + NotificationCenter.default.addObserver(self, + selector: #selector(keyboardWillHide(_:)), + name: UIResponder.keyboardWillHideNotification, + object: nil) + } + + @objc private func keyboardWillShow(_ notification: Notification) { + if let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect { + keyboardHeight = keyboardFrame.height + reloadConstraints() + } + } + + @objc private func keyboardWillHide(_ notification: Notification) { + keyboardHeight = 0 + reloadConstraints() + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + if traitCollection.verticalSizeClass != previousTraitCollection?.verticalSizeClass { + reloadConstraints() + } + } + + private func setupView(title: String?, subtitle: String?) { + if let activityIndicator = activityIndicator { + activityIndicator.hidesWhenStopped = true + activityIndicator.startAnimating() + if #available(iOS 13.0, *) { + activityIndicator.style = .medium + } else { + activityIndicator.style = .gray + } + } + + titleLabel.text = title + titleLabel.setFontStyle(.medium16, color: .blackPrimary) + titleLabel.textAlignment = .center + + subtitleLabel.text = subtitle + subtitleLabel.setFontStyle(.regular14, color: .blackSecondary) + subtitleLabel.textAlignment = .center + subtitleLabel.isHidden = subtitle == nil + subtitleLabel.numberOfLines = 2 + + stackView.axis = .vertical + stackView.alignment = .center + stackView.spacing = 8 + } + + private func layoutView() { + if let activityIndicator = activityIndicator { + stackView.addArrangedSubview(activityIndicator) + } + if let title = titleLabel.text, !title.isEmpty { + stackView.addArrangedSubview(titleLabel) + } + if let subtitle = subtitleLabel.text, !subtitle.isEmpty { + stackView.addArrangedSubview(subtitleLabel) + } + + addSubview(stackView) + stackView.translatesAutoresizingMaskIntoConstraints = false + + centerYConstraint = stackView.centerYAnchor.constraint(equalTo: centerYAnchor) + NSLayoutConstraint.activate([ + stackView.centerXAnchor.constraint(equalTo: centerXAnchor), + stackView.widthAnchor.constraint(lessThanOrEqualTo: widthAnchor, multiplier: 0.8), + centerYConstraint + ]) + } + + private func reloadConstraints() { + let offset = keyboardHeight > 0 ? max(bounds.height / 2 - keyboardHeight, minOffsetFromTheKeyboardTop + stackView.frame.height) : containerModalYTranslation / 2 + let maxOffset = bounds.height / 2 - maxOffsetFromTheTop + centerYConstraint.constant = -min(offset, maxOffset) + layoutIfNeeded() + } +} + +// MARK: - ModallyPresentedViewController +extension PlaceholderView: ModallyPresentedViewController { + func translationYDidUpdate(_ translationY: CGFloat) { + self.containerModalYTranslation = translationY + reloadConstraints() + } +} diff --git a/iphone/Maps/UI/Search/SearchOnMap/Presentation/MapPassthroughView.swift b/iphone/Maps/UI/Search/SearchOnMap/Presentation/MapPassthroughView.swift new file mode 100644 index 0000000000..7d60beafb6 --- /dev/null +++ b/iphone/Maps/UI/Search/SearchOnMap/Presentation/MapPassthroughView.swift @@ -0,0 +1,30 @@ +/// 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 + } +} diff --git a/iphone/Maps/UI/Search/SearchOnMap/Presentation/ModalScreenPresentationStep.swift b/iphone/Maps/UI/Search/SearchOnMap/Presentation/ModalScreenPresentationStep.swift new file mode 100644 index 0000000000..e6196ec034 --- /dev/null +++ b/iphone/Maps/UI/Search/SearchOnMap/Presentation/ModalScreenPresentationStep.swift @@ -0,0 +1,94 @@ +enum ModalScreenPresentationStep { + case fullScreen + case halfScreen + case compact + case hidden +} + +extension ModalScreenPresentationStep { + private enum Constants { + static let iPadWidth: CGFloat = 350 + static let compactHeightOffset: CGFloat = 120 + static let fullScreenHeightFactorPortrait: CGFloat = 0.1 + static let halfScreenHeightFactorPortrait: CGFloat = 0.55 + static let landscapeTopInset: CGFloat = 10 + } + + var upper: ModalScreenPresentationStep { + switch self { + case .fullScreen: + return .fullScreen + case .halfScreen: + return .fullScreen + case .compact: + return .halfScreen + case .hidden: + return .compact + } + } + + var lower: ModalScreenPresentationStep { + switch self { + case .fullScreen: + return .halfScreen + case .halfScreen: + return .compact + case .compact: + return .compact + case .hidden: + return .hidden + } + } + + var first: ModalScreenPresentationStep { + .fullScreen + } + + var last: ModalScreenPresentationStep { + .compact + } + + func frame(for viewController: UIViewController, in containerView: UIView) -> CGRect { + let isIPad = UIDevice.current.userInterfaceIdiom == .pad + let containerSize = containerView.bounds.size + let safeAreaInsets = containerView.safeAreaInsets + var frame = CGRect(origin: .zero, size: containerSize) + + if isIPad { + frame.size.width = Constants.iPadWidth + switch self { + case .hidden: + frame.origin.x = -Constants.iPadWidth + default: + frame.origin.x = .zero + } + return frame + } + + let isPortraitOrientation = viewController.traitCollection.verticalSizeClass == .regular + if isPortraitOrientation { + switch self { + case .fullScreen: + frame.origin.y = containerSize.height * Constants.fullScreenHeightFactorPortrait + case .halfScreen: + frame.origin.y = containerSize.height * Constants.halfScreenHeightFactorPortrait + case .compact: + frame.origin.y = containerSize.height - Constants.compactHeightOffset + case .hidden: + frame.origin.y = containerSize.height + } + } else { + frame.size.width = Constants.iPadWidth + frame.origin.x = safeAreaInsets.left + switch self { + case .fullScreen: + frame.origin.y = Constants.landscapeTopInset + case .halfScreen, .compact: + frame.origin.y = containerSize.height - Constants.compactHeightOffset + case .hidden: + frame.origin.y = containerSize.height + } + } + return frame + } +} diff --git a/iphone/Maps/UI/Search/SearchOnMap/Presentation/SearchOnMapModalPresentationController.swift b/iphone/Maps/UI/Search/SearchOnMap/Presentation/SearchOnMapModalPresentationController.swift new file mode 100644 index 0000000000..c459f8bca3 --- /dev/null +++ b/iphone/Maps/UI/Search/SearchOnMap/Presentation/SearchOnMapModalPresentationController.swift @@ -0,0 +1,226 @@ +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 + } +} diff --git a/iphone/Maps/UI/Search/SearchOnMap/Presentation/SearchOnMapModalTransitionManager.swift b/iphone/Maps/UI/Search/SearchOnMap/Presentation/SearchOnMapModalTransitionManager.swift new file mode 100644 index 0000000000..335b7d4d76 --- /dev/null +++ b/iphone/Maps/UI/Search/SearchOnMap/Presentation/SearchOnMapModalTransitionManager.swift @@ -0,0 +1,21 @@ +@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 + } +} diff --git a/iphone/Maps/UI/Search/SearchOnMap/Presentation/SideMenuDismissalAnimator.swift b/iphone/Maps/UI/Search/SearchOnMap/Presentation/SideMenuDismissalAnimator.swift new file mode 100644 index 0000000000..35515734d7 --- /dev/null +++ b/iphone/Maps/UI/Search/SearchOnMap/Presentation/SideMenuDismissalAnimator.swift @@ -0,0 +1,22 @@ +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) + }) + } +} diff --git a/iphone/Maps/UI/Search/SearchOnMap/Presentation/SideMenuPresentationAnimator.swift b/iphone/Maps/UI/Search/SearchOnMap/Presentation/SideMenuPresentationAnimator.swift new file mode 100644 index 0000000000..0bc83210f7 --- /dev/null +++ b/iphone/Maps/UI/Search/SearchOnMap/Presentation/SideMenuPresentationAnimator.swift @@ -0,0 +1,25 @@ +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) + }) + } +} diff --git a/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapHeaderView.swift b/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapHeaderView.swift new file mode 100644 index 0000000000..1dd937c832 --- /dev/null +++ b/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapHeaderView.swift @@ -0,0 +1,113 @@ +protocol SearchOnMapHeaderViewDelegate: UISearchBarDelegate { + func cancelButtonDidTap() +} + +final class SearchOnMapHeaderView: UIView { + weak var delegate: SearchOnMapHeaderViewDelegate? { + didSet { + searchBar.delegate = delegate + } + } + + private enum Constants { + 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) + } + + private let grabberView = UIView() + private let searchBar = UISearchBar() + private let cancelButton = UIButton() + + override init(frame: CGRect) { + super.init(frame: frame) + setupView() + layoutView() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupView() { + setStyle(.searchHeader) + layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] + + setupGrabberView() + setupSearchBar() + setupCancelButton() + } + + private func setupGrabberView() { + grabberView.setStyle(.background) + grabberView.layer.setCorner(radius: Constants.grabberHeight / 2) + iPadSpecific { [weak self] in + self?.grabberView.isHidden = true + } + } + + private func setupSearchBar() { + searchBar.placeholder = L("search") + searchBar.showsCancelButton = false + if #available(iOS 13.0, *) { + searchBar.searchTextField.clearButtonMode = .always + searchBar.returnKeyType = .search + searchBar.searchTextField.enablesReturnKeyAutomatically = true + } + } + + private func setupCancelButton() { + cancelButton.tintColor = .whitePrimaryText() + cancelButton.setStyle(.clearBackground) + cancelButton.setTitle(L("cancel"), for: .normal) + cancelButton.addTarget(self, action: #selector(cancelButtonTapped), for: .touchUpInside) + } + + private func layoutView() { + addSubview(grabberView) + addSubview(searchBar) + addSubview(cancelButton) + + grabberView.translatesAutoresizingMaskIntoConstraints = false + searchBar.translatesAutoresizingMaskIntoConstraints = false + cancelButton.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + grabberView.topAnchor.constraint(equalTo: topAnchor, constant: Constants.grabberTopMargin), + grabberView.centerXAnchor.constraint(equalTo: centerXAnchor), + 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), + + cancelButton.centerYAnchor.constraint(equalTo: searchBar.centerYAnchor), + cancelButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -Constants.cancelButtonInsets.right), + + bottomAnchor.constraint(equalTo: searchBar.bottomAnchor) + ]) + } + + @objc private func cancelButtonTapped() { + delegate?.cancelButtonDidTap() + } + + func setSearchText(_ text: String) { + searchBar.text = text + } + + func setIsSearching(_ isSearching: Bool) { + if isSearching { + searchBar.becomeFirstResponder() + } else if searchBar.isFirstResponder { + searchBar.resignFirstResponder() + } + } + + var searchText: SearchOnMap.SearchText { + SearchOnMap.SearchText(searchBar.text ?? "", locale: searchBar.textInputMode?.primaryLanguage) + } +} diff --git a/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapInteractor.swift b/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapInteractor.swift new file mode 100644 index 0000000000..3bbedc1dbc --- /dev/null +++ b/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapInteractor.swift @@ -0,0 +1,165 @@ +final class SearchOnMapInteractor: NSObject { + + private let presenter: SearchOnMapPresenter + private let searchManager: SearchManager.Type + private let routeManager: MWMRouter.Type + private var isUpdatesDisabled = false + private var showResultsOnMap: Bool = false + + var routingTooltipSearch: SearchOnMapRoutingTooltipSearch = .none + + init(presenter: SearchOnMapPresenter, + searchManager: SearchManager.Type = Search.self, + routeManager: MWMRouter.Type = MWMRouter.self) { + self.presenter = presenter + self.searchManager = searchManager + self.routeManager = routeManager + super.init() + searchManager.add(self) + } + + deinit { + searchManager.remove(self) + } + + func handle(_ event: SearchOnMap.Request) { + let response = resolve(event) + presenter.process(response) + } + + private func resolve(_ event: SearchOnMap.Request) -> SearchOnMap.Response { + switch event { + case .openSearch: + return .showHistoryAndCategory + case .hideSearch: + return .setSearchScreenHidden(true) + case .didStartDraggingSearch: + return .setIsTyping(false) + case .didStartTyping: + return .setIsTyping(true) + case .didType(let searchText): + return processTypedText(searchText) + case .clearButtonDidTap: + return processClearButtonDidTap() + case .didSelectText(let searchText, let isCategory): + return processSelectedText(searchText, isCategory: isCategory) + case .searchButtonDidTap(let searchText): + return processSearchButtonDidTap(searchText) + case .didSelectResult(let result, let index, let searchText): + return processSelectedResult(result, index: index, searchText: searchText) + case .didSelectPlaceOnMap: + return isIPad ? .none : .setSearchScreenHidden(true) + case .didDeselectPlaceOnMap: + return deselectPlaceOnMap() + case .didStartDraggingMap: + return .setSearchScreenCompact + case .didUpdatePresentationStep(let step): + return .updatePresentationStep(step) + case .closeSearch: + return closeSearch() + } + } + + private func processClearButtonDidTap() -> SearchOnMap.Response { + isUpdatesDisabled = true + searchManager.clear() + return .clearSearch + } + + private func processSearchButtonDidTap(_ searchText: SearchOnMap.SearchText) -> SearchOnMap.Response { + searchManager.saveQuery(searchText.text, + forInputLocale: searchText.locale) + showResultsOnMap = true + return .showOnTheMap + } + + private func processTypedText(_ searchText: SearchOnMap.SearchText) -> SearchOnMap.Response { + isUpdatesDisabled = false + showResultsOnMap = true + searchManager.searchQuery(searchText.text, + forInputLocale: searchText.locale, + withCategory: false) + return .startSearching + } + + private func processSelectedText(_ searchText: SearchOnMap.SearchText, isCategory: Bool) -> SearchOnMap.Response { + isUpdatesDisabled = false + searchManager.saveQuery(searchText.text, + forInputLocale: searchText.locale) + searchManager.searchQuery(searchText.text, + forInputLocale: searchText.locale, + withCategory: isCategory) + showResultsOnMap = true + return .selectText(searchText.text) + } + + private func processSelectedResult(_ result: SearchResult, index: Int, searchText: SearchOnMap.SearchText) -> SearchOnMap.Response { + switch result.itemType { + case .regular: + searchManager.saveQuery(searchText.text, + forInputLocale:searchText.locale) + switch routingTooltipSearch { + case .none: + searchManager.showResult(at: UInt(index)) + case .start: + let point = MWMRoutePoint(cgPoint: result.point, + title: result.titleText, + subtitle: result.addressText, + type: .start, + intermediateIndex: 0) + routeManager.build(from: point, bestRouter: false) + case .finish: + let point = MWMRoutePoint(cgPoint: result.point, + title: result.titleText, + subtitle: result.addressText, + type: .finish, + intermediateIndex: 0) + routeManager.build(to: point, bestRouter: false) + @unknown default: + fatalError("Unsupported routingTooltipSearch") + } + return isIPad ? .none : .setSearchScreenHidden(true) + case .suggestion: + searchManager.searchQuery(result.suggestion, + forInputLocale: searchText.locale, + withCategory: result.isPureSuggest) + return .selectText(result.suggestion) + @unknown default: + fatalError("Unsupported result type") + } + } + + private func deselectPlaceOnMap() -> SearchOnMap.Response { + routingTooltipSearch = .none + searchManager.showViewportSearchResultsOnMap() + return .setSearchScreenHidden(false) + } + + private func closeSearch() -> SearchOnMap.Response { + routingTooltipSearch = .none + isUpdatesDisabled = true + showResultsOnMap = false + searchManager.clear() + return .close + } +} + +// MARK: - MWMSearchObserver +extension SearchOnMapInteractor: MWMSearchObserver { + func onSearchCompleted() { + guard !isUpdatesDisabled else { return } + let results = searchManager.getResults() + if showResultsOnMap && !results.isEmpty { + searchManager.showEverywhereSearchResultsOnMap() + showResultsOnMap = false + } + presenter.process(.showResults(SearchOnMap.SearchResults(results), isSearchCompleted: true)) + } + + func onSearchResultsUpdated() { + guard !isUpdatesDisabled else { return } + let results = searchManager.getResults() + guard !results.isEmpty else { return } + presenter.process(.showResults(SearchOnMap.SearchResults(results), isSearchCompleted: false)) + } +} diff --git a/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapManager.swift b/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapManager.swift new file mode 100644 index 0000000000..982e7555c3 --- /dev/null +++ b/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapManager.swift @@ -0,0 +1,96 @@ +@objc +enum SearchOnMapState: Int { + case searching + case hidden + case closed +} + +@objc +enum SearchOnMapRoutingTooltipSearch: Int { + case none + case start + case finish +} + +@objc +protocol SearchOnMapManagerObserver: AnyObject { + func searchManager(didChangeState state: SearchOnMapState) +} + +@objcMembers +final class SearchOnMapManager: NSObject { + private let navigationController: UINavigationController + private weak var interactor: SearchOnMapInteractor? + private let observers = ListenerContainer<SearchOnMapManagerObserver>() + + // MARK: - Public properties + weak var viewController: UIViewController? + var isSearching: Bool { viewController != nil } + + init(navigationController: UINavigationController = MapViewController.shared()!.navigationController!) { + self.navigationController = navigationController + } + + // MARK: - Public methods + func startSearching(isRouting: Bool) { + if viewController != nil { + interactor?.handle(.openSearch) + return + } + FrameworkHelper.deactivateMapSelection() + let viewController = buildViewController(isRouting: isRouting) + self.viewController = viewController + self.interactor = viewController.interactor + navigationController.present(viewController, animated: true) + } + + func hide() { + interactor?.handle(.hideSearch) + } + + func close() { + interactor?.handle(.closeSearch) + } + + func setRoutingTooltip(_ tooltip: SearchOnMapRoutingTooltipSearch) { + interactor?.routingTooltipSearch = tooltip + } + + func setPlaceOnMapSelected(_ isSelected: Bool) { + interactor?.handle(isSelected ? .didSelectPlaceOnMap : .didDeselectPlaceOnMap) + } + + func setMapIsDragging() { + interactor?.handle(.didStartDraggingMap) + } + + func searchText(_ text: String, locale: String, isCategory: Bool) { + let searchText = SearchOnMap.SearchText(text, locale: locale) + interactor?.handle(.didSelectText(searchText, isCategory: isCategory)) + } + + func addObserver(_ observer: SearchOnMapManagerObserver) { + observers.addListener(observer) + } + + func removeObserver(_ observer: SearchOnMapManagerObserver) { + 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) } + }) + let interactor = SearchOnMapInteractor(presenter: presenter) + let viewController = SearchOnMapViewController(interactor: interactor) + presenter.view = viewController + viewController.modalPresentationStyle = .custom + viewController.transitioningDelegate = transitioningManager + return viewController + } +} diff --git a/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapModels.swift b/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapModels.swift new file mode 100644 index 0000000000..2f9db11c18 --- /dev/null +++ b/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapModels.swift @@ -0,0 +1,94 @@ +enum SearchOnMap { + struct ViewModel: Equatable { + enum ContentState: Equatable { + case historyAndCategory + case results(SearchResults) + case noResults + case searching + } + + var isTyping: Bool + var skipSuggestions: Bool + var searchingText: String? + var contentState: ContentState + var presentationStep: ModalScreenPresentationStep + } + + struct SearchResults: Equatable { + let results: [SearchResult] + let hasPartialMatch: Bool + let isEmpty: Bool + let count: Int + let suggestionsCount: Int + + init(_ results: [SearchResult]) { + self.results = results + self.hasPartialMatch = !results.allSatisfy { $0.highlightRanges.isEmpty } + self.isEmpty = results.isEmpty + self.count = results.count + self.suggestionsCount = results.filter { $0.itemType == .suggestion }.count + } + } + + struct SearchText { + let text: String + let locale: String + + init(_ text: String, locale: String? = nil) { + self.text = text + self.locale = locale ?? AppInfo.shared().languageId + } + } + + enum Request { + case openSearch + case hideSearch + case closeSearch + case didStartDraggingSearch + case didStartDraggingMap + case didStartTyping + case didType(SearchText) + case didSelectText(SearchText, isCategory: Bool) + case didSelectResult(SearchResult, atIndex: Int, withSearchText: SearchText) + case searchButtonDidTap(SearchText) + case clearButtonDidTap + case didSelectPlaceOnMap + case didDeselectPlaceOnMap + case didUpdatePresentationStep(ModalScreenPresentationStep) + } + + enum Response: Equatable { + case startSearching + case showOnTheMap + case setIsTyping(Bool) + case showHistoryAndCategory + case showResults(SearchResults, isSearchCompleted: Bool = false) + case selectText(String?) + case clearSearch + case setSearchScreenHidden(Bool) + case setSearchScreenCompact + case updatePresentationStep(ModalScreenPresentationStep) + case close + case none + } +} + +extension SearchOnMap.SearchResults { + static let empty = SearchOnMap.SearchResults([]) + + subscript(index: Int) -> SearchResult { + results[index] + } + + mutating func skipSuggestions() { + self = SearchOnMap.SearchResults(results.filter { $0.itemType != .suggestion }) + } +} + +extension SearchOnMap.ViewModel { + static let initial = SearchOnMap.ViewModel(isTyping: false, + skipSuggestions: false, + searchingText: nil, + contentState: .historyAndCategory, + presentationStep: .fullScreen) +} diff --git a/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapPresenter.swift b/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapPresenter.swift new file mode 100644 index 0000000000..aa422b027d --- /dev/null +++ b/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapPresenter.swift @@ -0,0 +1,117 @@ +final class SearchOnMapPresenter { + typealias Response = SearchOnMap.Response + typealias ViewModel = SearchOnMap.ViewModel + + weak var view: SearchOnMapView? + weak var presentationView: SearchOnMapModalPresentationView? { transitionManager.presentationController } + + private var searchState: SearchOnMapState = .searching { + didSet { + guard searchState != oldValue else { return } + didChangeState?(searchState) + } + } + + 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 + self.isRouting = isRouting + self.didChangeState = didChangeState + didChangeState?(searchState) + } + + func process(_ response: SearchOnMap.Response) { + guard response != .none else { return } + + if response == .close { + searchState = .closed + presentationView?.close() + return + } + + let showSearch = response == .setSearchScreenHidden(false) || response == .showHistoryAndCategory + guard viewModel.presentationStep != .hidden || showSearch else { + return + } + + let newViewModel = resolve(action: response, with: viewModel) + if viewModel != newViewModel { + viewModel = newViewModel + view?.render(newViewModel) + searchState = newViewModel.presentationStep.searchState + presentationView?.setPresentationStep(newViewModel.presentationStep) + } + } + + private func resolve(action: Response, with previousViewModel: ViewModel) -> ViewModel { + var viewModel = previousViewModel + viewModel.searchingText = nil // should not be nil only when the text is passed to the search field + + switch action { + case .startSearching: + viewModel.isTyping = true + viewModel.skipSuggestions = false + viewModel.contentState = .searching + case .showOnTheMap: + viewModel.isTyping = false + viewModel.skipSuggestions = true + viewModel.presentationStep = isRouting ? .hidden : .halfScreen + if case .results(var results) = viewModel.contentState, !results.isEmpty { + results.skipSuggestions() + viewModel.contentState = .results(results) + } + case .setIsTyping(let isSearching): + viewModel.isTyping = isSearching + if isSearching { + viewModel.presentationStep = .fullScreen + } + case .showHistoryAndCategory: + viewModel.isTyping = true + viewModel.contentState = .historyAndCategory + viewModel.presentationStep = .fullScreen + case .showResults(var searchResults, let isSearchCompleted): + if (viewModel.skipSuggestions) { + searchResults.skipSuggestions() + } + viewModel.contentState = searchResults.isEmpty && isSearchCompleted ? .noResults : .results(searchResults) + case .selectText(let text): + viewModel.isTyping = false + viewModel.skipSuggestions = false + viewModel.searchingText = text + viewModel.contentState = .searching + viewModel.presentationStep = isRouting ? .hidden : .halfScreen + case .clearSearch: + viewModel.searchingText = "" + viewModel.isTyping = true + viewModel.skipSuggestions = false + viewModel.contentState = .historyAndCategory + viewModel.presentationStep = .fullScreen + case .setSearchScreenHidden(let isHidden): + viewModel.isTyping = false + viewModel.presentationStep = isHidden ? .hidden : (isRouting ? .fullScreen : .halfScreen) + case .setSearchScreenCompact: + viewModel.isTyping = false + viewModel.presentationStep = .compact + case .updatePresentationStep(let step): + viewModel.presentationStep = step + case .close, .none: + break + } + return viewModel + } +} + +private extension ModalScreenPresentationStep { + var searchState: SearchOnMapState { + switch self { + case .fullScreen, .halfScreen, .compact: + return .searching + case .hidden: + return .hidden + } + } +} diff --git a/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapViewController.swift b/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapViewController.swift new file mode 100644 index 0000000000..0886e5126d --- /dev/null +++ b/iphone/Maps/UI/Search/SearchOnMap/SearchOnMapViewController.swift @@ -0,0 +1,362 @@ +protocol SearchOnMapView: AnyObject { + var scrollViewDelegate: SearchOnMapScrollViewDelegate? { get set } + + func render(_ viewModel: SearchOnMap.ViewModel) +} + +@objc +protocol SearchOnMapScrollViewDelegate: AnyObject { + func scrollViewDidScroll(_ scrollView: UIScrollView) +} + +final class SearchOnMapViewController: UIViewController { + typealias ViewModel = SearchOnMap.ViewModel + typealias ContentState = SearchOnMap.ViewModel.ContentState + 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 + } + + let interactor: SearchOnMapInteractor + weak var scrollViewDelegate: SearchOnMapScrollViewDelegate? + + private var searchResults = SearchOnMap.SearchResults([]) + + // MARK: - UI Elements + private let headerView = SearchOnMapHeaderView() + private let containerView = 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")) + + // MARK: - Init + init(interactor: SearchOnMapInteractor) { + self.interactor = interactor + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + // MARK: - Lifecycle + override func viewDidLoad() { + super.viewDidLoad() + setupViews() + layoutViews() + interactor.handle(.openSearch) + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + headerView.setIsSearching(false) + } + + // MARK: - Private methods + private func setupViews() { + view.setStyle(.background) + setupTapGestureRecognizer() + setupHeaderView() + setupContainerView() + setupResultsTableView() + setupHistoryAndCategoryTabView() + setupResultsTableView() + setupFiltersCollectionView() + } + + private func setupTapGestureRecognizer() { + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapOutside)) + tapGesture.cancelsTouchesInView = false + view.addGestureRecognizer(tapGesture) + } + + private func setupHeaderView() { + headerView.delegate = self + } + + private func setupContainerView() { + containerView.setStyle(.background) + } + + private func setupResultsTableView() { + resultsTableView.setStyle(.background) + resultsTableView.estimatedRowHeight = Constants.estimatedRowHeight + resultsTableView.rowHeight = UITableView.automaticDimension + resultsTableView.registerNib(cellClass: SearchSuggestionCell.self) + resultsTableView.registerNib(cellClass: SearchCommonCell.self) + resultsTableView.dataSource = self + resultsTableView.delegate = self + resultsTableView.keyboardDismissMode = .onDrag + } + + private func setupHistoryAndCategoryTabView() { + 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) + headerView.translatesAutoresizingMaskIntoConstraints = false + containerView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + headerView.topAnchor.constraint(equalTo: view.topAnchor), + headerView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + headerView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + + containerView.topAnchor.constraint(equalTo: headerView.bottomAnchor), + containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + containerView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + ]) + + layoutResultsView() + layoutHistoryAndCategoryTabView() + layoutSearchNoResultsView() + layoutSearchingView() + } + + private func layoutResultsView() { + containerView.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) + ]) + } + + private func layoutHistoryAndCategoryTabView() { + containerView.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) + ]) + } + + private func layoutSearchNoResultsView() { + searchNoResultsView.translatesAutoresizingMaskIntoConstraints = false + containerView.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) + ]) + } + + private func layoutSearchingView() { + containerView.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) + ]) + } + + // MARK: - Handle Button Actions + @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) { + switch content { + case .historyAndCategory: + historyAndCategoryTabViewController.reloadSearchHistory() + case let .results(results): + if searchResults != results { + searchResults = results + resultsTableView.reloadData() + } + case .noResults: + searchResults = .empty + resultsTableView.reloadData() + case .searching: + break + } + showView(viewToShow(for: content)) + } + + private func viewToShow(for content: ContentState) -> UIView { + switch content { + case .historyAndCategory: + return historyAndCategoryTabViewController.view + case .results: + return resultsTableView + case .noResults: + return searchNoResultsView + case .searching: + return searchingActivityView + } + } + + private func showView(_ view: UIView) { + let viewsToHide: [UIView] = [resultsTableView, + 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 + } + }) + } + + private func setIsSearching(_ isSearching: Bool) { + headerView.setIsSearching(isSearching) + } + + private func replaceSearchText(with text: String) { + headerView.setSearchText(text) + } +} + +// MARK: - Public methods +extension SearchOnMapViewController: SearchOnMapView { + func render(_ viewModel: ViewModel) { + setContent(viewModel.contentState) + setIsSearching(viewModel.isTyping) + if let searchingText = viewModel.searchingText { + replaceSearchText(with: searchingText) + } + } +} + +// MARK: - ModallyPresentedViewController +extension SearchOnMapViewController: ModallyPresentedViewController { + func translationYDidUpdate(_ translationY: CGFloat) { + self.containerModalYTranslation = translationY + resultsTableView.contentInset.bottom = translationY + historyAndCategoryTabViewController.translationYDidUpdate(translationY) + searchNoResultsView.translationYDidUpdate(translationY) + searchingActivityView.translationYDidUpdate(translationY) + } +} + +// MARK: - UITableViewDataSource +extension SearchOnMapViewController: UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + searchResults.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let result = searchResults[indexPath.row] + switch result.itemType { + case .regular: + let cell = tableView.dequeueReusableCell(cell: SearchCommonCell.self, indexPath: indexPath) + cell.configure(with: result, isPartialMatching: searchResults.hasPartialMatch) + return cell + case .suggestion: + let cell = tableView.dequeueReusableCell(cell: SearchSuggestionCell.self, indexPath: indexPath) + cell.configure(with: result, isPartialMatching: true) + cell.isLastCell = indexPath.row == searchResults.suggestionsCount - 1 + return cell + @unknown default: + fatalError("Unknown item type") + } + } +} + +// MARK: - UITableViewDelegate +extension SearchOnMapViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let result = searchResults[indexPath.row] + interactor.handle(.didSelectResult(result, atIndex: indexPath.row, 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 + } +} + +// MARK: - SearchOnMapHeaderViewDelegate +extension SearchOnMapViewController: SearchOnMapHeaderViewDelegate { + func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { + interactor.handle(.didStartTyping) + } + + func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { + guard !searchText.isEmpty else { + interactor.handle(.clearButtonDidTap) + return + } + 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))) + } + + func cancelButtonDidTap() { + interactor.handle(.closeSearch) + } +} + +// MARK: - SearchTabViewControllerDelegate +extension SearchOnMapViewController: SearchTabViewControllerDelegate { + func searchTabController(_ viewController: SearchTabViewController, didSearch text: String, withCategory: Bool) { + interactor.handle(.didSelectText(SearchText(text, locale: nil), isCategory: withCategory)) + } +} + diff --git a/iphone/Maps/UI/Search/Tabs/CategoriesTab/SearchCategoriesViewController.swift b/iphone/Maps/UI/Search/Tabs/CategoriesTab/SearchCategoriesViewController.swift index e9ab1f5998..745d81a5bb 100644 --- a/iphone/Maps/UI/Search/Tabs/CategoriesTab/SearchCategoriesViewController.swift +++ b/iphone/Maps/UI/Search/Tabs/CategoriesTab/SearchCategoriesViewController.swift @@ -1,4 +1,4 @@ -protocol SearchCategoriesViewControllerDelegate: AnyObject { +protocol SearchCategoriesViewControllerDelegate: SearchOnMapScrollViewDelegate { func categoriesViewController(_ viewController: SearchCategoriesViewController, didSelect category: String) } @@ -41,8 +41,19 @@ final class SearchCategoriesViewController: MWMTableViewController { tableView.deselectRow(at: indexPath, animated: true) } + override func scrollViewDidScroll(_ scrollView: UIScrollView) { + delegate?.scrollViewDidScroll(scrollView) + } + func category(at indexPath: IndexPath) -> String { let index = indexPath.row return categories[index] } } + +extension SearchCategoriesViewController: ModallyPresentedViewController { + func translationYDidUpdate(_ translationY: CGFloat) { + guard isViewLoaded else { return } + tableView.contentInset.bottom = translationY + view.safeAreaInsets.bottom + } +} diff --git a/iphone/Maps/UI/Search/Tabs/HistoryTab/SearchHistoryViewController.swift b/iphone/Maps/UI/Search/Tabs/HistoryTab/SearchHistoryViewController.swift index 01eba9fcd2..be08ea1c38 100644 --- a/iphone/Maps/UI/Search/Tabs/HistoryTab/SearchHistoryViewController.swift +++ b/iphone/Maps/UI/Search/Tabs/HistoryTab/SearchHistoryViewController.swift @@ -1,59 +1,97 @@ -protocol SearchHistoryViewControllerDelegate: AnyObject { +protocol SearchHistoryViewControllerDelegate: SearchOnMapScrollViewDelegate { func searchHistoryViewController(_ viewController: SearchHistoryViewController, didSelect query: String) } final class SearchHistoryViewController: MWMViewController { private weak var delegate: SearchHistoryViewControllerDelegate? - private var lastQueries: [String] + private var lastQueries: [String] = [] private let frameworkHelper: MWMSearchFrameworkHelper - private static let clearCellIdentifier = "SearchHistoryViewController_clearCellIdentifier" - - @IBOutlet private var tableView: UITableView! - @IBOutlet private weak var noResultsViewContainer: UIView! - + private let emptyHistoryView = PlaceholderView(title: L("search_history_title"), + subtitle: L("search_history_text")) + + private let tableView = UITableView() + + // MARK: - Init init(frameworkHelper: MWMSearchFrameworkHelper, delegate: SearchHistoryViewControllerDelegate?) { self.delegate = delegate - self.lastQueries = frameworkHelper.lastSearchQueries() self.frameworkHelper = frameworkHelper super.init(nibName: nil, bundle: nil) } - + + @available(*, unavailable) required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + + // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() - - if frameworkHelper.isSearchHistoryEmpty() { - showNoResultsView() - } else { - tableView.register(cell: SearchHistoryCell.self) - } + setupTableView() + setupNoResultsView() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + reload() + } + + // MARK: - Private methods + private func setupTableView() { + tableView.setStyle(.background) + tableView.register(cell: SearchHistoryCell.self) tableView.keyboardDismissMode = .onDrag + tableView.delegate = self + tableView.dataSource = self + tableView.tableFooterView = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 1)) + + view.addSubview(tableView) + tableView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + tableView.topAnchor.constraint(equalTo: view.topAnchor), + tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor) + ]) } - - func showNoResultsView() { - guard let noResultsView = MWMSearchNoResults.view(with: nil, - title: L("search_history_title"), - text: L("search_history_text")) else { - assertionFailure() - return + + private func setupNoResultsView() { + view.addSubview(emptyHistoryView) + emptyHistoryView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + emptyHistoryView.topAnchor.constraint(equalTo: view.topAnchor), + emptyHistoryView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + emptyHistoryView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + emptyHistoryView.bottomAnchor.constraint(equalTo: view.bottomAnchor) + ]) + } + + private func showEmptyHistoryView(_ isVisible: Bool = true, animated: Bool = true) { + UIView.transition(with: emptyHistoryView, + duration: animated ? kDefaultAnimationDuration : 0, + options: [.transitionCrossDissolve, .curveEaseInOut]) { + self.emptyHistoryView.alpha = isVisible ? 1.0 : 0.0 + self.emptyHistoryView.isHidden = !isVisible } - noResultsViewContainer.addSubview(noResultsView) - tableView.isHidden = true } - func clearSearchHistory() { + private func clearSearchHistory() { frameworkHelper.clearSearchHistory() - lastQueries = [] + reload() + } + + // MARK: - Public methods + func reload() { + guard isViewLoaded else { return } + lastQueries = frameworkHelper.lastSearchQueries() + showEmptyHistoryView(lastQueries.isEmpty ? true : false) + tableView.reloadData() } } extension SearchHistoryViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return frameworkHelper.isSearchHistoryEmpty() ? 0 : lastQueries.count + 1 + return lastQueries.isEmpty ? 0 : lastQueries.count + 1 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { @@ -71,16 +109,22 @@ extension SearchHistoryViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if indexPath.row == lastQueries.count { clearSearchHistory() - UIView.animate(withDuration: kDefaultAnimationDuration, - animations: { - tableView.alpha = 0.0 - }) { _ in - self.showNoResultsView() - } } else { let query = lastQueries[indexPath.row] delegate?.searchHistoryViewController(self, didSelect: query) } tableView.deselectRow(at: indexPath, animated: true) } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + delegate?.scrollViewDidScroll(scrollView) + } +} + +extension SearchHistoryViewController: ModallyPresentedViewController { + func translationYDidUpdate(_ translationY: CGFloat) { + guard isViewLoaded else { return } + tableView.contentInset.bottom = translationY + emptyHistoryView.translationYDidUpdate(translationY) + } } diff --git a/iphone/Maps/UI/Search/Tabs/HistoryTab/SearchHistoryViewController.xib b/iphone/Maps/UI/Search/Tabs/HistoryTab/SearchHistoryViewController.xib deleted file mode 100644 index 952a4e3cdb..0000000000 --- a/iphone/Maps/UI/Search/Tabs/HistoryTab/SearchHistoryViewController.xib +++ /dev/null @@ -1,56 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> - <device id="retina4_7" orientation="portrait" appearance="light"/> - <dependencies> - <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/> - <capability name="Safe area layout guides" minToolsVersion="9.0"/> - <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> - </dependencies> - <objects> - <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="SearchHistoryViewController" customModule="Organic_Maps" customModuleProvider="target"> - <connections> - <outlet property="noResultsViewContainer" destination="bcr-zs-NMw" id="zpc-sP-fbF"/> - <outlet property="tableView" destination="cDq-G7-5cR" id="Qo8-a6-Q6V"/> - <outlet property="view" destination="iN0-l3-epB" id="Ybt-gX-7O4"/> - </connections> - </placeholder> - <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> - <view contentMode="scaleToFill" id="iN0-l3-epB"> - <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <subviews> - <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="cDq-G7-5cR"> - <rect key="frame" x="0.0" y="20" width="375" height="647"/> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <connections> - <outlet property="dataSource" destination="-1" id="XhM-2x-4kQ"/> - <outlet property="delegate" destination="-1" id="sDX-YJ-iGy"/> - </connections> - </tableView> - <view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bcr-zs-NMw"> - <rect key="frame" x="27.5" y="183.5" width="320" height="320"/> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstAttribute="height" constant="320" id="07E-Xc-KMw"/> - <constraint firstAttribute="width" constant="320" id="sCQ-Q9-Pdw"/> - </constraints> - </view> - </subviews> - <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/> - <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - <constraints> - <constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="cDq-G7-5cR" secondAttribute="bottom" id="9QA-Kd-FQb"/> - <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="cDq-G7-5cR" secondAttribute="trailing" id="T3b-23-qG6"/> - <constraint firstItem="bcr-zs-NMw" firstAttribute="centerY" secondItem="vUN-kp-3ea" secondAttribute="centerY" id="Y7z-3L-jl5"/> - <constraint firstItem="cDq-G7-5cR" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" id="ezl-g4-mlD"/> - <constraint firstItem="cDq-G7-5cR" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="ot8-FU-S6n"/> - <constraint firstItem="bcr-zs-NMw" firstAttribute="centerX" secondItem="vUN-kp-3ea" secondAttribute="centerX" id="uMb-A3-NrS"/> - </constraints> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/> - </userDefinedRuntimeAttributes> - <point key="canvasLocation" x="53.600000000000001" y="48.125937031484263"/> - </view> - </objects> -</document> diff --git a/iphone/Maps/UI/Search/Tabs/SearchTabViewController.swift b/iphone/Maps/UI/Search/Tabs/SearchTabViewController.swift index 205d0a793a..e8ef5bc720 100644 --- a/iphone/Maps/UI/Search/Tabs/SearchTabViewController.swift +++ b/iphone/Maps/UI/Search/Tabs/SearchTabViewController.swift @@ -1,5 +1,5 @@ @objc(MWMSearchTabViewControllerDelegate) -protocol SearchTabViewControllerDelegate: AnyObject { +protocol SearchTabViewControllerDelegate: SearchOnMapScrollViewDelegate { func searchTabController(_ viewController: SearchTabViewController, didSearch: String, withCategory: Bool) } @@ -47,6 +47,22 @@ final class SearchTabViewController: TabViewController { super.viewDidDisappear(animated) activeTab = SearchActiveTab.init(rawValue: tabView.selectedIndex ?? 0) ?? .categories } + + func reloadSearchHistory() { + (viewControllers[SearchActiveTab.history.rawValue] as? SearchHistoryViewController)?.reload() + } +} + +extension SearchTabViewController: ModallyPresentedViewController { + func translationYDidUpdate(_ translationY: CGFloat) { + viewControllers.forEach { ($0 as? ModallyPresentedViewController)?.translationYDidUpdate(translationY) } + } +} + +extension SearchTabViewController: SearchOnMapScrollViewDelegate { + func scrollViewDidScroll(_ scrollView: UIScrollView) { + delegate?.scrollViewDidScroll(scrollView) + } } extension SearchTabViewController: SearchCategoriesViewControllerDelegate { -- 2.45.3 From d56af9e53d2544b62e4bde5a76ba2e9e922476c5 Mon Sep 17 00:00:00 2001 From: Kiryl Kaveryn <kirylkaveryn@gmail.com> Date: Tue, 28 Jan 2025 14:57:22 +0400 Subject: [PATCH 10/12] [ios] remove `setSearchOnMap` from the MWMSearch because the new search should always works as iPad Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com> --- .../Views/MWMNavigationInfoView.mm | 1 - iphone/Maps/Core/Search/MWMSearch.h | 2 -- iphone/Maps/Core/Search/MWMSearch.mm | 28 ++----------------- 3 files changed, 3 insertions(+), 28 deletions(-) diff --git a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.mm b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.mm index 0fd95e9fac..749a915818 100644 --- a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.mm +++ b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.mm @@ -191,7 +191,6 @@ BOOL defaultOrientation(CGSize const &size) { - (IBAction)searchButtonTouchUpInside:(MWMButton *)sender { auto const body = ^(NavigationSearchState state) { - [MWMSearch setSearchOnMap:YES]; NSString *query = [kSearchButtonRequest.at(state) stringByAppendingString:@" "]; NSString *locale = [[AppInfo sharedInfo] languageId]; // Category request from navigation search wheel. diff --git a/iphone/Maps/Core/Search/MWMSearch.h b/iphone/Maps/Core/Search/MWMSearch.h index 130d0c4b24..86422f89ac 100644 --- a/iphone/Maps/Core/Search/MWMSearch.h +++ b/iphone/Maps/Core/Search/MWMSearch.h @@ -28,8 +28,6 @@ NS_SWIFT_NAME(Search) + (NSUInteger)containerIndexWithRow:(NSUInteger)row; + (SearchResult *)resultWithContainerIndex:(NSUInteger)index; -+ (void)setSearchOnMap:(BOOL)searchOnMap; - + (NSUInteger)suggestionsCount; + (NSUInteger)resultsCount; diff --git a/iphone/Maps/Core/Search/MWMSearch.mm b/iphone/Maps/Core/Search/MWMSearch.mm index 88dca14f27..5b39f57d9a 100644 --- a/iphone/Maps/Core/Search/MWMSearch.mm +++ b/iphone/Maps/Core/Search/MWMSearch.mm @@ -17,7 +17,6 @@ using Observers = NSHashTable<Observer>; @interface MWMSearch () <MWMFrameworkDrapeObserver> @property(nonatomic) NSUInteger suggestionsCount; -@property(nonatomic) BOOL searchOnMap; @property(nonatomic) BOOL textChanged; @@ -111,16 +110,8 @@ using Observers = NSHashTable<Observer>; [self reset]; if (m_query.empty()) return; - - if (IPAD) { - [self searchInViewport]; - [self searchEverywhere]; - } else { - if (self.searchOnMap) - [self searchInViewport]; - else - [self searchEverywhere]; - } + [self searchInViewport]; + [self searchEverywhere]; } #pragma mark - Add/Remove Observers @@ -223,18 +214,6 @@ using Observers = NSHashTable<Observer>; [manager reset]; } -+ (void)setSearchOnMap:(BOOL)searchOnMap { - if (IPAD) - return; - MWMSearch *manager = [MWMSearch manager]; - if (manager.searchOnMap == searchOnMap) - return; - manager.searchOnMap = searchOnMap; - if (searchOnMap && ![MWMRouter isRoutingActive]) - GetFramework().ShowSearchResults(manager->m_everywhereResults); - [manager update]; -} - + (NSUInteger)suggestionsCount { return [MWMSearch manager].suggestionsCount; } @@ -280,8 +259,7 @@ using Observers = NSHashTable<Observer>; - (void)processViewportChangedEvent { if (!GetFramework().GetSearchAPI().IsViewportSearchActive()) return; - if (IPAD) - [self searchEverywhere]; + [self searchEverywhere]; } #pragma mark - Properties -- 2.45.3 From b984afbaf73810c1a39b146ecf7ebe35afe61fa2 Mon Sep 17 00:00:00 2001 From: Kiryl Kaveryn <kirylkaveryn@gmail.com> Date: Mon, 17 Feb 2025 20:56:56 +0400 Subject: [PATCH 11/12] [ios] replace MWMSearchManager with new SearchOnMapManager - fix layout of the place page container (configure it programatiacally) - use the new modal seearch VC everywhere - Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com> --- .../MWMMapViewControlsManager.h | 2 +- .../MWMMapViewControlsManager.mm | 60 +---- .../MWMNavigationDashboardManager.mm | 26 +- .../Views/MWMNavigationInfoView.h | 2 + .../Views/MWMNavigationInfoView.mm | 18 +- iphone/Maps/Classes/MapViewController.h | 4 +- iphone/Maps/Classes/MapViewController.mm | 117 ++++++--- .../TabBar/BottomTabBarInteractor.swift | 11 +- .../TabBar/BottomTabBarViewController.swift | 14 -- iphone/Maps/UI/Search/MWMSearchManager.mm | 16 +- iphone/Maps/UI/Storyboard/Main.storyboard | 235 +++++------------- 11 files changed, 213 insertions(+), 292 deletions(-) diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.h b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.h index 1a325c25b8..28d7963528 100644 --- a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.h +++ b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.h @@ -6,6 +6,7 @@ @class MapViewController; @class BottomTabBarViewController; @class TrackRecordingViewController; + @protocol MWMFeatureHolder; @interface MWMMapViewControlsManager : NSObject @@ -47,7 +48,6 @@ - (void)actionDownloadMaps:(MWMMapDownloaderMode)mode; - (BOOL)searchText:(NSString *)text forInputLocale:(NSString *)locale; - (void)searchTextOnMap:(NSString *)text forInputLocale:(NSString *)locale; -- (void)hideSearch; #pragma mark - MWMFeatureHolder diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm index 8944fc2075..8357f5e63e 100644 --- a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm +++ b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm @@ -5,7 +5,6 @@ #import "MWMNetworkPolicy+UI.h" #import "MWMPlacePageManager.h" #import "MWMPlacePageProtocol.h" -#import "MWMSearchManager.h" #import "MWMSideButtons.h" #import "MWMTrafficButtonViewController.h" #import "MWMMapWidgetsHelper.h" @@ -27,8 +26,7 @@ namespace { NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue"; } // namespace -@interface MWMMapViewControlsManager () <BottomMenuDelegate, - MWMSearchManagerObserver> +@interface MWMMapViewControlsManager () <BottomMenuDelegate> @property(nonatomic) MWMSideButtons *sideButtons; @property(nonatomic) MWMTrafficButtonViewController *trafficButton; @@ -36,7 +34,7 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue"; @property(nonatomic) UIViewController *menuController; @property(nonatomic) id<MWMPlacePageProtocol> placePageManager; @property(nonatomic) MWMNavigationDashboardManager *navigationManager; -@property(nonatomic) MWMSearchManager *searchManager; +@property(nonatomic) SearchOnMapManager *searchManager; @property(weak, nonatomic) MapViewController *ownerController; @@ -50,6 +48,7 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue"; + (MWMMapViewControlsManager *)manager { return [MapViewController sharedController].controlsManager; } + - (instancetype)initWithParentController:(MapViewController *)controller { if (!controller) return nil; @@ -67,6 +66,7 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue"; [TrackRecordingManager.shared addObserver:self recordingIsActiveDidChangeHandler:^(TrackRecordingState state, TrackInfo * trackInfo) { [self setTrackRecordingButtonHidden:state == TrackRecordingStateInactive]; }]; + self.searchManager = controller.searchManager; return self; } @@ -75,7 +75,6 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue"; } - (UIStatusBarStyle)preferredStatusBarStyle { - BOOL const isSearchUnderStatusBar = (self.searchManager.state != MWMSearchManagerStateHidden); BOOL const isNavigationUnderStatusBar = self.navigationManager.state != MWMNavigationDashboardStateHidden && self.navigationManager.state != MWMNavigationDashboardStateNavigation; BOOL const isMenuViewUnderStatusBar = self.menuState == MWMBottomMenuStateActive; @@ -83,7 +82,7 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue"; BOOL const isAddPlaceUnderStatusBar = [self.ownerController.view hasSubviewWithViewClass:[MWMAddPlaceNavigationBar class]]; BOOL const isNightMode = [UIColor isNightMode]; - BOOL const isSomethingUnderStatusBar = isSearchUnderStatusBar || isNavigationUnderStatusBar || + BOOL const isSomethingUnderStatusBar = isNavigationUnderStatusBar || isDirectionViewUnderStatusBar || isMenuViewUnderStatusBar || isAddPlaceUnderStatusBar; @@ -101,7 +100,6 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue"; [self.trafficButton viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; [self.trackRecordingButton viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; [self.tabBarController viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; - [self.searchManager viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; } #pragma mark - MWMPlacePageViewManager @@ -110,24 +108,19 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue"; if (![self searchText:text forInputLocale:locale]) return; - self.searchManager.state = MWMSearchManagerStateMapSearch; + [self.searchManager startSearchingWithIsRouting:NO]; } - (BOOL)searchText:(NSString *)text forInputLocale:(NSString *)locale { if (text.length == 0) return NO; - self.searchManager.state = MWMSearchManagerStateTableSearch; - [self.searchManager searchText:text forInputLocale:locale withCategory:NO]; + [self.searchManager startSearchingWithIsRouting:NO]; + [self.searchManager searchText:text locale:locale isCategory:NO]; return YES; } -- (void)hideSearch { - self.searchManager.state = MWMSearchManagerStateHidden; -} - -#pragma mark - BottomMenuDelegate - +#pragma mark - BottomMenu - (void)actionDownloadMaps:(MWMMapDownloaderMode)mode { [self.ownerController openMapsDownloader:mode]; } @@ -146,7 +139,7 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue"; MapViewController *ownerController = self.ownerController; self.isAddingPlace = YES; - self.searchManager.state = MWMSearchManagerStateHidden; + [self.searchManager close]; self.menuState = MWMBottomMenuStateHidden; self.trafficButtonHidden = YES; @@ -181,15 +174,6 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue"; [[MapsAppDelegate theApp] enableStandby]; } -#pragma mark - MWMSearchManagerObserver - -- (void)onSearchManagerStateChanged { - auto state = [MWMSearchManager manager].state; - if (!IPAD && state == MWMSearchManagerStateHidden) { - self.hidden = NO; - } -} - #pragma mark - Routing - (void)onRoutePrepare { @@ -201,16 +185,12 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue"; } - (void)onRouteRebuild { - if (IPAD) - self.searchManager.state = MWMSearchManagerStateHidden; - [self.ownerController.bookmarksCoordinator close]; [self.navigationManager onRoutePlanning]; self.promoButton.hidden = YES; } - (void)onRouteReady:(BOOL)hasWarnings { - self.searchManager.state = MWMSearchManagerStateHidden; [self.navigationManager onRouteReady:hasWarnings]; self.promoButton.hidden = YES; } @@ -226,7 +206,6 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue"; } - (void)onRouteStop { - self.searchManager.state = MWMSearchManagerStateHidden; self.sideButtons.zoomHidden = self.zoomHidden; [self.navigationManager onRouteStop]; self.disableStandbyOnRouteFollowing = NO; @@ -236,17 +215,6 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue"; #pragma mark - Properties -/* -- (UIButton *)promoButton { - if (!_promoButton) { - PromoCoordinator *coordinator = [[PromoCoordinator alloc] initWithViewController:self.ownerController - campaign:_promoDiscoveryCampaign]; - _promoButton = [[PromoButton alloc] initWithCoordinator:coordinator]; - } - return _promoButton; -} -*/ - - (MWMSideButtons *)sideButtons { if (!_sideButtons) _sideButtons = [[MWMSideButtons alloc] initWithParentView:self.ownerController.controlsView]; @@ -283,14 +251,6 @@ NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue"; return _navigationManager; } -- (MWMSearchManager *)searchManager { - if (!_searchManager) { - _searchManager = [[MWMSearchManager alloc] init]; - [MWMSearchManager addObserver:self]; - } - return _searchManager; -} - @synthesize menuState = _menuState; - (void)setHidden:(BOOL)hidden { diff --git a/iphone/Maps/Classes/CustomViews/NavigationDashboard/MWMNavigationDashboardManager.mm b/iphone/Maps/Classes/CustomViews/NavigationDashboard/MWMNavigationDashboardManager.mm index 42551ba1dc..7ec7351705 100644 --- a/iphone/Maps/Classes/CustomViews/NavigationDashboard/MWMNavigationDashboardManager.mm +++ b/iphone/Maps/Classes/CustomViews/NavigationDashboard/MWMNavigationDashboardManager.mm @@ -19,7 +19,7 @@ NSString *const kNavigationControlViewXibName = @"NavigationControlView"; @end -@interface MWMNavigationDashboardManager () <MWMSearchManagerObserver, MWMRoutePreviewDelegate> +@interface MWMNavigationDashboardManager () <SearchOnMapManagerObserver, MWMRoutePreviewDelegate> @property(copy, nonatomic) NSDictionary *etaAttributes; @property(copy, nonatomic) NSDictionary *etaSecondaryAttributes; @@ -52,6 +52,10 @@ NSString *const kNavigationControlViewXibName = @"NavigationControlView"; return self; } +- (SearchOnMapManager *)searchManager { + return [[MapViewController sharedController] searchManager]; +} + - (void)loadPreviewWithStatusBoxes { [NSBundle.mainBundle loadNibNamed:kRoutePreviewIPhoneXibName owner:self options:nil]; auto ownerView = self.ownerView; @@ -238,14 +242,20 @@ NSString *const kNavigationControlViewXibName = @"NavigationControlView"; - (IBAction)stopRoutingButtonAction { [MWMSearch clear]; [MWMRouter stopRouting]; + [self.searchManager close]; } -#pragma mark - MWMSearchManagerObserver +#pragma mark - SearchOnMapManagerObserver -- (void)onSearchManagerStateChanged { - auto state = [MWMSearchManager manager].state; - if (state == MWMSearchManagerStateMapSearch) - [self setMapSearch]; +- (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 @@ -261,9 +271,9 @@ NSString *const kNavigationControlViewXibName = @"NavigationControlView"; - (void)setState:(MWMNavigationDashboardState)state { if (state == MWMNavigationDashboardStateHidden) - [MWMSearchManager removeObserver:self]; + [self.searchManager removeObserver:self]; else - [MWMSearchManager addObserver:self]; + [self.searchManager addObserver:self]; switch (state) { case MWMNavigationDashboardStateHidden: [self stateHidden]; diff --git a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.h b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.h index 694ab2f5cc..5ea9e94c79 100644 --- a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.h +++ b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.h @@ -25,6 +25,8 @@ typedef NS_ENUM(NSUInteger, MWMNavigationInfoViewState) { @property(weak, nonatomic) UIView * ownerView; @property(nonatomic) CGRect availableArea; +- (void)setSearchState:(NavigationSearchState)searchState animated:(BOOL)animated; + - (void)onNavigationInfoUpdated:(MWMNavigationDashboardEntity *)info; - (void)setMapSearch; diff --git a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.mm b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.mm index 749a915818..34acbed972 100644 --- a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.mm +++ b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Views/MWMNavigationInfoView.mm @@ -6,7 +6,6 @@ #import "MWMLocationObserver.h" #import "MWMMapViewControlsCommon.h" #import "MWMSearch.h" -#import "MWMSearchManager.h" #import "MapViewController.h" #import "SwiftBridge.h" #import "UIImageView+Coloring.h" @@ -143,13 +142,16 @@ BOOL defaultOrientation(CGSize const &size) { [toastView configWithIsStart:YES withLocationButton:NO]; } +- (SearchOnMapManager *)searchManager { + return [MapViewController sharedController].searchManager; +} + - (IBAction)openSearch { BOOL const isStart = self.toastView.isStart; - auto searchManager = [MWMSearchManager manager]; - searchManager.routingTooltipSearch = - isStart ? MWMSearchManagerRoutingTooltipSearchStart : MWMSearchManagerRoutingTooltipSearchFinish; - searchManager.state = MWMSearchManagerStateDefault; + [self.searchManager setRoutingTooltip: + isStart ? SearchOnMapRoutingTooltipSearchStart : SearchOnMapRoutingTooltipSearchFinish ]; + [self.searchManager startSearchingWithIsRouting:YES]; } - (IBAction)addLocationRoutePoint { @@ -166,12 +168,12 @@ BOOL defaultOrientation(CGSize const &size) { - (IBAction)searchMainButtonTouchUpInside { switch (self.searchState) { case NavigationSearchState::Maximized: - [MWMSearchManager manager].state = MWMSearchManagerStateDefault; + [self.searchManager startSearchingWithIsRouting:YES]; [self setSearchState:NavigationSearchState::MinimizedNormal animated:YES]; break; case NavigationSearchState::MinimizedNormal: if (self.state == MWMNavigationInfoViewStatePrepare) { - [MWMSearchManager manager].state = MWMSearchManagerStateDefault; + [self.searchManager startSearchingWithIsRouting:YES]; } else { [self setSearchState:NavigationSearchState::Maximized animated:YES]; } @@ -183,7 +185,7 @@ BOOL defaultOrientation(CGSize const &size) { case NavigationSearchState::MinimizedFood: case NavigationSearchState::MinimizedATM: [MWMSearch clear]; - [MWMSearchManager manager].state = MWMSearchManagerStateHidden; + [self.searchManager hide]; [self setSearchState:NavigationSearchState::MinimizedNormal animated:YES]; break; } diff --git a/iphone/Maps/Classes/MapViewController.h b/iphone/Maps/Classes/MapViewController.h index f47edd744d..c6c1a3e3d5 100644 --- a/iphone/Maps/Classes/MapViewController.h +++ b/iphone/Maps/Classes/MapViewController.h @@ -6,6 +6,7 @@ @class EAGLView; @class MWMMapDownloadDialog; @class BookmarksCoordinator; +@class SearchOnMapManager; @protocol MWMLocationModeListener; @interface MapViewController : MWMViewController @@ -46,11 +47,10 @@ @property(nonatomic, readonly) MWMMapViewControlsManager * _Nonnull controlsManager; @property(nonatomic, readonly) MWMMapDownloadDialog * _Nonnull downloadDialog; @property(nonatomic, readonly) BookmarksCoordinator * _Nonnull bookmarksCoordinator; +@property(nonatomic, readonly) SearchOnMapManager * _Nonnull searchManager; @property(nonatomic) MWMMyPositionMode currentPositionMode; @property(strong, nonatomic) IBOutlet EAGLView * _Nonnull mapView; @property(strong, nonatomic) IBOutlet UIView * _Nonnull controlsView; -@property(strong, nonatomic) IBOutlet UIView * _Nonnull searchViewContainer; -@property(strong, nonatomic) IBOutlet NSLayoutConstraint * _Nonnull searchViewContainerLeadingConstraint; @end diff --git a/iphone/Maps/Classes/MapViewController.mm b/iphone/Maps/Classes/MapViewController.mm index f91e1d560b..93fc70cb37 100644 --- a/iphone/Maps/Classes/MapViewController.mm +++ b/iphone/Maps/Classes/MapViewController.mm @@ -30,6 +30,9 @@ extern NSString *const kMap2FBLoginSegue = @"Map2FBLogin"; extern NSString *const kMap2GoogleLoginSegue = @"Map2GoogleLogin"; +static CGFloat kPlacePageCompactWidth = 350; +static CGFloat kPlacePageLeadingOffset = IPAD ? 20 : 0; + typedef NS_ENUM(NSUInteger, UserTouchesAction) { UserTouchesActionNone, UserTouchesActionDrag, UserTouchesActionScale }; namespace { @@ -72,6 +75,7 @@ NSString *const kSettingsSegue = @"Map2Settings"; UIGestureRecognizerDelegate> @property(nonatomic, readwrite) MWMMapViewControlsManager *controlsManager; +@property(nonatomic, readwrite) SearchOnMapManager *searchManager; @property(nonatomic) BOOL disableStandbyOnLocationStateMode; @@ -97,7 +101,11 @@ NSString *const kSettingsSegue = @"Map2Settings"; @property(nonatomic) BOOL needDeferFocusNotification; @property(nonatomic) BOOL deferredFocusValue; @property(nonatomic) PlacePageViewController *placePageVC; -@property(nonatomic) IBOutlet UIView *placePageContainer; +@property(nonatomic) UIView *placePageContainer; + +@property(nonatomic) NSLayoutConstraint *placePageWidthConstraint; +@property(nonatomic) NSLayoutConstraint *placePageLeadingConstraint; +@property(nonatomic) NSLayoutConstraint *placePageTrailingConstraint; @end @@ -111,6 +119,9 @@ NSString *const kSettingsSegue = @"Map2Settings"; #pragma mark - Map Navigation - (void)showOrUpdatePlacePage:(PlacePageData *)data { + if (self.searchManager.isSearching) + [self.searchManager setPlaceOnMapSelected:YES]; + self.controlsManager.trafficButtonHidden = YES; if (self.placePageVC != nil) { [PlacePageBuilder update:(PlacePageViewController *)self.placePageVC with:data]; @@ -120,19 +131,58 @@ NSString *const kSettingsSegue = @"Map2Settings"; } - (void)showPlacePageFor:(PlacePageData *)data { - self.placePageVC = [PlacePageBuilder buildFor:data]; self.placePageContainer.hidden = NO; - self.placePageVC.view.translatesAutoresizingMaskIntoConstraints = NO; + self.placePageVC = [PlacePageBuilder buildFor:data]; [self.placePageContainer addSubview:self.placePageVC.view]; - [self.view bringSubviewToFront:self.placePageContainer]; - [NSLayoutConstraint activateConstraints:@[ - [self.placePageVC.view.topAnchor constraintEqualToAnchor:self.placePageContainer.safeAreaLayoutGuide.topAnchor], - [self.placePageVC.view.leftAnchor constraintEqualToAnchor:self.placePageContainer.leftAnchor], - [self.placePageVC.view.bottomAnchor constraintEqualToAnchor:self.placePageContainer.bottomAnchor], - [self.placePageVC.view.rightAnchor constraintEqualToAnchor:self.placePageContainer.rightAnchor] - ]]; [self addChildViewController:self.placePageVC]; [self.placePageVC didMoveToParentViewController:self]; + self.placePageVC.view.translatesAutoresizingMaskIntoConstraints = NO; + [NSLayoutConstraint activateConstraints:@[ + [self.placePageVC.view.topAnchor constraintEqualToAnchor:self.placePageContainer.topAnchor], + [self.placePageVC.view.leadingAnchor constraintEqualToAnchor:self.placePageContainer.leadingAnchor], + [self.placePageVC.view.bottomAnchor constraintEqualToAnchor:self.placePageContainer.bottomAnchor], + [self.placePageVC.view.trailingAnchor constraintEqualToAnchor:self.placePageContainer.trailingAnchor] + ]]; + [self updatePlacePageContainerConstraints]; +} + +- (void)setupPlacePageContainer { + self.placePageContainer = [[TouchTransparentView alloc] initWithFrame:self.view.bounds]; + [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; + + self.placePageWidthConstraint = [self.placePageContainer.widthAnchor constraintEqualToConstant:0]; + 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; + } + + [self updatePlacePageContainerConstraints]; +} + +- (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]; + leadingToSearchConstraint.priority = UILayoutPriorityDefaultHigh; + leadingToSearchConstraint.active = isLimitedWidth; + } + + [self.placePageWidthConstraint setActive:isLimitedWidth]; + [self.placePageTrailingConstraint setActive:!isLimitedWidth]; + [self.view layoutIfNeeded]; } - (void)dismissPlacePage { @@ -160,16 +210,10 @@ NSString *const kSettingsSegue = @"Map2Settings"; - (void)onMapObjectDeselected { [self hidePlacePage]; - MWMSearchManager * searchManager = MWMSearchManager.manager; - BOOL const isSearchResult = searchManager.state == MWMSearchManagerStateResult; + BOOL const isSearching = self.searchManager.isSearching; BOOL const isNavigationDashboardHidden = [MWMNavigationDashboardManager sharedManager].state == MWMNavigationDashboardStateHidden; - if (isSearchResult) { - if (isNavigationDashboardHidden) { - searchManager.state = MWMSearchManagerStateMapSearch; - } else { - searchManager.state = MWMSearchManagerStateHidden; - } - } + if (isSearching) + [self.searchManager setPlaceOnMapSelected:!isNavigationDashboardHidden]; // Always show the controls during the navigation or planning mode. if (!isNavigationDashboardHidden) self.controlsManager.hidden = NO; @@ -177,8 +221,7 @@ NSString *const kSettingsSegue = @"Map2Settings"; - (void)onSwitchFullScreen { BOOL const isNavigationDashboardHidden = MWMNavigationDashboardManager.sharedManager.state == MWMNavigationDashboardStateHidden; - BOOL const isSearchHidden = MWMSearchManager.manager.state == MWMSearchManagerStateHidden; - if (isSearchHidden && isNavigationDashboardHidden) { + if (!self.searchManager.isSearching && isNavigationDashboardHidden) { if (!self.controlsManager.hidden) [self dismissPlacePage]; self.controlsManager.hidden = !self.controlsManager.hidden; @@ -215,6 +258,10 @@ NSString *const kSettingsSegue = @"Map2Settings"; if ([MWMCarPlayService shared].isCarplayActivated) { return; } + + if (self.searchManager.isSearching && type == df::TouchEvent::TOUCH_MOVE) + [self.searchManager setMapIsDragging]; + NSArray *allTouches = [[event allTouches] allObjects]; if ([allTouches count] < 1) return; @@ -287,6 +334,12 @@ NSString *const kSettingsSegue = @"Map2Settings"; [self.controlsManager viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; } +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { + [super traitCollectionDidChange:previousTraitCollection]; + if (self.traitCollection.verticalSizeClass != previousTraitCollection.verticalSizeClass) + [self updatePlacePageContainerConstraints]; +} + - (void)didReceiveMemoryWarning { GetFramework().MemoryWarning(); [super didReceiveMemoryWarning]; @@ -305,8 +358,7 @@ NSString *const kSettingsSegue = @"Map2Settings"; - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - if ([MWMNavigationDashboardManager sharedManager].state == MWMNavigationDashboardStateHidden && - [MWMSearchManager manager].state == MWMSearchManagerStateHidden) + if ([MWMNavigationDashboardManager sharedManager].state == MWMNavigationDashboardStateHidden) self.controlsManager.menuState = self.controlsManager.menuRestoreState; [self updateStatusBarStyle]; @@ -319,6 +371,7 @@ NSString *const kSettingsSegue = @"Map2Settings"; - (void)viewDidLoad { [super viewDidLoad]; + [self setupPlacePageContainer]; if (@available(iOS 14.0, *)) [self setupTrackPadGestureRecognizers]; @@ -441,8 +494,7 @@ NSString *const kSettingsSegue = @"Map2Settings"; - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; - if ([MWMNavigationDashboardManager sharedManager].state == MWMNavigationDashboardStateHidden && - [MWMSearchManager manager].state == MWMSearchManagerStateHidden) + if ([MWMNavigationDashboardManager sharedManager].state == MWMNavigationDashboardStateHidden) self.controlsManager.menuRestoreState = self.controlsManager.menuState; GetFramework().SetRenderingDisabled(false); } @@ -605,15 +657,13 @@ NSString *const kSettingsSegue = @"Map2Settings"; - (void)performAction:(NSString *)action { [self.navigationController popToRootViewControllerAnimated:NO]; if (self.isViewLoaded) { - auto searchState = MWMSearchManagerStateHidden; [MWMRouter stopRouting]; if ([action isEqualToString:@"app.organicmaps.3daction.bookmarks"]) [self.bookmarksCoordinator open]; else if ([action isEqualToString:@"app.organicmaps.3daction.search"]) - searchState = MWMSearchManagerStateDefault; + [self.searchManager startSearchingWithIsRouting:NO]; else if ([action isEqualToString:@"app.organicmaps.3daction.route"]) [self.controlsManager onRoutePrepare]; - [MWMSearchManager manager].state = searchState; } else { dispatch_async(dispatch_get_main_queue(), ^{ [self performAction:action]; @@ -674,9 +724,20 @@ NSString *const kSettingsSegue = @"Map2Settings"; return _controlsManager; } +- (SearchOnMapManager *)searchManager { + if (!_searchManager) + _searchManager = [[SearchOnMapManager alloc] initWithNavigationController:self.navigationController]; + return _searchManager; +} + +- (UIView * _Nullable)searchViewContainer { + return self.searchManager.viewController.view; +} + - (BOOL)hasNavigationBar { return NO; } + - (MWMMapDownloadDialog *)downloadDialog { if (!_downloadDialog) _downloadDialog = [MWMMapDownloadDialog dialogForController:self]; diff --git a/iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarInteractor.swift b/iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarInteractor.swift index 24daa10607..6a88a62e44 100644 --- a/iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarInteractor.swift +++ b/iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarInteractor.swift @@ -11,22 +11,19 @@ class BottomTabBarInteractor { private weak var viewController: UIViewController? private weak var mapViewController: MapViewController? private weak var controlsManager: MWMMapViewControlsManager? - private weak var searchManager = MWMSearchManager.manager() - + private let searchManager: SearchOnMapManager + init(viewController: UIViewController, mapViewController: MapViewController, controlsManager: MWMMapViewControlsManager) { self.viewController = viewController self.mapViewController = mapViewController self.controlsManager = controlsManager + self.searchManager = mapViewController.searchManager } } extension BottomTabBarInteractor: BottomTabBarInteractorProtocol { func openSearch() { - if searchManager?.state == .hidden { - searchManager?.state = .default - } else { - searchManager?.state = .hidden - } + searchManager.isSearching ? searchManager.close() : searchManager.startSearching(isRouting: false) } func openHelp() { diff --git a/iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarViewController.swift b/iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarViewController.swift index af3da08ffa..05d3bc8d3b 100644 --- a/iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarViewController.swift +++ b/iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarViewController.swift @@ -32,8 +32,6 @@ class BottomTabBarViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() presenter.configure() - - MWMSearchManager.add(self) } override func viewWillAppear(_ animated: Bool) { @@ -45,10 +43,6 @@ class BottomTabBarViewController: UIViewController { updateBadge() } - deinit { - MWMSearchManager.remove(self) - } - static func updateAvailableArea(_ frame: CGRect) { BottomTabBarViewController.controller?.updateAvailableArea(frame) } @@ -119,11 +113,3 @@ private extension BottomTabBarViewController { UserDefaults.standard.set(true, forKey: kUDDidShowFirstTimeRoutingEducationalHint) } } - -// MARK: - MWMSearchManagerObserver -extension BottomTabBarViewController: MWMSearchManagerObserver { - func onSearchManagerStateChanged() { - let state = MWMSearchManager.manager().state; - self.searchButton.isSelected = state != .hidden - } -} diff --git a/iphone/Maps/UI/Search/MWMSearchManager.mm b/iphone/Maps/UI/Search/MWMSearchManager.mm index 691e79c5ec..29cd6c6e24 100644 --- a/iphone/Maps/UI/Search/MWMSearchManager.mm +++ b/iphone/Maps/UI/Search/MWMSearchManager.mm @@ -61,8 +61,14 @@ const CGFloat kWidthForiPad = 320; @implementation MWMSearchManager + (MWMSearchManager *)manager { - return [MWMMapViewControlsManager manager].searchManager; + static MWMSearchManager * manager; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + manager = [[MWMSearchManager alloc] init]; + }); + return manager; } + - (nullable instancetype)init { self = [super init]; if (self) { @@ -243,7 +249,6 @@ const CGFloat kWidthForiPad = 320; controlsManager.menuState = controlsManager.menuRestoreState; } [self viewHidden:NO]; - [MWMSearch setSearchOnMap:NO]; [self.tableViewController reloadData]; if (![self.navigationController.viewControllers containsObject:self.tableViewController]) @@ -265,7 +270,6 @@ const CGFloat kWidthForiPad = 320; [self viewHidden:navigationManagerState != MWMNavigationDashboardStateHidden]; self.controlsManager.menuState = MWMBottomMenuStateHidden; GetFramework().DeactivateMapSelection(); - [MWMSearch setSearchOnMap:YES]; [self.tableViewController reloadData]; [self.searchTextField resignFirstResponder]; @@ -523,10 +527,12 @@ const CGFloat kWidthForiPad = 320; return [MapViewController sharedController]; } - (UIView *)searchViewContainer { - return [MapViewController sharedController].searchViewContainer; +// return [MapViewController sharedController].searchViewContainer; + return nil; } - (NSLayoutConstraint *)searchViewContainerLeadingConstraint { - return [MapViewController sharedController].searchViewContainerLeadingConstraint; +// return [MapViewController sharedController].searchViewContainerLeadingConstraint; + return nil; } - (UIView *)actionBarContainer { return [MapViewController sharedController].controlsView; diff --git a/iphone/Maps/UI/Storyboard/Main.storyboard b/iphone/Maps/UI/Storyboard/Main.storyboard index a78cd06513..c1605e6180 100644 --- a/iphone/Maps/UI/Storyboard/Main.storyboard +++ b/iphone/Maps/UI/Storyboard/Main.storyboard @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Wns-nH-AQU"> - <device id="retina6_72" orientation="landscape" appearance="light"/> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Wns-nH-AQU"> + <device id="iPad13_0rounded" orientation="portrait" layout="fullscreen" appearance="light"/> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> @@ -14,26 +14,26 @@ <objects> <viewController id="xTf-lf-yxN" customClass="MapViewController" sceneMemberID="viewController"> <view key="view" clearsContextBeforeDrawing="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" id="USG-6L-Uhw"> - <rect key="frame" x="0.0" y="0.0" width="932" height="430"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1376"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ixC-IZ-Pvs" userLabel="CarPlayPlaceholderView" customClass="CarplayPlaceholderView" customModule="Organic_Maps" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="932" height="430"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1376"/> <color key="backgroundColor" systemColor="systemBackgroundColor"/> </view> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="aPn-pa-nCx" customClass="EAGLView"> - <rect key="frame" x="0.0" y="0.0" width="932" height="430"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1376"/> <color key="backgroundColor" red="0.8666666666666667" green="0.8666666666666667" blue="0.80000000000000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> </view> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rL1-9E-4b7"> - <rect key="frame" x="0.0" y="0.0" width="932" height="430"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1376"/> <subviews> <view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="65S-M4-TnM" customClass="NavigationInfoArea" customModule="Organic_Maps" customModuleProvider="target"> - <rect key="frame" x="59" y="0.0" width="814" height="409"/> + <rect key="frame" x="0.0" y="24" width="1032" height="1332"/> <color key="backgroundColor" red="0.0" green="0.0" blue="1" alpha="0.20000000000000001" colorSpace="calibratedRGB"/> </view> <view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TdT-ia-GP9"> - <rect key="frame" x="59" y="0.0" width="350" height="409"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1356"/> <color key="backgroundColor" red="0.0" green="0.0" blue="1" alpha="0.20000000000000001" colorSpace="calibratedRGB"/> <constraints> <constraint firstAttribute="width" constant="350" id="XLL-zv-Bym"/> @@ -50,7 +50,7 @@ </variation> </view> <view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="aVk-ab-LFJ" customClass="TabBarArea" customModule="Organic_Maps" customModuleProvider="target"> - <rect key="frame" x="59" y="361" width="350" height="69"/> + <rect key="frame" x="0.0" y="1308" width="1032" height="68"/> <color key="backgroundColor" red="0.0" green="0.0" blue="1" alpha="0.20000000000000001" colorSpace="calibratedRGB"/> <constraints> <constraint firstAttribute="width" priority="100" constant="350" id="aj4-lb-h52"/> @@ -67,26 +67,26 @@ </variation> </view> <view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="awj-9E-eBS" customClass="PlacePageArea" customModule="Organic_Maps" customModuleProvider="target"> - <rect key="frame" x="59" y="0.0" width="814" height="409"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1356"/> <color key="backgroundColor" red="1" green="0.0" blue="0.0" alpha="0.20000000000000001" colorSpace="calibratedRGB"/> </view> <view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="FFY-Dy-Wou" customClass="VisibleArea" customModule="Organic_Maps" customModuleProvider="target"> - <rect key="frame" x="59" y="0.0" width="814" height="409"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1356"/> <color key="backgroundColor" red="0.0" green="1" blue="0.0" alpha="0.20000000000000001" colorSpace="calibratedRGB"/> </view> <view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NI8-tV-i2B" customClass="WidgetsArea" customModule="Organic_Maps" customModuleProvider="target"> - <rect key="frame" x="59" y="0.0" width="814" height="409"/> + <rect key="frame" x="0.0" y="24" width="1032" height="1332"/> <color key="backgroundColor" red="0.0" green="1" blue="0.0" alpha="0.20000000000000001" colorSpace="calibratedRGB"/> </view> <view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="xJx-UU-IdV" customClass="SideButtonsArea" customModule="Organic_Maps" customModuleProvider="target"> - <rect key="frame" x="59" y="0.0" width="814" height="409"/> + <rect key="frame" x="0.0" y="24" width="1032" height="1332"/> <color key="backgroundColor" red="0.0" green="0.0" blue="1" alpha="0.20000000000000001" colorSpace="calibratedRGB"/> </view> <view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QKu-4A-UgP" customClass="TrafficButtonArea" customModule="Organic_Maps" customModuleProvider="target"> - <rect key="frame" x="59" y="0.0" width="814" height="409"/> + <rect key="frame" x="0.0" y="24" width="1032" height="1332"/> </view> <view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="yto-Gu-Ugz" userLabel="Track Recording Button Area" customClass="TrackRecordingButtonArea" customModule="Organic_Maps" customModuleProvider="target"> - <rect key="frame" x="0.0" y="59" width="430" height="839"/> + <rect key="frame" x="0.0" y="24" width="1032" height="1332"/> <color key="backgroundColor" red="0.0" green="1" blue="0.0" alpha="0.20000000000000001" colorSpace="calibratedRGB"/> </view> </subviews> @@ -98,64 +98,8 @@ <constraint firstItem="FFY-Dy-Wou" firstAttribute="top" secondItem="rL1-9E-4b7" secondAttribute="top" priority="100" id="svT-Vi-vvC"/> </constraints> </view> - <view contentMode="scaleToFill" placeholderIntrinsicWidth="infinite" placeholderIntrinsicHeight="500" translatesAutoresizingMaskIntoConstraints="NO" id="jio-3T-E6G" customClass="TouchTransparentView" customModule="Organic_Maps" customModuleProvider="target"> - <rect key="frame" x="59" y="0.0" width="350" height="430"/> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstAttribute="width" constant="350" id="Dd0-x6-7gc"> - <variation key="heightClass=regular-widthClass=regular" constant="320"/> - </constraint> - </constraints> - <variation key="default"> - <mask key="constraints"> - <exclude reference="Dd0-x6-7gc"/> - </mask> - </variation> - <variation key="heightClass=compact-widthClass=compact"> - <mask key="constraints"> - <include reference="Dd0-x6-7gc"/> - </mask> - </variation> - <variation key="heightClass=compact-widthClass=regular"> - <mask key="constraints"> - <include reference="Dd0-x6-7gc"/> - </mask> - </variation> - <variation key="heightClass=regular-widthClass=regular"> - <mask key="constraints"> - <include reference="Dd0-x6-7gc"/> - </mask> - </variation> - </view> - <view hidden="YES" contentMode="scaleToFill" placeholderIntrinsicWidth="infinite" placeholderIntrinsicHeight="500" translatesAutoresizingMaskIntoConstraints="NO" id="rbx-Oj-jeo" customClass="TouchTransparentView" customModule="Organic_Maps" customModuleProvider="target"> - <rect key="frame" x="59" y="0.0" width="350" height="430"/> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstAttribute="width" constant="350" id="6h8-a6-LWn"/> - </constraints> - <variation key="default"> - <mask key="constraints"> - <exclude reference="6h8-a6-LWn"/> - </mask> - </variation> - <variation key="heightClass=compact-widthClass=compact"> - <mask key="constraints"> - <include reference="6h8-a6-LWn"/> - </mask> - </variation> - <variation key="heightClass=compact-widthClass=regular"> - <mask key="constraints"> - <include reference="6h8-a6-LWn"/> - </mask> - </variation> - <variation key="heightClass=regular-widthClass=regular"> - <mask key="constraints"> - <include reference="6h8-a6-LWn"/> - </mask> - </variation> - </view> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="at1-V1-pzl" customClass="TouchTransparentView" customModule="Organic_Maps" customModuleProvider="target"> - <rect key="frame" x="59" y="209" width="814" height="152"/> + <rect key="frame" x="0.0" y="722" width="440" height="152"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <gestureRecognizers/> <constraints> @@ -171,14 +115,11 @@ <constraints> <constraint firstItem="utd-Jy-pE5" firstAttribute="bottom" secondItem="65S-M4-TnM" secondAttribute="bottom" priority="100" id="2E3-5i-6An"/> <constraint firstAttribute="bottom" secondItem="rL1-9E-4b7" secondAttribute="bottom" id="4fw-Xu-gAG"/> - <constraint firstItem="rbx-Oj-jeo" firstAttribute="leading" secondItem="utd-Jy-pE5" secondAttribute="leading" id="5Sh-l6-Icd"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="bottom" secondItem="QKu-4A-UgP" secondAttribute="bottom" priority="100" id="6ko-rI-S5u"/> <constraint firstAttribute="bottom" secondItem="ixC-IZ-Pvs" secondAttribute="bottom" id="7eh-og-Mpe"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="trailing" secondItem="aVk-ab-LFJ" secondAttribute="trailing" id="85b-Do-jO7"/> <constraint firstItem="at1-V1-pzl" firstAttribute="top" secondItem="utd-Jy-pE5" secondAttribute="bottom" priority="250" id="8JV-dP-iYZ"/> <constraint firstItem="TdT-ia-GP9" firstAttribute="leading" secondItem="utd-Jy-pE5" secondAttribute="leading" id="8YH-dJ-lHZ"/> - <constraint firstItem="utd-Jy-pE5" firstAttribute="trailing" secondItem="rbx-Oj-jeo" secondAttribute="trailing" id="9M9-8P-Hzb"/> - <constraint firstAttribute="bottom" secondItem="rbx-Oj-jeo" secondAttribute="bottom" id="9rR-QQ-c1P"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="top" secondItem="xJx-UU-IdV" secondAttribute="top" priority="100" id="BMq-jc-qfO"/> <constraint firstItem="ixC-IZ-Pvs" firstAttribute="top" secondItem="USG-6L-Uhw" secondAttribute="top" id="DSJ-0D-hwO"/> <constraint firstItem="rL1-9E-4b7" firstAttribute="top" secondItem="USG-6L-Uhw" secondAttribute="top" id="E89-WV-ZTh"/> @@ -187,8 +128,6 @@ <constraint firstItem="utd-Jy-pE5" firstAttribute="trailing" secondItem="at1-V1-pzl" secondAttribute="trailing" id="GKG-4Y-2Pq"/> <constraint firstAttribute="trailing" secondItem="aPn-pa-nCx" secondAttribute="trailing" id="GKJ-zm-8xb"/> <constraint firstItem="awj-9E-eBS" firstAttribute="leading" secondItem="utd-Jy-pE5" secondAttribute="leading" id="Hfm-gb-37H"/> - <constraint firstItem="rbx-Oj-jeo" firstAttribute="top" secondItem="utd-Jy-pE5" secondAttribute="top" id="J0B-xe-sNj"/> - <constraint firstItem="jio-3T-E6G" firstAttribute="leading" secondItem="utd-Jy-pE5" secondAttribute="leading" id="K9r-P1-yFI"/> <constraint firstAttribute="trailing" secondItem="ixC-IZ-Pvs" secondAttribute="trailing" id="O7f-qU-UN2"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="bottom" secondItem="at1-V1-pzl" secondAttribute="bottom" priority="750" constant="48" id="O8L-nd-nOa"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="bottom" secondItem="FFY-Dy-Wou" secondAttribute="bottom" priority="100" id="OE7-Qb-J0v"/> @@ -196,13 +135,11 @@ <constraint firstItem="utd-Jy-pE5" firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="awj-9E-eBS" secondAttribute="bottom" id="PFs-sL-oVA"/> <constraint firstAttribute="trailing" secondItem="rL1-9E-4b7" secondAttribute="trailing" id="QdS-Yx-ADV"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="trailing" secondItem="TdT-ia-GP9" secondAttribute="trailing" id="Rsb-fB-8bn"/> - <constraint firstItem="jio-3T-E6G" firstAttribute="leading" secondItem="utd-Jy-pE5" secondAttribute="leading" constant="-320" id="SAj-bF-qrr"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="xJx-UU-IdV" secondAttribute="bottom" id="SDX-4J-Jz5"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="bottom" secondItem="FFY-Dy-Wou" secondAttribute="bottom" id="TZk-MH-pMV"/> <constraint firstItem="yto-Gu-Ugz" firstAttribute="leading" secondItem="utd-Jy-pE5" secondAttribute="leading" id="TgS-dg-g9B"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="trailing" secondItem="yto-Gu-Ugz" secondAttribute="trailing" id="URB-3l-JiC"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="bottom" secondItem="xJx-UU-IdV" secondAttribute="bottom" priority="100" id="VfU-Zk-8IU"/> - <constraint firstItem="rbx-Oj-jeo" firstAttribute="leading" secondItem="jio-3T-E6G" secondAttribute="trailing" constant="20" id="W0l-NG-7lt"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="top" secondItem="QKu-4A-UgP" secondAttribute="top" priority="100" id="X96-y7-5oI"/> <constraint firstItem="aPn-pa-nCx" firstAttribute="leading" secondItem="USG-6L-Uhw" secondAttribute="leading" id="YFX-ma-vAf"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="FFY-Dy-Wou" secondAttribute="bottom" id="YUs-MJ-9w8"/> @@ -217,18 +154,14 @@ <constraint firstItem="xJx-UU-IdV" firstAttribute="leading" secondItem="utd-Jy-pE5" secondAttribute="leading" id="gn8-NV-oAa"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="bottom" secondItem="aVk-ab-LFJ" secondAttribute="top" priority="100" constant="48" id="jQI-62-O5O"/> <constraint firstItem="65S-M4-TnM" firstAttribute="leading" secondItem="utd-Jy-pE5" secondAttribute="leading" id="kan-ei-cIg"/> - <constraint firstAttribute="bottom" secondItem="jio-3T-E6G" secondAttribute="bottom" id="l6r-eW-Dbi"/> <constraint firstItem="NI8-tV-i2B" firstAttribute="leading" secondItem="utd-Jy-pE5" secondAttribute="leading" id="lwX-Xm-J8A"/> <constraint firstItem="ixC-IZ-Pvs" firstAttribute="leading" secondItem="USG-6L-Uhw" secondAttribute="leading" id="m7B-SH-DDh"/> <constraint firstItem="FFY-Dy-Wou" firstAttribute="leading" secondItem="utd-Jy-pE5" secondAttribute="leading" id="pc3-CW-vyV"/> <constraint firstItem="aPn-pa-nCx" firstAttribute="top" secondItem="USG-6L-Uhw" secondAttribute="top" id="pwE-hX-IML"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="bottom" secondItem="TdT-ia-GP9" secondAttribute="bottom" priority="100" id="pwZ-Fm-mHR"/> <constraint firstItem="aVk-ab-LFJ" firstAttribute="leading" secondItem="utd-Jy-pE5" secondAttribute="leading" id="qtc-CV-Hae"/> - <constraint firstAttribute="top" secondItem="jio-3T-E6G" secondAttribute="top" id="rYG-px-wH5"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="top" secondItem="NI8-tV-i2B" secondAttribute="top" priority="100" id="sSU-QE-9zI"/> - <constraint firstAttribute="trailing" secondItem="jio-3T-E6G" secondAttribute="trailing" id="t9l-Ud-h6j"/> <constraint firstAttribute="bottom" secondItem="aPn-pa-nCx" secondAttribute="bottom" id="tB3-eX-gUV"/> - <constraint firstItem="utd-Jy-pE5" firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="rbx-Oj-jeo" secondAttribute="bottom" constant="70" id="u9s-KY-yCt"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="bottom" secondItem="xJx-UU-IdV" secondAttribute="bottom" id="veF-Rn-BEm"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="top" secondItem="65S-M4-TnM" secondAttribute="top" priority="100" id="vfQ-ZT-Dlc"/> <constraint firstItem="utd-Jy-pE5" firstAttribute="trailing" secondItem="awj-9E-eBS" secondAttribute="trailing" id="wGo-EW-X9f"/> @@ -240,10 +173,6 @@ <exclude reference="SDX-4J-Jz5"/> <exclude reference="TZk-MH-pMV"/> <exclude reference="veF-Rn-BEm"/> - <exclude reference="u9s-KY-yCt"/> - <exclude reference="K9r-P1-yFI"/> - <exclude reference="SAj-bF-qrr"/> - <exclude reference="W0l-NG-7lt"/> </mask> </variation> <variation key="heightClass=compact"> @@ -252,7 +181,6 @@ <exclude reference="Rsb-fB-8bn"/> <include reference="TZk-MH-pMV"/> <include reference="veF-Rn-BEm"/> - <include reference="K9r-P1-yFI"/> </mask> </variation> <variation key="heightClass=regular"> @@ -260,34 +188,6 @@ <include reference="SDX-4J-Jz5"/> </mask> </variation> - <variation key="widthClass=compact"> - <mask key="constraints"> - <include reference="K9r-P1-yFI"/> - </mask> - </variation> - <variation key="heightClass=compact-widthClass=compact"> - <mask key="constraints"> - <exclude reference="9M9-8P-Hzb"/> - <exclude reference="t9l-Ud-h6j"/> - </mask> - </variation> - <variation key="heightClass=compact-widthClass=regular"> - <mask key="constraints"> - <exclude reference="9M9-8P-Hzb"/> - <exclude reference="t9l-Ud-h6j"/> - </mask> - </variation> - <variation key="heightClass=regular-widthClass=regular"> - <mask key="constraints"> - <exclude reference="9M9-8P-Hzb"/> - <include reference="u9s-KY-yCt"/> - <exclude reference="t9l-Ud-h6j"/> - <include reference="SAj-bF-qrr"/> - <exclude reference="9rR-QQ-c1P"/> - <include reference="W0l-NG-7lt"/> - <exclude reference="5Sh-l6-Icd"/> - </mask> - </variation> </view> <navigationItem key="navigationItem" id="8E8-0f-UV9"/> <connections> @@ -295,9 +195,6 @@ <outlet property="controlsView" destination="rL1-9E-4b7" id="sfV-7X-WlR"/> <outlet property="mapView" destination="aPn-pa-nCx" id="tCi-LW-1ll"/> <outlet property="placePageAreaKeyboard" destination="PFs-sL-oVA" id="O3P-ia-ZlX"/> - <outlet property="placePageContainer" destination="rbx-Oj-jeo" id="aFM-qm-QHB"/> - <outlet property="searchViewContainer" destination="jio-3T-E6G" id="Rjn-UE-zFx"/> - <outlet property="searchViewContainerLeadingConstraint" destination="SAj-bF-qrr" id="LJL-dW-eMM"/> <outlet property="sideButtonsAreaBottom" destination="VfU-Zk-8IU" id="MvP-Ki-4wP"/> <outlet property="sideButtonsAreaKeyboard" destination="SDX-4J-Jz5" id="kv9-zX-hbD"/> <outlet property="visibleAreaBottom" destination="OE7-Qb-J0v" id="isp-aT-LtA"/> @@ -342,7 +239,7 @@ <objects> <tableViewController storyboardIdentifier="MWMEditBookmarkController" id="lFr-lA-JTW" customClass="MWMEditBookmarkController" sceneMemberID="viewController"> <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="interactive" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="Rb3-ea-7LJ"> - <rect key="frame" x="0.0" y="0.0" width="932" height="430"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1376"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <connections> @@ -362,7 +259,7 @@ <navigationController id="Psz-BY-Fy4" customClass="MWMNavigationController" sceneMemberID="viewController"> <value key="contentSizeForViewInPopover" type="size" width="600" height="600"/> <navigationBar key="navigationBar" contentMode="scaleToFill" id="SUN-3A-xgM"> - <rect key="frame" x="0.0" y="0.0" width="932" height="44"/> + <rect key="frame" x="0.0" y="0.0" width="600" height="56"/> <autoresizingMask key="autoresizingMask"/> </navigationBar> <connections> @@ -378,7 +275,7 @@ <objects> <tableViewController id="Lfa-Zp-orR" customClass="MWMEditorViewController" sceneMemberID="viewController"> <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="interactive" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="HU6-ak-Eu1"> - <rect key="frame" x="0.0" y="0.0" width="932" height="430"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1376"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <connections> @@ -404,11 +301,11 @@ <objects> <viewController id="Ld6-gM-2hk" customClass="MWMOpeningHoursEditorViewController" sceneMemberID="viewController"> <view key="view" contentMode="scaleToFill" id="U1f-hD-9rl"> - <rect key="frame" x="0.0" y="0.0" width="932" height="430"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1376"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="interactive" dataMode="prototypes" style="grouped" separatorStyle="none" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" translatesAutoresizingMaskIntoConstraints="NO" id="X1H-IB-Nv1"> - <rect key="frame" x="0.0" y="0.0" width="932" height="365"/> + <rect key="frame" x="0.0" y="24" width="1032" height="1288"/> <color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <userDefinedRuntimeAttributes> <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="PressBackground"/> @@ -419,10 +316,10 @@ </connections> </tableView> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="aQv-7U-zAP"> - <rect key="frame" x="59" y="0.0" width="814" height="365"/> + <rect key="frame" x="0.0" y="24" width="1032" height="1288"/> <subviews> <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="PrH-u2-IEv" userLabel="Editor View" customClass="MWMTextView"> - <rect key="frame" x="0.0" y="36" width="814" height="88"/> + <rect key="frame" x="0.0" y="36" width="1032" height="88"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <constraints> <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="88" id="oAE-yX-hVe"/> @@ -439,7 +336,7 @@ </connections> </textView> <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="separator_image" translatesAutoresizingMaskIntoConstraints="NO" id="z2Z-G2-Np7"> - <rect key="frame" x="0.0" y="35" width="814" height="1"/> + <rect key="frame" x="0.0" y="35" width="1032" height="1"/> <constraints> <constraint firstAttribute="height" constant="1" id="xUX-ck-MQb"/> </constraints> @@ -448,7 +345,7 @@ </userDefinedRuntimeAttributes> </imageView> <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="separator_image" translatesAutoresizingMaskIntoConstraints="NO" id="ebA-fW-ddJ"> - <rect key="frame" x="0.0" y="123" width="814" height="1"/> + <rect key="frame" x="0.0" y="123" width="1032" height="1"/> <constraints> <constraint firstAttribute="height" constant="1" id="HOj-tZ-b3F"/> </constraints> @@ -457,7 +354,7 @@ </userDefinedRuntimeAttributes> </imageView> <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="separator_image" translatesAutoresizingMaskIntoConstraints="NO" id="5T5-Pp-hb5"> - <rect key="frame" x="0.0" y="320" width="814" height="1"/> + <rect key="frame" x="0.0" y="1243" width="1032" height="1"/> <constraints> <constraint firstAttribute="height" constant="1" id="ZXK-zv-uSz"/> </constraints> @@ -466,7 +363,7 @@ </userDefinedRuntimeAttributes> </imageView> <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="separator_image" translatesAutoresizingMaskIntoConstraints="NO" id="IX2-yp-0oa"> - <rect key="frame" x="0.0" y="164" width="814" height="1"/> + <rect key="frame" x="0.0" y="164" width="1032" height="1"/> <constraints> <constraint firstAttribute="height" constant="1" id="rD4-fE-ez2"/> </constraints> @@ -475,13 +372,13 @@ </userDefinedRuntimeAttributes> </imageView> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="85Z-MR-kUV" userLabel="Help View"> - <rect key="frame" x="0.0" y="164" width="814" height="328"/> + <rect key="frame" x="0.0" y="164" width="1032" height="324"/> <subviews> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="n79-h1-Nk3" userLabel="Button"> - <rect key="frame" x="0.0" y="0.0" width="814" height="44"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="44"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Example values" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="120" translatesAutoresizingMaskIntoConstraints="NO" id="dAM-iT-fzu"> - <rect key="frame" x="16.000000000000007" y="12" width="117.66666666666669" height="20"/> + <rect key="frame" x="16" y="12" width="117.5" height="20"/> <fontDescription key="fontDescription" type="system" pointSize="17"/> <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <nil key="highlightedColor"/> @@ -491,7 +388,7 @@ </userDefinedRuntimeAttributes> </label> <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_arrow_gray_down" translatesAutoresizingMaskIntoConstraints="NO" id="m7d-sG-5LN"> - <rect key="frame" x="782" y="10" width="24" height="24"/> + <rect key="frame" x="1000" y="10" width="24" height="24"/> <constraints> <constraint firstAttribute="height" constant="24" id="2aF-WV-ER2"/> <constraint firstAttribute="width" constant="24" id="eak-KY-Xaa"/> @@ -501,7 +398,7 @@ </userDefinedRuntimeAttributes> </imageView> <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="separator_image" translatesAutoresizingMaskIntoConstraints="NO" id="Suj-t5-ZWs"> - <rect key="frame" x="16" y="44" width="798" height="1"/> + <rect key="frame" x="16" y="44" width="1016" height="1"/> <constraints> <constraint firstAttribute="height" constant="1" id="cv8-Tg-Oin"/> </constraints> @@ -510,7 +407,7 @@ </userDefinedRuntimeAttributes> </imageView> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="MiP-Du-s3i"> - <rect key="frame" x="0.0" y="0.0" width="814" height="44"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="44"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <connections> <action selector="toggleExample" destination="Ld6-gM-2hk" eventType="touchUpInside" id="BGK-Ap-YBq"/> @@ -537,7 +434,7 @@ </userDefinedRuntimeAttributes> </view> <wkWebView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Gff-2B-vhp"> - <rect key="frame" x="10" y="54" width="373" height="260"/> + <rect key="frame" x="10" y="54" width="1012" height="260"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <constraints> <constraint firstAttribute="height" priority="750" constant="260" id="NdE-kI-erk"/> @@ -587,10 +484,10 @@ </constraints> </view> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SZQ-ra-FC3" userLabel="Mode Switch"> - <rect key="frame" x="0.0" y="365" width="932" height="44"/> + <rect key="frame" x="0.0" y="1312" width="1032" height="44"/> <subviews> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" lineBreakMode="tailTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fB1-w2-lJI"> - <rect key="frame" x="0.0" y="0.0" width="932" height="44"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="44"/> <inset key="contentEdgeInsets" minX="16" minY="0.0" maxX="0.0" maxY="0.0"/> <state key="normal" title="Simple Mode"/> <userDefinedRuntimeAttributes> @@ -601,7 +498,7 @@ </connections> </button> <imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_arrow_gray_right" translatesAutoresizingMaskIntoConstraints="NO" id="z8F-55-5rJ"> - <rect key="frame" x="900" y="10" width="24" height="24"/> + <rect key="frame" x="1000" y="10" width="24" height="24"/> <constraints> <constraint firstAttribute="width" constant="24" id="ypP-17-bfX"/> <constraint firstAttribute="height" constant="24" id="zE5-1N-qvh"/> @@ -611,7 +508,7 @@ </userDefinedRuntimeAttributes> </imageView> <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="separator_image" translatesAutoresizingMaskIntoConstraints="NO" id="onT-vv-01i"> - <rect key="frame" x="0.0" y="-1" width="932" height="1"/> + <rect key="frame" x="0.0" y="-1" width="1032" height="1"/> <constraints> <constraint firstAttribute="height" constant="1" id="UUW-c4-eNA"/> </constraints> @@ -679,26 +576,26 @@ <objects> <tableViewController id="ocL-kj-jxR" customClass="MWMEditorAdditionalNamesTableViewController" sceneMemberID="viewController"> <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="tQ2-XI-QWd"> - <rect key="frame" x="0.0" y="0.0" width="932" height="430"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1376"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <prototypes> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ListCellIdentifier" textLabel="JcK-nR-UGw" detailTextLabel="Cmi-x5-6Vt" style="IBUITableViewCellStyleSubtitle" id="RXe-xp-xlR" customClass="MWMTableViewCell"> - <rect key="frame" x="0.0" y="50" width="932" height="44"/> + <rect key="frame" x="0.0" y="50" width="1032" height="44"/> <autoresizingMask key="autoresizingMask"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="RXe-xp-xlR" id="g0x-Vt-1FI"> - <rect key="frame" x="59" y="0.0" width="814" height="44"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="44"/> <autoresizingMask key="autoresizingMask"/> <subviews> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="JcK-nR-UGw"> - <rect key="frame" x="20" y="6" width="31.666666666666668" height="19.333333333333332"/> + <rect key="frame" x="20" y="6" width="31.5" height="19.5"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <fontDescription key="fontDescription" type="system" pointSize="16"/> <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <nil key="highlightedColor"/> </label> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Detail" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Cmi-x5-6Vt"> - <rect key="frame" x="20" y="25.333333333333332" width="30.333333333333332" height="13.333333333333334"/> + <rect key="frame" x="20" y="25.5" width="30.5" height="13.5"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <fontDescription key="fontDescription" type="system" pointSize="11"/> <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> @@ -723,7 +620,7 @@ <objects> <tableViewController id="Heu-QR-M0N" customClass="MWMStreetEditorViewController" sceneMemberID="viewController"> <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="interactive" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="rJJ-UB-6u2"> - <rect key="frame" x="0.0" y="0.0" width="932" height="430"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1376"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <inset key="scrollIndicatorInsets" minX="0.0" minY="44" maxX="0.0" maxY="0.0"/> @@ -742,11 +639,11 @@ <objects> <viewController id="QlF-CJ-cEG" customClass="MWMObjectsCategorySelectorController" sceneMemberID="viewController"> <view key="view" contentMode="scaleToFill" id="MIY-NW-Joh"> - <rect key="frame" x="0.0" y="0.0" width="932" height="430"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1376"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="interactive" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" translatesAutoresizingMaskIntoConstraints="NO" id="JbV-y9-HBo"> - <rect key="frame" x="0.0" y="56" width="932" height="353"/> + <rect key="frame" x="0.0" y="80" width="1032" height="1276"/> <color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <userDefinedRuntimeAttributes> <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="TableView:PressBackground"/> @@ -757,7 +654,7 @@ </connections> </tableView> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rI9-RR-sKP" userLabel="Status Bar Background"> - <rect key="frame" x="0.0" y="-52" width="932" height="108"/> + <rect key="frame" x="0.0" y="-28" width="1032" height="108"/> <color key="backgroundColor" red="0.1215686275" green="0.59999999999999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="tintColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <constraints> @@ -768,7 +665,7 @@ </userDefinedRuntimeAttributes> </view> <searchBar contentMode="redraw" translatesAutoresizingMaskIntoConstraints="NO" id="gzF-B7-8pj"> - <rect key="frame" x="59" y="0.0" width="873" height="56"/> + <rect key="frame" x="0.0" y="24" width="1032" height="56"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <constraints> <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="44" id="2uI-k6-ahr"/> @@ -813,11 +710,11 @@ <objects> <viewController id="da4-KT-kzF" customClass="MWMCuisineEditorViewController" sceneMemberID="viewController"> <view key="view" contentMode="scaleToFill" id="iTG-qE-svw"> - <rect key="frame" x="0.0" y="0.0" width="932" height="430"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1376"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="interactive" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" translatesAutoresizingMaskIntoConstraints="NO" id="ina-WD-kps"> - <rect key="frame" x="0.0" y="56" width="932" height="353"/> + <rect key="frame" x="0.0" y="80" width="1032" height="1276"/> <color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <userDefinedRuntimeAttributes> <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="TableView:PressBackground"/> @@ -828,7 +725,7 @@ </connections> </tableView> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="HEU-Bu-3wh" userLabel="Status Bar Background"> - <rect key="frame" x="0.0" y="-52" width="932" height="108"/> + <rect key="frame" x="0.0" y="-28" width="1032" height="108"/> <color key="backgroundColor" red="0.1215686275" green="0.59999999999999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="tintColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <constraints> @@ -839,7 +736,7 @@ </userDefinedRuntimeAttributes> </view> <searchBar contentMode="redraw" translatesAutoresizingMaskIntoConstraints="NO" id="z6s-26-dP6"> - <rect key="frame" x="59" y="0.0" width="814" height="56"/> + <rect key="frame" x="0.0" y="24" width="1032" height="56"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <constraints> <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="44" id="UAk-z1-2EY"/> @@ -883,11 +780,11 @@ <objects> <viewController storyboardIdentifier="DownloadMapsViewController" id="h4a-ne-bSJ" customClass="MWMDownloadMapsViewController" sceneMemberID="viewController"> <view key="view" contentMode="scaleToFill" id="XQZ-0V-SyR"> - <rect key="frame" x="0.0" y="0.0" width="932" height="430"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1376"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="-1" estimatedSectionHeaderHeight="-1" sectionFooterHeight="1" translatesAutoresizingMaskIntoConstraints="NO" id="CwW-x8-G3j"> - <rect key="frame" x="0.0" y="0.0" width="932" height="430"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1376"/> <color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <gestureRecognizers/> <userDefinedRuntimeAttributes> @@ -899,13 +796,13 @@ </connections> </tableView> <containerView hidden="YES" opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kXO-Oh-2vO" userLabel="No Maps Container View"> - <rect key="frame" x="0.0" y="0.0" width="932" height="430"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1376"/> <connections> <segue destination="b8o-rZ-x0k" kind="embed" identifier="MapDownloaderNoResultsEmbedViewControllerSegue" id="ish-dC-mkH"/> </connections> </containerView> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="CDj-ol-HRP"> - <rect key="frame" x="0.0" y="345" width="932" height="85"/> + <rect key="frame" x="0.0" y="1292" width="1032" height="84"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <userDefinedRuntimeAttributes> <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="PressBackground"/> @@ -948,20 +845,20 @@ <objects> <viewController storyboardIdentifier="MWMNoMapsViewController" id="3el-Zi-2E4" customClass="MWMNoMapsViewController" sceneMemberID="viewController"> <view key="view" contentMode="scaleToFill" id="4WP-vj-Alg"> - <rect key="frame" x="0.0" y="0.0" width="932" height="430"/> + <rect key="frame" x="0.0" y="0.0" width="393" height="852"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Gmw-e3-n53" userLabel="Container" customClass="MWMNoMapsView"> - <rect key="frame" x="258.33333333333326" y="0.0" width="415.66666666666674" height="409"/> + <rect key="frame" x="13" y="24" width="367" height="804"/> <subviews> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dCZ-PN-2Ob" userLabel="BoundsView"> - <rect key="frame" x="16" y="0.0" width="383.66666666666669" height="409"/> + <rect key="frame" x="16" y="0.0" width="335" height="804"/> <subviews> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="87G-jh-N8H" userLabel="CenteredView"> - <rect key="frame" x="0.0" y="149.66666666666666" width="383.66666666666669" height="109.66666666666666"/> + <rect key="frame" x="0.0" y="347.5" width="335" height="109.5"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="У вас нет загруженных карт" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="abh-G0-Alr" userLabel="Title"> - <rect key="frame" x="0.0" y="40" width="383.66666666666669" height="24"/> + <rect key="frame" x="0.0" y="40" width="335" height="24"/> <fontDescription key="fontDescription" type="system" weight="medium" pointSize="20"/> <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <nil key="highlightedColor"/> @@ -971,7 +868,7 @@ </userDefinedRuntimeAttributes> </label> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Загрузите необходимые карты, чтобы находить места и пользоваться навигацией без интернета." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="LaW-Ad-mYI" userLabel="Text"> - <rect key="frame" x="0.0" y="76" width="383.66666666666669" height="33.666666666666657"/> + <rect key="frame" x="0.0" y="76" width="335" height="33.5"/> <fontDescription key="fontDescription" type="system" pointSize="14"/> <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <nil key="highlightedColor"/> @@ -1029,7 +926,7 @@ </connections> </view> <button opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Moj-UK-oyl" userLabel="DownloadMaps" customClass="MWMButton"> - <rect key="frame" x="346" y="325" width="240" height="44"/> + <rect key="frame" x="76.5" y="748" width="240" height="44"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <constraints> <constraint firstAttribute="width" constant="240" id="49x-bx-JJj"/> @@ -1074,11 +971,11 @@ <objects> <viewController storyboardIdentifier="SearchNoResultsViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="0VQ-EO-9Sv" customClass="SearchNoResultsViewController" customModule="Organic_Maps" customModuleProvider="target" sceneMemberID="viewController"> <view key="view" contentMode="scaleToFill" id="9jm-RW-DZK"> - <rect key="frame" x="0.0" y="0.0" width="932" height="430"/> + <rect key="frame" x="0.0" y="0.0" width="1032" height="1376"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="EbW-Mp-c6s"> - <rect key="frame" x="0.0" y="0.0" width="932" height="409"/> + <rect key="frame" x="0.0" y="172" width="1032" height="1032"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <constraints> <constraint firstAttribute="width" relation="greaterThanOrEqual" secondItem="EbW-Mp-c6s" secondAttribute="height" multiplier="1:1" id="tBC-sb-Q3g"/> -- 2.45.3 From a26158c0aba1ed2ca65c9b68b62d265162a7e863 Mon Sep 17 00:00:00 2001 From: Kiryl Kaveryn <kirylkaveryn@gmail.com> Date: Wed, 12 Feb 2025 19:53:34 +0400 Subject: [PATCH 12/12] [ios] remove outdated code related to the search the old MWMSearch... screen is not used anymore Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com> --- .../MWMMapViewControlsManager.h | 1 - iphone/Maps/Maps.xcodeproj/project.pbxproj | 56 -- iphone/Maps/UI/Search/MWMSearchContentView.h | 3 - iphone/Maps/UI/Search/MWMSearchContentView.m | 14 - .../Maps/UI/Search/MWMSearchManager+Layout.h | 8 - .../Maps/UI/Search/MWMSearchManager+Layout.m | 120 ---- iphone/Maps/UI/Search/MWMSearchManager.h | 34 -- iphone/Maps/UI/Search/MWMSearchManager.mm | 544 ------------------ .../Maps/UI/Search/MWMSearchManagerObserver.h | 5 - iphone/Maps/UI/Search/MWMSearchManagerState.h | 8 - iphone/Maps/UI/Search/MWMSearchView.xib | 266 --------- .../Maps/UI/Search/SearchActionBarView.swift | 56 -- iphone/Maps/UI/Search/SearchBar.swift | 57 -- iphone/Maps/UI/Search/SearchTextField.swift | 31 - .../UI/Search/TableView/MWMSearchTableView.h | 5 - .../UI/Search/TableView/MWMSearchTableView.m | 71 --- .../TableView/MWMSearchTableViewController.h | 27 - .../TableView/MWMSearchTableViewController.mm | 143 ----- .../MWMSearchTableViewController.xib | 79 --- .../Search/Tabs/MWMSearchTabbedViewProtocol.h | 12 - 20 files changed, 1540 deletions(-) delete mode 100644 iphone/Maps/UI/Search/MWMSearchContentView.h delete mode 100644 iphone/Maps/UI/Search/MWMSearchContentView.m delete mode 100644 iphone/Maps/UI/Search/MWMSearchManager+Layout.h delete mode 100644 iphone/Maps/UI/Search/MWMSearchManager+Layout.m delete mode 100644 iphone/Maps/UI/Search/MWMSearchManager.h delete mode 100644 iphone/Maps/UI/Search/MWMSearchManager.mm delete mode 100644 iphone/Maps/UI/Search/MWMSearchManagerObserver.h delete mode 100644 iphone/Maps/UI/Search/MWMSearchManagerState.h delete mode 100644 iphone/Maps/UI/Search/MWMSearchView.xib delete mode 100644 iphone/Maps/UI/Search/SearchActionBarView.swift delete mode 100644 iphone/Maps/UI/Search/SearchBar.swift delete mode 100644 iphone/Maps/UI/Search/SearchTextField.swift delete mode 100644 iphone/Maps/UI/Search/TableView/MWMSearchTableView.h delete mode 100644 iphone/Maps/UI/Search/TableView/MWMSearchTableView.m delete mode 100644 iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.h delete mode 100644 iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.mm delete mode 100644 iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.xib delete mode 100644 iphone/Maps/UI/Search/Tabs/MWMSearchTabbedViewProtocol.h diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.h b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.h index 28d7963528..b57d9bef95 100644 --- a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.h +++ b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.h @@ -1,7 +1,6 @@ #import "MWMBottomMenuState.h" #import "MWMMapDownloaderMode.h" #import "MWMNavigationDashboardManager.h" -#import "MWMSearchManager.h" @class MapViewController; @class BottomTabBarViewController; diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index bfc090f5f6..1023b1e8e8 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -86,7 +86,6 @@ 3490D2E31CE9DD2500D0B838 /* MWMSideButtonsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3490D2DD1CE9DD2500D0B838 /* MWMSideButtonsView.xib */; }; 349A13831DEC138C00C7DB60 /* MWMMobileInternetAlert.m in Sources */ = {isa = PBXBuildFile; fileRef = 349A13801DEC138C00C7DB60 /* MWMMobileInternetAlert.m */; }; 349A13851DEC138C00C7DB60 /* MWMMobileInternetAlert.xib in Resources */ = {isa = PBXBuildFile; fileRef = 349A13811DEC138C00C7DB60 /* MWMMobileInternetAlert.xib */; }; - 349D1ABC1E2D05EF004A2006 /* SearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349D1ABA1E2D05EF004A2006 /* SearchBar.swift */; }; 349D1AD51E2E325B004A2006 /* BottomMenuItemCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 349D1AC61E2E325B004A2006 /* BottomMenuItemCell.xib */; }; 349D1AE11E2E325C004A2006 /* BottomTabBarViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 349D1ACD1E2E325B004A2006 /* BottomTabBarViewController.xib */; }; 349D1CE41E3F836900A878FD /* UIViewController+Hierarchy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349D1CE21E3F836900A878FD /* UIViewController+Hierarchy.swift */; }; @@ -334,7 +333,6 @@ 99012852244732DB00C72B10 /* BottomTabBarBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9901284C244732DB00C72B10 /* BottomTabBarBuilder.swift */; }; 99012853244732DB00C72B10 /* BottomTabBarInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9901284D244732DB00C72B10 /* BottomTabBarInteractor.swift */; }; 990128562449A82500C72B10 /* BottomTabBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 990128552449A82400C72B10 /* BottomTabBarView.swift */; }; - 990F33B624BC915200D0F426 /* SearchActionBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 990F33B524BC915200D0F426 /* SearchActionBarView.swift */; }; 9917D17F2397B1D600A7E06E /* IPadModalPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9917D17E2397B1D600A7E06E /* IPadModalPresentationController.swift */; }; 991FCA2423B11E61009AD684 /* BookmarksStyleSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 991FCA2323B11E61009AD684 /* BookmarksStyleSheet.swift */; }; 993DF0B523F6B2EF00AC231A /* PlacePageTrackLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 993DF0B423F6B2EF00AC231A /* PlacePageTrackLayout.swift */; }; @@ -396,7 +394,6 @@ 995F1613244F0AA50060631D /* BottomMenuLayersCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 995F1611244F0AA40060631D /* BottomMenuLayersCell.swift */; }; 995F1614244F0AA50060631D /* BottomMenuLayersCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 995F1612244F0AA40060631D /* BottomMenuLayersCell.xib */; }; 996D108A24E3DBF2002DD0E2 /* BookmarksCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 996D108924E3DBF2002DD0E2 /* BookmarksCoordinator.swift */; }; - 9977E69C247BFB510073780C /* SearchTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9977E69B247BFB510073780C /* SearchTextField.swift */; }; 9977E6A12480E1EE0073780C /* BottomMenuLayerButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9977E6A02480E1EE0073780C /* BottomMenuLayerButton.swift */; }; 9977E6A32480F9BF0073780C /* BottomMenuLayerButtonRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9977E6A22480F9BF0073780C /* BottomMenuLayerButtonRenderer.swift */; }; 998927302449DE1500260CE2 /* TabBarArea.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9989272F2449DE1500260CE2 /* TabBarArea.swift */; }; @@ -630,20 +627,13 @@ F6E2FE7F1E097BA00083EBEC /* MWMPlacePageOpeningHoursCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F6E2FCBD1E097B9F0083EBEC /* MWMPlacePageOpeningHoursCell.xib */; }; F6E2FE821E097BA00083EBEC /* MWMPlacePageOpeningHoursDayView.m in Sources */ = {isa = PBXBuildFile; fileRef = F6E2FCBF1E097B9F0083EBEC /* MWMPlacePageOpeningHoursDayView.m */; }; F6E2FE851E097BA00083EBEC /* MWMPlacePageOpeningHoursWeekDayView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F6E2FCC01E097B9F0083EBEC /* MWMPlacePageOpeningHoursWeekDayView.xib */; }; - F6E2FED91E097BA00083EBEC /* MWMSearchContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = F6E2FCF51E097B9F0083EBEC /* MWMSearchContentView.m */; }; - F6E2FEDF1E097BA00083EBEC /* MWMSearchManager+Layout.m in Sources */ = {isa = PBXBuildFile; fileRef = F6E2FCF91E097B9F0083EBEC /* MWMSearchManager+Layout.m */; }; - F6E2FEE21E097BA00083EBEC /* MWMSearchManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = F6E2FCFB1E097B9F0083EBEC /* MWMSearchManager.mm */; }; F6E2FEE51E097BA00083EBEC /* MWMSearchNoResults.m in Sources */ = {isa = PBXBuildFile; fileRef = F6E2FCFD1E097B9F0083EBEC /* MWMSearchNoResults.m */; }; F6E2FEE81E097BA00083EBEC /* MWMSearchNoResults.xib in Resources */ = {isa = PBXBuildFile; fileRef = F6E2FCFE1E097B9F0083EBEC /* MWMSearchNoResults.xib */; }; - F6E2FEEE1E097BA00083EBEC /* MWMSearchView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F6E2FD011E097B9F0083EBEC /* MWMSearchView.xib */; }; F6E2FF2D1E097BA00083EBEC /* MWMSearchCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = F6E2FD2A1E097BA00083EBEC /* MWMSearchCell.mm */; }; F6E2FF301E097BA00083EBEC /* MWMSearchCommonCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = F6E2FD2C1E097BA00083EBEC /* MWMSearchCommonCell.mm */; }; F6E2FF331E097BA00083EBEC /* MWMSearchCommonCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F6E2FD2D1E097BA00083EBEC /* MWMSearchCommonCell.xib */; }; F6E2FF361E097BA00083EBEC /* MWMSearchSuggestionCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = F6E2FD2F1E097BA00083EBEC /* MWMSearchSuggestionCell.mm */; }; F6E2FF391E097BA00083EBEC /* MWMSearchSuggestionCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F6E2FD301E097BA00083EBEC /* MWMSearchSuggestionCell.xib */; }; - F6E2FF3C1E097BA00083EBEC /* MWMSearchTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = F6E2FD321E097BA00083EBEC /* MWMSearchTableView.m */; }; - F6E2FF3F1E097BA00083EBEC /* MWMSearchTableViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = F6E2FD341E097BA00083EBEC /* MWMSearchTableViewController.mm */; }; - F6E2FF421E097BA00083EBEC /* MWMSearchTableViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F6E2FD351E097BA00083EBEC /* MWMSearchTableViewController.xib */; }; F6E2FF451E097BA00083EBEC /* SettingsTableViewLinkCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6E2FD381E097BA00083EBEC /* SettingsTableViewLinkCell.swift */; }; F6E2FF481E097BA00083EBEC /* SettingsTableViewSelectableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6E2FD391E097BA00083EBEC /* SettingsTableViewSelectableCell.swift */; }; F6E2FF4B1E097BA00083EBEC /* SettingsTableViewSwitchCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6E2FD3A1E097BA00083EBEC /* SettingsTableViewSwitchCell.swift */; }; @@ -922,7 +912,6 @@ 34763EE91F2F394D00F4D2D3 /* MWMTextToSpeech+CPP.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MWMTextToSpeech+CPP.h"; sourceTree = "<group>"; }; 34763EEA1F2F3AD700F4D2D3 /* MWMTextToSpeechObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMTextToSpeechObserver.h; sourceTree = "<group>"; }; 34763F0B1F30CCAC00F4D2D3 /* MWMEditorCellType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMEditorCellType.h; sourceTree = "<group>"; }; - 347AD8081F28B4E6007ACB68 /* MWMSearchManagerObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMSearchManagerObserver.h; sourceTree = "<group>"; }; 34845DAD1E1649F6003D55B9 /* DownloaderNoResultsEmbedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloaderNoResultsEmbedViewController.swift; sourceTree = "<group>"; }; 34845DB11E165E24003D55B9 /* SearchNoResultsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchNoResultsViewController.swift; sourceTree = "<group>"; }; 34845DB51E166084003D55B9 /* Common.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Common.swift; sourceTree = "<group>"; }; @@ -945,7 +934,6 @@ 349A35771B53D4C9009677EE /* MWMCircularProgress.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MWMCircularProgress.xib; sourceTree = "<group>"; }; 349A35781B53D4C9009677EE /* MWMCircularProgressView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMCircularProgressView.h; sourceTree = "<group>"; }; 349A35791B53D4C9009677EE /* MWMCircularProgressView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MWMCircularProgressView.m; sourceTree = "<group>"; }; - 349D1ABA1E2D05EF004A2006 /* SearchBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchBar.swift; sourceTree = "<group>"; }; 349D1AC61E2E325B004A2006 /* BottomMenuItemCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BottomMenuItemCell.xib; sourceTree = "<group>"; }; 349D1ACD1E2E325B004A2006 /* BottomTabBarViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BottomTabBarViewController.xib; sourceTree = "<group>"; }; 349D1CE21E3F836900A878FD /* UIViewController+Hierarchy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Hierarchy.swift"; sourceTree = "<group>"; }; @@ -1007,7 +995,6 @@ 34B127E61FBDD358008713D9 /* MWMRouterTransitType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMRouterTransitType.h; sourceTree = "<group>"; }; 34B127E71FBDD410008713D9 /* MWMRouterTransitStepInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMRouterTransitStepInfo.h; sourceTree = "<group>"; }; 34B127E81FBDD410008713D9 /* MWMRouterTransitStepInfo.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMRouterTransitStepInfo.mm; sourceTree = "<group>"; }; - 34B3806B1F1E46E20087D65B /* MWMSearchManagerState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMSearchManagerState.h; sourceTree = "<group>"; }; 34B846A02029DCC10081ECCD /* BMCCategoriesHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BMCCategoriesHeader.swift; sourceTree = "<group>"; }; 34B846A72029E8110081ECCD /* BMCDefaultViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BMCDefaultViewModel.swift; sourceTree = "<group>"; }; 34B924401DC8A29C0008D971 /* MWMMailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMMailViewController.h; sourceTree = "<group>"; }; @@ -1265,7 +1252,6 @@ 9901284C244732DB00C72B10 /* BottomTabBarBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomTabBarBuilder.swift; sourceTree = "<group>"; }; 9901284D244732DB00C72B10 /* BottomTabBarInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomTabBarInteractor.swift; sourceTree = "<group>"; }; 990128552449A82400C72B10 /* BottomTabBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomTabBarView.swift; sourceTree = "<group>"; }; - 990F33B524BC915200D0F426 /* SearchActionBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchActionBarView.swift; sourceTree = "<group>"; }; 9917D17E2397B1D600A7E06E /* IPadModalPresentationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPadModalPresentationController.swift; sourceTree = "<group>"; }; 991FCA2323B11E61009AD684 /* BookmarksStyleSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksStyleSheet.swift; sourceTree = "<group>"; }; 993DF0B423F6B2EF00AC231A /* PlacePageTrackLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlacePageTrackLayout.swift; sourceTree = "<group>"; }; @@ -1331,7 +1317,6 @@ 995F1611244F0AA40060631D /* BottomMenuLayersCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomMenuLayersCell.swift; sourceTree = "<group>"; }; 995F1612244F0AA40060631D /* BottomMenuLayersCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BottomMenuLayersCell.xib; sourceTree = "<group>"; }; 996D108924E3DBF2002DD0E2 /* BookmarksCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksCoordinator.swift; sourceTree = "<group>"; }; - 9977E69B247BFB510073780C /* SearchTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTextField.swift; sourceTree = "<group>"; }; 9977E6A02480E1EE0073780C /* BottomMenuLayerButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomMenuLayerButton.swift; sourceTree = "<group>"; }; 9977E6A22480F9BF0073780C /* BottomMenuLayerButtonRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomMenuLayerButtonRenderer.swift; sourceTree = "<group>"; }; 9989272F2449DE1500260CE2 /* TabBarArea.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarArea.swift; sourceTree = "<group>"; }; @@ -1690,17 +1675,9 @@ F6E2FCBE1E097B9F0083EBEC /* MWMPlacePageOpeningHoursDayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMPlacePageOpeningHoursDayView.h; sourceTree = "<group>"; }; F6E2FCBF1E097B9F0083EBEC /* MWMPlacePageOpeningHoursDayView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MWMPlacePageOpeningHoursDayView.m; sourceTree = "<group>"; }; F6E2FCC01E097B9F0083EBEC /* MWMPlacePageOpeningHoursWeekDayView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MWMPlacePageOpeningHoursWeekDayView.xib; sourceTree = "<group>"; }; - F6E2FCF41E097B9F0083EBEC /* MWMSearchContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMSearchContentView.h; sourceTree = "<group>"; }; - F6E2FCF51E097B9F0083EBEC /* MWMSearchContentView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MWMSearchContentView.m; sourceTree = "<group>"; }; - F6E2FCF81E097B9F0083EBEC /* MWMSearchManager+Layout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MWMSearchManager+Layout.h"; sourceTree = "<group>"; }; - F6E2FCF91E097B9F0083EBEC /* MWMSearchManager+Layout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MWMSearchManager+Layout.m"; sourceTree = "<group>"; }; - F6E2FCFA1E097B9F0083EBEC /* MWMSearchManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMSearchManager.h; sourceTree = "<group>"; }; - F6E2FCFB1E097B9F0083EBEC /* MWMSearchManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMSearchManager.mm; sourceTree = "<group>"; }; F6E2FCFC1E097B9F0083EBEC /* MWMSearchNoResults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMSearchNoResults.h; sourceTree = "<group>"; }; F6E2FCFD1E097B9F0083EBEC /* MWMSearchNoResults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MWMSearchNoResults.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; F6E2FCFE1E097B9F0083EBEC /* MWMSearchNoResults.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MWMSearchNoResults.xib; sourceTree = "<group>"; }; - F6E2FD011E097B9F0083EBEC /* MWMSearchView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MWMSearchView.xib; sourceTree = "<group>"; }; - F6E2FD231E097BA00083EBEC /* MWMSearchTabbedViewProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMSearchTabbedViewProtocol.h; sourceTree = "<group>"; }; F6E2FD291E097BA00083EBEC /* MWMSearchCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMSearchCell.h; sourceTree = "<group>"; }; F6E2FD2A1E097BA00083EBEC /* MWMSearchCell.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMSearchCell.mm; sourceTree = "<group>"; }; F6E2FD2B1E097BA00083EBEC /* MWMSearchCommonCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMSearchCommonCell.h; sourceTree = "<group>"; }; @@ -1709,11 +1686,6 @@ F6E2FD2E1E097BA00083EBEC /* MWMSearchSuggestionCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMSearchSuggestionCell.h; sourceTree = "<group>"; }; F6E2FD2F1E097BA00083EBEC /* MWMSearchSuggestionCell.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMSearchSuggestionCell.mm; sourceTree = "<group>"; }; F6E2FD301E097BA00083EBEC /* MWMSearchSuggestionCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MWMSearchSuggestionCell.xib; sourceTree = "<group>"; }; - F6E2FD311E097BA00083EBEC /* MWMSearchTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMSearchTableView.h; sourceTree = "<group>"; }; - F6E2FD321E097BA00083EBEC /* MWMSearchTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MWMSearchTableView.m; sourceTree = "<group>"; }; - F6E2FD331E097BA00083EBEC /* MWMSearchTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMSearchTableViewController.h; sourceTree = "<group>"; }; - F6E2FD341E097BA00083EBEC /* MWMSearchTableViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = MWMSearchTableViewController.mm; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; - F6E2FD351E097BA00083EBEC /* MWMSearchTableViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MWMSearchTableViewController.xib; sourceTree = "<group>"; }; F6E2FD381E097BA00083EBEC /* SettingsTableViewLinkCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsTableViewLinkCell.swift; sourceTree = "<group>"; }; F6E2FD391E097BA00083EBEC /* SettingsTableViewSelectableCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsTableViewSelectableCell.swift; sourceTree = "<group>"; }; F6E2FD3A1E097BA00083EBEC /* SettingsTableViewSwitchCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsTableViewSwitchCell.swift; sourceTree = "<group>"; }; @@ -3894,24 +3866,12 @@ isa = PBXGroup; children = ( ED70D5882D539A2500738C1E /* SearchOnMap */, - F6E2FCF41E097B9F0083EBEC /* MWMSearchContentView.h */, - F6E2FCF51E097B9F0083EBEC /* MWMSearchContentView.m */, - F6E2FCF81E097B9F0083EBEC /* MWMSearchManager+Layout.h */, - F6E2FCF91E097B9F0083EBEC /* MWMSearchManager+Layout.m */, - F6E2FCFA1E097B9F0083EBEC /* MWMSearchManager.h */, - F6E2FCFB1E097B9F0083EBEC /* MWMSearchManager.mm */, F6E2FCFC1E097B9F0083EBEC /* MWMSearchNoResults.h */, F6E2FCFD1E097B9F0083EBEC /* MWMSearchNoResults.m */, F6E2FCFE1E097B9F0083EBEC /* MWMSearchNoResults.xib */, - 9977E69B247BFB510073780C /* SearchTextField.swift */, - F6E2FD011E097B9F0083EBEC /* MWMSearchView.xib */, F6E2FD021E097B9F0083EBEC /* Tabs */, F6E2FD281E097BA00083EBEC /* TableView */, 34845DB11E165E24003D55B9 /* SearchNoResultsViewController.swift */, - 349D1ABA1E2D05EF004A2006 /* SearchBar.swift */, - 34B3806B1F1E46E20087D65B /* MWMSearchManagerState.h */, - 347AD8081F28B4E6007ACB68 /* MWMSearchManagerObserver.h */, - 990F33B524BC915200D0F426 /* SearchActionBarView.swift */, 475ED78724C7D0F30063ADC7 /* ValueStepperView.swift */, ); path = Search; @@ -3922,7 +3882,6 @@ children = ( F6E2FD091E097B9F0083EBEC /* CategoriesTab */, F6E2FD0F1E097B9F0083EBEC /* HistoryTab */, - F6E2FD231E097BA00083EBEC /* MWMSearchTabbedViewProtocol.h */, 337F98A521D37B7400C8AC27 /* SearchTabViewController.swift */, ); path = Tabs; @@ -3957,11 +3916,6 @@ F6E2FD2E1E097BA00083EBEC /* MWMSearchSuggestionCell.h */, F6E2FD2F1E097BA00083EBEC /* MWMSearchSuggestionCell.mm */, F6E2FD301E097BA00083EBEC /* MWMSearchSuggestionCell.xib */, - F6E2FD311E097BA00083EBEC /* MWMSearchTableView.h */, - F6E2FD321E097BA00083EBEC /* MWMSearchTableView.m */, - F6E2FD331E097BA00083EBEC /* MWMSearchTableViewController.h */, - F6E2FD341E097BA00083EBEC /* MWMSearchTableViewController.mm */, - F6E2FD351E097BA00083EBEC /* MWMSearchTableViewController.xib */, ); path = TableView; sourceTree = "<group>"; @@ -4408,9 +4362,7 @@ F6E2FF391E097BA00083EBEC /* MWMSearchSuggestionCell.xib in Resources */, 34AB66231FC5AA330078E451 /* MWMiPadRoutePreview.xib in Resources */, BB25B1A71FB32767007276FA /* transit_colors.txt in Resources */, - F6E2FF421E097BA00083EBEC /* MWMSearchTableViewController.xib in Resources */, 34AB66681FC5AA330078E451 /* TransportTransitPedestrian.xib in Resources */, - F6E2FEEE1E097BA00083EBEC /* MWMSearchView.xib in Resources */, 4501B1952077C35A001B9173 /* resources-xxxhdpi_dark in Resources */, F6D67CDE2062BBA60032FD38 /* MWMBCCreateCategoryAlert.xib in Resources */, 3490D2E31CE9DD2500D0B838 /* MWMSideButtonsView.xib in Resources */, @@ -4501,7 +4453,6 @@ 34D3AFF61E37A36A004100F9 /* UICollectionView+Cells.swift in Sources */, 4767CDA420AAF66B00BD8166 /* NSAttributedString+HTML.swift in Sources */, 6741A9A91BF340DE002C974C /* MWMDefaultAlert.mm in Sources */, - 990F33B624BC915200D0F426 /* SearchActionBarView.swift in Sources */, 99514BBA23E82B450085D3A7 /* ElevationProfileViewController.swift in Sources */, 340708781F2B5D6C00029ECC /* DimBackground.swift in Sources */, 3490D2DF1CE9DD2500D0B838 /* MWMSideButtons.mm in Sources */, @@ -4553,8 +4504,6 @@ 6741A9B81BF340DE002C974C /* MapViewController.mm in Sources */, 34AB662C1FC5AA330078E451 /* RouteManagerViewModel.swift in Sources */, 3404F48B202894EA0090E401 /* BMCViewController.swift in Sources */, - 349D1ABC1E2D05EF004A2006 /* SearchBar.swift in Sources */, - F6E2FF3F1E097BA00083EBEC /* MWMSearchTableViewController.mm in Sources */, F6E2FDE01E097BA00083EBEC /* MWMEditorViewController.mm in Sources */, 6741A9C01BF340DE002C974C /* MWMTextView.m in Sources */, F6E2FDB61E097BA00083EBEC /* MWMEditorAdditionalNamesHeader.m in Sources */, @@ -4592,7 +4541,6 @@ F6791B141C43DF0B007A8A6E /* MWMStartButton.m in Sources */, 9977E6A12480E1EE0073780C /* BottomMenuLayerButton.swift in Sources */, 471527372491C20500E91BBA /* SelectBookmarkGroupViewController.swift in Sources */, - F6E2FEDF1E097BA00083EBEC /* MWMSearchManager+Layout.m in Sources */, F64D9CA01C899C350063FA30 /* MWMEditorViralAlert.mm in Sources */, 34AC8FD11EFC02C000E7F910 /* MWMRoutePoint.mm in Sources */, CDB4D5012231412900104869 /* ListTemplateBuilder.swift in Sources */, @@ -4754,7 +4702,6 @@ 3472B5CF200F4A2B00DC6CD5 /* BackgroundFetchTask.swift in Sources */, 477219052243E79500E5B227 /* DrivingOptionsViewController.swift in Sources */, CDB4D4E4222E8FF600104869 /* CarPlayService.swift in Sources */, - F6E2FF3C1E097BA00083EBEC /* MWMSearchTableView.m in Sources */, F6E2FF661E097BA00083EBEC /* MWMTTSSettingsViewController.mm in Sources */, 3454D7C21E07F045004AF2AD /* NSString+Categories.m in Sources */, ED2D74662D1435A600660FBF /* LiveActivityManager.swift in Sources */, @@ -4823,7 +4770,6 @@ CDCA27812243F59800167D87 /* CarPlayRouter.swift in Sources */, 34F5E0D41E3F254800B1C415 /* UIView+Hierarchy.swift in Sources */, 6741AA0B1BF340DE002C974C /* MWMMapViewControlsManager.mm in Sources */, - F6E2FED91E097BA00083EBEC /* MWMSearchContentView.m in Sources */, EDFDFB4C2B722C9C0013A44C /* InfoTableViewCell.swift in Sources */, 47CA68F8250F8AB700671019 /* BookmarksListSectionHeader.swift in Sources */, F6BD1D211CA412920047B8E8 /* MWMOsmAuthAlert.mm in Sources */, @@ -4850,7 +4796,6 @@ 4767CDA820AB401000BD8166 /* LinkTextView.swift in Sources */, 34763EE71F2F392300F4D2D3 /* MWMTextToSpeech.mm in Sources */, 998927402449ECC200260CE2 /* BottomMenuItemCell.swift in Sources */, - F6E2FEE21E097BA00083EBEC /* MWMSearchManager.mm in Sources */, F6E2FE221E097BA00083EBEC /* MWMOpeningHoursEditorViewController.mm in Sources */, ED79A5D72BDF8D6100952D1F /* SynchronizationStateResolver.swift in Sources */, 999FC12B23ABB4B800B0E6F9 /* FontStyleSheet.swift in Sources */, @@ -4877,7 +4822,6 @@ 34B127EA1FBDD410008713D9 /* MWMRouterTransitStepInfo.mm in Sources */, 34D3B02A1E389D05004100F9 /* MWMEditorAdditionalNameTableViewCell.m in Sources */, 993DF10523F6BDB100AC231A /* UINavigationItem+StyleStyle.swift in Sources */, - 9977E69C247BFB510073780C /* SearchTextField.swift in Sources */, 4726254921C27D4B00C7BAAD /* PlacePageDescriptionViewController.swift in Sources */, 039371B62C5B68CD00708377 /* UIFont+monospaced.swift in Sources */, 447DB4BC2BA78282000DF4C2 /* MWMOsmReauthAlert.mm in Sources */, diff --git a/iphone/Maps/UI/Search/MWMSearchContentView.h b/iphone/Maps/UI/Search/MWMSearchContentView.h deleted file mode 100644 index 5a28e7be6e..0000000000 --- a/iphone/Maps/UI/Search/MWMSearchContentView.h +++ /dev/null @@ -1,3 +0,0 @@ -@interface MWMSearchContentView : SolidTouchView - -@end diff --git a/iphone/Maps/UI/Search/MWMSearchContentView.m b/iphone/Maps/UI/Search/MWMSearchContentView.m deleted file mode 100644 index 6f9cb7c566..0000000000 --- a/iphone/Maps/UI/Search/MWMSearchContentView.m +++ /dev/null @@ -1,14 +0,0 @@ -#import "MWMSearchContentView.h" - -@implementation MWMSearchContentView - -- (void)layoutSubviews -{ - [self.subviews enumerateObjectsUsingBlock:^(UIView * view, NSUInteger idx, BOOL * stop) - { - view.frame = self.bounds; - }]; - [super layoutSubviews]; -} - -@end diff --git a/iphone/Maps/UI/Search/MWMSearchManager+Layout.h b/iphone/Maps/UI/Search/MWMSearchManager+Layout.h deleted file mode 100644 index c39e4c5980..0000000000 --- a/iphone/Maps/UI/Search/MWMSearchManager+Layout.h +++ /dev/null @@ -1,8 +0,0 @@ -#import "MWMSearchManager.h" - -@interface MWMSearchManager (Layout) - -- (void)layoutTopViews; -- (void)removeKeyboardObservers; - -@end diff --git a/iphone/Maps/UI/Search/MWMSearchManager+Layout.m b/iphone/Maps/UI/Search/MWMSearchManager+Layout.m deleted file mode 100644 index 2220e61943..0000000000 --- a/iphone/Maps/UI/Search/MWMSearchManager+Layout.m +++ /dev/null @@ -1,120 +0,0 @@ -#import <CoreApi/MWMCommon.h> -#import "MWMSearchManager+Layout.h" -#import "MapViewController.h" - -static CGFloat const changeModeViewOffsetNormal = -24; -static CGFloat const changeModeViewOffsetKeyboard = -12; - -@interface MWMSearchManager () - -@property(nonatomic) IBOutlet UIView *searchBarView; -@property(nonatomic) IBOutlet UIView *actionBarView; -@property(nonatomic) IBOutlet UIView *contentView; - -@property(nonatomic) NSLayoutConstraint *contentViewTopHidden; -@property(nonatomic) NSLayoutConstraint *contentViewBottomHidden; -@property(nonatomic) NSLayoutConstraint *actionBarViewBottomKeyboard; -@property(nonatomic) NSLayoutConstraint *actionBarViewBottomNormal; - -@property(weak, nonatomic, readonly) UIView *searchViewContainer; - -@end - -@implementation MWMSearchManager (Layout) - -- (void)layoutTopViews { - UIView *searchBarView = self.searchBarView; - UIView *changeModeView = self.actionBarView; - UIView *contentView = self.contentView; - UIView *parentView = self.searchViewContainer; - - searchBarView.translatesAutoresizingMaskIntoConstraints = NO; - changeModeView.translatesAutoresizingMaskIntoConstraints = NO; - contentView.translatesAutoresizingMaskIntoConstraints = NO; - - NSLayoutXAxisAnchor *leadingAnchor = parentView.leadingAnchor; - NSLayoutXAxisAnchor *trailingAnchor = parentView.trailingAnchor; - NSLayoutYAxisAnchor *topAnchor = parentView.safeAreaLayoutGuide.topAnchor; - NSLayoutYAxisAnchor *bottomAnchor = parentView.safeAreaLayoutGuide.bottomAnchor; - - [searchBarView.topAnchor constraintEqualToAnchor:topAnchor].active = YES; - [searchBarView.leadingAnchor constraintEqualToAnchor:leadingAnchor].active = YES; - if (IPAD) - [searchBarView.widthAnchor constraintEqualToConstant:kWidthForiPad].active = YES; - else - [searchBarView.trailingAnchor constraintEqualToAnchor:trailingAnchor].active = YES; - - [changeModeView.centerXAnchor constraintEqualToAnchor:parentView.centerXAnchor].active = YES; - self.actionBarViewBottomNormal = [changeModeView.bottomAnchor constraintEqualToAnchor:bottomAnchor - constant:changeModeViewOffsetNormal]; - self.actionBarViewBottomNormal.priority = UILayoutPriorityDefaultLow + 10; - self.actionBarViewBottomNormal.active = YES; - - self.actionBarViewBottomKeyboard = [changeModeView.bottomAnchor constraintEqualToAnchor:parentView.bottomAnchor - constant:changeModeViewOffsetKeyboard]; - self.actionBarViewBottomKeyboard.priority = UILayoutPriorityDefaultLow; - self.actionBarViewBottomKeyboard.active = YES; - - NSLayoutConstraint *contentViewTop = [contentView.topAnchor constraintEqualToAnchor:searchBarView.bottomAnchor]; - contentViewTop.priority = UILayoutPriorityDefaultLow + 10; - contentViewTop.active = YES; - - NSLayoutConstraint *contentViewBottom = [contentView.bottomAnchor constraintEqualToAnchor:parentView.bottomAnchor]; - contentViewBottom.priority = UILayoutPriorityDefaultLow + 10; - contentViewBottom.active = YES; - - self.contentViewTopHidden = [contentView.topAnchor constraintEqualToAnchor:parentView.bottomAnchor]; - self.contentViewTopHidden.priority = UILayoutPriorityDefaultLow; - self.contentViewTopHidden.active = YES; - - self.contentViewBottomHidden = [contentView.heightAnchor constraintEqualToAnchor:parentView.heightAnchor]; - self.contentViewBottomHidden.priority = UILayoutPriorityDefaultLow; - self.contentViewBottomHidden.active = YES; - - [contentView.leadingAnchor constraintEqualToAnchor:searchBarView.leadingAnchor].active = YES; - [contentView.trailingAnchor constraintEqualToAnchor:searchBarView.trailingAnchor].active = YES; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(keyboardWillShow:) - name:UIKeyboardWillShowNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(keyboardWillHide:) - name:UIKeyboardWillHideNotification - object:nil]; -} - -- (void)removeKeyboardObservers { - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -#pragma mark - keyboard movements -- (void)keyboardWillShow:(NSNotification *)notification { - CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size; - CGFloat offset = IPAD ? changeModeViewOffsetNormal : changeModeViewOffsetKeyboard; - if (self.actionBarView.isHidden) { - self.actionBarViewBottomKeyboard.constant = -keyboardSize.height + offset; - self.actionBarViewBottomKeyboard.priority = UILayoutPriorityDefaultHigh; - } else { - [UIView animateWithDuration:kDefaultAnimationDuration - animations:^{ - self.actionBarViewBottomKeyboard.constant = -keyboardSize.height + offset; - self.actionBarViewBottomKeyboard.priority = UILayoutPriorityDefaultHigh; - [self.actionBarView.superview layoutIfNeeded]; - }]; - } -} - -- (void)keyboardWillHide:(NSNotification *)notification { - if (self.actionBarView.isHidden) { - self.actionBarViewBottomKeyboard.priority = UILayoutPriorityDefaultLow; - } else { - [UIView animateWithDuration:kDefaultAnimationDuration - animations:^{ - self.actionBarViewBottomKeyboard.priority = UILayoutPriorityDefaultLow; - [self.actionBarView.superview layoutIfNeeded]; - }]; - } -} - -@end diff --git a/iphone/Maps/UI/Search/MWMSearchManager.h b/iphone/Maps/UI/Search/MWMSearchManager.h deleted file mode 100644 index 8c3309dd73..0000000000 --- a/iphone/Maps/UI/Search/MWMSearchManager.h +++ /dev/null @@ -1,34 +0,0 @@ -#import "MWMAlertViewController.h" -#import "MWMSearchManagerObserver.h" -#import "MWMSearchManagerState.h" - -typedef NS_ENUM(NSInteger, MWMSearchManagerRoutingTooltipSearch) { - MWMSearchManagerRoutingTooltipSearchNone, - MWMSearchManagerRoutingTooltipSearchStart, - MWMSearchManagerRoutingTooltipSearchFinish -}; -@class SearchTextField; - -@interface MWMSearchManager : NSObject - -extern const CGFloat kWidthForiPad; - -+ (nonnull MWMSearchManager *)manager NS_SWIFT_NAME(manager()); -+ (void)addObserver:(nonnull id<MWMSearchManagerObserver>)observer; -+ (void)removeObserver:(nonnull id<MWMSearchManagerObserver>)observer; - -@property(nullable, weak, nonatomic) IBOutlet SearchTextField *searchTextField; - -@property(nonatomic) MWMSearchManagerState state; -@property(nonatomic) MWMSearchManagerRoutingTooltipSearch routingTooltipSearch; - -@property(nonnull, nonatomic) IBOutletCollection(UIView) NSArray *topViews; - -- (void)searchText:(nonnull NSString *)text forInputLocale:(nullable NSString *)locale withCategory:(BOOL)isCategory; - -#pragma mark - Layout - -- (void)viewWillTransitionToSize:(CGSize)size - withTransitionCoordinator:(nonnull id<UIViewControllerTransitionCoordinator>)coordinator; - -@end diff --git a/iphone/Maps/UI/Search/MWMSearchManager.mm b/iphone/Maps/UI/Search/MWMSearchManager.mm deleted file mode 100644 index 29cd6c6e24..0000000000 --- a/iphone/Maps/UI/Search/MWMSearchManager.mm +++ /dev/null @@ -1,544 +0,0 @@ -#import "MWMSearchManager.h" -#import "MWMFrameworkListener.h" -#import "MWMMapViewControlsManager.h" -#import "MWMNoMapsViewController.h" -#import "MWMRoutePoint+CPP.h" -#import "MWMRouter.h" -#import "MWMSearchManager+Layout.h" -#import "MWMSearchTableViewController.h" -#import "MapViewController.h" -#import "SearchResult.h" -#import "SwiftBridge.h" - -namespace { -typedef NS_ENUM(NSUInteger, MWMSearchManagerActionBarState) { - MWMSearchManagerActionBarStateHidden, - MWMSearchManagerActionBarStateTabBar, - MWMSearchManagerActionBarStateModeFilter -}; - -using Observer = id<MWMSearchManagerObserver>; -using Observers = NSHashTable<Observer>; -} // namespace - -const CGFloat kWidthForiPad = 320; - -@interface MWMMapViewControlsManager () - -@property(nonatomic) MWMSearchManager *searchManager; - -@end - -@interface MWMSearchManager () <MWMSearchTableViewProtocol, - MWMSearchTabViewControllerDelegate, - UITextFieldDelegate, - MWMStorageObserver, - MWMSearchObserver> - -@property(weak, nonatomic, readonly) UIViewController *ownerController; -@property(weak, nonatomic, readonly) UIView *searchViewContainer; -@property(weak, nonatomic, readonly) UIView *actionBarContainer; -@property(weak, nonatomic, readonly) MWMMapViewControlsManager *controlsManager; - -@property(nonatomic) IBOutlet SearchBar *searchBarView; -@property(weak, nonatomic) IBOutlet SearchActionBarView *actionBarView; -@property(nonatomic) IBOutlet UIView *contentView; -@property(strong, nonatomic) IBOutlet UIView *tableViewContainer; - -@property(nonatomic) NSLayoutConstraint *contentViewTopHidden; -@property(nonatomic) NSLayoutConstraint *contentViewBottomHidden; -@property(nonatomic) NSLayoutConstraint *actionBarViewBottomKeyboard; -@property(nonatomic) NSLayoutConstraint *actionBarViewBottomNormal; - -@property(nonatomic) UINavigationController *navigationController; -@property(nonatomic) MWMSearchTableViewController *tableViewController; -@property(nonatomic) MWMNoMapsViewController *noMapsController; - -@property(nonatomic) Observers *observers; - -@end - -@implementation MWMSearchManager - -+ (MWMSearchManager *)manager { - static MWMSearchManager * manager; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - manager = [[MWMSearchManager alloc] init]; - }); - return manager; -} - -- (nullable instancetype)init { - self = [super init]; - if (self) { - [NSBundle.mainBundle loadNibNamed:@"MWMSearchView" owner:self options:nil]; - self.state = MWMSearchManagerStateHidden; - [MWMSearch addObserver:self]; - _observers = [Observers weakObjectsHashTable]; - } - return self; -} - -- (void)beginSearch { - if (self.state != MWMSearchManagerStateHidden) - self.state = MWMSearchManagerStateTableSearch; -} - -- (void)endSearch { - if (self.state != MWMSearchManagerStateHidden) - self.state = MWMSearchManagerStateDefault; - self.searchTextField.text = @""; - [MWMSearch clear]; -} - -- (void)closeSearch { - [self.searchTextField endEditing:YES]; - [self endSearch]; -} - -#pragma mark - Actions - -- (IBAction)textFieldDidEndEditing:(UITextField *)textField { - if (textField.text.length == 0) - [self endSearch]; -} - -- (IBAction)textFieldTextDidChange:(UITextField *)textField { - NSString *text = textField.text; - if (text.length > 0) { - [self beginSearch]; - [MWMSearch searchQuery:text forInputLocale:textField.textInputMode.primaryLanguage withCategory:NO]; - } else { - [self endSearch]; - } -} - -- (IBAction)cancelButtonPressed { - self.state = MWMSearchManagerStateHidden; -} - -- (IBAction)backButtonPressed { - self.state = MWMSearchManagerStateTableSearch; -} - -#pragma mark - Layout - -- (void)viewWillTransitionToSize:(CGSize)size - withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { - [self.navigationController viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; -} - -#pragma mark - UITextFieldDelegate - -- (void)textFieldDidBeginEditing:(UITextField *)textField { - BOOL const isEmpty = (textField.text.length == 0); - self.state = isEmpty ? MWMSearchManagerStateDefault : MWMSearchManagerStateTableSearch; -} - -- (BOOL)textFieldShouldReturn:(UITextField *)textField { - textField.text = [[textField.text stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet] - stringByAppendingString:@" "]; - [self textFieldTextDidChange:textField]; - [textField resignFirstResponder]; - return YES; -} - -#pragma mark - MWMSearchTabbedViewProtocol - -- (void)searchText:(NSString *)text forInputLocale:(NSString *)locale withCategory:(BOOL)isCategory { - [self beginSearch]; - self.searchTextField.text = text; - NSString *inputLocale = locale ?: self.searchTextField.textInputMode.primaryLanguage; - [MWMSearch searchQuery:text forInputLocale:inputLocale withCategory:isCategory]; -} - -- (void)dismissKeyboard { - [self.searchTextField resignFirstResponder]; -} -- (void)processSearchResultAtIndex:(NSInteger)index { - if (self.routingTooltipSearch == MWMSearchManagerRoutingTooltipSearchNone) { - [MWMSearch showResultAtIndex:index]; - } else { - BOOL const isStart = self.routingTooltipSearch == MWMSearchManagerRoutingTooltipSearchStart; - SearchResult * result = [MWMSearch resultWithContainerIndex:index]; - auto point = [[MWMRoutePoint alloc] initWithCGPoint:result.point - title:result.titleText - subtitle:result.addressText - type:isStart ? MWMRoutePointTypeStart : MWMRoutePointTypeFinish - intermediateIndex:0]; - if (isStart) - [MWMRouter buildFromPoint:point bestRouter:NO]; - else - [MWMRouter buildToPoint:point bestRouter:NO]; - } - if (!IPAD || [MWMNavigationDashboardManager sharedManager].state != MWMNavigationDashboardStateHidden) - self.state = MWMSearchManagerStateResult; -} - -#pragma mark - MWMStorageObserver - -- (void)processCountryEvent:(NSString *)countryId { - using namespace storage; - NodeStatuses nodeStatuses{}; - GetFramework().GetStorage().GetNodeStatuses(countryId.UTF8String, nodeStatuses); - if (nodeStatuses.m_status != NodeStatus::OnDisk) - return; - [self updateTopController]; - if (self.state == MWMSearchManagerStateTableSearch || self.state == MWMSearchManagerStateMapSearch) { - NSString *text = self.searchTextField.text; - if (text.length != 0) - [MWMSearch searchQuery:text forInputLocale:self.searchTextField.textInputMode.primaryLanguage withCategory:NO]; - } -} - -#pragma mark - State changes - -- (void)updateTopController { - UIViewController *selfTopVC = self.topController; - if (!selfTopVC || [selfTopVC isEqual:self.navigationController.topViewController]) - return; - NSMutableArray *viewControllers = [self.navigationController.viewControllers mutableCopy]; - viewControllers[0] = selfTopVC; - [self.navigationController setViewControllers:viewControllers animated:NO]; -} - -- (void)changeToHiddenState { - self.routingTooltipSearch = MWMSearchManagerRoutingTooltipSearchNone; - [self closeSearch]; - - MWMMapViewControlsManager *controlsManager = self.controlsManager; - auto const navigationManagerState = [MWMNavigationDashboardManager sharedManager].state; - if (navigationManagerState == MWMNavigationDashboardStateHidden) { - controlsManager.menuState = controlsManager.menuRestoreState; - } - [self viewHidden:YES]; -} - -- (void)changeToDefaultState { - MWMMapViewControlsManager *controlsManager = self.controlsManager; - - [self.navigationController popToRootViewControllerAnimated:NO]; - - self.searchBarView.state = SearchBarStateReady; - GetFramework().DeactivateMapSelection(); - [self animateConstraints:^{ - self.contentViewTopHidden.priority = UILayoutPriorityDefaultLow; - self.contentViewBottomHidden.priority = UILayoutPriorityDefaultLow; - }]; - auto const navigationManagerState = [MWMNavigationDashboardManager sharedManager].state; - if (navigationManagerState == MWMNavigationDashboardStateHidden) { - controlsManager.menuState = controlsManager.menuRestoreState; - } - [self viewHidden:NO]; - self.actionBarState = MWMSearchManagerActionBarStateHidden; - [self.searchTextField becomeFirstResponder]; - [self.searchBarView applyTheme]; -} - -- (void)changeToTableSearchState { - MWMMapViewControlsManager *controlsManager = self.controlsManager; - - [self.navigationController popToRootViewControllerAnimated:NO]; - - self.searchBarView.state = SearchBarStateReady; - GetFramework().DeactivateMapSelection(); - [self updateTableSearchActionBar]; - auto const navigationManagerState = [MWMNavigationDashboardManager sharedManager].state; - if (navigationManagerState == MWMNavigationDashboardStateHidden) { - controlsManager.menuState = controlsManager.menuRestoreState; - } - [self viewHidden:NO]; - [self.tableViewController reloadData]; - - if (![self.navigationController.viewControllers containsObject:self.tableViewController]) - [self.navigationController pushViewController:self.tableViewController animated:NO]; -} - -- (void)changeToMapSearchState { - [self.navigationController popToRootViewControllerAnimated:NO]; - - self.searchBarView.state = SearchBarStateBack; - self.actionBarState = MWMSearchManagerActionBarStateModeFilter; - if (!IPAD) { - [self animateConstraints:^{ - self.contentViewTopHidden.priority = UILayoutPriorityDefaultHigh; - self.contentViewBottomHidden.priority = UILayoutPriorityDefaultHigh; - }]; - } - auto const navigationManagerState = [MWMNavigationDashboardManager sharedManager].state; - [self viewHidden:navigationManagerState != MWMNavigationDashboardStateHidden]; - self.controlsManager.menuState = MWMBottomMenuStateHidden; - GetFramework().DeactivateMapSelection(); - [self.tableViewController reloadData]; - - [self.searchTextField resignFirstResponder]; - - if (navigationManagerState == MWMNavigationDashboardStateNavigation) { - self.searchTextField.text = @""; - } -} - -- (void)changeToResultSearchState { - [self.navigationController popToRootViewControllerAnimated:NO]; - - self.searchBarView.state = SearchBarStateBack; - self.actionBarState = MWMSearchManagerActionBarStateModeFilter; - if (!IPAD) { - [self animateConstraints:^{ - self.contentViewTopHidden.priority = UILayoutPriorityDefaultHigh; - self.contentViewBottomHidden.priority = UILayoutPriorityDefaultHigh; - - }]; - } - auto const navigationManagerState = [MWMNavigationDashboardManager sharedManager].state; - [self viewHidden:navigationManagerState != MWMNavigationDashboardStateHidden]; - [self.tableViewController reloadData]; - - [self.searchTextField resignFirstResponder]; - - if (navigationManagerState == MWMNavigationDashboardStateNavigation) { - self.searchTextField.text = @""; - } -} - -- (void)animateConstraints:(MWMVoidBlock)block { - UIView *parentView = self.searchViewContainer; - [parentView layoutIfNeeded]; - block(); - [UIView animateWithDuration:kDefaultAnimationDuration - animations:^{ - [parentView layoutIfNeeded]; - }]; -} - -#pragma mark - MWMSearchObserver - -- (void)onSearchCompleted { - if (self.state == MWMSearchManagerStateMapSearch || self.state == MWMSearchManagerStateResult) { - self.searchBarView.state = SearchBarStateBack; - } else { - self.searchBarView.state = SearchBarStateReady; - } - - if (self.state != MWMSearchManagerStateTableSearch) - return; - [self.tableViewController onSearchCompleted]; - [self updateTableSearchActionBar]; -} - -- (void)onSearchStarted { - self.searchBarView.state = SearchBarStateSearching; - if (self.state != MWMSearchManagerStateTableSearch) - return; - self.actionBarState = MWMSearchManagerActionBarStateModeFilter; -} - -- (void)onSearchResultsUpdated { - [self.tableViewController reloadData]; -} - -- (void)updateTableSearchActionBar { - if (self.state != MWMSearchManagerStateTableSearch) - return; - [self animateConstraints:^{ - BOOL hideActionBar = NO; - if ([MWMSearch resultsCount] == 0) - hideActionBar = YES; - else if (IPAD) - hideActionBar = YES; - self.actionBarState = - hideActionBar ? MWMSearchManagerActionBarStateHidden : MWMSearchManagerActionBarStateModeFilter; - - self.contentViewTopHidden.priority = UILayoutPriorityDefaultLow; - self.contentViewBottomHidden.priority = UILayoutPriorityDefaultLow; - }]; -} - -#pragma mark - Add/Remove Observers - -+ (void)addObserver:(id<MWMSearchManagerObserver>)observer { - [[MWMSearchManager manager].observers addObject:observer]; -} - -+ (void)removeObserver:(id<MWMSearchManagerObserver>)observer { - [[MWMSearchManager manager].observers removeObject:observer]; -} - -#pragma mark - MWMSearchManagerObserver - -- (void)onSearchManagerStateChanged { - for (Observer observer in self.observers) - [observer onSearchManagerStateChanged]; -} - -#pragma mark - Filters - -- (IBAction)changeMode { - switch (self.state) { - case MWMSearchManagerStateTableSearch: - self.state = MWMSearchManagerStateMapSearch; - break; - case MWMSearchManagerStateMapSearch: - self.state = MWMSearchManagerStateTableSearch; - break; - default: - break; - } -} - -#pragma mark - Properties - -- (UINavigationController *)navigationController { - if (!_navigationController) { - _navigationController = [[UINavigationController alloc] init]; - [self.contentView addSubview:_navigationController.view]; - _navigationController.navigationBarHidden = YES; - } - return _navigationController; -} - -- (UIViewController *)topController { - [[MWMStorage sharedStorage] removeObserver:self]; - self.noMapsController = nil; - switch (self.state) { - case MWMSearchManagerStateHidden: - return nil; - case MWMSearchManagerStateDefault: { - if (GetFramework().GetStorage().HaveDownloadedCountries()) { - MWMSearchTabViewController *tabViewController = [MWMSearchTabViewController new]; - tabViewController.delegate = self; - return tabViewController; - } - self.noMapsController = [MWMNoMapsViewController controller]; - [[MWMStorage sharedStorage] addObserver:self]; - return self.noMapsController; - } - case MWMSearchManagerStateTableSearch: - return self.tableViewController; - case MWMSearchManagerStateMapSearch: - return self.tableViewController; - case MWMSearchManagerStateResult: - return self.tableViewController; - } -} - -- (void)searchTabController:(MWMSearchTabViewController *)viewController - didSearch:(NSString *)didSearch - withCategory:(BOOL)isCategory -{ - [self searchText:didSearch forInputLocale:[[AppInfo sharedInfo] languageId] withCategory:isCategory]; -} - -- (MWMSearchTableViewController *)tableViewController { - if (!_tableViewController) - _tableViewController = [[MWMSearchTableViewController alloc] initWithDelegate:self]; - return _tableViewController; -} - -- (void)setState:(MWMSearchManagerState)state { - if (_state == state) - return; - - _state = state; - [self updateTopController]; - switch (state) { - case MWMSearchManagerStateHidden: - [self changeToHiddenState]; - break; - case MWMSearchManagerStateDefault: - [self changeToDefaultState]; - break; - case MWMSearchManagerStateTableSearch: - [self changeToTableSearchState]; - break; - case MWMSearchManagerStateMapSearch: - [self changeToMapSearchState]; - break; - case MWMSearchManagerStateResult: - [self changeToResultSearchState]; - break; - } - [self onSearchManagerStateChanged]; - [self.actionBarView updateForState:state]; - [[MapViewController sharedController] updateStatusBarStyle]; -} - -- (void)viewHidden:(BOOL)hidden { - UIView *searchBarView = self.searchBarView; - UIView *actionBarView = self.actionBarView; - UIView *contentView = self.contentView; - UIView *parentView = self.searchViewContainer; - - if (!hidden) { - if (searchBarView.superview) { - [parentView bringSubviewToFront:searchBarView]; - [parentView bringSubviewToFront:contentView]; - [parentView bringSubviewToFront:actionBarView]; - return; - } - [parentView addSubview:searchBarView]; - [parentView addSubview:contentView]; - [parentView addSubview:actionBarView]; - [self layoutTopViews]; - // Set Search controller default hidden state for iPad before it will be shown. - if (IPAD) { - self.searchViewContainerLeadingConstraint.constant = -kWidthForiPad; - [parentView.superview layoutIfNeeded]; - } - } - [UIView animateWithDuration:kDefaultAnimationDuration - animations:^{ - if (IPAD) { - self.searchViewContainerLeadingConstraint.constant = hidden ? -kWidthForiPad : 0; - [parentView.superview layoutIfNeeded]; - } else { - CGFloat const alpha = hidden ? 0 : 1; - contentView.alpha = alpha; - actionBarView.alpha = alpha; - searchBarView.alpha = alpha; - } - } - completion:^(BOOL finished) { - if (!hidden) - return; - [contentView removeFromSuperview]; - [actionBarView removeFromSuperview]; - [searchBarView removeFromSuperview]; - [self removeKeyboardObservers]; - }]; -} - -- (void)setActionBarState:(MWMSearchManagerActionBarState)actionBarState { - switch (actionBarState) { - case MWMSearchManagerActionBarStateHidden: - self.actionBarView.hidden = YES; - break; - case MWMSearchManagerActionBarStateTabBar: - self.actionBarView.hidden = YES; - break; - case MWMSearchManagerActionBarStateModeFilter: - self.actionBarView.hidden = NO; - break; - } -} - -- (UIViewController *)ownerController { - return [MapViewController sharedController]; -} -- (UIView *)searchViewContainer { -// return [MapViewController sharedController].searchViewContainer; - return nil; -} -- (NSLayoutConstraint *)searchViewContainerLeadingConstraint { -// return [MapViewController sharedController].searchViewContainerLeadingConstraint; - return nil; -} -- (UIView *)actionBarContainer { - return [MapViewController sharedController].controlsView; -} - -- (MWMMapViewControlsManager *)controlsManager { - return [MWMMapViewControlsManager manager]; -} -@end diff --git a/iphone/Maps/UI/Search/MWMSearchManagerObserver.h b/iphone/Maps/UI/Search/MWMSearchManagerObserver.h deleted file mode 100644 index e1ef8ab95c..0000000000 --- a/iphone/Maps/UI/Search/MWMSearchManagerObserver.h +++ /dev/null @@ -1,5 +0,0 @@ -@protocol MWMSearchManagerObserver<NSObject> - -- (void)onSearchManagerStateChanged; - -@end diff --git a/iphone/Maps/UI/Search/MWMSearchManagerState.h b/iphone/Maps/UI/Search/MWMSearchManagerState.h deleted file mode 100644 index 8ef6214263..0000000000 --- a/iphone/Maps/UI/Search/MWMSearchManagerState.h +++ /dev/null @@ -1,8 +0,0 @@ -typedef NS_ENUM(NSUInteger, MWMSearchManagerState) -{ - MWMSearchManagerStateHidden, - MWMSearchManagerStateDefault, - MWMSearchManagerStateTableSearch, - MWMSearchManagerStateMapSearch, - MWMSearchManagerStateResult -}; diff --git a/iphone/Maps/UI/Search/MWMSearchView.xib b/iphone/Maps/UI/Search/MWMSearchView.xib deleted file mode 100644 index 336dffe9a4..0000000000 --- a/iphone/Maps/UI/Search/MWMSearchView.xib +++ /dev/null @@ -1,266 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> - <device id="retina6_1" orientation="portrait" appearance="light"/> - <dependencies> - <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/> - <capability name="Safe area layout guides" minToolsVersion="9.0"/> - <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> - </dependencies> - <objects> - <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MWMSearchManager"> - <connections> - <outlet property="actionBarView" destination="d2A-Id-T62" id="UKk-U2-Mwl"/> - <outlet property="contentView" destination="u9y-bx-NGd" id="A03-AI-DbY"/> - <outlet property="searchBarView" destination="g9f-m8-RFv" id="opS-Ll-3Fn"/> - <outlet property="searchTextField" destination="ySb-oA-ZeW" id="Awu-Z4-EdK"/> - <outlet property="tableViewContainer" destination="QQn-4W-Y0s" id="5hb-b3-y8Q"/> - <outletCollection property="topViews" destination="u9y-bx-NGd" id="mAw-CD-QQV"/> - <outletCollection property="topViews" destination="d2A-Id-T62" id="Q0C-Eb-VyX"/> - </connections> - </placeholder> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="g9f-m8-RFv" customClass="SearchBar" customModule="Organic_Maps" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="320" height="88"/> - <subviews> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="C73-2V-To8" userLabel="StatusBarBackground"> - <rect key="frame" x="0.0" y="-100" width="320" height="188"/> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="SearchBarView"/> - </userDefinedRuntimeAttributes> - </view> - <stackView opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="DrM-QJ-TCl"> - <rect key="frame" x="0.0" y="0.0" width="320" height="88"/> - <subviews> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ghc-B6-erG"> - <rect key="frame" x="0.0" y="0.0" width="320" height="88"/> - <subviews> - <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="fill" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" adjustsFontSizeToFit="NO" minimumFontSize="17" clearButtonMode="always" translatesAutoresizingMaskIntoConstraints="NO" id="ySb-oA-ZeW" userLabel="Search" customClass="SearchTextField" customModule="Organic_Maps" customModuleProvider="target"> - <rect key="frame" x="16" y="8" width="232" height="72"/> - <accessibility key="accessibilityConfiguration" identifier="queryField"/> - <fontDescription key="fontDescription" type="system" pointSize="17"/> - <textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="search"/> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="SearchSearchTextField"/> - <userDefinedRuntimeAttribute type="string" keyPath="localizedPlaceholder" value="search"/> - </userDefinedRuntimeAttributes> - <connections> - <action selector="textFieldDidEndEditing:" destination="-1" eventType="editingDidEnd" id="CuD-rP-0bT"/> - <action selector="textFieldTextDidChange:" destination="-1" eventType="editingChanged" id="NU4-jh-Dd4"/> - <outlet property="delegate" destination="-1" id="U2Z-lL-bo1"/> - </connections> - </textField> - <button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="wordWrap" translatesAutoresizingMaskIntoConstraints="NO" id="qkR-cN-NI4"> - <rect key="frame" x="248" y="4" width="72" height="80"/> - <accessibility key="accessibilityConfiguration" identifier="cancelButton"/> - <constraints> - <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="72" id="8mI-K9-qAu"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="17"/> - <inset key="contentEdgeInsets" minX="4" minY="0.0" maxX="4" maxY="0.0"/> - <state key="normal" title="Cancel"> - <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - </state> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="cancel"/> - <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="whitePrimaryText"/> - </userDefinedRuntimeAttributes> - <connections> - <action selector="cancelButtonPressed" destination="-1" eventType="touchUpInside" id="cx8-zT-fgZ"/> - </connections> - </button> - <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_search" translatesAutoresizingMaskIntoConstraints="NO" id="ebE-7b-E9f"> - <rect key="frame" x="16" y="8" width="36" height="72"/> - <constraints> - <constraint firstAttribute="width" constant="36" id="8Ta-mE-6dI"/> - </constraints> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="SearchSearchTextFieldIcon"/> - </userDefinedRuntimeAttributes> - </imageView> - <activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" style="medium" translatesAutoresizingMaskIntoConstraints="NO" id="pnj-0S-fkE"> - <rect key="frame" x="16" y="8" width="36" height="72"/> - <constraints> - <constraint firstAttribute="width" constant="36" id="v0W-Aw-eSV"/> - </constraints> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="SearchSearchTextFieldIcon"/> - </userDefinedRuntimeAttributes> - </activityIndicatorView> - <button opaque="NO" contentMode="center" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="CrZ-Cz-xyW"> - <rect key="frame" x="16" y="8" width="36" height="72"/> - <constraints> - <constraint firstAttribute="width" constant="36" id="gfO-3m-OFv"/> - </constraints> - <state key="normal" image="ic_search_back"/> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="SearchSearchTextFieldIcon"/> - </userDefinedRuntimeAttributes> - <connections> - <action selector="backButtonPressed" destination="-1" eventType="touchUpInside" id="xyJ-fc-ged"/> - </connections> - </button> - </subviews> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstItem="pnj-0S-fkE" firstAttribute="top" secondItem="ySb-oA-ZeW" secondAttribute="top" id="4oA-1o-TMf"/> - <constraint firstItem="qkR-cN-NI4" firstAttribute="top" secondItem="ghc-B6-erG" secondAttribute="top" constant="4" id="FlS-h1-5Su"/> - <constraint firstItem="qkR-cN-NI4" firstAttribute="leading" secondItem="ySb-oA-ZeW" secondAttribute="trailing" id="HJ2-r6-yVw"/> - <constraint firstItem="ebE-7b-E9f" firstAttribute="bottom" secondItem="ySb-oA-ZeW" secondAttribute="bottom" id="R45-81-QPe"/> - <constraint firstItem="ebE-7b-E9f" firstAttribute="leading" secondItem="ySb-oA-ZeW" secondAttribute="leading" id="THA-Pu-i5t"/> - <constraint firstItem="CrZ-Cz-xyW" firstAttribute="top" secondItem="ySb-oA-ZeW" secondAttribute="top" id="WxT-Hy-CL1"/> - <constraint firstItem="CrZ-Cz-xyW" firstAttribute="bottom" secondItem="ySb-oA-ZeW" secondAttribute="bottom" id="XIJ-EQ-sfC"/> - <constraint firstAttribute="height" constant="52" id="evQ-q0-G3V"/> - <constraint firstAttribute="bottom" secondItem="qkR-cN-NI4" secondAttribute="bottom" constant="8" id="exK-0S-ML2"/> - <constraint firstAttribute="bottom" secondItem="ySb-oA-ZeW" secondAttribute="bottom" constant="8" id="kc1-xj-dnr"/> - <constraint firstItem="ySb-oA-ZeW" firstAttribute="leading" secondItem="ghc-B6-erG" secondAttribute="leading" constant="16" id="kch-1z-rhZ"/> - <constraint firstItem="CrZ-Cz-xyW" firstAttribute="leading" secondItem="ySb-oA-ZeW" secondAttribute="leading" id="m7Z-jP-Upg"/> - <constraint firstAttribute="trailing" secondItem="qkR-cN-NI4" secondAttribute="trailing" id="ngR-ML-Cnj"/> - <constraint firstItem="pnj-0S-fkE" firstAttribute="leading" secondItem="ySb-oA-ZeW" secondAttribute="leading" id="ove-r5-YFo"/> - <constraint firstItem="qkR-cN-NI4" firstAttribute="centerY" secondItem="ySb-oA-ZeW" secondAttribute="centerY" id="rPK-s8-GJi"/> - <constraint firstItem="pnj-0S-fkE" firstAttribute="bottom" secondItem="ySb-oA-ZeW" secondAttribute="bottom" id="udX-a7-hOd"/> - <constraint firstItem="ySb-oA-ZeW" firstAttribute="top" secondItem="ghc-B6-erG" secondAttribute="top" constant="8" id="yet-nD-qWN"/> - <constraint firstItem="ebE-7b-E9f" firstAttribute="top" secondItem="ySb-oA-ZeW" secondAttribute="top" id="zqb-O1-mO9"/> - </constraints> - </view> - </subviews> - </stackView> - </subviews> - <viewLayoutGuide key="safeArea" id="VX9-29-j2Z"/> - <color key="backgroundColor" red="0.12549019610000001" green="0.59607843140000005" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - <constraints> - <constraint firstItem="C73-2V-To8" firstAttribute="top" secondItem="g9f-m8-RFv" secondAttribute="top" constant="-100" id="Alh-jk-902"/> - <constraint firstItem="DrM-QJ-TCl" firstAttribute="leading" secondItem="g9f-m8-RFv" secondAttribute="leading" id="Fqj-iv-GAo"/> - <constraint firstAttribute="bottom" secondItem="DrM-QJ-TCl" secondAttribute="bottom" id="Wtv-8F-BmX"/> - <constraint firstAttribute="bottom" secondItem="C73-2V-To8" secondAttribute="bottom" id="e6z-lu-FFZ"/> - <constraint firstAttribute="trailing" secondItem="DrM-QJ-TCl" secondAttribute="trailing" id="eSZ-Cp-LJl"/> - <constraint firstAttribute="width" secondItem="DrM-QJ-TCl" secondAttribute="width" id="he4-ZG-kIs"/> - <constraint firstItem="C73-2V-To8" firstAttribute="leading" secondItem="g9f-m8-RFv" secondAttribute="leading" id="qhr-if-gM8"> - <variation key="heightClass=regular-widthClass=regular" constant="0.0"/> - </constraint> - <constraint firstAttribute="trailing" secondItem="C73-2V-To8" secondAttribute="trailing" id="xV9-by-bNe"> - <variation key="heightClass=regular-widthClass=regular" constant="0.0"/> - </constraint> - <constraint firstItem="DrM-QJ-TCl" firstAttribute="top" secondItem="g9f-m8-RFv" secondAttribute="top" id="zwx-cA-Bru"/> - </constraints> - <nil key="simulatedStatusBarMetrics"/> - <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="SearchBar"/> - </userDefinedRuntimeAttributes> - <connections> - <outlet property="activityIndicator" destination="pnj-0S-fkE" id="fLT-4I-s5b"/> - <outlet property="backButton" destination="CrZ-Cz-xyW" id="fIz-hG-JFM"/> - <outlet property="searchIcon" destination="ebE-7b-E9f" id="sCN-KI-2a1"/> - <outlet property="searchTextField" destination="ySb-oA-ZeW" id="xHR-vF-csP"/> - <outlet property="stackView" destination="DrM-QJ-TCl" id="Fzm-8G-JnZ"/> - </connections> - <point key="canvasLocation" x="225.59999999999999" y="-211.39430284857573"/> - </view> - <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> - <view contentMode="scaleToFill" id="d2A-Id-T62" customClass="SearchActionBarView" customModule="Organic_Maps" customModuleProvider="target" propertyAccessControl="none"> - <rect key="frame" x="0.0" y="0.0" width="361" height="36"/> - <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> - <subviews> - <stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="2gF-Kc-0a7"> - <rect key="frame" x="0.0" y="0.0" width="361" height="36"/> - <subviews> - <button opaque="NO" contentMode="scaleToFill" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="a8x-P8-Y8p" customClass="MWMButton"> - <rect key="frame" x="0.0" y="0.0" width="296" height="36"/> - <accessibility key="accessibilityConfiguration" identifier="viewOnMapButton"/> - <inset key="contentEdgeInsets" minX="10" minY="0.0" maxX="10" maxY="0.0"/> - <state key="normal" title="Map" image="ic_search_mode_map"/> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="SearchActionBarButton"/> - <userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="search_show_on_map"/> - </userDefinedRuntimeAttributes> - <connections> - <action selector="changeMode" destination="-1" eventType="touchUpInside" id="uAW-Mp-BVp"/> - </connections> - </button> - <button opaque="NO" contentMode="scaleToFill" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Enq-yv-2XZ" customClass="MWMButton"> - <rect key="frame" x="296" y="0.0" width="65" height="36"/> - <accessibility key="accessibilityConfiguration" identifier="viewOnMapButton"/> - <inset key="contentEdgeInsets" minX="10" minY="0.0" maxX="10" maxY="0.0"/> - <state key="normal" title="List" image="ic_search_mode_list"/> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="SearchActionBarButton"/> - <userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="search_in_table"/> - </userDefinedRuntimeAttributes> - <connections> - <action selector="changeMode" destination="-1" eventType="touchUpInside" id="yqV-vF-c3I"/> - </connections> - </button> - </subviews> - </stackView> - </subviews> - <viewLayoutGuide key="safeArea" id="Bgt-hj-RpB"/> - <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> - <constraints> - <constraint firstAttribute="trailing" secondItem="2gF-Kc-0a7" secondAttribute="trailing" id="Fdx-hX-coi"/> - <constraint firstItem="2gF-Kc-0a7" firstAttribute="top" secondItem="d2A-Id-T62" secondAttribute="top" id="Y62-Rf-LAh"/> - <constraint firstAttribute="height" constant="40" id="eLk-al-10m"/> - <constraint firstItem="2gF-Kc-0a7" firstAttribute="leading" secondItem="d2A-Id-T62" secondAttribute="leading" id="itE-d4-hzw"/> - <constraint firstAttribute="bottom" secondItem="2gF-Kc-0a7" secondAttribute="bottom" id="s4H-Jq-xk9"/> - </constraints> - <nil key="simulatedStatusBarMetrics"/> - <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="SearchActionBarView"/> - </userDefinedRuntimeAttributes> - <connections> - <outlet property="listButton" destination="Enq-yv-2XZ" id="DUY-wh-ohq"/> - <outlet property="mapButton" destination="a8x-P8-Y8p" id="Rtu-DA-x6c"/> - <outlet property="stackView" destination="2gF-Kc-0a7" id="08D-2t-Cpt"/> - </connections> - <point key="canvasLocation" x="193" y="-58"/> - </view> - <view contentMode="scaleToFill" id="u9y-bx-NGd" userLabel="Content" customClass="MWMSearchContentView"> - <rect key="frame" x="0.0" y="0.0" width="320" height="402"/> - <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> - <subviews> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jHn-5d-yXG" customClass="SolidTouchView"> - <rect key="frame" x="0.0" y="0.0" width="320" height="402"/> - <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/> - </userDefinedRuntimeAttributes> - </view> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QQn-4W-Y0s"> - <rect key="frame" x="0.0" y="0.0" width="320" height="368"/> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - </view> - </subviews> - <viewLayoutGuide key="safeArea" id="wvK-7O-o3d"/> - <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - <constraints> - <constraint firstItem="jHn-5d-yXG" firstAttribute="leading" secondItem="u9y-bx-NGd" secondAttribute="leading" id="1Qk-kN-iT5"> - <variation key="heightClass=regular-widthClass=regular" constant="0.0"/> - </constraint> - <constraint firstItem="wvK-7O-o3d" firstAttribute="bottom" secondItem="QQn-4W-Y0s" secondAttribute="bottom" id="5UL-RM-PTF"/> - <constraint firstItem="jHn-5d-yXG" firstAttribute="top" secondItem="u9y-bx-NGd" secondAttribute="top" id="Bgm-2G-pSD"/> - <constraint firstItem="QQn-4W-Y0s" firstAttribute="leading" secondItem="u9y-bx-NGd" secondAttribute="leading" id="CFW-pd-Qj7"/> - <constraint firstItem="QQn-4W-Y0s" firstAttribute="top" secondItem="u9y-bx-NGd" secondAttribute="top" id="T8W-hj-ces"/> - <constraint firstAttribute="trailing" secondItem="jHn-5d-yXG" secondAttribute="trailing" id="aKt-MM-Csi"> - <variation key="heightClass=regular-widthClass=regular" constant="0.0"/> - </constraint> - <constraint firstAttribute="bottom" secondItem="jHn-5d-yXG" secondAttribute="bottom" id="hm3-xK-oys"> - <variation key="heightClass=regular-widthClass=regular" constant="0.0"/> - </constraint> - <constraint firstAttribute="trailing" secondItem="QQn-4W-Y0s" secondAttribute="trailing" id="pBX-G3-gBz"/> - </constraints> - <nil key="simulatedStatusBarMetrics"/> - <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/> - </userDefinedRuntimeAttributes> - <point key="canvasLocation" x="236" y="229"/> - </view> - </objects> - <resources> - <image name="ic_search" width="28" height="28"/> - <image name="ic_search_back" width="24" height="24"/> - <image name="ic_search_mode_list" width="20" height="20"/> - <image name="ic_search_mode_map" width="20" height="20"/> - </resources> -</document> diff --git a/iphone/Maps/UI/Search/SearchActionBarView.swift b/iphone/Maps/UI/Search/SearchActionBarView.swift deleted file mode 100644 index 184304f5ca..0000000000 --- a/iphone/Maps/UI/Search/SearchActionBarView.swift +++ /dev/null @@ -1,56 +0,0 @@ -@objc class SearchActionBarView: SolidTouchView { - @IBOutlet private var mapButton: MWMButton! - @IBOutlet private var listButton: MWMButton! - - @IBOutlet private var stackView: UIStackView! - - override func awakeFromNib() { - super.awakeFromNib() - - hideView(mapButton, isHidden: true, animated: false) - hideView(listButton, isHidden: true, animated: false) - } - - @objc func updateForState(_ state: MWMSearchManagerState) { - hideView(mapButton, isHidden: true, animated: false) - hideView(listButton, isHidden: true, animated: false) - - iPhoneSpecific { - switch state { - case .tableSearch: - hideView(mapButton, isHidden: false, animated: true) - case .mapSearch: - hideView(listButton, isHidden: false, animated: true) - case .default: - break - case .hidden: fallthrough - case .result: fallthrough - @unknown default: - break - } - } - } - - private func hideView(_ view: UIView, isHidden: Bool, animated: Bool) { - view.isHidden = isHidden - if animated { - UIView.animate(withDuration: kDefaultAnimationDuration / 2, - delay: 0, - options: [.beginFromCurrentState], - animations: { - self.layoutIfNeeded() - }, completion: { complete in - if complete { - UIView.animate(withDuration: kDefaultAnimationDuration / 2, - delay: 0, options: [.beginFromCurrentState], - animations: { - view.alpha = isHidden ? 0 : 1 - }, completion: nil) - } - }) - } else { - view.alpha = isHidden ? 0 : 1 - view.isHidden = isHidden - } - } -} diff --git a/iphone/Maps/UI/Search/SearchBar.swift b/iphone/Maps/UI/Search/SearchBar.swift deleted file mode 100644 index 7bc4033471..0000000000 --- a/iphone/Maps/UI/Search/SearchBar.swift +++ /dev/null @@ -1,57 +0,0 @@ -@objc enum SearchBarState: Int { - case ready - case searching - case back -} - -final class SearchBar: SolidTouchView { - @IBOutlet private var searchIcon: UIImageView! - @IBOutlet private var activityIndicator: UIActivityIndicatorView! - @IBOutlet private var backButton: UIButton! - @IBOutlet private var searchTextField: SearchTextField! - @IBOutlet private var stackView: UIStackView! - - override var visibleAreaAffectDirections: MWMAvailableAreaAffectDirections { return alternative(iPhone: .top, iPad: .left) } - - override var placePageAreaAffectDirections: MWMAvailableAreaAffectDirections { return alternative(iPhone: [], iPad: .left) } - - override var widgetsAreaAffectDirections: MWMAvailableAreaAffectDirections { return alternative(iPhone: [], iPad: .left) } - - override var trafficButtonAreaAffectDirections: MWMAvailableAreaAffectDirections { return alternative(iPhone: .top, iPad: .left) } - - override var tabBarAreaAffectDirections: MWMAvailableAreaAffectDirections { return alternative(iPhone: [], iPad: .left) } - - override var trackRecordingButtonAreaAffectDirections: MWMAvailableAreaAffectDirections { return alternative(iPhone: .top, iPad: .left) } - - @objc var state: SearchBarState = .ready { - didSet { - if state != oldValue { - updateLeftView() - } - } - } - - override func awakeFromNib() { - super.awakeFromNib() - updateLeftView() - searchTextField.leftViewMode = UITextField.ViewMode.always - searchTextField.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 32, height: 32)) - searchTextField.applyTheme() - } - - private func updateLeftView() { - searchIcon.isHidden = true - activityIndicator.isHidden = true - backButton.isHidden = true - - switch state { - case .ready: - searchIcon.isHidden = false - case .searching: - activityIndicator.isHidden = false - activityIndicator.startAnimating() - case .back: - backButton.isHidden = false - } - } -} diff --git a/iphone/Maps/UI/Search/SearchTextField.swift b/iphone/Maps/UI/Search/SearchTextField.swift deleted file mode 100644 index 2f623249fd..0000000000 --- a/iphone/Maps/UI/Search/SearchTextField.swift +++ /dev/null @@ -1,31 +0,0 @@ -class SearchTextField: UITextField { - override func drawPlaceholder(in rect: CGRect) { - guard let font = font, let tint = tintColor else { - super.drawPlaceholder(in: rect); - return - } - placeholder?.draw( - in: rect, - withAttributes: [ - NSAttributedString.Key.font: font, - NSAttributedString.Key.foregroundColor: tint - ]) - } - - override func layoutSubviews() { - super.layoutSubviews() - for view in subviews { - if (view is UIButton) { - let button = view as? UIButton - let clearButtonImage: UIImage? - if #available(iOS 13.0, *) { - clearButtonImage = UIImage(named: "ic_clear")?.withRenderingMode(.alwaysTemplate).withTintColor(tintColor) - } else { - clearButtonImage = UIImage(named: "ic_search_clear_14") - } - button?.setImage(clearButtonImage, for: .normal) - button?.tintColor = tintColor - } - } - } -} diff --git a/iphone/Maps/UI/Search/TableView/MWMSearchTableView.h b/iphone/Maps/UI/Search/TableView/MWMSearchTableView.h deleted file mode 100644 index 253cb150c8..0000000000 --- a/iphone/Maps/UI/Search/TableView/MWMSearchTableView.h +++ /dev/null @@ -1,5 +0,0 @@ -@interface MWMSearchTableView : UIView - -- (void)hideNoResultsView:(BOOL)hide; - -@end diff --git a/iphone/Maps/UI/Search/TableView/MWMSearchTableView.m b/iphone/Maps/UI/Search/TableView/MWMSearchTableView.m deleted file mode 100644 index 40608e3a2a..0000000000 --- a/iphone/Maps/UI/Search/TableView/MWMSearchTableView.m +++ /dev/null @@ -1,71 +0,0 @@ -#import "MWMSearchTableView.h" -#import "MWMKeyboard.h" -#import "MWMSearchNoResults.h" - -@interface MWMSearchTableView ()<MWMKeyboardObserver> - -@property(weak, nonatomic) IBOutlet NSLayoutConstraint * noResultsBottomOffset; - -@property(weak, nonatomic) IBOutlet UIView * noResultsContainer; -@property(weak, nonatomic) IBOutlet UIView * noResultsWrapper; -@property(nonatomic) MWMSearchNoResults * noResultsView; - -@end - -@implementation MWMSearchTableView - -- (void)awakeFromNib -{ - [super awakeFromNib]; - CALayer * sl = self.layer; - sl.shouldRasterize = YES; - sl.rasterizationScale = UIScreen.mainScreen.scale; - [MWMKeyboard addObserver:self]; -} - -- (void)hideNoResultsView:(BOOL)hide -{ - if (hide) - { - self.noResultsContainer.hidden = YES; - [self.noResultsView removeFromSuperview]; - } - else - { - self.noResultsContainer.hidden = NO; - [self.noResultsWrapper addSubview:self.noResultsView]; - [self onKeyboardAnimation]; - } -} - -#pragma mark - MWMKeyboard - -- (void)onKeyboardAnimation -{ - CGFloat const keyboardHeight = [MWMKeyboard keyboardHeight]; - if (keyboardHeight >= self.height) - return; - - self.noResultsBottomOffset.constant = keyboardHeight; - if (self.superview) - [self layoutIfNeeded]; -} - -- (void)onKeyboardWillAnimate -{ - if (self.superview) - [self layoutIfNeeded]; -} - -- (MWMSearchNoResults *)noResultsView -{ - if (!_noResultsView) - { - _noResultsView = [MWMSearchNoResults viewWithImage:nil - title:L(@"search_not_found") - text:L(@"search_not_found_query")]; - } - return _noResultsView; -} - -@end diff --git a/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.h b/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.h deleted file mode 100644 index 470e7eadb8..0000000000 --- a/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.h +++ /dev/null @@ -1,27 +0,0 @@ -#import "MWMSearch.h" -#import "MWMSearchManager.h" -#import "MWMSearchTabbedViewProtocol.h" -#import "MWMViewController.h" - -#include <CoreApi/Framework.h> - -@class SearchTextField; - -@protocol MWMSearchTableViewProtocol<MWMSearchTabbedViewProtocol> - -@property(nullable, weak, nonatomic) SearchTextField * searchTextField; - -@property(nonatomic) MWMSearchManagerState state; - -- (void)processSearchResultAtIndex:(NSInteger)index; - -@end - -@interface MWMSearchTableViewController : MWMViewController<MWMSearchObserver> - -- (nonnull instancetype)init __attribute__((unavailable("init is not available"))); -- (nonnull instancetype)initWithDelegate:(nonnull id<MWMSearchTableViewProtocol>)delegate; - -- (void)reloadData; - -@end diff --git a/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.mm b/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.mm deleted file mode 100644 index eff7961360..0000000000 --- a/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.mm +++ /dev/null @@ -1,143 +0,0 @@ -#import "MWMSearchTableViewController.h" -#import "MWMSearchCommonCell.h" -#import "MWMSearchSuggestionCell.h" -#import "MWMSearchTableView.h" -#import "SearchResult.h" -#import "SwiftBridge.h" - -#include "platform/localization.hpp" - -#include "search/result.hpp" - -namespace { -NSString *GetLocalizedTypeName(search::Result const &result) { - return @(result.GetLocalizedFeatureType().c_str()); -} -} - -@interface MWMSearchTableViewController () <UITableViewDataSource, UITableViewDelegate> - -@property(weak, nonatomic) IBOutlet UITableView *tableView; - -@property(weak, nonatomic) id<MWMSearchTableViewProtocol> delegate; - -@end - -@implementation MWMSearchTableViewController - -- (nonnull instancetype)initWithDelegate:(id<MWMSearchTableViewProtocol>)delegate { - self = [super init]; - if (self) - _delegate = delegate; - return self; -} - -- (void)viewDidLoad { - [super viewDidLoad]; - [self setupTableView]; -} - -- (void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - self.tableView.hidden = NO; - self.tableView.insetsContentViewsToSafeArea = YES; - [(MWMSearchTableView *)self.view hideNoResultsView:YES]; - [self reloadData]; -} - -- (void)setupTableView { - UITableView *tableView = self.tableView; - tableView.estimatedRowHeight = 80.; - tableView.rowHeight = UITableViewAutomaticDimension; - [tableView registerNibWithCellClass:[MWMSearchSuggestionCell class]]; - [tableView registerNibWithCellClass:[MWMSearchCommonCell class]]; -} - -- (void)reloadData { - [self.tableView reloadData]; -} -#pragma mark - Layout - -- (void)viewWillTransitionToSize:(CGSize)size - withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { - [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; - [coordinator - animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) { - [self reloadData]; - } - completion:nil]; -} - -#pragma mark - UITableViewDataSource - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return [MWMSearch resultsCount]; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - if ([MWMSearch resultsCount] == 0) { - NSAssert(false, @"Invalid reload with outdated SearchIndex"); - return [tableView dequeueReusableCellWithCellClass:[MWMSearchCommonCell class] indexPath:indexPath]; - } - - auto const row = indexPath.row; - auto const containerIndex = [MWMSearch containerIndexWithRow:row]; - auto const & result = [MWMSearch resultWithContainerIndex:containerIndex]; - - switch ([MWMSearch resultTypeWithRow:row]) - { - case SearchItemTypeRegular: - { - auto cell = static_cast<MWMSearchCommonCell *>( - [tableView dequeueReusableCellWithCellClass:[MWMSearchCommonCell class] indexPath:indexPath]); - [cell configureWith:result isPartialMatching:YES]; - return cell; - } - case SearchItemTypeSuggestion: - { - auto cell = static_cast<MWMSearchSuggestionCell *>( - [tableView dequeueReusableCellWithCellClass:[MWMSearchSuggestionCell class] indexPath:indexPath]); - [cell configureWith:result isPartialMatching:YES]; - cell.isLastCell = row == [MWMSearch suggestionsCount] - 1; - return cell; - } - } -} - -#pragma mark - UITableViewDelegate - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - id<MWMSearchTableViewProtocol> delegate = self.delegate; - auto const row = indexPath.row; - auto const containerIndex = [MWMSearch containerIndexWithRow:row]; - auto const & result = [MWMSearch resultWithContainerIndex:containerIndex]; - - switch ([MWMSearch resultTypeWithRow:row]) - { - case SearchItemTypeRegular: - { - SearchTextField const * textField = delegate.searchTextField; - [MWMSearch saveQuery:textField.text forInputLocale:textField.textInputMode.primaryLanguage]; - [delegate processSearchResultAtIndex:indexPath.row]; - break; - } - case SearchItemTypeSuggestion: - { - [delegate searchText:result.suggestion - forInputLocale:nil - withCategory:result.isPureSuggest]; - break; - } - } -} - -#pragma mark - MWMSearchObserver - -- (void)onSearchCompleted { - [self reloadData]; - BOOL const noResults = [MWMSearch resultsCount] == 0; - self.tableView.hidden = noResults; - [(MWMSearchTableView *)self.view hideNoResultsView:!noResults]; -} - -@end diff --git a/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.xib b/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.xib deleted file mode 100644 index 5de7e20d67..0000000000 --- a/iphone/Maps/UI/Search/TableView/MWMSearchTableViewController.xib +++ /dev/null @@ -1,79 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> - <device id="retina4_7" orientation="portrait" appearance="light"/> - <dependencies> - <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/> - <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> - </dependencies> - <objects> - <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MWMSearchTableViewController"> - <connections> - <outlet property="tableView" destination="oZD-Er-6fn" id="Ayl-9n-mV7"/> - <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/> - </connections> - </placeholder> - <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> - <view clipsSubviews="YES" contentMode="scaleToFill" id="i5M-Pr-FkT" customClass="MWMSearchTableView" propertyAccessControl="none"> - <rect key="frame" x="0.0" y="0.0" width="320" height="460"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <subviews> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NsS-9j-1hr"> - <rect key="frame" x="0.0" y="0.0" width="320" height="460"/> - <subviews> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0ed-oS-Uw6"> - <rect key="frame" x="0.0" y="70" width="320" height="320"/> - <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> - <constraints> - <constraint firstAttribute="width" constant="320" id="iu3-jC-BuA"/> - <constraint firstAttribute="width" secondItem="0ed-oS-Uw6" secondAttribute="height" multiplier="1:1" priority="999" id="rBn-RY-c74"/> - </constraints> - </view> - </subviews> - <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> - <constraints> - <constraint firstItem="0ed-oS-Uw6" firstAttribute="centerY" secondItem="NsS-9j-1hr" secondAttribute="centerY" id="3os-g7-XiN"/> - <constraint firstItem="0ed-oS-Uw6" firstAttribute="centerX" secondItem="NsS-9j-1hr" secondAttribute="centerX" id="N4N-MH-XVw"/> - <constraint firstItem="0ed-oS-Uw6" firstAttribute="height" relation="lessThanOrEqual" secondItem="NsS-9j-1hr" secondAttribute="height" id="aad-2C-r3O"/> - </constraints> - </view> - <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="onDrag" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="oZD-Er-6fn"> - <rect key="frame" x="0.0" y="0.0" width="320" height="460"/> - <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> - <inset key="separatorInset" minX="15" minY="0.0" maxX="0.0" maxY="0.0"/> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/> - </userDefinedRuntimeAttributes> - <connections> - <outlet property="dataSource" destination="-1" id="T9n-AG-sHf"/> - <outlet property="delegate" destination="-1" id="Syl-wX-U5b"/> - </connections> - </tableView> - </subviews> - <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> - <constraints> - <constraint firstItem="oZD-Er-6fn" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" id="9cU-AY-lQv"/> - <constraint firstItem="NsS-9j-1hr" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" id="C7g-sy-qp4"/> - <constraint firstAttribute="trailing" secondItem="oZD-Er-6fn" secondAttribute="trailing" id="DoP-hv-QXb"/> - <constraint firstAttribute="bottom" secondItem="NsS-9j-1hr" secondAttribute="bottom" priority="750" id="P9i-5E-bpw"/> - <constraint firstItem="NsS-9j-1hr" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" id="csW-fl-Sp0"/> - <constraint firstAttribute="trailing" secondItem="NsS-9j-1hr" secondAttribute="trailing" id="dIC-Mf-12H"/> - <constraint firstItem="oZD-Er-6fn" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" id="iKy-dd-6Jt"/> - <constraint firstAttribute="bottom" secondItem="oZD-Er-6fn" secondAttribute="bottom" priority="750" id="rgu-oY-7XC"/> - </constraints> - <nil key="simulatedStatusBarMetrics"/> - <nil key="simulatedTopBarMetrics"/> - <nil key="simulatedBottomBarMetrics"/> - <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/> - </userDefinedRuntimeAttributes> - <connections> - <outlet property="noResultsBottomOffset" destination="P9i-5E-bpw" id="qTI-X5-KzS"/> - <outlet property="noResultsContainer" destination="NsS-9j-1hr" id="BfV-Wz-ztf"/> - <outlet property="noResultsWrapper" destination="0ed-oS-Uw6" id="RbW-yn-sl8"/> - </connections> - <point key="canvasLocation" x="139" y="154"/> - </view> - </objects> -</document> diff --git a/iphone/Maps/UI/Search/Tabs/MWMSearchTabbedViewProtocol.h b/iphone/Maps/UI/Search/Tabs/MWMSearchTabbedViewProtocol.h deleted file mode 100644 index 98147899b9..0000000000 --- a/iphone/Maps/UI/Search/Tabs/MWMSearchTabbedViewProtocol.h +++ /dev/null @@ -1,12 +0,0 @@ -#import "MWMSearchManagerState.h" - -@protocol MWMSearchTabbedViewProtocol <NSObject> - -@required - -@property(nonatomic) MWMSearchManagerState state; - -- (void)searchText:(NSString *)text forInputLocale:(NSString *)locale withCategory:(BOOL)isCategory; -- (void)dismissKeyboard; - -@end -- 2.45.3