From a7261c57254f57ad6cc06152199c2792362f8ca9 Mon Sep 17 00:00:00 2001 From: Aleksey Belousov Date: Thu, 23 Apr 2020 15:14:31 +0300 Subject: [PATCH] [iOS] implement guides gallery on map --- .../CoreApi/CoreApi.xcodeproj/project.pbxproj | 32 ++ iphone/CoreApi/CoreApi/CoreApi-swift.h | 2 + .../GuidesGallery/GuidesGalleryData+Core.h | 13 + .../GuidesGallery/GuidesGalleryData.h | 13 + .../GuidesGallery/GuidesGalleryData.mm | 31 ++ .../GuidesGallery/GuidesGalleryItem+Core.h | 19 + .../GuidesGallery/GuidesGalleryItem.h | 31 ++ .../GuidesGallery/GuidesGalleryItem.mm | 57 ++ .../CoreApi/PlacePageData/PlacePageData.h | 3 + .../CoreApi/PlacePageData/PlacePageData.mm | 11 + .../Categories/UICollectionView+Cells.swift | 8 + iphone/Maps/Classes/MapViewController.mm | 32 +- iphone/Maps/Core/Theme/Fonts.swift | 2 +- .../Core/Theme/GuidesGalleryStyleSheet.swift | 49 ++ iphone/Maps/Core/Theme/MainTheme.swift | 1 + .../Routes Gallery/Contents.json | 6 + .../Contents.json | 15 + .../attitude_icon.pdf | 88 +++ .../Contents.json | 15 + .../routes_gallery_camera.imageset/Vector.pdf | 98 ++++ .../Contents.json | 12 + .../image.pdf | 101 ++++ .../Contents.json | 15 + .../Shape.pdf | 79 +++ .../Contents.json | 15 + .../routes_gallery_clock.imageset/Shape.pdf | 84 +++ .../Contents.json | 15 + .../Union.pdf | 90 ++++ iphone/Maps/Maps.xcodeproj/project.pbxproj | 36 ++ .../ElevationProfilePresenter.swift | 12 +- .../GuidesGallery/GuidesGalleryBuilder.swift | 15 + .../GuidesGallery/GuidesGalleryCityCell.swift | 38 ++ .../GuidesGalleryOutdoorCell.swift | 52 ++ .../GuidesGalleryPresenter.swift | 86 +++ .../GuidesGalleryViewController.swift | 145 +++++ iphone/Maps/UI/PlacePage/PlacePage.storyboard | 500 ++++++++++++++++++ .../Maps/UI/PlacePage/PlacePageBuilder.swift | 4 +- .../Layouts/IPlacePageLayout.swift | 2 +- .../Layouts/PlacePageCommonLayout.swift | 2 +- .../Layouts/PlacePageElevationLayout.swift | 2 +- .../Layouts/PlacePageGalleryLayout.swift | 27 + .../UI/PlacePage/PlacePagePresenter.swift | 119 +---- .../PlacePage/PlacePageViewController.swift | 159 ++++-- iphone/Maps/UI/Storyboard/Main.storyboard | 149 +++--- 44 files changed, 2047 insertions(+), 238 deletions(-) create mode 100644 iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryData+Core.h create mode 100644 iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryData.h create mode 100644 iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryData.mm create mode 100644 iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryItem+Core.h create mode 100644 iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryItem.h create mode 100644 iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryItem.mm create mode 100644 iphone/Maps/Core/Theme/GuidesGalleryStyleSheet.swift create mode 100644 iphone/Maps/Images.xcassets/Routes Gallery/Contents.json create mode 100644 iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_attitude.imageset/Contents.json create mode 100644 iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_attitude.imageset/attitude_icon.pdf create mode 100644 iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_camera.imageset/Contents.json create mode 100644 iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_camera.imageset/Vector.pdf create mode 100644 iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_cell_image.imageset/Contents.json create mode 100644 iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_cell_image.imageset/image.pdf create mode 100644 iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_checkmark.imageset/Contents.json create mode 100644 iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_checkmark.imageset/Shape.pdf create mode 100644 iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_clock.imageset/Contents.json create mode 100644 iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_clock.imageset/Shape.pdf create mode 100644 iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_distance.imageset/Contents.json create mode 100644 iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_distance.imageset/Union.pdf create mode 100644 iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryBuilder.swift create mode 100644 iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryCityCell.swift create mode 100644 iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryOutdoorCell.swift create mode 100644 iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryPresenter.swift create mode 100644 iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryViewController.swift create mode 100644 iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/PlacePageGalleryLayout.swift diff --git a/iphone/CoreApi/CoreApi.xcodeproj/project.pbxproj b/iphone/CoreApi/CoreApi.xcodeproj/project.pbxproj index 24687e75ac..558ac16e5d 100644 --- a/iphone/CoreApi/CoreApi.xcodeproj/project.pbxproj +++ b/iphone/CoreApi/CoreApi.xcodeproj/project.pbxproj @@ -14,6 +14,12 @@ 470016082342541100EBF03D /* MWMTagGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 470016052342541000EBF03D /* MWMTagGroup.m */; }; 4700160F2342579000EBF03D /* MWMTag.h in Headers */ = {isa = PBXBuildFile; fileRef = 470016042342540F00EBF03D /* MWMTag.h */; settings = {ATTRIBUTES = (Public, ); }; }; 470016102342579200EBF03D /* MWMTagGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 470016032342540E00EBF03D /* MWMTagGroup.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 470B3620244DD9E400C0EA9E /* GuidesGalleryData.h in Headers */ = {isa = PBXBuildFile; fileRef = 470B361E244DD9E400C0EA9E /* GuidesGalleryData.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 470B3621244DD9E400C0EA9E /* GuidesGalleryData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 470B361F244DD9E400C0EA9E /* GuidesGalleryData.mm */; }; + 470B3624244DDA0A00C0EA9E /* GuidesGalleryItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 470B3622244DDA0A00C0EA9E /* GuidesGalleryItem.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 470B3625244DDA0A00C0EA9E /* GuidesGalleryItem.mm in Sources */ = {isa = PBXBuildFile; fileRef = 470B3623244DDA0A00C0EA9E /* GuidesGalleryItem.mm */; }; + 470B3628244DDA2600C0EA9E /* GuidesGalleryData+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 470B3626244DDA2600C0EA9E /* GuidesGalleryData+Core.h */; }; + 470B362C244DDBF600C0EA9E /* GuidesGalleryItem+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 470B362A244DDBF600C0EA9E /* GuidesGalleryItem+Core.h */; }; 4718C4322355FC3C00640DF1 /* MWMNetworkPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 4718C4302355FC3C00640DF1 /* MWMNetworkPolicy.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4718C4332355FC3C00640DF1 /* MWMNetworkPolicy.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4718C4312355FC3C00640DF1 /* MWMNetworkPolicy.mm */; }; 471AB98D23AB925D00F56D49 /* MWMMapSearchResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 471AB98B23AB925D00F56D49 /* MWMMapSearchResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -145,6 +151,12 @@ 470016062342541100EBF03D /* MWMTag.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MWMTag.m; sourceTree = ""; }; 4700161223425CFD00EBF03D /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 470016142342633D00EBF03D /* CoreApi.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = CoreApi.modulemap; sourceTree = ""; }; + 470B361E244DD9E400C0EA9E /* GuidesGalleryData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GuidesGalleryData.h; sourceTree = ""; }; + 470B361F244DD9E400C0EA9E /* GuidesGalleryData.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = GuidesGalleryData.mm; sourceTree = ""; }; + 470B3622244DDA0A00C0EA9E /* GuidesGalleryItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GuidesGalleryItem.h; sourceTree = ""; }; + 470B3623244DDA0A00C0EA9E /* GuidesGalleryItem.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = GuidesGalleryItem.mm; sourceTree = ""; }; + 470B3626244DDA2600C0EA9E /* GuidesGalleryData+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "GuidesGalleryData+Core.h"; sourceTree = ""; }; + 470B362A244DDBF600C0EA9E /* GuidesGalleryItem+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "GuidesGalleryItem+Core.h"; sourceTree = ""; }; 4718C4302355FC3C00640DF1 /* MWMNetworkPolicy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMNetworkPolicy.h; sourceTree = ""; }; 4718C4312355FC3C00640DF1 /* MWMNetworkPolicy.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMNetworkPolicy.mm; sourceTree = ""; }; 471AB98B23AB925D00F56D49 /* MWMMapSearchResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMMapSearchResult.h; sourceTree = ""; }; @@ -349,6 +361,19 @@ name = Frameworks; sourceTree = ""; }; + 470B361D244DD98B00C0EA9E /* GuidesGallery */ = { + isa = PBXGroup; + children = ( + 470B361E244DD9E400C0EA9E /* GuidesGalleryData.h */, + 470B3626244DDA2600C0EA9E /* GuidesGalleryData+Core.h */, + 470B361F244DD9E400C0EA9E /* GuidesGalleryData.mm */, + 470B3622244DDA0A00C0EA9E /* GuidesGalleryItem.h */, + 470B362A244DDBF600C0EA9E /* GuidesGalleryItem+Core.h */, + 470B3623244DDA0A00C0EA9E /* GuidesGalleryItem.mm */, + ); + path = GuidesGallery; + sourceTree = ""; + }; 4718C42F2355FC0D00640DF1 /* NetworkPolicy */ = { isa = PBXGroup; children = ( @@ -444,6 +469,7 @@ 47942D5B237CC3B500DEFAE3 /* Common */, 47942D65237CC3B500DEFAE3 /* Booking */, 9974CA2623DF1931003FE824 /* ElevationProfile */, + 470B361D244DD98B00C0EA9E /* GuidesGallery */, 47942D69237CC3B500DEFAE3 /* UGC */, ); path = PlacePageData; @@ -649,9 +675,11 @@ buildActionMask = 2147483647; files = ( 9957FAE8237AE5B000855F48 /* Logger.h in Headers */, + 470B3624244DDA0A00C0EA9E /* GuidesGalleryItem.h in Headers */, 470015F42342509C00EBF03D /* CoreApi.h in Headers */, 47942D74237CC41A00DEFAE3 /* HotelBookingData.h in Headers */, 479F705F234FBB8F00011E2E /* MWMCategory.h in Headers */, + 470B3620244DD9E400C0EA9E /* GuidesGalleryData.h in Headers */, 471AB99123AB931000F56D49 /* MWMMapSearchResult+Core.h in Headers */, 4738A8E0239FACE7007C0F43 /* CoreBanner.h in Headers */, 47942D6D237CC3E300DEFAE3 /* PlacePagePreviewData.h in Headers */, @@ -691,6 +719,7 @@ 47942D6E237CC3E800DEFAE3 /* PlacePagePreviewData+Core.h in Headers */, 4738A8E4239FB46E007C0F43 /* CoreBanner+Core.h in Headers */, 47942D7B237CC41A00DEFAE3 /* HotelRoom+Core.h in Headers */, + 470B3628244DDA2600C0EA9E /* GuidesGalleryData+Core.h in Headers */, 47942D94237D673E00DEFAE3 /* CatalogPromoItem.h in Headers */, 470016102342579200EBF03D /* MWMTagGroup.h in Headers */, 47942D87237CC55800DEFAE3 /* MWMOpeningHoursCommon.h in Headers */, @@ -721,6 +750,7 @@ 47D609DC234FE625008ECC47 /* MWMBookmarksObserver.h in Headers */, 471AB98D23AB925D00F56D49 /* MWMMapSearchResult.h in Headers */, 47942D6F237CC3F400DEFAE3 /* PlacePageInfoData.h in Headers */, + 470B362C244DDBF600C0EA9E /* GuidesGalleryItem+Core.h in Headers */, 475784C22344B422008291A4 /* Framework.h in Headers */, 47C637D22354A6FB00E12DE0 /* MWMEye.h in Headers */, ); @@ -802,6 +832,7 @@ 3D40DED723ED9C9500A0153A /* WebApi.mm in Sources */, 99103844237EDFA200893C9F /* DeepLinkData.m in Sources */, 47942D88237CCA8800DEFAE3 /* PlacePagePreviewData.mm in Sources */, + 470B3625244DDA0A00C0EA9E /* GuidesGalleryItem.mm in Sources */, 479F7050234FB60400011E2E /* MWMCatalogObserver.mm in Sources */, 47942D7F237CC43300DEFAE3 /* UgcData.mm in Sources */, 99F31EB823D5DD9000CE2CE1 /* PromoAfterBookingData.mm in Sources */, @@ -842,6 +873,7 @@ 47E8163623B1889C008FD836 /* MWMStorage.mm in Sources */, 9957FAE9237AE5B000855F48 /* Logger.mm in Sources */, 999D3A65237B097C00C5F7A8 /* DeepLinkSubscriptionData.mm in Sources */, + 470B3621244DD9E400C0EA9E /* GuidesGalleryData.mm in Sources */, 47942D7C237CC41A00DEFAE3 /* HotelRoom.mm in Sources */, 47942D76237CC41A00DEFAE3 /* HotelBookingData.mm in Sources */, ); diff --git a/iphone/CoreApi/CoreApi/CoreApi-swift.h b/iphone/CoreApi/CoreApi/CoreApi-swift.h index 594dd68fd9..1628920ee0 100644 --- a/iphone/CoreApi/CoreApi/CoreApi-swift.h +++ b/iphone/CoreApi/CoreApi/CoreApi-swift.h @@ -55,3 +55,5 @@ FOUNDATION_EXPORT const unsigned char CoreApiVersionString[]; #import #import #import +#import +#import diff --git a/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryData+Core.h b/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryData+Core.h new file mode 100644 index 0000000000..5f66b37a8e --- /dev/null +++ b/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryData+Core.h @@ -0,0 +1,13 @@ +#import "GuidesGalleryData.h" + +#include + +NS_ASSUME_NONNULL_BEGIN + +@interface GuidesGalleryData (Core) + +- (instancetype)initWithGuidesGallery:(GuidesManager::GuidesGallery const &)guidesGallery; + +@end + +NS_ASSUME_NONNULL_END diff --git a/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryData.h b/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryData.h new file mode 100644 index 0000000000..3cd4ca3abe --- /dev/null +++ b/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryData.h @@ -0,0 +1,13 @@ +#import + +@class GuidesGalleryItem; + +NS_ASSUME_NONNULL_BEGIN + +@interface GuidesGalleryData : NSObject + +@property(nonatomic, readonly) NSArray *galleryItems; + +@end + +NS_ASSUME_NONNULL_END diff --git a/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryData.mm b/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryData.mm new file mode 100644 index 0000000000..d2e427f7db --- /dev/null +++ b/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryData.mm @@ -0,0 +1,31 @@ +#import "GuidesGalleryData+Core.h" +#import "GuidesGalleryItem+Core.h" + +@implementation GuidesGalleryData + +@end + +@implementation GuidesGalleryData (Core) + +- (instancetype)initWithGuidesGallery:(GuidesManager::GuidesGallery const &)guidesGallery { + self = [super init]; + if (self) { + NSMutableArray *itemsArray = [NSMutableArray arrayWithCapacity:guidesGallery.m_items.size()]; + for (auto const &item : guidesGallery.m_items) { + GuidesGalleryItem *galleryItem; + switch (item.m_type) { + case GuidesManager::GuidesGallery::Item::Type::City: + galleryItem = [[CityGalleryItem alloc] initWithGuidesGalleryItem:item]; + break; + case GuidesManager::GuidesGallery::Item::Type::Outdoor: + galleryItem = [[OutdoorGalleryItem alloc] initWithGuidesGalleryItem:item]; + break; + } + [itemsArray addObject:galleryItem]; + } + _galleryItems = [itemsArray copy]; + } + return self; +} + +@end diff --git a/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryItem+Core.h b/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryItem+Core.h new file mode 100644 index 0000000000..c667d8bd65 --- /dev/null +++ b/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryItem+Core.h @@ -0,0 +1,19 @@ +#import "GuidesGalleryItem.h" + +#include + +NS_ASSUME_NONNULL_BEGIN + +@interface CityGalleryItem (Core) + +- (instancetype)initWithGuidesGalleryItem:(GuidesManager::GuidesGallery::Item const &)guidesGalleryItem; + +@end + +@interface OutdoorGalleryItem (Core) + +- (instancetype)initWithGuidesGalleryItem:(GuidesManager::GuidesGallery::Item const &)guidesGalleryItem; + +@end + +NS_ASSUME_NONNULL_END diff --git a/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryItem.h b/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryItem.h new file mode 100644 index 0000000000..5100bb1546 --- /dev/null +++ b/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryItem.h @@ -0,0 +1,31 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface GuidesGalleryItem : NSObject + +@property(nonatomic, readonly) NSString *guideId; +@property(nonatomic, readonly) NSString *url; +@property(nonatomic, readonly) NSString *imageUrl; +@property(nonatomic, readonly) NSString *title; +@property(nonatomic, readonly) BOOL downloaded; + +@end + +@interface CityGalleryItem : GuidesGalleryItem + +@property(nonatomic, readonly) NSInteger bookmarksCount; +@property(nonatomic, readonly) BOOL hasTrack; + +@end + +@interface OutdoorGalleryItem : GuidesGalleryItem + +@property(nonatomic, readonly) NSString *tag; +@property(nonatomic, readonly) double distance; +@property(nonatomic, readonly) NSUInteger duration; +@property(nonatomic, readonly) NSUInteger ascent; + +@end + +NS_ASSUME_NONNULL_END diff --git a/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryItem.mm b/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryItem.mm new file mode 100644 index 0000000000..448224de17 --- /dev/null +++ b/iphone/CoreApi/CoreApi/PlacePageData/GuidesGallery/GuidesGalleryItem.mm @@ -0,0 +1,57 @@ +#import "GuidesGalleryItem+Core.h" + +@implementation GuidesGalleryItem + +@end + +@implementation GuidesGalleryItem (Core) + +- (instancetype)initWithGuidesGalleryItem:(GuidesManager::GuidesGallery::Item const &)guidesGalleryItem { + self = [super init]; + if (self) { + _guideId = @(guidesGalleryItem.m_guideId.c_str()); + _url = @(guidesGalleryItem.m_url.c_str()); + _imageUrl = @(guidesGalleryItem.m_imageUrl.c_str()); + _title = @(guidesGalleryItem.m_title.c_str()); + _downloaded = guidesGalleryItem.m_downloaded; + } + return self; +} + +@end + +@implementation CityGalleryItem + +@end + +@implementation CityGalleryItem (Core) + +- (instancetype)initWithGuidesGalleryItem:(GuidesManager::GuidesGallery::Item const &)guidesGalleryItem { + self = [super initWithGuidesGalleryItem:guidesGalleryItem]; + if (self) { + _bookmarksCount = guidesGalleryItem.m_cityParams.m_bookmarksCount; + _hasTrack = guidesGalleryItem.m_cityParams.m_trackIsAvailable; + } + return self; +} + +@end + +@implementation OutdoorGalleryItem + +@end + +@implementation OutdoorGalleryItem (Core) + +- (instancetype)initWithGuidesGalleryItem:(GuidesManager::GuidesGallery::Item const &)guidesGalleryItem { + self = [super initWithGuidesGalleryItem:guidesGalleryItem]; + if (self) { + _tag = @(guidesGalleryItem.m_outdoorsParams.m_tag.c_str()); + _distance = guidesGalleryItem.m_outdoorsParams.m_distance; + _duration = guidesGalleryItem.m_outdoorsParams.m_duration; + _ascent = guidesGalleryItem.m_outdoorsParams.m_ascent; + } + return self; +} + +@end diff --git a/iphone/CoreApi/CoreApi/PlacePageData/PlacePageData.h b/iphone/CoreApi/CoreApi/PlacePageData/PlacePageData.h index 7dc713b887..964351cd8a 100644 --- a/iphone/CoreApi/CoreApi/PlacePageData/PlacePageData.h +++ b/iphone/CoreApi/CoreApi/PlacePageData/PlacePageData.h @@ -12,6 +12,7 @@ @class HotelRooms; @class UgcData; @class ElevationProfileData; +@class GuidesGalleryData; @class MWMMapNodeAttributes; typedef NS_ENUM(NSInteger, PlacePageSponsoredType) { @@ -47,6 +48,7 @@ NS_ASSUME_NONNULL_BEGIN @interface PlacePageData : NSObject +@property(nonatomic, readonly, class) BOOL isGuide; @property(nonatomic, readonly, nullable) PlacePageButtonsData *buttonsData; @property(nonatomic, readonly) PlacePagePreviewData *previewData; @property(nonatomic, readonly, nullable) PlacePageInfoData *infoData; @@ -60,6 +62,7 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, readonly, nullable) HotelRooms *hotelRooms; @property(nonatomic, readonly, nullable) UgcData *ugcData; @property(nonatomic, readonly, nullable) ElevationProfileData *elevationProfileData; +@property(nonatomic, readonly, nullable) GuidesGalleryData *guidesGalleryData; @property(nonatomic, readonly, nullable) MWMMapNodeAttributes *mapNodeAttributes; @property(nonatomic, readonly, nullable) NSString *bookingSearchUrl; @property(nonatomic, readonly) BOOL isLargeToponim; diff --git a/iphone/CoreApi/CoreApi/PlacePageData/PlacePageData.mm b/iphone/CoreApi/CoreApi/PlacePageData/PlacePageData.mm index 32014f95b4..e1ba096ba4 100644 --- a/iphone/CoreApi/CoreApi/PlacePageData/PlacePageData.mm +++ b/iphone/CoreApi/CoreApi/PlacePageData/PlacePageData.mm @@ -9,6 +9,7 @@ #import "HotelRooms+Core.h" #import "UgcData+Core.h" #import "ElevationProfileData+Core.h" +#import "GuidesGalleryData+Core.h" #import "MWMMapNodeAttributes.h" #include @@ -145,6 +146,12 @@ static PlacePageRoadType convertRoadType(RoadWarningMarkType roadType) { _previewData = [[PlacePagePreviewData alloc] initWithRawData:rawData()]; } + if (rawData().IsGuide()) { + auto const &gm = GetFramework().GetGuidesManager(); + auto const &galleryData = gm.GetGallery(); + _guidesGalleryData = [[GuidesGalleryData alloc] initWithGuidesGallery:galleryData]; + } + auto const &countryId = rawData().GetCountryId(); if (!countryId.empty()) { _mapNodeAttributes = [[MWMStorage sharedStorage] attributesForCountry:@(rawData().GetCountryId().c_str())]; @@ -230,6 +237,10 @@ static PlacePageRoadType convertRoadType(RoadWarningMarkType roadType) { } } ++ (BOOL)isGuide { + return rawData().IsGuide(); +} + #pragma mark - Private - (void)loadBookingDataWithCompletion:(MWMVoidBlock)completion { diff --git a/iphone/Maps/Categories/UICollectionView+Cells.swift b/iphone/Maps/Categories/UICollectionView+Cells.swift index b49cbd6f76..c2c9e2939e 100644 --- a/iphone/Maps/Categories/UICollectionView+Cells.swift +++ b/iphone/Maps/Categories/UICollectionView+Cells.swift @@ -6,4 +6,12 @@ extension UICollectionView { @objc func dequeueReusableCell(withCellClass cellClass: AnyClass, indexPath: IndexPath) -> UICollectionViewCell { return dequeueReusableCell(withReuseIdentifier: toString(cellClass), for: indexPath) } + + func register(cell: Cell.Type) where Cell: UICollectionViewCell { + register(cell, forCellWithReuseIdentifier: toString(cell)) + } + + func dequeueReusableCell(cell: Cell.Type, indexPath: IndexPath) -> Cell where Cell: UICollectionViewCell { + return dequeueReusableCell(withReuseIdentifier: toString(cell), for: indexPath) as! Cell + } } diff --git a/iphone/Maps/Classes/MapViewController.mm b/iphone/Maps/Classes/MapViewController.mm index b67725677f..d575cb02f6 100644 --- a/iphone/Maps/Classes/MapViewController.mm +++ b/iphone/Maps/Classes/MapViewController.mm @@ -100,6 +100,7 @@ NSString *const kPP2BookmarkEditingSegue = @"PP2BookmarkEditing"; @property(nonatomic) BOOL deferredFocusValue; @property(nonatomic) UIViewController *placePageVC; @property(nonatomic) IBOutlet UIView *placePageContainer; +@property(nonatomic) IBOutlet UIView *guidesCollectionContainer; @end @@ -114,17 +115,28 @@ NSString *const kPP2BookmarkEditingSegue = @"PP2BookmarkEditing"; - (void)showPlacePage { self.controlsManager.trafficButtonHidden = YES; - self.placePageVC = [PlacePageBuilder build]; - [self addChildViewController:self.placePageVC]; - self.placePageContainer.hidden = NO; - [self.placePageContainer addSubview:self.placePageVC.view]; + if (PlacePageData.isGuide) { + self.placePageVC = [MWMGuidesGalleryBuilder build]; + [self.guidesCollectionContainer addSubview:self.placePageVC.view]; + [NSLayoutConstraint activateConstraints:@[ + [self.placePageVC.view.topAnchor constraintEqualToAnchor:self.guidesCollectionContainer.topAnchor], + [self.placePageVC.view.leftAnchor constraintEqualToAnchor:self.guidesCollectionContainer.leftAnchor], + [self.placePageVC.view.bottomAnchor constraintEqualToAnchor:self.guidesCollectionContainer.bottomAnchor], + [self.placePageVC.view.rightAnchor constraintEqualToAnchor:self.guidesCollectionContainer.rightAnchor] + ]]; + } else { + self.placePageVC = [PlacePageBuilder build]; + self.placePageContainer.hidden = NO; + [self.placePageContainer addSubview:self.placePageVC.view]; + [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.placePageVC.view.translatesAutoresizingMaskIntoConstraints = NO; - [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]; } diff --git a/iphone/Maps/Core/Theme/Fonts.swift b/iphone/Maps/Core/Theme/Fonts.swift index d5cfdfc103..422ded0f27 100644 --- a/iphone/Maps/Core/Theme/Fonts.swift +++ b/iphone/Maps/Core/Theme/Fonts.swift @@ -45,7 +45,7 @@ class Fonts: IFonts { var heavy32 = UIFont.systemFont(ofSize: 32, weight:UIFont.Weight.heavy) var heavy38 = UIFont.systemFont(ofSize: 38, weight:UIFont.Weight.heavy) var italic16 = UIFont.italicSystemFont(ofSize: 16) - var semibold12 = UIFont.systemFont(ofSize: 14, weight:UIFont.Weight.semibold) + var semibold12 = UIFont.systemFont(ofSize: 12, weight:UIFont.Weight.semibold) var semibold14 = UIFont.systemFont(ofSize: 14, weight:UIFont.Weight.semibold) var semibold15 = UIFont.systemFont(ofSize: 15, weight:UIFont.Weight.semibold) var semibold16 = UIFont.systemFont(ofSize: 16, weight:UIFont.Weight.semibold) diff --git a/iphone/Maps/Core/Theme/GuidesGalleryStyleSheet.swift b/iphone/Maps/Core/Theme/GuidesGalleryStyleSheet.swift new file mode 100644 index 0000000000..965020b386 --- /dev/null +++ b/iphone/Maps/Core/Theme/GuidesGalleryStyleSheet.swift @@ -0,0 +1,49 @@ +class GuidesGalleryStyleSheet: IStyleSheet { + private enum Const { + static let cityColor = UIColor(red: 0.4, green: 0.225, blue: 0.75, alpha: 1) + static let outdoorColor = UIColor(red: 0.235, green: 0.549, blue: 0.235, alpha: 1) + } + + static func register(theme: Theme, colors: IColors, fonts: IFonts) { + theme.add(styleName: "GuidesGalleryCell") { (s) -> (Void) in + s.backgroundColor = colors.white + s.cornerRadius = 4 + s.clip = true + s.shadowColor = colors.shadow + s.shadowOpacity = 0.25 + s.shadowRadius = 4 + } + + theme.add(styleName: "GuidesGalleryCellImage") { (s) -> (Void) in + s.cornerRadius = 4 + s.clip = true + s.backgroundColor = colors.blackDividers + } + + theme.add(styleName: "GuidesGalleryCityLabel") { (s) -> (Void) in + s.fontColor = Const.cityColor + s.font = fonts.regular12 + } + + theme.add(styleName: "GuidesGalleryOutdoorLabel") { (s) -> (Void) in + s.fontColor = Const.outdoorColor + s.font = fonts.regular12 + } + + theme.add(styleName: "GuidesGalleryCityCheck") { (s) -> (Void) in + s.tintColor = Const.cityColor + } + + theme.add(styleName: "GuidesGalleryOutdoorCheck") { (s) -> (Void) in + s.tintColor = Const.outdoorColor + } + + theme.add(styleName: "GuidesGalleryShowButton") { (s) -> (Void) in + s.cornerRadius = 14 + s.clip = true + s.backgroundColor = colors.blackOpaque + s.font = fonts.semibold14 + s.fontColor = colors.blackSecondaryText + } + } +} diff --git a/iphone/Maps/Core/Theme/MainTheme.swift b/iphone/Maps/Core/Theme/MainTheme.swift index b7246ac119..9e8af3faca 100644 --- a/iphone/Maps/Core/Theme/MainTheme.swift +++ b/iphone/Maps/Core/Theme/MainTheme.swift @@ -11,6 +11,7 @@ class MainTheme: Theme { self.registerStyleSheet(SubscriptionsStyleSheet.self) self.registerStyleSheet(PlacePageStyleSheet.self) self.registerStyleSheet(PartnersStyleSheet.self) + self.registerStyleSheet(GuidesGalleryStyleSheet.self) } } diff --git a/iphone/Maps/Images.xcassets/Routes Gallery/Contents.json b/iphone/Maps/Images.xcassets/Routes Gallery/Contents.json new file mode 100644 index 0000000000..da4a164c91 --- /dev/null +++ b/iphone/Maps/Images.xcassets/Routes Gallery/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_attitude.imageset/Contents.json b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_attitude.imageset/Contents.json new file mode 100644 index 0000000000..b2a67a7a7d --- /dev/null +++ b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_attitude.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "attitude_icon.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_attitude.imageset/attitude_icon.pdf b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_attitude.imageset/attitude_icon.pdf new file mode 100644 index 0000000000..84c48978bc --- /dev/null +++ b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_attitude.imageset/attitude_icon.pdf @@ -0,0 +1,88 @@ +%PDF-1.7 + +1 0 obj + << /ExtGState << /E1 << /ca 0.540000 >> >> >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +/E1 gs +0.707107 0.707107 -0.707107 0.707107 15.188722 -7.188722 cm +0.000000 0.000000 0.000000 scn +0.000000 16.473242 m +-0.358985 16.473242 -0.650000 16.182228 -0.650000 15.823242 c +-0.650000 15.464257 -0.358985 15.173243 0.000000 15.173243 c +0.000000 16.473242 l +h +11.773327 15.363623 m +12.027167 15.617463 12.027167 16.029020 11.773327 16.282862 c +7.636752 20.419436 l +7.382912 20.673277 6.971354 20.673277 6.717514 20.419436 c +6.463673 20.165596 6.463673 19.754038 6.717514 19.500198 c +10.394468 15.823242 l +6.717514 12.146287 l +6.463673 11.892447 6.463673 11.480888 6.717514 11.227049 c +6.971354 10.973207 7.382912 10.973207 7.636752 11.227049 c +11.773327 15.363623 l +h +0.000000 15.173243 m +11.313707 15.173243 l +11.313707 16.473242 l +0.000000 16.473242 l +0.000000 15.173243 l +h +f +n +Q + +endstream +endobj + +3 0 obj + 818 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 16.000000 16.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Type /Catalog + /Pages 5 0 R + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000074 00000 n +0000000948 00000 n +0000000970 00000 n +0000001143 00000 n +0000001217 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1276 +%%EOF \ No newline at end of file diff --git a/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_camera.imageset/Contents.json b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_camera.imageset/Contents.json new file mode 100644 index 0000000000..01b71f16b5 --- /dev/null +++ b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_camera.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Vector.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_camera.imageset/Vector.pdf b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_camera.imageset/Vector.pdf new file mode 100644 index 0000000000..1475d3c52e --- /dev/null +++ b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_camera.imageset/Vector.pdf @@ -0,0 +1,98 @@ +%PDF-1.7 + +1 0 obj + << /ExtGState << /E1 << /ca 0.540000 >> >> >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +/E1 gs +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.000000 0.000000 0.000000 scn +6.666596 6.512928 m +5.479297 6.512928 4.516556 5.580032 4.516556 4.428926 c +4.516556 3.277819 5.479297 2.344787 6.666596 2.344787 c +7.854178 2.344787 8.816635 3.277819 8.816635 4.428926 c +8.816635 5.580032 7.854178 6.512928 6.666596 6.512928 c +h +11.419336 9.213226 m +9.746202 9.213226 l +9.556518 10.044436 8.792818 10.666504 7.878562 10.666504 c +5.454346 10.666504 l +4.540091 10.666504 3.776391 10.044436 3.586990 9.213226 c +1.913855 9.213226 l +0.856982 9.213226 0.000000 8.382841 0.000000 7.358154 c +0.000000 1.854910 l +0.000000 0.830223 0.856982 -0.000162 1.913855 -0.000162 c +11.419478 -0.000162 l +12.476352 -0.000162 13.333334 0.830223 13.333334 1.854910 c +13.333334 7.358154 l +13.333192 8.382841 12.476210 9.213226 11.419336 9.213226 c +h +6.666596 1.344833 m +4.909393 1.344833 3.484918 2.725694 3.484918 4.428926 c +3.484918 6.132157 4.909535 7.512881 6.666596 7.512881 c +8.423656 7.512881 9.848274 6.132020 9.848274 4.428926 c +9.848274 2.725557 8.423799 1.344833 6.666596 1.344833 c +h +11.439325 6.933274 m +11.075835 6.933274 10.781385 7.218818 10.781385 7.571006 c +10.781385 7.923195 11.075693 8.208739 11.439325 8.208739 c +11.802674 8.208739 12.097266 7.923333 12.097266 7.571006 c +12.097266 7.218818 11.802674 6.933274 11.439325 6.933274 c +h +f +n +Q + +endstream +endobj + +3 0 obj + 1380 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 13.333344 10.666504 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Type /Catalog + /Pages 5 0 R + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000074 00000 n +0000001510 00000 n +0000001533 00000 n +0000001706 00000 n +0000001780 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1839 +%%EOF \ No newline at end of file diff --git a/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_cell_image.imageset/Contents.json b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_cell_image.imageset/Contents.json new file mode 100644 index 0000000000..26467a3fad --- /dev/null +++ b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_cell_image.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "image.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_cell_image.imageset/image.pdf b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_cell_image.imageset/image.pdf new file mode 100644 index 0000000000..21b8327ea2 --- /dev/null +++ b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_cell_image.imageset/image.pdf @@ -0,0 +1,101 @@ +%PDF-1.7 + +1 0 obj + << /ExtGState << /E1 << /ca 0.300000 >> >> >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +/E1 gs +1.000000 0.000000 -0.000000 1.000000 2.667969 6.667480 cm +0.517647 0.517647 0.517647 scn +2.666667 26.666504 m +1.193333 26.666504 0.000000 25.473171 0.000000 23.999838 c +0.000000 2.666506 l +0.000000 1.193172 1.193333 -0.000160 2.666667 -0.000160 c +31.999998 -0.000160 l +33.473331 -0.000160 34.666664 1.193172 34.666664 2.666506 c +34.666664 23.999838 l +34.666664 25.473171 33.473331 26.666504 31.999998 26.666504 c +2.666667 26.666504 l +h +27.999998 22.666504 m +29.473331 22.666504 30.666664 21.473171 30.666664 19.999838 c +30.666664 18.526505 29.473331 17.333172 27.999998 17.333172 c +26.526665 17.333172 25.333332 18.526505 25.333332 19.999838 c +25.333332 21.473171 26.526665 22.666504 27.999998 22.666504 c +h +4.000000 19.999838 m +10.630208 13.369630 l +11.999999 11.999838 l +13.541666 10.458172 l +14.104333 9.895504 15.012854 9.900713 15.575520 10.463381 c +16.139521 11.026047 16.139521 11.941047 15.575520 12.505047 c +14.039062 14.038900 l +15.999999 15.999838 l +19.963541 12.036297 l +22.874998 9.124838 l +23.437666 8.562172 24.346186 8.567381 24.908854 9.130047 c +25.472853 9.692713 25.472853 10.607714 24.908854 11.171713 c +24.705729 11.372234 l +26.666664 13.333172 l +30.666664 9.333172 l +30.666664 3.999838 l +4.000000 3.999838 l +4.000000 19.999838 l +h +f +n +Q + +endstream +endobj + +3 0 obj + 1296 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 40.000000 40.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Type /Catalog + /Pages 5 0 R + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000074 00000 n +0000001426 00000 n +0000001449 00000 n +0000001622 00000 n +0000001696 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1755 +%%EOF \ No newline at end of file diff --git a/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_checkmark.imageset/Contents.json b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_checkmark.imageset/Contents.json new file mode 100644 index 0000000000..dd9951a065 --- /dev/null +++ b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_checkmark.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Shape.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_checkmark.imageset/Shape.pdf b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_checkmark.imageset/Shape.pdf new file mode 100644 index 0000000000..ca0fe82ea3 --- /dev/null +++ b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_checkmark.imageset/Shape.pdf @@ -0,0 +1,79 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.400000 0.225000 0.750000 scn +0.000000 10.000000 m +0.000000 15.523000 4.477000 20.000000 10.000000 20.000000 c +15.523000 20.000000 20.000000 15.523000 20.000000 10.000000 c +20.000000 4.477000 15.523000 0.000000 10.000000 0.000000 c +4.477000 0.000000 0.000000 4.477000 0.000000 10.000000 c +h +5.400000 10.153847 m +4.000000 8.807693 l +8.000000 5.000000 l +16.000000 12.653847 l +14.600000 14.000000 l +8.000000 7.692307 l +5.400000 10.153847 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 535 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 20.000000 20.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Type /Catalog + /Pages 5 0 R + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000000625 00000 n +0000000647 00000 n +0000000820 00000 n +0000000894 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +953 +%%EOF \ No newline at end of file diff --git a/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_clock.imageset/Contents.json b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_clock.imageset/Contents.json new file mode 100644 index 0000000000..dd9951a065 --- /dev/null +++ b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_clock.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Shape.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_clock.imageset/Shape.pdf b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_clock.imageset/Shape.pdf new file mode 100644 index 0000000000..7955bda95f --- /dev/null +++ b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_clock.imageset/Shape.pdf @@ -0,0 +1,84 @@ +%PDF-1.7 + +1 0 obj + << /ExtGState << /E1 << /ca 0.540000 >> >> >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +/E1 gs +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.000000 0.000000 0.000000 scn +5.500000 11.000000 m +2.462250 11.000000 0.000000 8.537750 0.000000 5.500000 c +0.000000 2.462500 2.462250 0.000000 5.500000 0.000000 c +8.537750 0.000000 11.000000 2.462500 11.000000 5.500000 c +11.000000 8.537750 8.537750 11.000000 5.500000 11.000000 c +5.500000 11.000000 l +h +6.000000 5.339000 m +4.875000 3.823250 l +4.714000 3.598750 4.401500 3.547500 4.177250 3.708500 c +3.771000 4.000000 l +5.000000 5.661000 l +5.000000 9.500000 l +5.500000 9.500000 l +5.776250 9.500000 6.000000 9.276000 6.000000 9.000000 c +6.000000 5.339000 l +6.000000 5.339000 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 681 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 11.000000 11.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Type /Catalog + /Pages 5 0 R + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000074 00000 n +0000000811 00000 n +0000000833 00000 n +0000001006 00000 n +0000001080 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1139 +%%EOF \ No newline at end of file diff --git a/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_distance.imageset/Contents.json b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_distance.imageset/Contents.json new file mode 100644 index 0000000000..163385c1f2 --- /dev/null +++ b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_distance.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Union.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_distance.imageset/Union.pdf b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_distance.imageset/Union.pdf new file mode 100644 index 0000000000..8826809944 --- /dev/null +++ b/iphone/Maps/Images.xcassets/Routes Gallery/routes_gallery_distance.imageset/Union.pdf @@ -0,0 +1,90 @@ +%PDF-1.7 + +1 0 obj + << /ExtGState << /E1 << /ca 0.540000 >> >> >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +/E1 gs +1.000000 0.000000 -0.000000 1.000000 0.000000 -0.111328 cm +0.000000 0.000000 0.000000 scn +3.495425 5.947850 m +3.699788 5.734469 3.692478 5.395821 3.479097 5.191458 c +1.865900 3.646437 l +9.775461 3.647981 l +8.164761 5.190612 l +7.951380 5.394975 7.944069 5.733623 8.148432 5.947003 c +8.352795 6.160384 8.691443 6.167695 8.904823 5.963331 c +11.478914 3.498025 l +11.584276 3.397116 11.643857 3.257555 11.643857 3.111665 c +11.643857 2.965775 11.584276 2.826214 11.478914 2.725305 c +8.904823 0.259999 l +8.691443 0.055635 8.352795 0.062946 8.148432 0.276327 c +7.944069 0.489707 7.951380 0.828355 8.164761 1.032718 c +9.778264 2.578033 l +1.868090 2.576488 l +3.479097 1.033565 l +3.692478 0.829202 3.699788 0.490554 3.495425 0.277173 c +3.291062 0.063792 2.952414 0.056482 2.739033 0.260845 c +0.164943 2.726151 l +0.059581 2.827060 0.000000 2.966622 0.000000 3.112511 c +0.000000 3.258401 0.059581 3.397962 0.164943 3.498871 c +2.739033 5.964178 l +2.952414 6.168540 3.291062 6.161231 3.495425 5.947850 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1035 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 11.643860 6.001465 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Type /Catalog + /Pages 5 0 R + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000074 00000 n +0000001165 00000 n +0000001188 00000 n +0000001360 00000 n +0000001434 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1493 +%%EOF \ No newline at end of file diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index c99622659d..2a8b9cc23f 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -314,6 +314,9 @@ 470A89FD21342A9D00D72FBF /* TutorialBlurView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 470A89FC21342A9D00D72FBF /* TutorialBlurView.swift */; }; 470A89FF2134517600D72FBF /* BookmarksTutorialBlur.xib in Resources */ = {isa = PBXBuildFile; fileRef = 470A89FE2134517600D72FBF /* BookmarksTutorialBlur.xib */; }; 470A8A012136097000D72FBF /* SubwayTutorialBlur.xib in Resources */ = {isa = PBXBuildFile; fileRef = 470A8A002136073000D72FBF /* SubwayTutorialBlur.xib */; }; + 470B3630244E2DB400C0EA9E /* GuidesGalleryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 470B362F244E2DB400C0EA9E /* GuidesGalleryViewController.swift */; }; + 470B3632244E2DE200C0EA9E /* GuidesGalleryCityCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 470B3631244E2DE200C0EA9E /* GuidesGalleryCityCell.swift */; }; + 470B3634244E2DF900C0EA9E /* GuidesGalleryOutdoorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 470B3633244E2DF900C0EA9E /* GuidesGalleryOutdoorCell.swift */; }; 470F0B7D238842EA006AEC94 /* ExpandableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 470F0B7C238842EA006AEC94 /* ExpandableLabel.swift */; }; 470F0B7F2388431E006AEC94 /* StarRatingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 470F0B7E2388431E006AEC94 /* StarRatingView.swift */; }; 470F5A5B2181DE7500754295 /* PaidRouteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 470F5A592181DE7400754295 /* PaidRouteViewController.swift */; }; @@ -345,9 +348,13 @@ 473CBF9B2164DD470059BD54 /* SettingsTableViewSelectableProgressCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 473CBF9A2164DD470059BD54 /* SettingsTableViewSelectableProgressCell.swift */; }; 4740184323F5BDE800A93C81 /* minizip.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4740184123F5BDD300A93C81 /* minizip.framework */; }; 4740184423F5BDE900A93C81 /* minizip.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4740184123F5BDD300A93C81 /* minizip.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 4747045424622EF0006E51E9 /* GuidesGalleryStyleSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4747045324622EF0006E51E9 /* GuidesGalleryStyleSheet.swift */; }; 474AC76C2139E4F2002F9BF9 /* RemoveAdsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 474AC76A2139E4F2002F9BF9 /* RemoveAdsViewController.swift */; }; 474AC76D2139E4F2002F9BF9 /* RemoveAdsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 474AC76B2139E4F2002F9BF9 /* RemoveAdsViewController.xib */; }; 474C9F5A213FF75800369009 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 474C9F59213FF75800369009 /* StoreKit.framework */; }; + 475EC36D244EDE66003BC295 /* GuidesGalleryPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 475EC36C244EDE66003BC295 /* GuidesGalleryPresenter.swift */; }; + 475EC36F244EF7A9003BC295 /* GuidesGalleryBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 475EC36E244EF7A9003BC295 /* GuidesGalleryBuilder.swift */; }; + 475EC373244F0992003BC295 /* PlacePageGalleryLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 475EC372244F0992003BC295 /* PlacePageGalleryLayout.swift */; }; 4767CD9F20AAD48A00BD8166 /* Checkmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4767CD9E20AAD48A00BD8166 /* Checkmark.swift */; }; 4767CDA420AAF66B00BD8166 /* NSAttributedString+HTML.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4767CDA320AAF66B00BD8166 /* NSAttributedString+HTML.swift */; }; 4767CDA620AB1F6200BD8166 /* LeftAlignedIconButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4767CDA520AB1F6200BD8166 /* LeftAlignedIconButton.swift */; }; @@ -1415,6 +1422,9 @@ 470A89FC21342A9D00D72FBF /* TutorialBlurView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TutorialBlurView.swift; sourceTree = ""; }; 470A89FE2134517600D72FBF /* BookmarksTutorialBlur.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BookmarksTutorialBlur.xib; sourceTree = ""; }; 470A8A002136073000D72FBF /* SubwayTutorialBlur.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SubwayTutorialBlur.xib; sourceTree = ""; }; + 470B362F244E2DB400C0EA9E /* GuidesGalleryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuidesGalleryViewController.swift; sourceTree = ""; }; + 470B3631244E2DE200C0EA9E /* GuidesGalleryCityCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuidesGalleryCityCell.swift; sourceTree = ""; }; + 470B3633244E2DF900C0EA9E /* GuidesGalleryOutdoorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuidesGalleryOutdoorCell.swift; sourceTree = ""; }; 470F0B7C238842EA006AEC94 /* ExpandableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpandableLabel.swift; sourceTree = ""; }; 470F0B7E2388431E006AEC94 /* StarRatingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StarRatingView.swift; sourceTree = ""; }; 470F5A592181DE7400754295 /* PaidRouteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaidRouteViewController.swift; sourceTree = ""; }; @@ -1450,11 +1460,15 @@ 4738A8E8239FC526007C0F43 /* AdBannerView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AdBannerView.xib; sourceTree = ""; }; 473CBF9A2164DD470059BD54 /* SettingsTableViewSelectableProgressCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTableViewSelectableProgressCell.swift; sourceTree = ""; }; 4740184123F5BDD300A93C81 /* minizip.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = minizip.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4747045324622EF0006E51E9 /* GuidesGalleryStyleSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuidesGalleryStyleSheet.swift; sourceTree = ""; }; 474902D8224A54EC008D71E0 /* MWMRoutingOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMRoutingOptions.h; sourceTree = ""; }; 474902D9224A54EC008D71E0 /* MWMRoutingOptions.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMRoutingOptions.mm; sourceTree = ""; }; 474AC76A2139E4F2002F9BF9 /* RemoveAdsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveAdsViewController.swift; sourceTree = ""; }; 474AC76B2139E4F2002F9BF9 /* RemoveAdsViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RemoveAdsViewController.xib; sourceTree = ""; }; 474C9F59213FF75800369009 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; + 475EC36C244EDE66003BC295 /* GuidesGalleryPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuidesGalleryPresenter.swift; sourceTree = ""; }; + 475EC36E244EF7A9003BC295 /* GuidesGalleryBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuidesGalleryBuilder.swift; sourceTree = ""; }; + 475EC372244F0992003BC295 /* PlacePageGalleryLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlacePageGalleryLayout.swift; sourceTree = ""; }; 4767CD9E20AAD48A00BD8166 /* Checkmark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkmark.swift; sourceTree = ""; }; 4767CDA320AAF66B00BD8166 /* NSAttributedString+HTML.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+HTML.swift"; sourceTree = ""; }; 4767CDA520AB1F6200BD8166 /* LeftAlignedIconButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeftAlignedIconButton.swift; sourceTree = ""; }; @@ -3382,6 +3396,18 @@ path = Promo; sourceTree = ""; }; + 470B362E244E2D7600C0EA9E /* GuidesGallery */ = { + isa = PBXGroup; + children = ( + 470B362F244E2DB400C0EA9E /* GuidesGalleryViewController.swift */, + 470B3631244E2DE200C0EA9E /* GuidesGalleryCityCell.swift */, + 470B3633244E2DF900C0EA9E /* GuidesGalleryOutdoorCell.swift */, + 475EC36C244EDE66003BC295 /* GuidesGalleryPresenter.swift */, + 475EC36E244EF7A9003BC295 /* GuidesGalleryBuilder.swift */, + ); + path = GuidesGallery; + sourceTree = ""; + }; 470F0B7B238842AD006AEC94 /* Views */ = { isa = PBXGroup; children = ( @@ -3768,6 +3794,7 @@ 999FC12A23ABB4B800B0E6F9 /* FontStyleSheet.swift */, 99A614D423C8911A00D8D8D0 /* AuthStyleSheet.swift */, 3DBD7BE32425015C00ED9FE8 /* ParntersStyleSheet.swift */, + 4747045324622EF0006E51E9 /* GuidesGalleryStyleSheet.swift */, ); path = Theme; sourceTree = ""; @@ -3873,6 +3900,7 @@ 99C964222428C0D500E41723 /* PlacePageHeader */, 993DF0C223F6BD0600AC231A /* ElevationDetails */, 99DEF9D523E420D2006BFD21 /* ElevationProfile */, + 470B362E244E2D7600C0EA9E /* GuidesGallery */, ); path = Components; sourceTree = ""; @@ -3883,6 +3911,7 @@ 99C6532123F2F506004322F3 /* IPlacePageLayout.swift */, 99F3EB0223F4178200C713F8 /* PlacePageCommonLayout.swift */, 993DF0B423F6B2EF00AC231A /* PlacePageElevationLayout.swift */, + 475EC372244F0992003BC295 /* PlacePageGalleryLayout.swift */, ); path = Layouts; sourceTree = ""; @@ -5550,10 +5579,12 @@ 47C8789022DF525A00A772DA /* SubscriptionSuccessViewController.swift in Sources */, 47CF2E6123BA090400D11C30 /* FacilitiesController.swift in Sources */, 3462258F1DDC5DBA001E8752 /* MWMSearchNoResultsAlert.mm in Sources */, + 475EC36F244EF7A9003BC295 /* GuidesGalleryBuilder.swift in Sources */, 470F5A7D2189BB2F00754295 /* PaidRoutePurchase.swift in Sources */, 99A906E323F6F7030005872B /* HotelPhotosViewController.swift in Sources */, 34AB66171FC5AA320078E451 /* MWMiPhoneRoutePreview.m in Sources */, 99A906EA23F6F7030005872B /* PlacePageInfoViewController.swift in Sources */, + 470B3634244E2DF900C0EA9E /* GuidesGalleryOutdoorCell.swift in Sources */, 47E6688A23196F0100057733 /* UIViewController+Subscription.swift in Sources */, 993DF11723F6BDB100AC231A /* UINavigationBarRenderer.swift in Sources */, 6741A9E71BF340DE002C974C /* MWMCircularProgressView.m in Sources */, @@ -5618,6 +5649,7 @@ 34AB66111FC5AA320078E451 /* NavigationTurnsView.swift in Sources */, 348A8DF81F66775A00D83026 /* RatingViewDelegate.swift in Sources */, 4716EABA21A325310029B886 /* IPaidRouteStatistics.swift in Sources */, + 475EC36D244EDE66003BC295 /* GuidesGalleryPresenter.swift in Sources */, 3490D2E11CE9DD2500D0B838 /* MWMSideButtonsView.mm in Sources */, 47F4F21523A6F06F0022FD56 /* AvailableMapsDataSource.swift in Sources */, 99012852244732DB00C72B10 /* BottomTabBarBuilder.swift in Sources */, @@ -5736,6 +5768,7 @@ CDCA2743223F8D1E00167D87 /* ListItemInfo.swift in Sources */, B32FE74020D2844600EF7446 /* DownloadedBookmarksViewController.swift in Sources */, 340416541E7C09C200E2B6D6 /* PhotoScalingView.swift in Sources */, + 475EC373244F0992003BC295 /* PlacePageGalleryLayout.swift in Sources */, 99A906EF23F6F73D0005872B /* StarRatingViewRenderer.swift in Sources */, 478F6FA823C5067C00054A53 /* MyReviewView.swift in Sources */, 993DF11F23F6BDB100AC231A /* UITableViewCellRenderer.swift in Sources */, @@ -5789,6 +5822,7 @@ 33046832219C57180041F3A8 /* CategorySettingsViewController.swift in Sources */, 9917D17D2396793A00A7E06E /* PaidRoutesSubscriptionCampaign.swift in Sources */, 3404165C1E7C29AE00E2B6D6 /* PhotosInteractionAnimator.swift in Sources */, + 470B3630244E2DB400C0EA9E /* GuidesGalleryViewController.swift in Sources */, 993F550E237C622700545511 /* DeepLinkCataloguePathStrategy.swift in Sources */, 3404756E1E081A4600C92850 /* MWMSearch.mm in Sources */, 6741AA191BF340DE002C974C /* MWMDownloaderDialogCell.m in Sources */, @@ -5811,10 +5845,12 @@ 346B42AC1DD5E3D20094EBEE /* MWMLocationNotFoundAlert.mm in Sources */, 340475091E08199E00C92850 /* MWMMyTarget.mm in Sources */, 340416501E7C086000E2B6D6 /* PhotoViewController.swift in Sources */, + 470B3632244E2DE200C0EA9E /* GuidesGalleryCityCell.swift in Sources */, 993DF0C923F6BD0600AC231A /* ElevationDetailsBuilder.swift in Sources */, 674A7E301C0DB10B003D48E1 /* MWMMapWidgets.mm in Sources */, 34AB66291FC5AA330078E451 /* RouteManagerViewController.swift in Sources */, 33B19C65218B46C100B323A7 /* SharingTagsViewController.swift in Sources */, + 4747045424622EF0006E51E9 /* GuidesGalleryStyleSheet.swift in Sources */, 3404754D1E081A4600C92850 /* MWMKeyboard.m in Sources */, 993DF10C23F6BDB100AC231A /* MWMTableViewCellRenderer.swift in Sources */, 3457C4261F680F1900028233 /* String+BoundingRect.swift in Sources */, diff --git a/iphone/Maps/UI/PlacePage/Components/ElevationProfile/ElevationProfilePresenter.swift b/iphone/Maps/UI/PlacePage/Components/ElevationProfile/ElevationProfilePresenter.swift index 0226457a2e..7e396557e6 100644 --- a/iphone/Maps/UI/PlacePage/Components/ElevationProfile/ElevationProfilePresenter.swift +++ b/iphone/Maps/UI/PlacePage/Components/ElevationProfile/ElevationProfilePresenter.swift @@ -224,9 +224,10 @@ extension ElevationProfileChartData: IChartData { } } -fileprivate struct ChartFormatter: IFormatter { +struct ChartFormatter: IFormatter { private let distanceFormatter: MKDistanceFormatter private let altFormatter: MeasurementFormatter + private let timeFormatter: DateComponentsFormatter private let imperial: Bool init(imperial: Bool) { @@ -238,6 +239,11 @@ fileprivate struct ChartFormatter: IFormatter { altFormatter = MeasurementFormatter() altFormatter.unitOptions = [.providedUnit] + + timeFormatter = DateComponentsFormatter() + timeFormatter.allowedUnits = [.day, .hour, .minute] + timeFormatter.unitsStyle = .abbreviated + timeFormatter.maximumUnitCount = 2 } func distanceString(from value: Double) -> String { @@ -249,4 +255,8 @@ fileprivate struct ChartFormatter: IFormatter { let measurement = Measurement(value: alt.rounded(), unit: imperial ? UnitLength.feet : UnitLength.meters) return altFormatter.string(from: measurement) } + + func timeString(from value: Double) -> String { + timeFormatter.string(from: value) ?? "" + } } diff --git a/iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryBuilder.swift b/iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryBuilder.swift new file mode 100644 index 0000000000..857a15a28b --- /dev/null +++ b/iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryBuilder.swift @@ -0,0 +1,15 @@ +@objc(MWMGuidesGalleryBuilder) +final class GuidesGalleryBuilder: NSObject { + @objc static func build() -> GuidesGalleryViewController { + let placePageData = PlacePageData(localizationProvider: OpeinigHoursLocalization()) + guard let guidesGalleryData = placePageData.guidesGalleryData else { + fatalError() + } + + let storyboard = UIStoryboard.instance(.placePage) + let viewController = storyboard.instantiateViewController(ofType: GuidesGalleryViewController.self); + let presenter = GuidesGalleryPresenter(view: viewController, guidesGallery: guidesGalleryData) + viewController.presenter = presenter + return viewController + } +} diff --git a/iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryCityCell.swift b/iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryCityCell.swift new file mode 100644 index 0000000000..63270c5f6b --- /dev/null +++ b/iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryCityCell.swift @@ -0,0 +1,38 @@ +import UIKit + +final class GuidesGalleryCityCell: UICollectionViewCell { + @IBOutlet private var imageView: UIImageView! + @IBOutlet private var checkmarkImageView: UIImageView! + @IBOutlet private var titleLabel: UILabel! + @IBOutlet private var subtitleLabel: UILabel! + @IBOutlet private var infoLabel: UILabel! + @IBOutlet private var infoContainerView: UIView! + @IBOutlet private var buttonContainerView: UIView! + + func config(_ item: IGuidesGalleryCityItemViewModel) { + if let imageUrl = item.imageUrl { + imageView.wi_setImage(with: imageUrl, transitionDuration: 0) { [weak self] (image, error) in + guard let _ = image else { return } + self?.imageView.contentMode = .scaleAspectFill + } + } + titleLabel.text = item.title + subtitleLabel.text = item.subtitle + if !item.downloaded { + infoLabel.text = item.info + } + infoContainerView.isHidden = item.downloaded + buttonContainerView.isHidden = !item.downloaded + checkmarkImageView.isHidden = !item.downloaded + } + + override func prepareForReuse() { + super.prepareForReuse() + imageView.wi_cancelImageRequest() + imageView.image = UIImage(named: "routes_gallery_cell_image") + imageView.contentMode = .center + titleLabel.text = nil + subtitleLabel.text = nil + infoLabel.text = nil + } +} diff --git a/iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryOutdoorCell.swift b/iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryOutdoorCell.swift new file mode 100644 index 0000000000..fe9f95135b --- /dev/null +++ b/iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryOutdoorCell.swift @@ -0,0 +1,52 @@ +import UIKit + +final class GuidesGalleryOutdoorCell: UICollectionViewCell { + @IBOutlet private var imageView: UIImageView! + @IBOutlet private var checkmarkImageView: UIImageView! + @IBOutlet private var titleLabel: UILabel! + @IBOutlet private var subtitleLabel: UILabel! + @IBOutlet private var timeLabel: UILabel! + @IBOutlet private var timeIcon: UIImageView! + @IBOutlet private var distanceLabel: UILabel! + @IBOutlet private var ascentLabel: UILabel! + @IBOutlet private var infoContainerView: UIView! + @IBOutlet private var buttonContainerView: UIView! + + func config(_ item: IGuidesGalleryOutdoorItemViewModel) { + if let imageUrl = item.imageUrl { + imageView.wi_setImage(with: imageUrl, transitionDuration: 0) { [weak self] (image, error) in + guard let _ = image else { return } + self?.imageView.contentMode = .scaleAspectFill + } + } + titleLabel.text = item.title + subtitleLabel.text = item.subtitle + if !item.downloaded { + if let duration = item.duration { + timeLabel.text = duration + } else { + timeLabel.isHidden = true + timeIcon.isHidden = true + } + distanceLabel.text = item.distance + ascentLabel.text = item.ascent + } + infoContainerView.isHidden = item.downloaded + buttonContainerView.isHidden = !item.downloaded + checkmarkImageView.isHidden = !item.downloaded + } + + override func prepareForReuse() { + super.prepareForReuse() + imageView.wi_cancelImageRequest() + imageView.image = UIImage(named: "routes_gallery_cell_image") + imageView.contentMode = .center + titleLabel.text = nil + subtitleLabel.text = nil + timeLabel.text = nil + timeLabel.isHidden = false + timeIcon.isHidden = false + distanceLabel.text = nil + ascentLabel.text = nil + } +} diff --git a/iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryPresenter.swift b/iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryPresenter.swift new file mode 100644 index 0000000000..7c66ffab1d --- /dev/null +++ b/iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryPresenter.swift @@ -0,0 +1,86 @@ +import Foundation + +protocol IGuidesGalleryPresenter { + func viewDidLoad() + func selectItemAtIndex(_ index: Int) +} + +final class GuidesGalleryPresenter { + private weak var view: IGuidesGalleryView? + private var guidesGallery: GuidesGalleryData + private let formatter = ChartFormatter(imperial: Settings.measurementUnits() == .imperial) + + init(view: IGuidesGalleryView, guidesGallery: GuidesGalleryData) { + self.view = view + self.guidesGallery = guidesGallery + } + + private func makeViewModel(_ item: GuidesGalleryItem) -> IGuidesGalleryItemViewModel { + switch item { + case let cityItem as CityGalleryItem: + return makeCityItemViewModel(cityItem) + case let outdoorItem as OutdoorGalleryItem: + return makeOutdoorItemViewModel(outdoorItem) + default: + fatalError("Unexpected item type \(item)") + } + } + + private func makeCityItemViewModel(_ item: CityGalleryItem) -> IGuidesGalleryCityItemViewModel { + GalleryCityItemViewModel(item) + } + + private func makeOutdoorItemViewModel(_ item: OutdoorGalleryItem) -> IGuidesGalleryOutdoorItemViewModel { + GalleryOutdoorItemViewModel(item, formatter: formatter) + } +} + +extension GuidesGalleryPresenter: IGuidesGalleryPresenter { + func viewDidLoad() { + view?.setGalleryItems(guidesGallery.galleryItems.map({ makeViewModel($0) })) + } + + func selectItemAtIndex(_ index: Int) { + + } +} + +fileprivate struct GalleryCityItemViewModel: IGuidesGalleryCityItemViewModel { + var title: String + var subtitle: String + var imageUrl: URL? + var downloaded: Bool + var info: String + + init(_ item: CityGalleryItem) { + title = item.title + subtitle = item.hasTrack ? L("routes_card_routes_tag") : L("routes_card_set_tag") + imageUrl = URL(string: item.imageUrl) + downloaded = item.downloaded + var infoString = String(coreFormat: "routes_card_number_of_points", arguments: [item.bookmarksCount]) + if item.hasTrack { + infoString.append(" \(L("routes_card_plus_track"))") + } + info = infoString + } +} + +fileprivate struct GalleryOutdoorItemViewModel: IGuidesGalleryOutdoorItemViewModel { + var title: String + var subtitle: String + var imageUrl: URL? + var downloaded: Bool + var distance: String + var duration: String? + var ascent: String + + init(_ item: OutdoorGalleryItem, formatter: ChartFormatter) { + title = item.title + subtitle = item.tag + imageUrl = URL(string: item.imageUrl) + downloaded = item.downloaded + duration = item.duration > 0 ? formatter.timeString(from: Double(item.duration)) : nil + distance = formatter.distanceString(from: item.distance) + ascent = formatter.altitudeString(from: Double(item.ascent)) + } +} diff --git a/iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryViewController.swift b/iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryViewController.swift new file mode 100644 index 0000000000..8bd4a83606 --- /dev/null +++ b/iphone/Maps/UI/PlacePage/Components/GuidesGallery/GuidesGalleryViewController.swift @@ -0,0 +1,145 @@ +import UIKit + +protocol IGuidesGalleryView: AnyObject { + func setGalleryItems(_ items: [IGuidesGalleryItemViewModel]) +} + +protocol IGuidesGalleryItemViewModel { + var title: String { get } + var subtitle: String { get } + var imageUrl: URL? { get } + var downloaded: Bool { get } +} + +protocol IGuidesGalleryCityItemViewModel: IGuidesGalleryItemViewModel { + var info: String { get } +} + +protocol IGuidesGalleryOutdoorItemViewModel: IGuidesGalleryItemViewModel { + var distance: String { get } + var duration: String? { get } + var ascent: String { get } +} + +final class GuidesGalleryViewController: UIViewController { + @IBOutlet private var collectionView: UICollectionView! + var presenter: IGuidesGalleryPresenter? + private var galleryItems: [IGuidesGalleryItemViewModel] = [] + private var selectedIndex = 0 + + override func viewDidLoad() { + super.viewDidLoad() + + let layout = RoutesGalleryLayout() + layout.onScrollToItem = { [weak self] index in + guard let self = self, self.selectedIndex != index else { return } + self.presenter?.selectItemAtIndex(index) + self.selectedIndex = index + } + collectionView.collectionViewLayout = layout + collectionView.decelerationRate = .fast + presenter?.viewDidLoad() + } + + private func applyTransform() { + guard let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return } + let pageWidth = layout.itemSize.width + layout.minimumLineSpacing + for cell in collectionView.visibleCells { + let cellX = cell.convert(.zero, to: view).x + let distance = abs(layout.sectionInset.left - cellX) + let scale = max(1 - distance / pageWidth, 0) + cell.transform = CGAffineTransform(translationX: 0, y: -8 * scale) + } + } +} + +extension GuidesGalleryViewController: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + galleryItems.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let galleryItem = galleryItems[indexPath.item] + switch galleryItem { + case let cityGalleryItem as IGuidesGalleryCityItemViewModel: + let cityCell = collectionView.dequeueReusableCell(cell: GuidesGalleryCityCell.self, indexPath: indexPath) + cityCell.config(cityGalleryItem) + return cityCell + case let outdoorGalleryItem as IGuidesGalleryOutdoorItemViewModel: + let outdoorCell = collectionView.dequeueReusableCell(cell: GuidesGalleryOutdoorCell.self, indexPath: indexPath) + outdoorCell.config(outdoorGalleryItem) + return outdoorCell + default: + fatalError("Unexpected item type \(galleryItem)") + } + } +} + +extension GuidesGalleryViewController: UICollectionViewDelegate { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + presenter?.selectItemAtIndex(indexPath.item) + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + applyTransform() + } +} + +extension GuidesGalleryViewController: IGuidesGalleryView { + func setGalleryItems(_ items: [IGuidesGalleryItemViewModel]) { + galleryItems = items + collectionView.reloadData() + collectionView.performBatchUpdates({ + + }) { [weak self] _ in + self?.applyTransform() + } + } +} + +fileprivate final class RoutesGalleryLayout: UICollectionViewFlowLayout { + typealias OnScrollToItemClosure = (Int) -> Void + var onScrollToItem: OnScrollToItemClosure? + + override func prepare() { + super.prepare() + + guard let collectionView = collectionView else { return } + let availableWidth = collectionView.bounds.width + let itemWidth = min(availableWidth - 48, 366) + itemSize = CGSize(width: itemWidth, height: 120) + sectionInset = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16) + collectionView.contentInset = UIEdgeInsets(top: 0, + left: 0, + bottom: 0, + right: availableWidth - itemWidth - sectionInset.left - sectionInset.right) + scrollDirection = .horizontal + minimumLineSpacing = 8 + minimumInteritemSpacing = 8 + } + + override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { + true + } + + override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint { + targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: .zero) + } + + override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, + withScrollingVelocity velocity: CGPoint) -> CGPoint { + guard let x = collectionView?.contentOffset.x else { return proposedContentOffset } + let pageWidth = itemSize.width + minimumLineSpacing + let index = x / pageWidth + let adjustedIndex: CGFloat + if velocity.x < 0 { + adjustedIndex = floor(index) + } else if velocity.x > 0 { + adjustedIndex = ceil(index) + } else { + adjustedIndex = round(index) + } + onScrollToItem?(Int(adjustedIndex)) + return CGPoint(x: adjustedIndex * pageWidth, y: 0) + } +} diff --git a/iphone/Maps/UI/PlacePage/PlacePage.storyboard b/iphone/Maps/UI/PlacePage/PlacePage.storyboard index c6aa4b76b0..9e047c6a90 100644 --- a/iphone/Maps/UI/PlacePage/PlacePage.storyboard +++ b/iphone/Maps/UI/PlacePage/PlacePage.storyboard @@ -3375,6 +3375,500 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3393,5 +3887,11 @@ + + + + + + diff --git a/iphone/Maps/UI/PlacePage/PlacePageBuilder.swift b/iphone/Maps/UI/PlacePage/PlacePageBuilder.swift index 4dbbae1c29..5333b5ac64 100644 --- a/iphone/Maps/UI/PlacePage/PlacePageBuilder.swift +++ b/iphone/Maps/UI/PlacePage/PlacePageBuilder.swift @@ -5,6 +5,7 @@ fatalError() } let data = PlacePageData(localizationProvider: OpeinigHoursLocalization()) + viewController.isPreviewPlus = data.isPreviewPlus let interactor = PlacePageInteractor(viewController: viewController, data: data) let layout:IPlacePageLayout if data.elevationProfileData != nil { @@ -14,8 +15,7 @@ } let presenter = PlacePagePresenter(view: viewController, interactor: interactor, - layout: layout, - isPreviewPlus: data.isPreviewPlus) + layout: layout) interactor.presenter = presenter viewController.presenter = presenter diff --git a/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/IPlacePageLayout.swift b/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/IPlacePageLayout.swift index 50a8fb0bd4..674053ca43 100644 --- a/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/IPlacePageLayout.swift +++ b/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/IPlacePageLayout.swift @@ -23,7 +23,7 @@ enum PlacePageState { protocol IPlacePageLayout: class { var presenter: PlacePagePresenterProtocol? { get set } - var header: PlacePageHeaderViewController { get } + var header: PlacePageHeaderViewController? { get } var viewControllers: [UIViewController] { get } var actionBar: ActionBarViewController? { get } var navigationBar: UIViewController? { get } diff --git a/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/PlacePageCommonLayout.swift b/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/PlacePageCommonLayout.swift index 98e390c578..a4226272fa 100644 --- a/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/PlacePageCommonLayout.swift +++ b/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/PlacePageCommonLayout.swift @@ -151,7 +151,7 @@ class PlacePageCommonLayout: NSObject, IPlacePageLayout { return vc } () - lazy var header: PlacePageHeaderViewController = { + lazy var header: PlacePageHeaderViewController? = { return PlacePageHeaderBuilder.build(data: placePageData.previewData, delegate: interactor, headerType: .flexible) } () diff --git a/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/PlacePageElevationLayout.swift b/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/PlacePageElevationLayout.swift index 24db165f33..2fee36945f 100644 --- a/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/PlacePageElevationLayout.swift +++ b/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/PlacePageElevationLayout.swift @@ -16,7 +16,7 @@ class PlacePageElevationLayout: IPlacePageLayout { var adState: AdBannerState = .unset - lazy var header: PlacePageHeaderViewController = { + lazy var header: PlacePageHeaderViewController? = { return PlacePageHeaderBuilder.build(data: placePageData.previewData, delegate: interactor, headerType: .flexible) } () diff --git a/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/PlacePageGalleryLayout.swift b/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/PlacePageGalleryLayout.swift new file mode 100644 index 0000000000..8efb4568cb --- /dev/null +++ b/iphone/Maps/UI/PlacePage/PlacePageLayout/Layouts/PlacePageGalleryLayout.swift @@ -0,0 +1,27 @@ +final class PlacePageGalleryLayout: IPlacePageLayout { + weak var presenter: PlacePagePresenterProtocol? + var header: PlacePageHeaderViewController? { nil } + var actionBar: ActionBarViewController? { nil } + var navigationBar: UIViewController? { nil } + var adState: AdBannerState = .unset + + var viewControllers: [UIViewController] { + [guidesGalleryViewController] + } + + lazy var guidesGalleryViewController: GuidesGalleryViewController = { + GuidesGalleryBuilder.build() + }() + + func calculateSteps(inScrollView scrollView: UIScrollView, compact: Bool) -> [PlacePageState] { + var steps: [PlacePageState] = [] + let scrollHeight = scrollView.height + steps.append(.closed(-scrollHeight)) + guard let previewView = guidesGalleryViewController.view else { + return steps + } + let previewFrame = scrollView.convert(previewView.bounds, from: previewView) + steps.append(.preview(previewFrame.maxY - scrollHeight)) + return steps + } +} diff --git a/iphone/Maps/UI/PlacePage/PlacePagePresenter.swift b/iphone/Maps/UI/PlacePage/PlacePagePresenter.swift index d988e5e88d..2c43f90fa6 100644 --- a/iphone/Maps/UI/PlacePage/PlacePagePresenter.swift +++ b/iphone/Maps/UI/PlacePage/PlacePagePresenter.swift @@ -1,149 +1,38 @@ protocol PlacePagePresenterProtocol: class { - var maxOffset: CGFloat { get } - - func configure() - func setAdState(_ state: AdBannerState) - func updateSteps() func updatePreviewOffset() func layoutIfNeeded() - func findNextStop(_ offset: CGFloat, velocity: CGFloat) -> PlacePageState func showNextStop() - func showLastStop() - func onOffsetChanged(_ offset: CGFloat) func closeAnimated() } class PlacePagePresenter: NSObject { private weak var view: PlacePageViewProtocol! private let interactor: PlacePageInteractorProtocol - private let isPreviewPlus: Bool private let layout: IPlacePageLayout - private var scrollSteps:[PlacePageState] = [] - private var isNavigationBarVisible = false init(view: PlacePageViewProtocol, interactor: PlacePageInteractorProtocol, - layout: IPlacePageLayout, - isPreviewPlus: Bool) { + layout: IPlacePageLayout) { self.view = view self.interactor = interactor self.layout = layout - self.isPreviewPlus = isPreviewPlus - } - - private func setNavigationBarVisible(_ val: Bool) { - guard val != isNavigationBarVisible, let navigationBar = layout.navigationBar else { return } - isNavigationBarVisible = val - if isNavigationBarVisible { - view.addNavigationBar(navigationBar) - } else { - navigationBar.removeFromParent() - navigationBar.view.removeFromSuperview() - } + view.setLayout(layout) } } // MARK: - PlacePagePresenterProtocol extension PlacePagePresenter: PlacePagePresenterProtocol { - var maxOffset: CGFloat { - get { - return scrollSteps.last?.offset ?? 0 - } - } - - func configure() { - view.addHeader(layout.header) - for viewController in layout.viewControllers { - view.addToStack(viewController) - } - - if let actionBar = layout.actionBar { - view.hideActionBar(false) - view.addActionBar(actionBar) - } else { - view.hideActionBar(true) - } - updatePreviewOffset() - } - - func setAdState(_ state: AdBannerState) { - layout.adState = state - } - - func updateSteps() { - layoutIfNeeded() - scrollSteps = layout.calculateSteps(inScrollView: view.scrollView, - compact: view.traitCollection.verticalSizeClass == .compact) - } - func updatePreviewOffset() { - updateSteps() - if !view.beginDragging { - let state = isPreviewPlus ? scrollSteps[2] : scrollSteps[1] - view.scrollTo(CGPoint(x: 0, y: state.offset)) - } + view.updatePreviewOffset() } func layoutIfNeeded() { view.layoutIfNeeded() } - private func findNearestStop(_ offset: CGFloat) -> PlacePageState { - var result = scrollSteps[0] - scrollSteps.suffix(from: 1).forEach { ppState in - if abs(result.offset - offset) > abs(ppState.offset - offset) { - result = ppState - } - } - return result - } - - func findNextStop(_ offset: CGFloat, velocity: CGFloat) -> PlacePageState { - if velocity == 0 { - return findNearestStop(offset) - } - - var result: PlacePageState - if velocity < 0 { - guard let first = scrollSteps.first else { return .closed(-view.scrollView.height) } - result = first - scrollSteps.suffix(from: 1).forEach { - if offset > $0.offset { - result = $0 - } - } - } else { - guard let last = scrollSteps.last else { return .closed(-view.scrollView.height) } - result = last - scrollSteps.reversed().suffix(from: 1).forEach { - if offset < $0.offset { - result = $0 - } - } - } - - return result - } - func showNextStop() { - if let nextStop = scrollSteps.last(where: { $0.offset > view.scrollView.contentOffset.y }) { - view.scrollTo(CGPoint(x: 0, y: nextStop.offset), forced: true) - } - } - - func showLastStop() { - if let lastStop = scrollSteps.last { - view.scrollTo(CGPoint(x: 0, y: lastStop.offset), forced: true) - } - } - - func onOffsetChanged(_ offset: CGFloat) { - if offset > 0 && !isNavigationBarVisible{ - setNavigationBarVisible(true) - } else if offset <= 0 && isNavigationBarVisible { - setNavigationBarVisible(false) - } + view.showNextStop() } func closeAnimated() { diff --git a/iphone/Maps/UI/PlacePage/PlacePageViewController.swift b/iphone/Maps/UI/PlacePage/PlacePageViewController.swift index fe89446e16..255ec562f9 100644 --- a/iphone/Maps/UI/PlacePage/PlacePageViewController.swift +++ b/iphone/Maps/UI/PlacePage/PlacePageViewController.swift @@ -1,23 +1,10 @@ protocol PlacePageViewProtocol: class { var presenter: PlacePagePresenterProtocol! { get set } - var scrollView: UIScrollView! { get set } - var beginDragging: Bool { get set } - var traitCollection: UITraitCollection { get } - - func addHeader(_ headerViewController: UIViewController) - func addToStack(_ viewController: UIViewController) - func addActionBar(_ actionBarViewController: UIViewController) - func hideActionBar(_ value: Bool) - func addNavigationBar(_ header: UIViewController) - func scrollTo(_ point: CGPoint, animated: Bool, forced: Bool, completion: (()->())?) + func setLayout(_ layout: IPlacePageLayout) func layoutIfNeeded() func closeAnimated() -} - -extension PlacePageViewProtocol { - func scrollTo(_ point: CGPoint, animated: Bool = true, forced: Bool = false, completion: (()->())? = nil) { - scrollTo(point, animated: animated, forced: forced, completion: completion) - } + func updatePreviewOffset() + func showNextStop() } final class PlacePageScrollView: UIScrollView { @@ -40,14 +27,32 @@ final class PlacePageScrollView: UIScrollView { MapViewController.shared() } private var previousTraitCollection: UITraitCollection? + private var layout: IPlacePageLayout! + private var scrollSteps:[PlacePageState] = [] + var isPreviewPlus: Bool = false + private var isNavigationBarVisible = false - let kActionBarHeight:CGFloat = 50 + let kActionBarHeight: CGFloat = 50 // MARK: - VC Lifecycle override func viewDidLoad() { super.viewDidLoad() - presenter?.configure() + + if let header = layout.header { + addHeader(header) + } + for viewController in layout.viewControllers { + addToStack(viewController) + } + + if let actionBar = layout.actionBar { + hideActionBar(false) + addActionBar(actionBar) + } else { + hideActionBar(true) + } + updatePreviewOffset() let bgView = UIView() bgView.styleName = "PPBackgroundView" @@ -61,7 +66,7 @@ final class PlacePageScrollView: UIScrollView { if previousTraitCollection == nil { scrollView.contentInset = alternativeSizeClass(iPhone: UIEdgeInsets(top: scrollView.height, left: 0, bottom: 0, right: 0), iPad: UIEdgeInsets.zero) - presenter.updateSteps() + updateSteps() } panGesture.isEnabled = alternativeSizeClass(iPhone: false, iPad: true) self.previousTraitCollection = self.traitCollection @@ -69,22 +74,79 @@ final class PlacePageScrollView: UIScrollView { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - presenter?.updatePreviewOffset() + updatePreviewOffset() } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) if self.previousTraitCollection != nil { DispatchQueue.main.async { - self.presenter.setAdState(.detailed) - self.presenter.updateSteps() - self.presenter.showLastStop() + self.layout.adState = .detailed + self.updateSteps() + self.showLastStop() self.scrollView.contentInset = self.alternativeSizeClass(iPhone: UIEdgeInsets(top: self.scrollView.height, left: 0, bottom: 0, right: 0), iPad: UIEdgeInsets.zero) } } } + func updateSteps() { + layoutIfNeeded() + scrollSteps = layout.calculateSteps(inScrollView: scrollView, + compact: traitCollection.verticalSizeClass == .compact) + } + + func updatePreviewOffset() { + updateSteps() + if !beginDragging { + let state = isPreviewPlus ? scrollSteps[2] : scrollSteps[1] + scrollTo(CGPoint(x: 0, y: state.offset)) + } + } + + func findNextStop(_ offset: CGFloat, velocity: CGFloat) -> PlacePageState { + if velocity == 0 { + return findNearestStop(offset) + } + + var result: PlacePageState + if velocity < 0 { + guard let first = scrollSteps.first else { return .closed(-scrollView.height) } + result = first + scrollSteps.suffix(from: 1).forEach { + if offset > $0.offset { + result = $0 + } + } + } else { + guard let last = scrollSteps.last else { return .closed(-scrollView.height) } + result = last + scrollSteps.reversed().suffix(from: 1).forEach { + if offset < $0.offset { + result = $0 + } + } + } + + return result + } + + private func findNearestStop(_ offset: CGFloat) -> PlacePageState { + var result = scrollSteps[0] + scrollSteps.suffix(from: 1).forEach { ppState in + if abs(result.offset - offset) > abs(ppState.offset - offset) { + result = ppState + } + } + return result + } + + func showLastStop() { + if let lastStop = scrollSteps.last { + scrollTo(CGPoint(x: 0, y: lastStop.offset), forced: true) + } + } + @IBAction func onPan(gesture: UIPanGestureRecognizer){ let xOffset = gesture.translation(in: view.superview).x gesture.setTranslation(CGPoint.zero, in: view.superview) @@ -108,10 +170,8 @@ final class PlacePageScrollView: UIScrollView { } extension PlacePageViewController: PlacePageViewProtocol { - override var traitCollection: UITraitCollection { - get { - return super.traitCollection - } + func setLayout(_ layout: IPlacePageLayout) { + self.layout = layout } func hideActionBar(_ value: Bool) { @@ -155,7 +215,7 @@ extension PlacePageViewController: PlacePageViewProtocol { ]) } - func scrollTo(_ point: CGPoint, animated: Bool, forced: Bool, completion: (()->())?) { + func scrollTo(_ point: CGPoint, animated: Bool = true, forced: Bool = false, completion: (()->())? = nil) { if alternativeSizeClass(iPhone: beginDragging, iPad: true) && !forced { return } @@ -178,6 +238,12 @@ extension PlacePageViewController: PlacePageViewProtocol { } } + func showNextStop() { + if let nextStop = scrollSteps.last(where: { $0.offset > scrollView.contentOffset.y }) { + scrollTo(CGPoint(x: 0, y: nextStop.offset), forced: true) + } + } + func layoutIfNeeded() { view.layoutIfNeeded() } @@ -209,7 +275,7 @@ extension PlacePageViewController: UIScrollViewDelegate { if scrollView.contentOffset.y < -scrollView.height + 1 && beginDragging { rootViewController.dismissPlacePage() } - presenter.onOffsetChanged(scrollView.contentOffset.y) + onOffsetChanged(scrollView.contentOffset.y) } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { @@ -219,31 +285,50 @@ extension PlacePageViewController: UIScrollViewDelegate { func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { - let maxOffset = presenter.maxOffset + let maxOffset = scrollSteps.last?.offset ?? 0 if targetContentOffset.pointee.y > maxOffset { - presenter?.setAdState(.detailed) + layout.adState = .detailed return } - let targetState = presenter.findNextStop(scrollView.contentOffset.y, velocity: velocity.y) + let targetState = findNextStop(scrollView.contentOffset.y, velocity: velocity.y) if targetState.offset > scrollView.contentSize.height - scrollView.contentInset.top { - presenter?.setAdState(.detailed) + layout.adState = .detailed return } switch targetState { case .closed(_): fallthrough case .preview(_): - presenter?.setAdState(.compact) + layout.adState = .compact case .previewPlus(_): fallthrough case .expanded(_): fallthrough case .full(_): - presenter?.setAdState(.detailed) + layout.adState = .detailed } - presenter.updateSteps() - let nextStep = presenter.findNextStop(scrollView.contentOffset.y, velocity: velocity.y) + updateSteps() + let nextStep = findNextStop(scrollView.contentOffset.y, velocity: velocity.y) targetContentOffset.pointee = CGPoint(x: 0, y: nextStep.offset) } + + func onOffsetChanged(_ offset: CGFloat) { + if offset > 0 && !isNavigationBarVisible{ + setNavigationBarVisible(true) + } else if offset <= 0 && isNavigationBarVisible { + setNavigationBarVisible(false) + } + } + + private func setNavigationBarVisible(_ visible: Bool) { + guard visible != isNavigationBarVisible, let navigationBar = layout.navigationBar else { return } + isNavigationBarVisible = visible + if isNavigationBarVisible { + addNavigationBar(navigationBar) + } else { + navigationBar.removeFromParent() + navigationBar.view.removeFromSuperview() + } + } } diff --git a/iphone/Maps/UI/Storyboard/Main.storyboard b/iphone/Maps/UI/Storyboard/Main.storyboard index d642b41151..91d89f7ddd 100644 --- a/iphone/Maps/UI/Storyboard/Main.storyboard +++ b/iphone/Maps/UI/Storyboard/Main.storyboard @@ -1,9 +1,9 @@ - - + + - + @@ -13,29 +13,29 @@ - + - + - + @@ -122,7 +122,7 @@ + + + + + + + @@ -161,10 +168,12 @@ + + @@ -176,6 +185,7 @@ + @@ -217,6 +227,7 @@ + @@ -237,7 +248,7 @@ - + @@ -261,7 +272,7 @@ - + @@ -281,7 +292,7 @@ - + @@ -297,7 +308,7 @@ - + @@ -323,11 +334,11 @@ - + - + @@ -338,10 +349,10 @@ - + - + @@ -358,7 +369,7 @@ - + @@ -367,7 +378,7 @@ - + @@ -376,7 +387,7 @@ - + @@ -385,7 +396,7 @@ - + @@ -394,10 +405,10 @@ - + - + - + @@ -420,7 +431,7 @@ - + @@ -429,7 +440,7 @@ - + @@ -526,7 +537,7 @@ - + @@ -594,15 +605,15 @@ - + - + - + - + @@ -683,7 +694,7 @@ - + @@ -728,11 +739,11 @@ - + - + @@ -743,7 +754,7 @@ - + @@ -754,7 +765,7 @@ - + @@ -798,11 +809,11 @@ - + - + @@ -814,17 +825,17 @@ - + - +