Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladiMihaylenko <vxmihaylenko@gmail.com>2016-10-05 13:54:11 +0300
committerVladiMihaylenko <vxmihaylenko@gmail.com>2016-10-17 17:17:50 +0300
commit54798069b30de479fd804e44a1a036ea3db1c753 (patch)
treefc811cbee313607ed370b2f89d561e3517f6614e
parentc3b17e75f77632139aaa0643e4b4084aade2a991 (diff)
[ios] New place page
-rw-r--r--iphone/Maps/Bookmarks/BookmarksRootVC.mm2
-rw-r--r--iphone/Maps/Bookmarks/BookmarksVC.h4
-rw-r--r--iphone/Maps/Bookmarks/BookmarksVC.mm6
-rw-r--r--iphone/Maps/Bookmarks/SelectSetVC.mm10
-rw-r--r--iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.h9
-rw-r--r--iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm41
-rw-r--r--iphone/Maps/Classes/CustomViews/MapViewControls/Search/TabbedView/BookmarksTab/MWMSearchBookmarksCell.mm2
-rw-r--r--iphone/Maps/Classes/MWMActionBarButton.mm4
-rw-r--r--iphone/Maps/Classes/MWMBasePlacePageView.mm1
-rw-r--r--iphone/Maps/Classes/MWMBookmarkCell.h12
-rw-r--r--iphone/Maps/Classes/MWMBookmarkCell.mm216
-rw-r--r--iphone/Maps/Classes/MWMDirectionView.h2
-rw-r--r--iphone/Maps/Classes/MWMDirectionView.mm35
-rw-r--r--iphone/Maps/Classes/MWMEditBookmarkController.h2
-rw-r--r--iphone/Maps/Classes/MWMEditBookmarkController.mm68
-rw-r--r--iphone/Maps/Classes/MWMOpeningHours.h32
-rw-r--r--iphone/Maps/Classes/MWMOpeningHours.mm128
-rw-r--r--iphone/Maps/Classes/MWMOpeningHoursCell.h10
-rw-r--r--iphone/Maps/Classes/MWMOpeningHoursCell.mm200
-rw-r--r--iphone/Maps/Classes/MWMPPView.h31
-rw-r--r--iphone/Maps/Classes/MWMPPView.mm101
-rw-r--r--iphone/Maps/Classes/MWMPlacePage.mm2
-rw-r--r--iphone/Maps/Classes/MWMPlacePageActionBar.h18
-rw-r--r--iphone/Maps/Classes/MWMPlacePageActionBar.mm79
-rw-r--r--iphone/Maps/Classes/MWMPlacePageActionBar.xib (renamed from iphone/Maps/Classes/PlacePageActionBar.xib)2
-rw-r--r--iphone/Maps/Classes/MWMPlacePageBookmarkCell.h9
-rw-r--r--iphone/Maps/Classes/MWMPlacePageBookmarkCell.mm1
-rw-r--r--iphone/Maps/Classes/MWMPlacePageBookmarkDelegate.h8
-rw-r--r--iphone/Maps/Classes/MWMPlacePageButtonCell.h11
-rw-r--r--iphone/Maps/Classes/MWMPlacePageButtonCell.mm61
-rw-r--r--iphone/Maps/Classes/MWMPlacePageCellUpdateProtocol.h5
-rw-r--r--iphone/Maps/Classes/MWMPlacePageData.h103
-rw-r--r--iphone/Maps/Classes/MWMPlacePageData.mm406
-rw-r--r--iphone/Maps/Classes/MWMPlacePageEntity.h7
-rw-r--r--iphone/Maps/Classes/MWMPlacePageEntity.mm23
-rw-r--r--iphone/Maps/Classes/MWMPlacePageInfoCell.h6
-rw-r--r--iphone/Maps/Classes/MWMPlacePageInfoCell.mm113
-rw-r--r--iphone/Maps/Classes/MWMPlacePageLayout.h42
-rw-r--r--iphone/Maps/Classes/MWMPlacePageLayout.mm530
-rw-r--r--iphone/Maps/Classes/MWMPlacePageManager.h5
-rw-r--r--iphone/Maps/Classes/MWMPlacePageManager.mm335
-rw-r--r--iphone/Maps/Classes/MWMPlacePagePreviewCell.h24
-rw-r--r--iphone/Maps/Classes/MWMPlacePagePreviewCell.mm426
-rw-r--r--iphone/Maps/Classes/MWMPlacePageProtocol.h58
-rw-r--r--iphone/Maps/Classes/MWMPlacePageViewManager.h7
-rw-r--r--iphone/Maps/Classes/MWMPlacePageViewManager.mm45
-rw-r--r--iphone/Maps/Classes/MapViewController.h2
-rw-r--r--iphone/Maps/Classes/MapViewController.mm21
-rw-r--r--iphone/Maps/Classes/Share/MWMActivityViewController.h4
-rw-r--r--iphone/Maps/Classes/Share/MWMActivityViewController.mm4
-rw-r--r--iphone/Maps/Classes/Share/MWMShareActivityItem.h21
-rw-r--r--iphone/Maps/Classes/Share/MWMShareActivityItem.mm40
52 files changed, 3155 insertions, 179 deletions
diff --git a/iphone/Maps/Bookmarks/BookmarksRootVC.mm b/iphone/Maps/Bookmarks/BookmarksRootVC.mm
index b2600575b4..77f2ad1ec0 100644
--- a/iphone/Maps/Bookmarks/BookmarksRootVC.mm
+++ b/iphone/Maps/Bookmarks/BookmarksRootVC.mm
@@ -219,7 +219,7 @@
}
else
{
- BookmarksVC * bvc = [[BookmarksVC alloc] initWithCategory:indexPath.row];
+ BookmarksVC * bvc = [[BookmarksVC alloc] initWithCategory:static_cast<int>(indexPath.row)];
[self.navigationController pushViewController:bvc animated:YES];
}
}
diff --git a/iphone/Maps/Bookmarks/BookmarksVC.h b/iphone/Maps/Bookmarks/BookmarksVC.h
index 52a78d376d..f19b207856 100644
--- a/iphone/Maps/Bookmarks/BookmarksVC.h
+++ b/iphone/Maps/Bookmarks/BookmarksVC.h
@@ -2,9 +2,9 @@
@interface BookmarksVC : MWMTableViewController <UITextFieldDelegate>
{
- size_t m_categoryIndex;
+ int m_categoryIndex;
}
-- (instancetype)initWithCategory:(size_t)index;
+- (instancetype)initWithCategory:(int)index;
@end
diff --git a/iphone/Maps/Bookmarks/BookmarksVC.mm b/iphone/Maps/Bookmarks/BookmarksVC.mm
index b08c08a68e..94ddc78439 100644
--- a/iphone/Maps/Bookmarks/BookmarksVC.mm
+++ b/iphone/Maps/Bookmarks/BookmarksVC.mm
@@ -38,7 +38,7 @@ extern NSString * const kBookmarksChangedNotification = @"BookmarksChangedNotifi
@implementation BookmarksVC
-- (instancetype)initWithCategory:(size_t)index
+- (instancetype)initWithCategory:(int)index
{
self = [super initWithStyle:UITableViewStyleGrouped];
if (self)
@@ -236,7 +236,7 @@ extern NSString * const kBookmarksChangedNotification = @"BookmarksChangedNotifi
// Same as "Close".
MapViewController * mapVC = self.navigationController.viewControllers.firstObject;
mapVC.controlsManager.searchHidden = YES;
- f.ShowBookmark(BookmarkAndCategory(m_categoryIndex, indexPath.row));
+ f.ShowBookmark({static_cast<int>(indexPath.row), m_categoryIndex});
[self.navigationController popToRootViewControllerAnimated:YES];
}
}
@@ -294,7 +294,7 @@ extern NSString * const kBookmarksChangedNotification = @"BookmarksChangedNotifi
}
else
{
- BookmarkAndCategory bookmarkAndCategory = BookmarkAndCategory(m_categoryIndex, indexPath.row);
+ BookmarkAndCategory bookmarkAndCategory{static_cast<int>(indexPath.row), m_categoryIndex};
NSValue * value = [NSValue valueWithBytes:&bookmarkAndCategory objCType:@encode(BookmarkAndCategory)];
[[NSNotificationCenter defaultCenter] postNotificationName:BOOKMARK_DELETED_NOTIFICATION object:value];
BookmarkCategory::Guard guard(*cat);
diff --git a/iphone/Maps/Bookmarks/SelectSetVC.mm b/iphone/Maps/Bookmarks/SelectSetVC.mm
index f5b8d29b4f..c3952b5cb4 100644
--- a/iphone/Maps/Bookmarks/SelectSetVC.mm
+++ b/iphone/Maps/Bookmarks/SelectSetVC.mm
@@ -35,7 +35,7 @@
[super viewDidLoad];
NSAssert(self.category, @"Category can't be nil!");
NSAssert(self.delegate, @"Delegate can't be nil!");
- NSAssert(IsValid(m_bac), @"Invalid BookmarkAndCategory's instance!");
+ NSAssert(m_bac.IsValid(), @"Invalid BookmarkAndCategory's instance!");
self.title = L(@"bookmark_sets");
}
@@ -67,7 +67,7 @@
if (cat)
cell.textLabel.text = @(cat->GetName().c_str());
- if (m_bac.first == indexPath.row)
+ if (m_bac.m_categoryIndex == indexPath.row)
cell.accessoryType = UITableViewCellAccessoryCheckmark;
else
cell.accessoryType = UITableViewCellAccessoryNone;
@@ -85,11 +85,11 @@
- (void)moveBookmarkToSetWithIndex:(int)setIndex
{
BookmarkAndCategory bac;
- bac.second = static_cast<int>(GetFramework().MoveBookmark(m_bac.second, m_bac.first, setIndex));
- bac.first = setIndex;
+ bac.m_bookmarkIndex = static_cast<int>(GetFramework().MoveBookmark(m_bac.m_bookmarkIndex, m_bac.m_categoryIndex, setIndex));
+ bac.m_categoryIndex = setIndex;
m_bac = bac;
- BookmarkCategory const * category = GetFramework().GetBookmarkManager().GetBmCategory(bac.first);
+ BookmarkCategory const * category = GetFramework().GetBookmarkManager().GetBmCategory(bac.m_categoryIndex);
self.category = @(category->GetName().c_str());
}
diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.h b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.h
index 9f218003f9..5af2b8b449 100644
--- a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.h
+++ b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.h
@@ -9,6 +9,7 @@
@class MapViewController;
@class MWMPlacePageEntity;
+@protocol MWMFeatureHolder;
@interface MWMMapViewControlsManager : NSObject
@@ -20,8 +21,8 @@
@property(nonatomic) MWMBottomMenuState menuState;
@property(nonatomic) MWMBottomMenuState menuRestoreState;
@property(nonatomic, readonly) MWMNavigationDashboardState navigationState;
-@property(nonatomic, readonly) MWMPlacePageEntity * placePageEntity;
@property(nonatomic) BOOL searchHidden;
+@property(nonatomic) BOOL isDirectionViewHidden;
- (instancetype)init __attribute__((unavailable("init is not available")));
- (instancetype)initWithParentController:(MapViewController *)controller;
@@ -40,8 +41,6 @@
#pragma mark - MWMPlacePageViewManager
-@property(nonatomic, readonly) BOOL isDirectionViewShown;
-
- (void)dismissPlacePage;
- (void)showPlacePage:(place_page::Info const &)info;
- (void)addPlacePageViews:(NSArray *)views;
@@ -68,4 +67,8 @@
- (void)searchFrameUpdated:(CGRect)frame;
- (void)searchText:(NSString *)text forInputLocale:(NSString *)locale;
+#pragma mark - MWMFeatureHolder
+
+- (id<MWMFeatureHolder>)featureHolder;
+
@end
diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm
index f5877cf524..06f83de0a5 100644
--- a/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm
+++ b/iphone/Maps/Classes/CustomViews/MapViewControls/MWMMapViewControlsManager.mm
@@ -10,6 +10,7 @@
#import "MWMFrameworkListener.h"
#import "MWMObjectsCategorySelectorController.h"
#import "MWMPlacePageEntity.h"
+#import "MWMPlacePageManager.h"
#import "MWMPlacePageViewManager.h"
#import "MWMRoutePreview.h"
#import "MWMRouter.h"
@@ -42,7 +43,7 @@ extern NSString * const kAlohalyticsTapEventKey;
@property(nonatomic) MWMSideButtons * sideButtons;
@property(nonatomic) MWMBottomMenuViewController * menuController;
-@property(nonatomic) MWMPlacePageViewManager * placePageManager;
+@property(nonatomic) id<MWMPlacePageProtocol> placePageManager;
@property(nonatomic) MWMNavigationDashboardManager * navigationManager;
@property(nonatomic) MWMSearchManager * searchManager;
@@ -84,7 +85,7 @@ extern NSString * const kAlohalyticsTapEventKey;
searchManagerState != MWMSearchManagerStateHidden) ||
self.navigationState == MWMNavigationDashboardStatePlanning ||
self.navigationState == MWMNavigationDashboardStateReady ||
- self.menuState == MWMBottomMenuStateActive || self.isDirectionViewShown ||
+ self.menuState == MWMBottomMenuStateActive || !self.isDirectionViewHidden ||
(isNightMode && self.navigationState != MWMNavigationDashboardStateHidden) ||
MapsAppDelegate.theApp.routingPlaneMode != MWMRoutingPlaneModeNone;
return (isLight || (!isLight && isNightMode)) ? UIStatusBarStyleLightContent
@@ -292,12 +293,19 @@ extern NSString * const kAlohalyticsTapEventKey;
{
if (IPAD)
return;
- CGSize const ownerViewSize = self.ownerController.view.size;
- if (ownerViewSize.width > ownerViewSize.height)
+ if (isIOS7)
{
- CGFloat const leftBound = frame.origin.x + frame.size.width;
- self.menuController.leftBound = leftBound;
- [MWMNavigationDashboardManager manager].leftBound = leftBound;
+ CGSize const ownerViewSize = self.ownerController.view.size;
+ if (ownerViewSize.width > ownerViewSize.height)
+ {
+ CGFloat const leftBound = frame.origin.x + frame.size.width;
+ self.menuController.leftBound = leftBound;
+ [MWMNavigationDashboardManager manager].leftBound = leftBound;
+ }
+ else
+ {
+ [self.sideButtons setBottomBound:frame.origin.y];
+ }
}
else
{
@@ -459,11 +467,13 @@ extern NSString * const kAlohalyticsTapEventKey;
return _menuController;
}
-- (MWMPlacePageViewManager *)placePageManager
+- (id)placePageManager
{
+ auto const PlacePageClass = isIOS7 || IPAD ? [MWMPlacePageViewManager class] : [MWMPlacePageManager class];
+
if (!_placePageManager)
_placePageManager =
- [[MWMPlacePageViewManager alloc] initWithViewController:self.ownerController];
+ [[PlacePageClass alloc] initWithViewController:self.ownerController];
return _placePageManager;
}
@@ -526,12 +536,6 @@ extern NSString * const kAlohalyticsTapEventKey;
}
- (MWMNavigationDashboardState)navigationState { return self.navigationManager.state; }
-- (MWMPlacePageEntity *)placePageEntity { return self.placePageManager.entity; }
-- (BOOL)isDirectionViewShown
-{
- return _placePageManager ? _placePageManager.isDirectionViewShown : NO;
-}
-
- (void)setTopBound:(CGFloat)topBound
{
if (IPAD)
@@ -558,4 +562,11 @@ extern NSString * const kAlohalyticsTapEventKey;
searchHidden ? MWMSearchManagerStateHidden : MWMSearchManagerStateDefault;
}
+#pragma mark - MWMFeatureHolder
+
+- (id<MWMFeatureHolder>)featureHolder
+{
+ return self.placePageManager;
+}
+
@end
diff --git a/iphone/Maps/Classes/CustomViews/MapViewControls/Search/TabbedView/BookmarksTab/MWMSearchBookmarksCell.mm b/iphone/Maps/Classes/CustomViews/MapViewControls/Search/TabbedView/BookmarksTab/MWMSearchBookmarksCell.mm
index 54cf32d377..3d2ac72aa7 100644
--- a/iphone/Maps/Classes/CustomViews/MapViewControls/Search/TabbedView/BookmarksTab/MWMSearchBookmarksCell.mm
+++ b/iphone/Maps/Classes/CustomViews/MapViewControls/Search/TabbedView/BookmarksTab/MWMSearchBookmarksCell.mm
@@ -57,7 +57,7 @@
- (IBAction)openBookmarks
{
- BookmarksVC * bvc = [[BookmarksVC alloc] initWithCategory:self.index];
+ BookmarksVC * bvc = [[BookmarksVC alloc] initWithCategory:static_cast<int>(self.index)];
UINavigationController * rootVC = (UINavigationController *)UIApplication.sharedApplication.delegate.window.rootViewController;
[rootVC pushViewController:bvc animated:YES];
}
diff --git a/iphone/Maps/Classes/MWMActionBarButton.mm b/iphone/Maps/Classes/MWMActionBarButton.mm
index 7e67a3bf1a..361ec00a44 100644
--- a/iphone/Maps/Classes/MWMActionBarButton.mm
+++ b/iphone/Maps/Classes/MWMActionBarButton.mm
@@ -1,3 +1,4 @@
+#import "Common.h"
#import "MWMActionBarButton.h"
#import "MWMButton.h"
#import "UIColor+MapsMeColor.h"
@@ -95,7 +96,8 @@ NSString * titleForButton(EButton type, BOOL isSelected)
button.delegate = delegate;
button.type = type;
[view addSubview:button];
- button.autoresizingMask = UIViewAutoresizingNone;
+ if (isIOS7)
+ button.autoresizingMask = UIViewAutoresizingNone;
[button configButton:isSelected];
}
diff --git a/iphone/Maps/Classes/MWMBasePlacePageView.mm b/iphone/Maps/Classes/MWMBasePlacePageView.mm
index 16e3b99c65..9c56f2f9db 100644
--- a/iphone/Maps/Classes/MWMBasePlacePageView.mm
+++ b/iphone/Maps/Classes/MWMBasePlacePageView.mm
@@ -6,6 +6,7 @@
#import "MWMPlacePage.h"
#import "MWMPlacePageActionBar.h"
#import "MWMPlacePageBookmarkCell.h"
+#import "MWMPlacePageBookmarkDelegate.h"
#import "MWMPlacePageButtonCell.h"
#import "MWMPlacePageEntity.h"
#import "MWMPlacePageInfoCell.h"
diff --git a/iphone/Maps/Classes/MWMBookmarkCell.h b/iphone/Maps/Classes/MWMBookmarkCell.h
new file mode 100644
index 0000000000..a9c30ab996
--- /dev/null
+++ b/iphone/Maps/Classes/MWMBookmarkCell.h
@@ -0,0 +1,12 @@
+#import "MWMTableViewCell.h"
+
+@protocol MWMPlacePageButtonsProtocol, MWMPlacePageCellUpdateProtocol;
+
+@interface MWMBookmarkCell : MWMTableViewCell
+
+- (void)configureWithText:(NSString *)text
+ updateCellDelegate:(id<MWMPlacePageCellUpdateProtocol>)updateCellDelegate
+ editBookmarkDelegate:(id<MWMPlacePageButtonsProtocol>)editBookmarkDelegate
+ isHTML:(BOOL)isHTML;
+
+@end
diff --git a/iphone/Maps/Classes/MWMBookmarkCell.mm b/iphone/Maps/Classes/MWMBookmarkCell.mm
new file mode 100644
index 0000000000..4698529b7d
--- /dev/null
+++ b/iphone/Maps/Classes/MWMBookmarkCell.mm
@@ -0,0 +1,216 @@
+#import "MWMPlacePageCellUpdateProtocol.h"
+#import "MWMBookmarkCell.h"
+
+#import "MWMPlacePageProtocol.h"
+#import "UIColor+MapsMeColor.h"
+#import "UIFont+MapsMeFonts.h"
+
+namespace
+{
+void * kContext = &kContext;
+NSString * const kTextViewContentSizeKeyPath = @"contentSize";
+
+} // namespace
+
+@interface MWMBookmarkCell ()
+
+@property(weak, nonatomic) IBOutlet UITextView * textView;
+@property(weak, nonatomic) IBOutlet UIImageView * spinner;
+@property(weak, nonatomic) IBOutlet UIButton * editButton;
+
+@property(weak, nonatomic) IBOutlet UIImageView * gradientView;
+
+@property(nonatomic) IBOutlet NSLayoutConstraint * textViewHeight;
+@property(weak, nonatomic) IBOutlet NSLayoutConstraint * moreButtonHeight;
+@property(nonatomic) IBOutlet NSLayoutConstraint * textViewZeroHeight;
+
+@property(weak, nonatomic) id<MWMPlacePageCellUpdateProtocol> updateCellDelegate;
+@property(weak, nonatomic) id<MWMPlacePageButtonsProtocol> editBookmarkDelegate;
+
+@property(copy, nonatomic) NSAttributedString * attributedHTML;
+
+@property (nonatomic) BOOL isOpen;
+
+@end
+
+@implementation MWMBookmarkCell
+
+- (void)awakeFromNib
+{
+ [super awakeFromNib];
+ [self registerObserver];
+}
+
+- (void)dealloc
+{
+ [self unregisterObserver];
+}
+
+- (void)unregisterObserver
+{
+ [self.textView removeObserver:self forKeyPath:kTextViewContentSizeKeyPath context:kContext];
+}
+
+- (void)registerObserver
+{
+ [self.textView addObserver:self forKeyPath:kTextViewContentSizeKeyPath options:NSKeyValueObservingOptionNew context:kContext];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath
+ ofObject:(id)object
+ change:(NSDictionary *)change
+ context:(void *)context
+{
+ if (context == kContext)
+ {
+ NSValue * s = change[@"new"];
+ CGFloat const height = s.CGSizeValue.height;
+ auto const boundedHeight = self.textViewHeight.constant;
+
+ if (height < boundedHeight || self.isOpen)
+ [self stateOpen:YES];
+ else if (height > boundedHeight && !self.isOpen)
+ [self stateOpen:NO];
+
+ [self setNeedsLayout];
+ [self.updateCellDelegate updateCellWithForceReposition:NO];
+ return;
+ }
+
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+}
+
+- (void)configureWithText:(NSString *)text
+ updateCellDelegate:(id<MWMPlacePageCellUpdateProtocol>)updateCellDelegate
+ editBookmarkDelegate:(id<MWMPlacePageButtonsProtocol>)editBookmarkDelegate
+ isHTML:(BOOL)isHTML
+{
+ self.attributedHTML = nil;
+ self.isOpen = NO;
+ self.textViewHeight.active = NO;
+ self.textViewZeroHeight.active = NO;
+ self.updateCellDelegate = updateCellDelegate;
+ self.editBookmarkDelegate = editBookmarkDelegate;
+
+ if (!text.length)
+ {
+ [self configWithEmptyDescription];
+ }
+ else if (isHTML)
+ {
+ [self configHTML:text];
+ }
+ else
+ {
+ [self configPlain:text];
+ }
+}
+
+- (void)configWithEmptyDescription
+{
+ [self stopSpinner];
+
+ [self stateOpen:YES];
+ self.textViewZeroHeight.active = YES;
+}
+
+- (void)configHTML:(NSString *)text
+{
+ if (self.attributedHTML)
+ {
+ [self stopSpinner];
+ self.textViewZeroHeight.active = NO;
+ self.textView.attributedText = self.attributedHTML;
+ if (fabs(self.textView.contentSize.height - 0.5) < 1)
+ [self.textView sizeToFit];
+ }
+ else
+ {
+ self.textViewZeroHeight.active = YES;
+ [self startSpinner];
+ [self stateOpen:YES];
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ NSDictionary<NSString *, id> * attr = @{
+ NSForegroundColorAttributeName : [UIColor blackPrimaryText],
+ NSFontAttributeName : [UIFont regular16]
+ };
+ NSError * error = nil;
+ NSMutableAttributedString * str = [[NSMutableAttributedString alloc]
+ initWithData:[text dataUsingEncoding:NSUnicodeStringEncoding]
+ options:@{
+ NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType
+ }
+ documentAttributes:nil
+ error:&error];
+ if (error)
+ {
+ // If we failed while attempting to render html than just show plain text in bookmark.
+ // Ussualy there is a problem only in iOS7.
+ self.attributedHTML = [[NSAttributedString alloc] initWithString:text attributes:attr];
+ }
+ else
+ {
+ [str addAttributes:attr range:{0, str.length}];
+ self.attributedHTML = str;
+ }
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+
+ [self configHTML:nil];
+ });
+ });
+ }
+}
+
+- (void)configPlain:(NSString *)text
+{
+ [self stopSpinner];
+ self.textView.text = text;
+}
+
+- (void)stateOpen:(BOOL)isOpen
+{
+ self.moreButtonHeight.constant = isOpen ? 0 : 33;
+ self.textViewHeight.active = !isOpen;
+ self.gradientView.hidden = isOpen;
+}
+
+- (void)startSpinner
+{
+// self.textViewZeroHeight.active = YES;
+ self.editButton.hidden = YES;
+ NSUInteger const animationImagesCount = 12;
+ NSMutableArray * animationImages = [NSMutableArray arrayWithCapacity:animationImagesCount];
+ NSString * postfix = [UIColor isNightMode] ? @"dark" : @"light";
+ for (NSUInteger i = 0; i < animationImagesCount; ++i)
+ animationImages[i] =
+ [UIImage imageNamed:[NSString stringWithFormat:@"Spinner_%@_%@", @(i + 1), postfix]];
+
+ self.spinner.animationDuration = 0.8;
+ self.spinner.animationImages = animationImages;
+ self.spinner.hidden = NO;
+ [self.spinner startAnimating];
+}
+
+- (void)stopSpinner
+{
+ [self.spinner stopAnimating];
+ self.editButton.hidden = NO;
+ self.spinner.hidden = YES;
+}
+
+- (IBAction)moreTap
+{
+ self.isOpen = YES;
+ [self stateOpen:YES];
+ [self setNeedsLayout];
+// [self.delegate updateCellWithForceReposition:NO];
+}
+
+- (IBAction)editTap
+{
+ [self.editBookmarkDelegate editBookmark];
+}
+
+@end
diff --git a/iphone/Maps/Classes/MWMDirectionView.h b/iphone/Maps/Classes/MWMDirectionView.h
index c782d78538..e9f8d99ee9 100644
--- a/iphone/Maps/Classes/MWMDirectionView.h
+++ b/iphone/Maps/Classes/MWMDirectionView.h
@@ -13,6 +13,8 @@
- (instancetype)initWithManager:(MWMPlacePageViewManager *)manager;
- (void)setDirectionArrowTransform:(CGAffineTransform)transform;
+- (void)show;
+
- (instancetype)initWithCoder:(NSCoder *)aDecoder __attribute__((unavailable("initWithCoder is not available")));
- (instancetype)initWithFrame:(CGRect)frame __attribute__((unavailable("initWithFrame is not available")));
- (instancetype)init __attribute__((unavailable("init is not available")));
diff --git a/iphone/Maps/Classes/MWMDirectionView.mm b/iphone/Maps/Classes/MWMDirectionView.mm
index bbbc956bee..a5625a0796 100644
--- a/iphone/Maps/Classes/MWMDirectionView.mm
+++ b/iphone/Maps/Classes/MWMDirectionView.mm
@@ -1,3 +1,6 @@
+#import "MapsAppDelegate.h"
+#import "MapViewController.h"
+#import "MWMMapViewControlsManager.h"
#import "MWMDirectionView.h"
#import "MWMPlacePageViewManager.h"
#import "UIFont+MapsMeFonts.h"
@@ -34,6 +37,36 @@ static CGFloat const kDirectionArrowSide = IPAD ? 260. : 160.;
self.directionArrow.autoresizingMask = UIViewAutoresizingNone;
}
+- (void)show
+{
+ UIView * superview = [MapsAppDelegate theApp].mapViewController.view;
+ [superview addSubview:self];
+ [superview endEditing:YES];
+ [self setNeedsLayout];
+}
+
+- (void)didMoveToSuperview
+{
+ [super didMoveToSuperview];
+
+ MapsAppDelegate * app = [MapsAppDelegate theApp];
+
+ MWMMapViewControlsManager * manager = [MWMMapViewControlsManager manager];
+
+ if (self.superview)
+ {
+ [app disableStandby];
+ manager.isDirectionViewHidden = NO;
+ }
+ else
+ {
+ [app enableStandby];
+ manager.isDirectionViewHidden = YES;
+ }
+
+ [app.mapViewController updateStatusBarStyle];
+}
+
- (void)layoutSubviews
{
UIView * superview = self.superview;
@@ -122,7 +155,7 @@ static CGFloat const kDirectionArrowSide = IPAD ? 260. : 160.;
{
// Prevent super call to stop event propagation
// [super touchesBegan:touches withEvent:event];
- [self.manager hideDirectionView];
+ [self removeFromSuperview];
}
@end
diff --git a/iphone/Maps/Classes/MWMEditBookmarkController.h b/iphone/Maps/Classes/MWMEditBookmarkController.h
index 261387e7ef..3e6ae72c9d 100644
--- a/iphone/Maps/Classes/MWMEditBookmarkController.h
+++ b/iphone/Maps/Classes/MWMEditBookmarkController.h
@@ -1,9 +1,11 @@
#import "MWMTableViewController.h"
@class MWMPlacePageViewManager;
+@class MWMPlacePageData;
@interface MWMEditBookmarkController : MWMTableViewController
+@property (weak, nonatomic) MWMPlacePageData * data;
@property (nonatomic) MWMPlacePageViewManager * manager;
@end
diff --git a/iphone/Maps/Classes/MWMEditBookmarkController.mm b/iphone/Maps/Classes/MWMEditBookmarkController.mm
index 8ef1de441d..6374b6a2fc 100644
--- a/iphone/Maps/Classes/MWMEditBookmarkController.mm
+++ b/iphone/Maps/Classes/MWMEditBookmarkController.mm
@@ -1,8 +1,10 @@
+#import "Common.h"
#import "MWMBookmarkTitleCell.h"
#import "MWMButtonCell.h"
#import "MWMBookmarkColorViewController.h"
#import "MWMEditBookmarkController.h"
#import "MWMNoteCell.h"
+#import "MWMPlacePageData.h"
#import "MWMPlacePageEntity.h"
#import "MWMPlacePageViewManager.h"
#import "SelectSetVC.h"
@@ -50,13 +52,23 @@ enum RowInMetaInfo
- (void)viewDidLoad
{
[super viewDidLoad];
- NSAssert(self.manager, @"Entity can't be nil!");
- MWMPlacePageEntity * en = self.manager.entity;
- self.cachedDescription = en.bookmarkDescription;
- self.cachedTitle = en.bookmarkTitle;
- self.cachedCategory = en.bookmarkCategory;
- self.cachedColor = en.bookmarkColor;
- m_cachedBac = en.bac;
+ NSAssert(self.manager || self.data, @"Entity and data can't be nil both!");
+ if (isIOS7 || IPAD)
+ {
+ MWMPlacePageEntity * en = self.manager.entity;
+ self.cachedDescription = en.bookmarkDescription;
+ self.cachedTitle = en.bookmarkTitle;
+ self.cachedCategory = en.bookmarkCategory;
+ self.cachedColor = en.bookmarkColor;
+ m_cachedBac = en.bac;
+ }
+ else
+ {
+ self.cachedDescription = self.data.bookmarkDescription;
+ self.cachedTitle = self.data.externalTitle ? self.data.externalTitle : self.data.title;
+ self.cachedCategory = self.data.bookmarkCategory;
+ self.cachedColor = self.data.bookmarkColor;
+ }
[self configNavBar];
[self registerCells];
@@ -95,14 +107,40 @@ enum RowInMetaInfo
- (void)onSave
{
[self.view endEditing:YES];
- MWMPlacePageEntity * en = self.manager.entity;
- en.bookmarkDescription = self.cachedDescription;
- en.bookmarkColor = self.cachedColor;
- en.bookmarkCategory = self.cachedCategory;
- en.bookmarkTitle = self.cachedTitle;
- en.bac = m_cachedBac;
- [en synchronize];
- [self.manager reloadBookmark];
+ if (isIOS7 || IPAD)
+ {
+ MWMPlacePageEntity * en = self.manager.entity;
+ en.bookmarkDescription = self.cachedDescription;
+ en.bookmarkColor = self.cachedColor;
+ en.bookmarkCategory = self.cachedCategory;
+ en.bookmarkTitle = self.cachedTitle;
+ en.bac = m_cachedBac;
+ [en synchronize];
+ [self.manager reloadBookmark];
+ }
+ else
+ {
+ Framework & f = GetFramework();
+ auto const bac = self.data.bac;
+ BookmarkCategory * category = f.GetBmCategory(bac.m_categoryIndex);
+ if (!category)
+ return;
+
+ {
+ BookmarkCategory::Guard guard(*category);
+ Bookmark * bookmark =
+ static_cast<Bookmark *>(guard.m_controller.GetUserMarkForEdit(bac.m_bookmarkIndex));
+ if (!bookmark)
+ return;
+
+ bookmark->SetType(self.cachedColor.UTF8String);
+ bookmark->SetDescription(self.cachedDescription.UTF8String);
+ bookmark->SetName(self.cachedTitle.UTF8String);
+ }
+
+ category->SaveToKMLFile();
+ f.UpdatePlacePageInfoForCurrentSelection();
+ }
[self backTap];
}
diff --git a/iphone/Maps/Classes/MWMOpeningHours.h b/iphone/Maps/Classes/MWMOpeningHours.h
new file mode 100644
index 0000000000..70558501c5
--- /dev/null
+++ b/iphone/Maps/Classes/MWMOpeningHours.h
@@ -0,0 +1,32 @@
+namespace osmoh
+{
+struct Day
+{
+ Day(NSString * workingDays, NSString * workingTimes, NSString * breaks) : m_workingDays(workingDays),
+ m_workingTimes(workingTimes),
+ m_breaks(breaks) {}
+
+ explicit Day(NSString * workingDays) : m_workingDays(workingDays),
+ m_isOpen(false) {}
+
+ NSString * TodayTime() const
+ {
+ return m_workingTimes ? [NSString stringWithFormat:@"%@ %@", m_workingDays, m_workingTimes] : m_workingDays;
+ }
+
+ NSString * m_workingDays;
+ NSString * m_workingTimes;
+ NSString * m_breaks;
+ bool m_isOpen = true;
+};
+
+} // namespace osmoh
+
+#include "std/string.hpp"
+#include "std/vector.hpp"
+
+@interface MWMOpeningHours : NSObject
+
++ (vector<osmoh::Day>)processRawString:(NSString *)str;
+
+@end
diff --git a/iphone/Maps/Classes/MWMOpeningHours.mm b/iphone/Maps/Classes/MWMOpeningHours.mm
new file mode 100644
index 0000000000..62fe8b8e53
--- /dev/null
+++ b/iphone/Maps/Classes/MWMOpeningHours.mm
@@ -0,0 +1,128 @@
+#import "MWMOpeningHours.h"
+#import "MWMOpeningHoursCommon.h"
+
+
+#include "3party/opening_hours/opening_hours.hpp"
+#include "editor/opening_hours_ui.hpp"
+#include "editor/ui2oh.hpp"
+
+using namespace editor;
+using namespace osmoh;
+
+namespace
+{
+NSString * stringFromTimeSpan(Timespan const & timeSpan)
+{
+ return [NSString stringWithFormat:@"%@ - %@", stringFromTime(timeSpan.GetStart()),
+ stringFromTime(timeSpan.GetEnd())];
+}
+
+NSString * breaksFromClosedTime(TTimespans const & closedTimes)
+{
+ NSMutableString * breaks = [@"" mutableCopy];
+ for (auto & ct : closedTimes)
+ {
+ [breaks appendString:@"\n"];
+ [breaks appendString:stringFromTimeSpan(ct)];
+ }
+ return breaks.copy;
+}
+
+void addToday(ui::TimeTable const & tt, vector<Day> & allDays)
+{
+ NSString * workingDays;
+ NSString * workingTimes;
+ NSString * breaks;
+
+ if (tt.IsTwentyFourHours())
+ {
+ workingDays = L(@"twentyfour_seven");
+ workingTimes = @"";
+ breaks = @"";
+ }
+ else
+ {
+ BOOL const everyDay = (tt.GetOpeningDays().size() == 7);
+ workingDays = everyDay ? L(@"daily") : L(@"today");
+ workingTimes = stringFromTimeSpan(tt.GetOpeningTime());
+ breaks = breaksFromClosedTime(tt.GetExcludeTime());
+ }
+
+ allDays.emplace(allDays.begin(), workingDays, workingTimes, breaks);
+}
+
+void addClosedToday(vector<Day> & allDays)
+{
+ allDays.emplace(allDays.begin(), L(@"day_off_today"));
+}
+
+void addDay(ui::TimeTable const & tt, vector<Day> & allDays)
+{
+ NSString * workingDays = stringFromOpeningDays(tt.GetOpeningDays());
+ NSString * workingTimes;
+ NSString * breaks;
+ if (tt.IsTwentyFourHours())
+ {
+ workingTimes = L(@"twentyfour_seven");
+ }
+ else
+ {
+ workingTimes = stringFromTimeSpan(tt.GetOpeningTime());
+ breaks = breaksFromClosedTime(tt.GetExcludeTime());
+ }
+ allDays.emplace_back(workingDays, workingTimes, breaks);
+}
+
+void addUnhandledDays(ui::TOpeningDays const & days, vector<Day> & allDays)
+{
+ if (days.empty())
+ return;
+
+ allDays.emplace_back(stringFromOpeningDays(days));
+}
+
+} // namespace
+
+@implementation MWMOpeningHours
+
++ (vector<Day>)processRawString:(NSString *)str
+{
+ ui::TimeTableSet timeTableSet;
+ osmoh::OpeningHours oh(str.UTF8String);
+ if (!MakeTimeTableSet(oh, timeTableSet))
+ return {};
+
+ vector<Day> days;
+
+ NSCalendar * cal = [NSCalendar currentCalendar];
+ cal.locale = [NSLocale currentLocale];
+
+ auto const timeTablesSize = timeTableSet.Size();
+ auto const today = static_cast<Weekday>([cal components:NSCalendarUnitWeekday fromDate:[NSDate date]].weekday);
+ auto const unhandledDays = timeTableSet.GetUnhandledDays();
+
+ /// Schedule contains more than one rule for all days or unhandled days.
+ BOOL const isExtendedSchedule = timeTablesSize != 1 || !unhandledDays.empty();
+ BOOL hasCurrentDay = NO;
+
+ for (auto const & tt : timeTableSet)
+ {
+ ui::TOpeningDays const & workingDays = tt.GetOpeningDays();
+ if (workingDays.find(today) != workingDays.end())
+ {
+ hasCurrentDay = YES;
+ addToday(tt, days);
+ }
+
+ if (isExtendedSchedule)
+ addDay(tt, days);
+ }
+
+ if (!hasCurrentDay)
+ addClosedToday(days);
+
+ addUnhandledDays(unhandledDays, days);
+ return days;
+}
+
+@end
diff --git a/iphone/Maps/Classes/MWMOpeningHoursCell.h b/iphone/Maps/Classes/MWMOpeningHoursCell.h
new file mode 100644
index 0000000000..211cf927a9
--- /dev/null
+++ b/iphone/Maps/Classes/MWMOpeningHoursCell.h
@@ -0,0 +1,10 @@
+#import "MWMTableViewCell.h"
+
+@protocol MWMPlacePageCellUpdateProtocol;
+
+@interface MWMOpeningHoursCell : MWMTableViewCell
+
+- (void)configureWithOpeningHours:(NSString *)openningHours
+ updateLayoutDelegate:(id<MWMPlacePageCellUpdateProtocol>)delegate
+ isClosedNow:(BOOL)isClosedNow;
+@end
diff --git a/iphone/Maps/Classes/MWMOpeningHoursCell.mm b/iphone/Maps/Classes/MWMOpeningHoursCell.mm
new file mode 100644
index 0000000000..850eaac266
--- /dev/null
+++ b/iphone/Maps/Classes/MWMOpeningHoursCell.mm
@@ -0,0 +1,200 @@
+#import "Common.h"
+#import "MWMOpeningHours.h"
+#import "MWMOpeningHoursCell.h"
+#import "MWMPlacePageCellUpdateProtocol.h"
+
+#include "std/array.hpp"
+
+namespace
+{
+array<NSString *, 2> kOHClasses = {{@"_MWMOHHeaderCell", @"_MWMOHSubCell"}};
+void * kContext = &kContext;
+NSString * const kTableViewContentSizeKeyPath = @"contentSize";
+
+} // namespace
+
+#pragma mark - _MWMOHHeaderCell
+
+@interface _MWMOHHeaderCell : MWMTableViewCell
+
+@property(weak, nonatomic) IBOutlet UILabel * today;
+@property(weak, nonatomic) IBOutlet UILabel * breaks;
+@property(weak, nonatomic) IBOutlet UILabel * closedNow;
+@property(weak, nonatomic) IBOutlet UIImageView * arrowIcon;
+@property(weak, nonatomic) IBOutlet UIImageView * separator;
+
+@property(copy, nonatomic) TMWMVoidBlock tapAction;
+
+@end
+
+@implementation _MWMOHHeaderCell
+
+- (IBAction)extendTap
+{
+ if (!self.tapAction)
+ return;
+
+ self.tapAction();
+ [UIView animateWithDuration:kDefaultAnimationDuration animations:^
+ {
+ self.arrowIcon.transform = CGAffineTransformIsIdentity(self.arrowIcon.transform) ?
+ CGAffineTransformMakeRotation(M_PI) :
+ CGAffineTransformIdentity;
+ }];
+}
+
+@end
+
+#pragma mark - _MWMOHSubCell
+
+@interface _MWMOHSubCell :MWMTableViewCell
+
+@property(weak, nonatomic) IBOutlet UILabel * days;
+@property(weak, nonatomic) IBOutlet UILabel * schedule;
+@property(weak, nonatomic) IBOutlet UILabel * breaks;
+@property(weak, nonatomic) IBOutlet UIImageView * separator;
+
+@end
+
+@implementation _MWMOHSubCell
+
+@end
+
+@interface MWMOpeningHoursCell () <UITableViewDelegate, UITableViewDataSource>
+
+@property(weak, nonatomic) IBOutlet UITableView * tableView;
+@property(weak, nonatomic) IBOutlet NSLayoutConstraint * tableViewHeight;
+
+@property(weak, nonatomic) id<MWMPlacePageCellUpdateProtocol> delegate;
+
+@property(nonatomic) BOOL isExtended;
+@property(nonatomic) BOOL isClosedNow;
+
+@end
+
+@implementation MWMOpeningHoursCell
+{
+ vector<osmoh::Day> m_days;
+}
+
+- (void)awakeFromNib
+{
+ [super awakeFromNib];
+ for (auto s : kOHClasses)
+ [self.tableView registerNib:[UINib nibWithNibName:s bundle:nil] forCellReuseIdentifier:s];
+
+ self.tableView.estimatedRowHeight = 48;
+ self.tableView.rowHeight = UITableViewAutomaticDimension;
+ [self registerObserver];
+}
+
+- (void)configureWithOpeningHours:(NSString *)openningHours
+ updateLayoutDelegate:(id<MWMPlacePageCellUpdateProtocol>)delegate
+ isClosedNow:(BOOL)isClosedNow;
+{
+ self.tableView.delegate = nil;
+ self.tableView.dataSource = nil;
+ self.delegate = delegate;
+ self.isClosedNow = isClosedNow;
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^
+ {
+ self->m_days = [MWMOpeningHours processRawString:openningHours];
+ dispatch_async(dispatch_get_main_queue(), ^
+ {
+ self.tableView.delegate = self;
+ self.tableView.dataSource = self;
+ [self.tableView reloadData];
+ });
+ });
+}
+
+#pragma mark - UITableView
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
+{
+ return self.isExtended ? m_days.size() : 1;
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ auto const & day = m_days[indexPath.row];
+ BOOL const isSeparatorHidden = self.isExtended ? indexPath.row == 0 : indexPath.row == m_days.size() - 1;
+ if (indexPath.row == 0)
+ {
+ _MWMOHHeaderCell * cell = [tableView dequeueReusableCellWithIdentifier:[_MWMOHHeaderCell className]];
+ cell.today.text = day.TodayTime();
+ cell.breaks.text = day.m_breaks;
+ cell.closedNow.text = self.isClosedNow ? L(@"closed_now") : nil;;
+ if (m_days.size() > 1)
+ {
+ cell.tapAction = ^
+ {
+ self.isExtended = !self.isExtended;
+
+ NSMutableArray<NSIndexPath *> * ip = [@[] mutableCopy];
+
+ for (auto i = 1; i < self->m_days.size(); i++)
+ [ip addObject:[NSIndexPath indexPathForRow:i inSection:0]];
+
+ if (self.isExtended)
+ [self.tableView insertRowsAtIndexPaths:ip withRowAnimation:UITableViewRowAnimationAutomatic];
+ else
+ [self.tableView deleteRowsAtIndexPaths:ip withRowAnimation:UITableViewRowAnimationAutomatic];
+ };
+ cell.arrowIcon.hidden = NO;
+ }
+ else
+ {
+ cell.tapAction = nil;
+ cell.arrowIcon.hidden = YES;
+ }
+ cell.separator.hidden = isSeparatorHidden;
+ return cell;
+ }
+ else
+ {
+ _MWMOHSubCell * cell = [tableView dequeueReusableCellWithIdentifier:[_MWMOHSubCell className]];
+ cell.days.text = day.m_workingDays;
+ cell.schedule.text = day.m_workingTimes ? day.m_workingTimes : L(@"closed");
+ cell.breaks.text = day.m_breaks;
+ cell.separator.hidden = isSeparatorHidden;
+ return cell;
+ }
+}
+
+#pragma mark - Observer's methods
+
+- (void)dealloc
+{
+ [self unregisterObserver];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath
+ ofObject:(id)object
+ change:(NSDictionary *)change
+ context:(void *)context
+{
+ if (context == kContext)
+ {
+ NSValue * s = change[@"new"];
+ CGFloat const height = s.CGSizeValue.height;
+ self.tableViewHeight.constant = height;
+ [self setNeedsLayout];
+ [self.delegate updateCellWithForceReposition:NO];
+ return;
+ }
+
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+}
+
+- (void)unregisterObserver
+{
+ [self.tableView removeObserver:self forKeyPath:kTableViewContentSizeKeyPath context:kContext];
+}
+
+- (void)registerObserver
+{
+ [self.tableView addObserver:self forKeyPath:kTableViewContentSizeKeyPath options:NSKeyValueObservingOptionNew context:kContext];
+}
+@end
diff --git a/iphone/Maps/Classes/MWMPPView.h b/iphone/Maps/Classes/MWMPPView.h
new file mode 100644
index 0000000000..60049b831b
--- /dev/null
+++ b/iphone/Maps/Classes/MWMPPView.h
@@ -0,0 +1,31 @@
+#pragma mark - MWMPPScrollView
+
+@protocol MWMPlacePageViewUpdateProtocol <NSObject>
+
+- (void)updateWithHeight:(CGFloat)height;
+
+@end
+
+@interface MWMPPScrollView : UIScrollView
+
+- (instancetype)initWithFrame:(CGRect)frame inactiveView:(UIView *)inactiveView;
+
+@property(weak, nonatomic) UIView * inactiveView;
+
+@end
+
+#pragma mark - MWMPPView
+
+@interface MWMPPView : UIView
+
+@property(weak, nonatomic) IBOutlet UIImageView * top;
+@property(weak, nonatomic) IBOutlet UIImageView * spinner;
+@property(weak, nonatomic) IBOutlet UITableView * tableView;
+
+@property(nonatomic) CGFloat currentContentHeight;
+@property(nonatomic) id<MWMPlacePageViewUpdateProtocol> delegate;
+
+- (void)hideTableView:(BOOL)isHidden;
+
+@end
+
diff --git a/iphone/Maps/Classes/MWMPPView.mm b/iphone/Maps/Classes/MWMPPView.mm
new file mode 100644
index 0000000000..d6c80dd84a
--- /dev/null
+++ b/iphone/Maps/Classes/MWMPPView.mm
@@ -0,0 +1,101 @@
+#import "MWMPPView.h"
+#import "UIColor+MapsMeColor.h"
+
+namespace
+{
+void * kContext = &kContext;
+NSString * const kTableViewContentSizeKeyPath = @"contentSize";
+
+} // namespace
+
+#pragma mark - MWMPPScrollView
+
+@implementation MWMPPScrollView
+
+- (instancetype)initWithFrame:(CGRect)frame inactiveView:(UIView *)inactiveView
+{
+ self = [super initWithFrame:frame];
+ if (self)
+ _inactiveView = inactiveView;
+ return self;
+}
+
+- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
+{
+ if (point.y > [self convertRect:self.inactiveView.bounds fromView:self.inactiveView].origin.y)
+ return YES;
+ return NO;
+}
+
+@end
+
+#pragma mark - MWMPPView
+
+@implementation MWMPPView
+
+- (void)hideTableView:(BOOL)isHidden
+{
+ if (isHidden)
+ {
+ self.tableView.alpha = 0.;
+ self.spinner.hidden = NO;
+ [self.spinner startAnimating];
+ }
+ else
+ {
+ self.tableView.alpha = 1.;
+ self.spinner.hidden = YES;
+ [self.spinner stopAnimating];
+ }
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath
+ ofObject:(id)object
+ change:(NSDictionary *)change
+ context:(void *)context
+{
+ if (context == kContext)
+ {
+ NSValue * s = change[@"new"];
+ CGFloat const height = s.CGSizeValue.height;
+ if (fabs(height - self.currentContentHeight) > 0.5)
+ {
+ self.currentContentHeight = height;
+ self.height = height + self.top.height;
+ [self setNeedsLayout];
+ [self.delegate updateWithHeight:self.height];
+ }
+ return;
+ }
+
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+}
+
+- (void)awakeFromNib
+{
+ [super awakeFromNib];
+ [self.tableView addObserver:self forKeyPath:kTableViewContentSizeKeyPath options:NSKeyValueObservingOptionNew context:kContext];
+
+ self.tableView.estimatedRowHeight = 44.;
+ self.tableView.rowHeight = UITableViewAutomaticDimension;
+
+ self.tableView.contentInset = {.top = -36};
+
+ NSUInteger const animationImagesCount = 12;
+ NSMutableArray * animationImages = [NSMutableArray arrayWithCapacity:animationImagesCount];
+ NSString * postfix = [UIColor isNightMode] ? @"dark" : @"light";
+ for (NSUInteger i = 0; i < animationImagesCount; ++i)
+ animationImages[i] = [UIImage imageNamed:[NSString stringWithFormat:@"Spinner_%@_%@", @(i+1), postfix]];
+
+ self.spinner.animationDuration = 0.8;
+ self.spinner.animationImages = animationImages;
+ [self.spinner startAnimating];
+}
+
+- (void)dealloc
+{
+ [self.tableView removeObserver:self forKeyPath:kTableViewContentSizeKeyPath context:kContext];
+}
+
+@end
+
diff --git a/iphone/Maps/Classes/MWMPlacePage.mm b/iphone/Maps/Classes/MWMPlacePage.mm
index baa1304972..9d411d5e9e 100644
--- a/iphone/Maps/Classes/MWMPlacePage.mm
+++ b/iphone/Maps/Classes/MWMPlacePage.mm
@@ -10,7 +10,7 @@
static NSString * const kPlacePageNibIdentifier = @"PlacePageView";
static NSString * const kPlacePageViewCenterKeyPath = @"center";
-extern NSString * const kPP2BookmarkEditingSegue = @"PP2BookmarkEditing";
+static NSString * const kPP2BookmarkEditingSegue = @"PP2BookmarkEditing";
extern NSString * const kPP2BookmarkEditingIPADSegue = @"PP2BookmarkEditingIPAD";
@interface MWMPlacePage ()
diff --git a/iphone/Maps/Classes/MWMPlacePageActionBar.h b/iphone/Maps/Classes/MWMPlacePageActionBar.h
index 0e09565350..1ccd7465c5 100644
--- a/iphone/Maps/Classes/MWMPlacePageActionBar.h
+++ b/iphone/Maps/Classes/MWMPlacePageActionBar.h
@@ -1,7 +1,25 @@
@class MWMPlacePageViewManager;
+@class MWMPlacePageData;
+
+@protocol MWMActionBarSharedData <NSObject>
+
+- (BOOL)isBookmark;
+- (BOOL)isBooking;
+- (BOOL)isApi;
+- (BOOL)isMyPosition;
+- (NSString *)title;
+- (NSString *)subtitle;
+- (NSString *)phoneNumber;
+
+@end
+
+@protocol MWMActionBarProtocol;
@interface MWMPlacePageActionBar : SolidTouchView
++ (MWMPlacePageActionBar *)actionBarWithDelegate:(id<MWMActionBarProtocol>)delegate;
+- (void)configureWithData:(id<MWMActionBarSharedData>)data;
+
@property (nonatomic) BOOL isBookmark;
- (UIView *)shareAnchor;
diff --git a/iphone/Maps/Classes/MWMPlacePageActionBar.mm b/iphone/Maps/Classes/MWMPlacePageActionBar.mm
index 5a85e0a7c3..2701045a79 100644
--- a/iphone/Maps/Classes/MWMPlacePageActionBar.mm
+++ b/iphone/Maps/Classes/MWMPlacePageActionBar.mm
@@ -3,6 +3,7 @@
#import "MWMActionBarButton.h"
#import "MWMBasePlacePageView.h"
#import "MWMPlacePageEntity.h"
+#import "MWMPlacePageProtocol.h"
#import "MWMPlacePageViewManager.h"
#import "MapViewController.h"
#import "MapsAppDelegate.h"
@@ -13,12 +14,6 @@
extern NSString * const kAlohalyticsTapEventKey;
-namespace
-{
-NSString * const kPlacePageActionBarNibName = @"PlacePageActionBar";
-
-} // namespace
-
@interface MWMPlacePageActionBar ()<MWMActionBarButtonDelegate>
{
vector<EButton> m_visibleButtons;
@@ -30,14 +25,34 @@ NSString * const kPlacePageActionBarNibName = @"PlacePageActionBar";
@property(weak, nonatomic) IBOutlet UIImageView * separator;
@property(nonatomic) BOOL isPrepareRouteMode;
+@property(weak, nonatomic) id<MWMActionBarSharedData> data;
+@property(weak, nonatomic) id<MWMActionBarProtocol> delegate;
+
@end
@implementation MWMPlacePageActionBar
++ (MWMPlacePageActionBar *)actionBarWithDelegate:(id<MWMActionBarProtocol>)delegate
+{
+ MWMPlacePageActionBar * bar = [[NSBundle.mainBundle loadNibNamed:[self className] owner:nil options:nil]
+ firstObject];
+ bar.delegate = delegate;
+ return bar;
+}
+
+- (void)configureWithData:(id<MWMActionBarSharedData>)data
+{
+ self.data = data;
+ self.isPrepareRouteMode = MapsAppDelegate.theApp.routingPlaneMode != MWMRoutingPlaneModeNone;
+ self.isBookmark = data.isBookmark;
+ [self configureButtons];
+ self.autoresizingMask = UIViewAutoresizingNone;
+}
+
+ (MWMPlacePageActionBar *)actionBarForPlacePageManager:(MWMPlacePageViewManager *)placePageManager
{
MWMPlacePageActionBar * bar =
- [[NSBundle.mainBundle loadNibNamed:kPlacePageActionBarNibName owner:nil options:nil]
+ [[NSBundle.mainBundle loadNibNamed:[self className] owner:nil options:nil]
firstObject];
[bar configureWithPlacePageManager:placePageManager];
return bar;
@@ -56,16 +71,16 @@ NSString * const kPlacePageActionBarNibName = @"PlacePageActionBar";
{
m_visibleButtons.clear();
m_additionalButtons.clear();
- MWMPlacePageEntity * entity = self.placePageManager.entity;
- NSString * phone = [entity getCellValue:MWMPlacePageCellTypePhoneNumber];
+ auto data = static_cast<id<MWMActionBarSharedData>>(isIOS7 ? self.placePageManager.entity : self.data);
+ NSString * phone = data.phoneNumber;
BOOL const isIphone = [[UIDevice currentDevice].model isEqualToString:@"iPhone"];
BOOL const isPhoneNotEmpty = phone.length > 0;
- BOOL const isBooking = entity.isBooking;
+ BOOL const isBooking = data.isBooking;
BOOL const itHasPhoneNumber = isIphone && isPhoneNotEmpty;
- BOOL const isApi = entity.isApi;
+ BOOL const isApi = data.isApi;
BOOL const isP2P = self.isPrepareRouteMode;
- BOOL const isMyPosition = entity.isMyPosition;
+ BOOL const isMyPosition = data.isMyPosition;
if (isMyPosition)
{
@@ -171,21 +186,24 @@ NSString * const kPlacePageActionBarNibName = @"PlacePageActionBar";
- (void)tapOnButtonWithType:(EButton)type
{
- MWMPlacePageViewManager * placePageManager = self.placePageManager;
+ id<MWMActionBarProtocol> delegate = IPAD ? self.placePageManager : self.delegate;
+
switch (type)
{
- case EButton::Api: [placePageManager apiBack]; break;
- case EButton::Booking: [placePageManager book:NO]; break;
- case EButton::Call: [placePageManager call]; break;
+ case EButton::Api: [delegate apiBack]; break;
+ case EButton::Booking: [delegate book:NO]; break;
+ case EButton::Call: [delegate call]; break;
case EButton::Bookmark:
if (self.isBookmark)
- [placePageManager removeBookmark];
+ [delegate removeBookmark];
else
- [placePageManager addBookmark];
+ [delegate addBookmark];
+
+ self.isBookmark = !self.isBookmark;
break;
- case EButton::RouteFrom: [placePageManager routeFrom]; break;
- case EButton::RouteTo: [placePageManager routeTo]; break;
- case EButton::Share: [placePageManager share]; break;
+ case EButton::RouteFrom: [delegate routeFrom]; break;
+ case EButton::RouteTo: [delegate routeTo]; break;
+ case EButton::Share: [delegate share]; break;
case EButton::More: [self showActionSheet]; break;
case EButton::Spacer: break;
}
@@ -196,16 +214,16 @@ NSString * const kPlacePageActionBarNibName = @"PlacePageActionBar";
- (void)showActionSheet
{
NSString * cancel = L(@"cancel");
- MWMPlacePageEntity * entity = self.placePageManager.entity;
- BOOL const isTitleNotEmpty = entity.title.length > 0;
- NSString * title = isTitleNotEmpty ? entity.title : entity.subtitle;
- NSString * subtitle = isTitleNotEmpty ? entity.subtitle : nil;
+ auto data = static_cast<id<MWMActionBarSharedData>>(IPAD ? self.placePageManager.entity : self.data);
+ BOOL const isTitleNotEmpty = data.title.length > 0;
+ NSString * title = isTitleNotEmpty ? data.title : data.subtitle;
+ NSString * subtitle = isTitleNotEmpty ? data.subtitle : nil;
UIViewController * vc = static_cast<UIViewController *>([MapViewController controller]);
NSMutableArray<NSString *> * titles = [@[] mutableCopy];
for (auto const buttonType : m_additionalButtons)
{
- BOOL const isSelected = buttonType == EButton::Bookmark ? self.isBookmark : NO;
+ BOOL const isSelected = buttonType == EButton::Bookmark ? data.isBookmark : NO;
if (NSString * title = titleForButton(buttonType, isSelected))
[titles addObject:title];
else
@@ -246,6 +264,10 @@ NSString * const kPlacePageActionBarNibName = @"PlacePageActionBar";
- (void)layoutSubviews
{
[super layoutSubviews];
+ self.width = self.superview.width;
+ if (IPAD)
+ self.origin = {0, self.superview.height - 48};
+
self.separator.width = self.width;
CGFloat const buttonWidth = self.width / self.buttons.count;
for (UIView * button in self.buttons)
@@ -255,6 +277,11 @@ NSString * const kPlacePageActionBarNibName = @"PlacePageActionBar";
}
}
+- (void)setFrame:(CGRect)frame
+{
+ [super setFrame:frame];
+}
+
#pragma mark - Properties
- (void)setIsBookmark:(BOOL)isBookmark
diff --git a/iphone/Maps/Classes/PlacePageActionBar.xib b/iphone/Maps/Classes/MWMPlacePageActionBar.xib
index 0d4a6fe67d..077efe79ec 100644
--- a/iphone/Maps/Classes/PlacePageActionBar.xib
+++ b/iphone/Maps/Classes/MWMPlacePageActionBar.xib
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10117" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
diff --git a/iphone/Maps/Classes/MWMPlacePageBookmarkCell.h b/iphone/Maps/Classes/MWMPlacePageBookmarkCell.h
index 3424bf4856..ac32f2671f 100644
--- a/iphone/Maps/Classes/MWMPlacePageBookmarkCell.h
+++ b/iphone/Maps/Classes/MWMPlacePageBookmarkCell.h
@@ -1,13 +1,6 @@
#import "MWMTableViewCell.h"
-@protocol MWMPlacePageBookmarkDelegate <NSObject>
-
-- (void)reloadBookmark;
-- (void)editBookmarkTap;
-- (void)moreTap;
-
-@end
-
+@protocol MWMPlacePageBookmarkDelegate;
@class MWMPlacePage;
@interface MWMPlacePageBookmarkCell : MWMTableViewCell
diff --git a/iphone/Maps/Classes/MWMPlacePageBookmarkCell.mm b/iphone/Maps/Classes/MWMPlacePageBookmarkCell.mm
index 842528fbee..024d8c303c 100644
--- a/iphone/Maps/Classes/MWMPlacePageBookmarkCell.mm
+++ b/iphone/Maps/Classes/MWMPlacePageBookmarkCell.mm
@@ -1,4 +1,5 @@
#import "MWMPlacePageBookmarkCell.h"
+#import "MWMPlacePageBookmarkDelegate.h"
#import "Common.h"
#import "MapViewController.h"
#import "Statistics.h"
diff --git a/iphone/Maps/Classes/MWMPlacePageBookmarkDelegate.h b/iphone/Maps/Classes/MWMPlacePageBookmarkDelegate.h
new file mode 100644
index 0000000000..e0e7202e92
--- /dev/null
+++ b/iphone/Maps/Classes/MWMPlacePageBookmarkDelegate.h
@@ -0,0 +1,8 @@
+@protocol MWMPlacePageBookmarkDelegate <NSObject>
+
+@required
+- (void)editBookmarkTap;
+- (void)reloadBookmark;
+- (void)moreTap;
+
+@end
diff --git a/iphone/Maps/Classes/MWMPlacePageButtonCell.h b/iphone/Maps/Classes/MWMPlacePageButtonCell.h
index e8007704d8..3c6c282b80 100644
--- a/iphone/Maps/Classes/MWMPlacePageButtonCell.h
+++ b/iphone/Maps/Classes/MWMPlacePageButtonCell.h
@@ -1,10 +1,19 @@
#import "MWMTableViewCell.h"
#import "MWMPlacePageEntity.h"
+#import "MWMPlacePageData.h"
@class MWMPlacePageViewManager;
+@protocol MWMPlacePageButtonsProtocol;
+
@interface MWMPlacePageButtonCell : MWMTableViewCell
-- (void)config:(MWMPlacePageViewManager *)manager forType:(MWMPlacePageCellType)type;
+- (void)config:(MWMPlacePageViewManager *)manager forType:(MWMPlacePageCellType)type NS_DEPRECATED_IOS(7_0, 8_0);
+
+- (void)configForRow:(place_page::ButtonsRows)row
+ withDelegate:(id<MWMPlacePageButtonsProtocol>)delegate NS_AVAILABLE_IOS(8_0);
+
+- (void)setEnabled:(BOOL)enabled NS_AVAILABLE_IOS(8_0);
+- (BOOL)isEnabled NS_AVAILABLE_IOS(8_0);
@end
diff --git a/iphone/Maps/Classes/MWMPlacePageButtonCell.mm b/iphone/Maps/Classes/MWMPlacePageButtonCell.mm
index 5b0f81416a..203bc3bfbd 100644
--- a/iphone/Maps/Classes/MWMPlacePageButtonCell.mm
+++ b/iphone/Maps/Classes/MWMPlacePageButtonCell.mm
@@ -1,7 +1,9 @@
+#import "Common.h"
#import "MWMPlacePageButtonCell.h"
#import "MWMFrameworkListener.h"
#import "MWMPlacePageViewManager.h"
#import "UIColor+MapsMeColor.h"
+#import "MWMPlacePageProtocol.h"
@interface MWMPlacePageButtonCell ()<MWMFrameworkStorageObserver>
@@ -10,6 +12,8 @@
@property(nonatomic) MWMPlacePageCellType type;
@property(nonatomic) storage::TCountryId countryId;
+@property(weak, nonatomic) id<MWMPlacePageButtonsProtocol> delegate;
+@property(nonatomic) place_page::ButtonsRows rowType;
@end
@implementation MWMPlacePageButtonCell
@@ -22,16 +26,59 @@
[self refreshButtonEnabledState];
}
+- (void)setEnabled:(BOOL)enabled
+{
+ self.titleButton.enabled = enabled;
+}
+
+- (BOOL)isEnabled
+{
+ return self.titleButton.isEnabled;
+}
+
+- (void)configForRow:(place_page::ButtonsRows)row withDelegate:(id<MWMPlacePageButtonsProtocol>)delegate
+{
+ self.delegate = delegate;
+ self.rowType = row;
+ switch(row)
+ {
+ case place_page::ButtonsRows::AddPlace:
+ [self.titleButton setTitle:L(@"placepage_add_place_button") forState:UIControlStateNormal];
+ break;
+ case place_page::ButtonsRows::EditPlace:
+ [self.titleButton setTitle:L(@"edit_place") forState:UIControlStateNormal];
+ break;
+ case place_page::ButtonsRows::AddBusiness:
+ [self.titleButton setTitle:L(@"placepage_add_business_button") forState:UIControlStateNormal];
+ break;
+ case place_page::ButtonsRows::HotelDescription:
+ [self.titleButton setTitle:L(@"details") forState:UIControlStateNormal];
+ break;
+ }
+}
+
- (IBAction)buttonTap
{
- MWMPlacePageViewManager * manager = self.manager;
- switch (self.type)
+ if (IPAD)
+ {
+ switch (self.type)
+ {
+ case MWMPlacePageCellTypeEditButton: [self.manager editPlace]; break;
+ case MWMPlacePageCellTypeAddBusinessButton: [self.manager addBusiness]; break;
+ case MWMPlacePageCellTypeAddPlaceButton: [self.manager addPlace]; break;
+ case MWMPlacePageCellTypeBookingMore: [self.manager book:YES]; break;
+ default: NSAssert(false, @"Incorrect cell type!"); break;
+ }
+ return;
+ }
+
+ using namespace place_page;
+ switch (self.rowType)
{
- case MWMPlacePageCellTypeEditButton: [manager editPlace]; break;
- case MWMPlacePageCellTypeAddBusinessButton: [manager addBusiness]; break;
- case MWMPlacePageCellTypeAddPlaceButton: [manager addPlace]; break;
- case MWMPlacePageCellTypeBookingMore: [manager book:YES]; break;
- default: NSAssert(false, @"Incorrect cell type!"); break;
+ case ButtonsRows::AddPlace: [self.delegate addPlace]; break;
+ case ButtonsRows::EditPlace: [self.delegate editPlace]; break;
+ case ButtonsRows::AddBusiness: [self.delegate addBusiness]; break;
+ case ButtonsRows::HotelDescription: [self.delegate book:YES]; break;
}
}
diff --git a/iphone/Maps/Classes/MWMPlacePageCellUpdateProtocol.h b/iphone/Maps/Classes/MWMPlacePageCellUpdateProtocol.h
new file mode 100644
index 0000000000..3b4bcfe7c1
--- /dev/null
+++ b/iphone/Maps/Classes/MWMPlacePageCellUpdateProtocol.h
@@ -0,0 +1,5 @@
+@protocol MWMPlacePageCellUpdateProtocol <NSObject>
+
+- (void)updateCellWithForceReposition:(BOOL)isForceReposition;
+
+@end
diff --git a/iphone/Maps/Classes/MWMPlacePageData.h b/iphone/Maps/Classes/MWMPlacePageData.h
new file mode 100644
index 0000000000..4496f1df43
--- /dev/null
+++ b/iphone/Maps/Classes/MWMPlacePageData.h
@@ -0,0 +1,103 @@
+#include "map/place_page_info.hpp"
+
+#include "std/vector.hpp"
+
+namespace place_page
+{
+enum class Sections
+{
+ Preview,
+ Bookmark,
+ Metainfo,
+ Buttons
+};
+
+enum class MetainfoRows
+{
+ OpeningHours,
+ Phone,
+ Address,
+ Website,
+ Email,
+ Cuisine,
+ Operator,
+ Internet,
+ Coordinate
+};
+
+enum class ButtonsRows
+{
+ AddBusiness,
+ EditPlace,
+ AddPlace,
+ HotelDescription
+};
+
+enum class OpeningHours
+{
+ AllDay,
+ Open,
+ Closed,
+ Unknown
+};
+
+} // namespace place_page_data
+
+/// ViewModel for place page.
+@interface MWMPlacePageData : NSObject
+
+// ready callback will be called from main queue.
+- (instancetype)initWithPlacePageInfo:(place_page::Info const &)info;
+
+- (void)updateBookmarkStatus:(BOOL)isBookmark;
+
+/// Country id for changing place page's fields which depend on application state.
+- (storage::TCountryId const &)countryId;
+- (FeatureID const &)featureId;
+
+// Regular
+- (NSString *)title;
+- (NSString *)subtitle;
+- (place_page::OpeningHours)schedule;
+- (NSString *)address;
+
+// Booking
+- (NSString *)bookingRating;
+- (NSString *)bookingApproximatePricing;
+- (NSURL *)bookingURL;
+- (NSURL *)bookingDescriptionURL;
+- (NSString *)hotelId;
+- (void)assignOnlinePriceToLabel:(UILabel *)label;
+
+// API
+- (NSString *)apiURL;
+
+// Bookmark
+- (NSString *)externalTitle;
+- (NSString *)bookmarkColor;
+- (NSString *)bookmarkDescription;
+- (NSString *)bookmarkCategory;
+- (BookmarkAndCategory)bac;
+
+// Table view's data
+- (vector<place_page::Sections> const &)sections;
+- (vector<place_page::MetainfoRows> const &)metainfoRows;
+- (vector<place_page::ButtonsRows> const &)buttonsRows;
+
+// Table view metainfo rows
+- (NSString *)stringForRow:(place_page::MetainfoRows)row;
+
+// Helpers
+- (NSString *)phoneNumber;
+- (BOOL)isBookmark;
+- (BOOL)isApi;
+- (BOOL)isBooking;
+- (BOOL)isHTMLDescription;
+- (BOOL)isMyPosition;
+
+// Coordinates
+- (m2::PointD const &)mercator;
+- (ms::LatLon)latLon;
++ (void)toggleCoordinateSystem;
+
+@end
diff --git a/iphone/Maps/Classes/MWMPlacePageData.mm b/iphone/Maps/Classes/MWMPlacePageData.mm
new file mode 100644
index 0000000000..15c934909b
--- /dev/null
+++ b/iphone/Maps/Classes/MWMPlacePageData.mm
@@ -0,0 +1,406 @@
+#import "MWMPlacePageData.h"
+
+#include "Framework.h"
+
+#include "base/string_utils.hpp"
+
+#include "3party/opening_hours/opening_hours.hpp"
+
+namespace
+{
+NSString * const kUserDefaultsLatLonAsDMSKey = @"UserDefaultsLatLonAsDMS";
+
+} // namespace
+
+using namespace place_page;
+
+@implementation MWMPlacePageData
+{
+ Info m_info;
+
+ vector<Sections> m_sections;
+ vector<MetainfoRows> m_metainfoRows;
+ vector<ButtonsRows> m_buttonsRows;
+}
+
+- (instancetype)initWithPlacePageInfo:(Info const &)info
+{
+ self = [super init];
+ if (self)
+ {
+ m_info = info;
+ [self fillSections];
+ }
+
+ return self;
+}
+
+- (void)fillSections
+{
+ m_sections.clear();
+ m_metainfoRows.clear();
+ m_buttonsRows.clear();
+
+ m_sections.push_back(Sections::Preview);
+
+ // It's bookmark.
+ if (m_info.IsBookmark())
+ m_sections.push_back(Sections::Bookmark);
+
+ // There is always at least coordinate meta field.
+ m_sections.push_back(Sections::Metainfo);
+ [self fillMetaInfoSection];
+
+ // There is at least one of these buttons.
+ if (m_info.ShouldShowAddPlace() || m_info.ShouldShowEditPlace() || m_info.ShouldShowAddBusiness() || m_info.IsSponsoredHotel())
+ {
+ m_sections.push_back(Sections::Buttons);
+ [self fillButtonsSection];
+ }
+}
+
+- (void)fillMetaInfoSection
+{
+ using namespace osm;
+ auto const availableProperties = m_info.AvailableProperties();
+ // We can't match each metadata property to its UI field and thats why we need to use our own enum.
+ for (auto const p : availableProperties)
+ {
+ switch (p)
+ {
+ case Props::OpeningHours:
+ m_metainfoRows.push_back(MetainfoRows::OpeningHours);
+ break;
+ case Props::Phone:
+ m_metainfoRows.push_back(MetainfoRows::Phone);
+ break;
+ case Props::Website:
+ m_metainfoRows.push_back(MetainfoRows::Website);
+ break;
+ case Props::Email:
+ m_metainfoRows.push_back(MetainfoRows::Email);
+ break;
+ case Props::Cuisine:
+ m_metainfoRows.push_back(MetainfoRows::Cuisine);
+ break;
+ case Props::Operator:
+ m_metainfoRows.push_back(MetainfoRows::Operator);
+ break;
+ case Props::Internet:
+ m_metainfoRows.push_back(MetainfoRows::Internet);
+ break;
+
+ case Props::Wikipedia:
+ case Props::Elevation:
+ case Props::Stars:
+ case Props::Flats:
+ case Props::BuildingLevels:
+ case Props::Fax:
+ break;
+ }
+ }
+
+ auto const address = m_info.GetAddress();
+ if (!address.empty())
+ m_metainfoRows.push_back(MetainfoRows::Address);
+
+ m_metainfoRows.push_back(MetainfoRows::Coordinate);
+}
+
+- (void)fillButtonsSection
+{
+ // We don't have to show edit, add place or business if it's booking object.
+ if (m_info.IsSponsoredHotel())
+ {
+ m_buttonsRows.push_back(ButtonsRows::HotelDescription);
+ return;
+ }
+
+ if (m_info.ShouldShowAddPlace())
+ m_buttonsRows.push_back(ButtonsRows::AddPlace);
+
+ if (m_info.ShouldShowEditPlace())
+ m_buttonsRows.push_back(ButtonsRows::EditPlace);
+
+ if (m_info.ShouldShowAddBusiness())
+ m_buttonsRows.push_back(ButtonsRows::AddBusiness);
+}
+
+- (void)updateBookmarkStatus:(BOOL)isBookmark
+{
+ auto & f = GetFramework();
+ auto & bmManager = f.GetBookmarkManager();
+ if (isBookmark)
+ {
+ auto const categoryIndex = static_cast<int>(f.LastEditedBMCategory());
+ BookmarkData bmData{m_info.FormatNewBookmarkName(), f.LastEditedBMType()};
+ auto const bookmarkIndex =
+ static_cast<int>(bmManager.AddBookmark(categoryIndex,self.mercator, bmData));
+
+ auto category = f.GetBmCategory(categoryIndex);
+ NSAssert(category, @"Category can't be nullptr!");
+ {
+ BookmarkCategory::Guard guard(*category);
+ Bookmark * bookmark =
+ static_cast<Bookmark *>(guard.m_controller.GetUserMarkForEdit(bookmarkIndex));
+ f.FillBookmarkInfo(*bookmark, {bookmarkIndex, categoryIndex}, m_info);
+ }
+ m_sections.insert(m_sections.begin() + 1, Sections::Bookmark);
+ }
+ else
+ {
+ auto const bac = m_info.GetBookmarkAndCategory();
+ auto category = bmManager.GetBmCategory(bac.m_categoryIndex);
+ NSAssert(category, @"Category can't be nullptr!");
+ {
+ BookmarkCategory::Guard guard(*category);
+ guard.m_controller.DeleteUserMark(bac.m_bookmarkIndex);
+ }
+ category->SaveToKMLFile();
+
+ m_info.m_bac = {};
+ m_sections.erase(remove(m_sections.begin(), m_sections.end(), Sections::Bookmark));
+ }
+}
+
+#pragma mark - Getters
+
+- (storage::TCountryId const &)countryId
+{
+ return m_info.m_countryId;
+}
+
+- (FeatureID const &)featureId
+{
+ return m_info.GetID();
+}
+
+- (NSString *)title
+{
+ return @(m_info.GetTitle().c_str());
+}
+
+- (NSString *)subtitle
+{
+ return @(m_info.GetSubtitle().c_str());
+}
+
+- (place_page::OpeningHours)schedule;
+{
+ auto const raw = m_info.GetOpeningHours();
+ if (raw.empty())
+ return place_page::OpeningHours::Unknown;
+
+ auto const t = time(nullptr);
+ osmoh::OpeningHours oh(raw);
+ if (oh.IsValid())
+ {
+
+ if (oh.IsTwentyFourHours())
+ return place_page::OpeningHours::AllDay;
+ else if (oh.IsOpen(t))
+ return place_page::OpeningHours::Open;
+ else if (oh.IsClosed(t))
+ return place_page::OpeningHours::Closed;
+ else
+ return place_page::OpeningHours::Unknown;
+ }
+ else
+ {
+ return place_page::OpeningHours::Unknown;
+ }
+}
+
+- (NSString *)bookingRating
+{
+ return m_info.IsHotel() ? @(m_info.GetRatingFormatted().c_str()) : nil;
+}
+
+- (NSString *)bookingApproximatePricing
+{
+ return m_info.IsHotel() ? @(m_info.GetApproximatePricing().c_str()) : nil;
+}
+
+- (NSURL *)bookingURL
+{
+ return m_info.IsSponsoredHotel() ? [NSURL URLWithString:@(m_info.m_sponsoredBookingUrl.c_str())] : nil;
+}
+
+- (NSURL *)bookingDescriptionURL
+{
+ return m_info.IsSponsoredHotel() ? [NSURL URLWithString:@(m_info.m_sponsoredDescriptionUrl.c_str())] : nil;
+}
+
+- (NSString *)hotelId
+{
+ return m_info.IsSponsoredHotel() ? @(m_info.GetMetadata().Get(feature::Metadata::FMD_SPONSORED_ID).c_str())
+ : nil;
+}
+
+- (void)assignOnlinePriceToLabel:(UILabel *)label
+{
+ NSAssert(m_info.IsSponsoredHotel(), @"Online price must be assigned to booking object!");
+ if (Platform::ConnectionStatus() == Platform::EConnectionType::CONNECTION_NONE)
+ return;
+
+ NSNumberFormatter * currencyFormatter = [[NSNumberFormatter alloc] init];
+ if (currencyFormatter.currencyCode.length != 3)
+ currencyFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
+
+ currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
+ currencyFormatter.maximumFractionDigits = 0;
+
+ string const currency = currencyFormatter.currencyCode.UTF8String;
+ auto & api = GetFramework().GetBookingApi();
+
+ auto const func = [self, label, currency, currencyFormatter](string const & minPrice, string const & priceCurrency)
+ {
+ if (currency != priceCurrency)
+ return;
+
+ NSNumberFormatter * decimalFormatter = [[NSNumberFormatter alloc] init];
+ decimalFormatter.numberStyle = NSNumberFormatterDecimalStyle;
+
+ NSString * currencyString = [currencyFormatter stringFromNumber:[decimalFormatter numberFromString:
+ [@(minPrice.c_str()) stringByReplacingOccurrencesOfString:@"."
+ withString:decimalFormatter.decimalSeparator]]];
+
+ NSString * pattern = [L(@"place_page_starting_from") stringByReplacingOccurrencesOfString:@"%s" withString:@"%@"];
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ label.text = [NSString stringWithFormat:pattern, currencyString];
+ });
+ };
+
+ api.GetMinPrice(self.hotelId.UTF8String, currency, func);
+}
+
+- (NSString *)address
+{
+ return @(m_info.GetAddress().c_str());
+}
+
+- (NSString *)apiURL
+{
+ return @(m_info.GetApiUrl().c_str());
+}
+
+- (NSString *)externalTitle
+{
+ return m_info.IsBookmark() && m_info.m_bookmarkTitle != m_info.GetTitle() ?
+ @(m_info.m_bookmarkTitle.c_str())
+ : nil;
+}
+
+- (NSString *)bookmarkColor
+{
+ return m_info.IsBookmark() ? @(m_info.m_bookmarkColorName.c_str()) : nil;;
+}
+
+- (NSString *)bookmarkDescription
+{
+ return m_info.IsBookmark() ? @(m_info.m_bookmarkDescription.c_str()) : nil;
+}
+
+- (NSString *)bookmarkCategory
+{
+ return m_info.IsBookmark() ? @(m_info.m_bookmarkCategoryName.c_str()) : nil;;
+}
+
+- (BookmarkAndCategory)bac;
+{
+ return m_info.IsBookmark() ? m_info.m_bac : BookmarkAndCategory();
+}
+
+- (vector<Sections> const &)sections
+{
+ return m_sections;
+}
+
+- (vector<MetainfoRows> const &)metainfoRows
+{
+ return m_metainfoRows;
+}
+
+- (vector<ButtonsRows> const &)buttonsRows
+{
+ return m_buttonsRows;
+}
+
+- (NSString *)stringForRow:(MetainfoRows)row
+{
+ switch (row)
+ {
+ case MetainfoRows::OpeningHours:
+ return @(m_info.GetOpeningHours().c_str());
+ case MetainfoRows::Phone:
+ return @(m_info.GetPhone().c_str());
+ case MetainfoRows::Address:
+ return @(m_info.GetAddress().c_str());
+ case MetainfoRows::Website:
+ return @(m_info.GetWebsite().c_str());
+ case MetainfoRows::Email:
+ return @(m_info.GetEmail().c_str());
+ case MetainfoRows::Cuisine:
+ return @(strings::JoinStrings(m_info.GetCuisines(), Info::kSubtitleSeparator).c_str());
+ case MetainfoRows::Operator:
+ return @(m_info.GetOperator().c_str());
+ case MetainfoRows::Internet:
+ return L(@"WiFi_available");
+ case MetainfoRows::Coordinate:
+ return @(m_info.GetFormattedCoordinate([[NSUserDefaults standardUserDefaults] boolForKey:kUserDefaultsLatLonAsDMSKey]).c_str());
+ }
+}
+
+#pragma mark - Helpres
+
+- (NSString *)phoneNumber
+{
+ return @(m_info.GetPhone().c_str());
+}
+
+- (BOOL)isBookmark
+{
+ return m_info.IsBookmark();
+}
+
+- (BOOL)isApi
+{
+ return m_info.HasApiUrl();
+}
+
+- (BOOL)isBooking
+{
+ return m_info.IsHotel();
+}
+
+- (BOOL)isMyPosition
+{
+ return m_info.IsMyPosition();
+}
+
+- (BOOL)isHTMLDescription
+{
+ return strings::IsHTML(m_info.m_bookmarkDescription);
+}
+
+#pragma mark - Coordinates
+
+- (m2::PointD const &)mercator
+{
+ return m_info.GetMercator();
+}
+
+- (ms::LatLon)latLon
+{
+ return m_info.GetLatLon();
+}
+
++ (void)toggleCoordinateSystem
+{
+ NSUserDefaults * ud = [NSUserDefaults standardUserDefaults];
+ [ud setBool:![ud boolForKey:kUserDefaultsLatLonAsDMSKey] forKey:kUserDefaultsLatLonAsDMSKey];
+ [ud synchronize];
+}
+
+@end
diff --git a/iphone/Maps/Classes/MWMPlacePageEntity.h b/iphone/Maps/Classes/MWMPlacePageEntity.h
index 9effcba53a..3f538c04b2 100644
--- a/iphone/Maps/Classes/MWMPlacePageEntity.h
+++ b/iphone/Maps/Classes/MWMPlacePageEntity.h
@@ -61,12 +61,13 @@ using MWMPlacePageCellTypeValueMap = map<MWMPlacePageCellType, string>;
- (BOOL)isBookmark;
- (BOOL)isApi;
- (BOOL)isBooking;
-- (ms::LatLon)latlon;
+- (ms::LatLon)latLon;
- (m2::PointD const &)mercator;
- (NSString *)apiURL;
-- (NSURL *)bookingUrl;
-- (NSURL *)bookingDescriptionUrl;
+- (NSURL *)bookingURL;
+- (NSURL *)bookingDescriptionURL;
- (NSString * )hotelId;
+- (NSString *)phoneNumber;
- (string)titleForNewBookmark;
- (instancetype)initWithInfo:(place_page::Info const &)info;
diff --git a/iphone/Maps/Classes/MWMPlacePageEntity.mm b/iphone/Maps/Classes/MWMPlacePageEntity.mm
index 47f95083f9..fc31f06d48 100644
--- a/iphone/Maps/Classes/MWMPlacePageEntity.mm
+++ b/iphone/Maps/Classes/MWMPlacePageEntity.mm
@@ -14,7 +14,7 @@
using feature::Metadata;
-extern NSString * const kUserDefaultsLatLonAsDMSKey = @"UserDefaultsLatLonAsDMS";
+static NSString * const kUserDefaultsLatLonAsDMSKey = @"UserDefaultsLatLonAsDMS";
namespace
{
@@ -162,9 +162,9 @@ void initFieldsMap()
- (void)configureBookmark
{
auto const bac = m_info.GetBookmarkAndCategory();
- BookmarkCategory * cat = GetFramework().GetBmCategory(bac.first);
+ BookmarkCategory * cat = GetFramework().GetBmCategory(bac.m_categoryIndex);
BookmarkData const & data =
- static_cast<Bookmark const *>(cat->GetUserMark(bac.second))->GetData();
+ static_cast<Bookmark const *>(cat->GetUserMark(bac.m_bookmarkIndex))->GetData();
self.bookmarkTitle = @(data.GetName().c_str());
self.bookmarkCategory = @(m_info.GetBookmarkCategoryName().c_str());
@@ -214,11 +214,11 @@ void initFieldsMap()
return haveField ? @(it->second.c_str()) : nil;
}
-- (NSURL *)bookingUrl
+- (NSURL *)bookingURL
{
return [self sponsoredUrl:NO];
}
-- (NSURL *)bookingDescriptionUrl { return [self sponsoredUrl:YES]; }
+- (NSURL *)bookingDescriptionURL { return [self sponsoredUrl:YES]; }
- (NSURL *)sponsoredUrl:(BOOL)isDescription
{
auto const & url =
@@ -238,7 +238,12 @@ void initFieldsMap()
return self.isBooking ? @(m_info.GetMetadata().Get(Metadata::FMD_SPONSORED_ID).c_str()) : nil;
}
-- (ms::LatLon)latlon { return m_info.GetLatLon(); }
+- (NSString *)phoneNumber
+{
+ return [self getCellValue:MWMPlacePageCellTypePhoneNumber];
+}
+
+- (ms::LatLon)latLon { return m_info.GetLatLon(); }
- (m2::PointD const &)mercator { return m_info.GetMercator(); }
- (NSString *)apiURL { return @(m_info.GetApiUrl().c_str()); }
- (string)titleForNewBookmark { return m_info.FormatNewBookmarkName(); }
@@ -246,7 +251,7 @@ void initFieldsMap()
{
BOOL const useDMSFormat =
[[NSUserDefaults standardUserDefaults] boolForKey:kUserDefaultsLatLonAsDMSKey];
- ms::LatLon const latlon = self.latlon;
+ ms::LatLon const latlon = self.latLon;
return @((useDMSFormat ? measurement_utils::FormatLatLon(latlon.lat, latlon.lon)
: measurement_utils::FormatLatLonAsDMS(latlon.lat, latlon.lon, 2))
.c_str());
@@ -295,14 +300,14 @@ void initFieldsMap()
- (void)synchronize
{
Framework & f = GetFramework();
- BookmarkCategory * category = f.GetBmCategory(self.bac.first);
+ BookmarkCategory * category = f.GetBmCategory(self.bac.m_categoryIndex);
if (!category)
return;
{
BookmarkCategory::Guard guard(*category);
Bookmark * bookmark =
- static_cast<Bookmark *>(guard.m_controller.GetUserMarkForEdit(self.bac.second));
+ static_cast<Bookmark *>(guard.m_controller.GetUserMarkForEdit(self.bac.m_bookmarkIndex));
if (!bookmark)
return;
diff --git a/iphone/Maps/Classes/MWMPlacePageInfoCell.h b/iphone/Maps/Classes/MWMPlacePageInfoCell.h
index 65c97eeaf0..daae2878a7 100644
--- a/iphone/Maps/Classes/MWMPlacePageInfoCell.h
+++ b/iphone/Maps/Classes/MWMPlacePageInfoCell.h
@@ -1,9 +1,13 @@
#import "MWMPlacePageEntity.h"
#import "MWMTableViewCell.h"
+#import "MWMPlacePageData.h"
+
@interface MWMPlacePageInfoCell : MWMTableViewCell
-- (void)configureWithType:(MWMPlacePageCellType)type info:(NSString *)info;
+- (void)configureWithType:(MWMPlacePageCellType)type info:(NSString *)info NS_DEPRECATED_IOS(7_0, 8_0);
+
+- (void)configWithRow:(place_page::MetainfoRows)row data:(MWMPlacePageData *)data NS_AVAILABLE_IOS(8_0);
@property(weak, nonatomic, readonly) IBOutlet UIImageView * icon;
@property(weak, nonatomic, readonly) IBOutlet id textContainer;
diff --git a/iphone/Maps/Classes/MWMPlacePageInfoCell.mm b/iphone/Maps/Classes/MWMPlacePageInfoCell.mm
index 1edbd5aa79..7c394bed07 100644
--- a/iphone/Maps/Classes/MWMPlacePageInfoCell.mm
+++ b/iphone/Maps/Classes/MWMPlacePageInfoCell.mm
@@ -19,7 +19,9 @@
@property(weak, nonatomic) IBOutlet UIButton * upperButton;
@property(weak, nonatomic) IBOutlet UIImageView * toggleImage;
-@property(nonatomic) MWMPlacePageCellType type;
+@property(nonatomic) MWMPlacePageCellType type NS_DEPRECATED_IOS(7_0, 8_0);
+@property(nonatomic) place_page::MetainfoRows rowType NS_AVAILABLE_IOS(8_0);
+@property(weak, nonatomic) MWMPlacePageData * data NS_AVAILABLE_IOS(8_0);
@end
@@ -37,8 +39,55 @@
}
}
+- (void)configWithRow:(place_page::MetainfoRows)row data:(MWMPlacePageData *)data;
+{
+ self.rowType = row;
+ self.data = data;
+ NSString * name;
+ switch (row)
+ {
+ case place_page::MetainfoRows::Address:
+ self.toggleImage.hidden = YES;
+ name = @"address";
+ break;
+ case place_page::MetainfoRows::Phone:
+ self.toggleImage.hidden = YES;
+ name = @"phone_number";
+ break;
+ case place_page::MetainfoRows::Website:
+ self.toggleImage.hidden = YES;
+ name = @"website";
+ break;
+ case place_page::MetainfoRows::Email:
+ self.toggleImage.hidden = YES;
+ name = @"email";
+ break;
+ case place_page::MetainfoRows::Cuisine:
+ self.toggleImage.hidden = YES;
+ name = @"cuisine";
+ break;
+ case place_page::MetainfoRows::Operator:
+ self.toggleImage.hidden = YES;
+ name = @"operator";
+ break;
+ case place_page::MetainfoRows::Internet:
+ self.toggleImage.hidden = YES;
+ name = @"wifi";
+ break;
+ case place_page::MetainfoRows::Coordinate:
+ self.toggleImage.hidden = NO;
+ name = @"coordinate";
+ break;
+ case place_page::MetainfoRows::OpeningHours:
+ NSAssert(false, @"Incorrect cell type!");
+ break;
+ }
+ [self configWithIconName:name data:[self.data stringForRow:row]];
+}
+
- (void)configureWithType:(MWMPlacePageCellType)type info:(NSString *)info;
{
+ self.type = type;
NSString * typeName;
switch (type)
{
@@ -70,14 +119,18 @@
default: NSAssert(false, @"Incorrect type!"); break;
}
+ [self configWithIconName:typeName data:info];
+}
+
+- (void)configWithIconName:(NSString *)name data:(NSString *)data
+{
UIImage * image =
- [UIImage imageNamed:[NSString stringWithFormat:@"%@%@", @"ic_placepage_", typeName]];
- self.type = type;
+ [UIImage imageNamed:[NSString stringWithFormat:@"%@%@", @"ic_placepage_", name]];
self.icon.image = image;
self.icon.mwm_coloring = [self.textContainer isKindOfClass:[UITextView class]]
? MWMImageColoringBlue
: MWMImageColoringBlack;
- [self changeText:info];
+ [self changeText:data];
UILongPressGestureRecognizer * longTap =
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longTap:)];
longTap.minimumPressDuration = 0.3;
@@ -123,25 +176,55 @@
- (IBAction)cellTap
{
- switch (self.type)
+ if (isIOS7)
{
- case MWMPlacePageCellTypeURL:
- case MWMPlacePageCellTypeWebsite:
+ switch (self.type)
+ {
+ case MWMPlacePageCellTypeURL:
+ case MWMPlacePageCellTypeWebsite:
+ [Statistics logEvent:kStatEventName(kStatPlacePage, kStatOpenSite)];
+ break;
+ case MWMPlacePageCellTypeEmail:
+ [Statistics logEvent:kStatEventName(kStatPlacePage, kStatSendEmail)];
+ break;
+ case MWMPlacePageCellTypePhoneNumber:
+ [Statistics logEvent:kStatEventName(kStatPlacePage, kStatCallPhoneNumber)];
+ break;
+ case MWMPlacePageCellTypeCoordinate:
+ [Statistics logEvent:kStatEventName(kStatPlacePage, kStatToggleCoordinates)];
+ [self.currentEntity toggleCoordinateSystem];
+ [self changeText:[self.currentEntity getCellValue:MWMPlacePageCellTypeCoordinate]];
+ break;
+ default: break;
+ }
+ return;
+ }
+
+ switch (self.rowType)
+ {
+ case place_page::MetainfoRows::Phone:
+ [Statistics logEvent:kStatEventName(kStatPlacePage, kStatCallPhoneNumber)];
+ break;
+ case place_page::MetainfoRows::Website:
[Statistics logEvent:kStatEventName(kStatPlacePage, kStatOpenSite)];
break;
- case MWMPlacePageCellTypeEmail:
+ case place_page::MetainfoRows::Email:
[Statistics logEvent:kStatEventName(kStatPlacePage, kStatSendEmail)];
break;
- case MWMPlacePageCellTypePhoneNumber:
- [Statistics logEvent:kStatEventName(kStatPlacePage, kStatCallPhoneNumber)];
- break;
- case MWMPlacePageCellTypeCoordinate:
+ case place_page::MetainfoRows::Coordinate:
[Statistics logEvent:kStatEventName(kStatPlacePage, kStatToggleCoordinates)];
- [self.currentEntity toggleCoordinateSystem];
- [self changeText:[self.currentEntity getCellValue:MWMPlacePageCellTypeCoordinate]];
+ [MWMPlacePageData toggleCoordinateSystem];
+ [self changeText:[self.data stringForRow:self.rowType]];
+ break;
+
+ case place_page::MetainfoRows::Cuisine:
+ case place_page::MetainfoRows::Operator:
+ case place_page::MetainfoRows::OpeningHours:
+ case place_page::MetainfoRows::Address:
+ case place_page::MetainfoRows::Internet:
break;
- default: break;
}
+
}
- (void)longTap:(UILongPressGestureRecognizer *)sender
diff --git a/iphone/Maps/Classes/MWMPlacePageLayout.h b/iphone/Maps/Classes/MWMPlacePageLayout.h
new file mode 100644
index 0000000000..e0e8d41aaa
--- /dev/null
+++ b/iphone/Maps/Classes/MWMPlacePageLayout.h
@@ -0,0 +1,42 @@
+#include "storage/storage_defines.hpp"
+
+@protocol MWMPlacePageLayoutDelegate <NSObject>
+
+- (void)onTopBoundChanged:(CGFloat)bound;
+- (void)shouldDestroyLayout;
+
+@end
+
+@protocol MWMPlacePageLayoutDataSource <NSObject>
+
+- (NSString *)distanceToObject;
+- (void)downloadSelectedArea;
+
+@end
+
+@class MWMPlacePageData, MWMPPView;
+@protocol MWMPlacePageButtonsProtocol, MWMActionBarProtocol;
+
+/// Helps with place page view layout and representation
+@interface MWMPlacePageLayout : NSObject
+
+- (instancetype)initWithOwnerView:(UIView *)view
+ delegate:(id<MWMPlacePageLayoutDelegate, MWMPlacePageButtonsProtocol, MWMActionBarProtocol>)delegate
+ dataSource:(id<MWMPlacePageLayoutDataSource>)dataSource;
+
+- (void)layoutWithSize:(CGSize const &)size;
+- (void)showWithData:(MWMPlacePageData *)data;
+- (void)close;
+
+- (void)mwm_refreshUI;
+
+- (UIView *)shareAnchor;
+
+- (void)reloadBookmarkSection:(BOOL)isBookmark;
+
+- (void)rotateDirectionArrowToAngle:(CGFloat)angle;
+- (void)setDistanceToObject:(NSString *)distance;
+
+- (void)processDownloaderEventWithStatus:(storage::NodeStatus)status progress:(CGFloat)progress;
+
+@end
diff --git a/iphone/Maps/Classes/MWMPlacePageLayout.mm b/iphone/Maps/Classes/MWMPlacePageLayout.mm
new file mode 100644
index 0000000000..b8e4a55a3c
--- /dev/null
+++ b/iphone/Maps/Classes/MWMPlacePageLayout.mm
@@ -0,0 +1,530 @@
+#import "MWMCircularProgress.h"
+#import "MWMPlacePageActionBar.h"
+#import "MWMPlacePageCellUpdateProtocol.h"
+#import "MWMPlacePageData.h"
+#import "MWMPlacePageLayout.h"
+#import "MWMPPView.h"
+
+#import "MWMBookmarkCell.h"
+#import "MWMPlacePageBookmarkCell.h"
+#import "MWMPlacePageButtonCell.h"
+#import "MWMPlacePageInfoCell.h"
+#import "MWMOpeningHoursCell.h"
+#import "MWMPlacePagePreviewCell.h"
+#import "UIColor+MapsMeColor.h"
+
+#include "storage/storage.hpp"
+
+#include "std/array.hpp"
+
+namespace
+{
+enum class ScrollDirection { Up, Down };
+
+enum class State { Bottom, Top };
+
+CGFloat const kMinOffset = 64;
+CGFloat const kOpenPlacePageStopValue = 0.7;
+
+array<NSString *, 1> kPreviewCells = {{@"MWMPlacePagePreviewCell"}};
+
+array<NSString *, 1> kBookmarkCells = {{@"MWMBookmarkCell"}};
+
+array<NSString *, 9> kMetaInfoCells = {{@"MWMOpeningHoursCell", @"PlacePageLinkCell", @"PlacePageInfoCell", @"PlacePageLinkCell", @"PlacePageLinkCell", @"PlacePageInfoCell", @"PlacePageInfoCell", @"PlacePageInfoCell", @"PlacePageInfoCell"}};
+
+array<NSString *, 1> kButtonsCells = {{@"MWMPlacePageButtonCell"}};
+
+NSTimeInterval const kAnimationDuration = 0.15;
+
+void animate(TMWMVoidBlock animate, TMWMVoidBlock completion = nil)
+{
+ [UIView animateWithDuration:kAnimationDuration delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
+ animate();
+ } completion:^(BOOL finished) {
+ if (completion)
+ completion();
+ }];
+}
+
+} // namespace
+
+@interface MWMPlacePageLayout () <UITableViewDelegate, UITableViewDataSource,
+ MWMPlacePageCellUpdateProtocol, MWMPlacePageViewUpdateProtocol>
+
+@property(weak, nonatomic) MWMPlacePageData * data;
+
+@property(weak, nonatomic) UIView * ownerView;
+@property(weak, nonatomic) id<MWMPlacePageLayoutDelegate, MWMPlacePageButtonsProtocol, MWMActionBarProtocol> delegate;
+@property(weak, nonatomic) id<MWMPlacePageLayoutDataSource> dataSource;
+
+@property(nonatomic) MWMPPScrollView * scrollView;
+@property(nonatomic) IBOutlet MWMPPView * placePageView;
+
+@property(nonatomic) ScrollDirection direction;
+@property(nonatomic) State state;
+
+@property(nonatomic) CGFloat portraitOpenContentOffset;
+@property(nonatomic) CGFloat landscapeOpenContentOffset;
+@property(nonatomic) CGFloat lastContentOffset;
+@property(nonatomic) CGFloat expandedContentOffset;
+
+@property(nonatomic) MWMPlacePagePreviewCell * ppPreviewCell;
+@property(nonatomic) MWMBookmarkCell * bookmarkCell;
+
+@property(nonatomic) MWMPlacePageActionBar * actionBar;
+
+@property(nonatomic) BOOL isPlacePageButtonsEnabled;
+@property(nonatomic) BOOL isDownloaderViewShown;
+
+@end
+
+@implementation MWMPlacePageLayout
+
+- (instancetype)initWithOwnerView:(UIView *)view
+ delegate:(id<MWMPlacePageLayoutDelegate, MWMPlacePageButtonsProtocol, MWMActionBarProtocol>)delegate
+ dataSource:(id<MWMPlacePageLayoutDataSource>)dataSource
+{
+ self = [super init];
+ if (self)
+ {
+ _ownerView = view;
+ _delegate = delegate;
+ _dataSource = dataSource;
+
+ [[NSBundle mainBundle] loadNibNamed:[MWMPPView className] owner:self options:nil];
+ auto view = self.ownerView;
+ auto const & size = view.size;
+ _placePageView.frame = {{0, size.height}, size};
+ _placePageView.delegate = self;
+ _scrollView = [[MWMPPScrollView alloc] initWithFrame:view.frame inactiveView:_placePageView];
+ _portraitOpenContentOffset = MAX(size.width, size.height) * kOpenPlacePageStopValue;
+ _landscapeOpenContentOffset = MIN(size.width, size.height) * kOpenPlacePageStopValue;
+ [_ownerView addSubview:_scrollView];
+ [_scrollView addSubview:_placePageView];
+
+ auto tv = _placePageView.tableView;
+ [tv registerNib:[UINib nibWithNibName:kPreviewCells[0] bundle:nil] forCellReuseIdentifier:kPreviewCells[0]];
+ [tv registerNib:[UINib nibWithNibName:kButtonsCells[0] bundle:nil] forCellReuseIdentifier:kButtonsCells[0]];
+ [tv registerNib:[UINib nibWithNibName:kBookmarkCells[0] bundle:nil] forCellReuseIdentifier:kBookmarkCells[0]];
+ }
+ return self;
+}
+
+- (void)registerCells
+{
+ auto tv = self.placePageView.tableView;
+ // Register all meta info cells.
+ for (auto const i : self.data.metainfoRows)
+ {
+ auto name = kMetaInfoCells[static_cast<size_t>(i)];
+ [tv registerNib:[UINib nibWithNibName:name bundle:nil] forCellReuseIdentifier:name];
+ }
+}
+
+- (void)layoutWithSize:(CGSize const &)size
+{
+ self.scrollView.frame = {{}, size};
+ self.placePageView.origin = {0., size.height};
+ self.actionBar.frame = {{0., size.height - 48}, {size.width, 48}};
+ [self.delegate onTopBoundChanged:self.scrollView.contentOffset.y];
+}
+
+- (UIView *)shareAnchor
+{
+ return self.actionBar.shareAnchor;
+}
+
+- (void)showWithData:(MWMPlacePageData *)data
+{
+ self.isPlacePageButtonsEnabled = YES;
+ self.data = nil;
+ self.ppPreviewCell = nil;
+ self.bookmarkCell = nil;
+
+ self.scrollView.delegate = self;
+ self.state = State::Bottom;
+
+ [self collapse];
+
+ self.data = data;
+ [self.actionBar configureWithData:static_cast<id<MWMActionBarSharedData>>(self.data)];
+ [self registerCells];
+ [self.placePageView.tableView reloadData];
+}
+
+- (void)rotateDirectionArrowToAngle:(CGFloat)angle
+{
+ [self.ppPreviewCell rotateDirectionArrowToAngle:angle];
+}
+
+- (void)setDistanceToObject:(NSString *)distance
+{
+ [self.ppPreviewCell setDistanceToObject:distance];
+}
+
+- (MWMPlacePageActionBar *)actionBar
+{
+ if (!_actionBar)
+ {
+ _actionBar = [MWMPlacePageActionBar actionBarWithDelegate:self.delegate];
+ UIView * superview = self.ownerView;
+ _actionBar.origin = {0., superview.height};
+ [superview addSubview:_actionBar];
+ }
+ return _actionBar;
+}
+
+- (void)close
+{
+ animate(^ {
+ self.actionBar.origin = {0., self.ownerView.height};
+ [self.scrollView setContentOffset:{} animated:YES];
+ }, ^{
+ [self.actionBar removeFromSuperview];
+ self.actionBar = nil;
+ [self.delegate shouldDestroyLayout];
+ });
+}
+
+- (void)mwm_refreshUI
+{
+ [self.placePageView mwm_refreshUI];
+ [self.actionBar mwm_refreshUI];
+}
+
+- (void)reloadBookmarkSection:(BOOL)isBookmark
+{
+ auto tv = self.placePageView.tableView;
+ NSIndexSet * set = [NSIndexSet indexSetWithIndex:static_cast<NSInteger>(place_page::Sections::Bookmark)];
+
+ if (isBookmark)
+ {
+ if (self.bookmarkCell)
+ [tv reloadSections:set withRowAnimation:UITableViewRowAnimationAutomatic];
+ else
+ [tv insertSections:set withRowAnimation:UITableViewRowAnimationAutomatic];
+ }
+ else
+ {
+ [tv deleteSections:set withRowAnimation:UITableViewRowAnimationAutomatic];
+ self.bookmarkCell = nil;
+ }
+}
+
+- (void)collapse
+{
+ self.actionBar.hidden = YES;
+
+ [self.placePageView hideTableView:YES];
+ self.scrollView.scrollEnabled = NO;
+
+ animate(^{
+ self.actionBar.origin = {0., self.ownerView.height};
+ [self.scrollView setContentOffset:{0., kMinOffset} animated:YES];
+ });
+}
+
+- (void)expand
+{
+ self.actionBar.hidden = NO;
+ self.scrollView.scrollEnabled = YES;
+
+ animate(^{
+ [self.placePageView hideTableView:NO];
+ self.actionBar.minY = self.actionBar.superview.height - self.actionBar.height;
+ self.expandedContentOffset = self.ppPreviewCell.height + self.placePageView.top.height + self.actionBar.height;
+ auto const targetOffset = self.state == State::Bottom ? self.expandedContentOffset : self.topContentOffset;
+ [self.scrollView setContentOffset:{0, targetOffset} animated:YES];
+ });
+}
+
+- (BOOL)isPortrait
+{
+ auto const & s = self.ownerView.size;
+ return s.height > s.width;
+}
+
+- (CGFloat)topContentOffset
+{
+ auto const target = self.isPortrait ? self.portraitOpenContentOffset : self.landscapeOpenContentOffset;
+ if (target > self.placePageView.height)
+ return self.placePageView.height;
+
+ return target;
+}
+
+#pragma mark - Downloader event
+
+- (void)processDownloaderEventWithStatus:(storage::NodeStatus)status progress:(CGFloat)progress
+{
+ using namespace storage;
+
+ switch(status)
+ {
+ case NodeStatus::Undefined:
+ {
+ self.isPlacePageButtonsEnabled = YES;
+ auto const & sections = self.data.sections;
+ auto const it = find(sections.begin(), sections.end(), place_page::Sections::Buttons);
+ if (it != sections.end())
+ {
+ [self.placePageView.tableView reloadSections:[NSIndexSet indexSetWithIndex:distance(sections.begin(), it)]
+ withRowAnimation:UITableViewRowAnimationAutomatic];
+ }
+
+ if (self.ppPreviewCell)
+ [self.ppPreviewCell setDownloaderViewHidden:YES animated:NO];
+ else
+ self.isDownloaderViewShown = NO;
+
+ break;
+ }
+
+ case NodeStatus::Downloading:
+ self.ppPreviewCell.mapDownloadProgress.progress = progress;
+ break;
+
+ case NodeStatus::InQueue:
+ self.ppPreviewCell.mapDownloadProgress.state = MWMCircularProgressStateSpinner;
+ break;
+
+ case NodeStatus::Error:
+ self.ppPreviewCell.mapDownloadProgress.state = MWMCircularProgressStateFailed;
+ break;
+
+ case NodeStatus::Partly:
+ break;
+
+ case NodeStatus::OnDiskOutOfDate:
+ case NodeStatus::OnDisk:
+ {
+ self.isPlacePageButtonsEnabled = YES;
+ auto const & sections = self.data.sections;
+ auto const it = find(sections.begin(), sections.end(), place_page::Sections::Buttons);
+ if (it != sections.end())
+ {
+ [self.placePageView.tableView reloadSections:[NSIndexSet indexSetWithIndex:distance(sections.begin(), it)]
+ withRowAnimation:UITableViewRowAnimationAutomatic];
+ }
+ [self.ppPreviewCell setDownloaderViewHidden:YES animated:NO];
+ break;
+ }
+
+ case NodeStatus::NotDownloaded:
+ self.isPlacePageButtonsEnabled = NO;
+ if (self.ppPreviewCell)
+ [self.ppPreviewCell setDownloaderViewHidden:NO animated:NO];
+ else
+ self.isDownloaderViewShown = YES;
+
+ break;
+ }
+}
+
+#pragma mark - UIScrollViewDelegate
+
+- (void)scrollViewDidScroll:(MWMPPScrollView *)scrollView
+{
+ if ([scrollView isEqual:self.placePageView.tableView])
+ return;
+
+ auto const offset = scrollView.contentOffset;
+ if (offset.y <= 0)
+ {
+ [self.scrollView removeFromSuperview];
+ [self.actionBar removeFromSuperview];
+ [self.delegate shouldDestroyLayout];
+ return;
+ }
+
+ if (offset.y > self.placePageView.height + 30)
+ {
+ auto const bounded = self.placePageView.height + 30;
+ [scrollView setContentOffset:{0, bounded}];
+ [self.delegate onTopBoundChanged:bounded];
+ }
+ else
+ {
+ [self.delegate onTopBoundChanged:offset.y];
+ }
+
+ self.direction = self.lastContentOffset < offset.y ? ScrollDirection::Up : ScrollDirection::Down;
+ self.lastContentOffset = offset.y;
+}
+
+- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
+ withVelocity:(CGPoint)velocity
+ targetContentOffset:(inout CGPoint *)targetContentOffset
+{
+ auto const actualOffset = scrollView.contentOffset.y;
+ auto const openOffset = self.isPortrait ? self.portraitOpenContentOffset : self.landscapeOpenContentOffset;
+ auto const targetOffset = (*targetContentOffset).y;
+
+ if (actualOffset > self.expandedContentOffset && actualOffset < openOffset)
+ {
+ self.state = self.direction == ScrollDirection::Up ? State::Top : State::Bottom;
+ (*targetContentOffset).y = self.direction == ScrollDirection::Up ? openOffset : self.expandedContentOffset;
+ }
+ else if (actualOffset > openOffset && targetOffset < openOffset)
+ {
+ self.state = State::Top;
+ (*targetContentOffset).y = openOffset;
+ }
+ else if (actualOffset < self.expandedContentOffset)
+ {
+ (*targetContentOffset).y = 0;
+ animate(^{
+ self.actionBar.origin = {0., self.ownerView.height};
+ });
+ }
+ else
+ {
+ self.state = State::Top;
+ }
+}
+
+- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
+{
+ if (decelerate)
+ return;
+
+ auto const actualOffset = scrollView.contentOffset.y;
+ auto const openOffset = self.isPortrait ? self.portraitOpenContentOffset : self.landscapeOpenContentOffset;
+ if (actualOffset < self.expandedContentOffset + 30)
+ {
+ self.state = State::Bottom;
+ animate(^{
+ [scrollView setContentOffset:{0, self.expandedContentOffset} animated:YES];
+ });
+ }
+ else if (actualOffset < openOffset)
+ {
+ self.state = self.direction == ScrollDirection::Up ? State::Top : State::Bottom;
+ animate(^{
+ [scrollView setContentOffset:{0, self.direction == ScrollDirection::Up ? openOffset : self.expandedContentOffset}
+ animated:YES];
+ });
+ }
+ else
+ {
+ self.state = State::Top;
+ }
+}
+
+#pragma mark - UITableViewDelegate & UITableViewDataSource
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
+{
+ if (!self.data)
+ return 0;
+ return self.data.sections.size();
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
+{
+ using namespace place_page;
+
+ switch (self.data.sections[section])
+ {
+ case Sections::Preview:
+ case Sections::Bookmark: return 1;
+ case Sections::Metainfo: return self.data.metainfoRows.size();
+ case Sections::Buttons: return self.data.buttonsRows.size();
+ }
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ using namespace place_page;
+
+ switch (self.data.sections[indexPath.section])
+ {
+ case Sections::Preview:
+ {
+ if (!self.ppPreviewCell)
+ self.ppPreviewCell = [tableView dequeueReusableCellWithIdentifier:[MWMPlacePagePreviewCell className]];
+
+ [self.ppPreviewCell configure:self.data updateLayoutDelegate:self dataSource:self.dataSource];
+ [self.ppPreviewCell setDownloaderViewHidden:!self.isDownloaderViewShown animated:NO];
+
+ return self.ppPreviewCell;
+ }
+ case Sections::Bookmark:
+ {
+ MWMBookmarkCell * c = [tableView dequeueReusableCellWithIdentifier:kBookmarkCells[0]];
+ [c configureWithText:self.data.bookmarkDescription
+ updateCellDelegate:self
+ editBookmarkDelegate:self.delegate
+ isHTML:self.data.isHTMLDescription];
+ return c;
+ }
+ case Sections::Metainfo:
+ {
+ auto const row = self.data.metainfoRows[indexPath.row];
+ auto cellName = kMetaInfoCells[static_cast<size_t>(row)];
+ UITableViewCell * c = [tableView dequeueReusableCellWithIdentifier:cellName];
+
+ switch (row)
+ {
+ case MetainfoRows::OpeningHours:
+ [static_cast<MWMOpeningHoursCell *>(c) configureWithOpeningHours:[self.data stringForRow:row]
+ updateLayoutDelegate:self
+ isClosedNow:self.data.schedule == OpeningHours::Closed];
+ break;
+ case MetainfoRows::Phone:
+ case MetainfoRows::Address:
+ case MetainfoRows::Website:
+ case MetainfoRows::Email:
+ case MetainfoRows::Cuisine:
+ case MetainfoRows::Operator:
+ case MetainfoRows::Internet:
+ case MetainfoRows::Coordinate:
+ [static_cast<MWMPlacePageInfoCell *>(c) configWithRow:row data:self.data];
+ break;
+ }
+ return c;
+ }
+ case Sections::Buttons:
+ {
+ MWMPlacePageButtonCell * c = [tableView dequeueReusableCellWithIdentifier:kButtonsCells[0]];
+ auto const row = self.data.buttonsRows[indexPath.row];
+ [c configForRow:row withDelegate:self.delegate];
+ if (row != ButtonsRows::HotelDescription)
+ [c setEnabled:self.isPlacePageButtonsEnabled];
+
+ return c;
+ }
+ }
+}
+
+#pragma mark - MWMPlacePageCellUpdateProtocol
+
+- (void)updateCellWithForceReposition:(BOOL)isForceReposition
+{
+ auto const update = isForceReposition ? @selector(updateWithExpand) : @selector(update);
+ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:update object:nil];
+ [self performSelector:update withObject:nil afterDelay:0.5];
+}
+
+- (void)update
+{
+ auto tableView = self.placePageView.tableView;
+ [tableView beginUpdates];
+ [tableView endUpdates];
+}
+
+- (void)updateWithExpand
+{
+ [self update];
+ [self expand];
+}
+
+#pragma mark - MWMPlacePageViewUpdateProtocol
+
+- (void)updateWithHeight:(CGFloat)height
+{
+ auto const & size = self.ownerView.size;
+ self.scrollView.contentSize = {size.width, size.height + height};
+}
+
+@end
diff --git a/iphone/Maps/Classes/MWMPlacePageManager.h b/iphone/Maps/Classes/MWMPlacePageManager.h
new file mode 100644
index 0000000000..65609662f7
--- /dev/null
+++ b/iphone/Maps/Classes/MWMPlacePageManager.h
@@ -0,0 +1,5 @@
+#import "MWMPlacePageProtocol.h"
+
+@interface MWMPlacePageManager : NSObject <MWMPlacePageProtocol>
+
+@end
diff --git a/iphone/Maps/Classes/MWMPlacePageManager.mm b/iphone/Maps/Classes/MWMPlacePageManager.mm
new file mode 100644
index 0000000000..c4f6d65022
--- /dev/null
+++ b/iphone/Maps/Classes/MWMPlacePageManager.mm
@@ -0,0 +1,335 @@
+#import "MapViewController.h"
+#import "MWMActivityViewController.h"
+#import "MWMAPIBar.h"
+#import "MWMCircularProgress.h"
+#import "MWMPlacePageManager.h"
+#import "MWMViewController.h"
+#import "MWMPlacePageLayout.h"
+#import "MWMPlacePageData.h"
+#import "MWMFrameworkListener.h"
+#import "MWMFrameworkObservers.h"
+#import "MWMLocationManager.h"
+#import "MWMRouter.h"
+#import "MWMStorage.h"
+#import "MWMEditBookmarkController.h"
+#import "Statistics.h"
+#import <Pushwoosh/PushNotificationManager.h>
+
+#include "geometry/distance_on_sphere.hpp"
+
+#include "platform/measurement_utils.hpp"
+
+@interface MWMPlacePageManager() <MWMFrameworkStorageObserver, MWMPlacePageLayoutDelegate,
+ MWMPlacePageLayoutDataSource, MWMLocationObserver>
+
+@property(weak, nonatomic) MWMViewController * ownerViewController;
+@property(nonatomic) MWMPlacePageEntity * entity;
+
+@property(nonatomic) MWMPlacePageLayout * layout;
+@property(nonatomic) MWMPlacePageData * data;
+
+@property(nonatomic) storage::NodeStatus currentDownloaderStatus;
+
+@end
+
+@implementation MWMPlacePageManager
+
+- (instancetype)initWithViewController:(MWMViewController *)viewController
+{
+ self = [super init];
+ if (self)
+ _ownerViewController = viewController;
+ return self;
+}
+
+- (void)showPlacePage:(place_page::Info const &)info
+{
+ self.currentDownloaderStatus = storage::NodeStatus::Undefined;
+ [MWMFrameworkListener addObserver:self];
+
+ self.data = [[MWMPlacePageData alloc] initWithPlacePageInfo:info];
+ [self.layout showWithData:self.data];
+
+ // Call for the first time to produce changes
+ [self processCountryEvent:self.data.countryId];
+
+ if (![MWMLocationManager lastHeading])
+ return;
+
+ [MWMLocationManager addObserver:self];
+ [self.layout setDistanceToObject:self.distanceToObject];
+}
+
+- (void)closePlacePage
+{
+ [_layout close];
+ [MWMLocationManager removeObserver:self];
+ [MWMFrameworkListener removeObserver:self];
+}
+
+#pragma mark - MWMPlacePageLayoutDataSource
+
+- (void)downloadSelectedArea
+{
+ auto const & countryId = self.data.countryId;
+ NodeAttrs nodeAttrs;
+ GetFramework().GetStorage().GetNodeAttrs(countryId, nodeAttrs);
+ MWMAlertViewController * avc = [MapViewController controller].alertController;
+ switch (nodeAttrs.m_status)
+ {
+ case NodeStatus::NotDownloaded:
+ case NodeStatus::Partly:
+ [MWMStorage downloadNode:countryId alertController:avc onSuccess:nil];
+ break;
+ case NodeStatus::Undefined:
+ case NodeStatus::Error: [MWMStorage retryDownloadNode:countryId]; break;
+ case NodeStatus::OnDiskOutOfDate: [MWMStorage updateNode:countryId alertController:avc]; break;
+ case NodeStatus::Downloading:
+ case NodeStatus::InQueue: [MWMStorage cancelDownloadNode:countryId]; break;
+ case NodeStatus::OnDisk: break;
+ }
+}
+
+- (NSString *)distanceToObject
+{
+ CLLocation * lastLocation = [MWMLocationManager lastLocation];
+ if (!lastLocation)
+ return @"";
+ string distance;
+ CLLocationCoordinate2D const coord = lastLocation.coordinate;
+ ms::LatLon const target = self.data.latLon;
+ measurement_utils::FormatDistance(
+ ms::DistanceOnEarth(coord.latitude, coord.longitude, target.lat, target.lon), distance);
+ return @(distance.c_str());
+}
+
+#pragma mark - MWMFrameworkStorageObserver
+
+- (void)processCountryEvent:(TCountryId const &)countryId
+{
+ if (countryId == kInvalidCountryId)
+ {
+ [_layout processDownloaderEventWithStatus:storage::NodeStatus::Undefined progress:0];
+ return;
+ }
+
+ if (self.data.countryId != countryId)
+ return;
+
+ NodeStatuses statuses;
+ GetFramework().GetStorage().GetNodeStatuses(countryId, statuses);
+
+ auto const status = statuses.m_status;
+ if (status == self.currentDownloaderStatus)
+ return;
+
+ self.currentDownloaderStatus = status;
+ [_layout processDownloaderEventWithStatus:status progress:0];
+}
+
+- (void)processCountry:(TCountryId const &)countryId progress:(MapFilesDownloader::TProgress const &)progress
+{
+ if (countryId == kInvalidCountryId || self.data.countryId != countryId)
+ return;
+
+ [_layout processDownloaderEventWithStatus:storage::NodeStatus::Downloading
+ progress:static_cast<CGFloat>(progress.first) / progress.second];
+}
+
+#pragma mark - MWMPlacePageLayout
+
+- (MWMPlacePageLayout *)layout
+{
+ if (!_layout)
+ {
+ _layout = [[MWMPlacePageLayout alloc] initWithOwnerView:self.ownerViewController.view
+ delegate:self
+ dataSource:self];
+ }
+
+ return _layout;
+}
+
+- (void)onTopBoundChanged:(CGFloat)bound
+{
+ [[MWMMapViewControlsManager manager] dragPlacePage:{{0, self.ownerViewController.view.height - bound}, {}}];
+}
+
+- (void)shouldDestroyLayout
+{
+ self.layout = nil;
+ GetFramework().DeactivateMapSelection(false);
+}
+
+#pragma mark - MWMLocationObserver
+
+- (void)onHeadingUpdate:(location::CompassInfo const &)info
+{
+ CLLocation * lastLocation = [MWMLocationManager lastLocation];
+ if (!lastLocation)
+ return;
+
+ CGFloat const angle = ang::AngleTo(lastLocation.mercator, self.data.mercator) + info.m_bearing;
+ [_layout rotateDirectionArrowToAngle:angle];
+}
+
+- (void)onLocationUpdate:(location::GpsInfo const &)locationInfo
+{
+ [_layout setDistanceToObject:self.distanceToObject];
+}
+
+- (void)mwm_refreshUI
+{
+ [_layout mwm_refreshUI];
+}
+
+- (void)dismissPlacePage
+{
+ [self closePlacePage];
+}
+
+- (void)hidePlacePage
+{
+ [self closePlacePage];
+}
+
+- (void)routeFrom
+{
+ [Statistics logEvent:kStatEventName(kStatPlacePage, kStatBuildRoute)
+ withParameters:@{kStatValue : kStatSource}];
+ [[MWMRouter router] buildFromPoint:self.target bestRouter:YES];
+ [self closePlacePage];
+}
+
+- (void)routeTo
+{
+ [Statistics logEvent:kStatEventName(kStatPlacePage, kStatBuildRoute)
+ withParameters:@{kStatValue : kStatDestination}];
+ [[MWMRouter router] buildToPoint:self.target bestRouter:YES];
+ [self closePlacePage];
+}
+
+- (MWMRoutePoint)target
+{
+ NSString * name = nil;
+ if (self.data.title.length > 0)
+ name = self.data.title;
+ else if (self.data.address.length > 0)
+ name = self.data.address;
+ else if (self.data.subtitle.length > 0)
+ name = self.data.subtitle;
+ else if (self.data.isBookmark)
+ name = self.data.externalTitle;
+ else
+ name = L(@"placepage_unknown_place");
+
+ m2::PointD const & org = self.data.mercator;
+ return self.data.isMyPosition ? MWMRoutePoint(org) : MWMRoutePoint(org, name);
+}
+
+- (void)share
+{
+ [Statistics logEvent:kStatEventName(kStatPlacePage, kStatShare)];
+ MWMActivityViewController * shareVC =
+ [MWMActivityViewController shareControllerForPlacePageObject:static_cast<id<MWMPlacePageObject>>(self.data)];
+ [shareVC presentInParentViewController:self.ownerViewController
+ anchorView:self.layout.shareAnchor];
+}
+
+- (void)editPlace
+{
+ [Statistics logEvent:kStatEventName(kStatPlacePage, kStatEdit)];
+ [[PushNotificationManager pushManager] setTags:@{ @"editor_edit_discovered" : @YES }];
+ [(MapViewController *)self.ownerViewController openEditor];
+}
+
+- (void)addBusiness
+{
+ [Statistics logEvent:kStatEditorAddClick withParameters:@{kStatValue : kStatPlacePage}];
+ [[MWMMapViewControlsManager manager] addPlace:YES hasPoint:NO point:m2::PointD()];
+}
+
+- (void)addPlace
+{
+ [Statistics logEvent:kStatEditorAddClick
+ withParameters:@{kStatValue : kStatPlacePageNonBuilding}];
+ [[MWMMapViewControlsManager manager] addPlace:NO hasPoint:YES point:self.data.mercator];
+}
+
+
+- (void)addBookmark
+{
+ [self.data updateBookmarkStatus:YES];
+ [self.layout reloadBookmarkSection:YES];
+}
+
+- (void)removeBookmark
+{
+ [Statistics logEvent:kStatEventName(kStatPlacePage, kStatBookmarks)
+ withParameters:@{kStatValue : kStatRemove}];
+ [self.data updateBookmarkStatus:NO];
+ [self.layout reloadBookmarkSection:NO];
+}
+
+- (void)editBookmark
+{
+ [[MapViewController controller] openBookmarkEditorWithData:self.data];
+}
+
+- (void)book:(BOOL)isDescription
+{
+ NSMutableDictionary * stat = [@{ kStatProvider : kStatBooking } mutableCopy];
+ MWMPlacePageData * data = self.data;
+ auto const latLon = data.latLon;
+ stat[kStatHotel] = data.hotelId;
+ stat[kStatHotelLat] = @(latLon.lat);
+ stat[kStatHotelLon] = @(latLon.lon);
+ [Statistics logEvent:isDescription ? kPlacePageHotelDetails : kPlacePageHotelBook
+ withParameters:stat
+ atLocation:[MWMLocationManager lastLocation]];
+
+ UIViewController * vc = static_cast<UIViewController *>([MapViewController controller]);
+ NSURL * url = isDescription ? self.data.bookingDescriptionURL : self.data.bookingURL;
+ NSAssert(url, @"Booking url can't be nil!");
+ [vc openUrl:url];
+}
+
+- (void)call
+{
+ NSAssert(self.data.phoneNumber, @"Phone number can't be nil!");
+ NSString * phoneNumber = [[@"telprompt:" stringByAppendingString:self.data.phoneNumber]
+ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ [[UIApplication sharedApplication] openURL:[NSURL URLWithString:phoneNumber]];
+}
+
+- (void)apiBack
+{
+ [Statistics logEvent:kStatEventName(kStatPlacePage, kStatAPI)];
+ [[UIApplication sharedApplication] openURL:[NSURL URLWithString:self.data.apiURL]];
+ [[MapViewController controller].apiBar back];
+}
+
+- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
+{
+ [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
+ [self->_layout layoutWithSize:size];
+ } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) { }];
+}
+
+#pragma mark - MWMFeatureHolder
+
+- (FeatureID const &)featureId
+{
+ return self.data.featureId;
+}
+
+#pragma mark - Deprecated
+
+@synthesize leftBound = _leftBound;
+@synthesize topBound = _topBound;
+- (void)setTopBound:(CGFloat)topBound {_topBound = 0;}
+- (void)setLeftBound:(CGFloat)leftBound {_leftBound = 0;}
+- (void)addSubviews:(NSArray *)views withNavigationController:(UINavigationController *)controller {}
+- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {}
+
+@end
diff --git a/iphone/Maps/Classes/MWMPlacePagePreviewCell.h b/iphone/Maps/Classes/MWMPlacePagePreviewCell.h
new file mode 100644
index 0000000000..dde52ca55b
--- /dev/null
+++ b/iphone/Maps/Classes/MWMPlacePagePreviewCell.h
@@ -0,0 +1,24 @@
+#import "MWMTableViewCell.h"
+
+@class MWMPlacePageData;
+@class MWMCircularProgress;
+
+@protocol MWMPlacePageCellUpdateProtocol;
+@protocol MWMPlacePageLayoutDataSource;
+
+@interface MWMPlacePagePreviewCell : MWMTableViewCell
+
+- (void)setDistanceToObject:(NSString *)distance;
+- (void)rotateDirectionArrowToAngle:(CGFloat)angle;
+
+- (void)setDownloaderViewHidden:(BOOL)isHidden animated:(BOOL)isAnimated;
+- (void)setDownloadingProgress:(CGFloat)progress;
+
+- (void)configure:(MWMPlacePageData *)data
+ updateLayoutDelegate:(id<MWMPlacePageCellUpdateProtocol>)delegate
+ dataSource:(id<MWMPlacePageLayoutDataSource>)dataSource;
+
+
+- (MWMCircularProgress *)mapDownloadProgress;
+
+@end
diff --git a/iphone/Maps/Classes/MWMPlacePagePreviewCell.mm b/iphone/Maps/Classes/MWMPlacePagePreviewCell.mm
new file mode 100644
index 0000000000..952140042d
--- /dev/null
+++ b/iphone/Maps/Classes/MWMPlacePagePreviewCell.mm
@@ -0,0 +1,426 @@
+#import "Common.h"
+#import "MWMDirectionView.h"
+#import "MWMPlacePageCellUpdateProtocol.h"
+#import "MWMPlacePageData.h"
+#import "MWMPlacePageLayout.h"
+#import "MWMPlacePagePreviewCell.h"
+#import "MWMCircularProgress.h"
+#import "UIColor+MapsmeColor.h"
+
+#include "std/array.hpp"
+#include "std/vector.hpp"
+
+namespace
+{
+array<NSString *, 6> kPPPClasses = {{@"_MWMPPPTitle",
+ @"_MWMPPPExternalTitle",
+ @"_MWMPPPSubtitle",
+ @"_MWMPPPSchedule",
+ @"_MWMPPPBooking",
+ @"_MWMPPPAddress"}};
+
+enum class Labels
+{
+ Title,
+ ExternalTitle,
+ Subtitle,
+ Schedule,
+ Booking,
+ Address
+};
+
+void * kContext = &kContext;
+NSString * const kTableViewContentSizeKeyPath = @"contentSize";
+CGFloat const kDefaultTableViewLeading = 16;
+CGFloat const kCompressedTableViewLeading = 56;
+
+} // namespace
+
+#pragma mark - Base
+
+// Base class for avoiding copy-paste in inheriting cells.
+@interface _MWMPPPCellBase : MWMTableViewCell
+
+@property(weak, nonatomic) IBOutlet UILabel * distance;
+@property(weak, nonatomic) IBOutlet UIImageView * compass;
+@property(weak, nonatomic) IBOutlet UIView * distanceView;
+@property(weak, nonatomic) IBOutlet NSLayoutConstraint * trailing;
+@property(copy, nonatomic) TMWMVoidBlock tapOnDistance;
+
+@end
+
+@implementation _MWMPPPCellBase
+
+- (IBAction)tap
+{
+ if (self.tapOnDistance)
+ self.tapOnDistance();
+}
+
+@end
+
+#pragma mark - Title
+
+@interface _MWMPPPTitle : _MWMPPPCellBase
+
+@property(weak, nonatomic) IBOutlet UILabel * title;
+
+@end
+
+@implementation _MWMPPPTitle
+@end
+
+#pragma mark - External Title
+
+@interface _MWMPPPExternalTitle : _MWMPPPCellBase
+
+@property(weak, nonatomic) IBOutlet UILabel * externalTitle;
+
+@end
+
+@implementation _MWMPPPExternalTitle
+@end
+
+#pragma mark - Subtitle
+
+@interface _MWMPPPSubtitle : _MWMPPPCellBase
+
+@property(weak, nonatomic) IBOutlet UILabel * subtitle;
+
+@end
+
+@implementation _MWMPPPSubtitle
+@end
+
+#pragma mark - Schedule
+
+@interface _MWMPPPSchedule : _MWMPPPCellBase
+
+@property(weak, nonatomic) IBOutlet UILabel * schedule;
+
+@end
+
+@implementation _MWMPPPSchedule
+@end
+
+#pragma mark - Booking
+
+@interface _MWMPPPBooking : MWMTableViewCell
+
+@property(weak, nonatomic) IBOutlet UILabel * rating;
+@property(weak, nonatomic) IBOutlet UILabel * pricing;
+
+@end
+
+@implementation _MWMPPPBooking
+@end
+
+#pragma mark - Address
+
+@interface _MWMPPPAddress : _MWMPPPCellBase
+
+@property(weak, nonatomic) IBOutlet UILabel * address;
+
+@end
+
+@implementation _MWMPPPAddress
+@end
+
+#pragma mark - Public
+
+@interface MWMPlacePagePreviewCell () <UITableViewDelegate, UITableViewDataSource, MWMCircularProgressProtocol>
+{
+ vector<Labels> m_cells;
+}
+
+@property(weak, nonatomic) IBOutlet UITableView * tableView;
+@property(weak, nonatomic) IBOutlet NSLayoutConstraint * tableViewHeight;
+@property(weak, nonatomic) NSLayoutConstraint * trailing;
+@property(weak, nonatomic) UIView * distanceView;
+
+@property(weak, nonatomic) IBOutlet UIView * downloaderParentView;
+@property(weak, nonatomic) IBOutlet NSLayoutConstraint * tableViewLeading;
+
+@property(nonatomic) MWMCircularProgress * mapDownloadProgress;
+
+@property(nonatomic) BOOL isDirectionViewAvailable;
+
+@property(weak, nonatomic) MWMPlacePageData * data;
+@property(weak, nonatomic) id<MWMPlacePageCellUpdateProtocol> delegate;
+@property(weak, nonatomic) id<MWMPlacePageLayoutDataSource> dataSource;
+
+@property(copy, nonatomic) NSString * distance;
+@property(weak, nonatomic) UIImageView * compass;
+@property(nonatomic) CGFloat currentContentHeight;
+
+@property(nonatomic) MWMDirectionView * directionView;
+
+@end
+
+@implementation MWMPlacePagePreviewCell
+
+- (void)awakeFromNib
+{
+ [super awakeFromNib];
+ for (auto const s : kPPPClasses)
+ [self.tableView registerNib:[UINib nibWithNibName:s bundle:nil] forCellReuseIdentifier:s];
+
+ self.tableView.estimatedRowHeight = 20;
+ self.tableView.rowHeight = UITableViewAutomaticDimension;
+ [self registerObserver];
+}
+
+- (void)dealloc
+{
+ [self unregisterObserver];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath
+ ofObject:(id)object
+ change:(NSDictionary *)change
+ context:(void *)context
+{
+ if (context == kContext)
+ {
+ NSValue * s = change[@"new"];
+ CGFloat const height = s.CGSizeValue.height;
+ if (abs(height - self.currentContentHeight) > 0.5)
+ {
+ self.currentContentHeight = height;
+ self.tableViewHeight.constant = height;
+ [self setNeedsLayout];
+ [self.delegate updateCellWithForceReposition:YES];
+ }
+ return;
+ }
+
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+}
+
+- (MWMDirectionView *)directionView
+{
+ if (!_directionView)
+ _directionView = [[MWMDirectionView alloc] initWithManager:nil];
+ return _directionView;
+}
+
+- (void)setIsDirectionViewAvailable:(BOOL)isDirectionViewAvailable
+{
+ if (_isDirectionViewAvailable == isDirectionViewAvailable)
+ return;
+ _isDirectionViewAvailable = isDirectionViewAvailable;
+ [self setNeedsLayout];
+}
+
+- (void)rotateDirectionArrowToAngle:(CGFloat)angle
+{
+ self.compass.layer.transform = CATransform3DMakeRotation(M_PI_2 - angle, 0, 0, 1);
+ self.directionView.directionArrow.layer.transform = CATransform3DMakeRotation(M_PI_2 - angle, 0, 0, 1);
+}
+
+- (void)setDistanceToObject:(NSString *)distance
+{
+ if (!distance.length)
+ {
+ self.isDirectionViewAvailable = NO;
+ [self setNeedsLayout];
+ return;
+ }
+
+ if ([self.distance isEqualToString:distance])
+ return;
+
+ self.distance = distance;
+ self.directionView.distanceLabel.text = distance;
+ self.isDirectionViewAvailable = YES;
+}
+
+- (void)unregisterObserver
+{
+ [self.tableView removeObserver:self forKeyPath:kTableViewContentSizeKeyPath context:kContext];
+}
+
+- (void)registerObserver
+{
+ [self.tableView addObserver:self forKeyPath:kTableViewContentSizeKeyPath options:NSKeyValueObservingOptionNew context:kContext];
+}
+
+- (void)setDownloadingProgress:(CGFloat)progress
+{
+ self.mapDownloadProgress.progress = progress;
+}
+
+- (void)setDownloaderViewHidden:(BOOL)isHidden animated:(BOOL)isAnimated
+{
+ self.downloaderParentView.hidden = isHidden;
+ self.tableViewLeading.constant = isHidden ? kDefaultTableViewLeading : kCompressedTableViewLeading;
+ [self setNeedsLayout];
+
+ if (!isHidden)
+ self.mapDownloadProgress.state = MWMCircularProgressStateNormal;
+
+ if (!isAnimated)
+ return;
+
+ [UIView animateWithDuration:kDefaultAnimationDuration animations:^{
+ [self layoutIfNeeded];
+ }];
+}
+
+- (void)configure:(MWMPlacePageData *)data
+ updateLayoutDelegate:(id<MWMPlacePageCellUpdateProtocol>)delegate
+ dataSource:(id<MWMPlacePageLayoutDataSource>)dataSource
+{
+ self.data = data;
+ self.delegate = delegate;
+ self.dataSource = dataSource;
+ [self setDistanceToObject:dataSource.distanceToObject];
+
+ m_cells.clear();
+
+ if (data.title.length)
+ m_cells.push_back(Labels::Title);
+
+ if (data.externalTitle.length)
+ m_cells.push_back(Labels::ExternalTitle);
+
+ if (data.subtitle.length)
+ m_cells.push_back(Labels::Subtitle);
+
+ if (data.schedule != place_page::OpeningHours::Unknown)
+ m_cells.push_back(Labels::Schedule);
+
+ if (data.bookingRating.length)
+ m_cells.push_back(Labels::Booking);
+
+ if (data.address.length)
+ m_cells.push_back(Labels::Address);
+
+ [self.tableView reloadData];
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
+{
+ return m_cells.size();
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ auto data = self.data;
+ _MWMPPPCellBase * c = nil;
+ BOOL const isNeedToShowDistance = self.isDirectionViewAvailable && (indexPath.row == m_cells.size() - 1);
+
+ switch (m_cells[indexPath.row])
+ {
+ case Labels::Title:
+ {
+ c = [tableView dequeueReusableCellWithIdentifier:[_MWMPPPTitle className]];
+ static_cast<_MWMPPPTitle *>(c).title.text = data.title;
+ break;
+ }
+ case Labels::ExternalTitle:
+ {
+ c = [tableView dequeueReusableCellWithIdentifier:[_MWMPPPExternalTitle className]];
+ static_cast<_MWMPPPExternalTitle *>(c).externalTitle.text = data.externalTitle;
+ break;
+ }
+ case Labels::Subtitle:
+ {
+ c = [tableView dequeueReusableCellWithIdentifier:[_MWMPPPSubtitle className]];
+ static_cast<_MWMPPPSubtitle *>(c).subtitle.text = data.subtitle;
+ break;
+ }
+ case Labels::Schedule:
+ {
+ c = [tableView dequeueReusableCellWithIdentifier:[_MWMPPPSchedule className]];
+ auto castedCell = static_cast<_MWMPPPSchedule *>(c);
+ switch(data.schedule)
+ {
+ case place_page::OpeningHours::AllDay:
+ castedCell.schedule.text = L(@"twentyfour_seven");
+ castedCell.schedule.textColor = [UIColor blackSecondaryText];
+ break;
+ case place_page::OpeningHours::Open:
+ castedCell.schedule.text = L(@"editor_time_open");
+ castedCell.schedule.textColor = [UIColor blackSecondaryText];
+ break;
+ case place_page::OpeningHours::Closed:
+ castedCell.schedule.text = L(@"closed_now");
+ castedCell.schedule.textColor = [UIColor red];
+ break;
+ case place_page::OpeningHours::Unknown:
+ NSAssert(false, @"Incorrect schedule!");
+ break;
+ }
+ break;
+ }
+ case Labels::Booking:
+ {
+ _MWMPPPBooking * c = [tableView dequeueReusableCellWithIdentifier:[_MWMPPPBooking className]];
+ c.rating.text = data.bookingRating;
+ c.pricing.text = data.bookingApproximatePricing;
+ [data assignOnlinePriceToLabel:c.pricing];
+ return c;
+ }
+ case Labels::Address:
+ {
+ c = [tableView dequeueReusableCellWithIdentifier:[_MWMPPPAddress className]];
+ static_cast<_MWMPPPAddress *>(c).address.text = data.address;
+ break;
+ }
+ }
+
+ if (isNeedToShowDistance)
+ [self showDistanceOnCell:c];
+ else
+ [self hideDistanceOnCell:c];
+
+ return c;
+}
+
+- (void)showDistanceOnCell:(_MWMPPPCellBase *)cell
+{
+ cell.trailing.priority = UILayoutPriorityDefaultLow;
+ cell.distance.text = self.distance;
+ cell.tapOnDistance = ^{ [self.directionView show]; };
+ [cell.contentView setNeedsLayout];
+ self.compass = cell.compass;
+ self.trailing = cell.trailing;
+ self.distanceView = cell.distanceView;
+ cell.distanceView.hidden = NO;
+}
+
+- (void)hideDistanceOnCell:(_MWMPPPCellBase *)cell
+{
+ cell.trailing.priority = UILayoutPriorityDefaultHigh;
+ [cell.contentView setNeedsLayout];
+ cell.distanceView.hidden = YES;
+}
+
+#pragma mark - MWMCircularProgressProtocol
+
+- (void)progressButtonPressed:(MWMCircularProgress *)progress
+{
+ [self.dataSource downloadSelectedArea];
+}
+
+#pragma mark - Properties
+
+- (MWMCircularProgress *)mapDownloadProgress
+{
+ if (!_mapDownloadProgress)
+ {
+ _mapDownloadProgress =
+ [MWMCircularProgress downloaderProgressForParentView:self.downloaderParentView];
+ _mapDownloadProgress.delegate = self;
+
+ MWMCircularProgressStateVec const affectedStates = {MWMCircularProgressStateNormal,
+ MWMCircularProgressStateSelected};
+
+ [_mapDownloadProgress setImage:[UIImage imageNamed:@"ic_download"] forStates:affectedStates];
+ [_mapDownloadProgress setColoring:MWMButtonColoringBlue forStates:affectedStates];
+ }
+ return _mapDownloadProgress;
+}
+
+@end
diff --git a/iphone/Maps/Classes/MWMPlacePageProtocol.h b/iphone/Maps/Classes/MWMPlacePageProtocol.h
new file mode 100644
index 0000000000..af0e6a4c35
--- /dev/null
+++ b/iphone/Maps/Classes/MWMPlacePageProtocol.h
@@ -0,0 +1,58 @@
+#import "MWMMapViewControlsManager.h"
+
+#include "Framework.h"
+
+@class MWMPlacePageEntity, MWMViewController;
+
+@protocol MWMActionBarProtocol <NSObject>
+
+- (void)routeFrom;
+- (void)routeTo;
+
+- (void)share;
+
+- (void)addBookmark;
+- (void)removeBookmark;
+
+- (void)call;
+- (void)book:(BOOL)isDecription;
+
+- (void)apiBack;
+
+@end
+
+@protocol MWMPlacePageButtonsProtocol <NSObject>
+
+- (void)editPlace;
+- (void)addPlace;
+- (void)addBusiness;
+- (void)book:(BOOL)isDescription;
+- (void)editBookmark;
+
+@end
+
+struct FeatureID;
+
+@protocol MWMFeatureHolder <NSObject>
+
+- (FeatureID const &)featureId;
+
+@end
+
+@protocol MWMPlacePageProtocol <MWMActionBarProtocol, MWMPlacePageButtonsProtocol, MWMFeatureHolder>
+
+@property(weak, nonatomic, readonly) MWMViewController * ownerViewController;
+@property(nonatomic) CGFloat topBound;
+@property(nonatomic) CGFloat leftBound;
+
+- (instancetype)initWithViewController:(MWMViewController *)viewController;
+- (void)showPlacePage:(place_page::Info const &)info;
+- (void)mwm_refreshUI;
+- (void)dismissPlacePage;
+- (void)hidePlacePage;
+- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation;
+- (void)viewWillTransitionToSize:(CGSize)size
+ withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator;
+- (void)addSubviews:(NSArray *)views withNavigationController:(UINavigationController *)controller;
+
+@end
diff --git a/iphone/Maps/Classes/MWMPlacePageViewManager.h b/iphone/Maps/Classes/MWMPlacePageViewManager.h
index 9f23e20ead..37e46f782a 100644
--- a/iphone/Maps/Classes/MWMPlacePageViewManager.h
+++ b/iphone/Maps/Classes/MWMPlacePageViewManager.h
@@ -2,16 +2,16 @@
#include "Framework.h"
+#include "MWMPlacePageProtocol.h"
+
@class MWMPlacePageEntity, MWMPlacePageNavigationBar, MWMViewController;
-@interface MWMPlacePageViewManager : NSObject
+@interface MWMPlacePageViewManager : NSObject <MWMPlacePageProtocol>
@property(weak, nonatomic, readonly) MWMViewController * ownerViewController;
@property(nonatomic, readonly) MWMPlacePageEntity * entity;
-@property(nonatomic) MWMPlacePageNavigationBar * iPhoneNavigationBar;
@property(nonatomic) CGFloat topBound;
@property(nonatomic) CGFloat leftBound;
-@property(nonatomic, readonly) BOOL isDirectionViewShown;
- (instancetype)initWithViewController:(MWMViewController *)viewController;
- (void)showPlacePage:(place_page::Info const &)info;
@@ -37,7 +37,6 @@
- (void)reloadBookmark;
- (void)dragPlacePage:(CGRect)frame;
- (void)showDirectionViewWithTitle:(NSString *)title type:(NSString *)type;
-- (void)hideDirectionView;
- (void)addSubviews:(NSArray *)views withNavigationController:(UINavigationController *)controller;
- (void)changeHeight:(CGFloat)height;
diff --git a/iphone/Maps/Classes/MWMPlacePageViewManager.mm b/iphone/Maps/Classes/MWMPlacePageViewManager.mm
index d886d64d42..f5033f8128 100644
--- a/iphone/Maps/Classes/MWMPlacePageViewManager.mm
+++ b/iphone/Maps/Classes/MWMPlacePageViewManager.mm
@@ -70,6 +70,11 @@ extern NSString * const kBookmarksChangedNotification;
[self configPlacePage];
}
+- (FeatureID const &)featureId
+{
+ return self.entity.featureID;
+}
+
#pragma mark - Layout
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation
@@ -211,7 +216,7 @@ extern NSString * const kBookmarksChangedNotification;
[Alohalytics logEvent:kAlohalyticsTapEventKey withValue:@"ppShare"];
MWMPlacePageEntity * entity = self.entity;
MWMActivityViewController * shareVC =
- [MWMActivityViewController shareControllerForPlacePageObject:entity];
+ [MWMActivityViewController shareControllerForPlacePageObject:static_cast<id<MWMPlacePageObject>>(entity)];
[shareVC presentInParentViewController:self.ownerViewController
anchorView:self.placePage.actionBar.shareAnchor];
}
@@ -220,7 +225,7 @@ extern NSString * const kBookmarksChangedNotification;
{
NSMutableDictionary * stat = [@{ kStatProvider : kStatBooking } mutableCopy];
MWMPlacePageEntity * en = self.entity;
- auto const latLon = en.latlon;
+ auto const latLon = en.latLon;
stat[kStatHotel] = en.hotelId;
stat[kStatHotelLat] = @(latLon.lat);
stat[kStatHotelLon] = @(latLon.lon);
@@ -229,7 +234,7 @@ extern NSString * const kBookmarksChangedNotification;
atLocation:[MWMLocationManager lastLocation]];
UIViewController * vc = static_cast<UIViewController *>([MapViewController controller]);
- NSURL * url = isDescription ? self.entity.bookingDescriptionUrl : self.entity.bookingUrl;
+ NSURL * url = isDescription ? self.entity.bookingDescriptionURL : self.entity.bookingURL;
NSAssert(url, @"Booking url can't be nil!");
[vc openUrl:url];
}
@@ -276,10 +281,10 @@ extern NSString * const kBookmarksChangedNotification;
withParameters:@{kStatValue : kStatAdd}];
Framework & f = GetFramework();
BookmarkData bmData = {self.entity.titleForNewBookmark, f.LastEditedBMType()};
- size_t const categoryIndex = f.LastEditedBMCategory();
- size_t const bookmarkIndex =
- f.GetBookmarkManager().AddBookmark(categoryIndex, self.entity.mercator, bmData);
- self.entity.bac = {categoryIndex, bookmarkIndex};
+ auto const categoryIndex = static_cast<int>(f.LastEditedBMCategory());
+ auto const bookmarkIndex =
+ static_cast<int>(f.GetBookmarkManager().AddBookmark(categoryIndex, self.entity.mercator, bmData));
+ self.entity.bac = {bookmarkIndex, categoryIndex};
self.entity.bookmarkTitle = @(bmData.GetName().c_str());
self.entity.bookmarkCategory = @(f.GetBmCategory(categoryIndex)->GetName().c_str());
[NSNotificationCenter.defaultCenter postNotificationName:kBookmarksChangedNotification
@@ -294,16 +299,16 @@ extern NSString * const kBookmarksChangedNotification;
[Statistics logEvent:kStatEventName(kStatPlacePage, kStatBookmarks)
withParameters:@{kStatValue : kStatRemove}];
Framework & f = GetFramework();
- BookmarkCategory * bookmarkCategory = f.GetBookmarkManager().GetBmCategory(self.entity.bac.first);
+ BookmarkCategory * bookmarkCategory = f.GetBookmarkManager().GetBmCategory(self.entity.bac.m_categoryIndex);
if (bookmarkCategory)
{
{
BookmarkCategory::Guard guard(*bookmarkCategory);
- guard.m_controller.DeleteUserMark(self.entity.bac.second);
+ guard.m_controller.DeleteUserMark(self.entity.bac.m_bookmarkIndex);
}
bookmarkCategory->SaveToKMLFile();
}
- self.entity.bac = MakeEmptyBookmarkAndCategory();
+ self.entity.bac = {};
self.entity.bookmarkTitle = nil;
self.entity.bookmarkCategory = nil;
[NSNotificationCenter.defaultCenter postNotificationName:kBookmarksChangedNotification
@@ -335,7 +340,7 @@ extern NSString * const kBookmarksChangedNotification;
return @"";
string distance;
CLLocationCoordinate2D const coord = lastLocation.coordinate;
- ms::LatLon const target = self.entity.latlon;
+ ms::LatLon const target = self.entity.latLon;
measurement_utils::FormatDistance(
ms::DistanceOnEarth(coord.latitude, coord.longitude, target.lat, target.lon), distance);
return @(distance.c_str());
@@ -344,26 +349,12 @@ extern NSString * const kBookmarksChangedNotification;
- (void)showDirectionViewWithTitle:(NSString *)title type:(NSString *)type
{
MWMDirectionView * directionView = self.directionView;
- UIView * ownerView = self.ownerViewController.view;
directionView.titleLabel.text = title;
directionView.typeLabel.text = type;
- [ownerView addSubview:directionView];
- [ownerView endEditing:YES];
- [directionView setNeedsLayout];
- MapsAppDelegate * app = [MapsAppDelegate theApp];
- [app.mapViewController updateStatusBarStyle];
- [app disableStandby];
+ [directionView show];
[self updateDistance];
}
-- (void)hideDirectionView
-{
- [self.directionView removeFromSuperview];
- MapsAppDelegate * app = [MapsAppDelegate theApp];
- [app.mapViewController updateStatusBarStyle];
- [app enableStandby];
-}
-
- (void)changeHeight:(CGFloat)height
{
if (!IPAD)
@@ -399,7 +390,7 @@ extern NSString * const kBookmarksChangedNotification;
return _directionView;
}
-- (BOOL)isDirectionViewShown { return self.directionView.superview != nil; }
- (void)setTopBound:(CGFloat)topBound { _topBound = self.placePage.topBound = topBound; }
- (void)setLeftBound:(CGFloat)leftBound { _leftBound = self.placePage.leftBound = leftBound; }
+- (void)editBookmark {}
@end
diff --git a/iphone/Maps/Classes/MapViewController.h b/iphone/Maps/Classes/MapViewController.h
index 2bde266871..aaa01fb826 100644
--- a/iphone/Maps/Classes/MapViewController.h
+++ b/iphone/Maps/Classes/MapViewController.h
@@ -12,6 +12,7 @@ struct AddressInfo;
@class MWMMapViewControlsManager;
@class MWMAPIBar;
+@class MWMPlacePageData;
@interface MapViewController : MWMViewController
@@ -31,6 +32,7 @@ struct AddressInfo;
- (void)openBookmarks;
- (void)openMapsDownloader:(mwm::DownloaderMode)mode;
- (void)openEditor;
+- (void)openBookmarkEditorWithData:(MWMPlacePageData *)data;
- (void)initialize;
diff --git a/iphone/Maps/Classes/MapViewController.mm b/iphone/Maps/Classes/MapViewController.mm
index acb07db231..6448ee8cdf 100644
--- a/iphone/Maps/Classes/MapViewController.mm
+++ b/iphone/Maps/Classes/MapViewController.mm
@@ -19,7 +19,9 @@
#import "MWMMapDownloaderViewController.h"
#import "MWMMapViewControlsManager.h"
#import "MWMPageController.h"
+#import "MWMPlacePageData.h"
#import "MWMPlacePageEntity.h"
+#import "MWMPlacePageProtocol.h"
#import "MWMRouter.h"
#import "MWMRouterSavedState.h"
#import "MWMSettings.h"
@@ -70,6 +72,8 @@ NSString * const kDownloaderSegue = @"Map2MapDownloaderSegue";
NSString * const kMigrationSegue = @"Map2MigrationSegue";
NSString * const kEditorSegue = @"Map2EditorSegue";
NSString * const kUDViralAlertWasShown = @"ViralAlertWasShown";
+NSString * const kPP2BookmarkEditingSegue = @"PP2BookmarkEditing";
+
// The first launch after process started. Used to skip "Not follow, no position" state and to run
// locator.
@@ -416,7 +420,8 @@ BOOL gIsFirstMyPositionMode = YES;
- (void)openEditor
{
using namespace osm_auth_ios;
- auto const & featureID = self.controlsManager.placePageEntity.info.GetID();
+
+ auto const & featureID = self.controlsManager.featureHolder.featureId;
[Statistics logEvent:kStatEditorEditStart
withParameters:@{
@@ -425,7 +430,12 @@ BOOL gIsFirstMyPositionMode = YES;
kStatEditorMWMName : @(featureID.GetMwmName().c_str()),
kStatEditorMWMVersion : @(featureID.GetMwmVersion())
}];
- [self performSegueWithIdentifier:kEditorSegue sender:self.controlsManager.placePageEntity];
+ [self performSegueWithIdentifier:kEditorSegue sender:self.controlsManager.featureHolder];
+}
+
+- (void)openBookmarkEditorWithData:(MWMPlacePageData *)data
+{
+ [self performSegueWithIdentifier:kPP2BookmarkEditingSegue sender:data];
}
- (void)processMyPositionStateModeEvent:(location::EMyPositionMode)mode
@@ -552,7 +562,12 @@ BOOL gIsFirstMyPositionMode = YES;
if ([segue.identifier isEqualToString:kEditorSegue])
{
MWMEditorViewController * dvc = segue.destinationViewController;
- [dvc setFeatureToEdit:static_cast<MWMPlacePageEntity *>(sender).featureID];
+ [dvc setFeatureToEdit:static_cast<id<MWMFeatureHolder>>(sender).featureId];
+ }
+ else if ([segue.identifier isEqualToString:kPP2BookmarkEditingSegue])
+ {
+ MWMEditBookmarkController * dvc = segue.destinationViewController;
+ dvc.data = static_cast<MWMPlacePageData *>(sender);
}
else if ([segue.identifier isEqualToString:kDownloaderSegue])
{
diff --git a/iphone/Maps/Classes/Share/MWMActivityViewController.h b/iphone/Maps/Classes/Share/MWMActivityViewController.h
index 14b2d9f266..46e6741fbe 100644
--- a/iphone/Maps/Classes/Share/MWMActivityViewController.h
+++ b/iphone/Maps/Classes/Share/MWMActivityViewController.h
@@ -1,4 +1,4 @@
-@class MWMPlacePageEntity;
+@protocol MWMPlacePageObject;
@interface MWMActivityViewController : UIActivityViewController
@@ -6,7 +6,7 @@
+ (instancetype)shareControllerForMyPosition:(CLLocationCoordinate2D const &)location;
-+ (instancetype)shareControllerForPlacePageObject:(MWMPlacePageEntity *)entity;
++ (instancetype)shareControllerForPlacePageObject:(id<MWMPlacePageObject>)object;
- (void)presentInParentViewController:(UIViewController *)parentVC anchorView:(UIView *)anchorView;
diff --git a/iphone/Maps/Classes/Share/MWMActivityViewController.mm b/iphone/Maps/Classes/Share/MWMActivityViewController.mm
index a9e9237019..40b689b646 100644
--- a/iphone/Maps/Classes/Share/MWMActivityViewController.mm
+++ b/iphone/Maps/Classes/Share/MWMActivityViewController.mm
@@ -32,10 +32,10 @@
return [[self alloc] initWithActivityItem:item];
}
-+ (instancetype)shareControllerForPlacePageObject:(MWMPlacePageEntity *)entity
++ (instancetype)shareControllerForPlacePageObject:(id<MWMPlacePageObject>)object;
{
MWMShareActivityItem * item =
- [[MWMShareActivityItem alloc] initForPlacePageObjectWithEntity:entity];
+ [[MWMShareActivityItem alloc] initForPlacePageObject:object];
return [[self alloc] initWithActivityItem:item];
}
diff --git a/iphone/Maps/Classes/Share/MWMShareActivityItem.h b/iphone/Maps/Classes/Share/MWMShareActivityItem.h
index 22be5da8eb..8e7b0826b8 100644
--- a/iphone/Maps/Classes/Share/MWMShareActivityItem.h
+++ b/iphone/Maps/Classes/Share/MWMShareActivityItem.h
@@ -1,8 +1,25 @@
-@class MWMPlacePageEntity;
+namespace ms
+{
+class LatLon;
+
+} // namespace ms
+
+@protocol MWMPlacePageObject <NSObject>
+
+- (BOOL)isMyPosition;
+- (BOOL)isBooking;
+- (NSString *)title;
+- (NSString *)subtitle;
+- (NSString *)address;
+- (NSURL *)bookingDescriptionURL;
+- (NSString *)phoneNumber;
+- (ms::LatLon)latLon;
+
+@end
@interface MWMShareActivityItem : NSObject<UIActivityItemSource>
- (instancetype)initForMyPositionAtLocation:(CLLocationCoordinate2D const &)location;
-- (instancetype)initForPlacePageObjectWithEntity:(MWMPlacePageEntity *)entity;
+- (instancetype)initForPlacePageObject:(id<MWMPlacePageObject>)object;
@end
diff --git a/iphone/Maps/Classes/Share/MWMShareActivityItem.mm b/iphone/Maps/Classes/Share/MWMShareActivityItem.mm
index b68bf35518..1693884555 100644
--- a/iphone/Maps/Classes/Share/MWMShareActivityItem.mm
+++ b/iphone/Maps/Classes/Share/MWMShareActivityItem.mm
@@ -15,7 +15,7 @@ NSString * httpGe0Url(NSString * shortUrl)
@interface MWMShareActivityItem ()
-@property(nonatomic) MWMPlacePageEntity * entity;
+@property(nonatomic) id<MWMPlacePageObject> object;
@property(nonatomic) CLLocationCoordinate2D location;
@property(nonatomic) BOOL isMyPosition;
@@ -34,16 +34,16 @@ NSString * httpGe0Url(NSString * shortUrl)
return self;
}
-- (instancetype)initForPlacePageObjectWithEntity:(MWMPlacePageEntity *)entity
+- (instancetype)initForPlacePageObject:(id<MWMPlacePageObject>)object
{
self = [super init];
if (self)
{
- NSAssert(entity, @"Entity can't be nil!");
- BOOL const isMyPosition = entity.isMyPosition;
+ NSAssert(object, @"Entity can't be nil!");
+ BOOL const isMyPosition = object.isMyPosition;
_isMyPosition = isMyPosition;
if (!isMyPosition)
- _entity = entity;
+ _object = object;
}
return self;
}
@@ -52,22 +52,22 @@ NSString * httpGe0Url(NSString * shortUrl)
{
auto & f = GetFramework();
- auto const title = ^NSString *(MWMPlacePageEntity * entity)
+ auto const title = ^NSString *(id<MWMPlacePageObject> obj)
{
- if (!entity || entity.isMyPosition)
+ if (!obj || obj.isMyPosition)
return L(@"my_position");
- else if (entity.title.length)
- return entity.title;
- else if (entity.subtitle.length)
- return entity.subtitle;
- else if (entity.address.length)
- return entity.address;
+ else if (obj.title.length)
+ return obj.title;
+ else if (obj.subtitle.length)
+ return obj.subtitle;
+ else if (obj.address.length)
+ return obj.address;
else
return @"";
};
- ms::LatLon const ll = self.entity ? self.entity.latlon : ms::LatLon(self.location.latitude, self.location.longitude);
- string const s = f.CodeGe0url(ll.lat, ll.lon, f.GetDrawScale(), title(self.entity).UTF8String);
+ ms::LatLon const ll = self.object ? self.object.latLon : ms::LatLon(self.location.latitude, self.location.longitude);
+ string const s = f.CodeGe0url(ll.lat, ll.lon, f.GetDrawScale(), title(self.object).UTF8String);
NSString * url = @(s.c_str());
if (!isShort)
@@ -108,7 +108,7 @@ NSString * httpGe0Url(NSString * shortUrl)
NSString * shortUrl = [self url:YES];
return [NSString stringWithFormat:@"%@\n%@", httpGe0Url(shortUrl),
self.isMyPosition ? L(@"my_position_share_email_subject")
- : self.entity.title];
+ : self.object.title];
}
- (NSString *)itemDefaultWithActivityType:(NSString *)activityType
@@ -123,13 +123,13 @@ NSString * httpGe0Url(NSString * shortUrl)
}
NSMutableString * result = [L(@"sharing_call_action_look") mutableCopy];
- vector<NSString *> strings{self.entity.title, self.entity.subtitle, self.entity.address,
- [self.entity getCellValue:MWMPlacePageCellTypePhoneNumber], url};
+ vector<NSString *> strings{self.object.title, self.object.subtitle, self.object.address,
+ self.object.phoneNumber, url};
- if (self.entity.isBooking)
+ if (self.object.isBooking)
{
strings.push_back(L(@"sharing_booking"));
- strings.push_back(self.entity.bookingDescriptionUrl.absoluteString);
+ strings.push_back(self.object.bookingDescriptionURL.absoluteString);
}
for (auto const str : strings)