diff --git a/iphone/Maps/Classes/SearchCell.h b/iphone/Maps/Classes/SearchCell.h new file mode 100644 index 0000000000..bb378a2800 --- /dev/null +++ b/iphone/Maps/Classes/SearchCell.h @@ -0,0 +1,18 @@ +#import + +@interface SearchCell : UITableViewCell +{ + UILabel * featureName; + UILabel * featureType; + UILabel * featureCountry; + UILabel * featureDistance; +} + +@property (nonatomic, retain) IBOutlet UILabel * featureName; +@property (nonatomic, retain) IBOutlet UILabel * featureType; +@property (nonatomic, retain) IBOutlet UILabel * featureCountry; +@property (nonatomic, retain) IBOutlet UILabel * featureDistance; + +- (id)initWithReuseIdentifier:(NSString *)identifier; + +@end diff --git a/iphone/Maps/Classes/SearchCell.mm b/iphone/Maps/Classes/SearchCell.mm new file mode 100644 index 0000000000..1a922b9931 --- /dev/null +++ b/iphone/Maps/Classes/SearchCell.mm @@ -0,0 +1,63 @@ +#import "SearchCell.h" +#import "CompassView.h" + +@implementation SearchCell + +@synthesize featureName; +@synthesize featureType; +@synthesize featureCountry; +@synthesize featureDistance; + +- (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] autorelease]; + featureName.font = large; + featureType = [[[UILabel alloc] init] autorelease]; + featureType.font = small; + featureType.textColor = [UIColor grayColor]; + featureCountry = [[[UILabel alloc] init] autorelease]; + featureCountry.font = small; + featureCountry.textColor = [UIColor grayColor]; + featureCountry.textAlignment = UITextAlignmentRight; + featureDistance = [[[UILabel alloc] init] autorelease]; + 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 padding + CGFloat const KPaddingX = 6.0; + CGFloat const KPaddingBottom = 4.0; + + r.origin.x += KPaddingX; + r.size.width -= 2 * KPaddingX; + r.size.height -= KPaddingBottom; + CGFloat const w = r.size.width; + CGFloat const h = r.size.height; + CGFloat const xDelim = r.origin.x + w / 3 * 2; + CGFloat const yDelim = r.origin.y + h / 3 * 2; + featureName.frame = CGRectMake(r.origin.x, r.origin.y, xDelim - r.origin.x, yDelim - r.origin.y); + featureType.frame = CGRectMake(r.origin.x, yDelim, xDelim - r.origin.x, r.origin.y + h - yDelim); + featureCountry.frame = CGRectMake(xDelim, r.origin.y, r.origin.x + w - xDelim, yDelim - r.origin.y); + featureDistance.frame = CGRectMake(xDelim, yDelim, r.origin.x + w - xDelim, r.origin.y + h - yDelim); +} + +@end diff --git a/iphone/Maps/Classes/SearchVC.mm b/iphone/Maps/Classes/SearchVC.mm index 0bdfe460fa..d6757f0386 100644 --- a/iphone/Maps/Classes/SearchVC.mm +++ b/iphone/Maps/Classes/SearchVC.mm @@ -2,6 +2,7 @@ #import "CompassView.h" #import "LocationManager.h" #import "SearchBannerChecker.h" +#import "SearchCell.h" #include "../../geometry/angles.hpp" #include "../../geometry/distance_on_sphere.hpp" @@ -9,6 +10,7 @@ #include "../../platform/platform.hpp" #include "../../indexer/mercator.hpp" #include "../../map/framework.hpp" +#include "../../map/measurement_utils.hpp" #include "../../search/result.hpp" SearchVC * g_searchVC = nil; @@ -48,8 +50,8 @@ static void OnSearchResultCallback(search::Results const & res, int queryId) { Wrapper * w = [[Wrapper alloc] initWithResult:res]; [g_searchVC performSelectorOnMainThread:@selector(addResult:) - withObject:w - waitUntilDone:NO]; + withObject:w + waitUntilDone:NO]; [w release]; } } @@ -280,49 +282,54 @@ static void OnSearchResultCallback(search::Results const & res, int queryId) //*********** End of SearchBar handlers ************************************* //*************************************************************************** -- (void)updateCellAngle:(UITableViewCell *)cell withIndex:(NSUInteger)index andAngle:(double)northDeg ++ (NSString *)formatDistance:(double)meters { - CLLocation * loc = [m_locationManager lastLocation]; - if (loc) + if (meters < 0.) + return nil; + + uint64_t shortUnits = (uint64_t)meters; + double longUnits = meters/1000.0; + // @TODO localize measurements + static NSString * shortLabel = @"m"; + static NSString * longLabel = @"km"; + Settings::Units u = Settings::Metric; + Settings::Get("Units", u); + switch (u) { - m2::PointD const center = m_results[index].GetFeatureCenter(); - double const angle = ang::AngleTo(m2::PointD(MercatorBounds::LonToX(loc.coordinate.longitude), - MercatorBounds::LatToY(loc.coordinate.latitude)), center) + northDeg / 180. * math::pi; + case Settings::Foot: + shortUnits = (uint64_t)MeasurementUtils::MetersToFeet(meters); + longUnits = MeasurementUtils::MetersToMiles(meters); + shortLabel = @"ft"; + longLabel = @"mi"; + break; - if (m_results[index].GetResultType() == search::Result::RESULT_FEATURE) - { - CompassView * cv = (CompassView *)cell.accessoryView; - if (!cv) - { - float const h = m_table.rowHeight * 0.6; - cv = [[CompassView alloc] initWithFrame:CGRectMake(0, 0, h, h)]; - cell.accessoryView = cv; - [cv release]; - } - cv.angle = angle; - } - } -} + case Settings::Yard: + shortUnits = (uint64_t)MeasurementUtils::MetersToYards(meters); + longUnits = MeasurementUtils::MetersToMiles(meters); + shortLabel = @"yd"; + longLabel = @"mi"; + break; -- (void)updateCellDistance:(UITableViewCell *)cell withIndex:(NSUInteger)index -{ - CLLocation * loc = [m_locationManager lastLocation]; - if (loc) - { - m2::PointD const center = m_results[index].GetFeatureCenter(); - double const centerLat = MercatorBounds::YToLat(center.y); - double const centerLon = MercatorBounds::XToLon(center.x); - double const distance = ms::DistanceOnEarth(loc.coordinate.latitude, loc.coordinate.longitude, centerLat, centerLon); - - // @TODO use imperial system from the settings if needed - // @TODO use meters too - // NSLocalizedString(@"%.1lf m", @"Search results - Metres") - // NSLocalizedString(@"%.1lf ft", @"Search results - Feet") - // NSLocalizedString(@"%.1lf mi", @"Search results - Miles") - // NSLocalizedString(@"%.1lf yd", @"Search results - Yards") - cell.detailTextLabel.text = [NSString stringWithFormat:NSLocalizedString(@"%.1lf km", @"Search results - Kilometres"), - distance / 1000.0]; + case Settings::Metric: + shortLabel = @"m"; + longLabel = @"km"; + break; } + + // NSLocalizedString(@"%.1lf m", @"Search results - Metres") + // NSLocalizedString(@"%.1lf ft", @"Search results - Feet") + // NSLocalizedString(@"%.1lf mi", @"Search results - Miles") + // NSLocalizedString(@"%.1lf yd", @"Search results - Yards") + + if (shortUnits < 1000) + return [NSString stringWithFormat:@"%qu %@", shortUnits, shortLabel]; + + uint64_t const longUnitsRounded = (uint64_t)(longUnits); + // reduce precision for big distances and remove zero for x.0-like numbers + if (longUnitsRounded > 10 || (longUnitsRounded && (uint64_t)(longUnits * 10.0) == longUnitsRounded * 10)) + return [NSString stringWithFormat:@"%qu %@", longUnitsRounded, longLabel]; + + return [NSString stringWithFormat:@"%.1lf %@", longUnits, longLabel]; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView @@ -335,30 +342,45 @@ static void OnSearchResultCallback(search::Results const & res, int queryId) return m_results.size(); } -- (UITableViewCell *)tableView:(UITableView *)tableView - cellForRowAtIndexPath:(NSIndexPath *)indexPath +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"SearchVCTableViewCell"]; - if (!cell) + if (indexPath.row >= m_results.size()) { - cell = [[[UITableViewCell alloc] - initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"SearchVCTableViewCell"] - autorelease]; + ASSERT(false, ("Invalid m_results with size", m_results.size())); + return nil; } - - cell.accessoryView = nil; - if (indexPath.row < m_results.size()) + + search::Result const & r = m_results[indexPath.row]; + switch (r.GetResultType()) { - search::Result const & r = m_results[indexPath.row]; - cell.textLabel.text = [NSString stringWithUTF8String:r.GetString()]; - if (r.GetResultType() == search::Result::RESULT_FEATURE) - [self updateCellDistance:cell withIndex:indexPath.row]; - else - cell.detailTextLabel.text = nil; + case search::Result::RESULT_FEATURE: + { + SearchCell * cell = (SearchCell *)[tableView dequeueReusableCellWithIdentifier:@"FeatureCell"]; + if (!cell) + cell = [[[SearchCell alloc] initWithReuseIdentifier:@"FeatureCell"] autorelease]; + + cell.featureName.text = [NSString stringWithUTF8String:r.GetString()]; + cell.featureCountry.text = [NSString stringWithUTF8String:r.GetRegionString()]; + cell.featureType.text = [NSString stringWithUTF8String:r.GetFeatureType()]; + cell.featureDistance.text = [SearchVC formatDistance:r.GetDistanceFromCenter()]; + return cell; + } + break; + + case search::Result::RESULT_SUGGESTION: + { + UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"SuggestionCell"]; + if (!cell) + cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"SuggestionCell"] autorelease]; + cell.textLabel.text = [NSString stringWithUTF8String:r.GetString()]; + return cell; + } + break; + + default: + ASSERT(false, ("Unsupported search result type")); + return nil; } - else - cell.textLabel.text = @"BUG"; - return cell; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath @@ -393,6 +415,23 @@ static void OnSearchResultCallback(search::Results const & res, int queryId) } +- (void)updateCompassFor:(UITableViewCell *)cell withResult:(search::Result const &)res andAngle:(double)angle +{ + CompassView * compass = nil; + if (cell.accessoryView == nil) + { + float const h = m_table.rowHeight * 0.6; + compass = [[CompassView alloc] initWithFrame:CGRectMake(0, 0, h, h)]; + cell.accessoryView = compass; + [compass release]; + } + else if ([cell.accessoryView isKindOfClass:[CompassView class]]) + compass = (CompassView *)cell.accessoryView; + + if (compass) + compass.angle = angle; +} + //****************************************************************** //*********** Location manager callbacks *************************** - (void)onLocationStatusChanged:(location::TLocationStatus)newStatus @@ -402,25 +441,28 @@ static void OnSearchResultCallback(search::Results const & res, int queryId) - (void)onGpsUpdate:(location::GpsInfo const &)info { - - NSArray * cells = [m_table visibleCells]; - for (NSUInteger i = 0; i < cells.count; ++i) - { - UITableViewCell * cell = (UITableViewCell *)[cells objectAtIndex:i]; - [self updateCellDistance:cell withIndex:[m_table indexPathForCell:cell].row]; - } m_framework->GetSearchEngine()->SetPosition(info.m_latitude, info.m_longitude); } - (void)onCompassUpdate:(location::CompassInfo const &)info { + CLLocation * loc = m_locationManager.lastLocation; + if (loc == nil) + return; + + double const northDeg = (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 const index = [m_table indexPathForCell:cell].row; - if (m_results[index].GetResultType() == search::Result::RESULT_FEATURE) - [self updateCellAngle:cell withIndex:index andAngle:((info.m_trueHeading < 0) ? info.m_magneticHeading : info.m_trueHeading)]; + search::Result const & res = m_results[[m_table indexPathForCell:cell].row]; + if (res.GetResultType() == search::Result::RESULT_FEATURE) + { + m2::PointD const center = res.GetFeatureCenter(); + double const angle = ang::AngleTo(m2::PointD(MercatorBounds::LonToX(loc.coordinate.longitude), + MercatorBounds::LatToY(loc.coordinate.latitude)), center) + northDeg / 180. * math::pi; + [self updateCompassFor:cell withResult:res andAngle:angle]; + } } } //*********** End of Location manager callbacks ******************** diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index 13f9da7610..a1db4cbdb5 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -82,6 +82,7 @@ FA5005671287BFCE002961F0 /* Icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FA5005611287BFCE002961F0 /* Icon@2x.png */; }; FA500588128907F0002961F0 /* visibility.txt in Resources */ = {isa = PBXBuildFile; fileRef = FA500587128907F0002961F0 /* visibility.txt */; }; FA64D9A913F975AD00350ECF /* types.txt in Resources */ = {isa = PBXBuildFile; fileRef = FA64D9A813F975AD00350ECF /* types.txt */; }; + 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 */; }; @@ -613,7 +614,7 @@ 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 1D3623240D0F684500981E51 /* MapsAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MapsAppDelegate.h; sourceTree = ""; }; 1D3623250D0F684500981E51 /* MapsAppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = MapsAppDelegate.mm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; - 1D6058910D05DD3D006BFB54 /* MapsWithMe.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; name = MapsWithMe.app; path = "MWM Beta.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 1D6058910D05DD3D006BFB54 /* MapsWithMe.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; name = MapsWithMe.app; path = "MWM Dbg Lite.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 288765070DF74369002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 28A0AB4B0D9B1048005BE974 /* Maps_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Maps_Prefix.pch; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; @@ -699,6 +700,8 @@ FA5005611287BFCE002961F0 /* Icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon@2x.png"; sourceTree = SOURCE_ROOT; }; FA500587128907F0002961F0 /* visibility.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = visibility.txt; path = ../../data/visibility.txt; sourceTree = SOURCE_ROOT; }; FA64D9A813F975AD00350ECF /* types.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = types.txt; path = ../../data/types.txt; sourceTree = SOURCE_ROOT; }; + 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; }; @@ -1269,8 +1272,6 @@ 080E96DDFE201D6D7F000001 /* Classes */ = { isa = PBXGroup; children = ( - FABF223C13FAA97A003D4D49 /* CompassView.h */, - FABF223D13FAA97A003D4D49 /* CompassView.mm */, FA09E00F13F71F6C007E69CA /* SearchVC.h */, FA09E01013F71F6C007E69CA /* SearchVC.mm */, EE7F297C1219ECA300EB67A9 /* RenderBuffer.hpp */, @@ -1285,6 +1286,10 @@ EED10A4411F78D120095FAD4 /* MapViewController.mm */, FA0B7E5F1487736200CAB3F2 /* SearchBannerChecker.h */, FA0B7E601487736200CAB3F2 /* SearchBannerChecker.mm */, + FA81AE3114D061BF00A0D70D /* SearchCell.h */, + FA81AE3214D061BF00A0D70D /* SearchCell.mm */, + FABF223C13FAA97A003D4D49 /* CompassView.h */, + FABF223D13FAA97A003D4D49 /* CompassView.mm */, ); path = Classes; sourceTree = ""; @@ -2648,6 +2653,7 @@ FAA5C2A2144F135F005337F6 /* LocationManager.mm in Sources */, FA0B7E611487736200CAB3F2 /* SearchBannerChecker.mm in Sources */, FA0B7E631487747B00CAB3F2 /* GetActiveConnectionType.mm in Sources */, + FA81AE3314D061BF00A0D70D /* SearchCell.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };