diff --git a/iphone/Maps/Classes/MapViewController.mm b/iphone/Maps/Classes/MapViewController.mm index 3ca85630cd..de8abd3ae8 100644 --- a/iphone/Maps/Classes/MapViewController.mm +++ b/iphone/Maps/Classes/MapViewController.mm @@ -1,5 +1,4 @@ #import "MapViewController.h" -#import "SearchVC.h" #import "MapsAppDelegate.h" #import "EAGLView.h" #import "BookmarksRootVC.h" @@ -19,6 +18,8 @@ #import "InAppMessagesManager.h" #import "InterstitialView.h" #import "MoreAppsVC.h" +#import "PlacePageView.h" +#import "SearchView.h" #import "../Settings/SettingsManager.h" #import "../../Common/CustomAlertView.h" @@ -42,12 +43,13 @@ const long long PRO_IDL = 510623322L; const long long LITE_IDL = 431183278L; -@interface MapViewController () +@interface MapViewController () @property (nonatomic) LocationButton * locationButton; -@property (nonatomic) UINavigationBar * apiNavigationBar; @property (nonatomic) ShareActionSheet * shareActionSheet; @property (nonatomic) UIButton * buyButton; +@property (nonatomic) PlacePageView * placePageView; +@property (nonatomic) SearchView * searchView; @end @@ -142,18 +144,20 @@ const long long LITE_IDL = 431183278L; #pragma mark - Map Navigation -- (void)positionBallonClickedLat:(double)lat lon:(double)lon +- (void)poiBalloonClicked:(m2::PointD const &)point info:(search::AddressInfo const &)addressInfo { - CGPoint const p = CGPointMake(MercatorBounds::LonToX(lon), MercatorBounds::LatToY(lat)); - PlacePreviewViewController * preview = [[PlacePreviewViewController alloc] initWithPoint:p]; - m_popoverPos = p; - [self pushViewController:preview]; + [self showPlacePageWithPoint:point addressInfo:addressInfo]; +} + +- (void)poiBalloonDismissed +{ + [self.placePageView setState:PlacePageStateHidden animated:YES]; } - (void)additionalLayer:(size_t)index { Framework & framework = GetFramework(); - Bookmark const * bookmark = framework.AdditionalPoiLayerGetBookmark(index); + Bookmark * bookmark = framework.AdditionalPoiLayerGetBookmark(index); [self.placePageView showBookmark:*bookmark]; [self.placePageView setState:PlacePageStateBitShown animated:YES]; @@ -165,12 +169,21 @@ const long long LITE_IDL = 431183278L; [self.placePageView setState:PlacePageStateBitShown animated:YES]; } -- (void)bookmarkBalloonClicked:(BookmarkAndCategory const &)bmAndCat +- (void)bookmarkBalloonClicked:(BookmarkAndCategory const &)bookmarkAndCategory { - PlacePageVC * vc = [[PlacePageVC alloc] initWithBookmark:bmAndCat]; - Bookmark const * bm = GetFramework().GetBmCategory(bmAndCat.first)->GetBookmark(bmAndCat.second); - m_popoverPos = CGPointMake(bm->GetOrg().x, bm->GetOrg().y); - [self pushViewController:vc]; + [self showPlacePageWithBookmarkAndCategory:bookmarkAndCategory]; +} + +- (void)showPlacePageWithPoint:(m2::PointD)point addressInfo:(search::AddressInfo)addressInfo +{ + [self.placePageView showPoint:point addressInfo:addressInfo]; + [self.placePageView setState:PlacePageStateBitShown animated:YES]; +} + +- (void)showPlacePageWithBookmarkAndCategory:(BookmarkAndCategory)bookmarkAndCategory +{ + [self.placePageView showBookmarkAndCategory:bookmarkAndCategory]; + [self.placePageView setState:PlacePageStateBitShown animated:YES]; } - (void)onMyPositionClicked:(id)sender @@ -281,26 +294,6 @@ const long long LITE_IDL = 431183278L; [self invalidate]; } -- (void)showSearchResultAsBookmarkAtMercatorPoint:(const m2::PointD &)pt withInfo:(const search::AddressInfo &)info -{ - -} - -- (void)showBalloonWithCategoryIndex:(int)cat andBookmarkIndex:(int)bm -{ - -} - -- (void)poiBalloonDismissed -{ - -} - -- (void)additionalLayer:(size_t)index -{ - -} - - (void)updatePointsFromEvent:(UIEvent *)event { NSSet * allTouches = [event allTouches]; @@ -388,6 +381,8 @@ const long long LITE_IDL = 431183278L; if (!self.sideToolbar.isMenuHidden) [self.sideToolbar setMenuHidden:YES animated:YES]; + if (self.placePageView.state == PlacePageStateOpened) + [self.placePageView setState:PlacePageStateBitShown animated:YES]; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event @@ -611,11 +606,21 @@ const long long LITE_IDL = 431183278L; } #endif - [self.view addSubview:self.apiNavigationBar]; [self.view addSubview:self.locationButton]; + + [self.view addSubview:self.searchView]; + [self.view addSubview:self.fadeView]; + + UIButton * slideView = [self slideView]; + [self.view addSubview:slideView]; + + [self.view addSubview:self.placePageView]; + [self.view addSubview:self.sideToolbar]; + self.sideToolbar.slideView = slideView; + [self.sideToolbar setMenuHidden:YES animated:NO]; } @@ -650,10 +655,10 @@ const long long LITE_IDL = 431183278L; typedef void (*POSITIONBalloonFnT)(id, SEL, double, double); typedef void (*POIBalloonFnT)(id, SEL, m2::PointD const &, search::AddressInfo const &); + typedef void (*POIBalloonDismissedFnT)(id, SEL); typedef void (*APIPOINTBalloonFnT)(id, SEL, url_scheme::ApiPoint const &); typedef void (*BOOKMARKBalloonFnT)(id, SEL, BookmarkAndCategory const &); - typedef void (*POIBalloonDismissedFnT)(id, SEL); - typedef void (*ADDITIONALLayerFnT)(id, SEL, size_t); + typedef void (*ADDITIONALLayerFnT)(id, SEL, size_t index); PinClickManager & manager = f.GetBalloonManager(); @@ -715,8 +720,8 @@ const long long LITE_IDL = 431183278L; { _placePageView = [[PlacePageView alloc] initWithFrame:CGRectMake(0, 0, self.view.width, 0)]; _placePageView.minY = self.view.height; - _placePageView.delegate = self; _placePageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; + _placePageView.delegate = self; [_placePageView addObserver:self forKeyPath:@"state" options:NSKeyValueObservingOptionNew context:nil]; } return _placePageView; @@ -740,7 +745,7 @@ const long long LITE_IDL = 431183278L; if (!_locationButton) { _locationButton = [[LocationButton alloc] initWithFrame:CGRectMake(0, 0, 60, 60)]; - _locationButton.center = CGPointMake(28, self.view.height - 28); + _locationButton.center = CGPointMake(28, LOCATION_BUTTON_MID_Y); _locationButton.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin; [_locationButton addTarget:self action:@selector(onMyPositionClicked:) forControlEvents:UIControlEventTouchUpInside]; } @@ -777,37 +782,44 @@ const long long LITE_IDL = 431183278L; } #define SLIDE_VIEW_DARK_PART_TAG 1 +#define SLIDE_VIEW_MID_Y (self.view.height - 35) - (SideToolbar *)sideToolbar { if (!_sideToolbar) { - _sideToolbar = [[SideToolbar alloc] initWithFrame:CGRectMake(self.view.width, 0, 260, self.view.height)]; + _sideToolbar = [[SideToolbar alloc] initWithFrame:CGRectMake(self.view.width, 0, 310, self.view.height)]; _sideToolbar.delegate = self; + } + return _sideToolbar; +} - UIButton * toolbarButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 50, 70)]; - toolbarButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin; - toolbarButton.maxX = self.view.width; - toolbarButton.midY = self.view.height - 35; - [toolbarButton addTarget:self action:@selector(toolbarButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; +- (UIButton *)slideView +{ + UIButton * toolbarButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 50, 70)]; + toolbarButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin; + toolbarButton.maxX = self.view.width; + toolbarButton.midY = SLIDE_VIEW_MID_Y; + [toolbarButton addTarget:self action:@selector(toolbarButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; - CGFloat tailShift = 7; + CGFloat tailShift = 7; - UIImageView * tailLight = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SlideViewLight"]]; - tailLight.maxX = toolbarButton.width; - tailLight.midY = toolbarButton.height / 2 + tailShift; - [toolbarButton addSubview:tailLight]; + UIImageView * tailLight = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SlideViewLight"]]; + tailLight.maxX = toolbarButton.width; + tailLight.midY = toolbarButton.height / 2 + tailShift; + [toolbarButton addSubview:tailLight]; - UIImageView * tailDark = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SlideViewDark"]]; - tailDark.maxX = toolbarButton.width; - tailDark.midY = toolbarButton.height / 2 + tailShift; - tailDark.alpha = 0; - tailDark.tag = SLIDE_VIEW_DARK_PART_TAG; - [toolbarButton addSubview:tailDark]; + UIImageView * tailDark = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SlideViewDark"]]; + tailDark.maxX = toolbarButton.width; + tailDark.midY = toolbarButton.height / 2 + tailShift; + tailDark.alpha = 0; + tailDark.tag = SLIDE_VIEW_DARK_PART_TAG; + [toolbarButton addSubview:tailDark]; - [self.view addSubview:toolbarButton]; + return toolbarButton; +} - _sideToolbar.slideView = toolbarButton; +#pragma mark - PlacePageViewDelegate - (void)placePageView:(PlacePageView *)placePage willEditBookmarkAndCategory:(BookmarkAndCategory const &)bookmarkAndCategory { @@ -819,17 +831,10 @@ const long long LITE_IDL = 431183278L; - (void)placePageView:(PlacePageView *)placePage willEditBookmarkWithInfo:(search::AddressInfo const &)addressInfo point:(m2::PointD const &)point { - if (!_apiNavigationBar) - { - CGFloat height = SYSTEM_VERSION_IS_LESS_THAN(@"7") ? 44 : 64; - _apiNavigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, self.view.width, height)]; - UINavigationItem * item = [[UINavigationItem alloc] init]; - _apiNavigationBar.items = @[item]; - _apiNavigationBar.autoresizingMask = UIViewAutoresizingFlexibleWidth; - _apiNavigationBar.topItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"back", nil) style: UIBarButtonItemStyleDone target:self action:@selector(returnToApiApp)]; - _apiNavigationBar.alpha = 0; - } - return _apiNavigationBar; + PlacePageVC * vc = [[PlacePageVC alloc] initWithInfo:addressInfo point:CGPointMake(point.x, point.y)]; + vc.delegate = self; + vc.mode = PlacePageVCModeEditing; + [self pushViewController:vc]; } - (void)placePageView:(PlacePageView *)placePage willShareInfo:(search::AddressInfo const &)addressInfo point:(m2::PointD const &)point @@ -847,26 +852,13 @@ const long long LITE_IDL = 431183278L; #pragma mark - SideToolbarDelegate -- (void)sideToolbar:(SideToolbar *)toolbar didPressButtonAtIndex:(NSInteger)buttonIndex +- (void)sideToolbar:(SideToolbar *)toolbar didPressItemWithName:(NSString *)itemName { - if (buttonIndex == 0) - { - if (GetPlatform().IsPro()) - { - SearchVC * vc = [[SearchVC alloc] init]; - [self.navigationController pushViewController:vc animated:YES]; - } - else - { - [[Statistics instance] logProposalReason:@"Search Screen" withAnswer:@"YES"]; - [[UIApplication sharedApplication] openProVersion]; - } - } - else if (buttonIndex == 1) + if ([itemName isEqualToString:@"Maps"]) { [[[MapsAppDelegate theApp] settingsManager] show:self]; } - else if (buttonIndex == 2) + else if ([itemName isEqualToString:@"Bookmarks"]) { if (GetPlatform().IsPro()) { @@ -879,12 +871,12 @@ const long long LITE_IDL = 431183278L; [[UIApplication sharedApplication] openProVersion]; } } - else if (buttonIndex == 3) + else if ([itemName isEqualToString:@"Settings"]) { SettingsViewController * vc = [self.mainStoryboard instantiateViewControllerWithIdentifier:[SettingsViewController className]]; [self.navigationController pushViewController:vc animated:YES]; } - else if (buttonIndex == 4) + else if ([itemName isEqualToString:@"Share"]) { CLLocation * location = [MapsAppDelegate theApp].m_locationManager.lastLocation; if (location) @@ -901,13 +893,41 @@ const long long LITE_IDL = 431183278L; [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"unknown_current_position", nil) message:nil delegate:nil cancelButtonTitle:NSLocalizedString(@"ok", nil) otherButtonTitles:nil] show]; } } - else if (buttonIndex == 5) + else if ([itemName isEqualToString:@"MoreApps"]) { MoreAppsVC * vc = [[MoreAppsVC alloc] init]; [self.navigationController pushViewController:vc animated:YES]; } } +- (void)sideToolbarDidCloseMenu:(SideToolbar *)toolbar +{ + [self.view bringSubviewToFront:self.placePageView]; +} + +- (void)sideToolbarWillCloseMenu:(SideToolbar *)toolbar +{ + [UIView animateWithDuration:0.25 animations:^{ + if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) + [self setNeedsStatusBarAppearanceUpdate]; + self.fadeView.alpha = 0; + UIView * darkTail = [self.sideToolbar.slideView viewWithTag:SLIDE_VIEW_DARK_PART_TAG]; + darkTail.alpha = 0; + }]; +} + +- (void)sideToolbarWillOpenMenu:(SideToolbar *)toolbar +{ + [UIView animateWithDuration:0.25 animations:^{ + if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) + [self setNeedsStatusBarAppearanceUpdate]; + self.fadeView.alpha = 1; + UIView * darkTail = [self.sideToolbar.slideView viewWithTag:SLIDE_VIEW_DARK_PART_TAG]; + darkTail.alpha = 1; + }]; + [self.view sendSubviewToBack:self.placePageView]; +} + - (void)sideToolbarDidUpdateShift:(SideToolbar *)toolbar { self.fadeView.alpha = toolbar.menuShift / (toolbar.maximumMenuShift - toolbar.minimumMenuShift); @@ -955,15 +975,13 @@ const long long LITE_IDL = 431183278L; - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if (object == self.sideToolbar && [keyPath isEqualToString:@"isMenuHidden"]) + if (object == self.searchView && [keyPath isEqualToString:@"active"]) { - [UIView animateWithDuration:0.25 animations:^{ - if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) - [self setNeedsStatusBarAppearanceUpdate]; - self.fadeView.alpha = self.sideToolbar.isMenuHidden ? 0 : 1; - UIView * darkTail = [self.sideToolbar.slideView viewWithTag:SLIDE_VIEW_DARK_PART_TAG]; - darkTail.alpha = self.sideToolbar.isMenuHidden ? 0 : 1; + [UIView animateWithDuration:0.2 animations:^{ + self.sideToolbar.slideView.maxX = self.searchView.active ? self.view.width + self.sideToolbar.slideView.width : self.view.width; }]; + if (self.searchView.active) + [self.placePageView setState:PlacePageStateHidden animated:YES]; } else if (object == self.placePageView && [keyPath isEqualToString:@"state"]) { diff --git a/iphone/Maps/Classes/SearchActivityProtocol.h b/iphone/Maps/Classes/SearchActivityProtocol.h new file mode 100644 index 0000000000..a4add1a760 --- /dev/null +++ b/iphone/Maps/Classes/SearchActivityProtocol.h @@ -0,0 +1,9 @@ + +#import + +@protocol SearchActivityProtocol + +- (void)setActive:(BOOL)active animated:(BOOL)animated; +@property (nonatomic, readonly) BOOL active; + +@end diff --git a/iphone/Maps/Classes/SearchBar.h b/iphone/Maps/Classes/SearchBar.h new file mode 100644 index 0000000000..4330449cc8 --- /dev/null +++ b/iphone/Maps/Classes/SearchBar.h @@ -0,0 +1,22 @@ + +#import +#import "SearchActivityProtocol.h" + +@class SearchBar; +@protocol SearchBarDelegate + +- (void)searchBarDidPressCancelButton:(SearchBar *)searchBar; +- (void)searchBarDidPressClearButton:(SearchBar *)searchBar; + +@end + +@interface SearchBar : UIView + +@property (nonatomic, readonly) UITextField * textField; +@property (nonatomic) BOOL isShowingResult; +@property (nonatomic, weak) id delegate; +@property (nonatomic) NSString * apiText; + +- (void)setSearching:(BOOL)searching; + +@end diff --git a/iphone/Maps/Classes/SearchBar.mm b/iphone/Maps/Classes/SearchBar.mm new file mode 100644 index 0000000000..c1786f259e --- /dev/null +++ b/iphone/Maps/Classes/SearchBar.mm @@ -0,0 +1,349 @@ + +#import "SearchBar.h" +#import "UIKitCategories.h" +#import "Framework.h" + +@interface SearchSpotView : UIView + +- (void)setAnimating:(BOOL)animating; + +@end + +@interface SearchSpotView () + +@property (nonatomic) UIImageView * spotImageView; +@property (nonatomic) UIActivityIndicatorView * activityView; +@property (nonatomic) NSTimer * timer; + +@end + +@implementation SearchSpotView + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + [self addSubview:self.activityView]; + [self addSubview:self.spotImageView]; + + return self; +} + +- (void)setAnimating:(BOOL)animating +{ + if (animating) + { + self.timer = [NSTimer scheduledTimerWithTimeInterval:0.15 target:self selector:@selector(timerSelector) userInfo:nil repeats:NO]; + } + else + { + [self.activityView stopAnimating]; + self.spotImageView.hidden = NO; + [self.timer invalidate]; + } +} + +- (void)timerSelector +{ + [self.activityView startAnimating]; + self.spotImageView.hidden = YES; +} + +- (UIActivityIndicatorView *)activityView +{ + if (!_activityView) + { + _activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + _activityView.center = CGPointMake(self.width / 2, self.height / 2); + } + return _activityView; +} + +- (UIImageView *)spotImageView +{ + if (!_spotImageView) + { + _spotImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SearchBarIcon"]]; + _spotImageView.center = CGPointMake(self.width / 2, self.height / 2); + } + return _spotImageView; +} + +@end + +@interface SearchBar () + +@property (nonatomic) UITextField * textField; +@property (nonatomic) SearchSpotView * searchImageView; +@property (nonatomic) UILabel * searchLabel; +@property (nonatomic) UIImageView * backgroundImageView; +@property (nonatomic) UIButton * clearButton; +@property (nonatomic) UIButton * cancelButton; + +@end + +@implementation SearchBar +@synthesize active = _active; + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + [self addSubview:self.backgroundImageView]; + + self.clearButton.midY = self.backgroundImageView.height / 2; + self.clearButton.maxX = self.backgroundImageView.width; + [self.backgroundImageView addSubview:self.clearButton]; + + [self addSubview:self.searchImageView]; + [self addSubview:self.searchLabel]; + + self.textField.midY = self.backgroundImageView.height / 2; + CGFloat fieldOffsetL = 39; + CGFloat fieldOffsetR = 24; + self.textField.frame = CGRectMake(fieldOffsetL, self.textField.origin.y, self.backgroundImageView.width - fieldOffsetL - fieldOffsetR, self.textField.height); + [self.backgroundImageView addSubview:self.textField]; + + self.cancelButton.midY = self.backgroundImageView.height / 2; + [self addSubview:self.cancelButton]; + + [self setActive:NO animated:NO]; + + if ([UITextField instancesRespondToSelector:@selector(setTintColor:)]) + [[UITextField appearance] setTintColor:[UIColor darkGrayColor]]; + + return self; +} + +- (void)layoutSubviews +{ + [self performAfterDelay:0 block:^{ + if (!self.active) + [self layoutImageAndLabelInNonActiveState]; + }]; +} + +- (void)setSearching:(BOOL)searching +{ + self.searchImageView.animating = searching; +} + +#define LABEL_MIN_X 23 + +- (void)setActive:(BOOL)active animated:(BOOL)animated +{ + CGFloat backgroundImageOffset = 10; + if (active) + { + [UIView animateWithDuration:(animated ? 0.1 : 0) delay:0.1 options:UIViewAnimationOptionCurveEaseInOut animations:^{ + self.textField.alpha = 1; + } completion:nil]; + + [UIView animateWithDuration:(animated ? 0.35 : 0) delay:0 damping:0.8 initialVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{ + if (self.isShowingResult) + { + self.searchLabel.minX = 47; + } + else + { + CGFloat spaceBetween = self.searchLabel.minX - self.searchImageView.minX; + self.searchImageView.minX = LABEL_MIN_X; + self.searchLabel.minX = self.searchImageView.minX + spaceBetween; + } + + self.searchImageView.alpha = 1; + self.searchLabel.alpha = 0; + + self.backgroundImageView.frame = CGRectMake(backgroundImageOffset, 0, self.width - backgroundImageOffset - self.cancelButton.width, self.backgroundImageView.height); + + self.cancelButton.alpha = 1; + self.cancelButton.maxX = self.width; + + self.clearButton.alpha = 1; + + } completion:nil]; + + [self performAfterDelay:0.1 block:^{ + [self.textField becomeFirstResponder]; + }]; + } + else + { + [UIView animateWithDuration:(animated ? 0.1 : 0) animations:^{ + self.textField.alpha = 0; + }]; + + [UIView animateWithDuration:(animated ? 0.45 : 0) delay:0 damping:0.8 initialVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{ + self.backgroundImageView.frame = CGRectMake(backgroundImageOffset, 0, self.width - 2 * backgroundImageOffset, self.backgroundImageView.height); + + self.searchLabel.text = self.isShowingResult ? self.textField.text : NSLocalizedString(@"search", nil); + [self.searchLabel sizeToFit]; + + [self layoutImageAndLabelInNonActiveState]; + + self.cancelButton.alpha = 0; + self.cancelButton.minX = self.width; + + if (!self.isShowingResult) + self.clearButton.alpha = 0; + + [self.textField resignFirstResponder]; + } completion:nil]; + } + _active = active; +} + +- (void)layoutImageAndLabelInNonActiveState +{ + self.searchLabel.alpha = 1; + if (self.isShowingResult) + { + self.searchLabel.midX = self.searchLabel.superview.width / 2; + self.searchImageView.alpha = 0; + self.searchImageView.minX = LABEL_MIN_X; + self.searchLabel.width = MIN(self.searchLabel.width, self.backgroundImageView.width - 54); + } + else + { + CGFloat width = self.searchImageView.width + 8 + self.searchLabel.width; + self.searchImageView.midY = self.backgroundImageView.height / 2; + self.searchImageView.minX = self.backgroundImageView.width / 2 - width / 2 + self.backgroundImageView.minX; + self.searchImageView.alpha = 1; + self.searchLabel.midY = self.backgroundImageView.height / 2; + self.searchLabel.maxX = self.backgroundImageView.width / 2 + width / 2 + self.backgroundImageView.minX; + } +} + +- (void)cancelButtonPressed:(id)sender +{ + [self.delegate searchBarDidPressCancelButton:self]; +} + +- (void)clearButtonPressed:(id)sender +{ + Framework & framework = GetFramework(); + GetFramework().ClearMapApiPoints(); + if (self.active) + { + self.textField.text = nil; + self.isShowingResult = NO; + [self.textField becomeFirstResponder]; + } + else + { + [self hideSearchedText]; + } + [self.delegate searchBarDidPressClearButton:self]; + + framework.GetBookmarkManager().AdditionalPoiLayerClear(); + framework.GetBalloonManager().RemovePin(); + framework.GetBalloonManager().Dismiss(); + framework.Invalidate(); +} + +- (void)hideSearchedText +{ + self.textField.text = nil; + self.isShowingResult = NO; + self.searchLabel.text = NSLocalizedString(@"search", nil); + [self.searchLabel sizeToFit]; + [UIView animateWithDuration:0.4 delay:0 damping:0.8 initialVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{ + [self layoutImageAndLabelInNonActiveState]; + self.clearButton.alpha = 0; + } completion:nil]; +} + +- (void)setApiText:(NSString *)apiText +{ + if (apiText) + { + self.textField.text = apiText; + self.isShowingResult = YES; + [self setActive:NO animated:YES]; + self.clearButton.alpha = 1; + } + else if (_apiText) + { + [self hideSearchedText]; + GetFramework().ClearMapApiPoints(); + } + _apiText = apiText; +} + +- (UILabel *)searchLabel +{ + if (!_searchLabel) + { + _searchLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 25)]; + _searchLabel.textColor = [UIColor blackColor]; + _searchLabel.backgroundColor = [UIColor clearColor]; + _searchLabel.textAlignment = NSTextAlignmentLeft; + _searchLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:15]; + } + return _searchLabel; +} + +- (SearchSpotView *)searchImageView +{ + if (!_searchImageView) + { + _searchImageView = [[SearchSpotView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)]; + _searchImageView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin; + } + return _searchImageView; +} + +- (UIButton *)cancelButton +{ + if (!_cancelButton) + { + _cancelButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 64, 44)]; + _cancelButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin; + _cancelButton.titleLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:15]; + [_cancelButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; + [_cancelButton setTitleColor:[UIColor grayColor] forState:UIControlStateHighlighted]; + [_cancelButton setTitle:NSLocalizedString(@"cancel", nil) forState:UIControlStateNormal]; + [_cancelButton addTarget:self action:@selector(cancelButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; + } + return _cancelButton; +} + +- (UIButton *)clearButton +{ + if (!_clearButton) + { + _clearButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 32, 44)]; + _clearButton.contentMode = UIViewContentModeCenter; + _clearButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; + [_clearButton setImage:[UIImage imageNamed:@"SearchBarClearButton"] forState:UIControlStateNormal]; + [_clearButton addTarget:self action:@selector(clearButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; + } + return _clearButton; +} + +- (UIImageView *)backgroundImageView +{ + if (!_backgroundImageView) + { + UIImage * image = [[UIImage imageNamed:@"SearchBarInactiveBackground"] resizableImageWithCapInsets:UIEdgeInsetsMake(6, 6, 6, 6)]; + _backgroundImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.width, image.size.height)]; + _backgroundImageView.image = image; + _backgroundImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _backgroundImageView.userInteractionEnabled = YES; + } + return _backgroundImageView; +} + +- (UITextField *)textField +{ + if (!_textField) + { + _textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, self.width, 22)]; + _textField.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _textField.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:15]; + _textField.returnKeyType = UIReturnKeySearch; + } + return _textField; +} + +@end diff --git a/iphone/Maps/Classes/SearchCell.h b/iphone/Maps/Classes/SearchCell.h index 1da3360344..0647902b77 100644 --- a/iphone/Maps/Classes/SearchCell.h +++ b/iphone/Maps/Classes/SearchCell.h @@ -1,12 +1,15 @@ + #import +typedef NS_ENUM(NSUInteger, SearchCellPosition) { + SearchCellPositionFirst = 1, + SearchCellPositionMiddle = 2, + SearchCellPositionLast = 3, + SearchCellPositionAlone = 4, +}; + @interface SearchCell : UITableViewCell -@property (nonatomic, readonly) UILabel * featureName; -@property (nonatomic, readonly) UILabel * featureType; -@property (nonatomic, readonly) UILabel * featureCountry; -@property (nonatomic, readonly) UILabel * featureDistance; - -- (id)initWithReuseIdentifier:(NSString *)identifier; +@property (nonatomic) SearchCellPosition position; @end diff --git a/iphone/Maps/Classes/SearchCell.m b/iphone/Maps/Classes/SearchCell.m new file mode 100644 index 0000000000..3b41b22a3e --- /dev/null +++ b/iphone/Maps/Classes/SearchCell.m @@ -0,0 +1,99 @@ + +#import "SearchCell.h" +#import "UIKitCategories.h" + +@interface SearchCell () + +@property (nonatomic) UIImageView * separatorImageView; +@property (nonatomic) UIImageView * backgroundImageView; + +@end + +@implementation SearchCell + +- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier +{ + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + + self.backgroundColor = [UIColor clearColor]; + self.selectionStyle = UITableViewCellSelectionStyleNone; + [self addSubview:self.backgroundImageView]; + [self.contentView addSubview:self.separatorImageView]; + + return self; +} + +- (void)setPosition:(SearchCellPosition)position +{ + if (position == _position) + return; + + UIImage * image; + switch (position) + { + case SearchCellPositionFirst: + image = [UIImage imageNamed:@"SearchCellBackgroundTop"]; + break; + case SearchCellPositionMiddle: + image = [UIImage imageNamed:@"SearchCellBackgroundMiddle"]; + break; + case SearchCellPositionLast: + image = [UIImage imageNamed:@"SearchCellBackgroundBottom"]; + break; + case SearchCellPositionAlone: + image = [UIImage imageNamed:@"SearchCellBackgroundAlone"]; + break; + } + self.backgroundImageView.image = [image resizableImageWithCapInsets:UIEdgeInsetsMake(6, 6, 6, 6)]; + + _position = position; +} + +- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated +{ + [super setHighlighted:highlighted animated:animated]; + self.backgroundImageView.alpha = highlighted ? 0.5 : 1; +} + +- (void)setSelected:(BOOL)selected animated:(BOOL)animated +{ + [super setSelected:selected animated:animated]; + self.backgroundImageView.alpha = selected ? 0.5 : 1; +} + +- (void)layoutSubviews +{ + self.backgroundImageView.size = CGSizeMake(self.width - 28, self.height); + self.backgroundImageView.midX = self.width / 2; + [self sendSubviewToBack:self.backgroundImageView]; + + self.contentView.frame = self.backgroundImageView.frame; + + self.separatorImageView.width = self.contentView.width; + self.separatorImageView.maxY = self.contentView.height; + self.separatorImageView.midX = self.contentView.width / 2; + self.separatorImageView.hidden = (self.position == SearchCellPositionLast || self.position == SearchCellPositionAlone); +} + +- (UIImageView *)backgroundImageView +{ + if (!_backgroundImageView) + { + _backgroundImageView = [[UIImageView alloc] initWithFrame:CGRectZero]; + _backgroundImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth; + } + return _backgroundImageView; +} + +- (UIImageView *)separatorImageView +{ + if (!_separatorImageView) + { + UIImage * image = [[UIImage imageNamed:@"SearchCellSeparator"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 40, 0, 40)]; + _separatorImageView = [[UIImageView alloc] initWithImage:image]; + _separatorImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth; + } + return _separatorImageView; +} + +@end diff --git a/iphone/Maps/Classes/SearchCell.mm b/iphone/Maps/Classes/SearchCell.mm deleted file mode 100644 index ddcf48ea86..0000000000 --- a/iphone/Maps/Classes/SearchCell.mm +++ /dev/null @@ -1,79 +0,0 @@ -#import "SearchCell.h" - -@implementation SearchCell - -- (id)initWithReuseIdentifier:(NSString *)identifier -{ - self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; - if (self) - { - // Fonts and sizes are hard-coded because they can't be easily retrieved - UIFont * large = [UIFont fontWithName:@"Helvetica-Bold" size:[UIFont labelFontSize] + 1]; - UIFont * small = [UIFont fontWithName:@"Helvetica" size:[UIFont systemFontSize]]; - - _featureName = [[UILabel alloc] init]; - _featureName.font = large; - _featureType = [[UILabel alloc] init]; - _featureType.font = small; - _featureType.textColor = [UIColor grayColor]; - _featureType.textAlignment = UITextAlignmentRight; - _featureCountry = [[UILabel alloc] init]; - _featureCountry.font = small; - _featureCountry.textColor = [UIColor grayColor]; - _featureDistance = [[UILabel alloc] init]; - _featureDistance.font = small; - _featureDistance.textColor = [UIColor grayColor]; - _featureDistance.textAlignment = UITextAlignmentRight; - - [self.contentView addSubview:_featureName]; - [self.contentView addSubview:_featureType]; - [self.contentView addSubview:_featureCountry]; - [self.contentView addSubview:_featureDistance]; - } - return self; -} - -- (void)layoutSubviews -{ - [super layoutSubviews]; - CGRect r = self.contentView.bounds; - // Leave some experimentally choosen paddings - CGFloat const KPaddingX = 9.0; - CGFloat const KPaddingBottom = 1.0; - - r.origin.x += KPaddingX; - r.size.width -= 2 * KPaddingX; - r.size.height -= KPaddingBottom; - - // Labels on the right should always fit and be visible, but not more than half of the cell - CGFloat const w = r.size.width; - CGFloat const h = r.size.height; - CGFloat const yDelim = (int)(r.origin.y + h / 5 * 3); - - CGFloat xTopDelim = (int)(r.origin.x + w / 2); - CGFloat xBottomDelim = xTopDelim; - if ([_featureDistance.text length]) - { - CGSize const distanceTextSize = [_featureDistance.text sizeWithFont:_featureDistance.font]; - if (xTopDelim + distanceTextSize.width < r.origin.x + w) - xTopDelim = r.origin.x + w - distanceTextSize.width - KPaddingX; - } - else // Sometimes distance is not available, so use full cell length for the name - { - xTopDelim = r.origin.x + w - KPaddingX; - } - - _featureName.frame = CGRectMake(r.origin.x, r.origin.y, xTopDelim - r.origin.x, yDelim - r.origin.y); - _featureDistance.frame = CGRectMake(xTopDelim, r.origin.y, r.origin.x + w - xTopDelim, yDelim - r.origin.y); - - if ([_featureType.text length]) - { - CGSize const typeTextSize = [_featureType.text sizeWithFont:_featureType.font]; - if (xBottomDelim + typeTextSize.width < r.origin.x + w) - xBottomDelim = r.origin.x + w - typeTextSize.width - KPaddingX; - } - _featureCountry.frame = CGRectMake(r.origin.x, yDelim, xBottomDelim - r.origin.x, r.origin.y + h - yDelim); - _featureType.frame = CGRectMake(xBottomDelim, yDelim, r.origin.x + w - xBottomDelim, r.origin.y + h - yDelim); -} - -@end diff --git a/iphone/Maps/Classes/SearchUniversalCell.h b/iphone/Maps/Classes/SearchUniversalCell.h new file mode 100644 index 0000000000..383f9a3fd9 --- /dev/null +++ b/iphone/Maps/Classes/SearchUniversalCell.h @@ -0,0 +1,13 @@ + +#import +#import "SearchCell.h" + +@interface SearchUniversalCell : SearchCell + +@property (nonatomic, readonly) UILabel * titleLabel; +@property (nonatomic, readonly) UILabel * subtitleLabel; +@property (nonatomic, readonly) UILabel * distanceLabel; + ++ (CGFloat)cellHeightWithTitle:(NSString *)title subtitle:(NSString *)subtitle viewWidth:(CGFloat)width; + +@end diff --git a/iphone/Maps/Classes/SearchUniversalCell.m b/iphone/Maps/Classes/SearchUniversalCell.m new file mode 100644 index 0000000000..b1611d4cad --- /dev/null +++ b/iphone/Maps/Classes/SearchUniversalCell.m @@ -0,0 +1,93 @@ + +#import "SearchUniversalCell.h" +#import "UIKitCategories.h" + +@interface SearchUniversalCell () + +@property (nonatomic) UILabel * titleLabel; +@property (nonatomic) UILabel * subtitleLabel; +@property (nonatomic) UILabel * distanceLabel; + +@end + +@implementation SearchUniversalCell + +- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier +{ + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + + [self.contentView addSubview:self.titleLabel]; + [self.contentView addSubview:self.subtitleLabel]; + [self.contentView addSubview:self.distanceLabel]; + + return self; +} + +#define TITLE_FONT [UIFont fontWithName:@"HelveticaNeue-Light" size:15] +#define TITLE_WIDTH_REST 150 +#define TITLE_HEIGHT 60 + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + self.distanceLabel.maxX = self.width - 45; + self.distanceLabel.midY = self.height / 2 - 0.5; + + self.titleLabel.width = self.width - TITLE_WIDTH_REST; + self.titleLabel.minX = 16; + [self.titleLabel sizeToFit]; + if ([self.subtitleLabel.text length]) + self.titleLabel.minY = 5; + else + self.titleLabel.midY = self.height / 2 - 1.5; + self.subtitleLabel.origin = CGPointMake(self.titleLabel.minX, self.titleLabel.maxY - 1); +} + ++ (CGFloat)cellHeightWithTitle:(NSString *)title subtitle:(NSString *)subtitle viewWidth:(CGFloat)width +{ + return MAX(44, [title sizeWithDrawSize:CGSizeMake(width - TITLE_WIDTH_REST, TITLE_HEIGHT) font:TITLE_FONT].height + ([subtitle length] ? 27 : 15)); +} + +- (UILabel *)distanceLabel +{ + if (!_distanceLabel) + { + _distanceLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 70, 16)]; + _distanceLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; + _distanceLabel.backgroundColor = [UIColor clearColor]; + _distanceLabel.font = [UIFont fontWithName:@"HelveticaNeue" size:15]; + _distanceLabel.textColor = [UIColor blackColor]; + _distanceLabel.textAlignment = NSTextAlignmentRight; + _distanceLabel.alpha = 0.5; + } + return _distanceLabel; +} + +- (UILabel *)subtitleLabel +{ + if (!_subtitleLabel) + { + _subtitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 190, 16)]; + _subtitleLabel.backgroundColor = [UIColor clearColor]; + _subtitleLabel.font = [UIFont fontWithName:@"HelveticaNeue" size:10]; + _subtitleLabel.textColor = [UIColor colorWithColorCode:@"666666"]; + } + return _subtitleLabel; +} + +- (UILabel *)titleLabel +{ + if (!_titleLabel) + { + _titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 0, TITLE_HEIGHT)]; + _titleLabel.backgroundColor = [UIColor clearColor]; + _titleLabel.font = TITLE_FONT; + _titleLabel.textColor = [UIColor blackColor]; + _titleLabel.numberOfLines = 0; + _titleLabel.lineBreakMode = NSLineBreakByWordWrapping; + } + return _titleLabel; +} + +@end diff --git a/iphone/Maps/Classes/SearchVC.h b/iphone/Maps/Classes/SearchVC.h deleted file mode 100644 index 223526f9e2..0000000000 --- a/iphone/Maps/Classes/SearchVC.h +++ /dev/null @@ -1,24 +0,0 @@ -#import -#import "LocationManager.h" -#import "ScopeView.h" - -class Framework; - -@interface SearchVC : UIViewController - -{ - Framework * m_framework; - LocationManager * m_locationManager; - UITableView * m_table; - // Zero when suggestions cells are not visible - NSInteger m_suggestionsCount; - NSArray * categoriesNames; -} - -@property (nonatomic) NSMutableArray * searchResults; - -@property (nonatomic) UISearchBar * searchBar; -@property (nonatomic) ScopeView * scopeView; - -@end diff --git a/iphone/Maps/Classes/SearchVC.mm b/iphone/Maps/Classes/SearchVC.mm deleted file mode 100644 index 7422ce1e40..0000000000 --- a/iphone/Maps/Classes/SearchVC.mm +++ /dev/null @@ -1,681 +0,0 @@ -#import "SearchVC.h" -#import "CompassView.h" -#import "LocationManager.h" -#import "SearchCell.h" -#import "BookmarksVC.h" -#import "CustomNavigationView.h" -#import "MapsAppDelegate.h" -#import "MapViewController.h" -#import "Statistics.h" -#import "Config.h" -#import "UIKitCategories.h" - -#include "Framework.h" - -#include "../../search/result.hpp" -#include "../../search/params.hpp" - -#include "../../indexer/mercator.hpp" - -#include "../../platform/platform.hpp" -#include "../../platform/preferred_languages.hpp" -#include "../../platform/settings.hpp" - -#include "../../geometry/angles.hpp" -#include "../../geometry/distance_on_sphere.hpp" - -/// When to display compass instead of country flags -#define MIN_COMPASS_DISTANCE 25000.0 - -/// To save search scope selection between launches -#define SEARCH_MODE_SETTING "SearchMode" - -SearchVC * g_searchVC = nil; - -@interface ResultsWrapper : NSObject -{ - search::Results m_results; -} - -// Stores search string which corresponds to these results. -@property(nonatomic, retain) NSString * searchString; - -- (id)initWithResults:(search::Results const &)res; - -- (int)getCount; -- (search::Result const &)getWithPosition:(NSInteger)pos; - -- (BOOL)isEndMarker; -- (BOOL)isEndedNormal; - -@end - -@implementation ResultsWrapper - -@synthesize searchString; - -- (id)initWithResults:(search::Results const &)res -{ - if ((self = [super init])) - m_results = res; - return self; -} - -- (int)getCount -{ - return static_cast(m_results.GetCount()); -} - -- (search::Result const &)getWithPosition:(NSInteger)pos -{ - return m_results.GetResult(pos); -} - -- (BOOL)isEndMarker -{ - return m_results.IsEndMarker(); -} - -- (BOOL)isEndedNormal -{ - return m_results.IsEndedNormal(); -} - -@end - - -// Last search results are stored between SearchVC sessions -// to appear instantly for the user, they also store last search text query. -//ResultsWrapper * g_lastSearchResults = nil; - -static NSInteger GetDefaultSearchScope() -{ - NSInteger value; - if (Settings::Get(SEARCH_MODE_SETTING, value)) - return value; - return 0; // 0 is default scope ("Near me") -} - -NSString * g_lastSearchRequest = nil; -NSInteger g_scopeSection = GetDefaultSearchScope(); -NSInteger g_numberOfRowsInEmptySearch = 0; - -static void OnSearchResultCallback(search::Results const & res) -{ - if (g_searchVC) - { - ResultsWrapper * w = [[ResultsWrapper alloc] initWithResults:res]; - [g_searchVC performSelectorOnMainThread:@selector(addResult:) - withObject:w waitUntilDone:NO]; - } -} - -///////////////////////////////////////////////////////////////////// - -@interface SearchVC () - -@property (nonatomic) BOOL searching; -@property (nonatomic) UIActivityIndicatorView * activityIndicator; - -@end - -@implementation SearchVC -@synthesize searchResults = _searchResults; - -- (id)init -{ - if ((self = [super initWithNibName:nil bundle:nil])) - { - m_framework = &GetFramework(); - m_locationManager = [MapsAppDelegate theApp].m_locationManager; - - double lat, lon; - bool const hasPt = [m_locationManager getLat:lat Lon:lon]; - m_framework->PrepareSearch(hasPt, lat, lon); - - //mycode init array of categories - categoriesNames = [[NSArray alloc] initWithObjects: - @"food", - @"shop", - @"hotel", - @"tourism", - @"entertainment", - @"atm", - @"bank", - @"transport", - @"fuel", - @"parking", - @"pharmacy", - @"hospital", - @"toilet", - @"post", - @"police", - nil]; - _searchResults = [[NSMutableArray alloc] initWithObjects:[[ResultsWrapper alloc] init], [[ResultsWrapper alloc] init], [[ResultsWrapper alloc] init], nil]; - if (!g_lastSearchRequest) - { - g_lastSearchRequest = @""; - } - } - return self; -} - -- (void)fillSearchParams:(search::SearchParams &)params withText:(NSString *)queryString -{ - params.m_query = [[queryString precomposedStringWithCompatibilityMapping] UTF8String]; - params.m_callback = bind(&OnSearchResultCallback, _1); - - // Set current keyboard input mode - // Note: input mode was introduced in iOS 4.2 (now we support 4.3+) - params.SetInputLanguage([[UITextInputMode currentInputMode].primaryLanguage UTF8String]); - - double lat, lon; - if ([m_locationManager getLat:lat Lon:lon]) - params.SetPosition(lat, lon); -} - -- (UISearchBar *)searchBar -{ - if (!_searchBar) - { - _searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.width - 80, 44)]; - _searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth; - _searchBar.delegate = self; - _searchBar.placeholder = NSLocalizedString(@"search_map", @"Search box placeholder text"); - _searchBar.barStyle = UISearchBarStyleDefault; - _searchBar.tintColor = [[UINavigationBar appearance] tintColor]; - - if (g_lastSearchRequest) - [_searchBar setText:g_lastSearchRequest]; - } - return _searchBar; -} - -- (ScopeView *)scopeView -{ - if (!_scopeView) - { - UISegmentedControl * segmentedControl = [[UISegmentedControl alloc] initWithItems:@[NSLocalizedString(@"search_mode_nearme", nil), - NSLocalizedString(@"search_mode_viewport", nil), - NSLocalizedString(@"search_mode_all", nil)]]; - CGFloat width = IPAD ? 400 : 310; - segmentedControl.frame = CGRectMake(0, 0, width, 32); - if (SYSTEM_VERSION_IS_LESS_THAN(@"7")) - segmentedControl.tintColor = [[UINavigationBar appearance] tintColor]; - else - segmentedControl.tintColor = [UIColor whiteColor]; - - segmentedControl.selectedSegmentIndex = GetDefaultSearchScope(); - segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar; - [segmentedControl addTarget:self action:@selector(segmentedControlValueChanged:) forControlEvents:UIControlEventValueChanged]; - _scopeView = [[ScopeView alloc] initWithFrame:CGRectMake(0, 0, self.view.width, 44) segmentedControl:segmentedControl]; - _scopeView.autoresizingMask = UIViewAutoresizingFlexibleWidth; - - if (SYSTEM_VERSION_IS_LESS_THAN(@"7")) - _scopeView.backgroundColor = [[UINavigationBar appearance] tintColor]; - else - _scopeView.backgroundColor = [UIColor colorWithColorCode:@"28384b"]; - } - return _scopeView; -} - -- (void)segmentedControlValueChanged:(UISegmentedControl *)segmentedControl -{ - g_scopeSection = segmentedControl.selectedSegmentIndex; - // Save selected search mode for future launches - Settings::Set(SEARCH_MODE_SETTING, g_scopeSection); - [self proceedSearchWithString:self.searchBar.text andForceSearch:YES]; -} - -- (void)viewDidLoad -{ - g_searchVC = self; - - m_table = [[UITableView alloc] initWithFrame:self.view.bounds]; - m_table.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - m_table.delegate = self; - m_table.dataSource = self; - m_table.contentInset = UIEdgeInsetsMake(self.scopeView.height, 0, 0, 0); - m_table.scrollIndicatorInsets = m_table.contentInset; - - self.view.backgroundColor = [UIColor whiteColor]; - [self.view addSubview:m_table]; - [self.view addSubview:self.scopeView]; - - self.navigationItem.titleView = self.searchBar; -} - -- (void)viewWillAppear:(BOOL)animated -{ - [super viewWillAppear:animated]; - - [m_locationManager start:self]; - - [self performAfterDelay:0 block:^{ - [self resizeNavigationBar]; - }]; -} - -- (void)resizeNavigationBar -{ - if (SYSTEM_VERSION_IS_LESS_THAN(@"6") || IPAD) - return; - - CGFloat landscapeHeight = 32; - CGFloat portraitHeight = 44; - self.navigationController.navigationBar.height = portraitHeight; - if (UIDeviceOrientationIsLandscape(self.interfaceOrientation)) - m_table.minY = portraitHeight - landscapeHeight; - else - m_table.minY = 0; - self.scopeView.minY = m_table.minY; -} - -- (void)viewDidAppear:(BOOL)animated -{ - // Relaunch search when view has appeared because of search indicator hack - // (we replace one control with another, and system calls unsupported method on it) - if (GetPlatform().IsPro()) - [self proceedSearchWithString:g_lastSearchRequest andForceSearch:YES]; - - [self.searchBar becomeFirstResponder]; -} - -- (void)viewWillDisappear:(BOOL)animated -{ - [m_locationManager stop:self]; - - // hide keyboard immediately - [self.searchBar resignFirstResponder]; - [super viewWillDisappear:animated]; - g_numberOfRowsInEmptySearch = 0; -} - -- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration -{ - [self resizeNavigationBar]; -} - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - return YES; // All orientations are supported. -} - -//************************************************************************** -//*********** SearchBar handlers ******************************************* -- (void)searchBar:(UISearchBar *)sender textDidChange:(NSString *)searchText -{ - self.searching = [searchText length] > 0; - g_lastSearchRequest = [[NSString alloc] initWithString:self.searchBar.text]; - [self clearCacheResults]; - [self proceedSearchWithString:self.searchBar.text andForceSearch:YES]; -} - -- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar -{ - [searchBar resignFirstResponder]; -} - -- (void)onCloseButton:(id)sender -{ - [self.navigationController popToRootViewControllerAnimated:YES]; -} -//*********** End of SearchBar handlers ************************************* -//*************************************************************************** - -- (void)setSearchBoxText:(NSString *)text -{ - self.searchBar.text = text; - // Manually send text change notification if control has no focus, - // OR if iOS 6 - it doesn't send textDidChange notification after text property update - if (![self.searchBar isFirstResponder] - || [UIDevice currentDevice].systemVersion.floatValue > 5.999) - [self searchBar:self.searchBar textDidChange:text]; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - m_suggestionsCount = self.searchBar.text.length ? 0 : 1; - //No text in search show categories - if (m_suggestionsCount) - { - return [categoriesNames count]; - } - //If no results we should show 0 strings if search is in progress or 1 string with message thaht there is no results - if (![[_searchResults objectAtIndex:g_scopeSection] getCount]) - { - return g_numberOfRowsInEmptySearch; - } - return [[_searchResults objectAtIndex:g_scopeSection] getCount]; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - NSInteger realRowIndex = indexPath.row; - if (m_suggestionsCount) - { - static NSString *CellIdentifier = @"categoryCell"; - - UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; - if (!cell) - { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; - } - - cell.textLabel.text = NSLocalizedString([categoriesNames objectAtIndex:indexPath.row], nil); - cell.imageView.image = [UIImage imageNamed:[categoriesNames objectAtIndex:indexPath.row]]; - - return cell; - } - //No search results - if ([self.searchBar.text length] != 0 && ![[_searchResults objectAtIndex:g_scopeSection] getCount] && g_numberOfRowsInEmptySearch) - { - UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"NoResultsCell"]; - if (!cell) - { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"NoResultsCell"]; - cell.selectionStyle = UITableViewCellSelectionStyleNone; - } - cell.textLabel.text = NSLocalizedString(@"no_search_results_found", nil); - - // check if we have no position in "near me" screen - if (![m_locationManager lastLocationIsValid] && g_scopeSection == 0) - cell.detailTextLabel.text = NSLocalizedString(@"unknown_current_position", nil); - else - cell.detailTextLabel.text = @""; - return cell; - } - - if ([_searchResults objectAtIndex:g_scopeSection] == nil || realRowIndex >= (NSInteger)[[_searchResults objectAtIndex:g_scopeSection] getCount]) - { - ASSERT(false, ("Invalid m_results with size", [[_searchResults objectAtIndex:g_scopeSection] getCount])); - return nil; - } - - search::Result const & r = [[_searchResults objectAtIndex:g_scopeSection] getWithPosition:realRowIndex]; - if (r.GetResultType() != search::Result::RESULT_SUGGESTION) - { - SearchCell * cell = (SearchCell *)[tableView dequeueReusableCellWithIdentifier:@"FeatureCell"]; - if (!cell) - cell = [[SearchCell alloc] initWithReuseIdentifier:@"FeatureCell"]; - - // Init common parameters - cell.featureName.text = [NSString stringWithUTF8String:r.GetString()]; - cell.featureCountry.text = [NSString stringWithUTF8String:r.GetRegionString()]; - cell.featureType.text = [NSString stringWithUTF8String:r.GetFeatureType()]; - - // Get current position and compass "north" direction - double azimut = -1.0; - double lat, lon; - - if ([m_locationManager getLat:lat Lon:lon]) - { - double north = -1.0; - [m_locationManager getNorthRad:north]; - - string distance; - if (!m_framework->GetDistanceAndAzimut(r.GetFeatureCenter(), - lat, lon, north, distance, azimut)) - { - // do not draw arrow for far away features - azimut = -1.0; - } - - cell.featureDistance.text = [NSString stringWithUTF8String:distance.c_str()]; - } - else - cell.featureDistance.text = nil; - - // Show flags only if it has one and no azimut to feature - char const * flag = r.GetRegionFlag(); - if (flag && azimut < 0.0) - { - UIImage * flagImage = [UIImage imageNamed:[NSString stringWithFormat:@"%s.png", flag]]; - UIImageView * imgView = [[UIImageView alloc] initWithImage:flagImage]; - cell.accessoryView = imgView; - } - else - { - // Try to reuse existing compass view - CompassView * compass; - if ([cell.accessoryView isKindOfClass:[CompassView class]]) - compass = (CompassView *)cell.accessoryView; - else - { - // Create compass view - float const h = (int)(m_table.rowHeight * 0.6); - compass = [[CompassView alloc] initWithFrame:CGRectMake(0, 0, h, h)]; - cell.accessoryView = compass; - } - - // Show arrow for valid azimut and if feature is not a continent (flag is exist) - compass.showArrow = (azimut >= 0.0 && flag) ? YES : NO; - compass.angle = azimut; - } - return cell; - } - else - { - UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"SuggestionCell"]; - if (!cell) - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"SuggestionCell"]; - cell.textLabel.text = [NSString stringWithUTF8String:r.GetString()]; - return cell; - } -} - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - NSInteger realRowIndex = indexPath.row; - // Suggestion cell was clicked - if (m_suggestionsCount) - { - [[Statistics instance] logEvent:@"Category Selection" withParameters:@{@"Category" : [categoriesNames objectAtIndex:realRowIndex]}]; - [self setSearchBoxText:[NSLocalizedString([categoriesNames objectAtIndex:realRowIndex], Search Suggestion) stringByAppendingString:@" "]]; - [m_table scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES]; - return; - } - - if (realRowIndex < (NSInteger)[[_searchResults objectAtIndex:g_scopeSection] getCount]) - { - search::Result const & res = [[_searchResults objectAtIndex:g_scopeSection] getWithPosition:realRowIndex]; - if (res.GetResultType() != search::Result::RESULT_SUGGESTION) - { - m_framework->ShowSearchResult(res); - - search::AddressInfo info; - info.MakeFrom(res); - - if (g_scopeSection == 0) - [[Statistics instance] logEvent:@"Search Filter" withParameters:@{@"Filter Name" : @"Near Me"}]; - else if (g_scopeSection == 1) - [[Statistics instance] logEvent:@"Search Filter" withParameters:@{@"Filter Name" : @"On the Screen"}]; - else - [[Statistics instance] logEvent:@"Search Filter" withParameters:@{@"Filter Name" : @"Everywhere"}]; - - [[MapsAppDelegate theApp].m_mapViewController showSearchResultAsBookmarkAtMercatorPoint:res.GetFeatureCenter() withInfo:info]; - - [self onCloseButton:nil]; - } - else - { - [self setSearchBoxText:[NSString stringWithUTF8String:res.GetSuggestionString()]]; - // Remove blue selection from the row - [tableView deselectRowAtIndexPath: indexPath animated:YES]; - } - } -} - -- (void)setSearching:(BOOL)searching -{ - if (searching) - [self.activityIndicator startAnimating]; - else - [self.activityIndicator stopAnimating]; - - _searching = searching; -} - -- (UIActivityIndicatorView *)activityIndicator -{ - if (!_activityIndicator) - { - _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; - _activityIndicator.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; - _activityIndicator.center = CGPointMake(self.searchBar.width - 45, self.searchBar.height / 2); - _activityIndicator.backgroundColor = [UIColor whiteColor]; - [self.searchBar addSubview:_activityIndicator]; - } - return _activityIndicator; -} - -// Called on UI thread from search threads -- (void)addResult:(id)res -{ - ResultsWrapper * w = (ResultsWrapper *)res; - - if ([w isEndMarker]) - { - if ([w isEndedNormal]) - { - g_numberOfRowsInEmptySearch = 1; - self.searching = NO; - [m_table reloadData]; - } - } - else - { - g_numberOfRowsInEmptySearch = 0; - [_searchResults replaceObjectAtIndex:g_scopeSection withObject:w]; - [m_table reloadData]; - } -} - -void setSearchType(search::SearchParams & params) -{ - switch (g_scopeSection) - { - case 0: - params.SetSearchMode(search::SearchParams::AROUND_POSITION); - break; - case 1: - params.SetSearchMode(search::SearchParams::IN_VIEWPORT); - break; - case 2: - params.SetSearchMode(search::SearchParams::ALL); - break; - default: - params.SetSearchMode(search::SearchParams::ALL); - break; - } -} - -//****************************************************************** -//*********** Location manager callbacks *************************** -- (void)onLocationError:(location::TLocationError)errorCode -{ - // Handle location status changes if necessary -} - -- (void)onLocationUpdate:(location::GpsInfo const &)info -{ - // Refresh search results with newer location. - if (![self.searchBar.text length]) - return; - search::SearchParams params; - setSearchType(params); - if (self.searchBar.text) - { - [self fillSearchParams:params withText:self.searchBar.text]; - - // hack, fillSearch Params return invalid position - params.SetPosition(info.m_latitude, info.m_longitude); - - g_numberOfRowsInEmptySearch = m_framework->Search(params) ? 0 : 1; - } -} - -- (void)onCompassUpdate:(location::CompassInfo const &)info -{ - double lat, lon; - if (![m_locationManager getLat:lat Lon:lon]) - return; - //check if categories are on the screen - if (!m_suggestionsCount && [[_searchResults objectAtIndex:g_scopeSection] getCount]) - { - double const northRad = (info.m_trueHeading < 0) ? info.m_magneticHeading : info.m_trueHeading; - NSArray * cells = m_table.visibleCells; - for (NSUInteger i = 0; i < cells.count; ++i) - { - UITableViewCell * cell = (UITableViewCell *)[cells objectAtIndex:i]; - NSInteger realRowIndex = [m_table indexPathForCell:cell].row; - search::Result const & res = [[_searchResults objectAtIndex:g_scopeSection] getWithPosition:realRowIndex]; - if (res.GetResultType() != search::Result::RESULT_SUGGESTION) - { - // Show compass only for cells without flags - if ([cell.accessoryView isKindOfClass:[CompassView class]]) - { - CompassView * compass = (CompassView *)cell.accessoryView; - m2::PointD const center = res.GetFeatureCenter(); - compass.angle = ang::AngleTo(m2::PointD(MercatorBounds::LonToX(lon), - MercatorBounds::LatToY(lat)), center) + northRad; - } - } - } - } -} -//*********** End of Location manager callbacks ******************** -//****************************************************************** - -// Dismiss virtual keyboard when touching tableview -- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView -{ - [self.searchBar resignFirstResponder]; -} - -// Dismiss virtual keyboard when "Search" button is pressed -- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar -{ - [self.searchBar resignFirstResponder]; -} - -// Callback from suggestion cell, called when icon is selected -- (void)onSuggestionSelected:(NSString *)suggestion -{ - [self setSearchBoxText:[suggestion stringByAppendingString:@" "]]; -} - -- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar -{ - [self onCloseButton:nil]; -} - -- (void)clearCacheResults -{ - for (int i = 0; i < [_searchResults count]; ++i) - { - [_searchResults replaceObjectAtIndex:i withObject:[[ResultsWrapper alloc] init]]; - } -} - -- (void)proceedSearchWithString:(NSString *)searchText andForceSearch:(BOOL)forceSearch -{ - g_numberOfRowsInEmptySearch = 0; - [m_table reloadData]; - if (![searchText length]) - return; - search::SearchParams params; - setSearchType(params); - if (forceSearch) - { - params.SetForceSearch(true); - } - [self fillSearchParams:params withText:searchText]; - if (!m_framework->Search(params)) - { - g_numberOfRowsInEmptySearch = 1; - [m_table reloadData]; - } -} - -@end diff --git a/iphone/Maps/Classes/SearchView.h b/iphone/Maps/Classes/SearchView.h new file mode 100644 index 0000000000..e069538aea --- /dev/null +++ b/iphone/Maps/Classes/SearchView.h @@ -0,0 +1,10 @@ + +#import +#import "SearchActivityProtocol.h" +#import "SearchBar.h" + +@interface SearchView : UIView + +@property (nonatomic) SearchBar * searchBar; + +@end diff --git a/iphone/Maps/Classes/SearchView.mm b/iphone/Maps/Classes/SearchView.mm new file mode 100644 index 0000000000..abd2c74850 --- /dev/null +++ b/iphone/Maps/Classes/SearchView.mm @@ -0,0 +1,574 @@ + +#import "SearchView.h" +#import "SegmentedControl.h" +#import "SearchUniversalCell.h" +#import "UIKitCategories.h" +#import "MapsAppDelegate.h" +#import "LocationManager.h" +#import "Statistics.h" +#import "MapViewController.h" +#import "LocationManager.h" + +#include "Framework.h" + +#include "../../search/result.hpp" +#include "../../search/params.hpp" + +#include "../../indexer/mercator.hpp" + +#include "../../platform/platform.hpp" +#include "../../platform/preferred_languages.hpp" +#include "../../platform/settings.hpp" + +#include "../../geometry/angles.hpp" +#include "../../geometry/distance_on_sphere.hpp" + + +@interface SearchResultsWrapper : NSObject + +- (id)initWithResults:(search::Results const &)res; + +- (search::Result const &)resultWithPosition:(NSInteger)position; +- (NSInteger)count; +- (BOOL)isEndMarker; +- (BOOL)isEndedNormal; + +@end + +@interface SearchResultsWrapper () + +@property (nonatomic) search::Results results; +@property (nonatomic) NSMutableDictionary * distances; + +@end + +@implementation SearchResultsWrapper + +- (id)initWithResults:(search::Results const &)results +{ + self = [super init]; + + self.results = results; + + return self; +} + +- (NSMutableDictionary *)distances +{ + if (!_distances) + _distances = [[NSMutableDictionary alloc] init]; + return _distances; +} + +- (NSInteger)count +{ + return self.results.GetCount(); +} + +- (search::Result const &)resultWithPosition:(NSInteger)position +{ + return self.results.GetResult(position); +} + +- (BOOL)isEndMarker +{ + return self.results.IsEndMarker(); +} + +- (BOOL)isEndedNormal +{ + return self.results.IsEndedNormal(); +} + +@end + + +@interface SearchView () + +@property (nonatomic) SegmentedControl * segmentedControl; +@property (nonatomic) UITableView * tableView; +@property (nonatomic) UIView * backgroundView; +@property (nonatomic) UIImageView * topBackgroundView; +@property (nonatomic) UILabel * emptyResultLabel; + +- (BOOL)isShowingCategories; + +@property (nonatomic) NSMutableArray * searchData; +@property (nonatomic) NSArray *categoriesNames; + +@end + +@implementation SearchView +@synthesize active = _active; + +__weak SearchView * selfPointer; + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + [self addSubview:self.backgroundView]; + [self addSubview:self.tableView]; + [self addSubview:self.topBackgroundView]; + [self addSubview:self.segmentedControl]; + [self addSubview:self.searchBar]; + [self addSubview:self.emptyResultLabel]; + + self.emptyResultLabel.center = CGPointMake(self.width / 2, 160); + self.emptyResultLabel.hidden = YES; + + self.searchBar.midX = self.width / 2; + UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(barTapped:)]; + [self.searchBar addGestureRecognizer:tap]; + + NSInteger value; + self.segmentedControl.selectedSegmentIndex = Settings::Get("SearchMode", value) ? value : 0; + self.segmentedControl.midX = self.width / 2; + + [self setActive:NO animated:NO]; + + selfPointer = self; + + double latitude; + double longitude; + LocationManager * locationManager = [MapsAppDelegate theApp].m_locationManager; + bool const hasPt = [locationManager getLat:latitude Lon:longitude]; + GetFramework().PrepareSearch(hasPt, latitude, longitude); + + [locationManager start:self]; + + return self; +} + +- (void)onLocationError:(location::TLocationError)errorCode +{ + NSLog(@"Location error in SearchView"); +} + +- (void)onLocationUpdate:(location::GpsInfo const &)info +{ + if ([self.searchBar.textField.text length]) + { + search::SearchParams params = [self searchParameters]; + params.SetPosition(info.m_latitude, info.m_longitude); + GetFramework().Search(params); + + [self recalculateDistances]; + [self.tableView reloadRowsAtIndexPaths:self.tableView.indexPathsForVisibleRows withRowAnimation:UITableViewRowAnimationNone]; + } +} + +- (void)recalculateDistances +{ + LocationManager * locationManager = [MapsAppDelegate theApp].m_locationManager; + double north = -1.0; + double azimut = -1.0; + [locationManager getNorthRad:north]; + double lat, lon; + if ([locationManager getLat:lat Lon:lon]) + { + for (NSInteger segment = 0; segment < [self.segmentedControl segmentsCount]; segment++) { + SearchResultsWrapper * wrapper = self.searchData[segment]; + for (NSInteger position = 0; position < [wrapper count]; position++) { + search::Result const & result = [wrapper resultWithPosition:position]; + string distance; + GetFramework().GetDistanceAndAzimut(result.GetFeatureCenter(), lat, lon, north, distance, azimut); + wrapper.distances[@(position)] = [NSString stringWithUTF8String:distance.c_str()]; + } + } + } +} + +- (void)onCompassUpdate:(location::CompassInfo const &)info +{ + +} + +- (search::SearchParams)searchParameters +{ + NSInteger scopeIndex = self.segmentedControl.selectedSegmentIndex; + search::SearchParams params; + if (scopeIndex == 0) + params.SetSearchMode(search::SearchParams::AROUND_POSITION); + else if (scopeIndex == 1) + params.SetSearchMode(search::SearchParams::IN_VIEWPORT); + else if (scopeIndex == 2) + params.SetSearchMode(search::SearchParams::ALL); + + params.m_query = [[self.searchBar.textField.text precomposedStringWithCompatibilityMapping] UTF8String]; + params.m_callback = bind(&OnSearchResultCallback, _1); + params.SetInputLanguage([[UITextInputMode currentInputMode].primaryLanguage UTF8String]); + params.SetForceSearch(true); + + return params; +} + +- (void)search +{ + self.emptyResultLabel.hidden = YES; + [self.searchBar setSearching:YES]; + search::SearchParams params = [self searchParameters]; + double lat, lon; + if ([[MapsAppDelegate theApp].m_locationManager getLat:lat Lon:lon]) + params.SetPosition(lat, lon); + GetFramework().Search(params); + [self.tableView reloadData]; +} + +static void OnSearchResultCallback(search::Results const & results) +{ + SearchResultsWrapper * wrapper = [[SearchResultsWrapper alloc] initWithResults:results]; + [selfPointer performSelectorOnMainThread:@selector(frameworkDidAddSearchResult:) withObject:wrapper waitUntilDone:NO]; +} + +- (void)frameworkDidAddSearchResult:(SearchResultsWrapper *)wrapper +{ + if ([wrapper isEndMarker]) + { + if ([wrapper isEndedNormal]) + { + [self.searchBar setSearching:NO]; + [self recalculateDistances]; + [self.tableView reloadData]; + } + } + else + { + self.emptyResultLabel.hidden = [self isShowingCategories] ? YES : ([wrapper count] > 0); + self.searchData[self.segmentedControl.selectedSegmentIndex] = wrapper; + [self.tableView reloadData]; + [self.tableView setContentOffset:CGPointMake(0, -self.tableView.contentInset.top) animated:YES]; + } +} + +- (void)searchBarDidPressClearButton:(SearchBar *)searchBar +{ + [self processTextChanging]; +} + +- (void)searchBarDidPressCancelButton:(id)searchBar +{ + [self setActive:NO animated:YES]; +} + +- (void)setActive:(BOOL)active animated:(BOOL)animated +{ + [self.searchBar setActive:active animated:animated]; + [self.segmentedControl setActive:active animated:animated]; + if (active) + { + [UIView animateWithDuration:(animated ? 0.4 : 0) delay:0 damping:0.9 initialVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ + self.backgroundView.alpha = 1; + self.topBackgroundView.alpha = 1; + self.tableView.alpha = 1; + self.tableView.minY = 0; + } completion:^(BOOL finished){ + [self.tableView setContentOffset:CGPointMake(0, -self.tableView.contentInset.top) animated:YES]; + }]; + } + else + { + [UIView animateWithDuration:(animated ? 0.4 : 0) delay:0 damping:0.9 initialVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ + self.backgroundView.alpha = 0; + self.topBackgroundView.alpha = 0; + self.tableView.alpha = 0; + self.tableView.minY = self.height; + } completion:nil]; + } + [self willChangeValueForKey:@"active"]; + _active = active; + [self didChangeValueForKey:@"active"]; +} + +- (void)barTapped:(UITapGestureRecognizer *)sender +{ + [self setActive:YES animated:YES]; +} + +- (void)textFieldTextChanged:(id)sender +{ + [self processTextChanging]; +} + +- (void)processTextChanging +{ + if ([self isShowingCategories]) + { + self.emptyResultLabel.hidden = YES; + [self.tableView reloadData]; + } + else + { + [self search]; + } +} + +- (BOOL)textFieldShouldReturn:(UITextField *)textField +{ + return [textField.text length] > 0; +} + +- (void)segmentedControl:(SegmentedControl *)segmentControl didSelectSegment:(NSInteger)segmentIndex +{ + [self search]; +} + +- (CGFloat)defaultSearchBarMinY +{ + if (SYSTEM_VERSION_IS_LESS_THAN(@"7")) + return self.width < self.height ? 10 : 4; + else + return self.width < self.height ? 30 : 20; +} + +- (void)layoutSubviews +{ + self.searchBar.minY = [self defaultSearchBarMinY]; + if (self.width < self.height) + { + self.segmentedControl.minY = self.searchBar.maxY - 4; + self.segmentedControl.height = 40; + self.topBackgroundView.height = self.segmentedControl.maxY + 3; + } + else + { + self.segmentedControl.minY = self.searchBar.maxY - 6; + self.segmentedControl.height = 27; + self.topBackgroundView.height = self.segmentedControl.maxY + 1; + } + self.tableView.contentInset = UIEdgeInsetsMake(self.topBackgroundView.maxY + 13.5, 0, 14, 0); + self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(self.topBackgroundView.maxY, 0, 0, 0); +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + SearchUniversalCell * cell = [tableView dequeueReusableCellWithIdentifier:@"UniversalCell"]; + if (!cell) + cell = [[SearchUniversalCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"UniversalCell"]; + + if ([self isShowingCategories]) + { + cell.titleLabel.text = NSLocalizedString(self.categoriesNames[indexPath.row], nil); + cell.subtitleLabel.text = nil; + cell.distanceLabel.text = nil; + } + else + { + if (indexPath.row == 0) + { + cell.titleLabel.text = @"Показать все"; + cell.subtitleLabel.text = nil; + cell.distanceLabel.text = nil; + } + else + { + SearchResultsWrapper * wrapper = self.searchData[self.segmentedControl.selectedSegmentIndex]; + NSInteger position = indexPath.row - 1; + search::Result const & result = [wrapper resultWithPosition:position]; + cell.titleLabel.text = [NSString stringWithUTF8String:result.GetString()]; + cell.subtitleLabel.text = result.GetRegionString() ? [NSString stringWithUTF8String:result.GetRegionString()] : nil; + cell.distanceLabel.text = wrapper.distances[@(position)]; + } + } + + if ([self rowsCount] == 1) + cell.position = SearchCellPositionAlone; + else if (indexPath.row == 0) + cell.position = SearchCellPositionFirst; + else if (indexPath.row == [self rowsCount] - 1) + cell.position = SearchCellPositionLast; + else + cell.position = SearchCellPositionMiddle; + + return cell; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + if ([self isShowingCategories]) + { + return [SearchUniversalCell cellHeightWithTitle:self.categoriesNames[indexPath.row] subtitle:nil viewWidth:tableView.width]; + } + else + { + if (indexPath.row == 0) + { + return [SearchUniversalCell cellHeightWithTitle:@"Показать все" subtitle:nil viewWidth:tableView.width]; + } + else + { + SearchResultsWrapper * wrapper = self.searchData[self.segmentedControl.selectedSegmentIndex]; + NSInteger position = indexPath.row - 1; + search::Result const & result = [wrapper resultWithPosition:position]; + NSString * title = [NSString stringWithUTF8String:result.GetString()]; + NSString * subtitle = [NSString stringWithUTF8String:result.GetRegionString()]; + return [SearchUniversalCell cellHeightWithTitle:title subtitle:subtitle viewWidth:tableView.width]; + } + } +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return [self rowsCount]; +} + +- (NSInteger)rowsCount +{ + SearchResultsWrapper * wrapper = self.searchData[self.segmentedControl.selectedSegmentIndex]; + NSInteger resultsCount = [wrapper count] ? [wrapper count] + 1 : 0; + return [self isShowingCategories] ? [self.categoriesNames count] : resultsCount; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + [tableView deselectRowAtIndexPath:indexPath animated:YES]; + if ([self isShowingCategories]) + { + [[Statistics instance] logEvent:@"Category Selection" withParameters:@{@"Category" : self.categoriesNames[indexPath.row]}]; + self.searchBar.textField.text = [NSLocalizedString(self.categoriesNames[indexPath.row], nil) stringByAppendingString:@" "]; + [self search]; + return; + } + + if (indexPath.row == 0) + { + GetFramework().ShowAllSearchResults(); + self.searchBar.isShowingResult = YES; + [self setActive:NO animated:YES]; + } + else + { + NSInteger segmentIndex = self.segmentedControl.selectedSegmentIndex; + NSInteger position = indexPath.row - 1; + search::Result const & result = [self.searchData[segmentIndex] resultWithPosition:position]; + if (result.GetResultType() == search::Result::RESULT_SUGGESTION) + { + self.searchBar.textField.text = [NSString stringWithUTF8String:result.GetSuggestionString()]; + [self search]; + return; + } + else + { + GetFramework().ShowSearchResult(result); + + if (segmentIndex == 0) + [[Statistics instance] logEvent:@"Search Filter" withParameters:@{@"Filter Name" : @"Near Me"}]; + else if (segmentIndex == 1) + [[Statistics instance] logEvent:@"Search Filter" withParameters:@{@"Filter Name" : @"On the Screen"}]; + else + [[Statistics instance] logEvent:@"Search Filter" withParameters:@{@"Filter Name" : @"Everywhere"}]; + + self.searchBar.textField.text = [NSString stringWithUTF8String:result.GetString()]; + + self.searchBar.isShowingResult = YES; + [self setActive:NO animated:YES]; + } + } +} + +- (void)scrollViewDidScroll:(UIScrollView *)scrollView +{ + if (!scrollView.decelerating && scrollView.dragging) + [self.searchBar.textField resignFirstResponder]; +} + +- (BOOL)isShowingCategories +{ + return ![self.searchBar.textField.text length]; +} + +- (NSMutableArray *)searchData +{ + if (!_searchData) + _searchData = [@[[[SearchResultsWrapper alloc] init], [[SearchResultsWrapper alloc] init], [[SearchResultsWrapper alloc] init]] mutableCopy]; + return _searchData; +} + +- (NSArray *)categoriesNames +{ + if (!_categoriesNames) + _categoriesNames = @[@"food", @"shop", @"hotel", @"tourism", @"entertainment", @"atm", @"bank", @"transport", @"fuel", @"parking", @"pharmacy", @"hospital", @"toilet", @"post", @"police",]; + return _categoriesNames; +} + +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event +{ + return CGRectContainsPoint(self.searchBar.frame, point) || self.active; +} + +- (UITableView *)tableView +{ + if (!_tableView) + { + _tableView = [[UITableView alloc] initWithFrame:self.bounds]; + _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + _tableView.delegate = self; + _tableView.dataSource = self; + _tableView.backgroundColor = [UIColor clearColor]; + _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + } + return _tableView; +} + +- (UIImageView *)topBackgroundView +{ + if (!_topBackgroundView) + { + _topBackgroundView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.width, 0)]; + _topBackgroundView.image = [[UIImage imageNamed:@"SearchViewTopBackground"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 0, 10, 0)]; + _topBackgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _topBackgroundView.userInteractionEnabled = YES; + } + return _topBackgroundView; +} + +- (UIView *)backgroundView +{ + if (!_backgroundView) + { + _backgroundView = [[UIView alloc] initWithFrame:self.bounds]; + _backgroundView.backgroundColor = [UIColor colorWithColorCode:@"efeff4"]; + _backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + } + return _backgroundView; +} + +- (SegmentedControl *)segmentedControl +{ + if (!_segmentedControl) + { + _segmentedControl = [[SegmentedControl alloc] initWithFrame:CGRectMake(0, 0, 294, 0)]; + _segmentedControl.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin; + _segmentedControl.delegate = self; + } + return _segmentedControl; +} + +- (SearchBar *)searchBar +{ + if (!_searchBar) + { + _searchBar = [[SearchBar alloc] initWithFrame:CGRectMake(0, 0, self.width, 44)]; + _searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth; + _searchBar.textField.delegate = self; + _searchBar.delegate = self; + [_searchBar.textField addTarget:self action:@selector(textFieldTextChanged:) forControlEvents:UIControlEventEditingChanged]; + } + return _searchBar; +} + +- (UILabel *)emptyResultLabel +{ + if (!_emptyResultLabel) + { + _emptyResultLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.width, 60)]; + _emptyResultLabel.backgroundColor = [UIColor clearColor]; + _emptyResultLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:14]; + _emptyResultLabel.text = @"Нет результатов"; + _emptyResultLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin; + _emptyResultLabel.textAlignment = NSTextAlignmentCenter; + } + return _emptyResultLabel; +} + +@end diff --git a/iphone/Maps/Classes/SegmentedControl.h b/iphone/Maps/Classes/SegmentedControl.h new file mode 100644 index 0000000000..1e251efca8 --- /dev/null +++ b/iphone/Maps/Classes/SegmentedControl.h @@ -0,0 +1,21 @@ + +#import +#import "SearchActivityProtocol.h" + +@class SegmentedControl; +@protocol SegmentedControlDelegate + +- (void)segmentedControl:(SegmentedControl *)segmentControl didSelectSegment:(NSInteger)segmentIndex; + +@end + +@interface SegmentedControl : UIView + +- (void)setActive:(BOOL)active animated:(BOOL)animated; +@property (nonatomic) NSInteger selectedSegmentIndex; + +@property (nonatomic) id delegate; + +- (NSInteger)segmentsCount; + +@end diff --git a/iphone/Maps/Classes/SegmentedControl.m b/iphone/Maps/Classes/SegmentedControl.m new file mode 100644 index 0000000000..f9dcf957fe --- /dev/null +++ b/iphone/Maps/Classes/SegmentedControl.m @@ -0,0 +1,144 @@ + +#import "SegmentedControl.h" +#import "UIKitCategories.h" + +@interface SegmentedControl () + +@property (nonatomic, readonly) UIButton * selectedButton; +@property (nonatomic) UIImageView * selectionImageView; + +@property (nonatomic) UIButton * leftButton; +@property (nonatomic) UIButton * centerButton; +@property (nonatomic) UIButton * rightButton; + +@end + +@implementation SegmentedControl +@synthesize active = _active; + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + [self addSubview:self.selectionImageView]; + + self.leftButton = [self buttonWithTitle:NSLocalizedString(@"search_mode_nearme", nil)]; + self.leftButton.tag = 0; + self.centerButton = [self buttonWithTitle:NSLocalizedString(@"search_mode_viewport", nil)]; + self.centerButton.tag = 1; + self.rightButton = [self buttonWithTitle:NSLocalizedString(@"search_mode_all", nil)]; + self.rightButton.tag = 2; + + [self setSelectedButton:self.leftButton animated:NO]; + [self setActive:NO animated:NO]; + + return self; +} + +- (NSInteger)segmentsCount +{ + return 3; +} + +- (UIButton *)buttonWithTitle:(NSString *)title +{ + UIButton * button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 90, 44)]; + [button setTitle:[title lowercaseString] forState:UIControlStateNormal]; + [button setTitleColor:[UIColor colorWithColorCode:@"333333"] forState:UIControlStateNormal]; + [button setTitleColor:[UIColor grayColor] forState:UIControlStateHighlighted]; + [button setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected]; + [button addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside]; + button.midY = self.height / 2; + button.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;; + button.titleLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:14]; + [self addSubview:button]; + + return button; +} + +- (UIImageView *)selectionImageView +{ + if (!_selectionImageView) + { + _selectionImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SegmentSelection"]]; + _selectionImageView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; + } + return _selectionImageView; +} + +- (void)setActive:(BOOL)active animated:(BOOL)animated +{ + if (active) + { + [UIView animateWithDuration:(animated ? 0.35 : 0) delay:0 damping:0.8 initialVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{ + NSInteger count = 3; + CGFloat start = (self.width / count) / 2; + CGFloat delta = self.width / count; + self.leftButton.midX = start; + self.centerButton.midX = start + delta; + self.rightButton.midX = start + 2 * delta; + self.selectionImageView.center = self.selectedButton.center; + self.leftButton.alpha = 1; + self.centerButton.alpha = 1; + self.rightButton.alpha = 1; + self.selectionImageView.alpha = 1; + } completion:^(BOOL finished){}]; + } + else + { + [UIView animateWithDuration:(animated ? 0.4 : 0) delay:0 damping:0.8 initialVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{ + self.leftButton.maxX = 0; + self.rightButton.minX = self.width; + self.centerButton.midX = self.width / 2; + self.selectionImageView.center = self.selectedButton.center; + self.leftButton.alpha = 0; + self.centerButton.alpha = 0; + self.rightButton.alpha = 0; + self.selectionImageView.alpha = 0; + } completion:^(BOOL finished){}]; + } + _active = active; +} + +- (void)layoutSubviews +{ + self.selectionImageView.center = self.selectedButton.center; +} + +- (NSInteger)selectedSegmentIndex +{ + return self.selectedButton.tag; +} + +- (void)setSelectedSegmentIndex:(NSInteger)selectedSegmentIndex +{ + if (selectedSegmentIndex == 0) + [self setSelectedButton:self.leftButton animated:NO]; + else if (selectedSegmentIndex == 1) + [self setSelectedButton:self.centerButton animated:NO]; + else if (selectedSegmentIndex == 2) + [self setSelectedButton:self.rightButton animated:NO]; +} + +- (void)setSelectedButton:(UIButton *)selectedButton animated:(BOOL)animated +{ + _selectedButton.selected = NO; + _selectedButton.titleLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:14]; + _selectedButton.userInteractionEnabled = YES; + selectedButton.selected = YES; + selectedButton.titleLabel.font = [UIFont fontWithName:@"HelveticaNeue" size:14]; + selectedButton.userInteractionEnabled = NO; + [UIView animateWithDuration:(animated ? 0.3 : 0) delay:0 damping:0.8 initialVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ + self.selectionImageView.center = selectedButton.center; + } completion:^(BOOL finished){}]; + + _selectedButton = selectedButton; +} + +- (void)buttonPressed:(UIButton *)sender +{ + [self setSelectedButton:sender animated:YES]; + [self.delegate segmentedControl:self didSelectSegment:sender.tag]; +} + +@end diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index 84e3d1611a..1e3dfe8dd3 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -61,6 +61,18 @@ 978F9248183B6671000D6C7C /* Main_iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 978F9246183B6671000D6C7C /* Main_iPhone.storyboard */; }; 978F9253183BD530000D6C7C /* NavigationController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 978F9252183BD530000D6C7C /* NavigationController.mm */; }; 978F9254183BD530000D6C7C /* NavigationController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 978F9252183BD530000D6C7C /* NavigationController.mm */; }; + 97A8000C18B21363000C07A2 /* SearchView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 97A8000B18B21363000C07A2 /* SearchView.mm */; }; + 97A8000D18B21363000C07A2 /* SearchView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 97A8000B18B21363000C07A2 /* SearchView.mm */; }; + 97A8001018B21395000C07A2 /* SearchBar.mm in Sources */ = {isa = PBXBuildFile; fileRef = 97A8000F18B21395000C07A2 /* SearchBar.mm */; }; + 97A8001118B21395000C07A2 /* SearchBar.mm in Sources */ = {isa = PBXBuildFile; fileRef = 97A8000F18B21395000C07A2 /* SearchBar.mm */; }; + 97A8001418B2140A000C07A2 /* SearchUniversalCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 97A8001318B2140A000C07A2 /* SearchUniversalCell.m */; }; + 97A8001518B2140A000C07A2 /* SearchUniversalCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 97A8001318B2140A000C07A2 /* SearchUniversalCell.m */; }; + 97A8001C18B25497000C07A2 /* SegmentedControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 97A8001B18B25497000C07A2 /* SegmentedControl.m */; }; + 97A8001D18B25497000C07A2 /* SegmentedControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 97A8001B18B25497000C07A2 /* SegmentedControl.m */; }; + 97A8002718B2741C000C07A2 /* SearchCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 97A8002618B2741C000C07A2 /* SearchCell.m */; }; + 97A8002818B2741C000C07A2 /* SearchCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 97A8002618B2741C000C07A2 /* SearchCell.m */; }; + 97ABBA4518C8DF620079333C /* PlacePageView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 97ABBA4418C8DF620079333C /* PlacePageView.mm */; }; + 97ABBA4618C8DF620079333C /* PlacePageView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 97ABBA4418C8DF620079333C /* PlacePageView.mm */; }; 97C9851E186AE3C500AF7E9E /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C9851C186AE3C500AF7E9E /* Reachability.m */; }; 97C9851F186AE3C500AF7E9E /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C9851C186AE3C500AF7E9E /* Reachability.m */; }; 97C98522186AE3CF00AF7E9E /* AppInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = 97C98520186AE3CF00AF7E9E /* AppInfo.mm */; }; @@ -286,7 +298,6 @@ FA054612155C465E001F4E37 /* SelectSetVC.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA054611155C465E001F4E37 /* SelectSetVC.mm */; }; FA054613155C465E001F4E37 /* SelectSetVC.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA054611155C465E001F4E37 /* SelectSetVC.mm */; }; FA065FED128614C400FEA989 /* MainWindow-iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = FA065FEC128614C400FEA989 /* MainWindow-iPad.xib */; }; - FA09E01113F71F6C007E69CA /* SearchVC.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA09E01013F71F6C007E69CA /* SearchVC.mm */; }; FA140651162A6288002BC1ED /* empty@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FA14064D162A6288002BC1ED /* empty@2x.png */; }; FA140652162A6288002BC1ED /* empty@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FA14064D162A6288002BC1ED /* empty@2x.png */; }; FA140653162A6288002BC1ED /* empty.png in Resources */ = {isa = PBXBuildFile; fileRef = FA14064E162A6288002BC1ED /* empty.png */; }; @@ -312,7 +323,6 @@ FA765AB51737BC6D00279CFF /* 64-lite.png in Resources */ = {isa = PBXBuildFile; fileRef = FA765AB11737BC6D00279CFF /* 64-lite.png */; }; FA765AB61737BC6D00279CFF /* 320-lite.png in Resources */ = {isa = PBXBuildFile; fileRef = FA765AB21737BC6D00279CFF /* 320-lite.png */; }; FA7F4B0017F1FFE800FAB1B5 /* World.mwm in Resources */ = {isa = PBXBuildFile; fileRef = FAFF42291347F101009BBB14 /* World.mwm */; }; - FA81AE3314D061BF00A0D70D /* SearchCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA81AE3214D061BF00A0D70D /* SearchCell.mm */; }; FA85F633145DDDC20090E1A0 /* packed_polygons.bin in Resources */ = {isa = PBXBuildFile; fileRef = FA85F632145DDDC20090E1A0 /* packed_polygons.bin */; }; FA87151B12B1518F00592DAF /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA87151A12B1518F00592DAF /* SystemConfiguration.framework */; }; FA8F8938132D5DB00048E3FE /* libtomcrypt.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FA8F8937132D5DB00048E3FE /* libtomcrypt.a */; }; @@ -1378,11 +1388,9 @@ FAFB08F0151215EE0041901D /* RenderContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = EE7F297E1219ECA300EB67A9 /* RenderContext.mm */; }; FAFB08F1151215EE0041901D /* WebViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = FAFCB63513366E78001A5C59 /* WebViewController.mm */; }; FAFB08F2151215EE0041901D /* CustomAlertView.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA34BEC81338D72F00FFB2A7 /* CustomAlertView.mm */; }; - FAFB08F3151215EE0041901D /* SearchVC.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA09E01013F71F6C007E69CA /* SearchVC.mm */; }; FAFB08F4151215EE0041901D /* CompassView.mm in Sources */ = {isa = PBXBuildFile; fileRef = FABF223D13FAA97A003D4D49 /* CompassView.mm */; }; FAFB08F5151215EE0041901D /* Preferences.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA29FDA9141E77F8004ADF66 /* Preferences.mm */; }; FAFB08F6151215EE0041901D /* LocationManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = FAA5C2A1144F135F005337F6 /* LocationManager.mm */; }; - FAFB08F8151215EE0041901D /* SearchCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA81AE3214D061BF00A0D70D /* SearchCell.mm */; }; FAFB08FB151215EE0041901D /* libfreetype.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EE12020311CD464100ABDD5D /* libfreetype.a */; }; FAFB08FC151215EE0041901D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; FAFB08FD151215EE0041901D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; @@ -1467,6 +1475,19 @@ 978F9246183B6671000D6C7C /* Main_iPhone.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main_iPhone.storyboard; sourceTree = ""; }; 978F9251183BD530000D6C7C /* NavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NavigationController.h; sourceTree = ""; }; 978F9252183BD530000D6C7C /* NavigationController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NavigationController.mm; sourceTree = ""; }; + 97A8000A18B21363000C07A2 /* SearchView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SearchView.h; sourceTree = ""; }; + 97A8000B18B21363000C07A2 /* SearchView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SearchView.mm; sourceTree = ""; }; + 97A8000E18B21395000C07A2 /* SearchBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SearchBar.h; sourceTree = ""; }; + 97A8000F18B21395000C07A2 /* SearchBar.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SearchBar.mm; sourceTree = ""; }; + 97A8001218B2140A000C07A2 /* SearchUniversalCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SearchUniversalCell.h; sourceTree = ""; }; + 97A8001318B2140A000C07A2 /* SearchUniversalCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SearchUniversalCell.m; sourceTree = ""; }; + 97A8001A18B25497000C07A2 /* SegmentedControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SegmentedControl.h; sourceTree = ""; }; + 97A8001B18B25497000C07A2 /* SegmentedControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SegmentedControl.m; sourceTree = ""; }; + 97A8002518B2741C000C07A2 /* SearchCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SearchCell.h; sourceTree = ""; }; + 97A8002618B2741C000C07A2 /* SearchCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SearchCell.m; sourceTree = ""; }; + 97A8002918B51238000C07A2 /* SearchActivityProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SearchActivityProtocol.h; sourceTree = ""; }; + 97ABBA4318C8DF620079333C /* PlacePageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlacePageView.h; sourceTree = ""; }; + 97ABBA4418C8DF620079333C /* PlacePageView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PlacePageView.mm; sourceTree = ""; }; 97C9851C186AE3C500AF7E9E /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = ""; }; 97C9851D186AE3C500AF7E9E /* Reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = ""; }; 97C98520186AE3CF00AF7E9E /* AppInfo.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AppInfo.mm; sourceTree = ""; }; @@ -1779,8 +1800,6 @@ FA054610155C465E001F4E37 /* SelectSetVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SelectSetVC.h; path = Bookmarks/SelectSetVC.h; sourceTree = SOURCE_ROOT; }; FA054611155C465E001F4E37 /* SelectSetVC.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SelectSetVC.mm; path = Bookmarks/SelectSetVC.mm; sourceTree = SOURCE_ROOT; }; FA065FEC128614C400FEA989 /* MainWindow-iPad.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "MainWindow-iPad.xib"; path = "Resources-iPad/MainWindow-iPad.xib"; sourceTree = SOURCE_ROOT; }; - FA09E00F13F71F6C007E69CA /* SearchVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SearchVC.h; sourceTree = ""; }; - FA09E01013F71F6C007E69CA /* SearchVC.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SearchVC.mm; sourceTree = ""; }; FA14064D162A6288002BC1ED /* empty@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "empty@2x.png"; path = "Bookmarks/empty@2x.png"; sourceTree = SOURCE_ROOT; }; FA14064E162A6288002BC1ED /* empty.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = empty.png; path = Bookmarks/empty.png; sourceTree = SOURCE_ROOT; }; FA14064F162A6288002BC1ED /* eye.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = eye.png; path = Bookmarks/eye.png; sourceTree = SOURCE_ROOT; }; @@ -1809,8 +1828,6 @@ FA765AB01737BC6D00279CFF /* 44x58-lite.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "44x58-lite.png"; sourceTree = ""; }; FA765AB11737BC6D00279CFF /* 64-lite.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "64-lite.png"; sourceTree = ""; }; FA765AB21737BC6D00279CFF /* 320-lite.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "320-lite.png"; sourceTree = ""; }; - FA81AE3114D061BF00A0D70D /* SearchCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SearchCell.h; sourceTree = ""; }; - FA81AE3214D061BF00A0D70D /* SearchCell.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SearchCell.mm; sourceTree = ""; }; FA85F632145DDDC20090E1A0 /* packed_polygons.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = packed_polygons.bin; path = ../../data/packed_polygons.bin; sourceTree = SOURCE_ROOT; }; FA87151A12B1518F00592DAF /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; FA8F8937132D5DB00048E3FE /* libtomcrypt.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libtomcrypt.a; sourceTree = SOURCE_ROOT; }; @@ -2461,14 +2478,14 @@ 97B4E9271851DAB300BEC5D7 /* Custom Views */, 9789DB53188D5DFF007C6FAE /* In App Messaging */, 9770A04618AD19D300126E5C /* More Apps */, + FA4135DF120A25B90062D5B4 /* Settings */, + 97A8000918B210DC000C07A2 /* Search */, 978F9251183BD530000D6C7C /* NavigationController.h */, 978F9252183BD530000D6C7C /* NavigationController.mm */, 97D40C0C184E389000A1D572 /* MailComposeViewController.h */, 97D40C0D184E389000A1D572 /* MailComposeViewController.m */, 97387544184E475000170BC4 /* MessageComposeViewController.h */, 97387545184E475000170BC4 /* MessageComposeViewController.m */, - FA09E00F13F71F6C007E69CA /* SearchVC.h */, - FA09E01013F71F6C007E69CA /* SearchVC.mm */, EE7F297C1219ECA300EB67A9 /* RenderBuffer.hpp */, EE7F297D1219ECA300EB67A9 /* RenderBuffer.mm */, EE16192B126E374500622BD0 /* RenderContext.hpp */, @@ -2479,8 +2496,6 @@ 1D3623250D0F684500981E51 /* MapsAppDelegate.mm */, 46F8A2EB10EB63040045521A /* MapViewController.h */, EED10A4411F78D120095FAD4 /* MapViewController.mm */, - FA81AE3114D061BF00A0D70D /* SearchCell.h */, - FA81AE3214D061BF00A0D70D /* SearchCell.mm */, FAF457E415597BC100DCCC49 /* Framework.h */, FAF457E615597D4600DCCC49 /* Framework.cpp */, EDC5C541175F2CA600420E92 /* ShareActionSheet.h */, @@ -2509,7 +2524,6 @@ FA36B8011540388B004560CC /* Bookmarks */, FA34BEC71338D6DB00FFB2A7 /* Common */, FA6E1F1B124E6B2800F59149 /* Platform */, - FA4135DF120A25B90062D5B4 /* Settings */, 080E96DDFE201D6D7F000001 /* Classes */, 974726401832306C006B7CB7 /* Categories */, 29B97315FDCFA39411CA2CEA /* Other Sources */, @@ -2659,6 +2673,24 @@ name = Cells; sourceTree = ""; }; + 97A8000918B210DC000C07A2 /* Search */ = { + isa = PBXGroup; + children = ( + 97A8000A18B21363000C07A2 /* SearchView.h */, + 97A8000B18B21363000C07A2 /* SearchView.mm */, + 97A8000E18B21395000C07A2 /* SearchBar.h */, + 97A8000F18B21395000C07A2 /* SearchBar.mm */, + 97A8001A18B25497000C07A2 /* SegmentedControl.h */, + 97A8001B18B25497000C07A2 /* SegmentedControl.m */, + 97A8002518B2741C000C07A2 /* SearchCell.h */, + 97A8002618B2741C000C07A2 /* SearchCell.m */, + 97A8001218B2140A000C07A2 /* SearchUniversalCell.h */, + 97A8001318B2140A000C07A2 /* SearchUniversalCell.m */, + 97A8002918B51238000C07A2 /* SearchActivityProtocol.h */, + ); + name = Search; + sourceTree = ""; + }; 97B4E9271851DAB300BEC5D7 /* Custom Views */ = { isa = PBXGroup; children = ( @@ -3186,6 +3218,7 @@ FA29FDA9141E77F8004ADF66 /* Preferences.mm */, ); name = Settings; + path = ..; sourceTree = ""; }; FA6E1F1B124E6B2800F59149 /* Platform */ = { @@ -5047,13 +5080,16 @@ FAFCB63613366E78001A5C59 /* WebViewController.mm in Sources */, FA34BECA1338D72F00FFB2A7 /* CustomAlertView.mm in Sources */, 97F61781183E6172009919E2 /* LocationButton.mm in Sources */, - FA09E01113F71F6C007E69CA /* SearchVC.mm in Sources */, 9747264318323080006B7CB7 /* UIKitCategories.m in Sources */, 97719D421843AF1E00BDD815 /* ScopeView.m in Sources */, + 97A8001418B2140A000C07A2 /* SearchUniversalCell.m in Sources */, + 97A8002718B2741C000C07A2 /* SearchCell.m in Sources */, 97D807B818A92AAB00D416E0 /* MoreAppsVC.mm in Sources */, FABF223E13FAA97A003D4D49 /* CompassView.mm in Sources */, 9789DB5A188D94F9007C6FAE /* InterstitialView.mm in Sources */, FA29FDAA141E77F8004ADF66 /* Preferences.mm in Sources */, + 97A8001C18B25497000C07A2 /* SegmentedControl.m in Sources */, + 97A8000C18B21363000C07A2 /* SearchView.mm in Sources */, FAA5C2A2144F135F005337F6 /* LocationManager.mm in Sources */, FA81AE3314D061BF00A0D70D /* SearchCell.mm in Sources */, 97DEA09618D75BB000C5F963 /* ContextViews.mm in Sources */, @@ -5079,6 +5115,7 @@ 9747277B18328E65006B7CB7 /* SideToolbar.mm in Sources */, 97F61794183E7445009919E2 /* LinkCell.m in Sources */, EDCB4E8E175E67120005AA35 /* PlacePreviewViewController.mm in Sources */, + 97A8001018B21395000C07A2 /* SearchBar.m in Sources */, EDC5C543175F2CA600420E92 /* ShareActionSheet.mm in Sources */, EDFC74D0177AE6C500FAF21F /* PlaceAndCompasView.mm in Sources */, ED48BBB117BE6EA8003E7E92 /* MWMApi.mm in Sources */, @@ -5101,6 +5138,7 @@ 97C98643186C487900AF7E9E /* MPInterstitialCustomEvent.m in Sources */, FAFB08EA151215EE0041901D /* MapsAppDelegate.mm in Sources */, FAFB08EB151215EE0041901D /* EAGLView.mm in Sources */, + 97A8001118B21395000C07A2 /* SearchBar.mm in Sources */, 97C98636186C487900AF7E9E /* MPAnalyticsTracker.m in Sources */, FAFB08EC151215EE0041901D /* MapViewController.mm in Sources */, 97C98601186C487900AF7E9E /* CJSONSerializedData.m in Sources */, @@ -5126,12 +5164,12 @@ 97C9863C186C487900AF7E9E /* MPSessionTracker.m in Sources */, 97C98637186C487900AF7E9E /* MPError.m in Sources */, 97C9863E186C487900AF7E9E /* MPTimer.m in Sources */, + 97A8001D18B25497000C07A2 /* SegmentedControl.m in Sources */, 97C9860C186C487900AF7E9E /* MPLegacyBannerCustomEventAdapter.m in Sources */, FAFB08F2151215EE0041901D /* CustomAlertView.mm in Sources */, 97F61782183E6172009919E2 /* LocationButton.mm in Sources */, 97C9860E186C487900AF7E9E /* MPAdAlertManager.m in Sources */, 97C985F1186C487900AF7E9E /* MPGoogleAdMobBannerCustomEvent.m in Sources */, - FAFB08F3151215EE0041901D /* SearchVC.mm in Sources */, 9747264418323080006B7CB7 /* UIKitCategories.m in Sources */, 97C9863D186C487900AF7E9E /* MPStoreKitProvider.m in Sources */, 97C98600186C487900AF7E9E /* CJSONSerialization.m in Sources */, @@ -5154,13 +5192,13 @@ 97C9861F186C487900AF7E9E /* MPInterstitialAdManager.m in Sources */, 97C9862B186C487900AF7E9E /* MRCalendarManager.m in Sources */, 97C98616186C487900AF7E9E /* MPLastResortDelegate.m in Sources */, - FAFB08F8151215EE0041901D /* SearchCell.mm in Sources */, 97C98635186C487900AF7E9E /* UIWebView+MPAdditions.m in Sources */, 97C985F2186C487900AF7E9E /* MPGoogleAdMobInterstitialCustomEvent.m in Sources */, 978F9241183B660F000D6C7C /* SettingsViewController.mm in Sources */, 97C98629186C487900AF7E9E /* MRAdViewDisplayController.m in Sources */, 97C98617186C487900AF7E9E /* MPProgressOverlayView.m in Sources */, 97C98632186C487900AF7E9E /* MRVideoPlayerManager.m in Sources */, + 97A8001518B2140A000C07A2 /* SearchUniversalCell.m in Sources */, 97C985F8186C487900AF7E9E /* MPMillennialInterstitialCustomEvent.m in Sources */, F7B90CD41521E6D200C054EE /* CustomNavigationView.mm in Sources */, 97C98605186C487900AF7E9E /* CJSONScanner.m in Sources */, @@ -5172,6 +5210,7 @@ 97C9860F186C487900AF7E9E /* MPAdBrowserController.m in Sources */, FA5D4F1A1557F79900E7D8BB /* PlacePageVC.mm in Sources */, FAF457E815597D4600DCCC49 /* Framework.cpp in Sources */, + 97A8002818B2741C000C07A2 /* SearchCell.m in Sources */, 97C9860A186C487900AF7E9E /* MPBannerCustomEventAdapter.m in Sources */, 97C98621186C487900AF7E9E /* MPInterstitialViewController.m in Sources */, FA054613155C465E001F4E37 /* SelectSetVC.mm in Sources */, @@ -5210,6 +5249,7 @@ EDFC74D1177AE6C500FAF21F /* PlaceAndCompasView.mm in Sources */, ED48BBB217BE6EA8003E7E92 /* MWMApi.mm in Sources */, 97C9862F186C487900AF7E9E /* MRJavaScriptEventEmitter.m in Sources */, + 97A8000D18B21363000C07A2 /* SearchView.mm in Sources */, 97C98628186C487900AF7E9E /* MRAdView.m in Sources */, 97C9862A186C487900AF7E9E /* MRBundleManager.m in Sources */, ED48BBBB17C2B1E2003E7E92 /* CircleView.mm in Sources */,