Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
9
iphone/Maps/UI/Autoupdate/MWMAutoupdateController.h
Normal file
9
iphone/Maps/UI/Autoupdate/MWMAutoupdateController.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#import "MWMViewController.h"
|
||||
|
||||
#include <CoreApi/Framework.h>
|
||||
|
||||
@interface MWMAutoupdateController : MWMViewController
|
||||
|
||||
+ (instancetype)instanceWithPurpose:(Framework::DoAfterUpdate)todo;
|
||||
|
||||
@end
|
||||
309
iphone/Maps/UI/Autoupdate/MWMAutoupdateController.mm
Normal file
309
iphone/Maps/UI/Autoupdate/MWMAutoupdateController.mm
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
#import "MWMAutoupdateController.h"
|
||||
#import "MWMCircularProgress.h"
|
||||
#import "MWMStorage+UI.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
#include "platform/downloader_defines.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace
|
||||
{
|
||||
NSString *RootId() { return @(GetFramework().GetStorage().GetRootId().c_str()); }
|
||||
enum class State
|
||||
{
|
||||
Downloading,
|
||||
Waiting
|
||||
};
|
||||
} // namespace
|
||||
|
||||
using namespace storage;
|
||||
|
||||
@interface MWMAutoupdateView : UIView
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UIImageView * image;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * imageMinHeight;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * imageHeight;
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UILabel * title;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * titleTopOffset;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * titleImageOffset;
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UILabel * text;
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UIButton * primaryButton;
|
||||
@property(weak, nonatomic) IBOutlet UIButton * secondaryButton;
|
||||
@property(weak, nonatomic) IBOutlet UIView * spinnerView;
|
||||
@property(weak, nonatomic) IBOutlet UILabel * progressLabel;
|
||||
@property(weak, nonatomic) IBOutlet UILabel * legendLabel;
|
||||
|
||||
@property(weak, nonatomic) id<MWMCircularProgressProtocol> delegate;
|
||||
|
||||
@property(nonatomic) MWMCircularProgress * spinner;
|
||||
@property(copy, nonatomic) NSString * updateSize;
|
||||
@property(nonatomic) State state;
|
||||
|
||||
- (void)startSpinner;
|
||||
- (void)stopSpinner;
|
||||
- (void)updateForSize:(CGSize)size;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMAutoupdateView
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
[self updateForSize:frame.size];
|
||||
super.frame = frame;
|
||||
}
|
||||
|
||||
- (void)updateForSize:(CGSize)size
|
||||
{
|
||||
BOOL const hideImage = (self.imageHeight.multiplier * size.height <= self.imageMinHeight.constant);
|
||||
self.titleImageOffset.priority = hideImage ? UILayoutPriorityDefaultLow : UILayoutPriorityDefaultHigh;
|
||||
self.image.hidden = hideImage;
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
-(void)setUpdateSize:(NSString *)updateSize
|
||||
{
|
||||
_updateSize = updateSize;
|
||||
self.primaryButton.localizedText =
|
||||
[NSString stringWithFormat:L(@"whats_new_auto_update_button_size"), self.updateSize];
|
||||
}
|
||||
|
||||
- (void)stateDownloading
|
||||
{
|
||||
self.state = State::Downloading;
|
||||
self.primaryButton.hidden = YES;
|
||||
[self startSpinner];
|
||||
self.secondaryButton.localizedText = L(@"downloader_hide_screen");
|
||||
}
|
||||
|
||||
- (void)stateWaiting
|
||||
{
|
||||
self.state = State::Waiting;
|
||||
[self stopSpinner];
|
||||
self.primaryButton.hidden = NO;
|
||||
self.secondaryButton.localizedText = L(@"whats_new_auto_update_button_later");
|
||||
}
|
||||
|
||||
- (void)startSpinner
|
||||
{
|
||||
self.primaryButton.hidden = YES;
|
||||
self.spinnerView.hidden = NO;
|
||||
self.progressLabel.hidden = NO;
|
||||
self.legendLabel.hidden = NO;
|
||||
self.spinner = [MWMCircularProgress downloaderProgressForParentView:self.spinnerView];
|
||||
self.spinner.delegate = self.delegate;
|
||||
self.spinner.state = MWMCircularProgressStateSpinner;
|
||||
}
|
||||
|
||||
- (void)stopSpinner
|
||||
{
|
||||
self.primaryButton.hidden = NO;
|
||||
self.spinnerView.hidden = YES;
|
||||
self.progressLabel.hidden = YES;
|
||||
self.legendLabel.hidden = YES;
|
||||
self.spinner = nil;
|
||||
}
|
||||
|
||||
- (void)setStatusForNodeName:(NSString *)nodeName rootAttributes:(NodeAttrs const &)nodeAttrs
|
||||
{
|
||||
auto const progress = nodeAttrs.m_downloadingProgress;
|
||||
if (progress.m_bytesTotal > 0)
|
||||
{
|
||||
CGFloat const prog = kMaxProgress * static_cast<CGFloat>(progress.m_bytesDownloaded) / progress.m_bytesTotal;
|
||||
self.spinner.progress = prog;
|
||||
|
||||
NSNumberFormatter * numberFormatter = [[NSNumberFormatter alloc] init];
|
||||
[numberFormatter setNumberStyle:NSNumberFormatterPercentStyle];
|
||||
[numberFormatter setMaximumFractionDigits:0];
|
||||
[numberFormatter setMultiplier:@100];
|
||||
NSString * percent = [numberFormatter stringFromNumber:@(prog)];
|
||||
NSString * downloadedSize = formattedSize(progress.m_bytesDownloaded);
|
||||
NSString * totalSize = formattedSize(progress.m_bytesTotal);
|
||||
self.progressLabel.text = [NSString stringWithFormat:L(@"downloader_percent"), percent, downloadedSize, totalSize];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.progressLabel.text = @"";
|
||||
}
|
||||
|
||||
BOOL const isApplying = nodeAttrs.m_status == storage::NodeStatus::Applying;
|
||||
NSString * format = L(isApplying ? @"downloader_applying" : @"downloader_process");
|
||||
self.legendLabel.text = [NSString stringWithFormat:format, nodeName];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface MWMAutoupdateController () <MWMCircularProgressProtocol, MWMStorageObserver>
|
||||
{
|
||||
std::unordered_set<CountryId> m_updatingCountries;
|
||||
}
|
||||
|
||||
@property(nonatomic) Framework::DoAfterUpdate todo;
|
||||
@property(nonatomic) MwmSize sizeInMB;
|
||||
@property(nonatomic) NodeErrorCode errorCode;
|
||||
@property(nonatomic) BOOL progressFinished;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMAutoupdateController
|
||||
|
||||
+ (instancetype)instanceWithPurpose:(Framework::DoAfterUpdate)todo
|
||||
{
|
||||
MWMAutoupdateController * controller =
|
||||
[[MWMAutoupdateController alloc] initWithNibName:[self className] bundle:NSBundle.mainBundle];
|
||||
controller.todo = todo;
|
||||
auto view = static_cast<MWMAutoupdateView *>(controller.view);
|
||||
view.delegate = controller;
|
||||
[[MWMStorage sharedStorage] addObserver:controller];
|
||||
[controller updateSize];
|
||||
return controller;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
self.progressFinished = NO;
|
||||
MWMAutoupdateView *view = (MWMAutoupdateView *)self.view;
|
||||
if (self.todo == Framework::DoAfterUpdate::AutoupdateMaps)
|
||||
{
|
||||
[view stateDownloading];
|
||||
[[MWMStorage sharedStorage] updateNode:RootId() onCancel:^{
|
||||
[self updateSize];
|
||||
[view stateWaiting];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[view stateWaiting];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dismiss
|
||||
{
|
||||
[static_cast<MWMAutoupdateView *>(self.view) stopSpinner];
|
||||
[self dismissViewControllerAnimated:YES completion:^{
|
||||
[[MWMStorage sharedStorage] removeObserver:self];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateSize
|
||||
{
|
||||
auto containerView = static_cast<MWMAutoupdateView *>(self.view);
|
||||
auto const & s = GetFramework().GetStorage();
|
||||
storage::Storage::UpdateInfo updateInfo;
|
||||
s.GetUpdateInfo(s.GetRootId(), updateInfo);
|
||||
MwmSize const updateSizeInBytes = updateInfo.m_totalDownloadSizeInBytes;
|
||||
containerView.updateSize = formattedSize(updateSizeInBytes);
|
||||
_sizeInMB = updateSizeInBytes / MB;
|
||||
}
|
||||
|
||||
- (IBAction)updateTap
|
||||
{
|
||||
MWMAutoupdateView *view = (MWMAutoupdateView *)self.view;
|
||||
[view stateDownloading];
|
||||
[[MWMStorage sharedStorage] updateNode:RootId() onCancel:^{
|
||||
[self updateSize];
|
||||
[view stateWaiting];
|
||||
}];
|
||||
}
|
||||
- (IBAction)hideTap { [self dismiss]; }
|
||||
|
||||
- (void)cancel
|
||||
{
|
||||
auto view = static_cast<MWMAutoupdateView *>(self.view);
|
||||
UIAlertController * alertController =
|
||||
[UIAlertController alertControllerWithTitle:nil
|
||||
message:nil
|
||||
preferredStyle:UIAlertControllerStyleActionSheet];
|
||||
alertController.popoverPresentationController.sourceView = view.secondaryButton;
|
||||
alertController.popoverPresentationController.sourceRect = view.secondaryButton.bounds;
|
||||
auto cancelDownloadAction =
|
||||
[UIAlertAction actionWithTitle:L(@"cancel_download")
|
||||
style:UIAlertActionStyleDestructive
|
||||
handler:^(UIAlertAction * action) {
|
||||
[[MWMStorage sharedStorage] cancelDownloadNode:RootId()];
|
||||
[self dismiss];
|
||||
}];
|
||||
[alertController addAction:cancelDownloadAction];
|
||||
auto cancelAction =
|
||||
[UIAlertAction actionWithTitle:L(@"cancel") style:UIAlertActionStyleCancel handler:nil];
|
||||
[alertController addAction:cancelAction];
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)viewWillTransitionToSize:(CGSize)size
|
||||
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
|
||||
{
|
||||
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
|
||||
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
|
||||
[static_cast<MWMAutoupdateView *>(self.view) updateForSize:size];
|
||||
} completion:nil];
|
||||
}
|
||||
|
||||
- (void)updateProcessStatus:(CountryId const &)countryId
|
||||
{
|
||||
auto const & s = GetFramework().GetStorage();
|
||||
NodeAttrs nodeAttrs;
|
||||
s.GetNodeAttrs(s.GetRootId(), nodeAttrs);
|
||||
auto view = static_cast<MWMAutoupdateView *>(self.view);
|
||||
NSString * nodeName = @(s.GetNodeLocalName(countryId).c_str());
|
||||
[view setStatusForNodeName:nodeName rootAttributes:nodeAttrs];
|
||||
if (nodeAttrs.m_downloadingProgress.m_bytesDownloaded == nodeAttrs.m_downloadingProgress.m_bytesTotal)
|
||||
self.progressFinished = YES;
|
||||
}
|
||||
|
||||
#pragma mark - MWMCircularProgressProtocol
|
||||
|
||||
- (void)progressButtonPressed:(MWMCircularProgress *)progress { [self cancel]; }
|
||||
|
||||
#pragma mark - MWMStorageObserver
|
||||
|
||||
- (void)processCountryEvent:(NSString *)countryId
|
||||
{
|
||||
NodeStatuses nodeStatuses;
|
||||
GetFramework().GetStorage().GetNodeStatuses(countryId.UTF8String, nodeStatuses);
|
||||
if (nodeStatuses.m_status == NodeStatus::Error)
|
||||
{
|
||||
self.errorCode = nodeStatuses.m_error;
|
||||
SEL const process = @selector(processError);
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:process object:nil];
|
||||
[self performSelector:process withObject:nil afterDelay:0.2];
|
||||
}
|
||||
|
||||
if (!nodeStatuses.m_groupNode)
|
||||
{
|
||||
switch (nodeStatuses.m_status)
|
||||
{
|
||||
case NodeStatus::Error:
|
||||
case NodeStatus::OnDisk: m_updatingCountries.erase(countryId.UTF8String); break;
|
||||
default: m_updatingCountries.insert(countryId.UTF8String);
|
||||
}
|
||||
}
|
||||
|
||||
if (self.progressFinished && m_updatingCountries.empty())
|
||||
[self dismiss];
|
||||
else
|
||||
[self updateProcessStatus:countryId.UTF8String];
|
||||
}
|
||||
|
||||
- (void)processError
|
||||
{
|
||||
[self updateSize];
|
||||
[static_cast<MWMAutoupdateView *>(self.view) stateWaiting];
|
||||
[[MWMStorage sharedStorage] cancelDownloadNode:RootId()];
|
||||
}
|
||||
|
||||
- (void)processCountry:(NSString *)countryId
|
||||
downloadedBytes:(uint64_t)downloadedBytes
|
||||
totalBytes:(uint64_t)totalBytes
|
||||
{
|
||||
if (m_updatingCountries.find(countryId.UTF8String) != m_updatingCountries.end())
|
||||
[self updateProcessStatus:countryId.UTF8String];
|
||||
}
|
||||
|
||||
@end
|
||||
203
iphone/Maps/UI/Autoupdate/MWMAutoupdateController.xib
Normal file
203
iphone/Maps/UI/Autoupdate/MWMAutoupdateController.xib
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MWMAutoupdateController">
|
||||
<connections>
|
||||
<outlet property="view" destination="4Q8-tQ-qqq" id="Efv-Uv-ihD"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="4Q8-tQ-qqq" customClass="MWMAutoupdateView" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="CrL-6X-EJl" userLabel="Container">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5Cw-y3-ipv" userLabel="BoundsView">
|
||||
<rect key="frame" x="16" y="100" width="382" height="650"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vVR-Wh-jf5" userLabel="CenteredView">
|
||||
<rect key="frame" x="0.0" y="198.5" width="382" height="253.5"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalCompressionResistancePriority="749" image="Logo" translatesAutoresizingMaskIntoConstraints="NO" id="ym1-j4-Fn7">
|
||||
<rect key="frame" x="111" y="0.0" width="160" height="160"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="lessThanOrEqual" priority="800" constant="240" id="0ZT-MS-DwR"/>
|
||||
<constraint firstAttribute="height" relation="lessThanOrEqual" priority="800" constant="160" id="lKy-F4-tpj"/>
|
||||
<constraint firstAttribute="width" secondItem="ym1-j4-Fn7" secondAttribute="height" multiplier="1:1" id="yrH-8J-BCh"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Update your downloaded maps " textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MdA-9O-3gH" userLabel="Title">
|
||||
<rect key="frame" x="0.0" y="180" width="382" height="24"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="20"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium18:blackPrimaryText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="whats_new_auto_update_title"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Updated maps supports information about objects in the current state" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WLZ-Z2-pj2" userLabel="Text">
|
||||
<rect key="frame" x="0.0" y="220" width="382" height="33.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="whats_new_auto_update_message"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="CenteredView"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="400" id="3h0-Vi-2o8"/>
|
||||
<constraint firstItem="WLZ-Z2-pj2" firstAttribute="top" secondItem="MdA-9O-3gH" secondAttribute="bottom" constant="16" id="7GI-WD-Pbc"/>
|
||||
<constraint firstItem="MdA-9O-3gH" firstAttribute="centerX" secondItem="vVR-Wh-jf5" secondAttribute="centerX" id="EEg-qo-ze1"/>
|
||||
<constraint firstItem="ym1-j4-Fn7" firstAttribute="centerX" secondItem="vVR-Wh-jf5" secondAttribute="centerX" id="Fdz-HB-l8c"/>
|
||||
<constraint firstItem="ym1-j4-Fn7" firstAttribute="top" secondItem="vVR-Wh-jf5" secondAttribute="top" id="Jen-Ph-CoE"/>
|
||||
<constraint firstAttribute="bottom" secondItem="WLZ-Z2-pj2" secondAttribute="bottom" id="KyM-Hx-UGh"/>
|
||||
<constraint firstItem="WLZ-Z2-pj2" firstAttribute="width" secondItem="vVR-Wh-jf5" secondAttribute="width" id="Mvt-BV-8I4"/>
|
||||
<constraint firstItem="WLZ-Z2-pj2" firstAttribute="centerX" secondItem="vVR-Wh-jf5" secondAttribute="centerX" id="RGt-pT-PAB"/>
|
||||
<constraint firstItem="MdA-9O-3gH" firstAttribute="width" secondItem="vVR-Wh-jf5" secondAttribute="width" id="Vmk-jQ-wvC"/>
|
||||
<constraint firstItem="MdA-9O-3gH" firstAttribute="top" secondItem="vVR-Wh-jf5" secondAttribute="top" priority="740" id="dh3-iA-fGg"/>
|
||||
<constraint firstItem="MdA-9O-3gH" firstAttribute="top" secondItem="ym1-j4-Fn7" secondAttribute="bottom" priority="750" constant="20" id="vk2-tC-DOE"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="BoundsView"/>
|
||||
<constraints>
|
||||
<constraint firstItem="vVR-Wh-jf5" firstAttribute="height" relation="lessThanOrEqual" secondItem="5Cw-y3-ipv" secondAttribute="height" id="6PW-WV-Zsn"/>
|
||||
<constraint firstItem="vVR-Wh-jf5" firstAttribute="leading" secondItem="5Cw-y3-ipv" secondAttribute="leading" priority="999" id="9Eo-eG-hWU"/>
|
||||
<constraint firstItem="vVR-Wh-jf5" firstAttribute="centerY" secondItem="5Cw-y3-ipv" secondAttribute="centerY" id="TGt-HK-faY"/>
|
||||
<constraint firstAttribute="trailing" secondItem="vVR-Wh-jf5" secondAttribute="trailing" priority="999" id="ZA8-2V-Loy"/>
|
||||
<constraint firstItem="vVR-Wh-jf5" firstAttribute="centerX" secondItem="5Cw-y3-ipv" secondAttribute="centerX" id="oRE-Lp-pk2"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<button opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5nF-zu-Yw0">
|
||||
<rect key="frame" x="87" y="778" width="240" height="44"/>
|
||||
<color key="backgroundColor" red="0.12549019610000001" green="0.58823529409999997" blue="0.95294117649999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="240" id="ARV-BQ-Avk"/>
|
||||
<constraint firstAttribute="height" constant="44" id="eRe-p3-Uls"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="Update All Maps">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="FlatNormalButton"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="updateTap" destination="-1" eventType="touchUpInside" id="Xf3-wC-jZS"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="wordWrap" translatesAutoresizingMaskIntoConstraints="NO" id="iEE-M5-NnV">
|
||||
<rect key="frame" x="87" y="832" width="240" height="44"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="240" id="adp-HR-zDl"/>
|
||||
<constraint firstAttribute="height" constant="44" id="qzn-GV-spI"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<state key="normal" title="Manualy update later ">
|
||||
<color key="titleColor" red="0.01176470588" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="FlatNormalTransButton"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="hideTap" destination="-1" eventType="touchUpInside" id="fN3-tg-y0A"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SqI-nm-lNO">
|
||||
<rect key="frame" x="189" y="766" width="36" height="36"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="36" id="Lfg-Ln-a6V"/>
|
||||
<constraint firstAttribute="height" constant="36" id="oVz-gc-j9W"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Bax-Ah-It4">
|
||||
<rect key="frame" x="207" y="810" width="0.0" height="0.0"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BGi-NW-WUx" propertyAccessControl="none">
|
||||
<rect key="frame" x="207" y="812" width="0.0" height="0.0"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="ContainerView"/>
|
||||
<constraints>
|
||||
<constraint firstItem="SqI-nm-lNO" firstAttribute="centerX" secondItem="CrL-6X-EJl" secondAttribute="centerX" id="2zw-eu-fGT"/>
|
||||
<constraint firstItem="BGi-NW-WUx" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="CrL-6X-EJl" secondAttribute="leading" constant="8" id="3Zi-QM-5As"/>
|
||||
<constraint firstItem="5nF-zu-Yw0" firstAttribute="centerX" secondItem="CrL-6X-EJl" secondAttribute="centerX" id="9se-pw-oRi"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="BGi-NW-WUx" secondAttribute="trailing" constant="8" id="DES-sa-ZaG"/>
|
||||
<constraint firstItem="iEE-M5-NnV" firstAttribute="top" secondItem="5nF-zu-Yw0" secondAttribute="bottom" priority="500" constant="10" id="LWi-4H-ezA"/>
|
||||
<constraint firstItem="BGi-NW-WUx" firstAttribute="top" secondItem="Bax-Ah-It4" secondAttribute="bottom" constant="2" id="M4K-cH-fj1"/>
|
||||
<constraint firstAttribute="bottom" secondItem="iEE-M5-NnV" secondAttribute="bottom" constant="20" id="Mag-7y-dYi"/>
|
||||
<constraint firstItem="SqI-nm-lNO" firstAttribute="top" relation="greaterThanOrEqual" secondItem="5Cw-y3-ipv" secondAttribute="bottom" constant="16" id="ORg-MD-Tx2"/>
|
||||
<constraint firstAttribute="trailing" secondItem="5Cw-y3-ipv" secondAttribute="trailing" constant="16" id="QsU-yA-a5N"/>
|
||||
<constraint firstItem="5Cw-y3-ipv" firstAttribute="top" secondItem="CrL-6X-EJl" secondAttribute="top" constant="100" id="S3w-Tp-ulT"/>
|
||||
<constraint firstItem="ym1-j4-Fn7" firstAttribute="height" secondItem="CrL-6X-EJl" secondAttribute="height" multiplier="0.3" priority="750" id="fWB-qe-uuj"/>
|
||||
<constraint firstItem="Bax-Ah-It4" firstAttribute="centerX" secondItem="SqI-nm-lNO" secondAttribute="centerX" id="hBq-hC-lp9"/>
|
||||
<constraint firstItem="BGi-NW-WUx" firstAttribute="centerX" secondItem="Bax-Ah-It4" secondAttribute="centerX" id="hg3-Yn-odO"/>
|
||||
<constraint firstItem="5Cw-y3-ipv" firstAttribute="leading" secondItem="CrL-6X-EJl" secondAttribute="leading" constant="16" id="iWS-dU-Nwv"/>
|
||||
<constraint firstItem="iEE-M5-NnV" firstAttribute="top" secondItem="BGi-NW-WUx" secondAttribute="bottom" constant="20" id="icj-Sj-aYh"/>
|
||||
<constraint firstItem="5nF-zu-Yw0" firstAttribute="top" secondItem="5Cw-y3-ipv" secondAttribute="bottom" priority="250" constant="24" id="jli-Ut-tZT"/>
|
||||
<constraint firstItem="iEE-M5-NnV" firstAttribute="centerX" secondItem="CrL-6X-EJl" secondAttribute="centerX" id="pHJ-KJ-l9f"/>
|
||||
<constraint firstItem="Bax-Ah-It4" firstAttribute="top" secondItem="SqI-nm-lNO" secondAttribute="bottom" constant="8" id="uM5-w3-HCC"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MapAutoupdateView"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="CrL-6X-EJl" firstAttribute="width" secondItem="4Q8-tQ-qqq" secondAttribute="width" id="TM3-Rp-oI1"/>
|
||||
<constraint firstItem="CrL-6X-EJl" firstAttribute="height" secondItem="4Q8-tQ-qqq" secondAttribute="height" id="nfT-Ff-8TY"/>
|
||||
<constraint firstItem="CrL-6X-EJl" firstAttribute="centerX" secondItem="4Q8-tQ-qqq" secondAttribute="centerX" id="owQ-ao-M5Q"/>
|
||||
<constraint firstItem="CrL-6X-EJl" firstAttribute="centerY" secondItem="4Q8-tQ-qqq" secondAttribute="centerY" id="sM6-Cf-eHx"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="image" destination="ym1-j4-Fn7" id="XrK-nD-Jii"/>
|
||||
<outlet property="imageHeight" destination="fWB-qe-uuj" id="k07-Lv-4Ui"/>
|
||||
<outlet property="imageMinHeight" destination="lKy-F4-tpj" id="zef-yg-5d3"/>
|
||||
<outlet property="legendLabel" destination="BGi-NW-WUx" id="VoN-Ko-l6w"/>
|
||||
<outlet property="primaryButton" destination="5nF-zu-Yw0" id="CAx-NV-Vke"/>
|
||||
<outlet property="progressLabel" destination="Bax-Ah-It4" id="Gcb-na-cit"/>
|
||||
<outlet property="secondaryButton" destination="iEE-M5-NnV" id="clP-02-Qne"/>
|
||||
<outlet property="spinnerView" destination="SqI-nm-lNO" id="dBJ-ee-nQn"/>
|
||||
<outlet property="text" destination="WLZ-Z2-pj2" id="XFP-yM-UH3"/>
|
||||
<outlet property="title" destination="MdA-9O-3gH" id="7NW-eO-on5"/>
|
||||
<outlet property="titleImageOffset" destination="vk2-tC-DOE" id="40I-iU-krz"/>
|
||||
<outlet property="titleTopOffset" destination="dh3-iA-fGg" id="pM9-PQ-ryI"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="138" y="154"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="Logo" width="1024" height="1024"/>
|
||||
</resources>
|
||||
</document>
|
||||
144
iphone/Maps/UI/AvailableArea/AvailableArea.swift
Normal file
144
iphone/Maps/UI/AvailableArea/AvailableArea.swift
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
class AvailableArea: UIView {
|
||||
private enum Const {
|
||||
static let observeKeyPath = "sublayers"
|
||||
}
|
||||
|
||||
var deferNotification: Bool { return true }
|
||||
|
||||
private(set) var orientation = UIDeviceOrientation.unknown {
|
||||
didSet {
|
||||
scheduleNotification()
|
||||
}
|
||||
}
|
||||
|
||||
var shouldUpdateAreaFrame: Bool {
|
||||
if let insets = UIApplication.shared.delegate?.window??.safeAreaInsets {
|
||||
return insets.left > 0 || insets.right > 0
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var areaFrame: CGRect {
|
||||
return alternative(iPhone: {
|
||||
var frame = self.frame
|
||||
if self.shouldUpdateAreaFrame {
|
||||
switch self.orientation {
|
||||
case .landscapeLeft:
|
||||
frame.origin.x -= 16
|
||||
frame.size.width += 60
|
||||
case .landscapeRight:
|
||||
frame.origin.x -= 44
|
||||
frame.size.width += 60
|
||||
default: break
|
||||
}
|
||||
}
|
||||
return frame
|
||||
}, iPad: { self.frame })()
|
||||
}
|
||||
|
||||
private var affectingViews = Set<UIView>()
|
||||
|
||||
override func didMoveToSuperview() {
|
||||
super.didMoveToSuperview()
|
||||
subscribe()
|
||||
update()
|
||||
}
|
||||
|
||||
deinit {
|
||||
unsubscribe()
|
||||
}
|
||||
|
||||
private func subscribe() {
|
||||
guard let ol = superview?.layer else { return }
|
||||
ol.addObserver(self, forKeyPath: Const.observeKeyPath, options: .new, context: nil)
|
||||
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
|
||||
|
||||
let nc = NotificationCenter.default
|
||||
nc.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: .main) { _ in
|
||||
let orientation = UIDevice.current.orientation
|
||||
guard !orientation.isFlat && orientation != .portraitUpsideDown else { return }
|
||||
self.orientation = orientation
|
||||
}
|
||||
}
|
||||
|
||||
private func unsubscribe() {
|
||||
guard let ol = superview?.layer else { return }
|
||||
ol.removeObserver(self, forKeyPath: Const.observeKeyPath)
|
||||
UIDevice.current.endGeneratingDeviceOrientationNotifications()
|
||||
}
|
||||
|
||||
override func observeValue(forKeyPath keyPath: String?, of _: Any?, change _: [NSKeyValueChangeKey: Any]?, context _: UnsafeMutableRawPointer?) {
|
||||
if keyPath == Const.observeKeyPath {
|
||||
DispatchQueue.main.async {
|
||||
self.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
scheduleNotification()
|
||||
}
|
||||
|
||||
private func newAffectingViews(view: UIView) -> Set<UIView> {
|
||||
var views = Set<UIView>()
|
||||
if isAreaAffectingView(view) {
|
||||
views.insert(view)
|
||||
}
|
||||
view.subviews.forEach {
|
||||
views.formUnion(newAffectingViews(view: $0))
|
||||
}
|
||||
return views
|
||||
}
|
||||
|
||||
private func update() {
|
||||
guard let sv = superview else { return }
|
||||
let newAVs = newAffectingViews(view: sv)
|
||||
newAVs.subtracting(affectingViews).forEach(addAffectingView)
|
||||
affectingViews = newAVs
|
||||
scheduleNotification()
|
||||
}
|
||||
|
||||
func addConstraints(otherView: UIView, directions: MWMAvailableAreaAffectDirections) {
|
||||
guard !directions.isEmpty else {
|
||||
LOG(.warning, "Attempt to add empty affecting directions from \(otherView) to \(self)")
|
||||
return
|
||||
}
|
||||
let add = { (sa: NSLayoutConstraint.Attribute, oa: NSLayoutConstraint.Attribute, rel: NSLayoutConstraint.Relation) in
|
||||
let c = NSLayoutConstraint(item: self, attribute: sa, relatedBy: rel, toItem: otherView, attribute: oa, multiplier: 1, constant: 0)
|
||||
c.priority = UILayoutPriority.defaultHigh
|
||||
c.isActive = true
|
||||
}
|
||||
[
|
||||
.top: (.top, .bottom, .greaterThanOrEqual),
|
||||
.bottom: (.bottom, .top, .lessThanOrEqual),
|
||||
.left: (.left, .right, .greaterThanOrEqual),
|
||||
.right: (.right, .left, .lessThanOrEqual),
|
||||
]
|
||||
.filter { directions.contains($0.key) }
|
||||
.map { $0.value }
|
||||
.forEach(add)
|
||||
}
|
||||
|
||||
@objc
|
||||
private func scheduleNotification() {
|
||||
if deferNotification {
|
||||
let selector = #selector(notifyObserver)
|
||||
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: selector, object: nil)
|
||||
perform(selector, with: nil, afterDelay: 0)
|
||||
} else {
|
||||
notifyObserver()
|
||||
}
|
||||
}
|
||||
|
||||
func isAreaAffectingView(_: UIView) -> Bool { return false }
|
||||
func addAffectingView(_: UIView) {}
|
||||
@objc func notifyObserver() {}
|
||||
}
|
||||
|
||||
extension MWMAvailableAreaAffectDirections: Hashable {
|
||||
public var hashValue: Int {
|
||||
return rawValue
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
typedef NS_OPTIONS(NSInteger, MWMAvailableAreaAffectDirections) {
|
||||
MWMAvailableAreaAffectDirectionsNone = 0,
|
||||
MWMAvailableAreaAffectDirectionsTop = 1 << 0,
|
||||
MWMAvailableAreaAffectDirectionsBottom = 1 << 1,
|
||||
MWMAvailableAreaAffectDirectionsLeft = 1 << 2,
|
||||
MWMAvailableAreaAffectDirectionsRight = 1 << 3
|
||||
};
|
||||
21
iphone/Maps/UI/AvailableArea/NavigationInfoArea.swift
Normal file
21
iphone/Maps/UI/AvailableArea/NavigationInfoArea.swift
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
final class NavigationInfoArea: AvailableArea {
|
||||
override func isAreaAffectingView(_ other: UIView) -> Bool {
|
||||
return !other.navigationInfoAreaAffectDirections.isEmpty
|
||||
}
|
||||
|
||||
override func addAffectingView(_ other: UIView) {
|
||||
let ov = other.navigationInfoAreaAffectView
|
||||
let directions = ov.navigationInfoAreaAffectDirections
|
||||
addConstraints(otherView: ov, directions: directions)
|
||||
}
|
||||
|
||||
override func notifyObserver() {
|
||||
MWMNavigationDashboardManager.updateNavigationInfoAvailableArea(areaFrame)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
var navigationInfoAreaAffectDirections: MWMAvailableAreaAffectDirections { return [] }
|
||||
|
||||
var navigationInfoAreaAffectView: UIView { return self }
|
||||
}
|
||||
25
iphone/Maps/UI/AvailableArea/PlacePageArea.swift
Normal file
25
iphone/Maps/UI/AvailableArea/PlacePageArea.swift
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
final class PlacePageArea: AvailableArea {
|
||||
override var areaFrame: CGRect {
|
||||
return frame
|
||||
}
|
||||
|
||||
override func isAreaAffectingView(_ other: UIView) -> Bool {
|
||||
return !other.placePageAreaAffectDirections.isEmpty
|
||||
}
|
||||
|
||||
override func addAffectingView(_ other: UIView) {
|
||||
let ov = other.placePageAreaAffectView
|
||||
let directions = ov.placePageAreaAffectDirections
|
||||
addConstraints(otherView: ov, directions: directions)
|
||||
}
|
||||
|
||||
override func notifyObserver() {
|
||||
MWMPlacePageManagerHelper.updateAvailableArea(areaFrame)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
@objc var placePageAreaAffectDirections: MWMAvailableAreaAffectDirections { return [] }
|
||||
|
||||
var placePageAreaAffectView: UIView { return self }
|
||||
}
|
||||
23
iphone/Maps/UI/AvailableArea/SideButtonsArea.swift
Normal file
23
iphone/Maps/UI/AvailableArea/SideButtonsArea.swift
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
final class SideButtonsArea: AvailableArea {
|
||||
override var deferNotification: Bool { return false }
|
||||
|
||||
override func isAreaAffectingView(_ other: UIView) -> Bool {
|
||||
return !other.sideButtonsAreaAffectDirections.isEmpty
|
||||
}
|
||||
|
||||
override func addAffectingView(_ other: UIView) {
|
||||
let ov = other.sideButtonsAreaAffectView
|
||||
let directions = ov.sideButtonsAreaAffectDirections
|
||||
addConstraints(otherView: ov, directions: directions)
|
||||
}
|
||||
|
||||
override func notifyObserver() {
|
||||
MWMSideButtons.updateAvailableArea(areaFrame)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
@objc var sideButtonsAreaAffectDirections: MWMAvailableAreaAffectDirections { return [] }
|
||||
|
||||
var sideButtonsAreaAffectView: UIView { return self }
|
||||
}
|
||||
29
iphone/Maps/UI/AvailableArea/TabBarArea.swift
Normal file
29
iphone/Maps/UI/AvailableArea/TabBarArea.swift
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
final class TabBarArea: AvailableArea {
|
||||
override var areaFrame: CGRect {
|
||||
var areaFrame = frame
|
||||
// Spacing is used only for devices with zero bottom safe area (such as SE).
|
||||
let additionalBottomSpacing: CGFloat = MapsAppDelegate.theApp().window.safeAreaInsets.bottom.isZero ? -10 : .zero
|
||||
areaFrame.origin.y += additionalBottomSpacing
|
||||
return areaFrame
|
||||
}
|
||||
|
||||
override func isAreaAffectingView(_ other: UIView) -> Bool {
|
||||
return !other.tabBarAreaAffectDirections.isEmpty
|
||||
}
|
||||
|
||||
override func addAffectingView(_ other: UIView) {
|
||||
let ov = other.tabBarAreaAffectView
|
||||
let directions = ov.tabBarAreaAffectDirections
|
||||
addConstraints(otherView: ov, directions: directions)
|
||||
}
|
||||
|
||||
override func notifyObserver() {
|
||||
BottomTabBarViewController.updateAvailableArea(areaFrame)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
@objc var tabBarAreaAffectDirections: MWMAvailableAreaAffectDirections { return [] }
|
||||
|
||||
var tabBarAreaAffectView: UIView { return self }
|
||||
}
|
||||
21
iphone/Maps/UI/AvailableArea/TrackRecordingButtonArea.swift
Normal file
21
iphone/Maps/UI/AvailableArea/TrackRecordingButtonArea.swift
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
final class TrackRecordingButtonArea: AvailableArea {
|
||||
override func isAreaAffectingView(_ other: UIView) -> Bool {
|
||||
return !other.trackRecordingButtonAreaAffectDirections.isEmpty
|
||||
}
|
||||
|
||||
override func addAffectingView(_ other: UIView) {
|
||||
let ov = other.trackRecordingButtonAreaAffectView
|
||||
let directions = ov.trackRecordingButtonAreaAffectDirections
|
||||
addConstraints(otherView: ov, directions: directions)
|
||||
}
|
||||
|
||||
override func notifyObserver() {
|
||||
TrackRecordingButtonViewController.updateAvailableArea(areaFrame)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
@objc var trackRecordingButtonAreaAffectDirections: MWMAvailableAreaAffectDirections { return [] }
|
||||
|
||||
var trackRecordingButtonAreaAffectView: UIView { return self }
|
||||
}
|
||||
21
iphone/Maps/UI/AvailableArea/TrafficButtonArea.swift
Normal file
21
iphone/Maps/UI/AvailableArea/TrafficButtonArea.swift
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
final class TrafficButtonArea: AvailableArea {
|
||||
override func isAreaAffectingView(_ other: UIView) -> Bool {
|
||||
return !other.trafficButtonAreaAffectDirections.isEmpty
|
||||
}
|
||||
|
||||
override func addAffectingView(_ other: UIView) {
|
||||
let ov = other.trafficButtonAreaAffectView
|
||||
let directions = ov.trafficButtonAreaAffectDirections
|
||||
addConstraints(otherView: ov, directions: directions)
|
||||
}
|
||||
|
||||
override func notifyObserver() {
|
||||
MWMTrafficButtonViewController.updateAvailableArea(areaFrame)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
@objc var trafficButtonAreaAffectDirections: MWMAvailableAreaAffectDirections { return [] }
|
||||
|
||||
var trafficButtonAreaAffectView: UIView { return self }
|
||||
}
|
||||
24
iphone/Maps/UI/AvailableArea/VisibleArea.swift
Normal file
24
iphone/Maps/UI/AvailableArea/VisibleArea.swift
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
final class VisibleArea: AvailableArea {
|
||||
override func isAreaAffectingView(_ other: UIView) -> Bool {
|
||||
return !other.visibleAreaAffectDirections.isEmpty
|
||||
}
|
||||
|
||||
override func addAffectingView(_ other: UIView) {
|
||||
let ov = other.visibleAreaAffectView
|
||||
let directions = ov.visibleAreaAffectDirections
|
||||
addConstraints(otherView: ov, directions: directions)
|
||||
}
|
||||
|
||||
override func notifyObserver() {
|
||||
if CarPlayService.shared.isCarplayActivated {
|
||||
return
|
||||
}
|
||||
FrameworkHelper.setVisibleViewport(areaFrame, scaleFactor: MapViewController.shared()?.mapView.contentScaleFactor ?? 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
@objc var visibleAreaAffectDirections: MWMAvailableAreaAffectDirections { return [] }
|
||||
|
||||
var visibleAreaAffectView: UIView { return self }
|
||||
}
|
||||
30
iphone/Maps/UI/AvailableArea/WidgetsArea.swift
Normal file
30
iphone/Maps/UI/AvailableArea/WidgetsArea.swift
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
final class WidgetsArea: AvailableArea {
|
||||
override var areaFrame: CGRect {
|
||||
return alternative(iPhone: {
|
||||
var frame = super.areaFrame
|
||||
frame.origin.y -= 16
|
||||
frame.size.height += 16
|
||||
return frame
|
||||
}, iPad: { super.areaFrame })()
|
||||
}
|
||||
|
||||
override func isAreaAffectingView(_ other: UIView) -> Bool {
|
||||
return !other.widgetsAreaAffectDirections.isEmpty
|
||||
}
|
||||
|
||||
override func addAffectingView(_ other: UIView) {
|
||||
let ov = other.widgetsAreaAffectView
|
||||
let directions = ov.widgetsAreaAffectDirections
|
||||
addConstraints(otherView: ov, directions: directions)
|
||||
}
|
||||
|
||||
override func notifyObserver() {
|
||||
MWMMapWidgetsHelper.updateAvailableArea(areaFrame)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
@objc var widgetsAreaAffectDirections: MWMAvailableAreaAffectDirections { return [] }
|
||||
|
||||
var widgetsAreaAffectView: UIView { return self }
|
||||
}
|
||||
11
iphone/Maps/UI/BottomMenu/MWMBottomMenuState.h
Normal file
11
iphone/Maps/UI/BottomMenu/MWMBottomMenuState.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef MWMBottomMenuState_h
|
||||
#define MWMBottomMenuState_h
|
||||
|
||||
typedef NS_ENUM(NSUInteger, MWMBottomMenuState) {
|
||||
MWMBottomMenuStateHidden,
|
||||
MWMBottomMenuStateInactive,
|
||||
MWMBottomMenuStateActive,
|
||||
MWMBottomMenuStateLayers
|
||||
};
|
||||
|
||||
#endif /* MWMBottomMenuState_h */
|
||||
36
iphone/Maps/UI/BottomMenu/Menu/BottomMenuBuilder.swift
Normal file
36
iphone/Maps/UI/BottomMenu/Menu/BottomMenuBuilder.swift
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
@objc class BottomMenuBuilder: NSObject {
|
||||
@objc static func buildMenu(mapViewController: MapViewController,
|
||||
controlsManager: MWMMapViewControlsManager,
|
||||
delegate: BottomMenuDelegate) -> UIViewController {
|
||||
return BottomMenuBuilder.build(mapViewController: mapViewController,
|
||||
controlsManager: controlsManager,
|
||||
delegate: delegate,
|
||||
sections: [.layers, .items])
|
||||
}
|
||||
|
||||
@objc static func buildLayers(mapViewController: MapViewController,
|
||||
controlsManager: MWMMapViewControlsManager,
|
||||
delegate: BottomMenuDelegate) -> UIViewController {
|
||||
return BottomMenuBuilder.build(mapViewController: mapViewController,
|
||||
controlsManager: controlsManager,
|
||||
delegate: delegate,
|
||||
sections: [.layers])
|
||||
}
|
||||
|
||||
private static func build(mapViewController: MapViewController,
|
||||
controlsManager: MWMMapViewControlsManager,
|
||||
delegate: BottomMenuDelegate,
|
||||
sections: [BottomMenuPresenter.Sections]) -> UIViewController {
|
||||
let viewController = BottomMenuViewController(nibName: nil, bundle: nil)
|
||||
let interactor = BottomMenuInteractor(viewController: viewController,
|
||||
mapViewController: mapViewController,
|
||||
controlsManager: controlsManager,
|
||||
delegate: delegate)
|
||||
let presenter = BottomMenuPresenter(view: viewController, interactor: interactor, sections: sections)
|
||||
|
||||
interactor.presenter = presenter
|
||||
viewController.presenter = presenter
|
||||
|
||||
return viewController
|
||||
}
|
||||
}
|
||||
101
iphone/Maps/UI/BottomMenu/Menu/BottomMenuInteractor.swift
Normal file
101
iphone/Maps/UI/BottomMenu/Menu/BottomMenuInteractor.swift
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
protocol BottomMenuInteractorProtocol: AnyObject {
|
||||
func close()
|
||||
func addPlace()
|
||||
func downloadMaps()
|
||||
func donate()
|
||||
func openHelp()
|
||||
func openSettings()
|
||||
func shareLocation(cell: BottomMenuItemCell)
|
||||
func toggleTrackRecording()
|
||||
}
|
||||
|
||||
@objc protocol BottomMenuDelegate {
|
||||
func actionDownloadMaps(_ mode: MWMMapDownloaderMode)
|
||||
func addPlace()
|
||||
func didFinishAddingPlace()
|
||||
}
|
||||
|
||||
class BottomMenuInteractor {
|
||||
weak var presenter: BottomMenuPresenterProtocol?
|
||||
private weak var viewController: UIViewController?
|
||||
private weak var mapViewController: MapViewController?
|
||||
private weak var delegate: BottomMenuDelegate?
|
||||
private weak var controlsManager: MWMMapViewControlsManager?
|
||||
|
||||
private let trackRecorder: TrackRecordingManager = .shared
|
||||
|
||||
init(viewController: UIViewController,
|
||||
mapViewController: MapViewController,
|
||||
controlsManager: MWMMapViewControlsManager,
|
||||
delegate: BottomMenuDelegate) {
|
||||
self.viewController = viewController
|
||||
self.mapViewController = mapViewController
|
||||
self.delegate = delegate
|
||||
self.controlsManager = controlsManager
|
||||
}
|
||||
}
|
||||
|
||||
extension BottomMenuInteractor: BottomMenuInteractorProtocol {
|
||||
func close() {
|
||||
guard let controlsManager = controlsManager else {
|
||||
fatalError()
|
||||
}
|
||||
controlsManager.menuState = controlsManager.menuRestoreState
|
||||
}
|
||||
|
||||
func addPlace() {
|
||||
delegate?.addPlace()
|
||||
}
|
||||
|
||||
func donate() {
|
||||
close()
|
||||
guard var url = SettingsBridge.donateUrl() else { return }
|
||||
if url == "https://www.comaps.app/donate/" {
|
||||
url = L("translated_om_site_url") + "donate/"
|
||||
}
|
||||
viewController?.openUrl(url, externally: true)
|
||||
}
|
||||
|
||||
func downloadMaps() {
|
||||
close()
|
||||
delegate?.actionDownloadMaps(.downloaded)
|
||||
}
|
||||
|
||||
func openHelp() {
|
||||
close()
|
||||
mapViewController?.openAbout()
|
||||
}
|
||||
|
||||
func openSettings() {
|
||||
close()
|
||||
mapViewController?.openSettings()
|
||||
}
|
||||
|
||||
func shareLocation(cell: BottomMenuItemCell) {
|
||||
guard let coordinates = LocationManager.lastLocation()?.coordinate else {
|
||||
viewController?.present(UIAlertController.unknownCurrentPosition(), animated: true, completion: nil)
|
||||
return
|
||||
}
|
||||
guard let viewController = viewController else { return }
|
||||
let vc = ActivityViewController.share(forMyPosition: coordinates)
|
||||
vc.present(inParentViewController: viewController, anchorView: cell.anchorView)
|
||||
}
|
||||
|
||||
func toggleTrackRecording() {
|
||||
close()
|
||||
let mapViewController = MapViewController.shared()!
|
||||
switch trackRecorder.recordingState {
|
||||
case .active:
|
||||
mapViewController.showTrackRecordingPlacePage()
|
||||
case .inactive:
|
||||
trackRecorder.start { result in
|
||||
switch result {
|
||||
case .success:
|
||||
mapViewController.showTrackRecordingPlacePage()
|
||||
case .failure:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
178
iphone/Maps/UI/BottomMenu/Menu/BottomMenuPresenter.swift
Normal file
178
iphone/Maps/UI/BottomMenu/Menu/BottomMenuPresenter.swift
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
protocol BottomMenuPresenterProtocol: UITableViewDelegate, UITableViewDataSource {
|
||||
func onClosePressed()
|
||||
func cellToHighlightIndexPath() -> IndexPath?
|
||||
func setCellHighlighted(_ highlighted: Bool)
|
||||
}
|
||||
|
||||
class BottomMenuPresenter: NSObject {
|
||||
enum CellType: Int, CaseIterable {
|
||||
case addPlace
|
||||
case recordTrack
|
||||
case share
|
||||
case donate
|
||||
case downloadMaps
|
||||
case settings
|
||||
case help
|
||||
}
|
||||
|
||||
enum Sections: Int {
|
||||
case layers
|
||||
case items
|
||||
}
|
||||
|
||||
private weak var view: BottomMenuViewProtocol?
|
||||
private let interactor: BottomMenuInteractorProtocol
|
||||
private let sections: [Sections]
|
||||
private var menuCells: [CellType]
|
||||
private let trackRecorder = TrackRecordingManager.shared
|
||||
private var cellToHighlight: CellType?
|
||||
|
||||
init(view: BottomMenuViewProtocol,
|
||||
interactor: BottomMenuInteractorProtocol,
|
||||
sections: [Sections]) {
|
||||
self.view = view
|
||||
self.interactor = interactor
|
||||
self.sections = sections
|
||||
self.menuCells = []
|
||||
self.cellToHighlight = Self.getCellToHighlight()
|
||||
super.init()
|
||||
}
|
||||
|
||||
private static func getCellToHighlight() -> CellType? {
|
||||
let featureToHighlightData = DeepLinkHandler.shared.getInAppFeatureHighlightData()
|
||||
guard let featureToHighlightData, featureToHighlightData.urlType == .menu else { return nil }
|
||||
switch featureToHighlightData.feature {
|
||||
case .trackRecorder: return .recordTrack
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension BottomMenuPresenter: BottomMenuPresenterProtocol {
|
||||
func onClosePressed() {
|
||||
interactor.close()
|
||||
}
|
||||
|
||||
func cellToHighlightIndexPath() -> IndexPath? {
|
||||
// Highlighting is enabled only for the .items section.
|
||||
guard let cellToHighlight,
|
||||
let sectionIndex = sections.firstIndex(of: .items),
|
||||
let cellIndex = menuCells.firstIndex(of: cellToHighlight) else { return nil }
|
||||
return IndexPath(row: cellIndex, section: sectionIndex)
|
||||
}
|
||||
|
||||
func setCellHighlighted(_ highlighted: Bool) {
|
||||
cellToHighlight = nil
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: -- UITableViewDataSource
|
||||
|
||||
extension BottomMenuPresenter {
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
sections.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
switch sections[section] {
|
||||
case .layers:
|
||||
return 1
|
||||
case .items:
|
||||
let leftButtonType = Settings.leftButtonType
|
||||
menuCells = CellType.allCases.filter { cell in
|
||||
if cell == .donate {
|
||||
return false
|
||||
} else if leftButtonType == .addPlace, cell == .addPlace {
|
||||
return false
|
||||
} else if leftButtonType == .recordTrack, cell == .recordTrack {
|
||||
return false
|
||||
} else if leftButtonType == .help, cell == .help {
|
||||
return false
|
||||
} else if leftButtonType == .settings, cell == .settings {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
return menuCells.count
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
switch sections[indexPath.section] {
|
||||
case .layers:
|
||||
let cell = tableView.dequeueReusableCell(cell: BottomMenuLayersCell.self)!
|
||||
cell.onClose = { [weak self] in self?.onClosePressed() }
|
||||
if sections.count > 1 {
|
||||
cell.addSeparator(.bottom)
|
||||
}
|
||||
return cell
|
||||
case .items:
|
||||
let cell = tableView.dequeueReusableCell(cell: BottomMenuItemCell.self)!
|
||||
switch menuCells[indexPath.row] {
|
||||
case .addPlace:
|
||||
let enabled = MWMNavigationDashboardManager.shared().state == .hidden && FrameworkHelper.canEditMapAtViewportCenter()
|
||||
cell.configure(imageName: "plus",
|
||||
title: L("placepage_add_place_button"),
|
||||
enabled: enabled)
|
||||
case .recordTrack:
|
||||
cell.configure(imageName: "track", title: L("start_track_recording"))
|
||||
case .downloadMaps:
|
||||
cell.configure(imageName: "ic_menu_download",
|
||||
title: L("download_maps"),
|
||||
badgeCount: MapsAppDelegate.theApp().badgeNumber())
|
||||
case .donate:
|
||||
cell.configure(imageName: "ic_menu_donate",
|
||||
title: L("donate"))
|
||||
case .help:
|
||||
cell.configure(imageName: "help",
|
||||
title: L("help"))
|
||||
case .settings:
|
||||
cell.configure(imageName: "gearshape.fill",
|
||||
title: L("settings"))
|
||||
case .share:
|
||||
cell.configure(imageName: "square.and.arrow.up",
|
||||
title: L("share_my_location"))
|
||||
}
|
||||
return cell
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: -- UITableViewDelegate
|
||||
|
||||
extension BottomMenuPresenter {
|
||||
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
|
||||
if let cell = tableView.cellForRow(at: indexPath) as? BottomMenuItemCell {
|
||||
return cell.isEnabled ? indexPath : nil
|
||||
}
|
||||
return indexPath
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
switch sections[indexPath.section] {
|
||||
case .layers:
|
||||
return;
|
||||
case .items:
|
||||
switch menuCells[indexPath.row] {
|
||||
case .addPlace:
|
||||
interactor.addPlace()
|
||||
case .recordTrack:
|
||||
interactor.toggleTrackRecording()
|
||||
case .downloadMaps:
|
||||
interactor.downloadMaps()
|
||||
case .donate:
|
||||
interactor.donate()
|
||||
case .help:
|
||||
interactor.openHelp()
|
||||
case .settings:
|
||||
interactor.openSettings()
|
||||
case .share:
|
||||
if let cell = tableView.cellForRow(at: indexPath) as? BottomMenuItemCell {
|
||||
interactor.shareLocation(cell: cell)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
100
iphone/Maps/UI/BottomMenu/Menu/BottomMenuViewController.swift
Normal file
100
iphone/Maps/UI/BottomMenu/Menu/BottomMenuViewController.swift
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
protocol BottomMenuViewProtocol: AnyObject {
|
||||
var presenter: BottomMenuPresenterProtocol? { get set }
|
||||
}
|
||||
|
||||
class BottomMenuViewController: MWMViewController {
|
||||
var presenter: BottomMenuPresenterProtocol?
|
||||
private let transitioningManager = BottomMenuTransitioningManager()
|
||||
|
||||
@IBOutlet var tableView: UITableView!
|
||||
@IBOutlet var heightConstraint: NSLayoutConstraint!
|
||||
@IBOutlet var bottomConstraint: NSLayoutConstraint!
|
||||
|
||||
lazy var chromeView: UIView = {
|
||||
let view = UIView()
|
||||
view.setStyle(.presentationBackground)
|
||||
return view
|
||||
}()
|
||||
|
||||
weak var containerView: UIView! {
|
||||
didSet {
|
||||
containerView.insertSubview(chromeView, at: 0)
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
tableView.layer.setCornerRadius(.buttonDefault, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner])
|
||||
tableView.sectionFooterHeight = 0
|
||||
|
||||
tableView.dataSource = presenter
|
||||
tableView.delegate = presenter
|
||||
tableView.registerNib(cell: BottomMenuItemCell.self)
|
||||
tableView.registerNib(cell: BottomMenuLayersCell.self)
|
||||
|
||||
NotificationCenter.default.addObserver(forName: UserDefaults.didChangeNotification, object: nil, queue: nil) { _ in
|
||||
DispatchQueue.main.async {
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
if let cellToHighlight = presenter?.cellToHighlightIndexPath() {
|
||||
tableView.cellForRow(at: cellToHighlight)?.highlight()
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
tableView.setNeedsLayout()
|
||||
tableView.layoutIfNeeded()
|
||||
heightConstraint.constant = min(tableView.contentSize.height, view.height)
|
||||
tableView.isScrollEnabled = tableView.contentSize.height > heightConstraint.constant;
|
||||
}
|
||||
|
||||
@IBAction func onClosePressed(_ sender: Any) {
|
||||
presenter?.onClosePressed()
|
||||
}
|
||||
|
||||
@IBAction func onPan(_ sender: UIPanGestureRecognizer) {
|
||||
let yOffset = sender.translation(in: view.superview).y
|
||||
let yVelocity = sender.velocity(in: view.superview).y
|
||||
sender.setTranslation(CGPoint.zero, in: view.superview)
|
||||
bottomConstraint.constant = min(bottomConstraint.constant - yOffset, 0);
|
||||
|
||||
let alpha = 1.0 - abs(bottomConstraint.constant / tableView.height)
|
||||
self.chromeView.alpha = alpha
|
||||
|
||||
let state = sender.state
|
||||
if state == .ended || state == .cancelled {
|
||||
if yVelocity > 0 || (yVelocity == 0 && alpha < 0.8) {
|
||||
presenter?.onClosePressed()
|
||||
} else {
|
||||
let duration = min(kDefaultAnimationDuration, TimeInterval(self.bottomConstraint.constant / yVelocity))
|
||||
self.view.layoutIfNeeded()
|
||||
UIView.animate(withDuration: duration) {
|
||||
self.chromeView.alpha = 1
|
||||
self.bottomConstraint.constant = 0
|
||||
self.view.layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override var transitioningDelegate: UIViewControllerTransitioningDelegate? {
|
||||
get { return transitioningManager }
|
||||
set { }
|
||||
}
|
||||
|
||||
override var modalPresentationStyle: UIModalPresentationStyle {
|
||||
get { return .custom }
|
||||
set { }
|
||||
}
|
||||
}
|
||||
|
||||
extension BottomMenuViewController: BottomMenuViewProtocol {
|
||||
|
||||
}
|
||||
95
iphone/Maps/UI/BottomMenu/Menu/BottomMenuViewController.xib
Normal file
95
iphone/Maps/UI/BottomMenu/Menu/BottomMenuViewController.xib
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="BottomMenuViewController" customModule="CoMaps" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="bottomConstraint" destination="Crm-Ym-Ikk" id="K0d-Ad-Q13"/>
|
||||
<outlet property="heightConstraint" destination="dYV-fi-iGj" id="chn-o3-rhF"/>
|
||||
<outlet property="tableView" destination="L4F-0e-1B7" id="dHQ-DU-QPO"/>
|
||||
<outlet property="view" destination="iN0-l3-epB" id="nOL-DH-swt"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="DD7-rW-ckP">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="862"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<connections>
|
||||
<outletCollection property="gestureRecognizers" destination="sXH-Kv-ZnQ" appends="YES" id="u82-AO-mZ1"/>
|
||||
</connections>
|
||||
</view>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="plain" separatorStyle="none" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="L4F-0e-1B7">
|
||||
<rect key="frame" x="0.0" y="562" width="414" height="300"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="350" id="Jyo-UJ-ltJ"/>
|
||||
<constraint firstAttribute="height" priority="750" constant="300" id="dYV-fi-iGj"/>
|
||||
</constraints>
|
||||
<variation key="default">
|
||||
<mask key="constraints">
|
||||
<exclude reference="Jyo-UJ-ltJ"/>
|
||||
</mask>
|
||||
</variation>
|
||||
<variation key="heightClass=compact">
|
||||
<mask key="constraints">
|
||||
<include reference="Jyo-UJ-ltJ"/>
|
||||
</mask>
|
||||
</variation>
|
||||
</tableView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="M4g-vy-YtE" userLabel="Bottom View">
|
||||
<rect key="frame" x="0.0" y="862" width="414" height="34"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<gestureRecognizers/>
|
||||
<constraints>
|
||||
<constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="L4F-0e-1B7" secondAttribute="bottom" id="Crm-Ym-Ikk"/>
|
||||
<constraint firstItem="M4g-vy-YtE" firstAttribute="top" secondItem="L4F-0e-1B7" secondAttribute="bottom" id="E7M-j3-lrN"/>
|
||||
<constraint firstItem="DD7-rW-ckP" firstAttribute="bottom" secondItem="vUN-kp-3ea" secondAttribute="bottom" id="LOj-yu-5nE"/>
|
||||
<constraint firstItem="M4g-vy-YtE" firstAttribute="trailing" secondItem="L4F-0e-1B7" secondAttribute="trailing" id="PdB-CC-VOI"/>
|
||||
<constraint firstItem="DD7-rW-ckP" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="Vxc-mf-jVJ"/>
|
||||
<constraint firstAttribute="trailing" secondItem="DD7-rW-ckP" secondAttribute="trailing" id="WAo-c4-geW"/>
|
||||
<constraint firstItem="M4g-vy-YtE" firstAttribute="leading" secondItem="L4F-0e-1B7" secondAttribute="leading" id="cjE-84-gfc"/>
|
||||
<constraint firstItem="DD7-rW-ckP" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="deF-nX-Ae5"/>
|
||||
<constraint firstItem="L4F-0e-1B7" firstAttribute="top" relation="greaterThanOrEqual" secondItem="iN0-l3-epB" secondAttribute="top" id="fDu-HA-dhq"/>
|
||||
<constraint firstItem="L4F-0e-1B7" firstAttribute="trailing" secondItem="vUN-kp-3ea" secondAttribute="trailing" id="g7h-Yh-azG"/>
|
||||
<constraint firstAttribute="bottom" secondItem="M4g-vy-YtE" secondAttribute="bottom" priority="750" id="hFA-7p-XKe"/>
|
||||
<constraint firstItem="L4F-0e-1B7" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="t8e-ZM-EdJ"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
|
||||
<variation key="heightClass=compact">
|
||||
<mask key="constraints">
|
||||
<exclude reference="g7h-Yh-azG"/>
|
||||
</mask>
|
||||
</variation>
|
||||
<connections>
|
||||
<outletCollection property="gestureRecognizers" destination="Rdk-jI-mZR" appends="YES" id="bbV-GO-4iF"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="137.68115942028987" y="153.34821428571428"/>
|
||||
</view>
|
||||
<tapGestureRecognizer cancelsTouchesInView="NO" id="sXH-Kv-ZnQ">
|
||||
<connections>
|
||||
<action selector="onClosePressed:" destination="-1" id="nMr-L4-IGY"/>
|
||||
</connections>
|
||||
</tapGestureRecognizer>
|
||||
<panGestureRecognizer delaysTouchesBegan="YES" delaysTouchesEnded="NO" minimumNumberOfTouches="1" id="Rdk-jI-mZR">
|
||||
<connections>
|
||||
<action selector="onPan:" destination="-1" id="yGW-xa-dnH"/>
|
||||
</connections>
|
||||
</panGestureRecognizer>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import UIKit
|
||||
|
||||
class BottomMenuItemCell: UITableViewCell {
|
||||
@IBOutlet private var label: UILabel!
|
||||
@IBOutlet private var badgeBackground: UIView!
|
||||
@IBOutlet private var badgeCountLabel: UILabel!
|
||||
@IBOutlet private var separator: UIView!
|
||||
@IBOutlet private var icon: UIImageView!
|
||||
@IBOutlet private var badgeSpacingConstraint: NSLayoutConstraint!
|
||||
@IBOutlet private var badgeBackgroundWidthConstraint: NSLayoutConstraint!
|
||||
var anchorView: UIView {
|
||||
get {
|
||||
return icon
|
||||
}
|
||||
}
|
||||
|
||||
private(set) var isEnabled: Bool = true
|
||||
|
||||
func configure(imageName: String, title: String, badgeCount: UInt = .zero, enabled: Bool = true) {
|
||||
if imageName == "help" {
|
||||
icon.image = Settings.LeftButtonType.help.image
|
||||
} else if imageName == "plus" {
|
||||
icon.image = Settings.LeftButtonType.addPlace.image
|
||||
} else if imageName == "track" {
|
||||
icon.image = Settings.LeftButtonType.recordTrack.image
|
||||
} else if imageName == "ic_menu_download" || imageName == "ic_menu_donate" {
|
||||
icon.image = UIImage(named: imageName)
|
||||
} else {
|
||||
let configuration = UIImage.SymbolConfiguration(pointSize: 22, weight: .semibold)
|
||||
icon.image = UIImage(systemName: imageName, withConfiguration: configuration)!
|
||||
}
|
||||
|
||||
label.text = title
|
||||
badgeBackground.isHidden = badgeCount == 0
|
||||
badgeCountLabel.text = "\(badgeCount)"
|
||||
if badgeCount == 0 {
|
||||
badgeSpacingConstraint.constant = 0
|
||||
badgeBackgroundWidthConstraint.constant = 0
|
||||
} else {
|
||||
badgeSpacingConstraint.constant = 8
|
||||
badgeBackgroundWidthConstraint.constant = 32
|
||||
}
|
||||
isEnabled = enabled
|
||||
icon.setStyleAndApply(isEnabled ? .black : .gray)
|
||||
label.setFontStyleAndApply(isEnabled ? .blackPrimary : .blackHint)
|
||||
}
|
||||
}
|
||||
107
iphone/Maps/UI/BottomMenu/Menu/Cells/BottomMenuItemCell.xib
Normal file
107
iphone/Maps/UI/BottomMenu/Menu/Cells/BottomMenuItemCell.xib
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="48" id="gZi-cd-LgO" customClass="BottomMenuItemCell" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="48"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="gZi-cd-LgO" id="rsu-1s-Lsp">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="48"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_menu_download" translatesAutoresizingMaskIntoConstraints="NO" id="8oJ-8z-qRL">
|
||||
<rect key="frame" x="16" y="10" width="28" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="28" id="Hth-JK-nv2"/>
|
||||
<constraint firstAttribute="height" constant="28" id="zil-Ys-XYB"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMBlack"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5Uc-o1-PsF">
|
||||
<rect key="frame" x="60" y="14" width="298" height="20"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular16:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="con-tP-3dJ" userLabel="DownloadBadgeBackground">
|
||||
<rect key="frame" x="366" y="14" width="32" height="20"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="3" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FT9-8n-RZm" userLabel="DownloadBadgeCount">
|
||||
<rect key="frame" x="0.0" y="0.0" width="32" height="20"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="FT9-8n-RZm" secondAttribute="trailing" id="Ge3-P2-idS"/>
|
||||
<constraint firstAttribute="bottom" secondItem="FT9-8n-RZm" secondAttribute="bottom" id="Kgn-y3-dBT"/>
|
||||
<constraint firstItem="FT9-8n-RZm" firstAttribute="top" secondItem="con-tP-3dJ" secondAttribute="top" id="LaN-Vb-w2N"/>
|
||||
<constraint firstAttribute="height" constant="20" id="Y5v-7h-SGf"/>
|
||||
<constraint firstItem="FT9-8n-RZm" firstAttribute="leading" secondItem="con-tP-3dJ" secondAttribute="leading" id="pE4-Qs-ctY"/>
|
||||
<constraint firstAttribute="width" constant="32" id="ubK-0L-pDn"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Badge"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4OJ-wN-dY4" userLabel="Separator">
|
||||
<rect key="frame" x="60" y="47" width="354" height="1"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="tDM-AP-ern"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="5Uc-o1-PsF" firstAttribute="leading" secondItem="4OJ-wN-dY4" secondAttribute="leading" id="9g1-Uo-zCa"/>
|
||||
<constraint firstAttribute="bottom" secondItem="8oJ-8z-qRL" secondAttribute="bottom" constant="10" id="KJP-IG-4FK"/>
|
||||
<constraint firstItem="FT9-8n-RZm" firstAttribute="leading" secondItem="5Uc-o1-PsF" secondAttribute="trailing" constant="8" id="RJA-NS-MXP"/>
|
||||
<constraint firstItem="8oJ-8z-qRL" firstAttribute="top" secondItem="rsu-1s-Lsp" secondAttribute="top" constant="10" id="bab-2f-YZY"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="4OJ-wN-dY4" secondAttribute="trailing" id="3Vq-qn-yhm"/>
|
||||
<constraint firstItem="4OJ-wN-dY4" firstAttribute="leading" secondItem="gZi-cd-LgO" secondAttribute="leading" constant="60" id="7wy-Q8-lja"/>
|
||||
<constraint firstAttribute="trailing" secondItem="con-tP-3dJ" secondAttribute="trailing" constant="16" id="Bm7-vh-vYz"/>
|
||||
<constraint firstItem="8oJ-8z-qRL" firstAttribute="leading" secondItem="gZi-cd-LgO" secondAttribute="leading" constant="16" id="I7K-bz-QOC"/>
|
||||
<constraint firstAttribute="bottom" secondItem="4OJ-wN-dY4" secondAttribute="bottom" id="SVQ-VP-J4d"/>
|
||||
<constraint firstItem="con-tP-3dJ" firstAttribute="centerY" secondItem="gZi-cd-LgO" secondAttribute="centerY" id="UY4-uw-Kda"/>
|
||||
<constraint firstItem="5Uc-o1-PsF" firstAttribute="centerY" secondItem="gZi-cd-LgO" secondAttribute="centerY" id="hVt-zE-3iE"/>
|
||||
<constraint firstItem="8oJ-8z-qRL" firstAttribute="centerY" secondItem="gZi-cd-LgO" secondAttribute="centerY" id="u2b-9I-SLh"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="badgeBackground" destination="con-tP-3dJ" id="vH5-Rd-uqS"/>
|
||||
<outlet property="badgeBackgroundWidthConstraint" destination="ubK-0L-pDn" id="D3B-sV-eGO"/>
|
||||
<outlet property="badgeCountLabel" destination="FT9-8n-RZm" id="HLY-2S-do9"/>
|
||||
<outlet property="badgeSpacingConstraint" destination="RJA-NS-MXP" id="Hpf-eq-1C1"/>
|
||||
<outlet property="icon" destination="8oJ-8z-qRL" id="qcD-Kv-c5l"/>
|
||||
<outlet property="label" destination="5Uc-o1-PsF" id="y4l-b6-MCt"/>
|
||||
<outlet property="separator" destination="4OJ-wN-dY4" id="Kkv-HO-0eK"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="337.68115942028987" y="527.00892857142856"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="ic_menu_download" width="28" height="28"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
final class BottomMenuLayerButton: VerticallyAlignedButton {
|
||||
private var badgeView: UIView?
|
||||
private let badgeSize = CGSize(width: 12, height: 12)
|
||||
private let badgeOffset = CGPoint(x: -3, y: 3)
|
||||
|
||||
var isBadgeHidden: Bool = true{
|
||||
didSet {
|
||||
if oldValue != isBadgeHidden {
|
||||
updateBadge()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
imageView.layer.masksToBounds = true
|
||||
updateBadge()
|
||||
}
|
||||
|
||||
private func updateBadge() {
|
||||
if isBadgeHidden {
|
||||
badgeView?.removeFromSuperview()
|
||||
badgeView = nil
|
||||
} else {
|
||||
if badgeView == nil {
|
||||
badgeView = UIView()
|
||||
badgeView?.setStyle(.badge)
|
||||
addSubview(badgeView!)
|
||||
}
|
||||
let imageFrame = imageView.frame
|
||||
badgeView?.frame = CGRect(x:imageFrame.minX + imageFrame.width - badgeSize.width / 2 + badgeOffset.x,
|
||||
y:imageFrame.minY - badgeSize.height/2 + badgeOffset.y,
|
||||
width: badgeSize.width,
|
||||
height: badgeSize.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
107
iphone/Maps/UI/BottomMenu/Menu/Cells/BottomMenuLayersCell.swift
Normal file
107
iphone/Maps/UI/BottomMenu/Menu/Cells/BottomMenuLayersCell.swift
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import UIKit
|
||||
|
||||
class BottomMenuLayersCell: UITableViewCell {
|
||||
@IBOutlet weak var closeButton: CircleImageButton!
|
||||
|
||||
@IBOutlet private var subwayButton: BottomMenuLayerButton! {
|
||||
didSet {
|
||||
updateSubwayButton()
|
||||
}
|
||||
}
|
||||
@IBOutlet private var isoLinesButton: BottomMenuLayerButton! {
|
||||
didSet {
|
||||
updateIsoLinesButton()
|
||||
}
|
||||
}
|
||||
@IBOutlet private var outdoorButton: BottomMenuLayerButton! {
|
||||
didSet {
|
||||
updateOutdoorButton()
|
||||
}
|
||||
}
|
||||
|
||||
var onClose: (()->())?
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
MapOverlayManager.add(self)
|
||||
closeButton.setImage(UIImage(named: "ic_close"))
|
||||
setupButtons()
|
||||
}
|
||||
|
||||
private func setupButtons() {
|
||||
outdoorButton.setupWith(image: UIImage(resource: .btnMenuOutdoors), text: L("button_layer_outdoor"))
|
||||
isoLinesButton.setupWith(image: UIImage(resource: .btnMenuIsomaps), text: L("button_layer_isolines"))
|
||||
subwayButton.setupWith(image: UIImage(resource: .btnMenuSubway), text: L("button_layer_subway"))
|
||||
}
|
||||
|
||||
deinit {
|
||||
MapOverlayManager.remove(self)
|
||||
}
|
||||
|
||||
override func setSelected(_ selected: Bool, animated: Bool) {
|
||||
super.setSelected(selected, animated: animated)
|
||||
}
|
||||
|
||||
private func updateSubwayButton() {
|
||||
let enabled = MapOverlayManager.transitEnabled()
|
||||
subwayButton.setStyleAndApply(styleFor(enabled))
|
||||
}
|
||||
|
||||
private func updateIsoLinesButton() {
|
||||
let enabled = MapOverlayManager.isoLinesEnabled()
|
||||
isoLinesButton.setStyleAndApply(styleFor(enabled))
|
||||
}
|
||||
|
||||
private func updateOutdoorButton() {
|
||||
let enabled = MapOverlayManager.outdoorEnabled()
|
||||
outdoorButton.setStyleAndApply(styleFor(enabled))
|
||||
}
|
||||
|
||||
@IBAction func onCloseButtonPressed(_ sender: Any) {
|
||||
onClose?()
|
||||
}
|
||||
|
||||
@IBAction func onSubwayButton(_ sender: Any) {
|
||||
let enable = !MapOverlayManager.transitEnabled()
|
||||
MapOverlayManager.setTransitEnabled(enable)
|
||||
}
|
||||
|
||||
@IBAction func onIsoLinesButton(_ sender: Any) {
|
||||
let enable = !MapOverlayManager.isoLinesEnabled()
|
||||
MapOverlayManager.setIsoLinesEnabled(enable)
|
||||
}
|
||||
|
||||
@IBAction func onOutdoorButton(_ sender: Any) {
|
||||
let enable = !MapOverlayManager.outdoorEnabled()
|
||||
MapOverlayManager.setOutdoorEnabled(enable)
|
||||
}
|
||||
}
|
||||
|
||||
extension BottomMenuLayersCell: MapOverlayManagerObserver {
|
||||
func onTransitStateUpdated() {
|
||||
updateSubwayButton()
|
||||
}
|
||||
|
||||
func onIsoLinesStateUpdated() {
|
||||
updateIsoLinesButton()
|
||||
}
|
||||
|
||||
func onOutdoorStateUpdated() {
|
||||
updateOutdoorButton()
|
||||
}
|
||||
}
|
||||
|
||||
private extension BottomMenuLayersCell {
|
||||
func styleFor(_ enabled: Bool) -> MapStyleSheet {
|
||||
enabled ? .mapMenuButtonEnabled : .mapMenuButtonDisabled
|
||||
}
|
||||
}
|
||||
|
||||
private extension BottomMenuLayerButton {
|
||||
func setupWith(image: UIImage, text: String) {
|
||||
self.image = image
|
||||
spacing = 10
|
||||
numberOfLines = 2
|
||||
localizedText = text
|
||||
}
|
||||
}
|
||||
151
iphone/Maps/UI/BottomMenu/Menu/Cells/BottomMenuLayersCell.xib
Normal file
151
iphone/Maps/UI/BottomMenu/Menu/Cells/BottomMenuLayersCell.xib
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="ipad9_7" orientation="landscape" layout="fullscreen" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" rowHeight="165" id="KGk-i7-Jjw" customClass="BottomMenuLayersCell" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="340" height="165"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" ambiguous="YES" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="340" height="165"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="W1i-v6-zbz">
|
||||
<rect key="frame" x="0.0" y="0.0" width="340" height="50"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Map Layers" textAlignment="natural" lineBreakMode="clip" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vuk-dn-n2c">
|
||||
<rect key="frame" x="119.5" y="13" width="101.5" height="24"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="20"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="blackPrimaryText:bold22"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="layers_title"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2xW-dK-D9y" customClass="CircleImageButton" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="296" y="11" width="28" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="28" id="BD2-bz-n13"/>
|
||||
<constraint firstAttribute="width" constant="28" id="Thu-MY-dQm"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<action selector="onCloseButtonPressed:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="8vd-Pg-Suh"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="50" id="00h-1i-skR"/>
|
||||
<constraint firstItem="2xW-dK-D9y" firstAttribute="leading" secondItem="Vuk-dn-n2c" secondAttribute="trailing" constant="8" id="4PG-Fm-yqS"/>
|
||||
<constraint firstItem="Vuk-dn-n2c" firstAttribute="centerY" secondItem="W1i-v6-zbz" secondAttribute="centerY" id="4du-pr-7hv"/>
|
||||
<constraint firstAttribute="height" constant="45" id="Ez1-s5-1EO"/>
|
||||
<constraint firstItem="Vuk-dn-n2c" firstAttribute="centerX" secondItem="W1i-v6-zbz" secondAttribute="centerX" id="XEG-CK-41Y"/>
|
||||
<constraint firstItem="Vuk-dn-n2c" firstAttribute="leading" secondItem="W1i-v6-zbz" secondAttribute="leading" constant="16" id="kSJ-Wa-nYA"/>
|
||||
<constraint firstAttribute="trailing" secondItem="2xW-dK-D9y" secondAttribute="trailing" constant="16" id="kae-50-2nG"/>
|
||||
<constraint firstItem="2xW-dK-D9y" firstAttribute="centerY" secondItem="Vuk-dn-n2c" secondAttribute="centerY" id="wCu-O0-cz8"/>
|
||||
</constraints>
|
||||
<variation key="default">
|
||||
<mask key="constraints">
|
||||
<exclude reference="00h-1i-skR"/>
|
||||
<exclude reference="Ez1-s5-1EO"/>
|
||||
<exclude reference="XEG-CK-41Y"/>
|
||||
</mask>
|
||||
</variation>
|
||||
<variation key="heightClass=compact">
|
||||
<mask key="constraints">
|
||||
<include reference="Ez1-s5-1EO"/>
|
||||
</mask>
|
||||
</variation>
|
||||
<variation key="heightClass=regular">
|
||||
<mask key="constraints">
|
||||
<include reference="00h-1i-skR"/>
|
||||
</mask>
|
||||
</variation>
|
||||
<variation key="heightClass=regular-widthClass=regular">
|
||||
<mask key="constraints">
|
||||
<include reference="XEG-CK-41Y"/>
|
||||
<exclude reference="kSJ-Wa-nYA"/>
|
||||
<exclude reference="4PG-Fm-yqS"/>
|
||||
</mask>
|
||||
</variation>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="sRd-zd-xSl">
|
||||
<rect key="frame" x="16" y="58" width="308" height="64"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="g13-pK-Eig" userLabel="Outdoor Button" customClass="BottomMenuLayerButton" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="102.5" height="64"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<connections>
|
||||
<action selector="onOutdoorButton:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="UQ2-jj-fPc"/>
|
||||
</connections>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="edA-Mo-3Vx" customClass="BottomMenuLayerButton" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="102.5" y="0.0" width="103" height="64"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<connections>
|
||||
<action selector="onIsoLinesButton:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="3LS-C2-2Mc"/>
|
||||
</connections>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4US-fZ-cyg" customClass="BottomMenuLayerButton" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="205.5" y="0.0" width="102.5" height="64"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<connections>
|
||||
<action selector="onSubwayButton:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="xxM-kP-gT1"/>
|
||||
</connections>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="70" id="d0H-kE-IWx"/>
|
||||
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="500" id="nea-IB-ZkL"/>
|
||||
<constraint firstAttribute="height" constant="64" id="t9j-kf-yze"/>
|
||||
</constraints>
|
||||
<variation key="default">
|
||||
<mask key="constraints">
|
||||
<exclude reference="d0H-kE-IWx"/>
|
||||
<exclude reference="t9j-kf-yze"/>
|
||||
</mask>
|
||||
</variation>
|
||||
<variation key="heightClass=compact">
|
||||
<mask key="constraints">
|
||||
<include reference="d0H-kE-IWx"/>
|
||||
</mask>
|
||||
</variation>
|
||||
<variation key="heightClass=regular">
|
||||
<mask key="constraints">
|
||||
<include reference="t9j-kf-yze"/>
|
||||
</mask>
|
||||
</variation>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="W1i-v6-zbz" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="6Tm-PS-VWZ"/>
|
||||
<constraint firstItem="sRd-zd-xSl" firstAttribute="centerX" secondItem="W1i-v6-zbz" secondAttribute="centerX" id="7AE-Qf-1L3"/>
|
||||
<constraint firstAttribute="trailing" secondItem="W1i-v6-zbz" secondAttribute="trailing" id="7ka-f7-sqc"/>
|
||||
<constraint firstItem="sRd-zd-xSl" firstAttribute="top" secondItem="W1i-v6-zbz" secondAttribute="bottom" constant="8" id="PTe-W7-QnR"/>
|
||||
<constraint firstItem="W1i-v6-zbz" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" id="RLh-Jr-7dm"/>
|
||||
<constraint firstAttribute="trailing" secondItem="sRd-zd-xSl" secondAttribute="trailing" priority="750" constant="16" id="Z8f-X6-r4N"/>
|
||||
<constraint firstItem="sRd-zd-xSl" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" priority="750" constant="16" id="hJE-fg-IEX"/>
|
||||
<constraint firstAttribute="bottom" secondItem="sRd-zd-xSl" secondAttribute="bottom" constant="26" id="iwo-vC-EI9"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<connections>
|
||||
<outlet property="closeButton" destination="2xW-dK-D9y" id="RQI-hb-JpS"/>
|
||||
<outlet property="isoLinesButton" destination="edA-Mo-3Vx" id="qoC-8w-EqY"/>
|
||||
<outlet property="outdoorButton" destination="g13-pK-Eig" id="ib1-aw-Qv9"/>
|
||||
<outlet property="subwayButton" destination="4US-fZ-cyg" id="eQB-HR-Wgl"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="137.6953125" y="201.953125"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
final class BottomMenuPresentationController: UIPresentationController {
|
||||
override func containerViewWillLayoutSubviews() {
|
||||
super.containerViewWillLayoutSubviews()
|
||||
(presentedViewController as? BottomMenuViewController)?.chromeView.frame = containerView!.bounds
|
||||
presentedView?.frame = frameOfPresentedViewInContainerView
|
||||
}
|
||||
|
||||
override func presentationTransitionWillBegin() {
|
||||
super.presentationTransitionWillBegin()
|
||||
guard let presentedViewController = presentedViewController as? BottomMenuViewController,
|
||||
let coordinator = presentedViewController.transitionCoordinator,
|
||||
let containerView = containerView else { return }
|
||||
|
||||
containerView.addSubview(presentedView!)
|
||||
presentedViewController.containerView = containerView
|
||||
presentedViewController.chromeView.frame = containerView.bounds
|
||||
presentedViewController.chromeView.alpha = 0
|
||||
|
||||
coordinator.animate(alongsideTransition: { _ in
|
||||
presentedViewController.chromeView.alpha = 1
|
||||
}, completion: nil)
|
||||
}
|
||||
|
||||
override func dismissalTransitionWillBegin() {
|
||||
super.dismissalTransitionWillBegin()
|
||||
guard let presentedViewController = presentedViewController as? BottomMenuViewController,
|
||||
let coordinator = presentedViewController.transitionCoordinator,
|
||||
let presentedView = presentedView else { return }
|
||||
|
||||
coordinator.animate(alongsideTransition: { _ in
|
||||
presentedViewController.chromeView.alpha = 0
|
||||
}, completion: { _ in
|
||||
presentedView.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
final class BottomMenuTransitioning: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
private let isPresentation: Bool
|
||||
|
||||
init(isPresentation: Bool) {
|
||||
self.isPresentation = isPresentation
|
||||
super.init()
|
||||
}
|
||||
|
||||
func transitionDuration(using _: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
return kDefaultAnimationDuration
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
guard let fromVC = transitionContext.viewController(forKey: .from),
|
||||
let toVC = transitionContext.viewController(forKey: .to) else { return }
|
||||
|
||||
let animatingVC = isPresentation ? toVC : fromVC
|
||||
guard let animatingView = animatingVC.view else { return }
|
||||
|
||||
let finalFrameForVC = transitionContext.finalFrame(for: animatingVC)
|
||||
var initialFrameForVC = finalFrameForVC
|
||||
initialFrameForVC.origin.y += initialFrameForVC.size.height
|
||||
|
||||
let initialFrame = isPresentation ? initialFrameForVC : finalFrameForVC
|
||||
let finalFrame = isPresentation ? finalFrameForVC : initialFrameForVC
|
||||
|
||||
animatingView.frame = initialFrame
|
||||
|
||||
UIView.animate(withDuration: transitionDuration(using: transitionContext),
|
||||
animations: { animatingView.frame = finalFrame },
|
||||
completion: { _ in
|
||||
transitionContext.completeTransition(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
final class BottomMenuTransitioningManager: NSObject, UIViewControllerTransitioningDelegate {
|
||||
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source _: UIViewController) -> UIPresentationController? {
|
||||
BottomMenuPresentationController(presentedViewController: presented,
|
||||
presenting: presenting)
|
||||
}
|
||||
|
||||
func animationController(forPresented _: UIViewController, presenting _: UIViewController, source _: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return BottomMenuTransitioning(isPresentation: true)
|
||||
}
|
||||
|
||||
func animationController(forDismissed _: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return BottomMenuTransitioning(isPresentation: false)
|
||||
}
|
||||
}
|
||||
14
iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarBuilder.swift
Normal file
14
iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarBuilder.swift
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
@objc class BottomTabBarBuilder: NSObject {
|
||||
@objc static func build(mapViewController: MapViewController, controlsManager: MWMMapViewControlsManager) -> BottomTabBarViewController {
|
||||
let viewController = BottomTabBarViewController(nibName: nil, bundle: nil)
|
||||
let interactor = BottomTabBarInteractor(viewController: viewController,
|
||||
mapViewController: mapViewController,
|
||||
controlsManager: controlsManager)
|
||||
let presenter = BottomTabBarPresenter(interactor: interactor)
|
||||
|
||||
interactor.presenter = presenter
|
||||
viewController.presenter = presenter
|
||||
|
||||
return viewController
|
||||
}
|
||||
}
|
||||
32
iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarButton.swift
Normal file
32
iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarButton.swift
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import UIKit
|
||||
|
||||
class BottomTabBarButton: MWMButton {
|
||||
@objc override func applyTheme() {
|
||||
if styleName.isEmpty {
|
||||
setStyle(.bottomTabBarButton)
|
||||
}
|
||||
|
||||
for style in StyleManager.shared.getStyle(styleName) where !style.isEmpty && !style.hasExclusion(view: self) {
|
||||
BottomTabBarButtonRenderer.render(self, style: style)
|
||||
}
|
||||
}
|
||||
|
||||
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||
return bounds.insetBy(dx: kExtendedTabBarTappableMargin, dy: kExtendedTabBarTappableMargin).contains(point)
|
||||
}
|
||||
}
|
||||
|
||||
class BottomTabBarButtonRenderer {
|
||||
class func render(_ control: BottomTabBarButton, style: Style) {
|
||||
UIViewRenderer.renderShadow(control, style: style)
|
||||
UIViewRenderer.renderBorder(control, style: style)
|
||||
|
||||
if let coloring = style.coloring {
|
||||
control.coloring = coloring
|
||||
}
|
||||
if let backgroundColor = style.backgroundColor {
|
||||
control.backgroundColor = backgroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
protocol BottomTabBarInteractorProtocol: AnyObject {
|
||||
func openSearch()
|
||||
func openLeftButton()
|
||||
func openBookmarks()
|
||||
func openMenu()
|
||||
}
|
||||
|
||||
class BottomTabBarInteractor {
|
||||
weak var presenter: BottomTabBarPresenterProtocol?
|
||||
private weak var viewController: UIViewController?
|
||||
private weak var mapViewController: MapViewController?
|
||||
private weak var controlsManager: MWMMapViewControlsManager?
|
||||
private let searchManager: SearchOnMapManager
|
||||
|
||||
init(viewController: UIViewController, mapViewController: MapViewController, controlsManager: MWMMapViewControlsManager) {
|
||||
self.viewController = viewController
|
||||
self.mapViewController = mapViewController
|
||||
self.controlsManager = controlsManager
|
||||
self.searchManager = mapViewController.searchManager
|
||||
}
|
||||
}
|
||||
|
||||
extension BottomTabBarInteractor: BottomTabBarInteractorProtocol {
|
||||
func openSearch() {
|
||||
searchManager.isSearching ? searchManager.close() : searchManager.startSearching(isRouting: false)
|
||||
}
|
||||
|
||||
func openLeftButton() {
|
||||
switch Settings.leftButtonType {
|
||||
case .addPlace:
|
||||
if let delegate = controlsManager as? BottomMenuDelegate {
|
||||
delegate.addPlace()
|
||||
}
|
||||
case .settings:
|
||||
mapViewController?.openSettings()
|
||||
case .recordTrack:
|
||||
let mapViewController = MapViewController.shared()!
|
||||
let trackRecorder: TrackRecordingManager = .shared
|
||||
switch trackRecorder.recordingState {
|
||||
case .active:
|
||||
mapViewController.showTrackRecordingPlacePage()
|
||||
case .inactive:
|
||||
trackRecorder.start { result in
|
||||
switch result {
|
||||
case .success:
|
||||
mapViewController.showTrackRecordingPlacePage()
|
||||
case .failure:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
mapViewController?.openAbout()
|
||||
}
|
||||
}
|
||||
|
||||
func openBookmarks() {
|
||||
mapViewController?.bookmarksCoordinator.open()
|
||||
}
|
||||
|
||||
func openMenu() {
|
||||
guard let state = controlsManager?.menuState else {
|
||||
fatalError("ERROR: Failed to retrieve the current MapViewControlsManager's state.")
|
||||
}
|
||||
switch state {
|
||||
case .inactive: controlsManager?.menuState = .active
|
||||
case .active: controlsManager?.menuState = .inactive
|
||||
case .hidden:
|
||||
// When the current controls manager's state is hidden, accidental taps on the menu button during the hiding animation should be skipped.
|
||||
break;
|
||||
case .layers: fallthrough
|
||||
@unknown default: fatalError("ERROR: Unexpected MapViewControlsManager's state: \(state)")
|
||||
}
|
||||
}
|
||||
}
|
||||
37
iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarPresenter.swift
Normal file
37
iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarPresenter.swift
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
protocol BottomTabBarPresenterProtocol: AnyObject {
|
||||
func configure()
|
||||
func onLeftButtonPressed()
|
||||
func onSearchButtonPressed()
|
||||
func onBookmarksButtonPressed()
|
||||
func onMenuButtonPressed()
|
||||
}
|
||||
|
||||
class BottomTabBarPresenter: NSObject {
|
||||
private let interactor: BottomTabBarInteractorProtocol
|
||||
|
||||
init(interactor: BottomTabBarInteractorProtocol) {
|
||||
self.interactor = interactor
|
||||
}
|
||||
}
|
||||
|
||||
extension BottomTabBarPresenter: BottomTabBarPresenterProtocol {
|
||||
func configure() {
|
||||
}
|
||||
|
||||
func onLeftButtonPressed() {
|
||||
interactor.openLeftButton()
|
||||
}
|
||||
|
||||
func onSearchButtonPressed() {
|
||||
interactor.openSearch()
|
||||
}
|
||||
|
||||
func onBookmarksButtonPressed() {
|
||||
interactor.openBookmarks()
|
||||
}
|
||||
|
||||
func onMenuButtonPressed() {
|
||||
interactor.openMenu()
|
||||
}
|
||||
}
|
||||
|
||||
28
iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarView.swift
Normal file
28
iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarView.swift
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
let kExtendedTabBarTappableMargin: CGFloat = -15
|
||||
|
||||
final class BottomTabBarView: SolidTouchView {
|
||||
|
||||
@IBOutlet var mainButtonsView: ExtendedBottomTabBarContainerView!
|
||||
|
||||
override var placePageAreaAffectDirections: MWMAvailableAreaAffectDirections {
|
||||
return alternative(iPhone: [], iPad: [.bottom])
|
||||
}
|
||||
|
||||
override var widgetsAreaAffectDirections: MWMAvailableAreaAffectDirections {
|
||||
return [.bottom]
|
||||
}
|
||||
|
||||
override var sideButtonsAreaAffectDirections: MWMAvailableAreaAffectDirections {
|
||||
return [.bottom]
|
||||
}
|
||||
|
||||
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||
return bounds.insetBy(dx: kExtendedTabBarTappableMargin, dy: kExtendedTabBarTappableMargin).contains(point)
|
||||
}
|
||||
}
|
||||
|
||||
final class ExtendedBottomTabBarContainerView: UIView {
|
||||
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||
return bounds.insetBy(dx: kExtendedTabBarTappableMargin, dy: kExtendedTabBarTappableMargin).contains(point)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
|
||||
class BottomTabBarViewController: UIViewController {
|
||||
var presenter: BottomTabBarPresenterProtocol!
|
||||
|
||||
@IBOutlet var leftButton: MWMButton?
|
||||
@IBOutlet var searchButton: MWMButton?
|
||||
@IBOutlet var searchConstraintWithLeftButton: NSLayoutConstraint?
|
||||
@IBOutlet var searchConstraintWithoutLeftButton: NSLayoutConstraint?
|
||||
@IBOutlet var bookmarksButton: MWMButton?
|
||||
@IBOutlet var bookmarksConstraintWithLeftButton: NSLayoutConstraint?
|
||||
@IBOutlet var bookmarksConstraintWithoutLeftButton: NSLayoutConstraint?
|
||||
@IBOutlet var moreButton: MWMButton?
|
||||
@IBOutlet var downloadBadge: UIView?
|
||||
|
||||
private var avaliableArea = CGRect.zero
|
||||
@objc var isHidden: Bool = false {
|
||||
didSet {
|
||||
updateFrame(animated: true)
|
||||
}
|
||||
}
|
||||
@objc var isApplicationBadgeHidden: Bool = true {
|
||||
didSet {
|
||||
updateBadge()
|
||||
}
|
||||
}
|
||||
var tabBarView: BottomTabBarView {
|
||||
return view as! BottomTabBarView
|
||||
}
|
||||
@objc static var controller: BottomTabBarViewController? {
|
||||
return MWMMapViewControlsManager.manager()?.tabBarController
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
presenter.configure()
|
||||
|
||||
NotificationCenter.default.addObserver(forName: UserDefaults.didChangeNotification, object: nil, queue: nil) { _ in
|
||||
DispatchQueue.main.async {
|
||||
self.updateLeftButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
leftButton?.imageView?.contentMode = .scaleAspectFit
|
||||
updateBadge()
|
||||
}
|
||||
|
||||
override func viewWillLayoutSubviews() {
|
||||
super.viewWillLayoutSubviews()
|
||||
updateLeftButton()
|
||||
}
|
||||
|
||||
static func updateAvailableArea(_ frame: CGRect) {
|
||||
BottomTabBarViewController.controller?.updateAvailableArea(frame)
|
||||
}
|
||||
|
||||
@IBAction func onSearchButtonPressed(_ sender: Any) {
|
||||
presenter.onSearchButtonPressed()
|
||||
}
|
||||
|
||||
@IBAction func onLeftButtonPressed(_ sender: Any) {
|
||||
presenter.onLeftButtonPressed()
|
||||
}
|
||||
|
||||
@IBAction func onBookmarksButtonPressed(_ sender: Any) {
|
||||
presenter.onBookmarksButtonPressed()
|
||||
}
|
||||
|
||||
@IBAction func onMenuButtonPressed(_ sender: Any) {
|
||||
presenter.onMenuButtonPressed()
|
||||
}
|
||||
|
||||
private func updateAvailableArea(_ frame:CGRect) {
|
||||
avaliableArea = frame
|
||||
updateFrame(animated: false)
|
||||
self.view.layoutIfNeeded()
|
||||
}
|
||||
|
||||
private func updateFrame(animated: Bool) {
|
||||
if avaliableArea == .zero {
|
||||
return
|
||||
}
|
||||
let newFrame = CGRect(x: avaliableArea.minX,
|
||||
y: isHidden ? avaliableArea.minY + avaliableArea.height : avaliableArea.minY,
|
||||
width: avaliableArea.width,
|
||||
height: avaliableArea.height)
|
||||
let alpha:CGFloat = isHidden ? 0 : 1
|
||||
if animated {
|
||||
UIView.animate(withDuration: kDefaultAnimationDuration,
|
||||
delay: 0,
|
||||
options: [.beginFromCurrentState],
|
||||
animations: {
|
||||
self.view.frame = newFrame
|
||||
self.view.alpha = alpha
|
||||
}, completion: nil)
|
||||
} else {
|
||||
self.view.frame = newFrame
|
||||
self.view.alpha = alpha
|
||||
}
|
||||
}
|
||||
|
||||
private func updateLeftButton() {
|
||||
let leftButtonType = Settings.leftButtonType
|
||||
if leftButtonType == .hidden {
|
||||
leftButton?.isHidden = true
|
||||
|
||||
if let searchConstraintWithLeftButton, let searchConstraintWithoutLeftButton, let bookmarksConstraintWithLeftButton, let bookmarksConstraintWithoutLeftButton {
|
||||
NSLayoutConstraint.activate([searchConstraintWithoutLeftButton, bookmarksConstraintWithoutLeftButton])
|
||||
NSLayoutConstraint.deactivate([searchConstraintWithLeftButton, bookmarksConstraintWithLeftButton])
|
||||
}
|
||||
} else {
|
||||
leftButton?.isHidden = false
|
||||
|
||||
leftButton?.setTitle(nil, for: .normal)
|
||||
leftButton?.setImage(leftButtonType.image, for: .normal)
|
||||
leftButton?.accessibilityLabel = leftButtonType.description;
|
||||
|
||||
if let searchConstraintWithLeftButton, let searchConstraintWithoutLeftButton, let bookmarksConstraintWithLeftButton, let bookmarksConstraintWithoutLeftButton {
|
||||
NSLayoutConstraint.activate([searchConstraintWithLeftButton, bookmarksConstraintWithLeftButton])
|
||||
NSLayoutConstraint.deactivate([searchConstraintWithoutLeftButton, bookmarksConstraintWithoutLeftButton])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateBadge() {
|
||||
downloadBadge?.isHidden = isApplicationBadgeHidden
|
||||
}
|
||||
}
|
||||
148
iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarViewController.xib
Normal file
148
iphone/Maps/UI/BottomMenu/TabBar/BottomTabBarViewController.xib
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="24128" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24063"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="BottomTabBarViewController" customModule="CoMaps" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="bookmarksButton" destination="dgG-ki-3tB" id="md5-3T-9tb"/>
|
||||
<outlet property="bookmarksConstraintWithLeftButton" destination="Jc7-nc-elY" id="gW7-8e-E6m"/>
|
||||
<outlet property="bookmarksConstraintWithoutLeftButton" destination="NRb-vj-MFg" id="C3Z-Ia-D6i"/>
|
||||
<outlet property="downloadBadge" destination="uDI-ZC-4wx" id="fAf-cy-Ozn"/>
|
||||
<outlet property="leftButton" destination="dzf-7Z-N6a" id="LMZ-H7-ftQ"/>
|
||||
<outlet property="moreButton" destination="svD-yi-GrZ" id="kjk-ZW-nZN"/>
|
||||
<outlet property="searchButton" destination="No0-ld-JX3" id="m5F-UT-j94"/>
|
||||
<outlet property="searchConstraintWithLeftButton" destination="tDb-w1-ueQ" id="WaI-Xb-1bu"/>
|
||||
<outlet property="searchConstraintWithoutLeftButton" destination="cQg-jW-uSD" id="cMy-EC-G07"/>
|
||||
<outlet property="view" destination="zuH-WU-hiP" id="eoa-4I-wKs"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="zuH-WU-hiP" customClass="BottomTabBarView" customModule="CoMaps" customModuleProvider="target" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="373" height="84"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vum-s3-PHx" userLabel="MainButtons" customClass="ExtendedBottomTabBarContainerView" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="373" height="48"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleAspectFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dzf-7Z-N6a" userLabel="LeftButton" customClass="BottomTabBarButton" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="22.5" y="0.0" width="48" height="48"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="helpButton"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="dzf-7Z-N6a" secondAttribute="height" id="qNJ-0K-sK0"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="30"/>
|
||||
<inset key="imageEdgeInsets" minX="9" minY="9" maxX="9" maxY="9"/>
|
||||
<state key="normal" image="info.circle" catalog="system"/>
|
||||
<connections>
|
||||
<action selector="onLeftButtonPressed:" destination="-1" eventType="touchUpInside" id="1gx-P2-sRJ"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="249" horizontalCompressionResistancePriority="751" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="No0-ld-JX3" userLabel="Search" customClass="BottomTabBarButton" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="116" y="0.0" width="48" height="48"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="searchButton"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="No0-ld-JX3" secondAttribute="height" id="2bW-fc-Hsh"/>
|
||||
</constraints>
|
||||
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
|
||||
<state key="normal" image="ic_menu_search"/>
|
||||
<connections>
|
||||
<action selector="onSearchButtonPressed:" destination="-1" eventType="touchUpInside" id="0D5-RB-HBQ"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleAspectFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dgG-ki-3tB" userLabel="Bookmarks" customClass="BottomTabBarButton" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="209" y="0.0" width="48" height="48"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="bookmarksButton"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="dgG-ki-3tB" secondAttribute="height" id="o3b-it-lrV"/>
|
||||
</constraints>
|
||||
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
|
||||
<state key="normal" image="ic_menu_bookmark_list"/>
|
||||
<connections>
|
||||
<action selector="onBookmarksButtonPressed:" destination="-1" eventType="touchUpInside" id="9Z1-eg-xth"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleAspectFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="svD-yi-GrZ" userLabel="Menu" customClass="BottomTabBarButton" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="302.5" y="0.0" width="48" height="48"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="menuButton"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="svD-yi-GrZ" secondAttribute="height" id="gmG-3a-Mqe"/>
|
||||
</constraints>
|
||||
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
|
||||
<state key="normal" image="ic_menu"/>
|
||||
<connections>
|
||||
<action selector="onMenuButtonPressed:" destination="-1" eventType="touchUpInside" id="rzb-y4-nR1"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uDI-ZC-4wx" userLabel="DownloadBadge">
|
||||
<rect key="frame" x="329.5" y="11" width="10" height="10"/>
|
||||
<color key="backgroundColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration">
|
||||
<accessibilityTraits key="traits" notEnabled="YES"/>
|
||||
</accessibility>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="10" id="tEP-Xi-qnU"/>
|
||||
<constraint firstAttribute="height" constant="10" id="wNg-5Z-7AO"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Badge"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<accessibility key="accessibilityConfiguration" identifier="MainButtons"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="48" id="69A-eu-uLp"/>
|
||||
<constraint firstItem="No0-ld-JX3" firstAttribute="centerY" secondItem="vum-s3-PHx" secondAttribute="centerY" id="8nL-zT-Y7b"/>
|
||||
<constraint firstItem="No0-ld-JX3" firstAttribute="height" secondItem="vum-s3-PHx" secondAttribute="height" id="9eR-I7-7at"/>
|
||||
<constraint firstItem="svD-yi-GrZ" firstAttribute="height" secondItem="vum-s3-PHx" secondAttribute="height" id="Fde-um-JL6"/>
|
||||
<constraint firstItem="dgG-ki-3tB" firstAttribute="centerX" secondItem="vum-s3-PHx" secondAttribute="centerX" multiplier="1.25" id="Jc7-nc-elY"/>
|
||||
<constraint firstItem="dgG-ki-3tB" firstAttribute="centerY" secondItem="vum-s3-PHx" secondAttribute="centerY" id="JjT-sc-hIY"/>
|
||||
<constraint firstItem="dgG-ki-3tB" firstAttribute="centerX" secondItem="vum-s3-PHx" secondAttribute="centerX" priority="900" id="NRb-vj-MFg"/>
|
||||
<constraint firstItem="svD-yi-GrZ" firstAttribute="centerX" secondItem="vum-s3-PHx" secondAttribute="centerX" multiplier="1.75" id="Q0b-gd-HwS"/>
|
||||
<constraint firstItem="dgG-ki-3tB" firstAttribute="height" secondItem="vum-s3-PHx" secondAttribute="height" id="Rs8-Hl-CAc"/>
|
||||
<constraint firstItem="uDI-ZC-4wx" firstAttribute="centerX" secondItem="svD-yi-GrZ" secondAttribute="centerX" constant="8" id="XNb-Ba-Hn7"/>
|
||||
<constraint firstItem="dzf-7Z-N6a" firstAttribute="centerY" secondItem="vum-s3-PHx" secondAttribute="centerY" id="Zug-zY-KIX"/>
|
||||
<constraint firstItem="No0-ld-JX3" firstAttribute="centerX" secondItem="vum-s3-PHx" secondAttribute="centerX" multiplier="0.25" priority="900" id="cQg-jW-uSD"/>
|
||||
<constraint firstItem="svD-yi-GrZ" firstAttribute="centerY" secondItem="vum-s3-PHx" secondAttribute="centerY" id="sja-hO-YY3"/>
|
||||
<constraint firstItem="No0-ld-JX3" firstAttribute="centerX" secondItem="vum-s3-PHx" secondAttribute="centerX" multiplier="0.75" id="tDb-w1-ueQ"/>
|
||||
<constraint firstItem="dzf-7Z-N6a" firstAttribute="centerX" secondItem="vum-s3-PHx" secondAttribute="centerX" multiplier="0.25" id="u3G-gY-98J"/>
|
||||
<constraint firstItem="dzf-7Z-N6a" firstAttribute="height" secondItem="vum-s3-PHx" secondAttribute="height" id="yTg-8g-H1p"/>
|
||||
<constraint firstItem="uDI-ZC-4wx" firstAttribute="centerY" secondItem="svD-yi-GrZ" secondAttribute="centerY" constant="-8" id="yq3-ui-IaL"/>
|
||||
</constraints>
|
||||
<variation key="default">
|
||||
<mask key="constraints">
|
||||
<exclude reference="cQg-jW-uSD"/>
|
||||
<exclude reference="NRb-vj-MFg"/>
|
||||
</mask>
|
||||
</variation>
|
||||
</view>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="aaw-Hz-zma"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="vum-s3-PHx" firstAttribute="top" secondItem="zuH-WU-hiP" secondAttribute="top" id="PQS-ro-25e"/>
|
||||
<constraint firstItem="vum-s3-PHx" firstAttribute="leading" secondItem="zuH-WU-hiP" secondAttribute="leading" id="kza-JN-Dul"/>
|
||||
<constraint firstAttribute="trailing" secondItem="vum-s3-PHx" secondAttribute="trailing" id="sM6-P2-rN9"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="mainButtonsView" destination="vum-s3-PHx" id="fBi-DA-orA"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="72" y="254"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="ic_menu" width="48" height="48"/>
|
||||
<image name="ic_menu_bookmark_list" width="48" height="48"/>
|
||||
<image name="ic_menu_search" width="48" height="48"/>
|
||||
<image name="info.circle" catalog="system" width="128" height="123"/>
|
||||
</resources>
|
||||
</document>
|
||||
202
iphone/Maps/UI/CarPlay/CarPlayMapViewController.swift
Normal file
202
iphone/Maps/UI/CarPlay/CarPlayMapViewController.swift
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
final class CarPlayMapViewController: MWMViewController {
|
||||
private(set) var mapView: EAGLView?
|
||||
@IBOutlet var speedInfoView: UIView!
|
||||
@IBOutlet var speedCamLimitContainer: UIView!
|
||||
@IBOutlet var speedCamImageView: UIImageView!
|
||||
@IBOutlet var speedCamLimitLabel: UILabel!
|
||||
@IBOutlet var currentSpeedView: UIView!
|
||||
@IBOutlet var currentSpeedLabel: UILabel!
|
||||
private var currentSpeedMps: Double = 0.0
|
||||
private var speedLimitMps: Double?
|
||||
private var speedCamLimitMps: Double?
|
||||
private var isCameraOnRoute: Bool = false
|
||||
private var viewPortState: CPViewPortState = .default
|
||||
private var isSpeedCamBlinking: Bool = false
|
||||
private var isLeftWheelCar: Bool {
|
||||
return self.speedInfoView.frame.origin.x > self.view.frame.midX
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
if mapView?.drapeEngineCreated == false && !MapsAppDelegate.isTestsEnvironment() {
|
||||
mapView?.createDrapeEngine()
|
||||
}
|
||||
updateVisibleViewPortState(viewPortState)
|
||||
}
|
||||
|
||||
func addMapView(_ mapView: EAGLView, mapButtonSafeAreaLayoutGuide: UILayoutGuide) {
|
||||
mapView.translatesAutoresizingMaskIntoConstraints = false
|
||||
removeMapView()
|
||||
|
||||
self.mapView = mapView
|
||||
mapView.frame = view.bounds
|
||||
view.insertSubview(mapView, at: 0)
|
||||
mapView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
|
||||
mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
|
||||
mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
|
||||
mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
|
||||
speedInfoView.trailingAnchor.constraint(equalTo: mapButtonSafeAreaLayoutGuide.trailingAnchor).isActive = true
|
||||
|
||||
speedCamLimitContainer.layer.borderWidth = 2.0
|
||||
}
|
||||
|
||||
func removeMapView() {
|
||||
if let mapView = self.mapView {
|
||||
mapView.removeFromSuperview()
|
||||
self.mapView = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func hideSpeedControl() {
|
||||
if !speedInfoView.isHidden {
|
||||
speedInfoView.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
func showSpeedControl() {
|
||||
if speedInfoView.isHidden {
|
||||
speedInfoView.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
func updateCurrentSpeed(_ speedMps: Double, speedLimitMps: Double?) {
|
||||
self.currentSpeedMps = speedMps
|
||||
self.speedLimitMps = speedLimitMps
|
||||
updateSpeedControl()
|
||||
}
|
||||
|
||||
func updateCameraInfo(isCameraOnRoute: Bool, speedLimitMps: Double?) {
|
||||
self.isCameraOnRoute = isCameraOnRoute
|
||||
self.speedCamLimitMps = speedLimitMps
|
||||
updateSpeedControl()
|
||||
}
|
||||
|
||||
private func BlinkSpeedCamLimit(blink: Bool)
|
||||
{
|
||||
if blink {
|
||||
if !isSpeedCamBlinking {
|
||||
speedCamLimitLabel.alpha = 0
|
||||
speedCamImageView.alpha = 1
|
||||
UIView.animate(withDuration: 0.5,
|
||||
delay:0.0,
|
||||
options:[.repeat, .autoreverse, .curveEaseOut],
|
||||
animations: { self.speedCamImageView.alpha = 0; self.speedCamLimitLabel.alpha = 1 })
|
||||
isSpeedCamBlinking = true
|
||||
}
|
||||
} else {
|
||||
if (isSpeedCamBlinking) {
|
||||
speedCamLimitLabel.layer.removeAllAnimations()
|
||||
speedCamImageView.layer.removeAllAnimations()
|
||||
isSpeedCamBlinking = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateSpeedControl() {
|
||||
let speedMeasure = Measure.init(asSpeed: currentSpeedMps)
|
||||
currentSpeedLabel.text = speedMeasure.valueAsString
|
||||
|
||||
if isCameraOnRoute {
|
||||
speedCamLimitContainer.layer.borderColor = UIColor.speedLimitRed().cgColor
|
||||
speedCamImageView.tintColor = UIColor.speedLimitRed()
|
||||
|
||||
// self.speedCamLimitMps comes from SpeedCamManager and is based on
|
||||
// the nearest speed camera info when it is close enough.
|
||||
// If it's unknown self.speedLimitMps is used, which is based on current road speed limit.
|
||||
if let speedCamLimitMps = (self.speedCamLimitMps ?? self.speedLimitMps) {
|
||||
BlinkSpeedCamLimit(blink: true)
|
||||
let speedCamLimitMeasure = Measure.init(asSpeed: speedCamLimitMps)
|
||||
speedCamLimitLabel.text = speedCamLimitMeasure.valueAsString
|
||||
speedCamLimitLabel.textColor = UIColor.speedLimitDarkGray()
|
||||
|
||||
currentSpeedLabel.textColor = UIColor.white
|
||||
if speedCamLimitMps >= currentSpeedMps {
|
||||
currentSpeedView.backgroundColor = UIColor.speedLimitGreen()
|
||||
} else {
|
||||
currentSpeedView.backgroundColor = UIColor.speedLimitRed()
|
||||
}
|
||||
} else {
|
||||
BlinkSpeedCamLimit(blink: false)
|
||||
speedCamLimitLabel.alpha = 0.0
|
||||
speedCamImageView.tintColor = UIColor.speedLimitRed()
|
||||
speedCamImageView.alpha = 1.0
|
||||
|
||||
currentSpeedLabel.textColor = UIColor.speedLimitDarkGray()
|
||||
currentSpeedView.backgroundColor = UIColor.speedLimitWhite()
|
||||
}
|
||||
} else { // !isCameraOnRoute
|
||||
BlinkSpeedCamLimit(blink: false)
|
||||
currentSpeedLabel.textColor = UIColor.speedLimitDarkGray()
|
||||
if let speedLimitMps = self.speedLimitMps {
|
||||
speedCamImageView.alpha = 0.0
|
||||
let speedLimitMeasure = Measure.init(asSpeed: speedLimitMps)
|
||||
speedCamLimitLabel.textColor = UIColor.speedLimitDarkGray()
|
||||
// speedLimitMps == 0 means unlimited speed.
|
||||
if speedLimitMeasure.value == 0 {
|
||||
speedCamLimitLabel.text = "🚀" //"∞"
|
||||
}
|
||||
else {
|
||||
speedCamLimitLabel.text = speedLimitMeasure.valueAsString;
|
||||
}
|
||||
speedCamLimitLabel.alpha = 1.0
|
||||
speedCamLimitContainer.layer.borderColor = UIColor.speedLimitRed().cgColor
|
||||
if currentSpeedMps > speedLimitMps {
|
||||
currentSpeedLabel.textColor = UIColor.speedLimitRed()
|
||||
}
|
||||
} else {
|
||||
speedCamImageView.tintColor = UIColor.speedLimitLightGray()
|
||||
speedCamImageView.alpha = 1.0
|
||||
speedCamLimitLabel.alpha = 0.0
|
||||
speedCamLimitContainer.layer.borderColor = UIColor.speedLimitLightGray().cgColor
|
||||
}
|
||||
currentSpeedView.backgroundColor = UIColor.speedLimitWhite()
|
||||
}
|
||||
}
|
||||
|
||||
func updateVisibleViewPortState(_ state: CPViewPortState) {
|
||||
if CarPlayService.shared.isCarplayActivated {
|
||||
viewPortState = state
|
||||
switch viewPortState {
|
||||
case .default:
|
||||
updateVisibleViewPortToDefaultState()
|
||||
case .preview:
|
||||
updateVisibleViewPortToPreviewState()
|
||||
case .navigation:
|
||||
updateVisibleViewPortToNavigationState()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateVisibleViewPortToPreviewState() {
|
||||
updateVisibleViewPort(frame: view.frame.inset(by: view.safeAreaInsets))
|
||||
}
|
||||
|
||||
private func updateVisibleViewPortToNavigationState() {
|
||||
updateVisibleViewPort(frame: view.frame.inset(by: view.safeAreaInsets))
|
||||
}
|
||||
|
||||
private func updateVisibleViewPortToDefaultState() {
|
||||
updateVisibleViewPort(frame: view.bounds)
|
||||
}
|
||||
|
||||
private func updateVisibleViewPort(frame: CGRect) {
|
||||
guard CarPlayService.shared.isCarplayActivated else { return }
|
||||
FrameworkHelper.setVisibleViewport(frame, scaleFactor: mapView?.contentScaleFactor ?? 1)
|
||||
}
|
||||
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
super.traitCollectionDidChange(previousTraitCollection)
|
||||
// Triggers the map style updating when CarPlay's 'Appearance' setting is changed.
|
||||
ThemeManager.invalidate()
|
||||
}
|
||||
|
||||
override func applyTheme() {
|
||||
super.applyTheme()
|
||||
updateSpeedControl()
|
||||
}
|
||||
}
|
||||
76
iphone/Maps/UI/ColorPicker/ColorPicker.swift
Normal file
76
iphone/Maps/UI/ColorPicker/ColorPicker.swift
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
enum ColorPickerType {
|
||||
case defaultColorPicker(UIColor)
|
||||
case bookmarkColorPicker(BookmarkColor)
|
||||
}
|
||||
|
||||
final class ColorPicker: NSObject {
|
||||
|
||||
static let shared = ColorPicker()
|
||||
|
||||
private var onUpdateColorHandler: ((UIColor) -> Void)?
|
||||
|
||||
// MARK: - Public
|
||||
/// Presents a color picker view controller modally from a specified root view controller.
|
||||
///
|
||||
/// - Uses native color picker on the iOS 14.0+ for the `defaultColorPicker` type on iPhone and iPad.
|
||||
/// - For the rest of the iOS versions, `bookmarkColorPicker` type and iPad designed for Mac uses a custom color picker.
|
||||
///
|
||||
func present(from rootViewController: UIViewController, pickerType: ColorPickerType, completionHandler: ((UIColor) -> Void)?) {
|
||||
onUpdateColorHandler = completionHandler
|
||||
let colorPickerViewController: UIViewController
|
||||
|
||||
switch pickerType {
|
||||
case .defaultColorPicker(let color):
|
||||
if !ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
colorPickerViewController = defaultColorPickerViewController(with: color)
|
||||
} else {
|
||||
colorPickerViewController = bookmarksColorPickerViewController(with: BookmarkColor.bookmarkColor(from: color) ?? .none)
|
||||
}
|
||||
case .bookmarkColorPicker(let bookmarkColor):
|
||||
colorPickerViewController = bookmarksColorPickerViewController(with: bookmarkColor)
|
||||
}
|
||||
rootViewController.present(colorPickerViewController, animated: true)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
@available(iOS 14.0, *)
|
||||
private func defaultColorPickerViewController(with selectedColor: UIColor) -> UIViewController {
|
||||
let colorPickerController = UIColorPickerViewController()
|
||||
colorPickerController.supportsAlpha = false
|
||||
colorPickerController.selectedColor = selectedColor
|
||||
colorPickerController.delegate = self
|
||||
return colorPickerController
|
||||
}
|
||||
|
||||
private func bookmarksColorPickerViewController(with selectedColor: BookmarkColor) -> UIViewController {
|
||||
let bookmarksColorViewController = BookmarkColorViewController(bookmarkColor: selectedColor)
|
||||
bookmarksColorViewController.delegate = self
|
||||
// The navigation controller is used for getting the navigation item with the title and the close button.
|
||||
let navigationController = UINavigationController(rootViewController: bookmarksColorViewController)
|
||||
return navigationController
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - BookmarkColorViewControllerDelegate
|
||||
extension ColorPicker: BookmarkColorViewControllerDelegate {
|
||||
func bookmarkColorViewController(_ viewController: BookmarkColorViewController, didSelect bookmarkColor: BookmarkColor) {
|
||||
onUpdateColorHandler?(bookmarkColor.color)
|
||||
onUpdateColorHandler = nil
|
||||
viewController.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIColorPickerViewControllerDelegate
|
||||
extension ColorPicker: UIColorPickerViewControllerDelegate {
|
||||
@available(iOS 14.0, *)
|
||||
func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) {
|
||||
onUpdateColorHandler?(viewController.selectedColor.sRGBColor)
|
||||
onUpdateColorHandler = nil
|
||||
viewController.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
func colorPickerViewControllerDidSelectColor(_ viewController: UIColorPickerViewController) {
|
||||
onUpdateColorHandler?(viewController.selectedColor.sRGBColor)
|
||||
}
|
||||
}
|
||||
25
iphone/Maps/UI/DocumentPicker/DocumentPicker.swift
Normal file
25
iphone/Maps/UI/DocumentPicker/DocumentPicker.swift
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
typealias URLsCompletionHandler = ([URL]) -> Void
|
||||
|
||||
final class DocumentPicker: NSObject {
|
||||
static let shared = DocumentPicker()
|
||||
private var completionHandler: URLsCompletionHandler?
|
||||
|
||||
func present(from rootViewController: UIViewController,
|
||||
fileTypes: [FileType] = [.kml, .kmz, .gpx],
|
||||
completionHandler: @escaping URLsCompletionHandler) {
|
||||
self.completionHandler = completionHandler
|
||||
let documentPickerViewController: UIDocumentPickerViewController
|
||||
documentPickerViewController = UIDocumentPickerViewController(forOpeningContentTypes: fileTypes.map(\.utType), asCopy: true)
|
||||
documentPickerViewController.delegate = self
|
||||
// TODO: Enable multiple selection when the multiple files parsing support will be added to the bookmark_manager.
|
||||
documentPickerViewController.allowsMultipleSelection = false
|
||||
rootViewController.present(documentPickerViewController, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIDocumentPickerDelegate
|
||||
extension DocumentPicker: UIDocumentPickerDelegate {
|
||||
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
||||
completionHandler?(urls)
|
||||
}
|
||||
}
|
||||
22
iphone/Maps/UI/DocumentPicker/FileType.swift
Normal file
22
iphone/Maps/UI/DocumentPicker/FileType.swift
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import UniformTypeIdentifiers
|
||||
|
||||
// TODO: (KK) Remove this type-wrapper and use custom UTTypeIdentifier that is registered into the Info.plist after updating to the iOS >= 14.0.
|
||||
struct FileType {
|
||||
let fileExtension: String
|
||||
let typeIdentifier: String
|
||||
}
|
||||
|
||||
extension FileType {
|
||||
static let kml = FileType(fileExtension: "kml", typeIdentifier: "com.google.earth.kml")
|
||||
static let kmz = FileType(fileExtension: "kmz", typeIdentifier: "com.google.earth.kmz")
|
||||
static let gpx = FileType(fileExtension: "gpx", typeIdentifier: "com.topografix.gpx")
|
||||
}
|
||||
|
||||
// MARK: - FileType + UTType
|
||||
extension FileType {
|
||||
@available(iOS 14.0, *)
|
||||
var utType: UTType {
|
||||
UTType(filenameExtension: fileExtension)!
|
||||
}
|
||||
}
|
||||
|
||||
168
iphone/Maps/UI/Downloader/AvailableMapsDataSource.swift
Normal file
168
iphone/Maps/UI/Downloader/AvailableMapsDataSource.swift
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
class AvailableMapsDataSource {
|
||||
struct Const {
|
||||
static let locationArrow = "➤"
|
||||
}
|
||||
|
||||
private let parentCountryId: String?
|
||||
|
||||
private var sections: [String]?
|
||||
private var sectionsContent: [String: [String]]?
|
||||
private var nearbySection: [String]?
|
||||
|
||||
fileprivate var searching = false
|
||||
fileprivate lazy var searchDataSource: IDownloaderDataSource = {
|
||||
SearchMapsDataSource()
|
||||
}()
|
||||
|
||||
init(_ parentCountryId: String? = nil, location: CLLocationCoordinate2D? = nil) {
|
||||
self.parentCountryId = parentCountryId
|
||||
let countryIds: [String]
|
||||
if let parentCountryId = parentCountryId {
|
||||
countryIds = Storage.shared().allCountries(withParent: parentCountryId)
|
||||
} else {
|
||||
countryIds = Storage.shared().allCountries()
|
||||
}
|
||||
configSections(countryIds, location: location)
|
||||
}
|
||||
|
||||
private func configSections(_ countryIds: [String], location: CLLocationCoordinate2D?) {
|
||||
let countries = countryIds.map {
|
||||
CountryIdAndName(countryId: $0, name: Storage.shared().name(forCountry: $0))
|
||||
}.sorted {
|
||||
$0.countryName.compare($1.countryName) == .orderedAscending
|
||||
}
|
||||
|
||||
sections = []
|
||||
sectionsContent = [:]
|
||||
|
||||
if let location = location, let nearbySection = Storage.shared().nearbyAvailableCountries(location) {
|
||||
sections?.append(Const.locationArrow)
|
||||
sectionsContent![Const.locationArrow] = nearbySection
|
||||
}
|
||||
|
||||
for country in countries {
|
||||
let section = parentCountryId == nil ? String(country.countryName.prefix(1)) : L("downloader_available_maps")
|
||||
if sections!.last != section {
|
||||
sections!.append(section)
|
||||
sectionsContent![section] = []
|
||||
}
|
||||
|
||||
var sectionCountries = sectionsContent![section]
|
||||
sectionCountries?.append(country.countryId)
|
||||
sectionsContent![section] = sectionCountries
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AvailableMapsDataSource: IDownloaderDataSource {
|
||||
var isEmpty: Bool {
|
||||
searching ? searchDataSource.isEmpty : false
|
||||
}
|
||||
|
||||
var title: String {
|
||||
guard let parentCountryId = parentCountryId else {
|
||||
return L("download_maps")
|
||||
}
|
||||
return Storage.shared().name(forCountry: parentCountryId)
|
||||
}
|
||||
|
||||
var isRoot: Bool {
|
||||
parentCountryId == nil
|
||||
}
|
||||
|
||||
var isSearching: Bool {
|
||||
searching
|
||||
}
|
||||
|
||||
func getParentCountryId() -> String {
|
||||
if parentCountryId != nil {
|
||||
return parentCountryId!
|
||||
}
|
||||
return Storage.shared().getRootId()
|
||||
}
|
||||
|
||||
func parentAttributes() -> MapNodeAttributes {
|
||||
guard let parentId = parentCountryId else {
|
||||
return Storage.shared().attributesForRoot()
|
||||
}
|
||||
return Storage.shared().attributes(forCountry: parentId)
|
||||
}
|
||||
|
||||
func numberOfSections() -> Int {
|
||||
searching ? searchDataSource.numberOfSections() : (sections?.count ?? 0)
|
||||
}
|
||||
|
||||
func numberOfItems(in section: Int) -> Int {
|
||||
if searching {
|
||||
return searchDataSource.numberOfItems(in: section)
|
||||
}
|
||||
let index = sections![section]
|
||||
return sectionsContent![index]!.count
|
||||
}
|
||||
|
||||
func item(at indexPath: IndexPath) -> MapNodeAttributes {
|
||||
if searching {
|
||||
return searchDataSource.item(at: indexPath)
|
||||
}
|
||||
let sectionIndex = sections![indexPath.section]
|
||||
let sectionItems = sectionsContent![sectionIndex]
|
||||
let countryId = sectionItems![indexPath.item]
|
||||
return Storage.shared().attributes(forCountry: countryId)
|
||||
}
|
||||
|
||||
func matchedName(at indexPath: IndexPath) -> String? {
|
||||
searching ? searchDataSource.matchedName(at: indexPath) : nil
|
||||
}
|
||||
|
||||
func title(for section: Int) -> String {
|
||||
if searching {
|
||||
return searchDataSource.title(for: section)
|
||||
}
|
||||
let title = sections![section]
|
||||
if title == Const.locationArrow {
|
||||
return L("downloader_near_me_subtitle")
|
||||
}
|
||||
return title
|
||||
}
|
||||
|
||||
func indexTitles() -> [String]? {
|
||||
if searching {
|
||||
return nil
|
||||
}
|
||||
if parentCountryId != nil {
|
||||
return nil
|
||||
}
|
||||
return sections
|
||||
}
|
||||
|
||||
func dataSourceFor(_ childId: String) -> IDownloaderDataSource {
|
||||
searching ? searchDataSource.dataSourceFor(childId) : AvailableMapsDataSource(childId)
|
||||
}
|
||||
|
||||
func reload(_ completion: () -> Void) {
|
||||
if searching {
|
||||
searchDataSource.reload(completion)
|
||||
}
|
||||
// do nothing.
|
||||
completion()
|
||||
}
|
||||
|
||||
func search(_ query: String, locale: String, update: @escaping (Bool) -> Void) {
|
||||
if query.isEmpty {
|
||||
cancelSearch()
|
||||
update(true)
|
||||
return
|
||||
}
|
||||
searchDataSource.search(query, locale: locale) { [weak self] (finished) in
|
||||
if finished {
|
||||
self?.searching = true
|
||||
update(finished)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancelSearch() {
|
||||
searching = false
|
||||
searchDataSource.cancelSearch()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#import "MWMTableViewCell.h"
|
||||
|
||||
@interface MWMMapDownloaderButtonTableViewCell : MWMTableViewCell
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#import "MWMMapDownloaderButtonTableViewCell.h"
|
||||
|
||||
@implementation MWMMapDownloaderButtonTableViewCell
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
[self config];
|
||||
}
|
||||
|
||||
- (void)prepareForReuse
|
||||
{
|
||||
[super prepareForReuse];
|
||||
[self config];
|
||||
}
|
||||
|
||||
- (void)config
|
||||
{
|
||||
if ([self respondsToSelector:@selector(setSeparatorInset:)])
|
||||
[self setSeparatorInset:UIEdgeInsetsZero];
|
||||
if ([self respondsToSelector:@selector(setLayoutMargins:)])
|
||||
[self setLayoutMargins:UIEdgeInsetsZero];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="0.0" shouldIndentWhileEditing="NO" reuseIdentifier="MWMMapDownloaderButtonTableViewCell" id="KGk-i7-Jjw" customClass="MWMMapDownloaderButtonTableViewCell" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Add Maps Button" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="dOW-mN-UY7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<color key="textColor" systemColor="linkColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:linkBlueText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="download_maps"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<inset key="separatorInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<point key="canvasLocation" x="139" y="155"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
<resources>
|
||||
<systemColor name="linkColor">
|
||||
<color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@interface MWMMapDownloaderCellHeader : UILabel
|
||||
|
||||
@end
|
||||
23
iphone/Maps/UI/Downloader/Cells/MWMMapDownloaderCellHeader.m
Normal file
23
iphone/Maps/UI/Downloader/Cells/MWMMapDownloaderCellHeader.m
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#import "MWMMapDownloaderCellHeader.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
@implementation MWMMapDownloaderCellHeader
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
[self setStyleNameAndApply:@"regular12:blackSecondaryText"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)drawTextInRect:(CGRect)rect
|
||||
{
|
||||
rect = UIEdgeInsetsInsetRect(rect, UIEdgeInsetsMake(0, 16, 0, 0));
|
||||
rect = UIEdgeInsetsInsetRect(rect, self.safeAreaInsets);
|
||||
[super drawTextInRect:rect];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#import "MWMMapDownloaderTableViewCell.h"
|
||||
|
||||
@interface MWMMapDownloaderLargeCountryTableViewCell : MWMMapDownloaderTableViewCell
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#import "MWMMapDownloaderLargeCountryTableViewCell.h"
|
||||
#import "MWMCircularProgress.h"
|
||||
|
||||
#import <CoreApi/MWMMapNodeAttributes.h>
|
||||
|
||||
@interface MWMMapDownloaderLargeCountryTableViewCell ()
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UILabel * mapsCount;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMMapDownloaderLargeCountryTableViewCell
|
||||
|
||||
#pragma mark - Config
|
||||
|
||||
- (void)config:(MWMMapNodeAttributes *)nodeAttrs searchQuery:(NSString *)searchQuery {
|
||||
[super config:nodeAttrs searchQuery:searchQuery];
|
||||
BOOL haveLocalMaps = (nodeAttrs.downloadedMwmCount != 0);
|
||||
NSString *ofMaps = haveLocalMaps ? [NSString stringWithFormat:L(@"downloader_of"), nodeAttrs.downloadedMwmCount, nodeAttrs.totalMwmCount] : @(nodeAttrs.totalMwmCount).stringValue;
|
||||
self.mapsCount.text = [NSString stringWithFormat:@"%@: %@", L(@"downloader_status_maps"), ofMaps];
|
||||
}
|
||||
|
||||
- (void)configProgress:(MWMMapNodeAttributes *)nodeAttrs {
|
||||
[super configProgress:nodeAttrs];
|
||||
if (nodeAttrs.nodeStatus == MWMMapNodeStatusPartly || nodeAttrs.nodeStatus == MWMMapNodeStatusNotDownloaded) {
|
||||
MWMCircularProgressStateVec affectedStates = @[@(MWMCircularProgressStateNormal), @(MWMCircularProgressStateSelected)];
|
||||
[self.progress setImageName:@"ic_folder" forStates:affectedStates];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="MWMMapDownloaderLargeCountryTableViewCell" rowHeight="62" id="4CW-jw-1JP" customClass="MWMMapDownloaderLargeCountryTableViewCell" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="62"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="4CW-jw-1JP" id="rFT-0r-5zy">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="62"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3NQ-tT-U9b">
|
||||
<rect key="frame" x="12" y="13" width="36" height="36"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="36" id="3vs-uR-5WA"/>
|
||||
<constraint firstAttribute="width" constant="36" id="WtB-IW-uuH"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="People's Republic of China" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cum-4j-JlQ">
|
||||
<rect key="frame" x="60" y="12" width="167" height="20"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.87" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="14 maps" lineBreakMode="wordWrap" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="851-fC-gEN">
|
||||
<rect key="frame" x="60" y="36" width="224" height="14"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.54000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular12:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="760" text="405 MB" textAlignment="right" lineBreakMode="wordWrap" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9Ni-mb-fA5">
|
||||
<rect key="frame" x="235" y="22.5" width="49" height="17"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_arrow_gray_right" translatesAutoresizingMaskIntoConstraints="NO" id="7HN-EK-QXO" userLabel="Arrow">
|
||||
<rect key="frame" x="284" y="17" width="28" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="28" id="OyB-rL-7rb"/>
|
||||
<constraint firstAttribute="height" constant="28" id="dNb-uE-TzF"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMGray"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="cum-4j-JlQ" firstAttribute="leading" secondItem="rFT-0r-5zy" secondAttribute="leading" constant="60" id="6W7-tW-rcf"/>
|
||||
<constraint firstAttribute="trailing" secondItem="9Ni-mb-fA5" secondAttribute="trailing" constant="36" id="C7I-fY-Gfw"/>
|
||||
<constraint firstItem="851-fC-gEN" firstAttribute="trailing" secondItem="9Ni-mb-fA5" secondAttribute="trailing" id="DJE-zo-NaQ"/>
|
||||
<constraint firstAttribute="trailing" secondItem="7HN-EK-QXO" secondAttribute="trailing" constant="8" id="EZJ-3g-VZ0"/>
|
||||
<constraint firstItem="3NQ-tT-U9b" firstAttribute="leading" secondItem="rFT-0r-5zy" secondAttribute="leading" constant="12" id="Km2-Bp-fFb"/>
|
||||
<constraint firstItem="851-fC-gEN" firstAttribute="leading" secondItem="cum-4j-JlQ" secondAttribute="leading" id="NHY-P5-axf"/>
|
||||
<constraint firstItem="cum-4j-JlQ" firstAttribute="top" secondItem="rFT-0r-5zy" secondAttribute="top" constant="12" id="SEx-Ax-stg"/>
|
||||
<constraint firstAttribute="bottom" secondItem="851-fC-gEN" secondAttribute="bottom" constant="12" id="ZwR-py-AZC"/>
|
||||
<constraint firstItem="851-fC-gEN" firstAttribute="top" secondItem="cum-4j-JlQ" secondAttribute="bottom" constant="4" id="aAE-b0-nwb"/>
|
||||
<constraint firstItem="9Ni-mb-fA5" firstAttribute="leading" secondItem="cum-4j-JlQ" secondAttribute="trailing" constant="8" id="hV0-8P-jwr"/>
|
||||
<constraint firstItem="9Ni-mb-fA5" firstAttribute="centerY" secondItem="rFT-0r-5zy" secondAttribute="centerY" id="tB8-k0-qYw"/>
|
||||
<constraint firstItem="7HN-EK-QXO" firstAttribute="centerY" secondItem="rFT-0r-5zy" secondAttribute="centerY" id="tjc-5f-EBn"/>
|
||||
<constraint firstItem="3NQ-tT-U9b" firstAttribute="centerY" secondItem="rFT-0r-5zy" secondAttribute="centerY" id="zd3-Ex-VJY"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<inset key="separatorInset" minX="60" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="downloadSize" destination="9Ni-mb-fA5" id="btQ-8t-pIV"/>
|
||||
<outlet property="mapsCount" destination="851-fC-gEN" id="dY0-zX-KS2"/>
|
||||
<outlet property="stateWrapper" destination="3NQ-tT-U9b" id="PJw-9U-8b3"/>
|
||||
<outlet property="title" destination="cum-4j-JlQ" id="f9c-KC-2dR"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="408.69565217391306" y="269.86607142857139"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="ic_arrow_gray_right" width="28" height="28"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#import "MWMMapDownloaderTableViewCell.h"
|
||||
|
||||
@interface MWMMapDownloaderPlaceTableViewCell : MWMMapDownloaderTableViewCell
|
||||
|
||||
@property (nonatomic) BOOL needDisplayArea;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
#import "MWMMapDownloaderPlaceTableViewCell.h"
|
||||
|
||||
#import <CoreApi/MWMMapNodeAttributes.h>
|
||||
|
||||
@interface MWMMapDownloaderPlaceTableViewCell ()
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UILabel *descriptionLabel;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *titleBottomOffset;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMMapDownloaderPlaceTableViewCell
|
||||
|
||||
#pragma mark - Config
|
||||
|
||||
- (void)config:(MWMMapNodeAttributes *)nodeAttrs searchQuery:(NSString *)searchQuery {
|
||||
[super config:nodeAttrs searchQuery:searchQuery];
|
||||
BOOL isDescriptionVisible = NO;
|
||||
NSDictionary *selectedAreaAttrs = @{NSFontAttributeName : [UIFont bold12]};
|
||||
NSDictionary *unselectedAreaAttrs = @{NSFontAttributeName : [UIFont regular12]};
|
||||
self.needDisplayArea = !nodeAttrs.hasParent;
|
||||
if (self.needDisplayArea && nodeAttrs.topmostParentInfo.count == 1) {
|
||||
isDescriptionVisible = nodeAttrs.hasParent;
|
||||
if (isDescriptionVisible) {
|
||||
self.descriptionLabel.attributedText = [self matchedString:nodeAttrs.topmostParentInfo[0].countryName
|
||||
selectedAttrs:selectedAreaAttrs
|
||||
unselectedAttrs:unselectedAreaAttrs];
|
||||
}
|
||||
}
|
||||
else if (nodeAttrs.nodeDescription.length > 0)
|
||||
{
|
||||
isDescriptionVisible = YES;
|
||||
self.descriptionLabel.attributedText = [self matchedString:nodeAttrs.nodeDescription
|
||||
selectedAttrs:selectedAreaAttrs
|
||||
unselectedAttrs:unselectedAreaAttrs];
|
||||
}
|
||||
self.descriptionLabel.hidden = !isDescriptionVisible;
|
||||
self.titleBottomOffset.priority =
|
||||
isDescriptionVisible ? UILayoutPriorityDefaultLow : UILayoutPriorityDefaultHigh;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="MWMMapDownloaderPlaceTableViewCell" rowHeight="62" id="1KI-85-wsU" customClass="MWMMapDownloaderPlaceTableViewCell" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="62"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="1KI-85-wsU" id="mRF-11-OKU">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="62"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Yb5-r1-Z2X">
|
||||
<rect key="frame" x="12" y="13" width="36" height="36"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="36" id="OjM-Wz-G8P"/>
|
||||
<constraint firstAttribute="width" constant="36" id="vYF-tP-Wtw"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="London" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Db-Yq-FlD">
|
||||
<rect key="frame" x="60" y="12" width="195" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" priority="750" constant="20" id="fnm-gO-0Fg"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.87" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="UK" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fev-4l-MY3">
|
||||
<rect key="frame" x="60" y="36" width="199" height="14"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.54000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular12:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="760" text="45 MB" textAlignment="right" lineBreakMode="wordWrap" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rqh-iy-Sx9">
|
||||
<rect key="frame" x="263" y="23" width="41" height="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="3Db-Yq-FlD" firstAttribute="leading" secondItem="mRF-11-OKU" secondAttribute="leading" constant="60" id="2CW-BR-iT1"/>
|
||||
<constraint firstItem="Yb5-r1-Z2X" firstAttribute="leading" secondItem="mRF-11-OKU" secondAttribute="leading" constant="12" id="GGp-H1-htH"/>
|
||||
<constraint firstItem="3Db-Yq-FlD" firstAttribute="top" secondItem="mRF-11-OKU" secondAttribute="top" constant="12" id="Jhy-gy-RcP"/>
|
||||
<constraint firstItem="rqh-iy-Sx9" firstAttribute="leading" secondItem="3Db-Yq-FlD" secondAttribute="trailing" constant="8" id="NhL-qc-Mcu"/>
|
||||
<constraint firstAttribute="trailing" secondItem="rqh-iy-Sx9" secondAttribute="trailing" constant="16" id="dil-Rw-64e"/>
|
||||
<constraint firstItem="fev-4l-MY3" firstAttribute="top" secondItem="3Db-Yq-FlD" secondAttribute="bottom" priority="500" constant="4" id="ePz-Xy-IoM"/>
|
||||
<constraint firstAttribute="bottom" secondItem="3Db-Yq-FlD" secondAttribute="bottom" priority="250" constant="12" id="ffv-VG-1xY"/>
|
||||
<constraint firstItem="Yb5-r1-Z2X" firstAttribute="centerY" secondItem="mRF-11-OKU" secondAttribute="centerY" id="g72-Dp-9Ky"/>
|
||||
<constraint firstItem="fev-4l-MY3" firstAttribute="leading" secondItem="3Db-Yq-FlD" secondAttribute="leading" id="kP7-cA-GeN"/>
|
||||
<constraint firstAttribute="bottom" secondItem="fev-4l-MY3" secondAttribute="bottom" priority="500" constant="12" id="mJE-YM-1uE"/>
|
||||
<constraint firstItem="rqh-iy-Sx9" firstAttribute="centerY" secondItem="mRF-11-OKU" secondAttribute="centerY" id="qZa-yl-a6W"/>
|
||||
<constraint firstItem="rqh-iy-Sx9" firstAttribute="leading" secondItem="fev-4l-MY3" secondAttribute="trailing" constant="4" id="rqB-DG-k9N"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<inset key="separatorInset" minX="60" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="descriptionLabel" destination="fev-4l-MY3" id="lyN-5S-iWE"/>
|
||||
<outlet property="downloadSize" destination="rqh-iy-Sx9" id="g9R-G7-dxQ"/>
|
||||
<outlet property="stateWrapper" destination="Yb5-r1-Z2X" id="iPW-N5-qJi"/>
|
||||
<outlet property="title" destination="3Db-Yq-FlD" id="qtt-YF-a0V"/>
|
||||
<outlet property="titleBottomOffset" destination="ffv-VG-1xY" id="0kL-3a-tKy"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="408.69565217391306" y="269.86607142857139"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#import "MWMMapDownloaderPlaceTableViewCell.h"
|
||||
|
||||
@interface MWMMapDownloaderSubplaceTableViewCell : MWMMapDownloaderPlaceTableViewCell
|
||||
|
||||
- (void)setSubplaceText:(NSString *)text;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#import "MWMMapDownloaderSubplaceTableViewCell.h"
|
||||
|
||||
@interface MWMMapDownloaderSubplaceTableViewCell ()
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UILabel *subPlace;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMMapDownloaderSubplaceTableViewCell
|
||||
|
||||
- (void)setSubplaceText:(NSString *)text {
|
||||
self.subPlace.attributedText = [self matchedString:text
|
||||
selectedAttrs:@{NSFontAttributeName : [UIFont bold14]}
|
||||
unselectedAttrs:@{NSFontAttributeName : [UIFont regular14]}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="MWMMapDownloaderSubplaceTableViewCell" rowHeight="82" id="1KI-85-wsU" customClass="MWMMapDownloaderSubplaceTableViewCell" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="82"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="1KI-85-wsU" id="mRF-11-OKU">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="82"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Yb5-r1-Z2X">
|
||||
<rect key="frame" x="12" y="23" width="36" height="36"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="36" id="OjM-Wz-G8P"/>
|
||||
<constraint firstAttribute="width" constant="36" id="vYF-tP-Wtw"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="760" text="Mossel Bay" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="x7m-Zm-8y6" propertyAccessControl="none">
|
||||
<rect key="frame" x="60" y="12" width="199" height="16.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.54000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="760" text="London" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Db-Yq-FlD">
|
||||
<rect key="frame" x="60" y="32.5" width="195" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" priority="750" constant="20" id="CiU-uC-JVj"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.87" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="UK" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fev-4l-MY3">
|
||||
<rect key="frame" x="60" y="56.5" width="199" height="14"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.54000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular12:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="760" text="45 MB" textAlignment="right" lineBreakMode="wordWrap" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rqh-iy-Sx9">
|
||||
<rect key="frame" x="263" y="33" width="41" height="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="3Db-Yq-FlD" firstAttribute="leading" secondItem="mRF-11-OKU" secondAttribute="leading" constant="60" id="4Sj-Yh-d4y"/>
|
||||
<constraint firstItem="3Db-Yq-FlD" firstAttribute="top" secondItem="x7m-Zm-8y6" secondAttribute="bottom" constant="4" id="AFW-TD-ADw"/>
|
||||
<constraint firstItem="x7m-Zm-8y6" firstAttribute="top" secondItem="mRF-11-OKU" secondAttribute="top" constant="12" id="E4o-jw-VEZ"/>
|
||||
<constraint firstItem="Yb5-r1-Z2X" firstAttribute="leading" secondItem="mRF-11-OKU" secondAttribute="leading" constant="12" id="GGp-H1-htH"/>
|
||||
<constraint firstItem="rqh-iy-Sx9" firstAttribute="leading" secondItem="x7m-Zm-8y6" secondAttribute="trailing" constant="4" id="GsC-fa-5kk"/>
|
||||
<constraint firstItem="rqh-iy-Sx9" firstAttribute="leading" secondItem="3Db-Yq-FlD" secondAttribute="trailing" constant="8" id="I7Q-yY-JMS"/>
|
||||
<constraint firstAttribute="bottom" secondItem="fev-4l-MY3" secondAttribute="bottom" priority="500" constant="12" id="Rnj-5e-Hfy"/>
|
||||
<constraint firstItem="fev-4l-MY3" firstAttribute="leading" secondItem="3Db-Yq-FlD" secondAttribute="leading" id="Yk6-jH-hKS"/>
|
||||
<constraint firstAttribute="bottom" secondItem="3Db-Yq-FlD" secondAttribute="bottom" priority="250" constant="12" id="Zic-It-hOL"/>
|
||||
<constraint firstAttribute="trailing" secondItem="rqh-iy-Sx9" secondAttribute="trailing" constant="16" id="dil-Rw-64e"/>
|
||||
<constraint firstItem="Yb5-r1-Z2X" firstAttribute="centerY" secondItem="mRF-11-OKU" secondAttribute="centerY" id="g72-Dp-9Ky"/>
|
||||
<constraint firstItem="rqh-iy-Sx9" firstAttribute="leading" secondItem="fev-4l-MY3" secondAttribute="trailing" constant="4" id="oqd-UU-h0T"/>
|
||||
<constraint firstItem="fev-4l-MY3" firstAttribute="top" secondItem="3Db-Yq-FlD" secondAttribute="bottom" constant="4" id="pwP-cL-stJ"/>
|
||||
<constraint firstItem="x7m-Zm-8y6" firstAttribute="leading" secondItem="3Db-Yq-FlD" secondAttribute="leading" id="pxg-tv-fNn"/>
|
||||
<constraint firstItem="rqh-iy-Sx9" firstAttribute="centerY" secondItem="mRF-11-OKU" secondAttribute="centerY" id="qZa-yl-a6W"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<inset key="separatorInset" minX="60" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="descriptionLabel" destination="fev-4l-MY3" id="1C2-OF-Vhu"/>
|
||||
<outlet property="downloadSize" destination="rqh-iy-Sx9" id="q4T-Pv-DrI"/>
|
||||
<outlet property="stateWrapper" destination="Yb5-r1-Z2X" id="eb8-Ut-gh5"/>
|
||||
<outlet property="subPlace" destination="x7m-Zm-8y6" id="63W-Bc-FdX"/>
|
||||
<outlet property="title" destination="3Db-Yq-FlD" id="JdN-yn-xLw"/>
|
||||
<outlet property="titleBottomOffset" destination="Zic-It-hOL" id="1gW-IS-6Fc"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="408.69565217391306" y="269.86607142857139"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
#import "MWMMapDownloaderMode.h"
|
||||
#import "MWMTableViewCell.h"
|
||||
|
||||
@class MWMCircularProgress;
|
||||
@class MWMMapNodeAttributes;
|
||||
@class MWMMapDownloaderTableViewCell;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol MWMMapDownloaderTableViewCellDelegate <NSObject>
|
||||
|
||||
- (void)mapDownloaderCellDidPressProgress:(MWMMapDownloaderTableViewCell *)cell;
|
||||
- (void)mapDownloaderCellDidLongPress:(MWMMapDownloaderTableViewCell *)cell;
|
||||
|
||||
@end
|
||||
|
||||
@interface MWMMapDownloaderTableViewCell : MWMTableViewCell
|
||||
|
||||
@property(nonatomic) MWMCircularProgress *progress;
|
||||
@property(weak, nonatomic) id<MWMMapDownloaderTableViewCellDelegate> delegate;
|
||||
@property(nonatomic) MWMMapDownloaderMode mode;
|
||||
@property(readonly, nonatomic) MWMMapNodeAttributes *nodeAttrs;
|
||||
|
||||
- (void)config:(MWMMapNodeAttributes *)nodeAttrs searchQuery:(nullable NSString *)searchQuery;
|
||||
- (void)configProgress:(MWMMapNodeAttributes *)nodeAttrs;
|
||||
- (void)setDownloadProgress:(CGFloat)progress;
|
||||
- (NSAttributedString *)matchedString:(NSString *)str
|
||||
selectedAttrs:(NSDictionary *)selectedAttrs
|
||||
unselectedAttrs:(NSDictionary *)unselectedAttrs;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
149
iphone/Maps/UI/Downloader/Cells/MWMMapDownloaderTableViewCell.m
Normal file
149
iphone/Maps/UI/Downloader/Cells/MWMMapDownloaderTableViewCell.m
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
#import "MWMMapDownloaderTableViewCell.h"
|
||||
#import "MWMCircularProgress.h"
|
||||
#import "NSString+Categories.h"
|
||||
|
||||
#import <CoreApi/MWMCommon.h>
|
||||
#import <CoreApi/MWMFrameworkHelper.h>
|
||||
#import <CoreApi/MWMMapNodeAttributes.h>
|
||||
|
||||
@interface MWMMapDownloaderTableViewCell () <MWMCircularProgressProtocol>
|
||||
|
||||
@property(copy, nonatomic) NSString *searchQuery;
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UIView *stateWrapper;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *title;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *downloadSize;
|
||||
|
||||
@property(strong, nonatomic) MWMMapNodeAttributes *nodeAttrs;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMMapDownloaderTableViewCell
|
||||
|
||||
- (void)awakeFromNib {
|
||||
[super awakeFromNib];
|
||||
UILongPressGestureRecognizer *lpGR = [[UILongPressGestureRecognizer alloc] initWithTarget:self
|
||||
action:@selector(onLongPress:)];
|
||||
[self addGestureRecognizer:lpGR];
|
||||
}
|
||||
|
||||
- (void)prepareForReuse {
|
||||
[super prepareForReuse];
|
||||
self.nodeAttrs = nil;
|
||||
}
|
||||
|
||||
- (void)onLongPress:(UILongPressGestureRecognizer *)sender {
|
||||
if (sender.state != UIGestureRecognizerStateBegan) {
|
||||
return;
|
||||
}
|
||||
[self.delegate mapDownloaderCellDidLongPress:self];
|
||||
}
|
||||
|
||||
#pragma mark - Search matching
|
||||
|
||||
- (NSAttributedString *)matchedString:(NSString *)str
|
||||
selectedAttrs:(NSDictionary *)selectedAttrs
|
||||
unselectedAttrs:(NSDictionary *)unselectedAttrs {
|
||||
NSMutableAttributedString *attrTitle = [[NSMutableAttributedString alloc] initWithString:str];
|
||||
[attrTitle addAttributes:unselectedAttrs range:NSMakeRange(0, str.length)];
|
||||
if (!self.searchQuery)
|
||||
return [attrTitle copy];
|
||||
for (NSValue *range in [str rangesOfString:self.searchQuery])
|
||||
[attrTitle addAttributes:selectedAttrs range:range.rangeValue];
|
||||
return [attrTitle copy];
|
||||
}
|
||||
|
||||
#pragma mark - Config
|
||||
|
||||
- (void)config:(MWMMapNodeAttributes *)nodeAttrs searchQuery:(NSString *)searchQuery {
|
||||
self.searchQuery = searchQuery;
|
||||
self.nodeAttrs = nodeAttrs;
|
||||
[self configProgress:nodeAttrs];
|
||||
|
||||
self.title.attributedText = [self matchedString:nodeAttrs.nodeName
|
||||
selectedAttrs:@{NSFontAttributeName: [UIFont bold17]}
|
||||
unselectedAttrs:@{NSFontAttributeName: [UIFont regular17]}];
|
||||
|
||||
uint64_t size = 0;
|
||||
BOOL isModeDownloaded = self.mode == MWMMapDownloaderModeDownloaded;
|
||||
|
||||
switch (nodeAttrs.nodeStatus) {
|
||||
case MWMMapNodeStatusUndefined:
|
||||
case MWMMapNodeStatusError:
|
||||
case MWMMapNodeStatusOnDiskOutOfDate:
|
||||
case MWMMapNodeStatusNotDownloaded:
|
||||
case MWMMapNodeStatusApplying:
|
||||
case MWMMapNodeStatusInQueue:
|
||||
case MWMMapNodeStatusPartly:
|
||||
size = isModeDownloaded ? nodeAttrs.downloadedSize : nodeAttrs.totalSize;
|
||||
break;
|
||||
case MWMMapNodeStatusDownloading:
|
||||
size = isModeDownloaded ? nodeAttrs.totalUpdateSizeBytes : nodeAttrs.totalSize - nodeAttrs.downloadingSize;
|
||||
break;
|
||||
case MWMMapNodeStatusOnDisk:
|
||||
size = isModeDownloaded ? nodeAttrs.totalSize : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
self.downloadSize.text = formattedSize(size);
|
||||
self.downloadSize.hidden = (size == 0);
|
||||
}
|
||||
|
||||
- (void)configProgress:(MWMMapNodeAttributes *)nodeAttrs {
|
||||
MWMCircularProgress *progress = self.progress;
|
||||
BOOL isModeDownloaded = self.mode == MWMMapDownloaderModeDownloaded;
|
||||
MWMButtonColoring coloring = isModeDownloaded ? MWMButtonColoringBlack : MWMButtonColoringBlue;
|
||||
switch (nodeAttrs.nodeStatus) {
|
||||
case MWMMapNodeStatusNotDownloaded:
|
||||
case MWMMapNodeStatusPartly: {
|
||||
MWMCircularProgressStateVec affectedStates = @[@(MWMCircularProgressStateNormal), @(MWMCircularProgressStateSelected)];
|
||||
[progress setImageName:@"ic_download" forStates:affectedStates];
|
||||
[progress setColoring:coloring forStates:affectedStates];
|
||||
progress.state = MWMCircularProgressStateNormal;
|
||||
break;
|
||||
}
|
||||
case MWMMapNodeStatusDownloading:
|
||||
progress.progress = kMaxProgress * nodeAttrs.downloadedSize / (isModeDownloaded ? nodeAttrs.totalUpdateSizeBytes : nodeAttrs.totalSize - nodeAttrs.downloadingSize);
|
||||
break;
|
||||
case MWMMapNodeStatusApplying:
|
||||
case MWMMapNodeStatusInQueue:
|
||||
progress.state = MWMCircularProgressStateSpinner;
|
||||
break;
|
||||
case MWMMapNodeStatusUndefined:
|
||||
case MWMMapNodeStatusError:
|
||||
progress.state = MWMCircularProgressStateFailed;
|
||||
break;
|
||||
case MWMMapNodeStatusOnDisk:
|
||||
progress.state = MWMCircularProgressStateCompleted;
|
||||
break;
|
||||
case MWMMapNodeStatusOnDiskOutOfDate: {
|
||||
MWMCircularProgressStateVec affectedStates = @[@(MWMCircularProgressStateNormal), @(MWMCircularProgressStateSelected)];
|
||||
[progress setImageName:@"ic_update" forStates:affectedStates];
|
||||
[progress setColoring:MWMButtonColoringOther forStates:affectedStates];
|
||||
progress.state = MWMCircularProgressStateNormal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setDownloadProgress:(CGFloat)progress {
|
||||
self.progress.progress = kMaxProgress * progress;
|
||||
}
|
||||
|
||||
#pragma mark - MWMCircularProgressProtocol
|
||||
|
||||
- (void)progressButtonPressed:(nonnull MWMCircularProgress *)progress {
|
||||
[self.delegate mapDownloaderCellDidPressProgress:self];
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (MWMCircularProgress *)progress {
|
||||
if (!_progress) {
|
||||
_progress = [MWMCircularProgress downloaderProgressForParentView:self.stateWrapper];
|
||||
_progress.delegate = self;
|
||||
}
|
||||
return _progress;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" horizontalCompressionResistancePriority="760" selectionStyle="default" indentationWidth="10" reuseIdentifier="MWMMapDownloaderTableViewCell" rowHeight="52" id="Igh-sI-4cU" customClass="MWMMapDownloaderTableViewCell" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="52"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Igh-sI-4cU" id="hYr-eg-wbg">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="52"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rTN-aw-5k7">
|
||||
<rect key="frame" x="12" y="8" width="36" height="36"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="36" id="H0T-d1-25M"/>
|
||||
<constraint firstAttribute="height" constant="36" id="aA3-Ok-RSD"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="300" verticalHuggingPriority="251" horizontalCompressionResistancePriority="800" text="30 MB" textAlignment="right" lineBreakMode="wordWrap" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lgA-X0-501">
|
||||
<rect key="frame" x="262.5" y="18" width="41.5" height="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="260" verticalHuggingPriority="249" horizontalCompressionResistancePriority="752" verticalCompressionResistancePriority="752" text="Algeria" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5kc-K7-7K8">
|
||||
<rect key="frame" x="60" y="16" width="194.5" height="20"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.87" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="lgA-X0-501" firstAttribute="centerY" secondItem="hYr-eg-wbg" secondAttribute="centerY" id="5QG-IM-adn"/>
|
||||
<constraint firstItem="5kc-K7-7K8" firstAttribute="leading" secondItem="hYr-eg-wbg" secondAttribute="leading" constant="60" id="6Dv-Pw-tTy"/>
|
||||
<constraint firstAttribute="trailing" secondItem="lgA-X0-501" secondAttribute="trailing" constant="16" id="Af9-0B-Y6q"/>
|
||||
<constraint firstItem="rTN-aw-5k7" firstAttribute="leading" secondItem="hYr-eg-wbg" secondAttribute="leading" constant="12" id="Jfc-5q-LZI"/>
|
||||
<constraint firstAttribute="bottom" secondItem="5kc-K7-7K8" secondAttribute="bottom" constant="16" id="ZLY-Tp-hnL"/>
|
||||
<constraint firstItem="lgA-X0-501" firstAttribute="leading" secondItem="5kc-K7-7K8" secondAttribute="trailing" constant="8" id="Zwe-tB-xNb"/>
|
||||
<constraint firstItem="5kc-K7-7K8" firstAttribute="top" secondItem="hYr-eg-wbg" secondAttribute="top" constant="16" id="anV-3S-XAR"/>
|
||||
<constraint firstItem="rTN-aw-5k7" firstAttribute="centerY" secondItem="hYr-eg-wbg" secondAttribute="centerY" id="tND-fx-OhO"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<inset key="separatorInset" minX="60" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="downloadSize" destination="lgA-X0-501" id="Dl4-r1-7lU"/>
|
||||
<outlet property="stateWrapper" destination="rTN-aw-5k7" id="3jJ-Zp-S0C"/>
|
||||
<outlet property="title" destination="5kc-K7-7K8" id="SDA-Yd-DD2"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="413.04347826086962" y="271.875"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
134
iphone/Maps/UI/Downloader/DownloadAllView/DownloadAllView.swift
Normal file
134
iphone/Maps/UI/Downloader/DownloadAllView/DownloadAllView.swift
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
import UIKit
|
||||
|
||||
protocol DownloadAllViewDelegate: AnyObject {
|
||||
func onDownloadButtonPressed()
|
||||
func onRetryButtonPressed()
|
||||
func onCancelButtonPressed()
|
||||
func onStateChanged(state: DownloadAllView.State)
|
||||
}
|
||||
|
||||
class DownloadAllView: UIView {
|
||||
enum State {
|
||||
case none
|
||||
case ready
|
||||
case error
|
||||
case dowloading
|
||||
}
|
||||
enum Style {
|
||||
case download
|
||||
case update
|
||||
}
|
||||
|
||||
@IBOutlet private var iconImageView: UIImageView!
|
||||
@IBOutlet private var title: UILabel!
|
||||
@IBOutlet private var downloadSizeLabel: UILabel!
|
||||
@IBOutlet private var stateWrapper: UIView!
|
||||
@IBOutlet private var downloadButton: UIButton!
|
||||
@IBOutlet private var titleCenterConstraint: NSLayoutConstraint!
|
||||
lazy private var progress: MWMCircularProgress = {
|
||||
let view = MWMCircularProgress.downloaderProgress(forParentView: stateWrapper)
|
||||
view.delegate = self
|
||||
return view
|
||||
}()
|
||||
|
||||
var isSizeHidden: Bool = false {
|
||||
didSet {
|
||||
if oldValue != isSizeHidden {
|
||||
updateView()
|
||||
}
|
||||
}
|
||||
}
|
||||
var style: Style = .download {
|
||||
didSet {
|
||||
if oldValue != style {
|
||||
updateView()
|
||||
}
|
||||
}
|
||||
}
|
||||
var state: State = .ready {
|
||||
didSet {
|
||||
if oldValue != state {
|
||||
updateView()
|
||||
delegate?.onStateChanged(state: state)
|
||||
}
|
||||
}
|
||||
}
|
||||
var downloadSize: UInt64 = 0 {
|
||||
didSet {
|
||||
downloadSizeLabel.text = formattedSize(downloadSize)
|
||||
}
|
||||
}
|
||||
var downloadProgress: CGFloat = 0 {
|
||||
didSet {
|
||||
self.progress.progress = downloadProgress
|
||||
}
|
||||
}
|
||||
weak var delegate: DownloadAllViewDelegate?
|
||||
|
||||
@IBAction func onDownloadButtonPress(_ sender: Any) {
|
||||
if state == .error {
|
||||
delegate?.onRetryButtonPressed()
|
||||
} else {
|
||||
delegate?.onDownloadButtonPressed()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateView() {
|
||||
let readyTitle: String
|
||||
let downloadingTitle: String
|
||||
let readyButtonTitle: String
|
||||
let errorTitle = L("country_status_download_failed")
|
||||
let errorButtonTitle = L("downloader_retry")
|
||||
|
||||
switch style {
|
||||
case .download:
|
||||
iconImageView.image = UIImage(named: "ic_download_all")
|
||||
readyTitle = L("downloader_download_all_button")
|
||||
downloadingTitle = L("downloader_loading_ios")
|
||||
readyButtonTitle = L("download_button")
|
||||
case .update:
|
||||
iconImageView.image = UIImage(named: "ic_update_all")
|
||||
readyTitle = L("downloader_update_maps")
|
||||
downloadingTitle = L("downloader_updating_ios")
|
||||
readyButtonTitle = L("downloader_update_all_button")
|
||||
}
|
||||
|
||||
titleCenterConstraint.priority = isSizeHidden ? .defaultHigh : .defaultLow
|
||||
downloadSizeLabel.isHidden = isSizeHidden
|
||||
|
||||
switch state {
|
||||
case .error:
|
||||
iconImageView.image = UIImage(named: "ic_download_error")
|
||||
title.text = errorTitle
|
||||
title.setFontStyleAndApply(.red)
|
||||
downloadButton.setTitle(errorButtonTitle, for: .normal)
|
||||
downloadButton.isHidden = false
|
||||
stateWrapper.isHidden = true
|
||||
progress.state = .spinner
|
||||
downloadSizeLabel.isHidden = false
|
||||
case .ready:
|
||||
title.text = readyTitle
|
||||
title.setFontStyleAndApply(.blackPrimary)
|
||||
downloadButton.setTitle(readyButtonTitle, for: .normal)
|
||||
downloadButton.isHidden = false
|
||||
stateWrapper.isHidden = true
|
||||
progress.state = .spinner
|
||||
downloadSizeLabel.isHidden = false
|
||||
case .dowloading:
|
||||
title.text = downloadingTitle
|
||||
title.setFontStyleAndApply(.blackPrimary)
|
||||
downloadButton.isHidden = true
|
||||
stateWrapper.isHidden = false
|
||||
progress.state = .spinner
|
||||
case .none:
|
||||
self.downloadButton.isHidden = true
|
||||
self.stateWrapper.isHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension DownloadAllView: MWMCircularProgressProtocol {
|
||||
func progressButtonPressed(_ progress: MWMCircularProgress) {
|
||||
delegate?.onCancelButtonPressed()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="iN0-l3-epB" customClass="DownloadAllView" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="64"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="cKg-JT-LQ3">
|
||||
<rect key="frame" x="16" y="20" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="24" id="IoI-GO-nG3"/>
|
||||
<constraint firstAttribute="height" constant="24" id="TMm-8V-jMo"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4vQ-dY-JAM">
|
||||
<rect key="frame" x="58" y="10" width="32" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" priority="750" constant="20" id="38P-x2-HCd"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.87" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="760" text="..." textAlignment="right" lineBreakMode="wordWrap" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mVA-td-yVW">
|
||||
<rect key="frame" x="58" y="38" width="12" height="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.66666666669999997" green="0.66666666669999997" blue="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NBt-lB-IvV">
|
||||
<rect key="frame" x="352" y="17" width="46" height="30"/>
|
||||
<state key="normal" title="Button"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="onDownloadButtonPress:" destination="iN0-l3-epB" eventType="touchUpInside" id="6kx-3L-wc2"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wlz-tD-b2M">
|
||||
<rect key="frame" x="354" y="14" width="36" height="36"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="36" id="C2G-Y6-VKF"/>
|
||||
<constraint firstAttribute="width" constant="36" id="Uo3-uS-WpU"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="mVA-td-yVW" firstAttribute="leading" secondItem="cKg-JT-LQ3" secondAttribute="trailing" constant="18" id="1J4-kU-lyG"/>
|
||||
<constraint firstItem="cKg-JT-LQ3" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="16" id="2AL-TB-z3U"/>
|
||||
<constraint firstItem="wlz-tD-b2M" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="8aq-IR-Ffz"/>
|
||||
<constraint firstItem="4vQ-dY-JAM" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" priority="500" constant="10" id="AOx-bc-GRy"/>
|
||||
<constraint firstItem="cKg-JT-LQ3" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="Ehd-uD-6HS"/>
|
||||
<constraint firstItem="4vQ-dY-JAM" firstAttribute="leading" secondItem="cKg-JT-LQ3" secondAttribute="trailing" constant="18" id="IH6-Jq-o9T"/>
|
||||
<constraint firstItem="NBt-lB-IvV" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="KUE-7v-eNY"/>
|
||||
<constraint firstAttribute="trailing" secondItem="NBt-lB-IvV" secondAttribute="trailing" constant="16" id="PgQ-HZ-Ui5"/>
|
||||
<constraint firstItem="4vQ-dY-JAM" firstAttribute="centerY" secondItem="cKg-JT-LQ3" secondAttribute="centerY" priority="250" id="gWz-3d-LHv"/>
|
||||
<constraint firstAttribute="bottom" secondItem="mVA-td-yVW" secondAttribute="bottom" constant="10" id="gj3-BP-k3L"/>
|
||||
<constraint firstAttribute="trailing" secondItem="wlz-tD-b2M" secondAttribute="trailing" constant="24" id="s3R-CP-1zA"/>
|
||||
</constraints>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="PressBackground"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="downloadButton" destination="NBt-lB-IvV" id="RDS-Sj-rmo"/>
|
||||
<outlet property="downloadSizeLabel" destination="mVA-td-yVW" id="G7H-Vl-OQQ"/>
|
||||
<outlet property="iconImageView" destination="cKg-JT-LQ3" id="lvM-rM-apH"/>
|
||||
<outlet property="stateWrapper" destination="wlz-tD-b2M" id="7XI-dP-yUS"/>
|
||||
<outlet property="title" destination="4vQ-dY-JAM" id="bZv-x2-pe5"/>
|
||||
<outlet property="titleCenterConstraint" destination="gWz-3d-LHv" id="6b1-Ah-2ns"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="137.68115942028987" y="-162.72321428571428"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
507
iphone/Maps/UI/Downloader/DownloadMapsViewController.swift
Normal file
507
iphone/Maps/UI/Downloader/DownloadMapsViewController.swift
Normal file
|
|
@ -0,0 +1,507 @@
|
|||
import UIKit
|
||||
|
||||
@objc(MWMDownloadMapsViewController)
|
||||
class DownloadMapsViewController: MWMViewController {
|
||||
// MARK: - Types
|
||||
|
||||
private enum NodeAction {
|
||||
case showOnMap
|
||||
case download
|
||||
case update
|
||||
case cancelDownload
|
||||
case retryDownload
|
||||
case delete
|
||||
}
|
||||
|
||||
private enum AllMapsButtonState {
|
||||
case none
|
||||
case download(String)
|
||||
case cancel(String)
|
||||
}
|
||||
|
||||
// MARK: - Outlets
|
||||
|
||||
@IBOutlet var tableView: UITableView!
|
||||
@IBOutlet var noMapsContainer: UIView!
|
||||
@IBOutlet var downloadAllViewContainer: UIView!
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
private var searchController = UISearchController(searchResultsController: nil)
|
||||
var dataSource: IDownloaderDataSource!
|
||||
@objc var mode: MWMMapDownloaderMode = .downloaded
|
||||
private var skipCountryEvent = false
|
||||
private var hasAddMapSection: Bool { dataSource.isRoot && mode == .downloaded }
|
||||
private let allMapsViewBottomOffsetConstant: CGFloat = 64
|
||||
|
||||
lazy var noSerchResultViewController: SearchNoResultsViewController = {
|
||||
let vc = storyboard!.instantiateViewController(ofType: SearchNoResultsViewController.self)
|
||||
view.insertSubview(vc.view, aboveSubview: tableView)
|
||||
vc.view.alignToSuperview()
|
||||
vc.view.isHidden = true
|
||||
addChild(vc)
|
||||
vc.didMove(toParent: self)
|
||||
return vc
|
||||
}()
|
||||
|
||||
lazy var downloadAllView: DownloadAllView = {
|
||||
let view = Bundle.main.load(viewClass: DownloadAllView.self)?.first as! DownloadAllView
|
||||
view.delegate = self
|
||||
downloadAllViewContainer.addSubview(view)
|
||||
downloadAllViewContainer.addSeparator()
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
view.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
|
||||
view.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
|
||||
view.topAnchor.constraint(equalTo: downloadAllViewContainer.topAnchor),
|
||||
view.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
|
||||
])
|
||||
return view
|
||||
}()
|
||||
|
||||
// MARK: - Methods
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
if dataSource == nil {
|
||||
switch mode {
|
||||
case .downloaded:
|
||||
dataSource = DownloadedMapsDataSource()
|
||||
case .available:
|
||||
dataSource = AvailableMapsDataSource(location: LocationManager.lastLocation()?.coordinate)
|
||||
@unknown default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
tableView.registerNib(cell: MWMMapDownloaderTableViewCell.self)
|
||||
tableView.registerNib(cell: MWMMapDownloaderPlaceTableViewCell.self)
|
||||
tableView.registerNib(cell: MWMMapDownloaderLargeCountryTableViewCell.self)
|
||||
tableView.registerNib(cell: MWMMapDownloaderSubplaceTableViewCell.self)
|
||||
tableView.registerNib(cell: MWMMapDownloaderButtonTableViewCell.self)
|
||||
title = dataSource.title
|
||||
if mode == .downloaded {
|
||||
let addMapsButton = button(with: UIImage(systemName: "plus"), action: #selector(onAddMaps))
|
||||
navigationItem.rightBarButtonItem = addMapsButton
|
||||
}
|
||||
noMapsContainer.isHidden = !dataSource.isEmpty || Storage.shared().downloadInProgress()
|
||||
extendedLayoutIncludesOpaqueBars = true
|
||||
searchController.searchBar.placeholder = L("downloader_search_field_hint")
|
||||
searchController.searchBar.delegate = self
|
||||
searchController.obscuresBackgroundDuringPresentation = false
|
||||
searchController.hidesNavigationBarDuringPresentation = alternativeSizeClass(iPhone: true, iPad: false)
|
||||
searchController.searchBar.applyTheme()
|
||||
navigationItem.searchController = searchController
|
||||
navigationItem.hidesSearchBarWhenScrolling = false
|
||||
|
||||
configButtons()
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
dataSource.reload {
|
||||
reloadData()
|
||||
noMapsContainer.isHidden = !dataSource.isEmpty || Storage.shared().downloadInProgress()
|
||||
}
|
||||
Storage.shared().add(self)
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
Storage.shared().remove(self)
|
||||
}
|
||||
|
||||
fileprivate func showChildren(_ nodeAttrs: MapNodeAttributes) {
|
||||
let vc = storyboard!.instantiateViewController(ofType: DownloadMapsViewController.self)
|
||||
vc.mode = dataSource.isSearching ? .available : mode
|
||||
vc.dataSource = dataSource.dataSourceFor(nodeAttrs.countryId)
|
||||
navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
|
||||
fileprivate func showActions(_ nodeAttrs: MapNodeAttributes, in cell: UITableViewCell) {
|
||||
let menuTitle = nodeAttrs.nodeName
|
||||
let multiparent = nodeAttrs.parentInfo.count > 1
|
||||
let message = dataSource.isRoot || multiparent ? nil : nodeAttrs.parentInfo.first?.countryName
|
||||
let actionSheet = UIAlertController(title: menuTitle, message: message, preferredStyle: .actionSheet)
|
||||
actionSheet.popoverPresentationController?.sourceView = cell
|
||||
actionSheet.popoverPresentationController?.sourceRect = cell.bounds
|
||||
|
||||
let actions: [NodeAction]
|
||||
switch nodeAttrs.nodeStatus {
|
||||
case .undefined:
|
||||
actions = []
|
||||
case .downloading, .applying, .inQueue:
|
||||
actions = [.cancelDownload]
|
||||
case .error:
|
||||
actions = nodeAttrs.downloadedMwmCount > 0 ? [.retryDownload, .delete] : [.retryDownload]
|
||||
case .onDiskOutOfDate:
|
||||
actions = [.showOnMap, .update, .delete]
|
||||
case .onDisk:
|
||||
actions = [.showOnMap, .delete]
|
||||
case .notDownloaded:
|
||||
actions = [.download]
|
||||
case .partly:
|
||||
actions = [.download, .delete]
|
||||
@unknown default:
|
||||
fatalError()
|
||||
}
|
||||
|
||||
addActions(actions, for: nodeAttrs, to: actionSheet)
|
||||
actionSheet.addAction(UIAlertAction(title: L("cancel"), style: .cancel))
|
||||
present(actionSheet, animated: true)
|
||||
}
|
||||
|
||||
private func addActions(_ actions: [NodeAction], for nodeAttrs: MapNodeAttributes, to actionSheet: UIAlertController) {
|
||||
actions.forEach { [unowned self] in
|
||||
let action: UIAlertAction
|
||||
switch $0 {
|
||||
case .showOnMap:
|
||||
action = UIAlertAction(title: L("zoom_to_country"), style: .default, handler: { _ in
|
||||
Storage.shared().showNode(nodeAttrs.countryId)
|
||||
self.navigationController?.popToRootViewController(animated: true)
|
||||
})
|
||||
case .download:
|
||||
let prefix = nodeAttrs.totalMwmCount == 1 ? L("downloader_download_map") : L("downloader_download_all_button")
|
||||
action = UIAlertAction(title: "\(prefix) (\(formattedSize(nodeAttrs.totalSize)))",
|
||||
style: .default,
|
||||
handler: { _ in
|
||||
Storage.shared().downloadNode(nodeAttrs.countryId)
|
||||
})
|
||||
case .update:
|
||||
let size = formattedSize(nodeAttrs.totalUpdateSizeBytes)
|
||||
let title = "\(L("downloader_status_outdated")) \(size)"
|
||||
action = UIAlertAction(title: title, style: .default, handler: { _ in
|
||||
Storage.shared().updateNode(nodeAttrs.countryId)
|
||||
})
|
||||
case .cancelDownload:
|
||||
action = UIAlertAction(title: L("cancel_download"), style: .destructive, handler: { _ in
|
||||
Storage.shared().cancelDownloadNode(nodeAttrs.countryId)
|
||||
})
|
||||
case .retryDownload:
|
||||
action = UIAlertAction(title: L("downloader_retry"), style: .destructive, handler: { _ in
|
||||
Storage.shared().retryDownloadNode(nodeAttrs.countryId)
|
||||
})
|
||||
case .delete:
|
||||
action = UIAlertAction(title: L("downloader_delete_map"), style: .destructive, handler: { _ in
|
||||
Storage.shared().deleteNode(nodeAttrs.countryId)
|
||||
})
|
||||
}
|
||||
actionSheet.addAction(action)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func reloadData() {
|
||||
tableView.reloadData()
|
||||
configButtons()
|
||||
}
|
||||
|
||||
fileprivate func configButtons() {
|
||||
downloadAllView.state = .none
|
||||
downloadAllView.isSizeHidden = false
|
||||
let parentAttributes = dataSource.parentAttributes()
|
||||
let error = parentAttributes.nodeStatus == .error || parentAttributes.nodeStatus == .undefined
|
||||
let downloading = parentAttributes.nodeStatus == .downloading || parentAttributes.nodeStatus == .inQueue || parentAttributes.nodeStatus == .applying
|
||||
switch mode {
|
||||
case .available:
|
||||
if dataSource.isRoot {
|
||||
break
|
||||
}
|
||||
if error {
|
||||
downloadAllView.state = .error
|
||||
} else if downloading {
|
||||
downloadAllView.state = .dowloading
|
||||
} else if parentAttributes.downloadedMwmCount < parentAttributes.totalMwmCount {
|
||||
downloadAllView.state = .ready
|
||||
downloadAllView.style = .download
|
||||
downloadAllView.downloadSize = parentAttributes.totalSize - parentAttributes.downloadedSize
|
||||
}
|
||||
case .downloaded:
|
||||
let isUpdate = parentAttributes.totalUpdateSizeBytes > 0
|
||||
let size = isUpdate ? parentAttributes.totalUpdateSizeBytes : parentAttributes.downloadingSize
|
||||
if error {
|
||||
downloadAllView.state = dataSource.isRoot ? .none : .error
|
||||
downloadAllView.downloadSize = parentAttributes.downloadingSize
|
||||
} else if downloading && dataSource is DownloadedMapsDataSource {
|
||||
downloadAllView.state = .dowloading
|
||||
if dataSource.isRoot {
|
||||
downloadAllView.style = .download
|
||||
downloadAllView.isSizeHidden = true
|
||||
}
|
||||
} else if isUpdate {
|
||||
downloadAllView.state = .ready
|
||||
downloadAllView.style = .update
|
||||
downloadAllView.downloadSize = size
|
||||
}
|
||||
@unknown default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func onAddMaps() {
|
||||
let vc = storyboard!.instantiateViewController(ofType: DownloadMapsViewController.self)
|
||||
if !dataSource.isRoot {
|
||||
vc.dataSource = AvailableMapsDataSource(dataSource.getParentCountryId())
|
||||
}
|
||||
vc.mode = .available
|
||||
navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
|
||||
extension DownloadMapsViewController: UITableViewDataSource {
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
dataSource.numberOfSections() + (hasAddMapSection ? 1 : 0)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
if hasAddMapSection && section == dataSource.numberOfSections() {
|
||||
return 1
|
||||
}
|
||||
return dataSource.numberOfItems(in: section)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
if hasAddMapSection && indexPath.section == dataSource.numberOfSections() {
|
||||
let cellType = MWMMapDownloaderButtonTableViewCell.self
|
||||
let buttonCell = tableView.dequeueReusableCell(cell: cellType, indexPath: indexPath)
|
||||
return buttonCell
|
||||
}
|
||||
|
||||
let nodeAttrs = dataSource.item(at: indexPath)
|
||||
let cell: MWMMapDownloaderTableViewCell
|
||||
if nodeAttrs.hasChildren {
|
||||
let cellType = MWMMapDownloaderLargeCountryTableViewCell.self
|
||||
let largeCountryCell = tableView.dequeueReusableCell(cell: cellType, indexPath: indexPath)
|
||||
cell = largeCountryCell
|
||||
} else if let matchedName = dataSource.matchedName(at: indexPath), matchedName != nodeAttrs.nodeName {
|
||||
let cellType = MWMMapDownloaderSubplaceTableViewCell.self
|
||||
let subplaceCell = tableView.dequeueReusableCell(cell: cellType, indexPath: indexPath)
|
||||
subplaceCell.setSubplaceText(matchedName)
|
||||
cell = subplaceCell
|
||||
} else if !nodeAttrs.hasParent {
|
||||
let cellType = MWMMapDownloaderTableViewCell.self
|
||||
let downloaderCell = tableView.dequeueReusableCell(cell: cellType, indexPath: indexPath)
|
||||
cell = downloaderCell
|
||||
} else {
|
||||
let cellType = MWMMapDownloaderPlaceTableViewCell.self
|
||||
let placeCell = tableView.dequeueReusableCell(cell: cellType, indexPath: indexPath)
|
||||
cell = placeCell
|
||||
}
|
||||
cell.mode = dataSource.isSearching ? .available : mode
|
||||
cell.config(nodeAttrs, searchQuery: searchController.searchBar.text)
|
||||
cell.delegate = self
|
||||
return cell
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
dataSource.title(for: section)
|
||||
}
|
||||
|
||||
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
|
||||
dataSource.indexTitles()
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
|
||||
index
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
||||
if indexPath.section == dataSource.numberOfSections() {
|
||||
return false
|
||||
}
|
||||
let nodeAttrs = dataSource.item(at: indexPath)
|
||||
switch nodeAttrs.nodeStatus {
|
||||
case .onDisk, .onDiskOutOfDate, .partly:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
|
||||
if editingStyle == .delete {
|
||||
let nodeAttrs = dataSource.item(at: indexPath)
|
||||
Storage.shared().deleteNode(nodeAttrs.countryId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
|
||||
extension DownloadMapsViewController: UITableViewDelegate {
|
||||
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
let headerView = MWMMapDownloaderCellHeader()
|
||||
if section != dataSource.numberOfSections() {
|
||||
headerView.text = dataSource.title(for: section)
|
||||
}
|
||||
return headerView
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
28
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
|
||||
section == dataSource.numberOfSections() - 1 ? 68 : 0
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
if indexPath.section == dataSource.numberOfSections() {
|
||||
onAddMaps()
|
||||
return
|
||||
}
|
||||
let nodeAttrs = dataSource.item(at: indexPath)
|
||||
if nodeAttrs.hasChildren {
|
||||
showChildren(dataSource.item(at: indexPath))
|
||||
return
|
||||
}
|
||||
showActions(nodeAttrs, in: tableView.cellForRow(at: indexPath)!)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIScrollViewDelegate
|
||||
|
||||
extension DownloadMapsViewController: UIScrollViewDelegate {
|
||||
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||
searchController.searchBar.resignFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MWMMapDownloaderTableViewCellDelegate
|
||||
|
||||
extension DownloadMapsViewController: MWMMapDownloaderTableViewCellDelegate {
|
||||
func mapDownloaderCellDidPressProgress(_ cell: MWMMapDownloaderTableViewCell) {
|
||||
guard let indexPath = tableView.indexPath(for: cell) else { return }
|
||||
let nodeAttrs = dataSource.item(at: indexPath)
|
||||
switch nodeAttrs.nodeStatus {
|
||||
case .undefined, .error:
|
||||
Storage.shared().retryDownloadNode(nodeAttrs.countryId)
|
||||
case .downloading, .applying, .inQueue:
|
||||
Storage.shared().cancelDownloadNode(nodeAttrs.countryId)
|
||||
case .onDiskOutOfDate:
|
||||
Storage.shared().updateNode(nodeAttrs.countryId)
|
||||
case .onDisk:
|
||||
// do nothing
|
||||
break
|
||||
case .notDownloaded, .partly:
|
||||
if nodeAttrs.hasChildren {
|
||||
showChildren(nodeAttrs)
|
||||
} else {
|
||||
Storage.shared().downloadNode(nodeAttrs.countryId)
|
||||
}
|
||||
@unknown default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
func mapDownloaderCellDidLongPress(_ cell: MWMMapDownloaderTableViewCell) {
|
||||
guard let indexPath = tableView.indexPath(for: cell) else { return }
|
||||
let nodeAttrs = dataSource.item(at: indexPath)
|
||||
showActions(nodeAttrs, in: cell)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - StorageObserver
|
||||
|
||||
extension DownloadMapsViewController: StorageObserver {
|
||||
func processCountryEvent(_ countryId: String) {
|
||||
if skipCountryEvent && countryId == dataSource.getParentCountryId() {
|
||||
return
|
||||
}
|
||||
dataSource.reload {
|
||||
reloadData()
|
||||
noMapsContainer.isHidden = !dataSource.isEmpty || Storage.shared().downloadInProgress()
|
||||
}
|
||||
if countryId == dataSource.getParentCountryId() {
|
||||
configButtons()
|
||||
}
|
||||
|
||||
for cell in tableView.visibleCells {
|
||||
guard let downloaderCell = cell as? MWMMapDownloaderTableViewCell else { continue }
|
||||
if downloaderCell.nodeAttrs.countryId != countryId { continue }
|
||||
guard let indexPath = tableView.indexPath(for: downloaderCell) else { return }
|
||||
downloaderCell.config(dataSource.item(at: indexPath), searchQuery: searchController.searchBar.text)
|
||||
}
|
||||
}
|
||||
|
||||
func processCountry(_ countryId: String, downloadedBytes: UInt64, totalBytes: UInt64) {
|
||||
for cell in tableView.visibleCells {
|
||||
guard let downloaderCell = cell as? MWMMapDownloaderTableViewCell else { continue }
|
||||
if downloaderCell.nodeAttrs.countryId != countryId { continue }
|
||||
downloaderCell.setDownloadProgress(CGFloat(downloadedBytes) / CGFloat(totalBytes))
|
||||
}
|
||||
|
||||
if countryId == dataSource.getParentCountryId() {
|
||||
downloadAllView.downloadProgress = CGFloat(downloadedBytes) / CGFloat(totalBytes)
|
||||
downloadAllView.downloadSize = totalBytes
|
||||
} else if dataSource.isRoot && dataSource is DownloadedMapsDataSource {
|
||||
downloadAllView.state = .dowloading
|
||||
downloadAllView.isSizeHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UISearchBarDelegate
|
||||
|
||||
extension DownloadMapsViewController: UISearchBarDelegate {
|
||||
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
|
||||
searchBar.text = nil
|
||||
searchBar.resignFirstResponder()
|
||||
dataSource.cancelSearch()
|
||||
reloadData()
|
||||
noSerchResultViewController.view.isHidden = true
|
||||
}
|
||||
|
||||
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
|
||||
let locale = searchBar.textInputMode?.primaryLanguage
|
||||
dataSource.search(searchText, locale: locale ?? "") { [weak self] finished in
|
||||
guard let self = self else { return }
|
||||
self.reloadData()
|
||||
self.noSerchResultViewController.view.isHidden = !self.dataSource.isEmpty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - DownloadAllViewDelegate
|
||||
|
||||
extension DownloadMapsViewController: DownloadAllViewDelegate {
|
||||
func onStateChanged(state: DownloadAllView.State) {
|
||||
if state == .none {
|
||||
downloadAllViewContainer.isHidden = true
|
||||
tableView.contentInset = UIEdgeInsets.zero
|
||||
} else {
|
||||
downloadAllViewContainer.isHidden = false
|
||||
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: allMapsViewBottomOffsetConstant, right: 0)
|
||||
}
|
||||
}
|
||||
|
||||
func onDownloadButtonPressed() {
|
||||
skipCountryEvent = true
|
||||
let id = dataSource.getParentCountryId()
|
||||
if mode == .downloaded {
|
||||
Storage.shared().updateNode(id)
|
||||
} else {
|
||||
Storage.shared().downloadNode(id)
|
||||
}
|
||||
skipCountryEvent = false
|
||||
processCountryEvent(id)
|
||||
}
|
||||
|
||||
func onRetryButtonPressed() {
|
||||
skipCountryEvent = true
|
||||
let id = dataSource.getParentCountryId()
|
||||
Storage.shared().retryDownloadNode(id)
|
||||
skipCountryEvent = false
|
||||
processCountryEvent(id)
|
||||
}
|
||||
|
||||
func onCancelButtonPressed() {
|
||||
skipCountryEvent = true
|
||||
let id = dataSource.getParentCountryId()
|
||||
Storage.shared().cancelDownloadNode(id)
|
||||
skipCountryEvent = false
|
||||
processCountryEvent(id)
|
||||
reloadData()
|
||||
}
|
||||
}
|
||||
139
iphone/Maps/UI/Downloader/DownloadedMapsDataSource.swift
Normal file
139
iphone/Maps/UI/Downloader/DownloadedMapsDataSource.swift
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
class DownloadedMapsDataSource {
|
||||
private let parentCountryId: String?
|
||||
private var countryIds: [String]
|
||||
|
||||
fileprivate var searching = false
|
||||
fileprivate lazy var searchDataSource: IDownloaderDataSource = {
|
||||
SearchMapsDataSource()
|
||||
}()
|
||||
|
||||
init(_ parentId: String? = nil) {
|
||||
self.parentCountryId = parentId
|
||||
countryIds = DownloadedMapsDataSource.loadData(parentId)
|
||||
}
|
||||
|
||||
private class func loadData(_ parentId: String?) -> [String] {
|
||||
let countryIds: [String]
|
||||
if let parentId = parentId {
|
||||
countryIds = Storage.shared().downloadedCountries(withParent: parentId)
|
||||
} else {
|
||||
countryIds = Storage.shared().downloadedCountries()
|
||||
}
|
||||
|
||||
return countryIds.map {
|
||||
CountryIdAndName(countryId: $0, name: Storage.shared().name(forCountry: $0))
|
||||
}.sorted {
|
||||
$0.countryName.compare($1.countryName) == .orderedAscending
|
||||
}.map {
|
||||
$0.countryId
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func reloadData() {
|
||||
countryIds = DownloadedMapsDataSource.loadData(parentCountryId)
|
||||
}
|
||||
}
|
||||
|
||||
extension DownloadedMapsDataSource: IDownloaderDataSource {
|
||||
var isEmpty: Bool {
|
||||
return searching ? searchDataSource.isEmpty : countryIds.isEmpty
|
||||
}
|
||||
|
||||
var title: String {
|
||||
guard let parentCountryId = parentCountryId else {
|
||||
return L("download_maps")
|
||||
}
|
||||
return Storage.shared().name(forCountry: parentCountryId)
|
||||
}
|
||||
|
||||
var isRoot: Bool {
|
||||
parentCountryId == nil
|
||||
}
|
||||
|
||||
var isSearching: Bool {
|
||||
searching
|
||||
}
|
||||
|
||||
func getParentCountryId() -> String {
|
||||
if parentCountryId != nil {
|
||||
return parentCountryId!
|
||||
}
|
||||
return Storage.shared().getRootId()
|
||||
}
|
||||
|
||||
func parentAttributes() -> MapNodeAttributes {
|
||||
guard let parentId = parentCountryId else {
|
||||
return Storage.shared().attributesForRoot()
|
||||
}
|
||||
return Storage.shared().attributes(forCountry: parentId)
|
||||
}
|
||||
|
||||
func numberOfSections() -> Int {
|
||||
searching ? searchDataSource.numberOfSections() : 1
|
||||
}
|
||||
|
||||
func numberOfItems(in section: Int) -> Int {
|
||||
searching ? searchDataSource.numberOfItems(in: section) : countryIds.count
|
||||
}
|
||||
|
||||
func item(at indexPath: IndexPath) -> MapNodeAttributes {
|
||||
if searching {
|
||||
return searchDataSource.item(at: indexPath)
|
||||
}
|
||||
guard indexPath.section == 0 else { fatalError() }
|
||||
let countryId = countryIds[indexPath.item]
|
||||
return Storage.shared().attributes(forCountry: countryId)
|
||||
}
|
||||
|
||||
func matchedName(at indexPath: IndexPath) -> String? {
|
||||
searching ? searchDataSource.matchedName(at: indexPath) : nil
|
||||
}
|
||||
|
||||
func title(for section: Int) -> String {
|
||||
if searching {
|
||||
return searchDataSource.title(for: section)
|
||||
}
|
||||
if let parentCountryId = parentCountryId {
|
||||
let attributes = Storage.shared().attributes(forCountry: parentCountryId)
|
||||
return Storage.shared().name(forCountry: parentCountryId) + " (\(formattedSize(attributes.downloadedSize)))"
|
||||
}
|
||||
let attributes = Storage.shared().attributesForRoot()
|
||||
return L("downloader_downloaded_subtitle") + " (\(formattedSize(attributes.downloadedSize)))"
|
||||
}
|
||||
|
||||
func indexTitles() -> [String]? {
|
||||
nil
|
||||
}
|
||||
|
||||
func dataSourceFor(_ childId: String) -> IDownloaderDataSource {
|
||||
searching ? searchDataSource.dataSourceFor(childId) : DownloadedMapsDataSource(childId)
|
||||
}
|
||||
|
||||
func reload(_ completion: () -> Void) {
|
||||
if searching {
|
||||
searchDataSource.reload(completion)
|
||||
return
|
||||
}
|
||||
reloadData()
|
||||
completion()
|
||||
}
|
||||
|
||||
func search(_ query: String, locale: String, update: @escaping (Bool) -> Void) {
|
||||
if query.isEmpty {
|
||||
cancelSearch()
|
||||
update(true)
|
||||
return
|
||||
}
|
||||
searchDataSource.search(query, locale: locale) { [weak self] (finished) in
|
||||
if finished {
|
||||
self?.searching = true
|
||||
update(finished)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancelSearch() {
|
||||
searching = false
|
||||
searchDataSource.cancelSearch()
|
||||
}
|
||||
}
|
||||
18
iphone/Maps/UI/Downloader/IDownloaderDataSource.swift
Normal file
18
iphone/Maps/UI/Downloader/IDownloaderDataSource.swift
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
protocol IDownloaderDataSource {
|
||||
var isEmpty: Bool { get }
|
||||
var title: String { get }
|
||||
var isRoot: Bool { get }
|
||||
var isSearching: Bool { get }
|
||||
func getParentCountryId() -> String
|
||||
func parentAttributes() -> MapNodeAttributes
|
||||
func numberOfSections() -> Int
|
||||
func numberOfItems(in section: Int) -> Int
|
||||
func item(at indexPath: IndexPath) -> MapNodeAttributes
|
||||
func matchedName(at indexPath: IndexPath) -> String?
|
||||
func title(for section: Int) -> String
|
||||
func indexTitles() -> [String]?
|
||||
func dataSourceFor(_ childId: String) -> IDownloaderDataSource
|
||||
func reload(_ completion: () -> Void)
|
||||
func search(_ query: String, locale: String, update: @escaping (_ completed: Bool) -> Void)
|
||||
func cancelSearch()
|
||||
}
|
||||
4
iphone/Maps/UI/Downloader/MWMMapDownloaderMode.h
Normal file
4
iphone/Maps/UI/Downloader/MWMMapDownloaderMode.h
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
typedef NS_ENUM(NSUInteger, MWMMapDownloaderMode) {
|
||||
MWMMapDownloaderModeDownloaded,
|
||||
MWMMapDownloaderModeAvailable
|
||||
};
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
@objc(MWMDownloaderNoResultsEmbedViewController)
|
||||
final class DownloaderNoResultsEmbed: UINavigationController {
|
||||
|
||||
@objc(MWMDownloaderNoResultsScreen)
|
||||
enum Screen: Int {
|
||||
|
||||
case noMaps
|
||||
case noSearchResults
|
||||
}
|
||||
|
||||
@objc var screen = Screen.noMaps {
|
||||
didSet {
|
||||
let controller: MWMViewController
|
||||
switch screen {
|
||||
case .noMaps: controller = MWMNoMapsViewController.controller()
|
||||
case .noSearchResults: controller = SearchNoResultsViewController.controller
|
||||
}
|
||||
setViewControllers([controller], animated: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
3
iphone/Maps/UI/Downloader/NoMaps/MWMNoMapsView.h
Normal file
3
iphone/Maps/UI/Downloader/NoMaps/MWMNoMapsView.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
@interface MWMNoMapsView : SolidTouchView
|
||||
|
||||
@end
|
||||
81
iphone/Maps/UI/Downloader/NoMaps/MWMNoMapsView.m
Normal file
81
iphone/Maps/UI/Downloader/NoMaps/MWMNoMapsView.m
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#import "MWMNoMapsView.h"
|
||||
#import "MWMKeyboard.h"
|
||||
|
||||
@interface MWMNoMapsView ()<MWMKeyboardObserver>
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UIImageView * image;
|
||||
@property(weak, nonatomic) IBOutlet UILabel * title;
|
||||
@property(weak, nonatomic) IBOutlet UILabel * text;
|
||||
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * containerWidth;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * containerHeight;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * containerTopOffset;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * containerBottomOffset;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * imageMinHeight;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * imageHeight;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * titleImageOffset;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * titleTopOffset;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * textTopOffset;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMNoMapsView
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
if (!IPAD)
|
||||
{
|
||||
self.containerWidth.active = NO;
|
||||
self.containerHeight.active = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.containerTopOffset.active = NO;
|
||||
}
|
||||
[MWMKeyboard addObserver:self];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
[self configForSize:self.frame.size];
|
||||
}
|
||||
|
||||
- (void)configForSize:(CGSize)size
|
||||
{
|
||||
CGSize iPadSize = CGSizeMake(520, 600);
|
||||
CGSize newSize = IPAD ? iPadSize : size;
|
||||
CGFloat width = newSize.width;
|
||||
CGFloat height = newSize.height;
|
||||
BOOL hideImage = (self.imageHeight.multiplier * height <= self.imageMinHeight.constant);
|
||||
if (hideImage)
|
||||
{
|
||||
self.titleImageOffset.priority = UILayoutPriorityDefaultLow;
|
||||
self.title.hidden = self.title.minY < self.titleTopOffset.constant;
|
||||
self.text.hidden = self.text.minY < self.textTopOffset.constant;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.titleImageOffset.priority = UILayoutPriorityDefaultHigh;
|
||||
self.title.hidden = NO;
|
||||
self.text.hidden = NO;
|
||||
}
|
||||
self.image.hidden = hideImage;
|
||||
if (IPAD)
|
||||
{
|
||||
self.containerWidth.constant = width;
|
||||
self.containerHeight.constant = height;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - MWMKeyboard
|
||||
|
||||
- (void)onKeyboardAnimation
|
||||
{
|
||||
self.containerBottomOffset.constant = [MWMKeyboard keyboardHeight];
|
||||
[self.superview layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (void)onKeyboardWillAnimate { [self.superview layoutIfNeeded]; }
|
||||
@end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#import "MWMViewController.h"
|
||||
|
||||
@interface MWMNoMapsViewController : MWMViewController
|
||||
|
||||
+ (MWMNoMapsViewController *)controller NS_SWIFT_NAME(controller());
|
||||
|
||||
@end
|
||||
19
iphone/Maps/UI/Downloader/NoMaps/MWMNoMapsViewController.mm
Normal file
19
iphone/Maps/UI/Downloader/NoMaps/MWMNoMapsViewController.mm
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#import "MWMNoMapsViewController.h"
|
||||
#import "MWMMapDownloaderMode.h"
|
||||
#import "MWMMapViewControlsManager.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
@implementation MWMNoMapsViewController
|
||||
|
||||
+ (MWMNoMapsViewController *)controller
|
||||
{
|
||||
auto storyboard = [UIStoryboard instance:MWMStoryboardMain];
|
||||
return [storyboard instantiateViewControllerWithIdentifier:[self className]];
|
||||
}
|
||||
|
||||
- (IBAction)downloadMaps
|
||||
{
|
||||
[[MWMMapViewControlsManager manager] actionDownloadMaps:MWMMapDownloaderModeAvailable];
|
||||
}
|
||||
|
||||
@end
|
||||
84
iphone/Maps/UI/Downloader/SearchMapsDataSource.swift
Normal file
84
iphone/Maps/UI/Downloader/SearchMapsDataSource.swift
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
class SearchMapsDataSource {
|
||||
typealias SearchCallback = (Bool) -> Void
|
||||
|
||||
fileprivate var searchResults: [MapSearchResult] = []
|
||||
fileprivate var searchId = 0
|
||||
fileprivate var onUpdate: SearchCallback?
|
||||
}
|
||||
|
||||
extension SearchMapsDataSource: IDownloaderDataSource {
|
||||
var isEmpty: Bool {
|
||||
searchResults.isEmpty
|
||||
}
|
||||
|
||||
var title: String {
|
||||
""
|
||||
}
|
||||
|
||||
var isRoot: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
var isSearching: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
func numberOfSections() -> Int {
|
||||
1
|
||||
}
|
||||
|
||||
func getParentCountryId() -> String {
|
||||
return Storage.shared().getRootId()
|
||||
}
|
||||
|
||||
func parentAttributes() -> MapNodeAttributes {
|
||||
return Storage.shared().attributesForRoot()
|
||||
}
|
||||
|
||||
func numberOfItems(in section: Int) -> Int {
|
||||
searchResults.count
|
||||
}
|
||||
|
||||
func item(at indexPath: IndexPath) -> MapNodeAttributes {
|
||||
Storage.shared().attributes(forCountry: searchResults[indexPath.item].countryId)
|
||||
}
|
||||
|
||||
func matchedName(at indexPath: IndexPath) -> String? {
|
||||
searchResults[indexPath.item].matchedName
|
||||
}
|
||||
|
||||
func title(for section: Int) -> String {
|
||||
L("downloader_search_results")
|
||||
}
|
||||
|
||||
func indexTitles() -> [String]? {
|
||||
nil
|
||||
}
|
||||
|
||||
func dataSourceFor(_ childId: String) -> IDownloaderDataSource {
|
||||
AvailableMapsDataSource(childId)
|
||||
}
|
||||
|
||||
func reload(_ completion: () -> Void) {
|
||||
completion()
|
||||
}
|
||||
|
||||
func search(_ query: String, locale: String, update: @escaping SearchCallback) {
|
||||
searchId += 1
|
||||
onUpdate = update
|
||||
FrameworkHelper.search(inDownloader: query, inputLocale: locale) { [weak self, searchId] (results, finished) in
|
||||
if searchId != self?.searchId {
|
||||
return
|
||||
}
|
||||
self?.searchResults = results
|
||||
if results.count > 0 || finished {
|
||||
self?.onUpdate?(finished)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancelSearch() {
|
||||
searchResults = []
|
||||
onUpdate = nil
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import UIKit
|
||||
|
||||
protocol BookmarkColorViewControllerDelegate: AnyObject {
|
||||
func bookmarkColorViewController(_ viewController: BookmarkColorViewController, didSelect color: BookmarkColor)
|
||||
}
|
||||
|
||||
final class BookmarkColorViewController: MWMTableViewController {
|
||||
weak var delegate: BookmarkColorViewControllerDelegate?
|
||||
private let bookmarkColor: BookmarkColor
|
||||
|
||||
private let colors: [BookmarkColor] = BookmarkColor.allCases
|
||||
|
||||
init(bookmarkColor: BookmarkColor) {
|
||||
self.bookmarkColor = bookmarkColor
|
||||
super.init(style: .grouped)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
title = L("change_color")
|
||||
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancelButtonDidTap))
|
||||
}
|
||||
|
||||
@objc private func cancelButtonDidTap() {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
colors.count
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueDefaultCell(for: indexPath)
|
||||
let color = colors[indexPath.row]
|
||||
let selected = color == bookmarkColor
|
||||
cell.textLabel?.text = color.title
|
||||
cell.imageView?.image = color.image(selected)
|
||||
cell.accessoryType = selected ? .checkmark : .none
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let color = colors[indexPath.row]
|
||||
delegate?.bookmarkColorViewController(self, didSelect: color)
|
||||
}
|
||||
}
|
||||
22
iphone/Maps/UI/EditBookmark/BookmarkTitleCell.swift
Normal file
22
iphone/Maps/UI/EditBookmark/BookmarkTitleCell.swift
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import UIKit
|
||||
|
||||
protocol BookmarkTitleCellDelegate: AnyObject {
|
||||
func didFinishEditingTitle(_ title: String)
|
||||
}
|
||||
|
||||
final class BookmarkTitleCell: MWMTableViewCell {
|
||||
@IBOutlet var textField: UITextField!
|
||||
weak var delegate: BookmarkTitleCellDelegate?
|
||||
|
||||
func configure(name: String, delegate: BookmarkTitleCellDelegate, hint: String) {
|
||||
textField.text = name
|
||||
textField.placeholder = hint
|
||||
self.delegate = delegate
|
||||
}
|
||||
}
|
||||
|
||||
extension BookmarkTitleCell: UITextFieldDelegate {
|
||||
func textFieldDidEndEditing(_ textField: UITextField) {
|
||||
delegate?.didFinishEditingTitle(textField.text ?? "")
|
||||
}
|
||||
}
|
||||
44
iphone/Maps/UI/EditBookmark/BookmarkTitleCell.xib
Normal file
44
iphone/Maps/UI/EditBookmark/BookmarkTitleCell.xib
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="oz5-CE-QZ8" customClass="BookmarkTitleCell" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="oz5-CE-QZ8" id="v5X-p8-TtT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="Ykx-ep-qjo">
|
||||
<rect key="frame" x="20" y="12" width="280" height="20"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:blackPrimaryText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedPlaceholder" value="placepage_bookmark_name_hint"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="oz5-CE-QZ8" id="d6l-ER-j3P"/>
|
||||
</connections>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="Ykx-ep-qjo" secondAttribute="bottom" constant="12" id="cfm-oE-z2I"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Ykx-ep-qjo" secondAttribute="trailing" constant="20" id="dyn-Ae-apB"/>
|
||||
<constraint firstItem="Ykx-ep-qjo" firstAttribute="top" secondItem="v5X-p8-TtT" secondAttribute="top" constant="12" id="e37-GE-Eth"/>
|
||||
<constraint firstItem="Ykx-ep-qjo" firstAttribute="leading" secondItem="v5X-p8-TtT" secondAttribute="leading" constant="20" id="gXL-r9-dr7"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="textField" destination="Ykx-ep-qjo" id="Vsa-Aq-FNB"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="234" y="339"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
104
iphone/Maps/UI/EditBookmark/BookmarkUIUtils.swift
Normal file
104
iphone/Maps/UI/EditBookmark/BookmarkUIUtils.swift
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
import Foundation
|
||||
|
||||
extension BookmarkColor {
|
||||
public static var allCases: [BookmarkColor] = [.red, .pink, .purple, .deepPurple, .blue, .lightBlue, .cyan, .teal, .green,
|
||||
.lime, .yellow, .orange, .deepOrange, .brown, .gray, .blueGray]
|
||||
|
||||
var title: String {
|
||||
localizedTitleForBookmarkColor(self)
|
||||
}
|
||||
|
||||
var color: UIColor {
|
||||
uiColorForBookmarkColor(self)
|
||||
}
|
||||
|
||||
func image(_ selected: Bool) -> UIImage {
|
||||
if selected {
|
||||
return circleImageForColor(color, frameSize: 22, iconName: "ic_bm_none")
|
||||
} else {
|
||||
return circleImageForColor(color, frameSize: 22, diameter: 14)
|
||||
}
|
||||
}
|
||||
|
||||
func image(_ iconName: String) -> UIImage {
|
||||
circleImageForColor(color, frameSize: 22, iconName: iconName)
|
||||
}
|
||||
|
||||
static func bookmarkColor(from color: UIColor) -> BookmarkColor? {
|
||||
allCases.first(where: { uiColorForBookmarkColor($0) == color })
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func titleForBookmarkColor(_ color: BookmarkColor) -> String {
|
||||
switch color {
|
||||
case .red: return "red"
|
||||
case .blue: return "blue"
|
||||
case .purple: return "purple"
|
||||
case .yellow: return "yellow"
|
||||
case .pink: return "pink"
|
||||
case .brown: return "brown"
|
||||
case .green: return "green"
|
||||
case .orange: return "orange"
|
||||
case .deepPurple: return "deep_purple"
|
||||
case .lightBlue: return "light_blue"
|
||||
case .cyan: return "cyan"
|
||||
case .teal: return "teal"
|
||||
case .lime: return "lime"
|
||||
case .deepOrange: return "deep_orange"
|
||||
case .gray: return "gray"
|
||||
case .blueGray: return "blue_gray"
|
||||
case .none, .count: return ""
|
||||
@unknown default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func localizedTitleForBookmarkColor(_ color: BookmarkColor) -> String {
|
||||
L(titleForBookmarkColor(color))
|
||||
}
|
||||
|
||||
fileprivate func rgbColor(_ r: CGFloat, _ g: CGFloat, _ b: CGFloat) -> UIColor {
|
||||
UIColor(red: r / 255, green: g / 255, blue: b / 255, alpha: 0.8)
|
||||
}
|
||||
|
||||
fileprivate func uiColorForBookmarkColor(_ color: BookmarkColor) -> UIColor {
|
||||
switch color {
|
||||
case .red: return rgbColor(229, 27, 35);
|
||||
case .pink: return rgbColor(255, 65, 130);
|
||||
case .purple: return rgbColor(155, 36, 178);
|
||||
case .deepPurple: return rgbColor(102, 57, 191);
|
||||
case .blue: return rgbColor(0, 102, 204);
|
||||
case .lightBlue: return rgbColor(36, 156, 242);
|
||||
case .cyan: return rgbColor(20, 190, 205);
|
||||
case .teal: return rgbColor(0, 165, 140);
|
||||
case .green: return rgbColor(60, 140, 60);
|
||||
case .lime: return rgbColor(147, 191, 57);
|
||||
case .yellow: return rgbColor(255, 200, 0);
|
||||
case .orange: return rgbColor(255, 150, 0);
|
||||
case .deepOrange: return rgbColor(240, 100, 50);
|
||||
case .brown: return rgbColor(128, 70, 51);
|
||||
case .gray: return rgbColor(115, 115, 115);
|
||||
case .blueGray: return rgbColor(89, 115, 128);
|
||||
case .none, .count:
|
||||
fatalError()
|
||||
@unknown default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
func circleImageForColor(_ color: UIColor,
|
||||
frameSize: CGFloat,
|
||||
diameter: CGFloat? = nil,
|
||||
iconName: String? = nil) -> UIImage {
|
||||
let renderer = UIGraphicsImageRenderer(size: CGSize(width: frameSize, height: frameSize))
|
||||
return renderer.image { context in
|
||||
let d = diameter ?? frameSize
|
||||
let rect = CGRect(x: (frameSize - d) / 2, y: (frameSize - d) / 2, width: d, height: d)
|
||||
context.cgContext.addEllipse(in: rect)
|
||||
context.cgContext.setFillColor(color.cgColor)
|
||||
context.cgContext.fillPath()
|
||||
|
||||
guard let iconName = iconName, let image = UIImage(named: iconName) else { return }
|
||||
image.draw(in: rect.insetBy(dx: 3, dy: 3))
|
||||
}
|
||||
}
|
||||
265
iphone/Maps/UI/EditBookmark/EditBookmarkViewController.swift
Normal file
265
iphone/Maps/UI/EditBookmark/EditBookmarkViewController.swift
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
import UIKit
|
||||
|
||||
@objc(MWMEditBookmarkController)
|
||||
final class EditBookmarkViewController: MWMTableViewController {
|
||||
private enum Sections: Int {
|
||||
case info
|
||||
case description
|
||||
case delete
|
||||
case count
|
||||
}
|
||||
|
||||
private enum InfoSectionRows: Int {
|
||||
case title
|
||||
case color
|
||||
case bookmarkGroup
|
||||
case count
|
||||
}
|
||||
|
||||
private var editingCompleted: ((Bool) -> Void)?
|
||||
|
||||
private var placePageData: PlacePageData?
|
||||
|
||||
private var noteCell: MWMNoteCell?
|
||||
private var bookmarkTitle: String?
|
||||
private var bookmarkDescription: String?
|
||||
private var bookmarkGroupTitle: String?
|
||||
private var bookmarkId = FrameworkHelper.invalidBookmarkId()
|
||||
private var bookmarkGroupId = FrameworkHelper.invalidCategoryId()
|
||||
private var newBookmarkGroupId = FrameworkHelper.invalidCategoryId()
|
||||
private var bookmarkColor: BookmarkColor!
|
||||
private let bookmarksManager = BookmarksManager.shared()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
guard bookmarkId != FrameworkHelper.invalidBookmarkId() || placePageData != nil else {
|
||||
fatalError("controller should be configured with placePageData or bookmarkId first")
|
||||
}
|
||||
|
||||
title = L("bookmark").capitalized
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .save,
|
||||
target: self,
|
||||
action: #selector(onSave))
|
||||
|
||||
tableView.registerNib(cell: BookmarkTitleCell.self)
|
||||
tableView.registerNib(cell: MWMButtonCell.self)
|
||||
tableView.registerNib(cell: MWMNoteCell.self)
|
||||
|
||||
addToBookmarksManagerObserverList()
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
updateBookmarkIfNeeded()
|
||||
}
|
||||
|
||||
deinit {
|
||||
removeFromBookmarksManagerObserverList()
|
||||
}
|
||||
|
||||
func configure(with bookmarkId: MWMMarkID, editCompletion completion: ((Bool) -> Void)?) {
|
||||
self.bookmarkId = bookmarkId
|
||||
|
||||
let bookmark = bookmarksManager.bookmark(withId: bookmarkId)
|
||||
|
||||
bookmarkTitle = bookmark.bookmarkName
|
||||
bookmarkColor = bookmark.bookmarkColor
|
||||
|
||||
bookmarkDescription = bookmarksManager.description(forBookmarkId: bookmarkId)
|
||||
|
||||
let bookmarkGroup = bookmarksManager.category(forBookmarkId: bookmarkId)
|
||||
bookmarkGroupId = bookmarkGroup.categoryId
|
||||
bookmarkGroupTitle = bookmarkGroup.title
|
||||
|
||||
editingCompleted = completion
|
||||
}
|
||||
|
||||
@objc(configureWithPlacePageData:)
|
||||
func configure(with placePageData: PlacePageData) {
|
||||
guard let bookmarkData = placePageData.bookmarkData else { fatalError("placePageData and bookmarkData can't be nil") }
|
||||
self.placePageData = placePageData
|
||||
|
||||
bookmarkTitle = placePageData.previewData.title
|
||||
bookmarkDescription = bookmarkData.bookmarkDescription
|
||||
bookmarkGroupTitle = bookmarkData.bookmarkCategory
|
||||
bookmarkId = bookmarkData.bookmarkId
|
||||
bookmarkGroupId = bookmarkData.bookmarkGroupId
|
||||
bookmarkColor = bookmarkData.color
|
||||
|
||||
editingCompleted = nil
|
||||
}
|
||||
|
||||
// MARK: - Table view data source
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
Sections.count.rawValue
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
switch Sections(rawValue: section) {
|
||||
case .info:
|
||||
return InfoSectionRows.count.rawValue
|
||||
case .description, .delete:
|
||||
return 1
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
switch Sections(rawValue: indexPath.section) {
|
||||
case .info:
|
||||
switch InfoSectionRows(rawValue: indexPath.row) {
|
||||
case .title:
|
||||
let cell = tableView.dequeueReusableCell(cell: BookmarkTitleCell.self, indexPath: indexPath)
|
||||
cell.configure(name: bookmarkTitle ?? "", delegate: self, hint: L("placepage_bookmark_name_hint"))
|
||||
return cell
|
||||
case .color:
|
||||
let cell = tableView.dequeueDefaultCell(for: indexPath)
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
cell.textLabel?.text = bookmarkColor.title
|
||||
cell.imageView?.image = circleImageForColor(bookmarkColor.color, frameSize: 28, diameter: 22, iconName: "ic_bm_none")
|
||||
return cell
|
||||
case .bookmarkGroup:
|
||||
let cell = tableView.dequeueDefaultCell(for: indexPath)
|
||||
cell.textLabel?.text = bookmarkGroupTitle
|
||||
cell.imageView?.image = UIImage(named: "ic_folder")
|
||||
cell.imageView?.setStyle(.black)
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
return cell;
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
case .description:
|
||||
if let noteCell = noteCell {
|
||||
return noteCell
|
||||
} else {
|
||||
let cell = tableView.dequeueReusableCell(cell: MWMNoteCell.self, indexPath: indexPath)
|
||||
cell.config(with: self, noteText: bookmarkDescription ?? "", placeholder: L("placepage_personal_notes_hint"))
|
||||
noteCell = cell
|
||||
return cell
|
||||
}
|
||||
case .delete:
|
||||
let cell = tableView.dequeueReusableCell(cell: MWMButtonCell.self, indexPath: indexPath)
|
||||
cell.configure(with: self, title: L("placepage_delete_bookmark_button"), enabled: true)
|
||||
return cell
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
switch InfoSectionRows(rawValue: indexPath.row) {
|
||||
case .color:
|
||||
openColorPicker()
|
||||
case .bookmarkGroup:
|
||||
openGroupPicker()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func updateBookmarkIfNeeded() {
|
||||
// Skip for the regular place page.
|
||||
guard bookmarkId != FrameworkHelper.invalidBookmarkId() else { return }
|
||||
// TODO: Update the bookmark content on the Edit screen instead of closing it when the bookmark gets updated from cloud.
|
||||
if !bookmarksManager.hasBookmark(bookmarkId) {
|
||||
goBack()
|
||||
}
|
||||
}
|
||||
|
||||
private func addToBookmarksManagerObserverList() {
|
||||
bookmarksManager.add(self)
|
||||
}
|
||||
|
||||
private func removeFromBookmarksManagerObserverList() {
|
||||
bookmarksManager.remove(self)
|
||||
}
|
||||
|
||||
@objc private func onSave() {
|
||||
view.endEditing(true)
|
||||
|
||||
BookmarksManager.shared().updateBookmark(bookmarkId,
|
||||
setGroupId: bookmarkGroupId,
|
||||
title: bookmarkTitle ?? "",
|
||||
color: bookmarkColor,
|
||||
description: bookmarkDescription ?? "")
|
||||
if let placePageData = placePageData {
|
||||
FrameworkHelper.updatePlacePageData()
|
||||
placePageData.updateBookmarkStatus()
|
||||
}
|
||||
editingCompleted?(true)
|
||||
goBack()
|
||||
}
|
||||
|
||||
@objc private func openColorPicker() {
|
||||
ColorPicker.shared.present(from: self, pickerType: .bookmarkColorPicker(bookmarkColor), completionHandler: { [weak self] color in
|
||||
self?.bookmarkColor = BookmarkColor.bookmarkColor(from: color)
|
||||
self?.tableView.reloadRows(at: [IndexPath(row: InfoSectionRows.color.rawValue, section: Sections.info.rawValue)], with: .none)
|
||||
})
|
||||
}
|
||||
|
||||
private func openGroupPicker() {
|
||||
let groupViewController = SelectBookmarkGroupViewController(groupName: bookmarkGroupTitle ?? "", groupId: bookmarkGroupId)
|
||||
let navigationController = UINavigationController(rootViewController: groupViewController)
|
||||
groupViewController.delegate = self
|
||||
present(navigationController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension EditBookmarkViewController: BookmarkTitleCellDelegate {
|
||||
func didFinishEditingTitle(_ title: String) {
|
||||
bookmarkTitle = title
|
||||
}
|
||||
}
|
||||
|
||||
extension EditBookmarkViewController: MWMNoteCellDelegate {
|
||||
func cell(_ cell: MWMNoteCell, didChangeSizeAndText text: String) {
|
||||
UIView.setAnimationsEnabled(false)
|
||||
tableView.refresh()
|
||||
UIView.setAnimationsEnabled(true)
|
||||
}
|
||||
|
||||
func cell(_ cell: MWMNoteCell, didFinishEditingWithText text: String) {
|
||||
bookmarkDescription = text
|
||||
}
|
||||
}
|
||||
|
||||
extension EditBookmarkViewController: MWMButtonCellDelegate {
|
||||
func cellDidPressButton(_ cell: UITableViewCell) {
|
||||
BookmarksManager.shared().deleteBookmark(bookmarkId)
|
||||
if let placePageData = placePageData {
|
||||
FrameworkHelper.updateAfterDeleteBookmark()
|
||||
placePageData.updateBookmarkStatus()
|
||||
}
|
||||
goBack()
|
||||
}
|
||||
}
|
||||
|
||||
extension EditBookmarkViewController: SelectBookmarkGroupViewControllerDelegate {
|
||||
func bookmarkGroupViewController(_ viewController: SelectBookmarkGroupViewController,
|
||||
didSelect groupTitle: String,
|
||||
groupId: MWMMarkGroupID) {
|
||||
viewController.dismiss(animated: true)
|
||||
bookmarkGroupTitle = groupTitle
|
||||
bookmarkGroupId = groupId
|
||||
tableView.reloadRows(at: [IndexPath(row: InfoSectionRows.bookmarkGroup.rawValue, section: Sections.info.rawValue)], with: .none)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - BookmarksObserver
|
||||
extension EditBookmarkViewController: BookmarksObserver {
|
||||
func onBookmarksLoadFinished() {
|
||||
updateBookmarkIfNeeded()
|
||||
}
|
||||
|
||||
func onBookmarksCategoryDeleted(_ groupId: MWMMarkGroupID) {
|
||||
if bookmarkGroupId == groupId {
|
||||
goBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
236
iphone/Maps/UI/EditBookmark/EditTrackViewController.swift
Normal file
236
iphone/Maps/UI/EditBookmark/EditTrackViewController.swift
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
import UIKit
|
||||
|
||||
final class EditTrackViewController: MWMTableViewController {
|
||||
private enum Sections: Int {
|
||||
case info
|
||||
case delete
|
||||
case count
|
||||
}
|
||||
|
||||
private enum InfoSectionRows: Int {
|
||||
case title
|
||||
case color
|
||||
//case lineWidth // TODO: possible new section & ability - edit track line width
|
||||
case bookmarkGroup
|
||||
case count
|
||||
}
|
||||
|
||||
private var editingCompleted: (Bool) -> Void
|
||||
|
||||
private var placePageData: PlacePageData?
|
||||
private let trackId: MWMTrackID
|
||||
private var trackTitle: String?
|
||||
private var trackGroupTitle: String?
|
||||
private var trackGroupId = FrameworkHelper.invalidCategoryId()
|
||||
private var trackColor: UIColor
|
||||
|
||||
private let bookmarksManager = BookmarksManager.shared()
|
||||
|
||||
@objc
|
||||
init(trackId: MWMTrackID, editCompletion completion: @escaping (Bool) -> Void) {
|
||||
self.trackId = trackId
|
||||
|
||||
let track = bookmarksManager.track(withId: trackId)
|
||||
self.trackTitle = track.trackName
|
||||
self.trackColor = track.trackColor
|
||||
|
||||
let category = bookmarksManager.category(forTrackId: trackId)
|
||||
self.trackGroupId = category.categoryId
|
||||
self.trackGroupTitle = category.title
|
||||
|
||||
self.editingCompleted = completion
|
||||
super.init(style: .grouped)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
updateTrackIfNeeded()
|
||||
}
|
||||
|
||||
deinit {
|
||||
removeFromBookmarksManagerObserverList()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
title = L("track_title")
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .save,
|
||||
target: self,
|
||||
action: #selector(onSave))
|
||||
|
||||
tableView.registerNib(cell: BookmarkTitleCell.self)
|
||||
tableView.registerNib(cell: MWMButtonCell.self)
|
||||
|
||||
addToBookmarksManagerObserverList()
|
||||
}
|
||||
|
||||
// MARK: - Table view data source
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
Sections.count.rawValue
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
switch Sections(rawValue: section) {
|
||||
case .info:
|
||||
return InfoSectionRows.count.rawValue
|
||||
case .delete:
|
||||
return 1
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
switch Sections(rawValue: indexPath.section) {
|
||||
case .info:
|
||||
switch InfoSectionRows(rawValue: indexPath.row) {
|
||||
case .title:
|
||||
let cell = tableView.dequeueReusableCell(cell: BookmarkTitleCell.self, indexPath: indexPath)
|
||||
cell.configure(name: trackTitle ?? "", delegate: self, hint: L("placepage_track_name_hint"))
|
||||
return cell
|
||||
case .color:
|
||||
let cell = tableView.dequeueDefaultCell(for: indexPath)
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
cell.textLabel?.text = L("change_color")
|
||||
cell.imageView?.image = circleImageForColor(trackColor, frameSize: 28, diameter: 22)
|
||||
return cell
|
||||
case .bookmarkGroup:
|
||||
let cell = tableView.dequeueDefaultCell(for: indexPath)
|
||||
cell.textLabel?.text = trackGroupTitle
|
||||
cell.imageView?.image = UIImage(named: "ic_folder")
|
||||
cell.imageView?.setStyle(.black)
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
return cell;
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
case .delete:
|
||||
let cell = tableView.dequeueReusableCell(cell: MWMButtonCell.self, indexPath: indexPath)
|
||||
cell.configure(with: self, title: L("placepage_delete_track_button"), enabled: true)
|
||||
return cell
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
switch InfoSectionRows(rawValue: indexPath.row) {
|
||||
case .color:
|
||||
openColorPicker()
|
||||
case .bookmarkGroup:
|
||||
openGroupPicker()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func updateTrackIfNeeded() {
|
||||
// TODO: Update the track content on the Edit screen instead of closing it when the track gets updated from cloud.
|
||||
if !bookmarksManager.hasTrack(trackId) {
|
||||
goBack()
|
||||
}
|
||||
}
|
||||
|
||||
private func addToBookmarksManagerObserverList() {
|
||||
bookmarksManager.add(self)
|
||||
}
|
||||
|
||||
private func removeFromBookmarksManagerObserverList() {
|
||||
bookmarksManager.remove(self)
|
||||
}
|
||||
|
||||
@objc private func onSave() {
|
||||
view.endEditing(true)
|
||||
BookmarksManager.shared().updateTrack(trackId, setGroupId: trackGroupId, color: trackColor, title: trackTitle ?? "")
|
||||
editingCompleted(true)
|
||||
goBack()
|
||||
}
|
||||
|
||||
private func updateColor(_ color: UIColor) {
|
||||
trackColor = color
|
||||
tableView.reloadRows(at: [IndexPath(row: InfoSectionRows.color.rawValue, section: Sections.info.rawValue)],
|
||||
with: .none)
|
||||
}
|
||||
|
||||
@objc private func openColorPicker() {
|
||||
ColorPicker.shared.present(from: self, pickerType: .defaultColorPicker(trackColor), completionHandler: { [weak self] color in
|
||||
self?.updateColor(color)
|
||||
})
|
||||
}
|
||||
|
||||
private func openGroupPicker() {
|
||||
let groupViewController = SelectBookmarkGroupViewController(groupName: trackGroupTitle ?? "", groupId: trackGroupId)
|
||||
groupViewController.delegate = self
|
||||
let navigationController = UINavigationController(rootViewController: groupViewController)
|
||||
present(navigationController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension EditTrackViewController: BookmarkTitleCellDelegate {
|
||||
func didFinishEditingTitle(_ title: String) {
|
||||
trackTitle = title
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MWMButtonCellDelegate
|
||||
|
||||
extension EditTrackViewController: MWMButtonCellDelegate {
|
||||
func cellDidPressButton(_ cell: UITableViewCell) {
|
||||
guard let indexPath = tableView.indexPath(for: cell) else {
|
||||
fatalError("Invalid cell")
|
||||
}
|
||||
switch Sections(rawValue: indexPath.section) {
|
||||
case .info:
|
||||
break
|
||||
case .delete:
|
||||
bookmarksManager.deleteTrack(trackId)
|
||||
goBack()
|
||||
default:
|
||||
fatalError("Invalid section")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - BookmarkColorViewControllerDelegate
|
||||
|
||||
extension EditTrackViewController: BookmarkColorViewControllerDelegate {
|
||||
func bookmarkColorViewController(_ viewController: BookmarkColorViewController, didSelect bookmarkColor: BookmarkColor) {
|
||||
viewController.dismiss(animated: true)
|
||||
updateColor(bookmarkColor.color)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SelectBookmarkGroupViewControllerDelegate
|
||||
extension EditTrackViewController: SelectBookmarkGroupViewControllerDelegate {
|
||||
func bookmarkGroupViewController(_ viewController: SelectBookmarkGroupViewController,
|
||||
didSelect groupTitle: String,
|
||||
groupId: MWMMarkGroupID) {
|
||||
viewController.dismiss(animated: true)
|
||||
trackGroupTitle = groupTitle
|
||||
trackGroupId = groupId
|
||||
tableView.reloadRows(at: [IndexPath(row: InfoSectionRows.bookmarkGroup.rawValue, section: Sections.info.rawValue)],
|
||||
with: .none)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - BookmarksObserver
|
||||
extension EditTrackViewController: BookmarksObserver {
|
||||
func onBookmarksLoadFinished() {
|
||||
updateTrackIfNeeded()
|
||||
}
|
||||
|
||||
func onBookmarksCategoryDeleted(_ groupId: MWMMarkGroupID) {
|
||||
if trackGroupId == groupId {
|
||||
goBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
import UIKit
|
||||
|
||||
protocol SelectBookmarkGroupViewControllerDelegate: AnyObject {
|
||||
func bookmarkGroupViewController(_ viewController: SelectBookmarkGroupViewController,
|
||||
didSelect groupTitle: String,
|
||||
groupId: MWMMarkGroupID)
|
||||
}
|
||||
|
||||
final class SelectBookmarkGroupViewController: MWMTableViewController {
|
||||
private enum Sections: Int {
|
||||
case addGroup
|
||||
case groups
|
||||
case count
|
||||
}
|
||||
|
||||
weak var delegate: SelectBookmarkGroupViewControllerDelegate?
|
||||
private let groupName: String
|
||||
private let groupId: MWMMarkGroupID
|
||||
private let bookmarkGroups = BookmarksManager.shared().sortedUserCategories()
|
||||
|
||||
init(groupName: String, groupId: MWMMarkGroupID) {
|
||||
self.groupName = groupName
|
||||
self.groupId = groupId
|
||||
super.init(style: .grouped)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
title = L("bookmark_sets");
|
||||
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancelButtonDidTap))
|
||||
}
|
||||
|
||||
@objc private func cancelButtonDidTap() {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
Sections.count.rawValue
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
switch Sections(rawValue: section) {
|
||||
case .addGroup:
|
||||
return 1
|
||||
case .groups:
|
||||
return bookmarkGroups.count
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueDefaultCell(for: indexPath)
|
||||
switch Sections(rawValue: indexPath.section) {
|
||||
case .addGroup:
|
||||
cell.textLabel?.text = L("add_new_set")
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
case .groups:
|
||||
let bookmarkGroup = bookmarkGroups[indexPath.row]
|
||||
cell.textLabel?.text = bookmarkGroup.title
|
||||
cell.accessoryType = bookmarkGroup.categoryId == groupId ? .checkmark : .none
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
switch Sections(rawValue: indexPath.section) {
|
||||
case .addGroup:
|
||||
createNewGroup()
|
||||
case .groups:
|
||||
let selectedGroup = bookmarkGroups[indexPath.row]
|
||||
delegate?.bookmarkGroupViewController(self, didSelect: selectedGroup.title, groupId: selectedGroup.categoryId)
|
||||
default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
private func createNewGroup() {
|
||||
alertController.presentCreateBookmarkCategoryAlert(withMaxCharacterNum: 60, minCharacterNum: 0) {
|
||||
[unowned self] name -> Bool in
|
||||
guard BookmarksManager.shared().checkCategoryName(name) else { return false }
|
||||
let newGroupId = BookmarksManager.shared().createCategory(withName: name)
|
||||
self.delegate?.bookmarkGroupViewController(self, didSelect: name, groupId: newGroupId)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
39
iphone/Maps/UI/EditBookmark/legacy_bookmark_colors.h
Normal file
39
iphone/Maps/UI/EditBookmark/legacy_bookmark_colors.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#import "CircleView.h"
|
||||
|
||||
#include "kml/types.hpp"
|
||||
|
||||
namespace ios_bookmark_ui_helper
|
||||
{
|
||||
inline UIColor * UIColorForRGB(int red, int green, int blue)
|
||||
{
|
||||
return [UIColor colorWithRed:red/255.f green:green/255.f blue:blue/255.f alpha:0.8];
|
||||
}
|
||||
|
||||
inline UIColor * UIColorForBookmarkColor(kml::PredefinedColor color)
|
||||
{
|
||||
auto const dpColor = kml::ColorFromPredefinedColor(color);
|
||||
return UIColorForRGB(dpColor.red, dpColor.green, dpColor.blue);
|
||||
}
|
||||
|
||||
inline UIImage * ImageForBookmark(kml::PredefinedColor color, kml::BookmarkIcon icon)
|
||||
{
|
||||
CGFloat const kPinDiameter = 22;
|
||||
|
||||
NSString *imageName = [NSString stringWithFormat:@"%@%@", @"ic_bm_", [@(kml::ToString(icon).c_str()) lowercaseString]];
|
||||
|
||||
return [CircleView createCircleImageWithDiameter:kPinDiameter
|
||||
andColor:UIColorForBookmarkColor(color)
|
||||
andImageName:imageName];
|
||||
}
|
||||
|
||||
inline UIImage * ImageForTrack(float red, float green, float blue)
|
||||
{
|
||||
CGFloat const kPinDiameter = 22;
|
||||
return [CircleView createCircleImageWithDiameter:kPinDiameter
|
||||
andColor:[UIColor colorWithRed:red
|
||||
green:green
|
||||
blue:blue
|
||||
alpha:1.f]];
|
||||
}
|
||||
} // namespace ios_bookmark_ui_helper
|
||||
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#import "MWMTableViewController.h"
|
||||
|
||||
#include "indexer/editable_map_object.hpp"
|
||||
|
||||
@protocol MWMEditorAdditionalNamesProtocol <NSObject>
|
||||
|
||||
- (void)addAdditionalName:(NSInteger)languageIndex;
|
||||
|
||||
@end
|
||||
|
||||
@interface MWMEditorAdditionalNamesTableViewController : MWMTableViewController
|
||||
|
||||
- (void)configWithDelegate:(id<MWMEditorAdditionalNamesProtocol>)delegate
|
||||
name:(StringUtf8Multilang const &)name
|
||||
additionalSkipLanguageCodes:(std::vector<NSInteger>)additionalSkipLanguageCodes;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
#import "MWMEditorAdditionalNamesTableViewController.h"
|
||||
#import "MWMTableViewCell.h"
|
||||
|
||||
#import <CoreApi/StringUtils.h>
|
||||
|
||||
@interface MWMEditorAdditionalNamesTableViewController ()
|
||||
|
||||
@property (weak, nonatomic) id<MWMEditorAdditionalNamesProtocol> delegate;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMEditorAdditionalNamesTableViewController
|
||||
{
|
||||
StringUtf8Multilang m_name;
|
||||
std::vector<StringUtf8Multilang::Lang> m_languages;
|
||||
std::vector<NSInteger> m_additionalSkipLanguageCodes;
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
|
||||
- (void)configWithDelegate:(id<MWMEditorAdditionalNamesProtocol>)delegate
|
||||
name:(StringUtf8Multilang const &)name
|
||||
additionalSkipLanguageCodes:(std::vector<NSInteger>)additionalSkipLanguageCodes
|
||||
{
|
||||
self.delegate = delegate;
|
||||
m_name = name;
|
||||
m_additionalSkipLanguageCodes = additionalSkipLanguageCodes;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.title = L(@"choose_language");
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
auto const getIndex = [](std::string_view lang) { return StringUtf8Multilang::GetLangIndex(lang); };
|
||||
StringUtf8Multilang::Languages const & supportedLanguages = StringUtf8Multilang::GetSupportedLanguages();
|
||||
m_languages.clear();
|
||||
|
||||
auto constexpr kDefaultCode = StringUtf8Multilang::kDefaultCode;
|
||||
for (auto const & language : supportedLanguages)
|
||||
{
|
||||
auto const langIndex = getIndex(language.m_code);
|
||||
if (langIndex != kDefaultCode && m_name.HasString(langIndex))
|
||||
continue;
|
||||
auto it = std::find(m_additionalSkipLanguageCodes.begin(), m_additionalSkipLanguageCodes.end(), langIndex);
|
||||
if (it == m_additionalSkipLanguageCodes.end())
|
||||
m_languages.push_back(language);
|
||||
}
|
||||
|
||||
std::sort(m_languages.begin(), m_languages.end(),
|
||||
[&getIndex](StringUtf8Multilang::Lang const & lhs, StringUtf8Multilang::Lang const & rhs) {
|
||||
// Default name can be changed in advanced mode, but it should be last in list of names.
|
||||
if (getIndex(lhs.m_code) == kDefaultCode && getIndex(rhs.m_code) != kDefaultCode)
|
||||
return false;
|
||||
if (getIndex(lhs.m_code) != kDefaultCode && getIndex(rhs.m_code) == kDefaultCode)
|
||||
return true;
|
||||
|
||||
return std::string(lhs.m_code) < std::string(rhs.m_code);
|
||||
});
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
MWMTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"ListCellIdentifier"];
|
||||
NSInteger const index = indexPath.row;
|
||||
StringUtf8Multilang::Lang const & lang = m_languages[index];
|
||||
cell.textLabel.text = ToNSString(lang.m_name);
|
||||
cell.detailTextLabel.text = ToNSString(lang.m_code);
|
||||
cell.accessoryType = UITableViewCellAccessoryNone;
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
return m_languages.size();
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
NSInteger const index = indexPath.row;
|
||||
StringUtf8Multilang::Lang const & language = m_languages[index];
|
||||
|
||||
[self.delegate addAdditionalName:StringUtf8Multilang::GetLangIndex(language.m_code)];
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@objc(MWMEditorAdditionalNamePlaceholderTableViewCell)
|
||||
final class EditorAdditionalNamePlaceholderTableViewCell: MWMTableViewCell {
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" rowHeight="16" id="7Br-fC-W0o" customClass="MWMEditorAdditionalNamePlaceholderTableViewCell" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="7Br-fC-W0o" id="58c-Uv-d0K">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tYH-si-HMY">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="16"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" priority="750" constant="16" id="A96-CL-Yji"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="tYH-si-HMY" firstAttribute="leading" secondItem="58c-Uv-d0K" secondAttribute="leading" id="GTY-XA-kVU"/>
|
||||
<constraint firstAttribute="trailing" secondItem="tYH-si-HMY" secondAttribute="trailing" id="VOL-Tl-l3w"/>
|
||||
<constraint firstAttribute="bottom" secondItem="tYH-si-HMY" secondAttribute="bottom" id="g0D-w9-dGP"/>
|
||||
<constraint firstItem="tYH-si-HMY" firstAttribute="top" secondItem="58c-Uv-d0K" secondAttribute="top" id="qyd-oG-Ixi"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<point key="canvasLocation" x="225" y="282"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
17
iphone/Maps/UI/Editor/Cells/MWMButtonCell.h
Normal file
17
iphone/Maps/UI/Editor/Cells/MWMButtonCell.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#import "MWMTableViewCell.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol MWMButtonCellDelegate <NSObject>
|
||||
|
||||
- (void)cellDidPressButton:(UITableViewCell *)cell;
|
||||
|
||||
@end
|
||||
|
||||
@interface MWMButtonCell : MWMTableViewCell
|
||||
|
||||
- (void)configureWithDelegate:(id<MWMButtonCellDelegate>)delegate title:(NSString *)title enabled:(BOOL)enabled;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
22
iphone/Maps/UI/Editor/Cells/MWMButtonCell.m
Normal file
22
iphone/Maps/UI/Editor/Cells/MWMButtonCell.m
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#import "MWMButtonCell.h"
|
||||
|
||||
@interface MWMButtonCell ()
|
||||
|
||||
@property(nonatomic) IBOutlet UIButton *button;
|
||||
@property(weak, nonatomic) id<MWMButtonCellDelegate> delegate;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMButtonCell
|
||||
|
||||
- (void)configureWithDelegate:(id<MWMButtonCellDelegate>)delegate title:(NSString *)title enabled:(BOOL)enabled {
|
||||
[self.button setTitle:title forState:UIControlStateNormal];
|
||||
self.button.enabled = enabled;
|
||||
self.delegate = delegate;
|
||||
}
|
||||
|
||||
- (IBAction)buttonTap {
|
||||
[self.delegate cellDidPressButton:self];
|
||||
}
|
||||
|
||||
@end
|
||||
48
iphone/Maps/UI/Editor/Cells/MWMButtonCell.xib
Normal file
48
iphone/Maps/UI/Editor/Cells/MWMButtonCell.xib
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="KGk-i7-Jjw" customClass="MWMButtonCell" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="c7Y-Nr-P4C">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="44" id="Z6u-ES-sTJ"/>
|
||||
</constraints>
|
||||
<state key="normal" title="Button">
|
||||
<color key="titleColor" red="0.95686274510000002" green="0.26274509800000001" blue="0.21176470589999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="BookmarksCategoryDeleteButton"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="buttonTap" destination="KGk-i7-Jjw" eventType="touchUpInside" id="kS2-aj-wfq"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="c7Y-Nr-P4C" secondAttribute="trailing" id="AVP-GR-W7u"/>
|
||||
<constraint firstAttribute="bottom" secondItem="c7Y-Nr-P4C" secondAttribute="bottom" id="F3N-Zb-tnQ"/>
|
||||
<constraint firstItem="c7Y-Nr-P4C" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" id="JQx-63-Ane"/>
|
||||
<constraint firstItem="c7Y-Nr-P4C" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="fTj-1o-vWJ"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="button" destination="c7Y-Nr-P4C" id="UDx-n5-6fr"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="139" y="155"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#import "MWMEditorCommon.h"
|
||||
#import "MWMTableViewCell.h"
|
||||
|
||||
@interface MWMEditorAddAdditionalNameTableViewCell : MWMTableViewCell
|
||||
|
||||
- (void)configWithDelegate:(id<MWMEditorAdditionalName>)delegate;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
#import "MWMEditorAddAdditionalNameTableViewCell.h"
|
||||
|
||||
@interface MWMEditorAddAdditionalNameTableViewCell ()
|
||||
|
||||
@property(weak, nonatomic) id<MWMEditorAdditionalName> delegate;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMEditorAddAdditionalNameTableViewCell
|
||||
|
||||
- (void)configWithDelegate:(id<MWMEditorAdditionalName>)delegate { self.delegate = delegate; }
|
||||
- (IBAction)addLanguageTap { [self.delegate editAdditionalNameLanguage:NSNotFound]; }
|
||||
@end
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="81H-Jz-Sl2" customClass="MWMEditorAddAdditionalNameTableViewCell" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="81H-Jz-Sl2" id="EHS-hP-aIE">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ua5-4V-1PH" customClass="MWMButton">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" priority="750" constant="44" id="oVm-ep-LLL"/>
|
||||
</constraints>
|
||||
<inset key="contentEdgeInsets" minX="12" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
<inset key="titleEdgeInsets" minX="14" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
<state key="normal" title="add_language" image="plus.circle.fill" catalog="system"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="FlatNormalTransButtonBig"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="add_language"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="addLanguageTap" destination="81H-Jz-Sl2" eventType="touchUpInside" id="Lw7-kU-hIK"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="Ua5-4V-1PH" secondAttribute="bottom" id="Nmp-Cj-ffg"/>
|
||||
<constraint firstItem="Ua5-4V-1PH" firstAttribute="leading" secondItem="EHS-hP-aIE" secondAttribute="leading" id="XP5-8s-Vka"/>
|
||||
<constraint firstItem="Ua5-4V-1PH" firstAttribute="top" secondItem="EHS-hP-aIE" secondAttribute="top" id="YAU-Xb-h9Y"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Ua5-4V-1PH" secondAttribute="trailing" id="f6J-6o-AjX"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCellContentView>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<point key="canvasLocation" x="342" y="257"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="plus.circle.fill" catalog="system" width="128" height="123"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#import "MWMEditorCommon.h"
|
||||
#import "MWMTableViewCell.h"
|
||||
|
||||
@interface MWMEditorAdditionalNameTableViewCell : MWMTableViewCell
|
||||
|
||||
@property(nonatomic, readonly) NSInteger code;
|
||||
|
||||
- (void)configWithDelegate:(id<MWMEditorAdditionalName>)delegate
|
||||
langCode:(NSInteger)langCode
|
||||
langName:(NSString *)langName
|
||||
name:(NSString *)name
|
||||
errorMessage:(NSString *)errorMessage
|
||||
isValid:(BOOL)isValid
|
||||
keyboardType:(UIKeyboardType)keyboardType;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
#import "MWMEditorAdditionalNameTableViewCell.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
static CGFloat const kErrorLabelHeight = 16;
|
||||
|
||||
@interface MWMEditorAdditionalNameTableViewCell ()
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UIStackView * stackView;
|
||||
@property(weak, nonatomic) IBOutlet UILabel * languageLabel;
|
||||
@property(weak, nonatomic) IBOutlet UITextField * textField;
|
||||
@property(weak, nonatomic) IBOutlet UILabel * errorLabel;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * errorLabelHeight;
|
||||
|
||||
@property(nonatomic, readwrite) NSInteger code;
|
||||
|
||||
@property(weak, nonatomic) id<MWMEditorAdditionalName> delegate;
|
||||
|
||||
@property(nonatomic) BOOL isValid;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMEditorAdditionalNameTableViewCell
|
||||
|
||||
- (void)configWithDelegate:(id<MWMEditorAdditionalName>)delegate
|
||||
langCode:(NSInteger)langCode
|
||||
langName:(NSString *)langName
|
||||
name:(NSString *)name
|
||||
errorMessage:(NSString *)errorMessage
|
||||
isValid:(BOOL)isValid
|
||||
keyboardType:(UIKeyboardType)keyboardType
|
||||
{
|
||||
self.delegate = delegate;
|
||||
self.code = langCode;
|
||||
self.languageLabel.text = langName;
|
||||
self.errorLabel.text = errorMessage;
|
||||
self.isValid = isValid;
|
||||
self.textField.text = name;
|
||||
self.textField.keyboardType = keyboardType;
|
||||
self.textField.backgroundColor = [UIColor clearColor];
|
||||
[self processValidation];
|
||||
}
|
||||
|
||||
- (void)processValidation
|
||||
{
|
||||
if (self.isValid)
|
||||
{
|
||||
self.errorLabelHeight.constant = 0;
|
||||
[self.contentView setStyleNameAndApply: @"Background"];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.errorLabelHeight.constant = kErrorLabelHeight;
|
||||
[self.contentView setStyleNameAndApply: @"ErrorBackground"];
|
||||
}
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (void)changeInvalidCellState
|
||||
{
|
||||
if (self.isValid)
|
||||
return;
|
||||
self.isValid = YES;
|
||||
[self processValidation];
|
||||
[self.delegate tryToChangeInvalidStateForCell:self];
|
||||
}
|
||||
|
||||
#pragma mark - UITextFieldDelegate
|
||||
|
||||
- (BOOL)textField:(UITextField *)textField
|
||||
shouldChangeCharactersInRange:(NSRange)range
|
||||
replacementString:(NSString *)string
|
||||
{
|
||||
[self changeInvalidCellState];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)textFieldShouldClear:(UITextField *)textField
|
||||
{
|
||||
[self changeInvalidCellState];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)textFieldDidEndEditing:(UITextField *)textField
|
||||
{
|
||||
[self.delegate cell:self changedText:textField.text];
|
||||
}
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField
|
||||
{
|
||||
[textField resignFirstResponder];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
// Allow to tap on the whole cell to start editing.
|
||||
UIView * view = [super hitTest:point withEvent:event];
|
||||
if (view == self.stackView)
|
||||
return self.textField;
|
||||
return view;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="hfo-cP-AGX" customClass="MWMEditorAdditionalNameTableViewCell">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="hfo-cP-AGX" id="JQH-ks-NoC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillProportionally" alignment="top" spacing="3" translatesAutoresizingMaskIntoConstraints="NO" id="eca-XW-QZR">
|
||||
<rect key="frame" x="16" y="16" width="288" height="12"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="zh-classical" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="efb-nS-cjm">
|
||||
<rect key="frame" x="0.0" y="0.0" width="77" height="5"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" horizontalCompressionResistancePriority="499" contentHorizontalAlignment="left" contentVerticalAlignment="center" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="9BY-PA-dlA">
|
||||
<rect key="frame" x="0.0" y="8" width="5" height="4"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<textInputTraits key="textInputTraits"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:blackPrimaryText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedPlaceholder" value="editor_edit_place_name_hint"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="hfo-cP-AGX" id="jkD-0x-Ods"/>
|
||||
</connections>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="9BY-PA-dlA" firstAttribute="width" secondItem="eca-XW-QZR" secondAttribute="width" id="OSr-03-hPe"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="252" translatesAutoresizingMaskIntoConstraints="NO" id="SZa-Bj-se1">
|
||||
<rect key="frame" x="60" y="32" width="252" height="0.0"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" priority="999" id="CPF-uE-pzx"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" red="0.95686274510000002" green="0.26274509800000001" blue="0.21176470589999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular12:redText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="SZa-Bj-se1" firstAttribute="leading" secondItem="JQH-ks-NoC" secondAttribute="leading" constant="60" id="BcS-gb-9vl"/>
|
||||
<constraint firstAttribute="trailing" secondItem="SZa-Bj-se1" secondAttribute="trailing" constant="8" id="EBU-G3-tgp"/>
|
||||
<constraint firstItem="eca-XW-QZR" firstAttribute="top" secondItem="JQH-ks-NoC" secondAttribute="top" constant="16" id="FJ5-Zj-wLR"/>
|
||||
<constraint firstAttribute="trailing" secondItem="eca-XW-QZR" secondAttribute="trailing" constant="16" id="NZO-C9-ZCS"/>
|
||||
<constraint firstItem="SZa-Bj-se1" firstAttribute="top" secondItem="eca-XW-QZR" secondAttribute="bottom" constant="4" id="T9Z-G5-LVc"/>
|
||||
<constraint firstAttribute="bottom" secondItem="SZa-Bj-se1" secondAttribute="bottom" constant="12" id="TdX-jx-CpD"/>
|
||||
<constraint firstItem="eca-XW-QZR" firstAttribute="leading" secondItem="JQH-ks-NoC" secondAttribute="leading" constant="16" id="dbb-bG-8uT"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCellContentView>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="errorLabel" destination="SZa-Bj-se1" id="hqN-vX-0Hw"/>
|
||||
<outlet property="errorLabelHeight" destination="CPF-uE-pzx" id="X4g-6X-W0e"/>
|
||||
<outlet property="languageLabel" destination="efb-nS-cjm" id="13M-9D-Qbf"/>
|
||||
<outlet property="stackView" destination="eca-XW-QZR" id="RPt-fc-aoR"/>
|
||||
<outlet property="textField" destination="9BY-PA-dlA" id="SCv-JL-5TL"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="340.57971014492756" y="256.47321428571428"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
11
iphone/Maps/UI/Editor/Cells/MWMEditorCategoryCell.h
Normal file
11
iphone/Maps/UI/Editor/Cells/MWMEditorCategoryCell.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#import "MWMTableViewCell.h"
|
||||
|
||||
@protocol MWMEditorCellProtocol;
|
||||
|
||||
@interface MWMEditorCategoryCell : MWMTableViewCell
|
||||
|
||||
- (void)configureWithDelegate:(id<MWMEditorCellProtocol>)delegate
|
||||
detailTitle:(NSString *)detail
|
||||
isCreating:(BOOL)isCreating;
|
||||
|
||||
@end
|
||||
44
iphone/Maps/UI/Editor/Cells/MWMEditorCategoryCell.m
Normal file
44
iphone/Maps/UI/Editor/Cells/MWMEditorCategoryCell.m
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#import "MWMEditorCategoryCell.h"
|
||||
#import "MWMEditorCommon.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
CGFloat const kDetailShortRightSpace = 16;
|
||||
|
||||
@interface MWMEditorCategoryCell ()
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UIImageView * accessoryIcon;
|
||||
@property(weak, nonatomic) IBOutlet UILabel * detail;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * detailRightSpace;
|
||||
@property(weak, nonatomic) id<MWMEditorCellProtocol> delegate;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMEditorCategoryCell
|
||||
|
||||
- (void)configureWithDelegate:(id<MWMEditorCellProtocol>)delegate
|
||||
detailTitle:(NSString *)detail
|
||||
isCreating:(BOOL)isCreating
|
||||
{
|
||||
self.delegate = delegate;
|
||||
self.detail.text = detail;
|
||||
self.accessoryIcon.hidden = !isCreating;
|
||||
if (isCreating)
|
||||
{
|
||||
self.selectedBackgroundView = [[UIView alloc] init];
|
||||
self.selectedBackgroundView.styleName = @"PressBackground";
|
||||
}
|
||||
else
|
||||
{
|
||||
self.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
self.detailRightSpace.constant = kDetailShortRightSpace;
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)cellTap
|
||||
{
|
||||
if (self.accessoryIcon.hidden)
|
||||
return;
|
||||
[self.delegate cellDidPressButton:self];
|
||||
}
|
||||
|
||||
@end
|
||||
92
iphone/Maps/UI/Editor/Cells/MWMEditorCategoryCell.xib
Normal file
92
iphone/Maps/UI/Editor/Cells/MWMEditorCategoryCell.xib
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="rsc-tP-qGq" customClass="MWMEditorCategoryCell" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="rsc-tP-qGq" id="mWc-I7-4kZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_arrow_gray_right" translatesAutoresizingMaskIntoConstraints="NO" id="uA3-ru-Ja1">
|
||||
<rect key="frame" x="286" y="8" width="28" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="28" id="CW0-pF-saL"/>
|
||||
<constraint firstAttribute="height" constant="28" id="P6z-Sg-ZyD"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMGray"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Jfx-Kc-WOc">
|
||||
<rect key="frame" x="16" y="12" width="226" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="50" id="BaE-rI-CN2"/>
|
||||
<constraint firstAttribute="height" constant="20" id="oLW-um-5dA"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:blackPrimaryText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="editor_edit_place_category_title"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="501" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qrd-hP-nIJ">
|
||||
<rect key="frame" x="250" y="11.5" width="42" height="21"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="21" id="hsO-dV-WsY"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="icU-WX-U52">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<connections>
|
||||
<action selector="cellTap" destination="rsc-tP-qGq" eventType="touchUpInside" id="dru-SF-L7z"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="Jfx-Kc-WOc" firstAttribute="leading" secondItem="mWc-I7-4kZ" secondAttribute="leading" constant="16" id="5lg-6C-NQt"/>
|
||||
<constraint firstItem="qrd-hP-nIJ" firstAttribute="leading" secondItem="Jfx-Kc-WOc" secondAttribute="trailing" constant="8" id="ETn-mX-ysj"/>
|
||||
<constraint firstItem="Jfx-Kc-WOc" firstAttribute="centerY" secondItem="mWc-I7-4kZ" secondAttribute="centerY" id="P6u-UL-5f7"/>
|
||||
<constraint firstAttribute="trailing" secondItem="qrd-hP-nIJ" secondAttribute="trailing" constant="28" id="Rwj-G9-EoG"/>
|
||||
<constraint firstAttribute="trailing" secondItem="uA3-ru-Ja1" secondAttribute="trailing" constant="6" id="byM-mn-aQu"/>
|
||||
<constraint firstItem="uA3-ru-Ja1" firstAttribute="centerY" secondItem="mWc-I7-4kZ" secondAttribute="centerY" id="sVc-Kt-8ZX"/>
|
||||
<constraint firstItem="qrd-hP-nIJ" firstAttribute="centerY" secondItem="mWc-I7-4kZ" secondAttribute="centerY" id="wSH-Lz-TEL"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<constraints>
|
||||
<constraint firstItem="icU-WX-U52" firstAttribute="centerX" secondItem="rsc-tP-qGq" secondAttribute="centerX" id="F9k-Ej-LFc"/>
|
||||
<constraint firstItem="icU-WX-U52" firstAttribute="width" secondItem="rsc-tP-qGq" secondAttribute="width" id="ZUi-Rh-jlL"/>
|
||||
<constraint firstItem="icU-WX-U52" firstAttribute="height" secondItem="rsc-tP-qGq" secondAttribute="height" id="dy8-tF-aiq"/>
|
||||
<constraint firstItem="icU-WX-U52" firstAttribute="centerY" secondItem="rsc-tP-qGq" secondAttribute="centerY" id="uky-CX-2ye"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="accessoryIcon" destination="uA3-ru-Ja1" id="esf-Xi-gLG"/>
|
||||
<outlet property="detail" destination="qrd-hP-nIJ" id="IOE-vz-HaI"/>
|
||||
<outlet property="detailRightSpace" destination="Rwj-G9-EoG" id="H8f-mf-vOJ"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="501.44927536231887" y="199.55357142857142"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="ic_arrow_gray_right" width="28" height="28"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#import "MWMEditorCommon.h"
|
||||
#import "MWMTableViewCell.h"
|
||||
#include "indexer/yes_no_unknown.hpp"
|
||||
|
||||
@interface MWMEditorSegmentedTableViewCell : MWMTableViewCell
|
||||
|
||||
- (void)configWithDelegate:(id<MWMEditorCellProtocol>)delegate
|
||||
icon:(UIImage *)icon
|
||||
text:(NSString *)text
|
||||
value:(YesNoUnknown)value;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
#import "MWMEditorSegmentedTableViewCell.hpp"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
@interface MWMEditorSegmentedTableViewCell ()
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UIImageView * icon;
|
||||
@property(weak, nonatomic) IBOutlet UILabel * label;
|
||||
@property(weak, nonatomic) IBOutlet UISegmentedControl * segmentedControl;
|
||||
|
||||
@property(weak, nonatomic) id<MWMEditorCellProtocol> delegate;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMEditorSegmentedTableViewCell
|
||||
|
||||
- (void)configWithDelegate:(id<MWMEditorCellProtocol>)delegate
|
||||
icon:(UIImage *)icon
|
||||
text:(NSString *)text
|
||||
value:(YesNoUnknown)value
|
||||
{
|
||||
self.delegate = delegate;
|
||||
self.icon.image = icon;
|
||||
self.icon.styleName = @"MWMBlack";
|
||||
self.label.text = text;
|
||||
|
||||
[self.segmentedControl setTitle:NSLocalizedString(@"no", nil) forSegmentAtIndex:0];
|
||||
[self.segmentedControl setTitle:NSLocalizedString(@"yes", nil) forSegmentAtIndex:2];
|
||||
|
||||
switch(value)
|
||||
{
|
||||
case Yes:
|
||||
self.segmentedControl.selectedSegmentIndex = 2;
|
||||
break;
|
||||
case No:
|
||||
self.segmentedControl.selectedSegmentIndex = 0;
|
||||
break;
|
||||
case Unknown:
|
||||
self.segmentedControl.selectedSegmentIndex = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
[self setTextColorWithSegmentedValue:value];
|
||||
}
|
||||
|
||||
- (void)setTextColorWithSegmentedValue:(YesNoUnknown)value
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case Yes:
|
||||
case No:
|
||||
self.label.textColor = [UIColor blackPrimaryText];
|
||||
break;
|
||||
case Unknown:
|
||||
self.label.textColor = [UIColor blackHintText];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)valueChanged
|
||||
{
|
||||
YesNoUnknown value;
|
||||
switch (self.segmentedControl.selectedSegmentIndex)
|
||||
{
|
||||
case 0:
|
||||
value = No;
|
||||
break;
|
||||
case 1:
|
||||
value = Unknown;
|
||||
break;
|
||||
case 2:
|
||||
value = Yes;
|
||||
break;
|
||||
default:
|
||||
value = Unknown;
|
||||
NSAssert(false, @"Unexpected YesNoUnknown value %ld", static_cast<long>(self.segmentedControl.selectedSegmentIndex));
|
||||
}
|
||||
|
||||
[self.delegate cell:self changeSegmented:value];
|
||||
[self setTextColorWithSegmentedValue:value];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="qme-9J-aMf" customClass="MWMEditorSegmentedTableViewCell" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="qme-9J-aMf" id="fpo-VO-awT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="j3u-fF-pG6">
|
||||
<rect key="frame" x="16" y="8" width="28" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="28" id="Ce3-kg-QsX"/>
|
||||
<constraint firstAttribute="width" constant="28" id="MT3-eQ-SOL"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMBlack"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="187" translatesAutoresizingMaskIntoConstraints="NO" id="QFf-wR-pPw">
|
||||
<rect key="frame" x="60" y="0.0" width="133" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" priority="750" constant="44" id="XYk-fv-D9R"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<segmentedControl opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="left" contentVerticalAlignment="top" apportionsSegmentWidthsByContent="YES" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="Rp8-YJ-AzV">
|
||||
<rect key="frame" x="201" y="6.5" width="103" height="32"/>
|
||||
<segments>
|
||||
<segment title="No"/>
|
||||
<segment title="-"/>
|
||||
<segment title="Yes"/>
|
||||
</segments>
|
||||
<connections>
|
||||
<action selector="valueChanged" destination="qme-9J-aMf" eventType="valueChanged" id="jR4-p8-Ldo"/>
|
||||
</connections>
|
||||
</segmentedControl>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="QFf-wR-pPw" firstAttribute="top" secondItem="fpo-VO-awT" secondAttribute="top" id="3mH-Ua-EUl"/>
|
||||
<constraint firstItem="j3u-fF-pG6" firstAttribute="centerY" secondItem="fpo-VO-awT" secondAttribute="centerY" id="4xr-PJ-Dl2"/>
|
||||
<constraint firstItem="j3u-fF-pG6" firstAttribute="leading" secondItem="fpo-VO-awT" secondAttribute="leading" constant="16" id="TFM-QG-P2e"/>
|
||||
<constraint firstAttribute="bottom" secondItem="QFf-wR-pPw" secondAttribute="bottom" id="YAg-Wx-DLo"/>
|
||||
<constraint firstItem="Rp8-YJ-AzV" firstAttribute="centerY" secondItem="fpo-VO-awT" secondAttribute="centerY" id="aTy-ab-vvF"/>
|
||||
<constraint firstItem="Rp8-YJ-AzV" firstAttribute="leading" secondItem="QFf-wR-pPw" secondAttribute="trailing" constant="8" id="ge8-tr-S4S"/>
|
||||
<constraint firstItem="QFf-wR-pPw" firstAttribute="leading" secondItem="fpo-VO-awT" secondAttribute="leading" constant="60" id="mNR-IV-cek"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Rp8-YJ-AzV" secondAttribute="trailing" constant="16" id="z6B-PV-rXg"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCellContentView>
|
||||
<inset key="separatorInset" minX="60" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="icon" destination="j3u-fF-pG6" id="nIz-cY-MCW"/>
|
||||
<outlet property="label" destination="QFf-wR-pPw" id="1MO-Vf-bt6"/>
|
||||
<outlet property="segmentedControl" destination="Rp8-YJ-AzV" id="C9T-O3-dMM"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="137.59999999999999" y="154.72263868065968"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
11
iphone/Maps/UI/Editor/Cells/MWMEditorSelectTableViewCell.h
Normal file
11
iphone/Maps/UI/Editor/Cells/MWMEditorSelectTableViewCell.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#import "MWMEditorCommon.h"
|
||||
#import "MWMTableViewCell.h"
|
||||
|
||||
@interface MWMEditorSelectTableViewCell : MWMTableViewCell
|
||||
|
||||
- (void)configWithDelegate:(id<MWMEditorCellProtocol>)delegate
|
||||
icon:(UIImage *)icon
|
||||
text:(NSString *)text
|
||||
placeholder:(NSString *)placeholder;
|
||||
|
||||
@end
|
||||
46
iphone/Maps/UI/Editor/Cells/MWMEditorSelectTableViewCell.m
Normal file
46
iphone/Maps/UI/Editor/Cells/MWMEditorSelectTableViewCell.m
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#import "MWMEditorSelectTableViewCell.h"
|
||||
#import <CoreApi/MWMCommon.h>
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
@interface MWMEditorSelectTableViewCell ()
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UIImageView * icon;
|
||||
@property(weak, nonatomic) IBOutlet UILabel * label;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * labelLeadingOffset;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * labelTrailingOffset;
|
||||
@property(weak, nonatomic) IBOutlet UIImageView * grayArrow;
|
||||
|
||||
@property(weak, nonatomic) id<MWMEditorCellProtocol> delegate;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMEditorSelectTableViewCell
|
||||
|
||||
- (void)configWithDelegate:(id<MWMEditorCellProtocol>)delegate
|
||||
icon:(UIImage *)icon
|
||||
text:(NSString *)text
|
||||
placeholder:(NSString *)placeholder
|
||||
{
|
||||
self.delegate = delegate;
|
||||
self.icon.hidden = NO;
|
||||
self.icon.image = icon;
|
||||
self.icon.styleName = @"MWMBlack";
|
||||
if (text && text.length != 0)
|
||||
{
|
||||
self.label.text = text;
|
||||
[self.label setStyleNameAndApply: @"blackPrimaryText"];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.label.text = placeholder;
|
||||
[self.label setStyleNameAndApply: @"blackHintText"];
|
||||
}
|
||||
self.label.preferredMaxLayoutWidth =
|
||||
self.width - self.labelLeadingOffset.constant - self.labelTrailingOffset.constant;
|
||||
|
||||
if (isInterfaceRightToLeft())
|
||||
self.grayArrow.transform = CGAffineTransformMakeScale(-1, 1);
|
||||
}
|
||||
|
||||
- (IBAction)selectAction { [self.delegate cellDidPressButton:self]; }
|
||||
@end
|
||||
93
iphone/Maps/UI/Editor/Cells/MWMEditorSelectTableViewCell.xib
Normal file
93
iphone/Maps/UI/Editor/Cells/MWMEditorSelectTableViewCell.xib
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="xiI-ev-aU2" customClass="MWMEditorSelectTableViewCell" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="xiI-ev-aU2" id="V1p-K9-KlE">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="xur-tJ-HA9">
|
||||
<rect key="frame" x="16" y="8" width="28" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="28" id="Oak-eT-K2q"/>
|
||||
<constraint firstAttribute="height" constant="28" id="s7g-CH-05Z"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMBlack"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="220" translatesAutoresizingMaskIntoConstraints="NO" id="MBe-6r-IsA">
|
||||
<rect key="frame" x="60" y="8" width="220" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" priority="750" constant="28" id="7cv-Wq-vWC"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_arrow_gray_right" translatesAutoresizingMaskIntoConstraints="NO" id="jLw-GA-z3y">
|
||||
<rect key="frame" x="286" y="8" width="28" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="28" id="Ki5-4h-NRK"/>
|
||||
<constraint firstAttribute="height" constant="28" id="PG8-LW-72y"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMGray"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</imageView>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nq9-KC-Ujh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<connections>
|
||||
<action selector="selectAction" destination="xiI-ev-aU2" eventType="touchUpInside" id="xXk-7q-LQQ"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="jLw-GA-z3y" secondAttribute="trailing" constant="6" id="3Bg-20-t7E"/>
|
||||
<constraint firstAttribute="trailing" secondItem="nq9-KC-Ujh" secondAttribute="trailing" id="5gd-06-ZOC"/>
|
||||
<constraint firstItem="MBe-6r-IsA" firstAttribute="top" secondItem="V1p-K9-KlE" secondAttribute="top" constant="8" id="6FB-in-VCz"/>
|
||||
<constraint firstAttribute="bottom" secondItem="MBe-6r-IsA" secondAttribute="bottom" constant="8" id="CG0-ar-vx9"/>
|
||||
<constraint firstItem="xur-tJ-HA9" firstAttribute="leading" secondItem="V1p-K9-KlE" secondAttribute="leading" constant="16" id="DlR-yh-2JK"/>
|
||||
<constraint firstItem="MBe-6r-IsA" firstAttribute="leading" secondItem="V1p-K9-KlE" secondAttribute="leading" constant="60" id="RGA-XL-gLq"/>
|
||||
<constraint firstItem="xur-tJ-HA9" firstAttribute="centerY" secondItem="V1p-K9-KlE" secondAttribute="centerY" id="agx-Kf-DpK"/>
|
||||
<constraint firstAttribute="bottom" secondItem="nq9-KC-Ujh" secondAttribute="bottom" id="fK8-AO-JKa"/>
|
||||
<constraint firstItem="nq9-KC-Ujh" firstAttribute="top" secondItem="V1p-K9-KlE" secondAttribute="top" id="ibI-hy-19F"/>
|
||||
<constraint firstItem="jLw-GA-z3y" firstAttribute="centerY" secondItem="V1p-K9-KlE" secondAttribute="centerY" id="nY0-7m-bcZ"/>
|
||||
<constraint firstItem="nq9-KC-Ujh" firstAttribute="leading" secondItem="V1p-K9-KlE" secondAttribute="leading" id="tyN-aq-pC1"/>
|
||||
<constraint firstAttribute="trailing" secondItem="MBe-6r-IsA" secondAttribute="trailing" constant="40" id="yG7-RB-5Pa"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCellContentView>
|
||||
<inset key="separatorInset" minX="60" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="grayArrow" destination="jLw-GA-z3y" id="5NK-K5-4IO"/>
|
||||
<outlet property="icon" destination="xur-tJ-HA9" id="ObZ-fc-tbA"/>
|
||||
<outlet property="label" destination="MBe-6r-IsA" id="mvf-Q3-vMz"/>
|
||||
<outlet property="labelLeadingOffset" destination="RGA-XL-gLq" id="Tb4-Ew-iDe"/>
|
||||
<outlet property="labelTrailingOffset" destination="yG7-RB-5Pa" id="NqK-3D-zMQ"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="139" y="155"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="ic_arrow_gray_right" width="28" height="28"/>
|
||||
</resources>
|
||||
</document>
|
||||
11
iphone/Maps/UI/Editor/Cells/MWMEditorSwitchTableViewCell.h
Normal file
11
iphone/Maps/UI/Editor/Cells/MWMEditorSwitchTableViewCell.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#import "MWMEditorCommon.h"
|
||||
#import "MWMTableViewCell.h"
|
||||
|
||||
@interface MWMEditorSwitchTableViewCell : MWMTableViewCell
|
||||
|
||||
- (void)configWithDelegate:(id<MWMEditorCellProtocol>)delegate
|
||||
icon:(UIImage *)icon
|
||||
text:(NSString *)text
|
||||
on:(BOOL)on;
|
||||
|
||||
@end
|
||||
41
iphone/Maps/UI/Editor/Cells/MWMEditorSwitchTableViewCell.m
Normal file
41
iphone/Maps/UI/Editor/Cells/MWMEditorSwitchTableViewCell.m
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#import "MWMEditorSwitchTableViewCell.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
@interface MWMEditorSwitchTableViewCell ()
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UIImageView * icon;
|
||||
@property(weak, nonatomic) IBOutlet UILabel * label;
|
||||
@property(weak, nonatomic) IBOutlet UISwitch * switchControl;
|
||||
|
||||
@property(weak, nonatomic) id<MWMEditorCellProtocol> delegate;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMEditorSwitchTableViewCell
|
||||
|
||||
- (void)configWithDelegate:(id<MWMEditorCellProtocol>)delegate
|
||||
icon:(UIImage *)icon
|
||||
text:(NSString *)text
|
||||
on:(BOOL)on
|
||||
{
|
||||
self.delegate = delegate;
|
||||
self.icon.image = icon;
|
||||
self.icon.styleName = @"MWMBlack";
|
||||
self.label.text = text;
|
||||
self.switchControl.on = on;
|
||||
[self setTextColorWithSwitchValue:on];
|
||||
}
|
||||
|
||||
- (void)setTextColorWithSwitchValue:(BOOL)value
|
||||
{
|
||||
self.label.textColor = value ? [UIColor blackPrimaryText] : [UIColor blackHintText];
|
||||
}
|
||||
|
||||
- (IBAction)valueChanged
|
||||
{
|
||||
BOOL const value = self.switchControl.on;
|
||||
[self.delegate cell:self changeSwitch:value];
|
||||
[self setTextColorWithSwitchValue:value];
|
||||
}
|
||||
|
||||
@end
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue