Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -0,0 +1,33 @@
class ActivityIndicator: UIView {
let spinner = UIImageView(image: UIImage(named: "ic_24px_spinner"))
override var intrinsicContentSize: CGSize {
return CGSize(width: 24, height: 24)
}
init() {
super.init(frame: CGRect(x: 0, y: 0, width: 24, height: 24))
setup()
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
addSubview(spinner)
NSLayoutConstraint.activate([
spinner.topAnchor.constraint(equalTo: topAnchor),
spinner.leftAnchor.constraint(equalTo: leftAnchor),
spinner.bottomAnchor.constraint(equalTo: bottomAnchor),
spinner.rightAnchor.constraint(equalTo: rightAnchor)
])
startRotation()
}
}

View file

@ -0,0 +1,40 @@
#import "MWMButton.h"
#import "MWMCircularProgressState.h"
#import "UIImageView+Coloring.h"
NS_ASSUME_NONNULL_BEGIN
@class MWMCircularProgress;
typedef NSArray<NSNumber *> *MWMCircularProgressStateVec;
@protocol MWMCircularProgressProtocol<NSObject>
- (void)progressButtonPressed:(MWMCircularProgress *)progress;
@end
@interface MWMCircularProgress: NSObject<CAAnimationDelegate>
+ (instancetype)downloaderProgressForParentView:(UIView *)parentView;
@property(nonatomic) CGFloat progress;
@property(nonatomic) MWMCircularProgressState state;
@property(weak, nonatomic, nullable) id<MWMCircularProgressProtocol> delegate;
- (void)setSpinnerColoring:(MWMImageColoring)coloring;
- (void)setSpinnerBackgroundColor:(UIColor *)backgroundColor;
- (void)setInvertColor:(BOOL)invertColor;
- (void)setCancelButtonHidden;
- (nonnull instancetype)init __attribute__((unavailable("init is not available")));
- (nonnull instancetype)initWithParentView:(UIView *)parentView;
- (void)setImageName:(nullable NSString *)imageName
forStates:(MWMCircularProgressStateVec)states;
- (void)setColor:(UIColor *)color forStates:(MWMCircularProgressStateVec)states;
- (void)setColoring:(MWMButtonColoring)coloring
forStates:(MWMCircularProgressStateVec)states;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,152 @@
#import "MWMCircularProgress.h"
#import "MWMCircularProgressView.h"
@interface MWMCircularProgressView ()
@property(nonatomic) BOOL suspendRefreshProgress;
@end
@interface MWMCircularProgress ()
@property(nonatomic) IBOutlet MWMCircularProgressView * rootView;
@property(nonatomic) NSNumber * nextProgressToAnimate;
@end
@implementation MWMCircularProgress
+ (nonnull instancetype)downloaderProgressForParentView:(nonnull UIView *)parentView
{
MWMCircularProgress * progress = [[self alloc] initWithParentView:parentView];
progress.rootView.suspendRefreshProgress = YES;
[progress setImageName:@"ic_download"
forStates:@[@(MWMCircularProgressStateNormal), @(MWMCircularProgressStateSelected)]];
[progress setImageName:@"ic_close_spinner"
forStates:@[@(MWMCircularProgressStateProgress), @(MWMCircularProgressStateSpinner)]];
[progress setImageName:@"ic_download_error" forStates:@[@(MWMCircularProgressStateFailed)]];
[progress setImageName:@"ic_check" forStates:@[@(MWMCircularProgressStateCompleted)]];
[progress setColoring:MWMButtonColoringBlack
forStates:@[@(MWMCircularProgressStateNormal), @(MWMCircularProgressStateSelected),
@(MWMCircularProgressStateProgress), @(MWMCircularProgressStateSpinner)]];
[progress setColoring:MWMButtonColoringOther forStates:@[@(MWMCircularProgressStateFailed)]];
[progress setColoring:MWMButtonColoringBlue forStates:@[@(MWMCircularProgressStateCompleted)]];
progress.rootView.suspendRefreshProgress = NO;
return progress;
}
- (nonnull instancetype)initWithParentView:(nonnull UIView *)parentView
{
self = [super init];
if (self)
{
[[UINib nibWithNibName:NSStringFromClass([self class]) bundle:nil] instantiateWithOwner:self options:nil];
[parentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
[parentView addSubview:self.rootView];
self.rootView.translatesAutoresizingMaskIntoConstraints = NO;
[self.rootView.topAnchor constraintEqualToAnchor:parentView.topAnchor].active = YES;
[self.rootView.bottomAnchor constraintEqualToAnchor:parentView.bottomAnchor].active = YES;
[self.rootView.leadingAnchor constraintEqualToAnchor:parentView.leadingAnchor].active = YES;
[self.rootView.trailingAnchor constraintEqualToAnchor:parentView.trailingAnchor].active = YES;
self.state = MWMCircularProgressStateNormal;
}
return self;
}
- (void)dealloc { [self.rootView removeFromSuperview]; }
- (void)reset
{
_progress = 0.;
[self.rootView updatePath:0.];
self.nextProgressToAnimate = nil;
}
- (void)setSpinnerColoring:(MWMImageColoring)coloring
{
[self.rootView setSpinnerColoring:coloring];
}
- (void)setSpinnerBackgroundColor:(nonnull UIColor *)backgroundColor
{
[self.rootView setSpinnerBackgroundColor:backgroundColor];
}
- (void)setImageName:(nullable NSString *)imageName
forStates:(MWMCircularProgressStateVec)states
{
for (NSNumber *state in states)
[self.rootView setImageName:imageName forState:(MWMCircularProgressState)state.integerValue];
}
- (void)setColor:(UIColor *)color forStates:(MWMCircularProgressStateVec)states
{
for (NSNumber *state in states)
[self.rootView setColor:color forState:(MWMCircularProgressState)state.integerValue];
}
- (void)setColoring:(MWMButtonColoring)coloring
forStates:(MWMCircularProgressStateVec)states
{
for (NSNumber *state in states)
[self.rootView setColoring:coloring forState:(MWMCircularProgressState)state.integerValue];
}
- (void)setInvertColor:(BOOL)invertColor { self.rootView.isInvertColor = invertColor; }
#pragma mark - Animation
- (void)animationDidStop:(CABasicAnimation *)anim finished:(BOOL)flag
{
if (self.nextProgressToAnimate)
{
self.progress = self.nextProgressToAnimate.doubleValue;
self.nextProgressToAnimate = nil;
}
}
#pragma mark - Actions
- (void)setCancelButtonHidden
{
self.rootView.buttonView.hidden = YES;
}
- (IBAction)buttonTouchUpInside:(UIButton *)sender { [self.delegate progressButtonPressed:self]; }
#pragma mark - Properties
- (void)setProgress:(CGFloat)progress
{
if (progress < _progress || progress <= 0)
{
self.state = MWMCircularProgressStateSpinner;
return;
}
self.rootView.state = MWMCircularProgressStateProgress;
if (self.rootView.animating)
{
if (progress > self.nextProgressToAnimate.floatValue)
self.nextProgressToAnimate = @(progress);
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.rootView animateFromValue:self->_progress toValue:progress];
self->_progress = progress;
});
}
}
- (void)setState:(MWMCircularProgressState)state
{
dispatch_async(dispatch_get_main_queue(), ^{
[self reset];
self.rootView.state = state;
});
}
- (MWMCircularProgressState)state { return self.rootView.state; }
@end

View file

@ -0,0 +1,64 @@
<?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" useSafeAreas="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="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="MWMCircularProgress">
<connections>
<outlet property="rootView" destination="2DE-Qh-89K" id="1DR-Lj-Wv9"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="2DE-Qh-89K" customClass="MWMCircularProgressView" propertyAccessControl="none">
<rect key="frame" x="0.0" y="0.0" width="32" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="viF-Ee-7h0" customClass="MWMButton">
<rect key="frame" x="0.0" y="0.0" width="32" height="32"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<state key="normal">
<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="MWMBlack"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="buttonTouchUpInside:" destination="-1" eventType="touchUpInside" id="aPG-4e-uSw"/>
</connections>
</button>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="aMt-XV-9UK" userLabel="Spinner">
<rect key="frame" x="0.0" y="0.0" width="32" height="32"/>
</imageView>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="viF-Ee-7h0" firstAttribute="top" secondItem="2DE-Qh-89K" secondAttribute="top" id="3gS-24-Olj"/>
<constraint firstItem="aMt-XV-9UK" firstAttribute="width" relation="lessThanOrEqual" secondItem="vnA-dQ-QpI" secondAttribute="width" id="Bxj-4M-UlX"/>
<constraint firstItem="aMt-XV-9UK" firstAttribute="centerX" secondItem="vnA-dQ-QpI" secondAttribute="centerX" id="EJm-Be-grG"/>
<constraint firstItem="aMt-XV-9UK" firstAttribute="leading" secondItem="2DE-Qh-89K" secondAttribute="leading" priority="100" id="JK6-QX-JcI"/>
<constraint firstAttribute="bottom" secondItem="aMt-XV-9UK" secondAttribute="bottom" priority="100" id="VFI-Yh-q1q"/>
<constraint firstItem="aMt-XV-9UK" firstAttribute="centerY" secondItem="vnA-dQ-QpI" secondAttribute="centerY" id="eVP-mP-bGc"/>
<constraint firstItem="aMt-XV-9UK" firstAttribute="height" relation="lessThanOrEqual" secondItem="vnA-dQ-QpI" secondAttribute="height" id="eWY-kL-sQD"/>
<constraint firstAttribute="trailing" secondItem="viF-Ee-7h0" secondAttribute="trailing" id="fQT-G3-rJF"/>
<constraint firstItem="aMt-XV-9UK" firstAttribute="top" secondItem="2DE-Qh-89K" secondAttribute="top" priority="100" id="fbs-bw-DIs"/>
<constraint firstAttribute="bottom" secondItem="viF-Ee-7h0" secondAttribute="bottom" id="j4s-7V-lg9"/>
<constraint firstAttribute="trailing" secondItem="aMt-XV-9UK" secondAttribute="trailing" priority="100" id="k01-wU-25n"/>
<constraint firstItem="viF-Ee-7h0" firstAttribute="leading" secondItem="2DE-Qh-89K" secondAttribute="leading" id="sHS-iu-Url"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<viewLayoutGuide key="safeArea" id="vnA-dQ-QpI"/>
<connections>
<outlet property="button" destination="viF-Ee-7h0" id="0zP-Ye-sRs"/>
<outlet property="owner" destination="-1" id="0n7-PI-5tO"/>
<outlet property="spinner" destination="aMt-XV-9UK" id="bmp-bV-h3C"/>
</connections>
<point key="canvasLocation" x="139" y="155"/>
</view>
</objects>
</document>

View file

@ -0,0 +1,8 @@
typedef NS_ENUM(NSInteger, MWMCircularProgressState) {
MWMCircularProgressStateNormal,
MWMCircularProgressStateSelected,
MWMCircularProgressStateProgress,
MWMCircularProgressStateSpinner,
MWMCircularProgressStateFailed,
MWMCircularProgressStateCompleted
};

View file

@ -0,0 +1,27 @@
#import "MWMButton.h"
#import "MWMCircularProgress.h"
@interface MWMCircularProgressView : UIView
@property(nonatomic, readonly) BOOL animating;
@property(nonatomic) MWMCircularProgressState state;
@property(nonatomic) BOOL isInvertColor;
- (nonnull instancetype)initWithFrame:(CGRect)frame
__attribute__((unavailable("initWithFrame is not available")));
- (nonnull instancetype)init __attribute__((unavailable("init is not available")));
- (void)setSpinnerColoring:(MWMImageColoring)coloring;
- (void)setSpinnerBackgroundColor:(nonnull UIColor *)backgroundColor;
- (void)setImageName:(nullable NSString *)imageName forState:(MWMCircularProgressState)state;
- (void)setColor:(nonnull UIColor *)color forState:(MWMCircularProgressState)state;
- (void)setColoring:(MWMButtonColoring)coloring forState:(MWMCircularProgressState)state;
- (void)animateFromValue:(CGFloat)fromValue toValue:(CGFloat)toValue;
- (void)updatePath:(CGFloat)progress;
- (UIView * _Nullable)buttonView;
@end

View file

@ -0,0 +1,261 @@
#import "MWMCircularProgressView.h"
#import "SwiftBridge.h"
#import "UIImageView+Coloring.h"
static CGFloat const kLineWidth = 2.0;
static NSString * const kAnimationKey = @"CircleAnimation";
static CGFloat angleWithProgress(CGFloat progress) { return 2.0 * M_PI * progress - M_PI_2; }
@interface MWMCircularProgressView ()
@property(nonatomic) CAShapeLayer * backgroundLayer;
@property(nonatomic) CAShapeLayer * progressLayer;
@property(nonatomic) UIColor * spinnerBackgroundColor;
@property(nonatomic, readonly) CGColorRef progressLayerColor;
@property(nonatomic) NSMutableDictionary * colors;
@property(nonatomic) NSMutableDictionary<NSNumber *, NSNumber *> * buttonColoring;
@property(nonatomic) NSMutableDictionary<NSNumber *, NSString *> * images;
@property(weak, nonatomic) IBOutlet MWMCircularProgress * owner;
@property(weak, nonatomic) IBOutlet UIImageView * spinner;
@property(weak, nonatomic) IBOutlet MWMButton * button;
@property(nonatomic) BOOL suspendRefreshProgress;
@end
@implementation MWMCircularProgressView
- (void)awakeFromNib
{
[super awakeFromNib];
self.suspendRefreshProgress = YES;
[self setupColors];
[self setupButtonColoring];
self.images = [NSMutableDictionary dictionary];
[self setupAnimationLayers];
self.suspendRefreshProgress = NO;
}
#pragma mark - Setup
- (void)setupColors
{
self.colors = [NSMutableDictionary dictionary];
UIColor * progressColor = [_spinnerBackgroundColor isEqual:UIColor.clearColor]
? UIColor.whiteColor
: [UIColor linkBlue];
UIColor * clearColor = UIColor.clearColor;
[self setSpinnerColoring:MWMImageColoringGray];
[self setColor:clearColor forState:MWMCircularProgressStateNormal];
[self setColor:clearColor forState:MWMCircularProgressStateSelected];
[self setColor:progressColor forState:MWMCircularProgressStateProgress];
[self setColor:progressColor forState:MWMCircularProgressStateSpinner];
[self setColor:clearColor forState:MWMCircularProgressStateFailed];
[self setColor:clearColor forState:MWMCircularProgressStateCompleted];
}
- (void)setupButtonColoring
{
self.buttonColoring = [NSMutableDictionary dictionary];
[self setColoring:MWMButtonColoringBlack forState:MWMCircularProgressStateNormal];
[self setColoring:MWMButtonColoringBlue forState:MWMCircularProgressStateSelected];
[self setColoring:MWMButtonColoringBlue forState:MWMCircularProgressStateProgress];
[self setColoring:MWMButtonColoringBlue forState:MWMCircularProgressStateSpinner];
[self setColoring:MWMButtonColoringBlue forState:MWMCircularProgressStateFailed];
[self setColoring:MWMButtonColoringBlue forState:MWMCircularProgressStateCompleted];
}
- (void)applyTheme
{
[super applyTheme];
self.suspendRefreshProgress = YES;
[self setupColors];
self.suspendRefreshProgress = NO;
}
- (void)setupAnimationLayers
{
self.backgroundLayer = [CAShapeLayer layer];
self.progressLayer = [CAShapeLayer layer];
[self refreshProgress];
[self.layer addSublayer:self.backgroundLayer];
[self.layer addSublayer:self.progressLayer];
}
- (void)setSpinnerColoring:(MWMImageColoring)coloring { self.spinner.mwm_coloring = coloring; }
- (void)setImageName:(nullable NSString *)imageName forState:(MWMCircularProgressState)state
{
self.images[@(state)] = imageName;
[self refreshProgress];
}
- (void)setColor:(UIColor *)color forState:(MWMCircularProgressState)state
{
self.colors[@(state)] = color;
[self refreshProgress];
}
- (void)setColoring:(MWMButtonColoring)coloring forState:(MWMCircularProgressState)state
{
self.buttonColoring[@(state)] = @(coloring);
[self refreshProgress];
}
#pragma mark - Progress
- (void)refreshProgress
{
if (self.suspendRefreshProgress)
return;
self.backgroundLayer.fillColor = self.progressLayer.fillColor = UIColor.clearColor.CGColor;
self.backgroundLayer.lineWidth = self.progressLayer.lineWidth = kLineWidth;
self.backgroundLayer.strokeColor = self.spinnerBackgroundColor.CGColor;
[self updateBackgroundPath];
self.progressLayer.strokeColor = self.progressLayerColor;
NSString * imageName = self.images[@(self.state)];
if (imageName)
{
[self.button setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
UIImage *hl = [UIImage imageNamed:[imageName stringByAppendingString:@"_highlighted"]];
if (hl)
[self.button setImage:hl forState:UIControlStateHighlighted];
}
else
{
[self.button setImage:nil forState:UIControlStateNormal];
[self.button setImage:nil forState:UIControlStateHighlighted];
}
self.button.coloring = (MWMButtonColoring)self.buttonColoring[@(self.state)].unsignedIntegerValue;
}
- (void)updatePath:(CGFloat)progress
{
[self updateBackgroundPath];
if (progress > 0.0)
{
self.state =
progress < 1.0 ? MWMCircularProgressStateProgress : MWMCircularProgressStateCompleted;
[self stopSpinner];
}
self.progressLayer.path = [self pathWithProgress:progress].CGPath;
}
- (void)updateBackgroundPath
{
self.backgroundLayer.path = [self pathWithProgress:1.0].CGPath;
}
- (UIBezierPath *)pathWithProgress:(CGFloat)progress
{
CGPoint center = CGPointMake(self.width / 2.0, self.height / 2.0);
CGFloat radius = MIN(center.x, center.y) - kLineWidth;
UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:center
radius:radius
startAngle:angleWithProgress(0.0)
endAngle:angleWithProgress(progress)
clockwise:YES];
return path;
}
#pragma mark - Spinner
- (void)startSpinner
{
if (self.spinner.hidden)
{
self.spinner.hidden = NO;
self.backgroundLayer.hidden = self.progressLayer.hidden = YES;
}
NSString * postfix = ([UIColor isNightMode] && !self.isInvertColor) ||
(![UIColor isNightMode] && self.isInvertColor) ||
_spinnerBackgroundColor
? @"dark"
: @"light";
UIImage * image = [UIImage imageNamed:[NSString stringWithFormat:@"Spinner_%@", postfix]];
self.spinner.image = image;
[self.spinner startRotation:1];
}
- (void)stopSpinner
{
if (self.spinner.hidden)
return;
self.spinner.hidden = YES;
self.backgroundLayer.hidden = self.progressLayer.hidden = NO;
[self.spinner stopRotation];
}
#pragma mark - Animation
- (void)animateFromValue:(CGFloat)fromValue toValue:(CGFloat)toValue
{
[self updatePath:toValue];
CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animation.duration = kDefaultAnimationDuration;
animation.repeatCount = 1;
animation.fromValue = @(fromValue / toValue);
animation.toValue = @1;
animation.timingFunction =
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.delegate = self.owner;
[self.progressLayer addAnimation:animation forKey:kAnimationKey];
}
#pragma mark - Properties
- (UIView * _Nullable)buttonView
{
return self.button;
}
- (void)setState:(MWMCircularProgressState)state
{
if (state == MWMCircularProgressStateSpinner)
[self startSpinner];
else if (_state == MWMCircularProgressStateSpinner)
[self stopSpinner];
if (_state == state)
return;
_state = state;
[self refreshProgress];
}
- (UIColor *)spinnerBackgroundColor
{
if (_spinnerBackgroundColor)
return _spinnerBackgroundColor;
switch (self.state)
{
case MWMCircularProgressStateProgress: return [UIColor pressBackground];
default: return UIColor.clearColor;
}
}
- (CGColorRef)progressLayerColor
{
UIColor * color = self.colors[@(self.state)];
return color.CGColor;
}
- (void)setFrame:(CGRect)frame
{
BOOL needrefreshProgress = !CGRectEqualToRect(self.frame, frame);
super.frame = frame;
if (needrefreshProgress)
[self refreshProgress];
}
- (BOOL)animating { return [self.progressLayer animationForKey:kAnimationKey] != nil; }
- (void)setSuspendRefreshProgress:(BOOL)suspendRefreshProgress
{
_suspendRefreshProgress = suspendRefreshProgress;
if (!suspendRefreshProgress)
[self refreshProgress];
}
@end

View file

@ -0,0 +1,50 @@
@IBDesignable
class GradientView: UIView {
enum GradientDirection {
case horizontal
case vertical
}
var gradientDirection: GradientDirection = .vertical {
didSet {
let angle = gradientDirection == .horizontal ? -CGFloat.pi / 2 : 0
gradientLayer.transform = CATransform3DMakeRotation(angle, 0, 0, 1)
}
}
@IBInspectable
var startColor: UIColor = .clear {
didSet {
updateColors()
}
}
@IBInspectable
var endColor: UIColor = .lightGray {
didSet {
updateColors()
}
}
private func updateColors() {
gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
}
override class var layerClass: AnyClass {
return CAGradientLayer.self
}
var gradientLayer: CAGradientLayer {
return layer as! CAGradientLayer
}
override init(frame: CGRect) {
super.init(frame: frame)
updateColors()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
updateColors()
}
}

View file

@ -0,0 +1,10 @@
static NSTimeInterval const kMenuViewHideFramesCount = 4.0;
static inline NSTimeInterval framesDuration(NSTimeInterval const framesCount)
{
static NSTimeInterval const kFPS = 30.0;
static NSTimeInterval const kFrameDuration = 1.0 / kFPS;
return kFrameDuration * framesCount;
}
static CGFloat const kViewControlsOffsetToBounds = 6;

View file

@ -0,0 +1,11 @@
static NSTimeInterval const kMenuViewHideFramesCount = 7.0;
static NSTimeInterval const kMenuViewMoveFramesCount = 7.0;
static inline NSTimeInterval framesDuration(NSTimeInterval const framesCount)
{
static NSTimeInterval const kFPS = 30.0;
static NSTimeInterval const kFrameDuration = 1.0 / kFPS;
return kFrameDuration * framesCount;
}
static CGFloat const kViewControlsOffsetToBounds = 4.0;

View file

@ -0,0 +1,64 @@
#import "MWMBottomMenuState.h"
#import "MWMMapDownloaderMode.h"
#import "MWMNavigationDashboardManager.h"
@class MapViewController;
@class BottomTabBarViewController;
@class TrackRecordingButtonViewController;
@class SearchQuery;
typedef NS_ENUM(NSUInteger, TrackRecordingButtonState) {
TrackRecordingButtonStateHidden,
TrackRecordingButtonStateVisible,
TrackRecordingButtonStateClosed,
};
@protocol MWMFeatureHolder;
@interface MWMMapViewControlsManager : NSObject
+ (MWMMapViewControlsManager *)manager NS_SWIFT_NAME(manager());
@property(nonatomic) BOOL hidden;
@property(nonatomic) BOOL zoomHidden;
@property(nonatomic) BOOL sideButtonsHidden;
@property(nonatomic) BOOL trafficButtonHidden;
@property(nonatomic) MWMBottomMenuState menuState;
@property(nonatomic) MWMBottomMenuState menuRestoreState;
@property(nonatomic) BOOL isDirectionViewHidden;
@property(nonatomic) BottomTabBarViewController * tabBarController;
@property(nonatomic) TrackRecordingButtonViewController * trackRecordingButton;
- (instancetype)init __attribute__((unavailable("init is not available")));
- (instancetype)initWithParentController:(MapViewController *)controller;
- (UIStatusBarStyle)preferredStatusBarStyle;
#pragma mark - Layout
- (UIView *)anchorView;
- (void)viewWillTransitionToSize:(CGSize)size
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator;
- (void)setTrackRecordingButtonState:(TrackRecordingButtonState)state;
#pragma mark - MWMNavigationDashboardManager
- (void)onRoutePrepare;
- (void)onRouteRebuild;
- (void)onRouteReady:(BOOL)hasWarnings;
- (void)onRouteStart;
- (void)onRouteStop;
#pragma mark - MWMSearchManager
- (void)actionDownloadMaps:(MWMMapDownloaderMode)mode;
- (BOOL)search:(SearchQuery *)query;
- (void)searchOnMap:(SearchQuery *)query;
#pragma mark - MWMFeatureHolder
- (id<MWMFeatureHolder>)featureHolder;
@end

View file

@ -0,0 +1,334 @@
#import "MWMMapViewControlsManager.h"
#import "MWMAddPlaceNavigationBar.h"
#import "MWMMapDownloadDialog.h"
#import "MWMMapViewControlsManager+AddPlace.h"
#import "MWMNetworkPolicy+UI.h"
#import "MWMPlacePageManager.h"
#import "MWMPlacePageProtocol.h"
#import "MWMSideButtons.h"
#import "MWMTrafficButtonViewController.h"
#import "MWMMapWidgetsHelper.h"
#import "MapViewController.h"
#import "MapsAppDelegate.h"
#import "SwiftBridge.h"
#include <CoreApi/Framework.h>
#import <CoreApi/MWMFrameworkHelper.h>
#include "platform/local_country_file_utils.hpp"
#include "platform/platform.hpp"
#include "storage/storage_helpers.hpp"
#include "map/place_page_info.hpp"
namespace {
NSString *const kMapToCategorySelectorSegue = @"MapToCategorySelectorSegue";
} // namespace
@interface MWMMapViewControlsManager () <BottomMenuDelegate>
@property(nonatomic) MWMSideButtons * sideButtons;
@property(nonatomic) MWMTrafficButtonViewController * trafficButton;
@property(nonatomic) UIButton * promoButton;
@property(nonatomic) UIViewController * menuController;
@property(nonatomic) id<MWMPlacePageProtocol> placePageManager;
@property(nonatomic) MWMNavigationDashboardManager * navigationManager;
@property(nonatomic) SearchOnMapManager * searchManager;
@property(weak, nonatomic) MapViewController * ownerController;
@property(nonatomic) BOOL disableStandbyOnRouteFollowing;
@property(nonatomic) BOOL isAddingPlace;
@end
@implementation MWMMapViewControlsManager
+ (MWMMapViewControlsManager *)manager {
return [MapViewController sharedController].controlsManager;
}
- (instancetype)initWithParentController:(MapViewController *)controller {
if (!controller)
return nil;
self = [super init];
if (!self)
return nil;
self.ownerController = controller;
self.hidden = NO;
self.sideButtonsHidden = NO;
self.trafficButtonHidden = NO;
self.isDirectionViewHidden = YES;
self.menuState = MWMBottomMenuStateInactive;
self.menuRestoreState = MWMBottomMenuStateInactive;
self.isAddingPlace = NO;
self.searchManager = controller.searchManager;
return self;
}
- (UIStatusBarStyle)preferredStatusBarStyle {
BOOL const isNavigationUnderStatusBar = self.navigationManager.state != MWMNavigationDashboardStateHidden &&
self.navigationManager.state != MWMNavigationDashboardStateNavigation;
BOOL const isMenuViewUnderStatusBar = self.menuState == MWMBottomMenuStateActive;
BOOL const isDirectionViewUnderStatusBar = !self.isDirectionViewHidden;
BOOL const isAddPlaceUnderStatusBar =
[self.ownerController.view hasSubviewWithViewClass:[MWMAddPlaceNavigationBar class]];
BOOL const isNightMode = [UIColor isNightMode];
BOOL const isSomethingUnderStatusBar = isNavigationUnderStatusBar ||
isDirectionViewUnderStatusBar || isMenuViewUnderStatusBar ||
isAddPlaceUnderStatusBar;
return isSomethingUnderStatusBar || isNightMode ? UIStatusBarStyleLightContent : UIStatusBarStyleDefault;
}
#pragma mark - Layout
- (UIView *)anchorView {
return self.tabBarController.view;
}
- (void)viewWillTransitionToSize:(CGSize)size
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[self.trafficButton viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[self.trackRecordingButton viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[self.tabBarController viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}
#pragma mark - MWMPlacePageViewManager
- (void)searchOnMap:(SearchQuery *)query {
if (![self search:query])
return;
[self.searchManager startSearchingWithIsRouting:NO];
}
- (BOOL)search:(SearchQuery *)query {
if (query.text.length == 0)
return NO;
[self.searchManager startSearchingWithIsRouting:NO];
[self.searchManager searchText:query];
return YES;
}
#pragma mark - BottomMenu
- (void)actionDownloadMaps:(MWMMapDownloaderMode)mode {
[self.ownerController openMapsDownloader:mode];
}
- (void)didFinishAddingPlace {
self.isAddingPlace = NO;
self.trafficButtonHidden = NO;
self.menuState = MWMBottomMenuStateInactive;
}
- (void)addPlace {
[self addPlace:NO position:nullptr];
}
- (void)addPlace:(BOOL)isBusiness position:(m2::PointD const *)optionalPosition {
MapViewController *ownerController = self.ownerController;
self.isAddingPlace = YES;
[self.searchManager close];
self.menuState = MWMBottomMenuStateHidden;
self.trafficButtonHidden = YES;
[ownerController dismissPlacePage];
[MWMAddPlaceNavigationBar showInSuperview:ownerController.view
isBusiness:isBusiness
position:optionalPosition
doneBlock:^{
if ([MWMFrameworkHelper canEditMapAtViewportCenter])
[ownerController performSegueWithIdentifier:kMapToCategorySelectorSegue sender:nil];
else
[ownerController.alertController presentIncorrectFeauturePositionAlert];
[self didFinishAddingPlace];
}
cancelBlock:^{
[self didFinishAddingPlace];
}];
[ownerController setNeedsStatusBarAppearanceUpdate];
}
#pragma mark - MWMNavigationDashboardManager
- (void)setDisableStandbyOnRouteFollowing:(BOOL)disableStandbyOnRouteFollowing {
if (_disableStandbyOnRouteFollowing == disableStandbyOnRouteFollowing)
return;
_disableStandbyOnRouteFollowing = disableStandbyOnRouteFollowing;
if (disableStandbyOnRouteFollowing)
[[MapsAppDelegate theApp] disableStandby];
else
[[MapsAppDelegate theApp] enableStandby];
}
#pragma mark - Routing
- (void)onRoutePrepare {
auto nm = self.navigationManager;
[nm onRoutePrepare];
[nm onRoutePointsUpdated];
[self.ownerController.bookmarksCoordinator close];
self.promoButton.hidden = YES;
}
- (void)onRouteRebuild {
[self.ownerController.bookmarksCoordinator close];
[self.navigationManager onRoutePlanning];
self.promoButton.hidden = YES;
}
- (void)onRouteReady:(BOOL)hasWarnings {
[self.navigationManager onRouteReady:hasWarnings];
self.promoButton.hidden = YES;
}
- (void)onRouteStart {
self.hidden = NO;
self.sideButtons.zoomHidden = self.zoomHidden;
self.sideButtonsHidden = NO;
self.disableStandbyOnRouteFollowing = YES;
self.trafficButtonHidden = YES;
[self.navigationManager onRouteStart];
self.promoButton.hidden = YES;
}
- (void)onRouteStop {
self.sideButtons.zoomHidden = self.zoomHidden;
[self.navigationManager onRouteStop];
self.disableStandbyOnRouteFollowing = NO;
self.trafficButtonHidden = NO;
self.promoButton.hidden = YES;
}
#pragma mark - Properties
- (MWMSideButtons *)sideButtons {
if (!_sideButtons)
_sideButtons = [[MWMSideButtons alloc] initWithParentView:self.ownerController.controlsView];
return _sideButtons;
}
- (MWMTrafficButtonViewController *)trafficButton {
if (!_trafficButton)
_trafficButton = [[MWMTrafficButtonViewController alloc] init];
return _trafficButton;
}
- (BottomTabBarViewController *)tabBarController {
if (!_tabBarController) {
MapViewController * ownerController = _ownerController;
_tabBarController = [BottomTabBarBuilder buildWithMapViewController:ownerController controlsManager:self];
[ownerController addChildViewController:_tabBarController];
UIView * tabBarViewSuperView = ownerController.controlsView;
[tabBarViewSuperView addSubview:_tabBarController.view];
}
return _tabBarController;
}
- (id<MWMPlacePageProtocol>)placePageManager {
if (!_placePageManager)
_placePageManager = [[MWMPlacePageManager alloc] init];
return _placePageManager;
}
- (MWMNavigationDashboardManager *)navigationManager {
if (!_navigationManager)
_navigationManager = [[MWMNavigationDashboardManager alloc] initWithParentView:self.ownerController.controlsView];
return _navigationManager;
}
@synthesize menuState = _menuState;
- (void)setHidden:(BOOL)hidden {
if (_hidden == hidden)
return;
// Do not hide the controls view during the place adding process.
if (!_isAddingPlace)
_hidden = hidden;
self.sideButtonsHidden = _sideButtonsHidden;
self.trafficButtonHidden = _trafficButtonHidden;
self.menuState = hidden ? MWMBottomMenuStateHidden : MWMBottomMenuStateInactive;
}
- (void)setZoomHidden:(BOOL)zoomHidden {
_zoomHidden = zoomHidden;
self.sideButtons.zoomHidden = zoomHidden;
}
- (void)setSideButtonsHidden:(BOOL)sideButtonsHidden {
_sideButtonsHidden = sideButtonsHidden;
self.sideButtons.hidden = self.hidden || sideButtonsHidden;
}
- (void)setTrafficButtonHidden:(BOOL)trafficButtonHidden {
BOOL const isNavigation = self.navigationManager.state == MWMNavigationDashboardStateNavigation;
_trafficButtonHidden = isNavigation || trafficButtonHidden;
self.trafficButton.hidden = self.hidden || _trafficButtonHidden;
}
- (void)setTrackRecordingButtonState:(TrackRecordingButtonState)state {
if (!_trackRecordingButton) {
_trackRecordingButton = [[TrackRecordingButtonViewController alloc] init];
}
[self.trackRecordingButton setState:state completion:^{
[MWMMapWidgetsHelper updateLayoutForAvailableArea];
}];
if (state == TrackRecordingButtonStateClosed)
_trackRecordingButton = nil;
}
- (void)setMenuState:(MWMBottomMenuState)menuState {
_menuState = menuState;
MapViewController * ownerController = _ownerController;
switch (_menuState) {
case MWMBottomMenuStateActive:
_tabBarController.isHidden = NO;
if (_menuController == nil) {
_menuController = [BottomMenuBuilder buildMenuWithMapViewController:ownerController
controlsManager:self
delegate:self];
[ownerController presentViewController:_menuController animated:YES completion:nil];
}
break;
case MWMBottomMenuStateLayers:
_tabBarController.isHidden = NO;
if (_menuController == nil) {
_menuController = [BottomMenuBuilder buildLayersWithMapViewController:ownerController
controlsManager:self
delegate:self];
[ownerController presentViewController:_menuController animated:YES completion:nil];
}
break;
case MWMBottomMenuStateInactive:
_tabBarController.isHidden = NO;
if (_menuController != nil) {
[_menuController dismissViewControllerAnimated:YES completion:nil];
_menuController = nil;
}
break;
case MWMBottomMenuStateHidden:
_tabBarController.isHidden = YES;
if (_menuController != nil) {
[_menuController dismissViewControllerAnimated:YES completion:nil];
_menuController = nil;
}
break;
default:
break;
}
}
#pragma mark - MWMFeatureHolder
- (id<MWMFeatureHolder>)featureHolder {
return self.placePageManager;
}
@end

View file

@ -0,0 +1,18 @@
#import "MWMMyPositionMode.h"
@interface MWMSideButtons : NSObject
+ (MWMSideButtons *)buttons;
@property (nonatomic) BOOL zoomHidden;
@property (nonatomic) BOOL hidden;
@property (nonatomic, readonly) UIView *view;
- (instancetype)init __attribute__((unavailable("init is not available")));
- (instancetype)initWithParentView:(UIView *)view;
- (void)processMyPositionStateModeEvent:(MWMMyPositionMode)mode;
+ (void)updateAvailableArea:(CGRect)frame;
@end

View file

@ -0,0 +1,149 @@
#import "MWMSideButtons.h"
#import "MWMButton.h"
#import "MWMLocationManager.h"
#import "MWMMapViewControlsManager.h"
#import "MWMRouter.h"
#import "MWMSettings.h"
#import "MWMSideButtonsView.h"
#import "SwiftBridge.h"
#include <CoreApi/Framework.h>
namespace
{
NSString * const kMWMSideButtonsViewNibName = @"MWMSideButtonsView";
NSString * const kUDDidShowLongTapToShowSideButtonsToast = @"kUDDidShowLongTapToShowSideButtonsToast";
} // namespace
@interface MWMMapViewControlsManager ()
@property(nonatomic) MWMSideButtons * sideButtons;
@end
@interface MWMSideButtons ()
@property(nonatomic) IBOutlet MWMSideButtonsView * sideView;
@property(weak, nonatomic) IBOutlet MWMButton * zoomInButton;
@property(weak, nonatomic) IBOutlet MWMButton * zoomOutButton;
@property(weak, nonatomic) IBOutlet MWMButton * locationButton;
@property(nonatomic) BOOL zoomSwipeEnabled;
@property(nonatomic, readonly) BOOL isZoomEnabled;
@property(nonatomic) MWMMyPositionMode locationMode;
@end
@implementation MWMSideButtons
- (UIView *)view {
return self.sideView;
}
+ (MWMSideButtons *)buttons { return [MWMMapViewControlsManager manager].sideButtons; }
- (instancetype)initWithParentView:(UIView *)view
{
self = [super init];
if (self)
{
[NSBundle.mainBundle loadNibNamed:kMWMSideButtonsViewNibName owner:self options:nil];
[view addSubview:self.sideView];
[self.sideView setNeedsLayout];
self.zoomSwipeEnabled = NO;
self.zoomHidden = NO;
}
return self;
}
+ (void)updateAvailableArea:(CGRect)frame { [[self buttons].sideView updateAvailableArea:frame]; }
- (void)zoomIn
{
GetFramework().Scale(Framework::SCALE_MAG, true);
}
- (void)zoomOut
{
GetFramework().Scale(Framework::SCALE_MIN, true);
}
- (void)processMyPositionStateModeEvent:(MWMMyPositionMode)mode
{
[self refreshLocationButtonState:mode];
self.locationMode = mode;
}
#pragma mark - Location button
- (void)refreshLocationButtonState:(MWMMyPositionMode)state
{
MWMButton * locBtn = self.locationButton;
[locBtn.imageView stopRotation];
switch (state)
{
case MWMMyPositionModePendingPosition:
{
[locBtn setStyleNameAndApply: @"ButtonPending"];
[locBtn.imageView startRotation:1];
break;
}
case MWMMyPositionModeNotFollow:
case MWMMyPositionModeNotFollowNoPosition: [locBtn setStyleNameAndApply: @"ButtonGetPosition"]; break;
case MWMMyPositionModeFollow: [locBtn setStyleNameAndApply: @"ButtonFollow"]; break;
case MWMMyPositionModeFollowAndRotate: [locBtn setStyleNameAndApply: @"ButtonFollowAndRotate"]; break;
}
}
#pragma mark - Actions
- (IBAction)zoomTouchDown:(UIButton *)sender { self.zoomSwipeEnabled = YES; }
- (IBAction)zoomTouchUpInside:(UIButton *)sender
{
self.zoomSwipeEnabled = NO;
if ([sender isEqual:self.zoomInButton])
[self zoomIn];
else
[self zoomOut];
}
- (IBAction)zoomTouchUpOutside:(UIButton *)sender { self.zoomSwipeEnabled = NO; }
- (IBAction)zoomSwipe:(UIPanGestureRecognizer *)sender
{
if (!self.zoomSwipeEnabled)
return;
UIView * const superview = self.sideView.superview;
CGFloat const translation =
-[sender translationInView:superview].y / superview.bounds.size.height;
CGFloat const scaleFactor = exp(translation);
GetFramework().Scale(scaleFactor, false);
}
- (IBAction)locationTouchUpInside
{
[MWMLocationManager enableLocationAlert];
GetFramework().SwitchMyPositionNextMode();
}
#pragma mark - Properties
- (BOOL)zoomHidden { return self.sideView.zoomHidden; }
- (void)setZoomHidden:(BOOL)zoomHidden
{
if ([MWMRouter isRoutingActive])
self.sideView.zoomHidden = NO;
else
self.sideView.zoomHidden = [MWMSettings zoomButtonsEnabled] ? zoomHidden : YES;
}
- (BOOL)hidden { return self.sideView.hidden; }
- (void)setHidden:(BOOL)hidden
{
if (!self.hidden && hidden)
[Toast showWithText:L(@"long_tap_toast")];
return [self.sideView setHidden:hidden animated:YES];
}
@end

View file

@ -0,0 +1,12 @@
@interface MWMSideButtonsView : UIView
@property (nonatomic) BOOL zoomHidden;
- (instancetype)initWithFrame:(CGRect)frame __attribute__((unavailable("initWithFrame is not available")));
- (instancetype)init __attribute__((unavailable("init is not available")));
- (void)setHidden:(BOOL)hidden animated:(BOOL)animated;
- (void)updateAvailableArea:(CGRect)frame;
@end

View file

@ -0,0 +1,170 @@
#import "MWMSideButtonsView.h"
#import "MWMButton.h"
#import "MWMRouter.h"
#import "MWMMapViewControlsCommon.h"
#include "base/math.hpp"
namespace {
CGFloat const kLocationButtonSpacingMax = 52;
CGFloat const kLocationButtonSpacingMin = 8;
CGFloat const kButtonsTopOffset = 6;
CGFloat const kButtonsBottomOffset = 6;
} // namespace
@interface MWMSideButtonsView ()
@property(weak, nonatomic) IBOutlet MWMButton *zoomIn;
@property(weak, nonatomic) IBOutlet MWMButton *zoomOut;
@property(weak, nonatomic) IBOutlet MWMButton *location;
@property(nonatomic) CGRect availableArea;
@end
@implementation MWMSideButtonsView
- (void)awakeFromNib {
[super awakeFromNib];
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
- (void)layoutSubviews {
CGFloat spacing = self.availableHeight - self.zoomOut.maxY - self.location.height;
spacing = math::Clamp(spacing, kLocationButtonSpacingMin, kLocationButtonSpacingMax);
if (!IPAD && (UIDevice.currentDevice.orientation == UIDeviceOrientationLandscapeLeft || UIDevice.currentDevice.orientation == UIDeviceOrientationLandscapeRight) && [MWMRouter isRoutingActive]) {
spacing = spacing - 36;
}
self.location.minY = self.zoomOut.maxY + spacing;
self.bounds = {{}, {self.zoomOut.width, self.location.maxY}};
if (self.zoomHidden)
self.height = self.location.height;
self.location.maxY = self.height;
[self animate];
[super layoutSubviews];
}
- (void)layoutXPosition:(BOOL)hidden {
if (UIApplication.sharedApplication.userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) {
if (hidden)
self.maxX = 0;
else
self.minX = self.availableArea.origin.x + kViewControlsOffsetToBounds;
} else {
const auto availableAreaMaxX = self.availableArea.origin.x + self.availableArea.size.width;
if (hidden)
self.minX = self.superview.width;
else
self.maxX = availableAreaMaxX - kViewControlsOffsetToBounds;
}
}
- (void)layoutYPosition {
CGFloat const centerShift = (self.height - self.zoomIn.midY - self.zoomOut.midY) / 2;
[UIView animateWithDuration:kDefaultAnimationDuration
animations:^{
self.midY = centerShift + self.superview.height / 2;
if ([MWMRouter isRoutingActive]) {
self.midY = self.midY - 18;
}
if (self.maxY > self.bottomBound)
self.maxY = self.bottomBound;
}];
}
- (void)fadeZoomButtonsShow:(BOOL)show {
CGFloat const alpha = show ? 1.0 : 0.0;
[UIView animateWithDuration:kDefaultAnimationDuration
animations:^{
self.zoomIn.alpha = alpha;
self.zoomOut.alpha = alpha;
}];
}
- (void)fadeLocationButtonShow:(BOOL)show {
[UIView animateWithDuration:kDefaultAnimationDuration
animations:^{
self.location.alpha = show ? 1.0 : 0.0;
}];
}
// Show/hide zoom and location buttons depending on available vertical space.
- (void)animate {
[self layoutYPosition];
BOOL const isZoomHidden = self.zoomIn.alpha == 0.0;
BOOL const willZoomHide = (self.location.maxY > self.availableHeight);
if (willZoomHide != isZoomHidden)
[self fadeZoomButtonsShow: !willZoomHide];
BOOL const isLocationHidden = self.location.alpha == 0.0;
BOOL const willLocationHide = (self.location.height > self.availableHeight);
if (willLocationHide != isLocationHidden)
[self fadeLocationButtonShow: !willLocationHide];
}
#pragma mark - Properties
- (void)setZoomHidden:(BOOL)zoomHidden {
_zoomHidden = zoomHidden;
self.zoomIn.hidden = zoomHidden;
self.zoomOut.hidden = zoomHidden;
[self setNeedsLayout];
}
- (void)setHidden:(BOOL)hidden animated:(BOOL)animated {
if (animated) {
if (self.hidden == hidden)
return;
// Side buttons should be visible during any our show/hide anamation.
// Visibility should be detemined by alpha, not self.hidden.
self.hidden = NO;
[UIView animateWithDuration:kDefaultAnimationDuration
animations:^{
self.alpha = hidden ? 0.0 : 1.0;
[self layoutXPosition:hidden];
}
completion:^(BOOL finished) {
self.hidden = hidden;
}];
} else {
self.hidden = hidden;
}
}
- (void)updateAvailableArea:(CGRect)frame {
if (CGRectEqualToRect(self.availableArea, frame))
return;
// If during our show/hide animation position is changed it is corrupted.
// Such kind of animation has 2 keys (opacity and position).
// But there are other animation cases like change of orientation.
// So we can use condition below:
// if (self.layer.animationKeys.count != 2)
// More elegant way is to check if x values are changed.
// If no - there is no need to update self x values.
if (self.availableArea.origin.x != frame.origin.x || self.availableArea.size.width != frame.size.width)
{
self.availableArea = frame;
[self layoutXPosition:self.hidden];
}
else
self.availableArea = frame;
[self setNeedsLayout];
}
- (CGFloat)availableHeight {
return self.availableArea.size.height - kButtonsTopOffset - kButtonsBottomOffset;
}
- (CGFloat)topBound {
return self.availableArea.origin.y + kButtonsTopOffset;
}
- (CGFloat)bottomBound {
auto const area = self.availableArea;
return area.origin.y + area.size.height - kButtonsBottomOffset;
}
@end

View file

@ -0,0 +1,91 @@
<?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" useSafeAreas="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="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="MWMSideButtons">
<connections>
<outlet property="locationButton" destination="fUK-2V-4ya" id="CBK-kp-DFE"/>
<outlet property="sideView" destination="ek2-ZW-pCm" id="sbV-Vv-Wrp"/>
<outlet property="zoomInButton" destination="NO3-Xl-Oka" id="ePH-BR-gfW"/>
<outlet property="zoomOutButton" destination="hwn-8L-cFX" id="fYk-mf-gUY"/>
</connections>
</placeholder>
<view contentMode="scaleToFill" id="ek2-ZW-pCm" customClass="MWMSideButtonsView" propertyAccessControl="none">
<rect key="frame" x="0.0" y="0.0" width="56" height="228"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NO3-Xl-Oka" userLabel="ZoomIn" customClass="MWMButton">
<rect key="frame" x="0.0" y="0.0" width="56" height="56"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" image="btn_zoom_in_light">
<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="ButtonZoomIn"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="zoomTouchDown:" destination="-1" eventType="touchDown" id="5VF-m8-Lwc"/>
<action selector="zoomTouchUpInside:" destination="-1" eventType="touchUpInside" id="wbL-zf-fH8"/>
<action selector="zoomTouchUpOutside:" destination="-1" eventType="touchUpOutside" id="w6V-A2-cZM"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hwn-8L-cFX" userLabel="ZoomOut" customClass="MWMButton">
<rect key="frame" x="0.0" y="64" width="56" height="56"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" image="btn_zoom_out_light">
<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="ButtonZoomOut"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="zoomTouchDown:" destination="-1" eventType="touchDown" id="o4X-Kp-9ka"/>
<action selector="zoomTouchUpInside:" destination="-1" eventType="touchUpInside" id="Gcq-hm-Nk8"/>
<action selector="zoomTouchUpOutside:" destination="-1" eventType="touchUpOutside" id="cX7-sp-3L3"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fUK-2V-4ya" userLabel="Location" customClass="MWMButton">
<rect key="frame" x="0.0" y="172" width="56" height="56"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" image="btn_get_position_light">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="locationTouchUpInside" destination="-1" eventType="touchUpInside" id="CMC-xb-Dpk"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<gestureRecognizers/>
<nil key="simulatedStatusBarMetrics"/>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<viewLayoutGuide key="safeArea" id="rMs-pl-xhs"/>
<connections>
<outlet property="location" destination="fUK-2V-4ya" id="lgn-MB-VVy"/>
<outlet property="zoomIn" destination="NO3-Xl-Oka" id="1sc-ei-oRj"/>
<outlet property="zoomOut" destination="hwn-8L-cFX" id="htY-bc-Ugh"/>
<outletCollection property="gestureRecognizers" destination="6qU-Ff-Ae5" appends="YES" id="jeT-Jr-P7T"/>
</connections>
<point key="canvasLocation" x="165" y="-6"/>
</view>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<panGestureRecognizer minimumNumberOfTouches="1" id="6qU-Ff-Ae5">
<connections>
<action selector="zoomSwipe:" destination="-1" id="jq1-Qs-vUJ"/>
</connections>
</panGestureRecognizer>
</objects>
<resources>
<image name="btn_get_position_light" width="56" height="56"/>
<image name="btn_zoom_in_light" width="56" height="56"/>
<image name="btn_zoom_out_light" width="56" height="56"/>
</resources>
</document>

View file

@ -0,0 +1,13 @@
@class MWMZoomButtonsView;
@interface MWMZoomButtons : NSObject
@property (nonatomic) BOOL hidden;
- (instancetype)init __attribute__((unavailable("init is not available")));
- (instancetype)initWithParentView:(UIView *)view;
- (void)setTopBound:(CGFloat)bound;
- (void)setBottomBound:(CGFloat)bound;
- (void)mwm_refreshUI;
@end

View file

@ -0,0 +1,122 @@
#import "MWMZoomButtons.h"
#import "MWMZoomButtonsView.h"
#import "Statistics.h"
#include "Framework.h"
#include "platform/settings.hpp"
#include "indexer/scales.hpp"
static NSString * const kMWMZoomButtonsViewNibName = @"MWMZoomButtonsView";
@interface MWMZoomButtons()
@property (nonatomic) IBOutlet MWMZoomButtonsView * zoomView;
@property (weak, nonatomic) IBOutlet UIButton * zoomInButton;
@property (weak, nonatomic) IBOutlet UIButton * zoomOutButton;
@property (nonatomic) BOOL zoomSwipeEnabled;
@property (nonatomic, readonly) BOOL isZoomEnabled;
@end
@implementation MWMZoomButtons
- (instancetype)initWithParentView:(UIView *)view
{
self = [super init];
if (self)
{
[[NSBundle mainBundle] loadNibNamed:kMWMZoomButtonsViewNibName owner:self options:nil];
[view addSubview:self.zoomView];
[self.zoomView layoutIfNeeded];
self.zoomView.topBound = 0.0;
self.zoomView.bottomBound = view.height;
self.zoomSwipeEnabled = NO;
}
return self;
}
- (void)setTopBound:(CGFloat)bound
{
self.zoomView.topBound = bound;
}
- (void)setBottomBound:(CGFloat)bound
{
self.zoomView.bottomBound = bound;
}
- (void)zoomIn
{
[Statistics logEvent:kStatEventName(kStatZoom, kStatIn)];
GetFramework().Scale(Framework::SCALE_MAG, true);
}
- (void)zoomOut
{
[Statistics logEvent:kStatEventName(kStatZoom, kStatOut)];
GetFramework().Scale(Framework::SCALE_MIN, true);
}
- (void)mwm_refreshUI
{
[self.zoomView mwm_refreshUI];
}
#pragma mark - Actions
- (IBAction)zoomTouchDown:(UIButton *)sender
{
self.zoomSwipeEnabled = YES;
}
- (IBAction)zoomTouchUpInside:(UIButton *)sender
{
self.zoomSwipeEnabled = NO;
if ([sender isEqual:self.zoomInButton])
[self zoomIn];
else
[self zoomOut];
}
- (IBAction)zoomTouchUpOutside:(UIButton *)sender
{
self.zoomSwipeEnabled = NO;
}
- (IBAction)zoomSwipe:(UIPanGestureRecognizer *)sender
{
if (!self.zoomSwipeEnabled)
return;
UIView * const superview = self.zoomView.superview;
CGFloat const translation = -[sender translationInView:superview].y / superview.bounds.size.height;
CGFloat const scaleFactor = exp(translation);
GetFramework().Scale(scaleFactor, false);
}
#pragma mark - Properties
- (BOOL)isZoomEnabled
{
bool zoomButtonsEnabled = true;
(void)settings::Get("ZoomButtonsEnabled", zoomButtonsEnabled);
return zoomButtonsEnabled;
}
- (BOOL)hidden
{
return self.isZoomEnabled ? self.zoomView.hidden : YES;
}
- (void)setHidden:(BOOL)hidden
{
if (self.isZoomEnabled)
[self.zoomView setHidden:hidden animated:YES];
else
self.zoomView.hidden = YES;
}
@end

View file

@ -0,0 +1,13 @@
#import <UIKit/UIKit.h>
@interface MWMZoomButtonsView : UIView
@property (nonatomic) CGFloat topBound;
@property (nonatomic) CGFloat bottomBound;
- (instancetype)initWithFrame:(CGRect)frame __attribute__((unavailable("initWithFrame is not available")));
- (instancetype)init __attribute__((unavailable("init is not available")));
- (void)setHidden:(BOOL)hidden animated:(BOOL)animated;
@end

View file

@ -0,0 +1,137 @@
#import "Common.h"
#import "MWMZoomButtonsView.h"
#import "MWMMapViewControlsCommon.h"
static CGFloat const kZoomViewOffsetToTopBound = 12.0;
static CGFloat const kZoomViewOffsetToBottomBound = 40.0;
static CGFloat const kZoomViewOffsetToFrameBound = 294.0;
static CGFloat const kZoomViewHideBoundPercent = 0.4;
@interface MWMZoomButtonsView()
@property (nonatomic) CGRect defaultBounds;
@end
@implementation MWMZoomButtonsView
- (void)awakeFromNib
{
self.defaultBounds = self.bounds;
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
- (void)layoutSubviews
{
self.bounds = self.defaultBounds;
[self layoutXPosition:self.hidden];
[self layoutYPosition];
[super layoutSubviews];
}
- (void)layoutXPosition:(BOOL)hidden
{
if (hidden)
self.minX = self.superview.width;
else
self.maxX = self.superview.width - kViewControlsOffsetToBounds;
}
- (void)layoutYPosition
{
CGFloat const maxY = MIN(self.superview.height - kZoomViewOffsetToFrameBound, self.bottomBound - kZoomViewOffsetToBottomBound);
self.minY = MAX(maxY - self.height, self.topBound + kZoomViewOffsetToTopBound);
}
- (void)moveAnimated
{
if (self.hidden)
return;
[UIView animateWithDuration:framesDuration(kMenuViewMoveFramesCount) animations:^{ [self layoutYPosition]; }];
}
- (void)fadeAnimatedIn:(BOOL)show
{
[UIView animateWithDuration:framesDuration(kMenuViewHideFramesCount) animations:^{ self.alpha = show ? 1.0 : 0.0; }];
}
- (void)animate
{
CGFloat const hideBound = kZoomViewHideBoundPercent * self.superview.height;
BOOL const isHidden = self.alpha == 0.0;
BOOL const willHide = (self.bottomBound < hideBound) || (self.defaultBounds.size.height > self.bottomBound - self.topBound);
if (willHide)
{
if (!isHidden)
[self fadeAnimatedIn:NO];
}
else
{
[self moveAnimated];
if (isHidden)
[self fadeAnimatedIn:YES];
}
}
#pragma mark - Properties
- (void)setHidden:(BOOL)hidden animated:(BOOL)animated
{
if (animated)
{
if (self.hidden == hidden)
return;
if (!hidden)
self.hidden = NO;
[self layoutXPosition:!hidden];
[UIView animateWithDuration:framesDuration(kMenuViewHideFramesCount) animations:^
{
[self layoutXPosition:hidden];
}
completion:^(BOOL finished)
{
if (hidden)
self.hidden = YES;
}];
}
else
{
self.hidden = hidden;
}
}
@synthesize topBound = _topBound;
- (void)setTopBound:(CGFloat)topBound
{
if (equalScreenDimensions(_topBound, topBound))
return;
_topBound = topBound;
[self animate];
}
- (CGFloat)topBound
{
return MAX(0.0, _topBound);
}
@synthesize bottomBound = _bottomBound;
- (void)setBottomBound:(CGFloat)bottomBound
{
if (equalScreenDimensions(_bottomBound, bottomBound))
return;
_bottomBound = bottomBound;
[self animate];
}
- (CGFloat)bottomBound
{
if (!self.superview)
return _bottomBound;
BOOL const isPortrait = self.superview.width < self.superview.height;
CGFloat limit = IPAD ? 320.0 : isPortrait ? 200.0 : 80.0;
return MIN(self.superview.height - limit, _bottomBound);
}
@end

View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="15A284" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MWMZoomButtons">
<connections>
<outlet property="zoomInButton" destination="NO3-Xl-Oka" id="ePH-BR-gfW"/>
<outlet property="zoomOutButton" destination="hwn-8L-cFX" id="fYk-mf-gUY"/>
<outlet property="zoomView" destination="ek2-ZW-pCm" id="N0e-Rh-Unp"/>
</connections>
</placeholder>
<view contentMode="scaleToFill" id="ek2-ZW-pCm" customClass="MWMZoomButtonsView">
<rect key="frame" x="0.0" y="0.0" width="56" height="116"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="NO3-Xl-Oka" userLabel="ZoomIn" customClass="MWMButton">
<rect key="frame" x="0.0" y="0.0" width="56" height="56"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" image="btn_zoom_in_light">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="imageName" value="btn_zoom_in"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="zoomTouchDown:" destination="-1" eventType="touchDown" id="5VF-m8-Lwc"/>
<action selector="zoomTouchUpInside:" destination="-1" eventType="touchUpInside" id="wbL-zf-fH8"/>
<action selector="zoomTouchUpOutside:" destination="-1" eventType="touchUpOutside" id="w6V-A2-cZM"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="hwn-8L-cFX" userLabel="ZoomOut" customClass="MWMButton">
<rect key="frame" x="0.0" y="60" width="56" height="56"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" image="btn_zoom_out_light">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="imageName" value="btn_zoom_out"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="zoomTouchDown:" destination="-1" eventType="touchDown" id="o4X-Kp-9ka"/>
<action selector="zoomTouchUpInside:" destination="-1" eventType="touchUpInside" id="Gcq-hm-Nk8"/>
<action selector="zoomTouchUpOutside:" destination="-1" eventType="touchUpOutside" id="cX7-sp-3L3"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<gestureRecognizers/>
<nil key="simulatedStatusBarMetrics"/>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outletCollection property="gestureRecognizers" destination="6qU-Ff-Ae5" appends="YES" id="jeT-Jr-P7T"/>
</connections>
<point key="canvasLocation" x="165" y="-6"/>
</view>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<panGestureRecognizer minimumNumberOfTouches="1" id="6qU-Ff-Ae5">
<connections>
<action selector="zoomSwipe:" destination="-1" id="jq1-Qs-vUJ"/>
</connections>
</panGestureRecognizer>
</objects>
<resources>
<image name="btn_zoom_in_light" width="56" height="56"/>
<image name="btn_zoom_out_light" width="56" height="56"/>
</resources>
</document>

View file

@ -0,0 +1,157 @@
final class TrackRecordingButtonViewController: MWMViewController {
private enum Constants {
static let buttonDiameter = CGFloat(48)
static let topOffset = CGFloat(6)
static let trailingOffset = CGFloat(10)
static let blinkingDuration = 1.0
static let color: (lighter: UIColor, darker: UIColor) = (.red, .red.darker(percent: 0.3))
}
private let trackRecordingManager: TrackRecordingManager = .shared
private let button = BottomTabBarButton()
private var blinkingTimer: Timer?
private var topConstraint = NSLayoutConstraint()
private var trailingConstraint = NSLayoutConstraint()
private var state: TrackRecordingButtonState = .hidden
private static var availableArea: CGRect = .zero
private static var topConstraintValue: CGFloat {
availableArea.origin.y + Constants.topOffset
}
private static var trailingConstraintValue: CGFloat {
-(UIScreen.main.bounds.maxX - availableArea.maxX + Constants.trailingOffset)
}
@objc
init() {
super.init(nibName: nil, bundle: nil)
let ownerViewController = MapViewController.shared()
ownerViewController?.addChild(self)
ownerViewController?.controlsView.addSubview(view)
self.setupView()
self.layout()
self.startTimer()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
setState(self.state, completion: nil)
}
// MARK: - Public methods
@objc
func setState(_ state: TrackRecordingButtonState, completion: (() -> Void)?) {
self.state = state
switch state {
case .visible:
setHidden(false, completion: nil)
case .hidden:
setHidden(true, completion: completion)
case .closed:
close(completion: completion)
@unknown default:
fatalError()
}
}
// MARK: - Private methods
private func setupView() {
view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
button.setStyleAndApply(.trackRecordingWidgetButton)
button.tintColor = Constants.color.darker
button.translatesAutoresizingMaskIntoConstraints = false
button.setImage(UIImage(resource: .icMenuBookmarkTrackRecording), for: .normal)
button.addTarget(self, action: #selector(didTap), for: .touchUpInside)
button.isHidden = true
}
private func layout() {
guard let superview = view.superview else { return }
topConstraint = view.topAnchor.constraint(equalTo: superview.topAnchor, constant: Self.topConstraintValue)
trailingConstraint = view.trailingAnchor.constraint(equalTo: superview.trailingAnchor, constant: Self.trailingConstraintValue)
NSLayoutConstraint.activate([
topConstraint,
trailingConstraint,
view.widthAnchor.constraint(equalToConstant: Constants.buttonDiameter),
view.heightAnchor.constraint(equalToConstant: Constants.buttonDiameter),
button.leadingAnchor.constraint(equalTo: view.leadingAnchor),
button.trailingAnchor.constraint(equalTo: view.trailingAnchor),
button.topAnchor.constraint(equalTo: view.topAnchor),
button.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}
private func updateLayout() {
guard let superview = view.superview else { return }
superview.animateConstraints {
self.topConstraint.constant = Self.topConstraintValue
self.trailingConstraint.constant = Self.trailingConstraintValue
}
}
private func startTimer() {
guard blinkingTimer == nil else { return }
var lighter = false
let timer = Timer.scheduledTimer(withTimeInterval: Constants.blinkingDuration, repeats: true) { [weak self] _ in
guard let self = self else { return }
UIView.animate(withDuration: Constants.blinkingDuration, animations: {
self.button.tintColor = lighter ? Constants.color.lighter : Constants.color.darker
lighter.toggle()
})
}
blinkingTimer = timer
RunLoop.current.add(timer, forMode: .common)
}
private func stopTimer() {
blinkingTimer?.invalidate()
blinkingTimer = nil
}
private func setHidden(_ hidden: Bool, completion: (() -> Void)?) {
UIView.transition(with: self.view,
duration: kDefaultAnimationDuration,
options: .transitionCrossDissolve,
animations: {
self.button.isHidden = hidden
}) { _ in
completion?()
}
}
private func close(completion: (() -> Void)?) {
stopTimer()
setHidden(true) { [weak self] in
guard let self else { return }
self.removeFromParent()
self.view.removeFromSuperview()
completion?()
}
}
static func updateAvailableArea(_ frame: CGRect) {
availableArea = frame
guard let button = MapViewController.shared()?.controlsManager.trackRecordingButton else { return }
DispatchQueue.main.async {
button.updateLayout()
}
}
// MARK: - Actions
@objc
private func didTap(_ sender: Any) {
MapViewController.shared()?.showTrackRecordingPlacePage()
}
}

View file

@ -0,0 +1,11 @@
#import "MWMViewController.h"
@interface MWMTrafficButtonViewController : MWMViewController
+ (MWMTrafficButtonViewController *)controller;
@property(nonatomic) BOOL hidden;
+ (void)updateAvailableArea:(CGRect)frame;
@end

View file

@ -0,0 +1,216 @@
#import "MWMTrafficButtonViewController.h"
#import <CoreApi/MWMMapOverlayManager.h>
#import "MWMAlertViewController.h"
#import "MWMButton.h"
#import "MWMMapViewControlsCommon.h"
#import "MWMMapViewControlsManager.h"
#import "MapViewController.h"
#import "SwiftBridge.h"
#import "base/assert.hpp"
namespace {
CGFloat const kTopOffset = 6;
NSArray<UIImage *> *imagesWithName(NSString *name) {
NSUInteger const imagesCount = 3;
NSMutableArray<UIImage *> *images = [NSMutableArray arrayWithCapacity:imagesCount];
NSString *mode = [UIColor isNightMode] ? @"dark" : @"light";
for (NSUInteger i = 1; i <= imagesCount; i += 1) {
NSString *imageName = [NSString stringWithFormat:@"%@_%@_%@", name, mode, @(i).stringValue];
[images addObject:static_cast<UIImage *_Nonnull>([UIImage imageNamed:imageName])];
}
return [images copy];
}
} // namespace
@interface MWMMapViewControlsManager ()
@property(nonatomic) MWMTrafficButtonViewController *trafficButton;
@end
@interface MWMTrafficButtonViewController () <MWMMapOverlayManagerObserver, ThemeListener>
@property(nonatomic) NSLayoutConstraint *topOffset;
@property(nonatomic) NSLayoutConstraint *leftOffset;
@property(nonatomic) CGRect availableArea;
@end
@implementation MWMTrafficButtonViewController
+ (MWMTrafficButtonViewController *)controller {
return [MWMMapViewControlsManager manager].trafficButton;
}
- (instancetype)init {
self = [super init];
if (self) {
MapViewController *ovc = [MapViewController sharedController];
[ovc addChildViewController:self];
[ovc.controlsView addSubview:self.view];
[self configLayout];
[self applyTheme];
[StyleManager.shared addListener:self];
[MWMMapOverlayManager addObserver:self];
}
return self;
}
- (void)dealloc {
[StyleManager.shared removeListener:self];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[Toast hideAll];
}
- (void)configLayout {
UIView *sv = self.view;
UIView *ov = sv.superview;
self.topOffset = [sv.topAnchor constraintEqualToAnchor:ov.topAnchor constant:kTopOffset];
self.topOffset.active = YES;
self.leftOffset = [sv.leadingAnchor constraintEqualToAnchor:ov.leadingAnchor constant:kViewControlsOffsetToBounds];
self.leftOffset.active = YES;
}
- (void)setHidden:(BOOL)hidden {
_hidden = hidden;
[self refreshLayout];
}
- (void)refreshLayout {
dispatch_async(dispatch_get_main_queue(), ^{
auto const availableArea = self.availableArea;
auto const fitInAvailableArea = CGRectGetMaxY(self.view.frame) < CGRectGetMaxY(availableArea) + kTopOffset;
auto const shouldHide = self.hidden || !fitInAvailableArea;
auto const leftOffset = shouldHide ? -self.view.width : availableArea.origin.x + kViewControlsOffsetToBounds;
self.topOffset.constant = availableArea.origin.y + kTopOffset;
self.leftOffset.constant = leftOffset;
self.view.alpha = shouldHide ? 0 : 1;
});
}
- (void)handleTrafficState:(MWMMapOverlayTrafficState)state {
MWMButton *btn = (MWMButton *)self.view;
UIImageView *iv = btn.imageView;
switch (state) {
case MWMMapOverlayTrafficStateDisabled:
CHECK(false, ("Incorrect traffic manager state."));
break;
case MWMMapOverlayTrafficStateEnabled:
btn.imageName = @"btn_traffic_on";
break;
case MWMMapOverlayTrafficStateWaitingData:
iv.animationImages = imagesWithName(@"btn_traffic_update");
iv.animationDuration = 0.8;
[iv startAnimating];
break;
case MWMMapOverlayTrafficStateOutdated:
btn.imageName = @"btn_traffic_outdated";
break;
case MWMMapOverlayTrafficStateNoData:
btn.imageName = @"btn_traffic_on";
[Toast showWithText:L(@"traffic_data_unavailable")];
break;
case MWMMapOverlayTrafficStateNetworkError:
[MWMMapOverlayManager setTrafficEnabled:NO];
[[MWMAlertViewController activeAlertController] presentNoConnectionAlert];
break;
case MWMMapOverlayTrafficStateExpiredData:
btn.imageName = @"btn_traffic_outdated";
[Toast showWithText:L(@"traffic_update_maps_text")];
break;
case MWMMapOverlayTrafficStateExpiredApp:
btn.imageName = @"btn_traffic_outdated";
[Toast showWithText:L(@"traffic_update_app_message")];
break;
}
}
- (void)handleIsolinesState:(MWMMapOverlayIsolinesState)state {
switch (state) {
case MWMMapOverlayIsolinesStateDisabled:
break;
case MWMMapOverlayIsolinesStateEnabled:
if (![MWMMapOverlayManager isolinesVisible])
[Toast showWithText:L(@"isolines_toast_zooms_1_10")];
break;
case MWMMapOverlayIsolinesStateExpiredData:
[MWMAlertViewController.activeAlertController presentInfoAlert:L(@"isolines_activation_error_dialog")];
[MWMMapOverlayManager setIsoLinesEnabled:NO];
break;
case MWMMapOverlayIsolinesStateNoData:
[MWMAlertViewController.activeAlertController presentInfoAlert:L(@"isolines_location_error_dialog")];
[MWMMapOverlayManager setIsoLinesEnabled:NO];
break;
}
}
- (void)applyTheme {
MWMButton *btn = static_cast<MWMButton *>(self.view);
UIImageView *iv = btn.imageView;
// Traffic state machine: https://confluence.mail.ru/pages/viewpage.action?pageId=103680959
[iv stopAnimating];
if ([MWMMapOverlayManager trafficEnabled]) {
[self handleTrafficState:[MWMMapOverlayManager trafficState]];
} else if ([MWMMapOverlayManager transitEnabled]) {
btn.imageName = @"btn_subway_on";
if ([MWMMapOverlayManager transitState] == MWMMapOverlayTransitStateNoData)
[Toast showWithText:L(@"subway_data_unavailable")];
} else if ([MWMMapOverlayManager isoLinesEnabled]) {
btn.imageName = @"btn_isoMap_on";
[self handleIsolinesState:[MWMMapOverlayManager isolinesState]];
} else if ([MWMMapOverlayManager outdoorEnabled]) {
btn.imageName = @"btn_isoMap_on";
} else {
btn.imageName = @"btn_layers";
}
}
- (IBAction)buttonTouchUpInside {
BOOL needsToDisableMapLayer =
[MWMMapOverlayManager trafficEnabled] ||
[MWMMapOverlayManager transitEnabled] ||
[MWMMapOverlayManager isoLinesEnabled] ||
[MWMMapOverlayManager outdoorEnabled];
if (needsToDisableMapLayer) {
[MWMMapOverlayManager setTrafficEnabled:NO];
[MWMMapOverlayManager setTransitEnabled:NO];
[MWMMapOverlayManager setIsoLinesEnabled:NO];
[MWMMapOverlayManager setOutdoorEnabled:NO];
} else {
MWMMapViewControlsManager.manager.menuState = MWMBottomMenuStateLayers;
}
}
+ (void)updateAvailableArea:(CGRect)frame {
auto controller = [self controller];
if (CGRectEqualToRect(controller.availableArea, frame))
return;
controller.availableArea = frame;
[controller refreshLayout];
}
#pragma mark - MWMMapOverlayManagerObserver
- (void)onTrafficStateUpdated {
[self applyTheme];
}
- (void)onTransitStateUpdated {
[self applyTheme];
}
- (void)onIsoLinesStateUpdated {
[self applyTheme];
}
- (void)onOutdoorStateUpdated {
[self applyTheme];
}
@end

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<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="MWMTrafficButtonViewController">
<connections>
<outlet property="view" destination="WVx-0E-RoH" id="0Ev-19-Sxq"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="WVx-0E-RoH" customClass="MWMButton" propertyAccessControl="all">
<rect key="frame" x="0.0" y="0.0" width="56" height="56"/>
<accessibility key="accessibilityConfiguration" identifier="layers_button"/>
<constraints>
<constraint firstAttribute="height" constant="56" id="24f-V4-Vuf"/>
<constraint firstAttribute="width" constant="56" id="hko-xz-hRz"/>
</constraints>
<viewLayoutGuide key="safeArea" id="iUc-A7-STp"/>
<state key="normal" image="btn_traffic_on_light"/>
<connections>
<action selector="buttonTouchUpInside" destination="-1" eventType="touchUpInside" id="fKZ-g8-4ML"/>
</connections>
<point key="canvasLocation" x="0.0" y="0.0"/>
</button>
</objects>
<resources>
<image name="btn_traffic_on_light" width="56" height="56"/>
</resources>
</document>

View file

@ -0,0 +1,27 @@
@class MWMRouterTransitStepInfo;
@interface MWMNavigationDashboardEntity : NSObject
@property(copy, nonatomic, readonly) NSArray<MWMRouterTransitStepInfo *> *transitSteps;
@property(copy, nonatomic, readonly) NSString *distanceToTurn;
@property(copy, nonatomic, readonly) NSString *streetName;
@property(copy, nonatomic, readonly) NSString *targetDistance;
@property(copy, nonatomic, readonly) NSString *targetUnits;
@property(copy, nonatomic, readonly) NSString *turnUnits;
@property(nonatomic, readonly) double speedLimitMps;
@property(nonatomic, readonly) BOOL isValid;
@property(nonatomic, readonly) CGFloat progress;
@property(nonatomic, readonly) NSUInteger roundExitNumber;
@property(nonatomic, readonly) NSUInteger timeToTarget;
@property(nonatomic, readonly) UIImage *nextTurnImage;
@property(nonatomic, readonly) UIImage *turnImage;
@property(nonatomic, readonly) NSString * arrival;
- (NSAttributedString *) estimate;
+ (NSAttributedString *) estimateDot;
+ (instancetype) new __attribute__((unavailable("init is not available")));
@end

View file

@ -0,0 +1,16 @@
#import "MWMNavigationDashboardManager.h"
#import "MWMRoutePoint.h"
#import "MWMRouterType.h"
namespace routing {
class FollowingInfo;
}
struct TransitRouteInfo;
@interface MWMNavigationDashboardManager (Entity)
- (void)updateFollowingInfo:(routing::FollowingInfo const &)info routePoints:(NSArray<MWMRoutePoint *> *)points type:(MWMRouterType)type;
- (void)updateTransitInfo:(TransitRouteInfo const &)info;
@end

View file

@ -0,0 +1,255 @@
#import "MWMLocationManager.h"
#import "MWMNavigationDashboardEntity.h"
#import "MWMNavigationDashboardManager+Entity.h"
#import "MWMRouter.h"
#import "MWMRouterTransitStepInfo.h"
#import "SwiftBridge.h"
#import <AudioToolbox/AudioServices.h>
#import <CoreApi/Framework.h>
#import <CoreApi/DurationFormatter.h>
#include "routing/following_info.hpp"
#include "routing/turns.hpp"
#include "map/routing_manager.hpp"
#include "platform/location.hpp"
#include "geometry/distance_on_sphere.hpp"
namespace {
UIImage * image(routing::turns::CarDirection t, bool isNextTurn) {
if (![MWMLocationManager lastLocation])
return nil;
using namespace routing::turns;
NSString * imageName;
switch (t) {
case CarDirection::ExitHighwayToRight: imageName = @"ic_exit_highway_to_right"; break;
case CarDirection::TurnSlightRight: imageName = @"slight_right"; break;
case CarDirection::TurnRight: imageName = @"simple_right"; break;
case CarDirection::TurnSharpRight: imageName = @"sharp_right"; break;
case CarDirection::ExitHighwayToLeft: imageName = @"ic_exit_highway_to_left"; break;
case CarDirection::TurnSlightLeft: imageName = @"slight_left"; break;
case CarDirection::TurnLeft: imageName = @"simple_left"; break;
case CarDirection::TurnSharpLeft: imageName = @"sharp_left"; break;
case CarDirection::UTurnLeft: imageName = @"uturn_left"; break;
case CarDirection::UTurnRight: imageName = @"uturn_right"; break;
case CarDirection::ReachedYourDestination: imageName = @"finish_point"; break;
case CarDirection::LeaveRoundAbout:
case CarDirection::EnterRoundAbout: imageName = @"round"; break;
case CarDirection::GoStraight: imageName = @"straight"; break;
case CarDirection::StartAtEndOfStreet:
case CarDirection::StayOnRoundAbout:
case CarDirection::Count:
case CarDirection::None: imageName = isNextTurn ? nil : @"straight"; break;
}
if (!imageName)
return nil;
return [UIImage imageNamed:isNextTurn ? [imageName stringByAppendingString:@"_then"] : imageName];
}
UIImage * image(routing::turns::PedestrianDirection t) {
if (![MWMLocationManager lastLocation])
return nil;
using namespace routing::turns;
NSString * imageName;
switch (t) {
case PedestrianDirection::TurnRight: imageName = @"simple_right"; break;
case PedestrianDirection::TurnLeft: imageName = @"simple_left"; break;
case PedestrianDirection::ReachedYourDestination: imageName = @"finish_point"; break;
case PedestrianDirection::GoStraight:
case PedestrianDirection::Count:
case PedestrianDirection::None: imageName = @"straight"; break;
}
if (!imageName)
return nil;
return [UIImage imageNamed:imageName];
}
NSArray<MWMRouterTransitStepInfo *> *buildRouteTransitSteps(NSArray<MWMRoutePoint *> *points) {
// Generate step info in format: (Segment 1 distance) (1) (Segment 2 distance) (2) ... (n-1) (Segment N distance).
NSMutableArray<MWMRouterTransitStepInfo *> * steps = [NSMutableArray arrayWithCapacity:[points count] * 2 - 1];
auto const numPoints = [points count];
for (int i = 0; i < numPoints - 1; i++) {
MWMRoutePoint* segmentStart = points[i];
MWMRoutePoint* segmentEnd = points[i + 1];
auto const distance = platform::Distance::CreateFormatted(
ms::DistanceOnEarth(segmentStart.latitude, segmentStart.longitude, segmentEnd.latitude, segmentEnd.longitude));
MWMRouterTransitStepInfo* segmentInfo = [[MWMRouterTransitStepInfo alloc] init];
segmentInfo.type = MWMRouterTransitTypeRuler;
segmentInfo.distance = @(distance.GetDistanceString().c_str());
segmentInfo.distanceUnits = @(distance.GetUnitsString().c_str());
steps[i * 2] = segmentInfo;
if (i < numPoints - 2) {
MWMRouterTransitStepInfo* stopInfo = [[MWMRouterTransitStepInfo alloc] init];
stopInfo.type = MWMRouterTransitTypeIntermediatePoint;
stopInfo.intermediateIndex = i;
steps[i * 2 + 1] = stopInfo;
}
}
return steps;
}
} // namespace
@interface MWMNavigationDashboardEntity ()
@property(copy, nonatomic, readwrite) NSArray<MWMRouterTransitStepInfo *> * transitSteps;
@property(copy, nonatomic, readwrite) NSString * distanceToTurn;
@property(copy, nonatomic, readwrite) NSString * streetName;
@property(copy, nonatomic, readwrite) NSString * targetDistance;
@property(copy, nonatomic, readwrite) NSString * targetUnits;
@property(copy, nonatomic, readwrite) NSString * turnUnits;
@property(nonatomic, readwrite) double speedLimitMps;
@property(nonatomic, readwrite) BOOL isValid;
@property(nonatomic, readwrite) CGFloat progress;
@property(nonatomic, readwrite) NSUInteger roundExitNumber;
@property(nonatomic, readwrite) NSUInteger timeToTarget;
@property(nonatomic, readwrite) UIImage * nextTurnImage;
@property(nonatomic, readwrite) UIImage * turnImage;
@property(nonatomic, readwrite) BOOL showEta;
@property(nonatomic, readwrite) BOOL isWalk;
@end
@implementation MWMNavigationDashboardEntity
- (NSString *)arrival
{
auto arrivalDate = [[NSDate date] dateByAddingTimeInterval:self.timeToTarget];
return [DateTimeFormatter dateStringFrom:arrivalDate
dateStyle:NSDateFormatterNoStyle
timeStyle:NSDateFormatterShortStyle];
}
+ (NSAttributedString *)estimateDot
{
auto attributes = @{
NSForegroundColorAttributeName: [UIColor blackSecondaryText],
NSFontAttributeName: [UIFont medium17]
};
return [[NSAttributedString alloc] initWithString:@" • " attributes:attributes];
}
- (NSAttributedString *)estimate {
NSDictionary * primaryAttributes = @{NSForegroundColorAttributeName: [UIColor blackPrimaryText], NSFontAttributeName: [UIFont medium17]};
NSDictionary * secondaryAttributes = @{NSForegroundColorAttributeName: [UIColor blackSecondaryText], NSFontAttributeName: [UIFont medium17]};
auto result = [[NSMutableAttributedString alloc] initWithString:@""];
if (self.showEta) {
NSString * eta = [DurationFormatter durationStringFromTimeInterval:self.timeToTarget];
[result appendAttributedString:[[NSMutableAttributedString alloc] initWithString:eta attributes:primaryAttributes]];
[result appendAttributedString:MWMNavigationDashboardEntity.estimateDot];
}
if (self.isWalk) {
UIFont * font = primaryAttributes[NSFontAttributeName];
auto textAttachment = [[NSTextAttachment alloc] init];
auto image = [UIImage imageNamed:@"ic_walk"];
textAttachment.image = image;
auto const height = font.lineHeight;
auto const y = height - image.size.height;
auto const width = image.size.width * height / image.size.height;
textAttachment.bounds = CGRectIntegral({{0, y}, {width, height}});
NSMutableAttributedString * attrStringWithImage =
[NSAttributedString attributedStringWithAttachment:textAttachment].mutableCopy;
[attrStringWithImage addAttributes:secondaryAttributes range:NSMakeRange(0, attrStringWithImage.length)];
[result appendAttributedString:attrStringWithImage];
}
auto target = [NSString stringWithFormat:@"%@ %@", self.targetDistance, self.targetUnits];
[result appendAttributedString:[[NSAttributedString alloc] initWithString:target attributes:secondaryAttributes]];
return result;
}
@end
@interface MWMRouterTransitStepInfo ()
- (instancetype)initWithStepInfo:(TransitStepInfo const &)info;
@end
@interface MWMNavigationDashboardManager ()
@property(copy, nonatomic) NSDictionary * etaAttributes;
@property(copy, nonatomic) NSDictionary * etaSecondaryAttributes;
@property(nonatomic) MWMNavigationDashboardEntity * entity;
- (void)onNavigationInfoUpdated;
@end
@implementation MWMNavigationDashboardManager (Entity)
- (void)updateFollowingInfo:(routing::FollowingInfo const &)info routePoints:(NSArray<MWMRoutePoint *> *)points type:(MWMRouterType)type {
if ([MWMRouter isRouteFinished]) {
[MWMRouter stopRouting];
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
return;
}
if (auto entity = self.entity) {
BOOL const showEta = (type != MWMRouterTypeRuler);
entity.isValid = YES;
entity.timeToTarget = info.m_time;
entity.targetDistance = @(info.m_distToTarget.GetDistanceString().c_str());
entity.targetUnits = @(info.m_distToTarget.GetUnitsString().c_str());
entity.progress = info.m_completionPercent;
entity.distanceToTurn = @(info.m_distToTurn.GetDistanceString().c_str());
entity.turnUnits = @(info.m_distToTurn.GetUnitsString().c_str());
entity.streetName = @(info.m_nextStreetName.c_str());
entity.speedLimitMps = info.m_speedLimitMps;
entity.isWalk = NO;
entity.showEta = showEta;
if (type == MWMRouterTypeRuler && [points count] > 2)
entity.transitSteps = buildRouteTransitSteps(points);
else
entity.transitSteps = [[NSArray alloc] init];
if (type == MWMRouterTypePedestrian) {
entity.turnImage = image(info.m_pedestrianTurn);
} else {
using namespace routing::turns;
CarDirection const turn = info.m_turn;
entity.turnImage = image(turn, false);
entity.nextTurnImage = image(info.m_nextTurn, true);
BOOL const isRound = turn == CarDirection::EnterRoundAbout || turn == CarDirection::StayOnRoundAbout ||
turn == CarDirection::LeaveRoundAbout;
if (isRound)
entity.roundExitNumber = info.m_exitNum;
else
entity.roundExitNumber = 0;
}
}
[self onNavigationInfoUpdated];
}
- (void)updateTransitInfo:(TransitRouteInfo const &)info {
if (auto entity = self.entity) {
entity.timeToTarget = info.m_totalTimeInSec;
entity.targetDistance = @(info.m_totalPedestrianDistanceStr.c_str());
entity.targetUnits = @(info.m_totalPedestrianUnitsSuffix.c_str());
entity.isValid = YES;
entity.isWalk = YES;
entity.showEta = YES;
NSMutableArray<MWMRouterTransitStepInfo *> * transitSteps = [NSMutableArray new];
for (auto const &stepInfo : info.m_steps)
[transitSteps addObject:[[MWMRouterTransitStepInfo alloc] initWithStepInfo:stepInfo]];
entity.transitSteps = transitSteps;
}
[self onNavigationInfoUpdated];
}
@end

View file

@ -0,0 +1,30 @@
typedef NS_ENUM(NSUInteger, MWMNavigationDashboardState) {
MWMNavigationDashboardStateHidden,
MWMNavigationDashboardStatePrepare,
MWMNavigationDashboardStatePlanning,
MWMNavigationDashboardStateError,
MWMNavigationDashboardStateReady,
MWMNavigationDashboardStateNavigation
};
@interface MWMNavigationDashboardManager : NSObject
+ (nonnull MWMNavigationDashboardManager *)sharedManager;
@property(nonatomic, readonly) MWMNavigationDashboardState state;
- (instancetype _Nonnull)init __attribute__((unavailable("init is not available")));
- (instancetype _Nonnull)initWithParentView:(UIView *_Nonnull)view;
- (void)setRouteBuilderProgress:(CGFloat)progress;
- (void)onRoutePrepare;
- (void)onRoutePlanning;
- (void)onRouteError:(NSString *_Nonnull)error;
- (void)onRouteReady:(BOOL)hasWarnings;
- (void)onRouteStart;
- (void)onRouteStop;
- (void)onRoutePointsUpdated;
+ (void)updateNavigationInfoAvailableArea:(CGRect)frame;
@end

View file

@ -0,0 +1,376 @@
#import "MWMNavigationDashboardManager.h"
#import "MWMMapViewControlsManager.h"
#import "MWMNavigationInfoView.h"
#import "MWMRoutePreview.h"
#import "MWMSearch.h"
#import "MapViewController.h"
#import "SwiftBridge.h"
namespace {
NSString *const kRoutePreviewIPhoneXibName = @"MWMiPhoneRoutePreview";
NSString *const kNavigationInfoViewXibName = @"MWMNavigationInfoView";
NSString *const kNavigationControlViewXibName = @"NavigationControlView";
} // namespace
@interface MWMMapViewControlsManager ()
@property(nonatomic) MWMNavigationDashboardManager *navigationManager;
@end
@interface MWMNavigationDashboardManager () <SearchOnMapManagerObserver, MWMRoutePreviewDelegate>
@property(copy, nonatomic) NSDictionary *etaAttributes;
@property(copy, nonatomic) NSDictionary *etaSecondaryAttributes;
@property(copy, nonatomic) NSString *errorMessage;
@property(nonatomic) IBOutlet MWMBaseRoutePreviewStatus *baseRoutePreviewStatus;
@property(nonatomic) IBOutlet MWMNavigationControlView *navigationControlView;
@property(nonatomic) IBOutlet MWMNavigationInfoView *navigationInfoView;
@property(nonatomic) IBOutlet MWMRoutePreview *routePreview;
@property(nonatomic) IBOutlet MWMTransportRoutePreviewStatus *transportRoutePreviewStatus;
@property(nonatomic) IBOutletCollection(MWMRouteStartButton) NSArray *goButtons;
@property(nonatomic) MWMNavigationDashboardEntity *entity;
@property(nonatomic) MWMRouteManagerTransitioningManager *routeManagerTransitioningManager;
@property(weak, nonatomic) IBOutlet UIButton *showRouteManagerButton;
@property(weak, nonatomic) IBOutlet UIView *goButtonsContainer;
@property(weak, nonatomic) UIView *ownerView;
@end
@implementation MWMNavigationDashboardManager
+ (MWMNavigationDashboardManager *)sharedManager {
return [MWMMapViewControlsManager manager].navigationManager;
}
- (instancetype)initWithParentView:(UIView *)view {
self = [super init];
if (self) {
_ownerView = view;
}
return self;
}
- (SearchOnMapManager *)searchManager {
return [[MapViewController sharedController] searchManager];
}
- (void)loadPreviewWithStatusBoxes {
[NSBundle.mainBundle loadNibNamed:kRoutePreviewIPhoneXibName owner:self options:nil];
auto ownerView = self.ownerView;
_baseRoutePreviewStatus.ownerView = ownerView;
_transportRoutePreviewStatus.ownerView = ownerView;
}
#pragma mark - MWMRoutePreview
- (void)setRouteBuilderProgress:(CGFloat)progress {
[self.routePreview router:[MWMRouter type] setProgress:progress / 100.];
}
#pragma mark - MWMNavigationGo
- (IBAction)routingStartTouchUpInside {
[MWMRouter startRouting];
}
- (void)updateGoButtonTitle {
NSString *title = L(@"p2p_start");
for (MWMRouteStartButton *button in self.goButtons)
[button setTitle:title forState:UIControlStateNormal];
}
- (void)onNavigationInfoUpdated {
auto entity = self.entity;
if (!entity.isValid)
return;
[_navigationInfoView onNavigationInfoUpdated:entity];
bool const isPublicTransport = [MWMRouter type] == MWMRouterTypePublicTransport;
bool const isRuler = [MWMRouter type] == MWMRouterTypeRuler;
if (isPublicTransport || isRuler)
[_transportRoutePreviewStatus onNavigationInfoUpdated:entity prependDistance:isRuler];
else
[_baseRoutePreviewStatus onNavigationInfoUpdated:entity];
[_navigationControlView onNavigationInfoUpdated:entity];
}
#pragma mark - On route updates
- (void)onRoutePrepare {
self.state = MWMNavigationDashboardStatePrepare;
self.routePreview.drivingOptionsState = MWMDrivingOptionsStateNone;
}
- (void)onRoutePlanning {
self.state = MWMNavigationDashboardStatePlanning;
self.routePreview.drivingOptionsState = MWMDrivingOptionsStateNone;
}
- (void)onRouteError:(NSString *)error {
self.errorMessage = error;
self.state = MWMNavigationDashboardStateError;
self.routePreview.drivingOptionsState =
[MWMRouter hasActiveDrivingOptions] ? MWMDrivingOptionsStateChange : MWMDrivingOptionsStateNone;
}
- (void)onRouteReady:(BOOL)hasWarnings {
if (self.state != MWMNavigationDashboardStateNavigation)
self.state = MWMNavigationDashboardStateReady;
MWMRouterType const routerType = [MWMRouter type];
if (routerType == MWMRouterTypePublicTransport || routerType == MWMRouterTypeRuler) {
// For Public Transport and Ruler modes, there are no road restrictions, so always hide the button.
self.routePreview.drivingOptionsState = MWMDrivingOptionsStateNone;
} else {
// For all other modes (Vehicle, Pedestrian, Bicycle), show the button.
if ([MWMRouter hasActiveDrivingOptions]) {
self.routePreview.drivingOptionsState = MWMDrivingOptionsStateChange;
} else {
self.routePreview.drivingOptionsState = MWMDrivingOptionsStateDefine;
}
}
}
- (void)onRoutePointsUpdated {
if (self.state == MWMNavigationDashboardStateHidden)
self.state = MWMNavigationDashboardStatePrepare;
[self.navigationInfoView updateToastView];
}
#pragma mark - State changes
- (void)stateHidden {
self.routePreview = nil;
self.navigationInfoView.state = MWMNavigationInfoViewStateHidden;
self.navigationInfoView = nil;
_navigationControlView.isVisible = NO;
_navigationControlView = nil;
[self.baseRoutePreviewStatus hide];
[_transportRoutePreviewStatus hide];
_transportRoutePreviewStatus = nil;
}
- (void)statePrepare {
self.navigationInfoView.state = MWMNavigationInfoViewStatePrepare;
if (self.searchManager.isSearching)
[self.navigationInfoView setSearchState:NavigationSearchState::MinimizedSearch animated:YES];
auto routePreview = self.routePreview;
[routePreview addToView:self.ownerView];
[routePreview statePrepare];
[routePreview selectRouter:[MWMRouter type]];
[self updateGoButtonTitle];
[self.baseRoutePreviewStatus hide];
[_transportRoutePreviewStatus hide];
for (MWMRouteStartButton *button in self.goButtons)
[button statePrepare];
}
- (void)statePlanning {
[self statePrepare];
[self.routePreview router:[MWMRouter type] setState:MWMCircularProgressStateSpinner];
[self setRouteBuilderProgress:0.];
}
- (void)stateError {
if (_state == MWMNavigationDashboardStateReady)
return;
NSAssert(_state == MWMNavigationDashboardStatePlanning, @"Invalid state change (error)");
auto routePreview = self.routePreview;
[routePreview router:[MWMRouter type] setState:MWMCircularProgressStateFailed];
[self updateGoButtonTitle];
[self.baseRoutePreviewStatus showErrorWithMessage:self.errorMessage];
for (MWMRouteStartButton *button in self.goButtons)
[button stateError];
}
- (void)stateReady {
// TODO: Here assert sometimes fires with _state = MWMNavigationDashboardStateReady, if app was stopped while navigating and then restarted.
// Also in ruler mode when new point is added by single tap on the map state MWMNavigationDashboardStatePlanning is skipped and we get _state = MWMNavigationDashboardStateReady.
NSAssert(_state == MWMNavigationDashboardStatePlanning || _state == MWMNavigationDashboardStateReady, @"Invalid state change (ready)");
[self setRouteBuilderProgress:100.];
[self updateGoButtonTitle];
bool const isTransport = ([MWMRouter type] == MWMRouterTypePublicTransport);
bool const isRuler = ([MWMRouter type] == MWMRouterTypeRuler);
if (isTransport || isRuler)
[self.transportRoutePreviewStatus showReady];
else
[self.baseRoutePreviewStatus showReady];
self.goButtonsContainer.hidden = isTransport || isRuler;
for (MWMRouteStartButton *button in self.goButtons)
{
if (isRuler)
[button stateHidden];
else
[button stateReady];
}
}
- (void)onRouteStart {
[MWMSearch clear];
[self.searchManager close];
self.state = MWMNavigationDashboardStateNavigation;
}
- (void)onRouteStop {
self.state = MWMNavigationDashboardStateHidden;
}
- (void)stateNavigation {
self.routePreview = nil;
self.navigationInfoView.state = MWMNavigationInfoViewStateNavigation;
self.navigationControlView.isVisible = YES;
[self.baseRoutePreviewStatus hide];
[_transportRoutePreviewStatus hide];
_transportRoutePreviewStatus = nil;
[self onNavigationInfoUpdated];
}
#pragma mark - MWMRoutePreviewStatus
- (IBAction)showRouteManager {
auto routeManagerViewModel = [[MWMRouteManagerViewModel alloc] init];
auto routeManager = [[MWMRouteManagerViewController alloc] initWithViewModel:routeManagerViewModel];
routeManager.modalPresentationStyle = UIModalPresentationCustom;
self.routeManagerTransitioningManager = [[MWMRouteManagerTransitioningManager alloc] init];
routeManager.transitioningDelegate = self.routeManagerTransitioningManager;
[[MapViewController sharedController] presentViewController:routeManager animated:YES completion:nil];
}
- (IBAction)saveRouteAsTrack:(id)sender {
[MWMFrameworkHelper saveRouteAsTrack];
[self.baseRoutePreviewStatus setRouteSaved:YES];
}
#pragma mark - MWMNavigationControlView
- (IBAction)ttsButtonAction {
BOOL const isEnabled = [MWMTextToSpeech tts].active;
[MWMTextToSpeech tts].active = !isEnabled;
}
- (IBAction)settingsButtonAction {
[[MapViewController sharedController] openSettings];
}
- (IBAction)stopRoutingButtonAction {
[MWMSearch clear];
[MWMRouter stopRouting];
[self.searchManager close];
}
#pragma mark - SearchOnMapManagerObserver
- (void)searchManagerWithDidChangeState:(SearchOnMapState)state {
switch (state) {
case SearchOnMapStateClosed:
[self.navigationInfoView setSearchState:NavigationSearchState::MinimizedNormal animated:YES];
break;
case SearchOnMapStateHidden:
case SearchOnMapStateSearching:
[self.navigationInfoView setSearchState:NavigationSearchState::MinimizedSearch animated:YES];
break;
}
}
#pragma mark - Available area
+ (void)updateNavigationInfoAvailableArea:(CGRect)frame {
[[self sharedManager] updateNavigationInfoAvailableArea:frame];
}
- (void)updateNavigationInfoAvailableArea:(CGRect)frame {
_navigationInfoView.availableArea = frame;
}
#pragma mark - Properties
- (void)setState:(MWMNavigationDashboardState)state {
if (state == MWMNavigationDashboardStateHidden)
[self.searchManager removeObserver:self];
else
[self.searchManager addObserver:self];
switch (state) {
case MWMNavigationDashboardStateHidden:
[self stateHidden];
break;
case MWMNavigationDashboardStatePrepare:
[self statePrepare];
break;
case MWMNavigationDashboardStatePlanning:
[self statePlanning];
break;
case MWMNavigationDashboardStateError:
[self stateError];
break;
case MWMNavigationDashboardStateReady:
[self stateReady];
break;
case MWMNavigationDashboardStateNavigation:
[self stateNavigation];
break;
}
_state = state;
[[MapViewController sharedController] updateStatusBarStyle];
// Restore bottom buttons only if they were not already hidden by tapping anywhere on an empty map.
if (!MWMMapViewControlsManager.manager.hidden)
BottomTabBarViewController.controller.isHidden = state != MWMNavigationDashboardStateHidden;
}
@synthesize routePreview = _routePreview;
- (MWMRoutePreview *)routePreview {
if (!_routePreview)
[self loadPreviewWithStatusBoxes];
return _routePreview;
}
- (void)setRoutePreview:(MWMRoutePreview *)routePreview {
if (routePreview == _routePreview)
return;
[_routePreview remove];
_routePreview = routePreview;
_routePreview.delegate = self;
}
- (MWMBaseRoutePreviewStatus *)baseRoutePreviewStatus {
if (!_baseRoutePreviewStatus)
[self loadPreviewWithStatusBoxes];
return _baseRoutePreviewStatus;
}
- (MWMTransportRoutePreviewStatus *)transportRoutePreviewStatus {
if (!_transportRoutePreviewStatus)
[self loadPreviewWithStatusBoxes];
return _transportRoutePreviewStatus;
}
- (MWMNavigationInfoView *)navigationInfoView {
if (!_navigationInfoView) {
[NSBundle.mainBundle loadNibNamed:kNavigationInfoViewXibName owner:self options:nil];
_navigationInfoView.state = MWMNavigationInfoViewStateHidden;
_navigationInfoView.ownerView = self.ownerView;
}
return _navigationInfoView;
}
- (MWMNavigationControlView *)navigationControlView {
if (!_navigationControlView) {
[NSBundle.mainBundle loadNibNamed:kNavigationControlViewXibName owner:self options:nil];
_navigationControlView.ownerView = self.ownerView;
}
return _navigationControlView;
}
- (MWMNavigationDashboardEntity *)entity {
if (!_entity)
_entity = [[MWMNavigationDashboardEntity alloc] init];
return _entity;
}
#pragma mark - MWMRoutePreviewDelegate
- (void)routePreviewDidPressDrivingOptions:(MWMRoutePreview *)routePreview {
[[MapViewController sharedController] openDrivingOptions];
}
@end

View file

@ -0,0 +1,34 @@
enum class NavigationSearchState
{
Maximized,
MinimizedNormal,
MinimizedSearch,
MinimizedGas,
MinimizedParking,
MinimizedEat,
MinimizedFood,
MinimizedATM
};
typedef NS_ENUM(NSUInteger, MWMNavigationInfoViewState) {
MWMNavigationInfoViewStateHidden,
MWMNavigationInfoViewStatePrepare,
MWMNavigationInfoViewStateNavigation
};
@class MWMNavigationDashboardEntity;
@interface MWMNavigationInfoView : UIView
@property(nonatomic, readonly) NavigationSearchState searchState;
@property(nonatomic) MWMNavigationInfoViewState state;
@property(weak, nonatomic) UIView * ownerView;
@property(nonatomic) CGRect availableArea;
- (void)setSearchState:(NavigationSearchState)searchState animated:(BOOL)animated;
- (void)onNavigationInfoUpdated:(MWMNavigationDashboardEntity *)info;
- (void)updateToastView;
@end

View file

@ -0,0 +1,490 @@
#import "MWMNavigationInfoView.h"
#import "CLLocation+Mercator.h"
#import "MWMButton.h"
#import "MWMLocationHelpers.h"
#import "MWMLocationManager.h"
#import "MWMLocationObserver.h"
#import "MWMMapViewControlsCommon.h"
#import "MWMSearch.h"
#import "MapViewController.h"
#import "SwiftBridge.h"
#import "UIImageView+Coloring.h"
#include "geometry/angles.hpp"
namespace
{
CGFloat constexpr kTurnsiPhoneWidth = 96;
CGFloat constexpr kTurnsiPadWidth = 140;
CGFloat constexpr kSearchButtonsViewHeightPortrait = 200;
CGFloat constexpr kSearchButtonsViewWidthPortrait = 200;
CGFloat constexpr kSearchButtonsViewHeightLandscape = 56;
CGFloat constexpr kSearchButtonsViewWidthLandscape = 286;
CGFloat constexpr kSearchButtonsSideSize = 44;
CGFloat constexpr kBaseTurnsTopOffset = 28;
CGFloat constexpr kShiftedTurnsTopOffset = 8;
NSTimeInterval constexpr kCollapseSearchTimeout = 5.0;
std::map<NavigationSearchState, NSString *> const kSearchStateButtonImageNames{
{NavigationSearchState::Maximized, @"ic_routing_search"},
{NavigationSearchState::MinimizedNormal, @"ic_routing_search"},
{NavigationSearchState::MinimizedSearch, @"ic_routing_search_off"},
{NavigationSearchState::MinimizedGas, @"ic_routing_fuel_off"},
{NavigationSearchState::MinimizedParking, @"ic_routing_parking_off"},
{NavigationSearchState::MinimizedEat, @"ic_routing_eat_off"},
{NavigationSearchState::MinimizedFood, @"ic_routing_food_off"},
{NavigationSearchState::MinimizedATM, @"ic_routing_atm_off"}};
std::map<NavigationSearchState, NSString *> const kSearchButtonRequest{
{NavigationSearchState::MinimizedGas, L(@"category_fuel")},
{NavigationSearchState::MinimizedParking, L(@"category_parking")},
{NavigationSearchState::MinimizedEat, L(@"category_eat")},
{NavigationSearchState::MinimizedFood, L(@"category_food")},
{NavigationSearchState::MinimizedATM, L(@"category_atm")}};
BOOL defaultOrientation(CGSize const &size) {
CGSize const &mapViewSize = [MapViewController sharedController].view.frame.size;
CGFloat const minWidth = MIN(mapViewSize.width, mapViewSize.height);
return IPAD || (size.height > size.width && size.width >= minWidth);
}
} // namespace
@interface MWMNavigationInfoView () <MWMLocationObserver>
@property(weak, nonatomic) IBOutlet UIView *streetNameView;
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *streetNameTopOffsetConstraint;
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *streetNameViewHideOffset;
@property(weak, nonatomic) IBOutlet UILabel *streetNameLabel;
@property(weak, nonatomic) IBOutlet UIView *turnsView;
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *turnsViewHideOffset;
@property(weak, nonatomic) IBOutlet UIImageView *nextTurnImageView;
@property(weak, nonatomic) IBOutlet UILabel *roundTurnLabel;
@property(weak, nonatomic) IBOutlet UILabel *distanceToNextTurnLabel;
@property(weak, nonatomic) IBOutlet UIView *secondTurnView;
@property(weak, nonatomic) IBOutlet UIImageView *secondTurnImageView;
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *turnsWidth;
@property(weak, nonatomic) IBOutlet UIView *searchButtonsView;
@property(weak, nonatomic) IBOutlet MWMButton *searchMainButton;
@property(weak, nonatomic) IBOutlet MWMButton *bookmarksButton;
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *searchButtonsViewHeight;
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *searchButtonsViewWidth;
@property(nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *searchLandscapeConstraints;
@property(nonatomic) IBOutletCollection(UIButton) NSArray *searchButtons;
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *searchButtonsSideSize;
@property(weak, nonatomic) IBOutlet MWMButton *searchGasButton;
@property(weak, nonatomic) IBOutlet MWMButton *searchParkingButton;
@property(weak, nonatomic) IBOutlet MWMButton *searchEatButton;
@property(weak, nonatomic) IBOutlet MWMButton *searchFoodButton;
@property(weak, nonatomic) IBOutlet MWMButton *searchATMButton;
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *turnsTopOffset;
@property(weak, nonatomic) IBOutlet MWMNavigationAddPointToastView *toastView;
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *toastViewHideOffset;
@property(nonatomic, readwrite) NavigationSearchState searchState;
@property(nonatomic) BOOL isVisible;
@property(weak, nonatomic) MWMNavigationDashboardEntity *navigationInfo;
@property(nonatomic) BOOL hasLocation;
@property(nonatomic) NSLayoutConstraint *topConstraint;
@property(nonatomic) NSLayoutConstraint *leftConstraint;
@property(nonatomic) NSLayoutConstraint *widthConstraint;
@property(nonatomic) NSLayoutConstraint *heightConstraint;
@end
@implementation MWMNavigationInfoView
- (void)updateToastView {
// -S-F-L -> Start
// -S-F+L -> Finish
// -S+F-L -> Start
// -S+F+L -> Start + Use
// +S-F-L -> Finish
// +S-F+L -> Finish
// +S+F-L -> Hide
// +S+F+L -> Hide
BOOL const hasStart = ([MWMRouter startPoint] != nil);
BOOL const hasFinish = ([MWMRouter finishPoint] != nil);
self.hasLocation = ([MWMLocationManager lastLocation] != nil);
if (hasStart && hasFinish) {
[self setToastViewHidden:YES];
return;
}
[self setToastViewHidden:NO];
auto toastView = self.toastView;
if (hasStart) {
[toastView configWithIsStart:NO withLocationButton:NO];
return;
}
if (hasFinish) {
[toastView configWithIsStart:YES withLocationButton:self.hasLocation];
return;
}
if (self.hasLocation)
[toastView configWithIsStart:NO withLocationButton:NO];
else
[toastView configWithIsStart:YES withLocationButton:NO];
}
- (SearchOnMapManager *)searchManager {
return [MapViewController sharedController].searchManager;
}
- (IBAction)openSearch {
BOOL const isStart = self.toastView.isStart;
[self.searchManager setRoutingTooltip:
isStart ? SearchOnMapRoutingTooltipSearchStart : SearchOnMapRoutingTooltipSearchFinish ];
[self.searchManager startSearchingWithIsRouting:YES];
}
- (IBAction)addLocationRoutePoint {
NSAssert(![MWMRouter startPoint], @"Action button is active while start point is available");
NSAssert([MWMLocationManager lastLocation], @"Action button is active while my location is not available");
[MWMRouter buildFromPoint:[[MWMRoutePoint alloc] initWithLastLocationAndType:MWMRoutePointTypeStart
intermediateIndex:0]
bestRouter:NO];
}
#pragma mark - Search
- (IBAction)searchMainButtonTouchUpInside {
switch (self.searchState) {
case NavigationSearchState::Maximized:
[self.searchManager startSearchingWithIsRouting:YES];
[self setSearchState:NavigationSearchState::MinimizedNormal animated:YES];
break;
case NavigationSearchState::MinimizedNormal:
if (self.state == MWMNavigationInfoViewStatePrepare) {
[self.searchManager startSearchingWithIsRouting:YES];
} else {
[self setSearchState:NavigationSearchState::Maximized animated:YES];
}
break;
case NavigationSearchState::MinimizedSearch:
case NavigationSearchState::MinimizedGas:
case NavigationSearchState::MinimizedParking:
case NavigationSearchState::MinimizedEat:
case NavigationSearchState::MinimizedFood:
case NavigationSearchState::MinimizedATM:
[MWMSearch clear];
[self.searchManager hide];
[self setSearchState:NavigationSearchState::MinimizedNormal animated:YES];
break;
}
}
- (IBAction)searchButtonTouchUpInside:(MWMButton *)sender {
auto const body = ^(NavigationSearchState state) {
NSString * text = [kSearchButtonRequest.at(state) stringByAppendingString:@" "];
NSString * locale = [[AppInfo sharedInfo] languageId];
// Category request from navigation search wheel.
SearchQuery * query = [[SearchQuery alloc] init:text locale:locale source:SearchTextSourceCategory];
[MWMSearch searchQuery:query];
[self setSearchState:state animated:YES];
};
if (sender == self.searchGasButton)
body(NavigationSearchState::MinimizedGas);
else if (sender == self.searchParkingButton)
body(NavigationSearchState::MinimizedParking);
else if (sender == self.searchEatButton)
body(NavigationSearchState::MinimizedEat);
else if (sender == self.searchFoodButton)
body(NavigationSearchState::MinimizedFood);
else if (sender == self.searchATMButton)
body(NavigationSearchState::MinimizedATM);
}
- (IBAction)bookmarksButtonTouchUpInside {
[[MapViewController sharedController].bookmarksCoordinator open];
}
- (void)collapseSearchOnTimer {
[self setSearchState:NavigationSearchState::MinimizedNormal animated:YES];
}
- (void)layoutSearch {
BOOL const defaultView = defaultOrientation(self.availableArea.size);
CGFloat alpha = 0;
CGFloat searchButtonsSideSize = 0;
self.searchButtonsViewWidth.constant = 0;
self.searchButtonsViewHeight.constant = 0;
if (self.searchState == NavigationSearchState::Maximized) {
alpha = 1;
searchButtonsSideSize = kSearchButtonsSideSize;
self.searchButtonsViewWidth.constant =
defaultView ? kSearchButtonsViewWidthPortrait : kSearchButtonsViewWidthLandscape;
self.searchButtonsViewHeight.constant =
defaultView ? kSearchButtonsViewHeightPortrait : kSearchButtonsViewHeightLandscape;
}
for (UIButton *searchButton in self.searchButtons)
searchButton.alpha = alpha;
UILayoutPriority const priority = (defaultView ? UILayoutPriorityDefaultLow : UILayoutPriorityDefaultHigh);
for (NSLayoutConstraint *constraint in self.searchLandscapeConstraints)
constraint.priority = priority;
self.searchButtonsSideSize.constant = searchButtonsSideSize;
}
#pragma mark - MWMNavigationDashboardManager
- (void)onNavigationInfoUpdated:(MWMNavigationDashboardEntity *)info {
self.navigationInfo = info;
if (self.state != MWMNavigationInfoViewStateNavigation)
return;
if (info.streetName.length != 0) {
[self setStreetNameVisible:YES];
self.streetNameLabel.text = info.streetName;
} else {
[self setStreetNameVisible:NO];
}
if (info.turnImage) {
[self setTurnsViewVisible:YES];
self.nextTurnImageView.image = info.turnImage;
if (info.roundExitNumber == 0) {
self.roundTurnLabel.hidden = YES;
} else {
self.roundTurnLabel.hidden = NO;
self.roundTurnLabel.text = @(info.roundExitNumber).stringValue;
}
NSDictionary *turnNumberAttributes =
@{NSForegroundColorAttributeName: [UIColor white], NSFontAttributeName: IPAD ? [UIFont bold36] : [UIFont bold28]};
NSDictionary *turnLegendAttributes =
@{NSForegroundColorAttributeName: [UIColor white], NSFontAttributeName: IPAD ? [UIFont bold24] : [UIFont bold16]};
NSMutableAttributedString *distance = [[NSMutableAttributedString alloc] initWithString:info.distanceToTurn
attributes:turnNumberAttributes];
[distance appendAttributedString:[[NSAttributedString alloc]
initWithString:[NSString stringWithFormat:@" %@", info.turnUnits]
attributes:turnLegendAttributes]];
self.distanceToNextTurnLabel.attributedText = distance;
if (info.nextTurnImage) {
self.secondTurnView.hidden = NO;
self.secondTurnImageView.image = info.nextTurnImage;
} else {
self.secondTurnView.hidden = YES;
}
} else {
[self setTurnsViewVisible:NO];
}
[self setNeedsLayout];
}
#pragma mark - MWMLocationObserver
- (void)onLocationUpdate:(CLLocation *)location {
BOOL const hasLocation = ([MWMLocationManager lastLocation] != nil);
if (self.hasLocation != hasLocation)
[self updateToastView];
}
#pragma mark - SolidTouchView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (self.searchState == NavigationSearchState::Maximized)
return;
[super touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if (self.searchState == NavigationSearchState::Maximized)
return;
[super touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (self.searchState == NavigationSearchState::Maximized)
[self setSearchState:NavigationSearchState::MinimizedNormal animated:YES];
else
[super touchesEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
if (self.searchState == NavigationSearchState::Maximized)
[self setSearchState:NavigationSearchState::MinimizedNormal animated:YES];
else
[super touchesCancelled:touches withEvent:event];
}
- (void)configLayout {
UIView *ov = self.superview;
self.translatesAutoresizingMaskIntoConstraints = NO;
self.topConstraint = [self.topAnchor constraintEqualToAnchor:ov.topAnchor];
self.topConstraint.active = YES;
self.leftConstraint = [self.leadingAnchor constraintEqualToAnchor:ov.leadingAnchor];
self.leftConstraint.active = YES;
self.widthConstraint = [self.widthAnchor constraintEqualToConstant:ov.frame.size.width];
self.widthConstraint.active = YES;
self.heightConstraint = [self.heightAnchor constraintEqualToConstant:ov.frame.size.height];
self.heightConstraint.active = YES;
self.streetNameTopOffsetConstraint.constant = self.additionalStreetNameTopOffset;
}
// Additional spacing for devices with a small top safe area (such as SE or when the device is in landscape mode).
- (CGFloat)additionalStreetNameTopOffset {
return MapsAppDelegate.theApp.window.safeAreaInsets.top <= 20 ? 10 : 0;;
}
- (void)refreshLayout {
dispatch_async(dispatch_get_main_queue(), ^{
if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation))
self.streetNameLabel.numberOfLines = 1;
else
self.streetNameLabel.numberOfLines = 2;
auto const availableArea = self.availableArea;
[self animateConstraintsWithAnimations:^{
self.topConstraint.constant = availableArea.origin.y;
self.leftConstraint.constant = availableArea.origin.x + kViewControlsOffsetToBounds;
self.widthConstraint.constant = availableArea.size.width - kViewControlsOffsetToBounds;
self.heightConstraint.constant = availableArea.size.height;
[self layoutSearch];
self.turnsTopOffset.constant = availableArea.origin.y > 0 ? kShiftedTurnsTopOffset : kBaseTurnsTopOffset;
self.searchButtonsView.layer.cornerRadius =
(defaultOrientation(availableArea.size) ? kSearchButtonsViewHeightPortrait
: kSearchButtonsViewHeightLandscape) /
2;
if (@available(iOS 13.0, *)) {
self.searchButtonsView.layer.cornerCurve = kCACornerCurveContinuous;
}
self.streetNameTopOffsetConstraint.constant = self.additionalStreetNameTopOffset;
}];
});
}
#pragma mark - Properties
- (void)setAvailableArea:(CGRect)availableArea {
if (CGRectEqualToRect(_availableArea, availableArea))
return;
_availableArea = availableArea;
[self refreshLayout];
}
- (void)setSearchState:(NavigationSearchState)searchState animated:(BOOL)animated {
self.searchState = searchState;
auto block = ^{
[self layoutSearch];
[self layoutIfNeeded];
};
if (animated) {
[self layoutIfNeeded];
[UIView animateWithDuration:kDefaultAnimationDuration animations:block];
} else {
block();
}
SEL const collapseSelector = @selector(collapseSearchOnTimer);
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:collapseSelector object:self];
if (self.searchState == NavigationSearchState::Maximized) {
[self.superview bringSubviewToFront:self];
[self performSelector:collapseSelector withObject:self afterDelay:kCollapseSearchTimeout];
} else {
[self.superview sendSubviewToBack:self];
}
}
- (void)setSearchState:(NavigationSearchState)searchState {
_searchState = searchState;
self.searchMainButton.imageName = kSearchStateButtonImageNames.at(searchState);
}
- (void)setState:(MWMNavigationInfoViewState)state {
if (_state == state)
return;
_state = state;
switch (state) {
case MWMNavigationInfoViewStateHidden:
self.isVisible = NO;
[self setToastViewHidden:YES];
[MWMLocationManager removeObserver:self];
break;
case MWMNavigationInfoViewStateNavigation:
self.isVisible = YES;
if ([MWMRouter type] == MWMRouterTypePedestrian)
[MWMLocationManager addObserver:self];
else
[MWMLocationManager removeObserver:self];
break;
case MWMNavigationInfoViewStatePrepare:
self.isVisible = YES;
[self setStreetNameVisible:NO];
[self setTurnsViewVisible:NO];
[MWMLocationManager addObserver:self];
break;
}
}
- (void)setStreetNameVisible:(BOOL)isVisible {
self.streetNameView.hidden = !isVisible;
self.streetNameViewHideOffset.priority = isVisible ? UILayoutPriorityDefaultLow : UILayoutPriorityDefaultHigh;
}
- (void)setTurnsViewVisible:(BOOL)isVisible {
self.turnsView.hidden = !isVisible;
self.turnsViewHideOffset.priority = isVisible ? UILayoutPriorityDefaultLow : UILayoutPriorityDefaultHigh;
}
- (void)setIsVisible:(BOOL)isVisible {
if (_isVisible == isVisible)
return;
_isVisible = isVisible;
[self setNeedsLayout];
if (isVisible) {
[self setSearchState:NavigationSearchState::MinimizedNormal animated:NO];
self.turnsWidth.constant = IPAD ? kTurnsiPadWidth : kTurnsiPhoneWidth;
UIView *sv = self.ownerView;
NSAssert(sv != nil, @"Superview can't be nil");
if ([sv.subviews containsObject:self])
return;
[sv insertSubview:self atIndex:0];
[self configLayout];
}
[UIView animateWithDuration:kDefaultAnimationDuration
animations:^{
[self layoutIfNeeded];
}
completion:^(BOOL finished) {
if (!isVisible)
[self removeFromSuperview];
}];
}
- (void)setToastViewHidden:(BOOL)hidden {
if (!hidden)
self.toastView.hidden = NO;
[self setNeedsLayout];
self.toastViewHideOffset.priority = (hidden ? UILayoutPriorityDefaultHigh : UILayoutPriorityDefaultLow);
[UIView animateWithDuration:kDefaultAnimationDuration
animations:^{
[self layoutIfNeeded];
}
completion:^(BOOL finished) {
if (hidden)
self.toastView.hidden = YES;
}];
}
// MARK: Update Theme
- (void)applyTheme {
[self setSearchState:_searchState];
}
@end

View file

@ -0,0 +1,449 @@
<?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" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
<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="MWMNavigationDashboardManager">
<connections>
<outlet property="navigationInfoView" destination="iN0-l3-epB" id="jfj-m1-I85"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="MWMNavigationInfoView">
<rect key="frame" x="0.0" y="0.0" width="1024" height="1366"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view opaque="NO" multipleTouchEnabled="YES" contentMode="center" translatesAutoresizingMaskIntoConstraints="NO" id="Tzc-l6-Vvt" customClass="MWMNavigationAddPointToastView">
<rect key="frame" x="0.0" y="1366" width="1024" height="48"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jmn-hv-OlG">
<rect key="frame" x="-100" y="0.0" width="1224" height="100"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="100" id="6em-0e-zym"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
</userDefinedRuntimeAttributes>
</view>
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="240" verticalHuggingPriority="240" contentHorizontalAlignment="left" contentVerticalAlignment="center" lineBreakMode="wordWrap" translatesAutoresizingMaskIntoConstraints="NO" id="OJ3-VB-hqA">
<rect key="frame" x="0.0" y="0.0" width="971" height="48"/>
<constraints>
<constraint firstAttribute="height" constant="48" id="oPW-KZ-iX3"/>
</constraints>
<inset key="contentEdgeInsets" minX="12" minY="0.0" maxX="0.0" maxY="0.0"/>
<inset key="titleEdgeInsets" minX="12" minY="0.0" maxX="0.0" maxY="0.0"/>
<state key="normal" title="Button" image="ic_search_toast"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="FlatNormalTransButton:MWMBlue:regular16"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="openSearch" destination="iN0-l3-epB" eventType="touchUpInside" id="rRN-4C-luO"/>
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="pWX-NL-dy3">
<rect key="frame" x="971" y="8" width="1" height="32"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" constant="1" id="5sH-RH-vPb"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
</userDefinedRuntimeAttributes>
</view>
<button opaque="NO" contentMode="scaleToFill" horizontalCompressionResistancePriority="900" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="wordWrap" translatesAutoresizingMaskIntoConstraints="NO" id="fh6-Nu-aYU">
<rect key="frame" x="972" y="0.0" width="52" height="48"/>
<accessibility key="accessibilityConfiguration" identifier="routePlaningButtomPanelUseLocation"/>
<constraints>
<constraint firstAttribute="height" constant="48" id="1d3-V3-ab6"/>
<constraint firstAttribute="width" secondItem="fh6-Nu-aYU" secondAttribute="height" multiplier="1:1" priority="750" id="Y41-YU-X4s"/>
</constraints>
<inset key="contentEdgeInsets" minX="16" minY="0.0" maxX="16" maxY="0.0"/>
<state key="normal" image="ic_get_position"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMBlue"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="addLocationRoutePoint" destination="iN0-l3-epB" eventType="touchUpInside" id="4bf-b1-JqQ"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<accessibility key="accessibilityConfiguration" identifier="routePlaningButtomPanel"/>
<constraints>
<constraint firstItem="OJ3-VB-hqA" firstAttribute="leading" secondItem="Tzc-l6-Vvt" secondAttribute="leading" id="Dhd-ED-QZD"/>
<constraint firstAttribute="height" constant="48" id="E4J-Rq-3CZ"/>
<constraint firstItem="fh6-Nu-aYU" firstAttribute="leading" secondItem="pWX-NL-dy3" secondAttribute="trailing" id="GWY-Hv-xMd"/>
<constraint firstAttribute="trailing" secondItem="jmn-hv-OlG" secondAttribute="trailing" constant="-100" id="IwI-ua-fZg"/>
<constraint firstItem="pWX-NL-dy3" firstAttribute="top" secondItem="fh6-Nu-aYU" secondAttribute="top" constant="8" id="QuG-5X-6WM"/>
<constraint firstItem="jmn-hv-OlG" firstAttribute="leading" secondItem="Tzc-l6-Vvt" secondAttribute="leading" constant="-100" id="Vc5-xh-8nV"/>
<constraint firstItem="OJ3-VB-hqA" firstAttribute="top" secondItem="Tzc-l6-Vvt" secondAttribute="top" id="dYq-JZ-UgV"/>
<constraint firstItem="fh6-Nu-aYU" firstAttribute="top" secondItem="Tzc-l6-Vvt" secondAttribute="top" id="kaA-3q-cFB"/>
<constraint firstItem="pWX-NL-dy3" firstAttribute="leading" secondItem="OJ3-VB-hqA" secondAttribute="trailing" priority="999" id="ngH-HH-FLo"/>
<constraint firstAttribute="trailing" secondItem="fh6-Nu-aYU" secondAttribute="trailing" id="o3O-CY-AKb"/>
<constraint firstItem="jmn-hv-OlG" firstAttribute="top" secondItem="Tzc-l6-Vvt" secondAttribute="top" id="oKf-hI-i5g"/>
<constraint firstItem="pWX-NL-dy3" firstAttribute="bottom" secondItem="fh6-Nu-aYU" secondAttribute="bottom" constant="-8" id="whl-Po-qqC"/>
</constraints>
<connections>
<outlet property="actionButton" destination="OJ3-VB-hqA" id="MW0-KG-h24"/>
<outlet property="extraBottomBackground" destination="jmn-hv-OlG" id="D8x-Ph-Pm5"/>
<outlet property="locationButton" destination="fh6-Nu-aYU" id="2dI-Ab-cCH"/>
<outlet property="separator" destination="pWX-NL-dy3" id="DC7-YE-KjK"/>
</connections>
</view>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="YYv-pG-Wkw" userLabel="Street name" customClass="NavigationStreetNameView" customModule="CoMaps" customModuleProvider="target">
<rect key="frame" x="-100" y="0.0" width="1224" height="65"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="WID-Um-Va6" userLabel="BackgroundView">
<rect key="frame" x="-100" y="-100" width="1424" height="165"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="StreetNameBackgroundView"/>
</userDefinedRuntimeAttributes>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="600" verticalCompressionResistancePriority="600" text="Ленинградский проспект" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="ShI-bz-5g8">
<rect key="frame" x="204" y="24" width="900" height="29"/>
<accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" staticText="YES" notEnabled="YES"/>
</accessibility>
<fontDescription key="fontDescription" type="boldSystem" pointSize="24"/>
<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="blackPrimaryText"/>
</userDefinedRuntimeAttributes>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" notEnabled="YES"/>
</accessibility>
<constraints>
<constraint firstAttribute="bottom" secondItem="WID-Um-Va6" secondAttribute="bottom" id="5ox-Cb-hJc"/>
<constraint firstItem="ShI-bz-5g8" firstAttribute="top" secondItem="YYv-pG-Wkw" secondAttribute="top" id="KK1-dA-hII"/>
<constraint firstAttribute="trailing" secondItem="WID-Um-Va6" secondAttribute="trailing" constant="-100" id="f4N-aC-XO3"/>
<constraint firstItem="ShI-bz-5g8" firstAttribute="leading" secondItem="YYv-pG-Wkw" secondAttribute="leading" priority="300" constant="112" id="nVP-3U-KG2"/>
<constraint firstItem="WID-Um-Va6" firstAttribute="leading" secondItem="YYv-pG-Wkw" secondAttribute="leading" constant="-100" id="s8Q-4n-55U"/>
<constraint firstItem="WID-Um-Va6" firstAttribute="top" secondItem="YYv-pG-Wkw" secondAttribute="top" constant="-100" id="spZ-F3-yP2"/>
<constraint firstAttribute="bottom" secondItem="ShI-bz-5g8" secondAttribute="bottom" constant="10" id="w0r-Ip-f9E"/>
</constraints>
</view>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Aa6-N8-acP" userLabel="Turns" customClass="NavigationTurnsView" customModule="CoMaps" customModuleProvider="target">
<rect key="frame" x="0.0" y="28" width="96" height="153"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wt8-EO-MKz" userLabel="First turn" customClass="SolidTouchView">
<rect key="frame" x="0.0" y="0.0" width="96" height="117"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tnR-CA-hkI">
<rect key="frame" x="12" y="8" width="72" height="72"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="AJe-8N-rpk">
<rect key="frame" x="0.0" y="0.0" width="72" height="72"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMWhite"/>
</userDefinedRuntimeAttributes>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="1" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="teB-QQ-oib">
<rect key="frame" x="30" y="21.5" width="12" height="29"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="24"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="whiteText"/>
</userDefinedRuntimeAttributes>
</label>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="teB-QQ-oib" firstAttribute="centerX" secondItem="AJe-8N-rpk" secondAttribute="centerX" id="00j-DX-kej"/>
<constraint firstItem="AJe-8N-rpk" firstAttribute="leading" secondItem="tnR-CA-hkI" secondAttribute="leading" id="Tae-YV-lwB"/>
<constraint firstAttribute="bottom" secondItem="AJe-8N-rpk" secondAttribute="bottom" id="Wwf-8B-PvC"/>
<constraint firstItem="teB-QQ-oib" firstAttribute="centerY" secondItem="AJe-8N-rpk" secondAttribute="centerY" id="YC1-n7-f1q"/>
<constraint firstAttribute="width" secondItem="tnR-CA-hkI" secondAttribute="height" multiplier="1:1" id="aPD-g3-T2h"/>
<constraint firstItem="AJe-8N-rpk" firstAttribute="top" secondItem="tnR-CA-hkI" secondAttribute="top" id="uM5-Zp-UMt"/>
<constraint firstAttribute="trailing" secondItem="AJe-8N-rpk" secondAttribute="trailing" id="yRy-fp-msB"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="810" text="0000 ft" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="KuR-1J-VI2">
<rect key="frame" x="4" y="84" width="88" height="29"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="24"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="whiteText"/>
</userDefinedRuntimeAttributes>
</label>
</subviews>
<color key="backgroundColor" red="0.11764705882352941" green="0.58823529411764708" blue="0.94117647058823528" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="tnR-CA-hkI" secondAttribute="trailing" constant="12" id="0ZA-zO-Xxs"/>
<constraint firstAttribute="bottom" secondItem="KuR-1J-VI2" secondAttribute="bottom" constant="4" id="JFV-hc-Pil"/>
<constraint firstItem="tnR-CA-hkI" firstAttribute="leading" secondItem="wt8-EO-MKz" secondAttribute="leading" constant="12" id="OYn-Nn-Osu"/>
<constraint firstAttribute="trailing" secondItem="KuR-1J-VI2" secondAttribute="trailing" constant="4" id="W2n-vT-neW"/>
<constraint firstItem="tnR-CA-hkI" firstAttribute="top" secondItem="wt8-EO-MKz" secondAttribute="top" constant="8" id="YaL-ra-ytk"/>
<constraint firstItem="KuR-1J-VI2" firstAttribute="leading" secondItem="wt8-EO-MKz" secondAttribute="leading" constant="4" id="mx0-ex-eQt"/>
<constraint firstItem="KuR-1J-VI2" firstAttribute="top" secondItem="tnR-CA-hkI" secondAttribute="bottom" constant="4" id="y5u-lH-Ytt"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="FirstTurnView"/>
</userDefinedRuntimeAttributes>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8Zu-Ff-6p2" userLabel="Second turn" customClass="SolidTouchView">
<rect key="frame" x="0.0" y="121" width="96" height="32"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="CKi-sy-dNO">
<rect key="frame" x="36" y="4" width="24" height="24"/>
<constraints>
<constraint firstAttribute="width" constant="24" id="4ye-1W-bU2"/>
<constraint firstAttribute="height" constant="24" id="Fxo-9b-MUJ"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMBlack"/>
</userDefinedRuntimeAttributes>
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.80000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="CKi-sy-dNO" firstAttribute="centerX" secondItem="8Zu-Ff-6p2" secondAttribute="centerX" id="dmg-3w-VeP"/>
<constraint firstItem="CKi-sy-dNO" firstAttribute="centerY" secondItem="8Zu-Ff-6p2" secondAttribute="centerY" id="kDl-Jt-3ST"/>
<constraint firstAttribute="height" constant="32" id="n57-ET-oyc"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="SecondTurnView"/>
</userDefinedRuntimeAttributes>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" notEnabled="YES"/>
</accessibility>
<constraints>
<constraint firstAttribute="width" constant="96" id="6Ja-Xp-g8h"/>
<constraint firstAttribute="trailing" secondItem="wt8-EO-MKz" secondAttribute="trailing" id="AQV-lK-vNF"/>
<constraint firstItem="wt8-EO-MKz" firstAttribute="leading" secondItem="Aa6-N8-acP" secondAttribute="leading" id="KMA-Ox-aG4"/>
<constraint firstItem="8Zu-Ff-6p2" firstAttribute="leading" secondItem="Aa6-N8-acP" secondAttribute="leading" id="Qqb-Gy-k4c"/>
<constraint firstAttribute="trailing" secondItem="8Zu-Ff-6p2" secondAttribute="trailing" id="Zim-Gu-q0F"/>
<constraint firstAttribute="bottom" secondItem="8Zu-Ff-6p2" secondAttribute="bottom" id="lqa-St-hm1"/>
<constraint firstItem="8Zu-Ff-6p2" firstAttribute="top" secondItem="wt8-EO-MKz" secondAttribute="bottom" constant="4" id="r3K-xx-PSf"/>
<constraint firstItem="wt8-EO-MKz" firstAttribute="top" secondItem="Aa6-N8-acP" secondAttribute="top" id="v5F-4v-Sfr"/>
</constraints>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="XMv-au-OYf" customClass="MWMButton">
<rect key="frame" x="0.0" y="623" width="56" height="56"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="56" id="ehZ-Oc-96g"/>
<constraint firstAttribute="height" constant="56" id="p3i-Ly-sTx"/>
</constraints>
<state key="normal" image="ic_routing_search_light"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="ButtonMapBookmarks"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="bookmarksButtonTouchUpInside" destination="iN0-l3-epB" eventType="touchUpInside" id="YwB-gA-E8p"/>
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TMW-aw-1RT">
<rect key="frame" x="-72" y="615" width="200" height="200"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2Bv-7H-IZt" userLabel="Gas" customClass="MWMButton">
<rect key="frame" x="78" y="14" width="44" height="44"/>
<constraints>
<constraint firstAttribute="width" priority="750" constant="44" id="KKe-fH-h2n"/>
<constraint firstAttribute="width" secondItem="2Bv-7H-IZt" secondAttribute="height" multiplier="1:1" id="mN1-Tu-Mon"/>
</constraints>
<state key="normal" image="ic_routing_fuel_on"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMBlack"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="searchButtonTouchUpInside:" destination="iN0-l3-epB" eventType="touchUpInside" id="14l-BK-iDr"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="RP3-01-Pj7" userLabel="Parking" customClass="MWMButton">
<rect key="frame" x="122" y="34" width="44" height="44"/>
<state key="normal" image="ic_routing_parking_on"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMBlack"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="searchButtonTouchUpInside:" destination="iN0-l3-epB" eventType="touchUpInside" id="uiN-0q-kol"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GcK-FI-VXw" userLabel="Eat" customClass="MWMButton">
<rect key="frame" x="142" y="78" width="44" height="44"/>
<state key="normal" image="ic_routing_eat_on"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMBlack"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="searchButtonTouchUpInside:" destination="iN0-l3-epB" eventType="touchUpInside" id="3le-Cd-rvC"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ggf-tc-U3R" userLabel="Food" customClass="MWMButton">
<rect key="frame" x="122" y="122" width="44" height="44"/>
<state key="normal" image="ic_routing_food_on"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMBlack"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="searchButtonTouchUpInside:" destination="iN0-l3-epB" eventType="touchUpInside" id="hkk-Xo-uPg"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uoS-b2-FcN" userLabel="ATM" customClass="MWMButton">
<rect key="frame" x="78" y="142" width="44" height="44"/>
<state key="normal" image="ic_routing_atm_on"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMBlack"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="searchButtonTouchUpInside:" destination="iN0-l3-epB" eventType="touchUpInside" id="Pqo-CT-Cog"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="uoS-b2-FcN" firstAttribute="height" secondItem="2Bv-7H-IZt" secondAttribute="height" id="2F7-5g-ErR"/>
<constraint firstItem="Ggf-tc-U3R" firstAttribute="width" secondItem="2Bv-7H-IZt" secondAttribute="width" id="2rN-Yz-3se"/>
<constraint firstItem="GcK-FI-VXw" firstAttribute="trailing" secondItem="Ggf-tc-U3R" secondAttribute="leading" priority="260" id="89h-kJ-0pV"/>
<constraint firstItem="RP3-01-Pj7" firstAttribute="leading" secondItem="2Bv-7H-IZt" secondAttribute="trailing" priority="250" id="C9o-Bx-KXq"/>
<constraint firstAttribute="height" constant="200" id="ClV-rj-I5W"/>
<constraint firstAttribute="trailing" secondItem="Ggf-tc-U3R" secondAttribute="trailing" priority="500" constant="34" id="Cxa-Ms-3ud"/>
<constraint firstItem="uoS-b2-FcN" firstAttribute="centerX" secondItem="TMW-aw-1RT" secondAttribute="centerX" priority="500" id="Fva-Vq-x1E"/>
<constraint firstItem="RP3-01-Pj7" firstAttribute="width" secondItem="2Bv-7H-IZt" secondAttribute="width" id="G7j-oT-tw9"/>
<constraint firstItem="GcK-FI-VXw" firstAttribute="centerY" secondItem="TMW-aw-1RT" secondAttribute="centerY" id="HaH-4G-jWc"/>
<constraint firstItem="RP3-01-Pj7" firstAttribute="height" secondItem="2Bv-7H-IZt" secondAttribute="height" id="M5K-C3-PxI"/>
<constraint firstItem="2Bv-7H-IZt" firstAttribute="centerY" secondItem="TMW-aw-1RT" secondAttribute="centerY" priority="250" id="NcX-Rf-Cvm"/>
<constraint firstItem="GcK-FI-VXw" firstAttribute="width" secondItem="2Bv-7H-IZt" secondAttribute="width" id="Qxb-Oh-gdO"/>
<constraint firstItem="GcK-FI-VXw" firstAttribute="height" secondItem="2Bv-7H-IZt" secondAttribute="height" id="Rct-GE-0m2"/>
<constraint firstAttribute="bottom" secondItem="uoS-b2-FcN" secondAttribute="bottom" priority="500" constant="14" id="Skj-gh-LVA"/>
<constraint firstAttribute="bottom" secondItem="Ggf-tc-U3R" secondAttribute="bottom" priority="500" constant="34" id="ZWZ-hv-s2X"/>
<constraint firstAttribute="trailing" secondItem="RP3-01-Pj7" secondAttribute="trailing" priority="500" constant="34" id="ZrU-lQ-I2d"/>
<constraint firstItem="uoS-b2-FcN" firstAttribute="width" secondItem="2Bv-7H-IZt" secondAttribute="width" id="bqX-bU-qi0"/>
<constraint firstItem="RP3-01-Pj7" firstAttribute="top" secondItem="TMW-aw-1RT" secondAttribute="top" priority="500" constant="34" id="eMY-fr-9hS"/>
<constraint firstItem="2Bv-7H-IZt" firstAttribute="top" secondItem="TMW-aw-1RT" secondAttribute="top" priority="500" constant="14" id="eOX-wt-nUe"/>
<constraint firstItem="Ggf-tc-U3R" firstAttribute="height" secondItem="2Bv-7H-IZt" secondAttribute="height" id="ece-M8-eEa"/>
<constraint firstItem="RP3-01-Pj7" firstAttribute="centerY" secondItem="TMW-aw-1RT" secondAttribute="centerY" priority="250" id="iSp-hy-tmq"/>
<constraint firstItem="uoS-b2-FcN" firstAttribute="centerY" secondItem="TMW-aw-1RT" secondAttribute="centerY" priority="250" id="j4g-jo-knc"/>
<constraint firstAttribute="trailing" secondItem="GcK-FI-VXw" secondAttribute="trailing" priority="500" constant="14" id="jlf-5n-9UR"/>
<constraint firstAttribute="trailing" secondItem="uoS-b2-FcN" secondAttribute="trailing" priority="250" constant="8" id="lOI-E1-KoS"/>
<constraint firstAttribute="width" constant="200" id="mOu-Vw-eLb"/>
<constraint firstItem="GcK-FI-VXw" firstAttribute="leading" secondItem="RP3-01-Pj7" secondAttribute="trailing" priority="260" id="nxu-rz-dZp"/>
<constraint firstItem="2Bv-7H-IZt" firstAttribute="centerX" secondItem="TMW-aw-1RT" secondAttribute="centerX" priority="500" id="s89-b9-XaO"/>
<constraint firstItem="Ggf-tc-U3R" firstAttribute="centerY" secondItem="TMW-aw-1RT" secondAttribute="centerY" priority="250" id="sVL-ni-mon"/>
<constraint firstItem="2Bv-7H-IZt" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="TMW-aw-1RT" secondAttribute="leading" id="vbk-wb-RCv"/>
<constraint firstItem="Ggf-tc-U3R" firstAttribute="trailing" secondItem="uoS-b2-FcN" secondAttribute="leading" priority="250" id="vcn-Dj-WUu"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MenuBackground"/>
</userDefinedRuntimeAttributes>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Xna-Q1-7zW" customClass="MWMButton">
<rect key="frame" x="0.0" y="687" width="56" height="56"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="searchButton"/>
<constraints>
<constraint firstAttribute="width" constant="56" id="IJq-4U-XHW"/>
<constraint firstAttribute="height" constant="56" id="cvr-aL-Qfk"/>
</constraints>
<state key="normal" image="ic_routing_search_light"/>
<connections>
<action selector="searchMainButtonTouchUpInside" destination="iN0-l3-epB" eventType="touchUpInside" id="gd5-zu-3i5"/>
</connections>
</button>
</subviews>
<viewLayoutGuide key="safeArea" id="2Hp-l9-4oj"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="YYv-pG-Wkw" secondAttribute="trailing" constant="-100" id="15D-kN-gda"/>
<constraint firstAttribute="trailing" secondItem="Tzc-l6-Vvt" secondAttribute="trailing" id="26j-tD-P9y"/>
<constraint firstItem="TMW-aw-1RT" firstAttribute="centerY" secondItem="Xna-Q1-7zW" secondAttribute="centerY" id="652-PO-iav"/>
<constraint firstItem="YYv-pG-Wkw" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="-100" id="IBQ-gK-Cpe"/>
<constraint firstItem="Xna-Q1-7zW" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="J7x-54-WT5"/>
<constraint firstItem="Tzc-l6-Vvt" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="bottom" priority="770" id="JqF-da-c1k"/>
<constraint firstItem="Xna-Q1-7zW" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" priority="500" constant="32" id="KQb-ju-1bf"/>
<constraint firstItem="YYv-pG-Wkw" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" priority="500" id="Rd5-Hl-fSF"/>
<constraint firstAttribute="trailingMargin" secondItem="ShI-bz-5g8" secondAttribute="trailing" id="RsT-Ct-FiQ"/>
<constraint firstItem="Tzc-l6-Vvt" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="TvX-b3-3Sa"/>
<constraint firstItem="XMv-au-OYf" firstAttribute="top" relation="greaterThanOrEqual" secondItem="wt8-EO-MKz" secondAttribute="bottom" priority="800" constant="8" id="Vtn-PC-puN"/>
<constraint firstItem="XMv-au-OYf" firstAttribute="centerX" secondItem="Xna-Q1-7zW" secondAttribute="centerX" id="XwW-6c-UzJ"/>
<constraint firstItem="TMW-aw-1RT" firstAttribute="leading" secondItem="Xna-Q1-7zW" secondAttribute="leading" priority="250" constant="-2" id="Y9b-UG-buF"/>
<constraint firstItem="Xna-Q1-7zW" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" priority="250" constant="64" id="boQ-v0-PUh"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="Xna-Q1-7zW" secondAttribute="bottom" constant="48" id="btg-Xq-UVZ"/>
<constraint firstItem="TMW-aw-1RT" firstAttribute="centerX" secondItem="Xna-Q1-7zW" secondAttribute="centerX" priority="500" id="ctJ-SL-eKZ"/>
<constraint firstItem="Aa6-N8-acP" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="28" id="goU-O7-rpS"/>
<constraint firstItem="XMv-au-OYf" firstAttribute="top" relation="greaterThanOrEqual" secondItem="Aa6-N8-acP" secondAttribute="bottom" priority="800" id="hiU-Sn-hG1"/>
<constraint firstItem="ShI-bz-5g8" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Aa6-N8-acP" secondAttribute="trailing" priority="999" constant="8" id="mdA-B9-tvQ"/>
<constraint firstItem="Tzc-l6-Vvt" firstAttribute="top" relation="greaterThanOrEqual" secondItem="Xna-Q1-7zW" secondAttribute="bottom" priority="740" constant="12" id="pFP-r6-dGK"/>
<constraint firstItem="Aa6-N8-acP" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="pXD-dk-ku2"/>
<constraint firstItem="Xna-Q1-7zW" firstAttribute="top" secondItem="XMv-au-OYf" secondAttribute="bottom" constant="8" id="tEP-Qf-uBZ"/>
<constraint firstAttribute="top" secondItem="YYv-pG-Wkw" secondAttribute="bottom" priority="250" id="xMz-Tm-KEn"/>
<constraint firstAttribute="leading" secondItem="Aa6-N8-acP" secondAttribute="trailing" priority="250" id="xO2-jM-2bk"/>
<constraint firstAttribute="bottom" secondItem="Tzc-l6-Vvt" secondAttribute="bottom" priority="760" id="yP1-ip-l24"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="bookmarksButton" destination="XMv-au-OYf" id="g3E-jo-X2E"/>
<outlet property="distanceToNextTurnLabel" destination="KuR-1J-VI2" id="n0i-t9-Vj2"/>
<outlet property="nextTurnImageView" destination="AJe-8N-rpk" id="iMm-9u-rPI"/>
<outlet property="roundTurnLabel" destination="teB-QQ-oib" id="5p2-zz-Pe9"/>
<outlet property="searchATMButton" destination="uoS-b2-FcN" id="Sp2-hc-Lob"/>
<outlet property="searchButtonsSideSize" destination="KKe-fH-h2n" id="5EK-YA-bXw"/>
<outlet property="searchButtonsView" destination="TMW-aw-1RT" id="bQd-e2-Jey"/>
<outlet property="searchButtonsViewHeight" destination="ClV-rj-I5W" id="egz-NO-EAn"/>
<outlet property="searchButtonsViewWidth" destination="mOu-Vw-eLb" id="SsK-HE-lHC"/>
<outlet property="searchEatButton" destination="GcK-FI-VXw" id="G0h-uf-9uS"/>
<outlet property="searchFoodButton" destination="Ggf-tc-U3R" id="RJJ-mh-rwy"/>
<outlet property="searchGasButton" destination="2Bv-7H-IZt" id="hUg-CH-kWQ"/>
<outlet property="searchMainButton" destination="Xna-Q1-7zW" id="v48-fa-MQE"/>
<outlet property="searchParkingButton" destination="RP3-01-Pj7" id="sdL-mN-sLE"/>
<outlet property="secondTurnImageView" destination="CKi-sy-dNO" id="A29-2z-4oh"/>
<outlet property="secondTurnView" destination="8Zu-Ff-6p2" id="yEK-rY-S50"/>
<outlet property="streetNameLabel" destination="ShI-bz-5g8" id="eZd-Es-g0l"/>
<outlet property="streetNameTopOffsetConstraint" destination="KK1-dA-hII" id="d2I-37-Guw"/>
<outlet property="streetNameView" destination="YYv-pG-Wkw" id="gbk-SH-idq"/>
<outlet property="streetNameViewHideOffset" destination="xMz-Tm-KEn" id="Lwv-ep-zvo"/>
<outlet property="toastView" destination="Tzc-l6-Vvt" id="gTw-kd-UAJ"/>
<outlet property="toastViewHideOffset" destination="JqF-da-c1k" id="WtX-JJ-0lS"/>
<outlet property="turnsTopOffset" destination="goU-O7-rpS" id="MEy-6X-7Qq"/>
<outlet property="turnsView" destination="Aa6-N8-acP" id="daB-uQ-UFM"/>
<outlet property="turnsViewHideOffset" destination="xO2-jM-2bk" id="U11-pU-2ka"/>
<outlet property="turnsWidth" destination="6Ja-Xp-g8h" id="kCr-a1-fph"/>
<outletCollection property="searchLandscapeConstraints" destination="iSp-hy-tmq" id="h2M-dj-EiD"/>
<outletCollection property="searchLandscapeConstraints" destination="C9o-Bx-KXq" id="5a5-4m-Dyf"/>
<outletCollection property="searchButtonsSideSize" destination="KKe-fH-h2n" id="Okh-Is-iMi"/>
<outletCollection property="searchButtons" destination="uoS-b2-FcN" id="1Ly-Ve-2RT"/>
<outletCollection property="searchButtons" destination="2Bv-7H-IZt" id="co8-GL-K6m"/>
<outletCollection property="searchLandscapeConstraints" destination="lOI-E1-KoS" id="Zeu-Nj-vBU"/>
<outletCollection property="searchLandscapeConstraints" destination="89h-kJ-0pV" id="xBD-bu-bfe"/>
<outletCollection property="searchLandscapeConstraints" destination="sVL-ni-mon" id="DTz-eP-FWo"/>
<outletCollection property="searchButtons" destination="Ggf-tc-U3R" id="FIp-4y-5eA"/>
<outletCollection property="searchButtons" destination="GcK-FI-VXw" id="Ykw-0h-Emh"/>
<outletCollection property="searchLandscapeConstraints" destination="nxu-rz-dZp" id="Y1O-08-buw"/>
<outletCollection property="searchLandscapeConstraints" destination="vcn-Dj-WUu" id="kjW-Jd-TLm"/>
<outletCollection property="searchLandscapeConstraints" destination="j4g-jo-knc" id="EG6-ya-ahs"/>
<outletCollection property="searchLandscapeConstraints" destination="NcX-Rf-Cvm" id="vwd-uF-iTf"/>
<outletCollection property="searchButtons" destination="RP3-01-Pj7" id="1nL-Ib-y49"/>
<outletCollection property="searchLandscapeConstraints" destination="Y9b-UG-buF" id="cjs-so-6PQ"/>
<outletCollection property="searchLandscapeConstraints" destination="boQ-v0-PUh" id="cV7-rz-brS"/>
</connections>
<point key="canvasLocation" x="33.5" y="54.5"/>
</view>
</objects>
<resources>
<image name="ic_get_position" width="20" height="20"/>
<image name="ic_routing_atm_on" width="24" height="24"/>
<image name="ic_routing_eat_on" width="24" height="24"/>
<image name="ic_routing_food_on" width="24" height="24"/>
<image name="ic_routing_fuel_on" width="24" height="24"/>
<image name="ic_routing_parking_on" width="24" height="24"/>
<image name="ic_routing_search_light" width="56" height="56"/>
<image name="ic_search_toast" width="24" height="24"/>
</resources>
</document>

View file

@ -0,0 +1,26 @@
@objc(MWMNavigationAddPointToastView)
final class NavigationAddPointToastView: UIView {
@IBOutlet private var actionButton: UIButton!
@IBOutlet private var locationButton: UIButton!
@IBOutlet private var separator: UIView!
@IBOutlet private weak var extraBottomBackground: UIView!
@objc private(set) var isStart = true
@objc func config(isStart: Bool, withLocationButton: Bool) {
self.isStart = isStart
let text = isStart ? L("routing_add_start_point") : L("routing_add_finish_point")
actionButton.setTitle(text, for: .normal)
if withLocationButton {
locationButton.isHidden = false
separator.isHidden = false
} else {
locationButton.isHidden = true
separator.isHidden = true
}
}
override var widgetsAreaAffectDirections: MWMAvailableAreaAffectDirections { return .bottom }
override var sideButtonsAreaAffectDirections: MWMAvailableAreaAffectDirections { return .bottom }
}

View file

@ -0,0 +1,275 @@
@objc(MWMNavigationControlView)
final class NavigationControlView: SolidTouchView, MWMTextToSpeechObserver, MapOverlayManagerObserver {
@IBOutlet private weak var distanceLabel: UILabel!
@IBOutlet private weak var distanceLegendLabel: UILabel!
@IBOutlet private weak var distanceWithLegendLabel: UILabel!
@IBOutlet private weak var progressView: UIView!
@IBOutlet private weak var routingProgress: NSLayoutConstraint!
@IBOutlet private weak var speedLabel: UILabel!
@IBOutlet private weak var speedBackground: UIView!
@IBOutlet private weak var speedLegendLabel: UILabel!
@IBOutlet private weak var speedWithLegendLabel: UILabel!
@IBOutlet private weak var timeLabel: UILabel!
@IBOutlet private weak var timePageControl: UIPageControl!
@IBOutlet private weak var extendButton: UIButton! {
didSet {
setExtendButtonImage()
}
}
@IBOutlet private weak var ttsButton: UIButton! {
didSet {
ttsButton.setImage(#imageLiteral(resourceName: "ic_voice_off"), for: .normal)
ttsButton.setImage(#imageLiteral(resourceName: "ic_voice_on"), for: .selected)
ttsButton.setImage(#imageLiteral(resourceName: "ic_voice_on"), for: [.selected, .highlighted])
onTTSStatusUpdated()
}
}
private lazy var dimBackground: DimBackground = {
DimBackground(mainView: self, tapAction: { [weak self] in
self?.diminish()
})
}()
@objc weak var ownerView: UIView!
@IBOutlet private weak var extendedView: UIView!
private weak var navigationInfo: MWMNavigationDashboardEntity?
private var extendedConstraint: NSLayoutConstraint!
private var notExtendedConstraint: NSLayoutConstraint!
@objc var isVisible = false {
didSet {
guard isVisible != oldValue else { return }
if isVisible {
addView()
} else {
removeView()
}
}
}
private var isExtended = false {
willSet {
guard isExtended != newValue else { return }
morphExtendButton()
}
didSet {
guard isVisible && superview != nil else { return }
guard isExtended != oldValue else { return }
dimBackground.setVisible(isExtended, completion: nil)
extendedView.isHidden = !isExtended
superview!.animateConstraints(animations: {
if (self.isExtended) {
self.notExtendedConstraint.isActive = false
self.extendedConstraint.isActive = true
} else {
self.extendedConstraint.isActive = false
self.notExtendedConstraint.isActive = true
}
})
}
}
private func addView() {
guard superview != ownerView else { return }
ownerView.addSubview(self)
let lg = ownerView.safeAreaLayoutGuide
leadingAnchor.constraint(equalTo: lg.leadingAnchor).isActive = true
trailingAnchor.constraint(equalTo: lg.trailingAnchor).isActive = true
extendedConstraint = bottomAnchor.constraint(equalTo: lg.bottomAnchor)
extendedConstraint.isActive = false
notExtendedConstraint = progressView.bottomAnchor.constraint(equalTo: lg.bottomAnchor)
notExtendedConstraint.isActive = true
}
private func removeView() {
dimBackground.setVisible(false, completion: {
self.removeFromSuperview()
})
}
override func awakeFromNib() {
super.awakeFromNib()
updateLegendSize()
MWMTextToSpeech.add(self)
MapOverlayManager.add(self)
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
updateLegendSize()
}
func updateLegendSize() {
let isCompact = traitCollection.verticalSizeClass == .compact
distanceLabel.isHidden = isCompact
distanceLegendLabel.isHidden = isCompact
distanceWithLegendLabel.isHidden = !isCompact
speedLabel.isHidden = isCompact
speedLegendLabel.isHidden = isCompact
speedWithLegendLabel.isHidden = !isCompact
let pgScale: CGFloat = isCompact ? 0.7 : 1
timePageControl.transform = CGAffineTransform(scaleX: pgScale, y: pgScale)
}
@objc func onNavigationInfoUpdated(_ info: MWMNavigationDashboardEntity) {
navigationInfo = info
let routingNumberAttributes: [NSAttributedString.Key: Any] =
[
NSAttributedString.Key.foregroundColor: UIColor.blackPrimaryText(),
NSAttributedString.Key.font: UIFont.bold24()
]
let routingLegendAttributes: [NSAttributedString.Key: Any] =
[
NSAttributedString.Key.foregroundColor: UIColor.blackSecondaryText(),
NSAttributedString.Key.font: UIFont.bold14()
]
if timePageControl.currentPage == 0 {
timeLabel.text = DurationFormatter.durationString(from: TimeInterval(info.timeToTarget))
} else {
timeLabel.text = info.arrival
}
var distanceWithLegend: NSMutableAttributedString?
if let targetDistance = info.targetDistance {
distanceLabel.text = targetDistance
distanceWithLegend = NSMutableAttributedString(string: targetDistance, attributes: routingNumberAttributes)
}
if let targetUnits = info.targetUnits {
distanceLegendLabel.text = targetUnits
if let distanceWithLegend = distanceWithLegend {
distanceWithLegend.append(NSAttributedString(string: targetUnits, attributes: routingLegendAttributes))
distanceWithLegendLabel.attributedText = distanceWithLegend
}
}
var speedMps = 0.0
if let s = LocationManager.lastLocation()?.speed, s > 0 {
speedMps = s
}
let speedMeasure = Measure(asSpeed: speedMps)
var speed = speedMeasure.valueAsString;
/// @todo Draw speed limit sign similar to the CarPlay implemenation.
// speedLimitMps >= 0 means known limited speed.
if (info.speedLimitMps >= 0) {
// Short delimeter to not overlap with timeToTarget longer than an hour.
let delimeter = info.timeToTarget < 60 * 60 ? " / " : "/"
let speedLimitMeasure = Measure(asSpeed: info.speedLimitMps)
// speedLimitMps == 0 means unlimited speed.
speed += delimeter + (info.speedLimitMps == 0 ? "" : speedLimitMeasure.valueAsString)
}
speedLabel.text = speed
speedLegendLabel.text = speedMeasure.unit
let speedWithLegend = NSMutableAttributedString(string: speed, attributes: routingNumberAttributes)
speedWithLegend.append(NSAttributedString(string: speedMeasure.unit, attributes: routingLegendAttributes))
speedWithLegendLabel.attributedText = speedWithLegend
if MWMRouter.isSpeedCamLimitExceeded() {
speedLabel.textColor = UIColor.white()
speedBackground.backgroundColor = UIColor.buttonRed()
} else {
let isSpeedLimitExceeded = info.speedLimitMps > 0 && speedMps > info.speedLimitMps
speedLabel.textColor = isSpeedLimitExceeded ? UIColor.buttonRed() : UIColor.blackPrimaryText()
speedBackground.backgroundColor = UIColor.clear
}
speedLegendLabel.textColor = speedLabel.textColor
speedWithLegendLabel.textColor = speedLabel.textColor
routingProgress.constant = progressView.width * info.progress / 100
}
@IBAction
private func toggleInfoAction() {
if let navigationInfo = navigationInfo {
timePageControl.currentPage = (timePageControl.currentPage + 1) % timePageControl.numberOfPages
onNavigationInfoUpdated(navigationInfo)
}
refreshDiminishTimer()
}
@IBAction
private func extendAction() {
isExtended = !isExtended
refreshDiminishTimer()
}
private func morphExtendButton() {
guard let imageView = extendButton.imageView else { return }
let morphImagesCount = 6
let startValue = isExtended ? morphImagesCount : 1
let endValue = isExtended ? 0 : morphImagesCount + 1
let stepValue = isExtended ? -1 : 1
var morphImages: [UIImage] = []
let nightMode = UIColor.isNightMode() ? "dark" : "light"
for i in stride(from: startValue, to: endValue, by: stepValue) {
let imageName = "ic_menu_\(i)_\(nightMode)"
morphImages.append(UIImage(named: imageName)!)
}
imageView.animationImages = morphImages
imageView.animationRepeatCount = 1
imageView.image = morphImages.last
imageView.startAnimating()
setExtendButtonImage()
}
private func setExtendButtonImage() {
DispatchQueue.main.async {
guard let imageView = self.extendButton.imageView else { return }
if imageView.isAnimating {
self.setExtendButtonImage()
} else {
self.extendButton.setImage(self.isExtended ? #imageLiteral(resourceName: "ic_menu_down") : #imageLiteral(resourceName: "ic_menu"), for: .normal)
}
}
}
private func refreshDiminishTimer() {
let sel = #selector(diminish)
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: sel, object: self)
perform(sel, with: self, afterDelay: 5)
}
@objc
private func diminish() {
isExtended = false
}
func onTTSStatusUpdated() {
guard MWMRouter.isRoutingActive() else { return }
ttsButton.isHidden = !MWMTextToSpeech.isTTSEnabled()
if !ttsButton.isHidden {
ttsButton.isSelected = MWMTextToSpeech.tts().active
}
refreshDiminishTimer()
}
override func applyTheme() {
super.applyTheme()
onTTSStatusUpdated()
}
override var sideButtonsAreaAffectDirections: MWMAvailableAreaAffectDirections {
return .bottom
}
override var widgetsAreaAffectDirections: MWMAvailableAreaAffectDirections {
return alternative(iPhone: .bottom, iPad: [])
}
override var trackRecordingButtonAreaAffectDirections: MWMAvailableAreaAffectDirections {
return .bottom
}
}

View file

@ -0,0 +1,337 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina3_5" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
<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="MWMNavigationDashboardManager">
<connections>
<outlet property="navigationControlView" destination="9fq-65-qd9" id="KXU-52-J0l"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9fq-65-qd9" customClass="MWMNavigationControlView" propertyAccessControl="none">
<rect key="frame" x="0.0" y="0.0" width="320" height="116"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jBZ-WO-Nz7">
<rect key="frame" x="-100" y="0.0" width="520" height="216"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
</userDefinedRuntimeAttributes>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="u7O-n6-ZXy">
<rect key="frame" x="0.0" y="0.0" width="320" height="52"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QLk-JN-wfO">
<rect key="frame" x="0.0" y="0.0" width="66" height="48"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="LY7-dx-CtV">
<rect key="frame" x="33" y="4" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="22"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="b3M-75-5HW">
<rect key="frame" x="33" y="4" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="22"/>
<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="blackPrimaryText"/>
</userDefinedRuntimeAttributes>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wgS-eK-vfD">
<rect key="frame" x="33" y="10.5" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="12"/>
<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="blackSecondaryText"/>
</userDefinedRuntimeAttributes>
</label>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" priority="750" constant="72" id="6Dl-DP-LFT">
<variation key="heightClass=compact" constant="128"/>
<variation key="heightClass=regular" constant="88"/>
<variation key="heightClass=compact-widthClass=compact" constant="72"/>
</constraint>
<constraint firstItem="wgS-eK-vfD" firstAttribute="centerX" secondItem="QLk-JN-wfO" secondAttribute="centerX" id="9qv-fX-QKO"/>
<constraint firstItem="b3M-75-5HW" firstAttribute="top" secondItem="QLk-JN-wfO" secondAttribute="top" constant="4" id="FQd-Km-nUE"/>
<constraint firstItem="b3M-75-5HW" firstAttribute="centerX" secondItem="QLk-JN-wfO" secondAttribute="centerX" priority="750" id="guh-Tk-WSm"/>
<constraint firstItem="LY7-dx-CtV" firstAttribute="centerX" secondItem="QLk-JN-wfO" secondAttribute="centerX" priority="750" id="ngn-iE-YWb"/>
<constraint firstItem="b3M-75-5HW" firstAttribute="top" secondItem="LY7-dx-CtV" secondAttribute="top" id="oMq-Rn-clI"/>
<constraint firstItem="LY7-dx-CtV" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="QLk-JN-wfO" secondAttribute="leading" priority="800" id="oeQ-cx-SRl"/>
<constraint firstItem="b3M-75-5HW" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="QLk-JN-wfO" secondAttribute="leading" priority="800" id="yar-Nc-c7c"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="HNY-h1-A95">
<rect key="frame" x="66" y="0.0" width="128" height="48"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wEc-R2-1Tv">
<rect key="frame" x="64" y="4" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="22"/>
<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="blackPrimaryText"/>
</userDefinedRuntimeAttributes>
</label>
<pageControl opaque="NO" contentMode="scaleToFill" verticalCompressionResistancePriority="100" contentHorizontalAlignment="center" contentVerticalAlignment="center" numberOfPages="2" translatesAutoresizingMaskIntoConstraints="NO" id="ppd-rW-dYo">
<rect key="frame" x="13" y="4" width="102.5" height="13"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="13" id="BmO-Rd-DKp"/>
</constraints>
</pageControl>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="wEc-R2-1Tv" firstAttribute="top" secondItem="HNY-h1-A95" secondAttribute="top" constant="4" id="56P-TX-Y1M"/>
<constraint firstItem="ppd-rW-dYo" firstAttribute="top" secondItem="wEc-R2-1Tv" secondAttribute="bottom" id="PPK-he-lFX">
<variation key="heightClass=compact" constant="-6"/>
</constraint>
<constraint firstItem="wEc-R2-1Tv" firstAttribute="centerX" secondItem="HNY-h1-A95" secondAttribute="centerX" id="cQo-a6-jlH"/>
<constraint firstAttribute="width" constant="128" id="htd-vx-Rfi">
<variation key="heightClass=compact-widthClass=compact" constant="112"/>
</constraint>
<constraint firstItem="ppd-rW-dYo" firstAttribute="centerX" secondItem="HNY-h1-A95" secondAttribute="centerX" id="yab-6h-4LX"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sUB-KS-dnn">
<rect key="frame" x="194" y="0.0" width="66" height="48"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fxK-Ae-ebd">
<rect key="frame" x="33" y="4" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="22"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vbY-p5-3bX">
<rect key="frame" x="33" y="4" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="22"/>
<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="blackPrimaryText"/>
</userDefinedRuntimeAttributes>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7dx-1W-LWs">
<rect key="frame" x="33" y="10.5" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="12"/>
<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="blackSecondaryText"/>
</userDefinedRuntimeAttributes>
</label>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="fxK-Ae-ebd" firstAttribute="top" secondItem="vbY-p5-3bX" secondAttribute="top" id="0D2-Df-CBg"/>
<constraint firstItem="vbY-p5-3bX" firstAttribute="centerX" secondItem="sUB-KS-dnn" secondAttribute="centerX" priority="750" id="5d9-Lu-0Cm"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="vbY-p5-3bX" secondAttribute="trailing" priority="800" id="8nO-wt-deB"/>
<constraint firstItem="fxK-Ae-ebd" firstAttribute="centerX" secondItem="sUB-KS-dnn" secondAttribute="centerX" priority="750" id="O2N-5H-j6e"/>
<constraint firstItem="vbY-p5-3bX" firstAttribute="top" secondItem="sUB-KS-dnn" secondAttribute="top" constant="4" id="afW-J0-gsi"/>
<constraint firstItem="7dx-1W-LWs" firstAttribute="centerX" secondItem="sUB-KS-dnn" secondAttribute="centerX" id="loT-82-20Y"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="fxK-Ae-ebd" secondAttribute="trailing" priority="800" id="qlE-z0-Q0A"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Q0w-Me-x4p">
<rect key="frame" x="0.0" y="48" width="320" height="4"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JiD-R1-DnS">
<rect key="frame" x="0.0" y="0.0" width="0.0" height="4"/>
<color key="backgroundColor" red="0.1176470588" green="0.58823529409999997" blue="0.94117647059999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" id="vxl-iA-p8N"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="BlueBackground"/>
</userDefinedRuntimeAttributes>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="JiD-R1-DnS" secondAttribute="bottom" id="91c-nx-vVB"/>
<constraint firstAttribute="height" constant="4" id="DSq-vZ-frm"/>
<constraint firstItem="JiD-R1-DnS" firstAttribute="top" secondItem="Q0w-Me-x4p" secondAttribute="top" id="Dwi-Ld-Q1A"/>
<constraint firstItem="JiD-R1-DnS" firstAttribute="leading" secondItem="Q0w-Me-x4p" secondAttribute="leading" id="eon-hA-hQn"/>
</constraints>
</view>
<button contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="UPO-sh-VhS" userLabel="ToggleInfo">
<rect key="frame" x="0.0" y="0.0" width="260" height="48"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<action selector="toggleInfoAction" destination="9fq-65-qd9" eventType="touchUpInside" id="h7P-d8-FJM"/>
</connections>
</button>
<button opaque="NO" contentMode="center" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="XwA-oz-M3Q" customClass="MWMButton">
<rect key="frame" x="260" y="0.0" width="60" height="52"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="menuButton"/>
<constraints>
<constraint firstAttribute="width" constant="60" id="2B5-kg-8KQ"/>
</constraints>
<state key="normal" image="ic_menu"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMBlack"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="extendAction" destination="9fq-65-qd9" eventType="touchUpInside" id="uAL-Qn-4ne"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="QLk-JN-wfO" secondAttribute="bottom" constant="4" id="0AT-pb-GFy"/>
<constraint firstItem="QLk-JN-wfO" firstAttribute="leading" secondItem="u7O-n6-ZXy" secondAttribute="leading" id="0hr-A3-h7F">
<variation key="heightClass=compact" constant="16"/>
<variation key="heightClass=regular-widthClass=regular" constant="16"/>
</constraint>
<constraint firstItem="UPO-sh-VhS" firstAttribute="top" secondItem="u7O-n6-ZXy" secondAttribute="top" id="2Y4-nO-oxo"/>
<constraint firstItem="XwA-oz-M3Q" firstAttribute="top" secondItem="u7O-n6-ZXy" secondAttribute="top" id="5lH-DU-U8t"/>
<constraint firstItem="QLk-JN-wfO" firstAttribute="top" secondItem="u7O-n6-ZXy" secondAttribute="top" id="68j-wb-JVW"/>
<constraint firstAttribute="bottom" secondItem="sUB-KS-dnn" secondAttribute="bottom" constant="4" id="6n2-89-jZF"/>
<constraint firstItem="sUB-KS-dnn" firstAttribute="width" secondItem="QLk-JN-wfO" secondAttribute="width" id="7xg-vu-wVh"/>
<constraint firstAttribute="trailing" secondItem="Q0w-Me-x4p" secondAttribute="trailing" id="Ljf-W6-hhb"/>
<constraint firstItem="HNY-h1-A95" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="QLk-JN-wfO" secondAttribute="trailing" id="Mog-4d-Hi5"/>
<constraint firstAttribute="trailing" secondItem="XwA-oz-M3Q" secondAttribute="trailing" id="QZv-2x-d1t"/>
<constraint firstAttribute="bottom" secondItem="XwA-oz-M3Q" secondAttribute="bottom" id="UJh-uZ-ODO"/>
<constraint firstItem="sUB-KS-dnn" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="HNY-h1-A95" secondAttribute="trailing" id="Uh8-ST-KVx"/>
<constraint firstAttribute="bottom" secondItem="HNY-h1-A95" secondAttribute="bottom" constant="4" id="XWQ-fY-sAs"/>
<constraint firstItem="XwA-oz-M3Q" firstAttribute="leading" secondItem="sUB-KS-dnn" secondAttribute="trailing" id="ZvS-46-HNH">
<variation key="heightClass=compact" constant="16"/>
<variation key="heightClass=regular-widthClass=regular" constant="16"/>
</constraint>
<constraint firstItem="HNY-h1-A95" firstAttribute="centerX" secondItem="u7O-n6-ZXy" secondAttribute="centerX" priority="700" id="dnJ-0H-Kj4"/>
<constraint firstItem="wgS-eK-vfD" firstAttribute="centerY" secondItem="ppd-rW-dYo" secondAttribute="centerY" priority="500" id="eEB-zX-D6C"/>
<constraint firstItem="7dx-1W-LWs" firstAttribute="centerY" secondItem="ppd-rW-dYo" secondAttribute="centerY" priority="500" id="eEw-8m-1R6"/>
<constraint firstAttribute="height" constant="52" id="gKx-sK-cYM">
<variation key="heightClass=compact" constant="40"/>
<variation key="heightClass=regular-widthClass=regular" constant="56"/>
</constraint>
<constraint firstAttribute="bottom" secondItem="UPO-sh-VhS" secondAttribute="bottom" constant="4" id="gcG-aR-OyZ"/>
<constraint firstItem="Q0w-Me-x4p" firstAttribute="leading" secondItem="u7O-n6-ZXy" secondAttribute="leading" id="m3u-NJ-pQ3"/>
<constraint firstItem="sUB-KS-dnn" firstAttribute="top" secondItem="u7O-n6-ZXy" secondAttribute="top" id="sdn-AJ-ItP"/>
<constraint firstAttribute="bottom" secondItem="Q0w-Me-x4p" secondAttribute="bottom" id="sl4-ja-PfO"/>
<constraint firstItem="HNY-h1-A95" firstAttribute="top" secondItem="u7O-n6-ZXy" secondAttribute="top" id="t19-k5-MzQ"/>
<constraint firstItem="UPO-sh-VhS" firstAttribute="leading" secondItem="u7O-n6-ZXy" secondAttribute="leading" id="tlC-se-vPa"/>
<constraint firstItem="XwA-oz-M3Q" firstAttribute="leading" secondItem="UPO-sh-VhS" secondAttribute="trailing" id="viH-dq-rwd"/>
</constraints>
</view>
<view hidden="YES" clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="CMT-MI-d8X">
<rect key="frame" x="0.0" y="52" width="320" height="64"/>
<subviews>
<stackView opaque="NO" contentMode="right" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="udk-ZC-Gz0">
<rect key="frame" x="103" y="0.0" width="93" height="64"/>
<subviews>
<button opaque="NO" contentMode="center" horizontalHuggingPriority="249" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Bfb-5v-iWA" customClass="MWMButton">
<rect key="frame" x="0.0" y="0.0" width="57" height="64"/>
<state key="normal" image="ic_voice_on"/>
<state key="selected" image="ic_voice_off"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMBlack"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="ttsButtonAction" destination="-1" eventType="touchUpInside" id="2U8-vo-LOQ"/>
</connections>
</button>
<button opaque="NO" contentMode="center" horizontalHuggingPriority="249" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="skM-Xx-3En" userLabel="Settings" customClass="MWMButton">
<rect key="frame" x="65" y="0.0" width="28" height="64"/>
<state key="normal" image="ic_menu_settings"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMBlack"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="settingsButtonAction" destination="-1" eventType="touchUpInside" id="m7v-S7-aqm"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="Bfb-5v-iWA" firstAttribute="width" secondItem="udk-ZC-Gz0" secondAttribute="height" multiplier="57:64" id="Pvi-bX-9ME"/>
</constraints>
</stackView>
<button opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" horizontalHuggingPriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mtt-q0-SUx" customClass="MWMStopButton">
<rect key="frame" x="204" y="14" width="96" height="36"/>
<color key="backgroundColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="140" id="4E4-UU-lhc"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="96" id="N0o-f3-nuw"/>
<constraint firstAttribute="height" priority="750" constant="36" id="Pfs-0w-XX9"/>
</constraints>
<state key="normal" title="STOP">
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="FlatRedButton:regular17"/>
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="navigation_stop_button"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="stopRoutingButtonAction" destination="-1" eventType="touchUpInside" id="Xvf-fv-3GR"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="mtt-q0-SUx" firstAttribute="top" secondItem="CMT-MI-d8X" secondAttribute="top" priority="750" constant="14" id="L5z-8y-Duk"/>
<constraint firstAttribute="height" constant="64" id="LaX-SD-Kzr"/>
<constraint firstItem="mtt-q0-SUx" firstAttribute="leading" secondItem="udk-ZC-Gz0" secondAttribute="trailing" constant="8" id="QSr-OG-Imw"/>
<constraint firstItem="udk-ZC-Gz0" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="CMT-MI-d8X" secondAttribute="leading" priority="250" constant="8" id="V36-Jb-U8R"/>
<constraint firstItem="udk-ZC-Gz0" firstAttribute="top" secondItem="CMT-MI-d8X" secondAttribute="top" id="W8O-lT-eqz"/>
<constraint firstAttribute="bottom" secondItem="udk-ZC-Gz0" secondAttribute="bottom" id="bMN-ct-cOQ"/>
<constraint firstAttribute="trailing" secondItem="mtt-q0-SUx" secondAttribute="trailing" constant="20" id="huM-XX-MM9"/>
</constraints>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="ghU-PK-XSf"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.80000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="u7O-n6-ZXy" secondAttribute="trailing" id="2zR-Xc-sbx"/>
<constraint firstItem="ghU-PK-XSf" firstAttribute="trailing" secondItem="jBZ-WO-Nz7" secondAttribute="trailing" constant="-100" id="5Dl-67-eRO"/>
<constraint firstItem="jBZ-WO-Nz7" firstAttribute="leading" secondItem="ghU-PK-XSf" secondAttribute="leading" constant="-100" id="6OQ-UN-0gr"/>
<constraint firstItem="ghU-PK-XSf" firstAttribute="top" secondItem="jBZ-WO-Nz7" secondAttribute="top" id="CxJ-e3-177"/>
<constraint firstItem="u7O-n6-ZXy" firstAttribute="top" secondItem="9fq-65-qd9" secondAttribute="top" id="Qaw-S4-hJf"/>
<constraint firstAttribute="bottom" secondItem="CMT-MI-d8X" secondAttribute="bottom" id="QmH-op-EWM"/>
<constraint firstItem="CMT-MI-d8X" firstAttribute="top" secondItem="u7O-n6-ZXy" secondAttribute="bottom" id="SaY-tH-rNS"/>
<constraint firstAttribute="trailing" secondItem="CMT-MI-d8X" secondAttribute="trailing" id="Y3v-mx-s41"/>
<constraint firstItem="u7O-n6-ZXy" firstAttribute="leading" secondItem="9fq-65-qd9" secondAttribute="leading" id="myS-vi-80i"/>
<constraint firstItem="CMT-MI-d8X" firstAttribute="leading" secondItem="9fq-65-qd9" secondAttribute="leading" id="rEB-5o-z8o"/>
<constraint firstItem="jBZ-WO-Nz7" firstAttribute="bottom" secondItem="ghU-PK-XSf" secondAttribute="bottom" constant="100" id="wrU-Qn-62D"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="distanceLabel" destination="vbY-p5-3bX" id="uxW-PA-dra"/>
<outlet property="distanceLegendLabel" destination="7dx-1W-LWs" id="Kmh-8u-jgj"/>
<outlet property="distanceWithLegendLabel" destination="fxK-Ae-ebd" id="qhH-AB-nFm"/>
<outlet property="extendButton" destination="XwA-oz-M3Q" id="76Y-Qw-uPC"/>
<outlet property="extendedView" destination="CMT-MI-d8X" id="1OK-Ps-fL4"/>
<outlet property="progressView" destination="Q0w-Me-x4p" id="efq-ve-QAZ"/>
<outlet property="routingProgress" destination="vxl-iA-p8N" id="gw7-Pn-5m7"/>
<outlet property="speedBackground" destination="QLk-JN-wfO" id="aYN-db-Xaa"/>
<outlet property="speedLabel" destination="b3M-75-5HW" id="bLg-uX-05a"/>
<outlet property="speedLegendLabel" destination="wgS-eK-vfD" id="ZOP-Ro-KbD"/>
<outlet property="speedWithLegendLabel" destination="LY7-dx-CtV" id="tK9-Em-ztB"/>
<outlet property="timeLabel" destination="wEc-R2-1Tv" id="cfd-vS-d1G"/>
<outlet property="timePageControl" destination="ppd-rW-dYo" id="U1U-6U-JjR"/>
<outlet property="ttsButton" destination="Bfb-5v-iWA" id="GMb-ft-G6X"/>
</connections>
<point key="canvasLocation" x="33.600000000000001" y="53.073463268365821"/>
</view>
</objects>
<resources>
<image name="ic_menu" width="48" height="48"/>
<image name="ic_menu_settings" width="28" height="28"/>
<image name="ic_voice_off" width="28" height="28"/>
<image name="ic_voice_on" width="28" height="28"/>
</resources>
</document>

View file

@ -0,0 +1,17 @@
final class NavigationStreetNameView: SolidTouchView {
override var visibleAreaAffectDirections: MWMAvailableAreaAffectDirections {
return alternative(iPhone: [], iPad: .top)
}
override var placePageAreaAffectDirections: MWMAvailableAreaAffectDirections {
return alternative(iPhone: [], iPad: .top)
}
override var sideButtonsAreaAffectDirections: MWMAvailableAreaAffectDirections {
return .top
}
override var trackRecordingButtonAreaAffectDirections: MWMAvailableAreaAffectDirections {
return .top
}
}

View file

@ -0,0 +1,3 @@
final class NavigationTurnsView: UIView {
override var placePageAreaAffectDirections: MWMAvailableAreaAffectDirections { return alternative(iPhone: [], iPad: .left) }
}

View file

@ -0,0 +1,31 @@
#import "MWMCircularProgressState.h"
#import "MWMRouterType.h"
typedef NS_ENUM(NSInteger, MWMDrivingOptionsState) {
MWMDrivingOptionsStateNone,
MWMDrivingOptionsStateDefine,
MWMDrivingOptionsStateChange
};
@class MWMRoutePreview;
@protocol MWMRoutePreviewDelegate
- (void)routePreviewDidPressDrivingOptions:(MWMRoutePreview *)routePreview;
@end
@interface MWMRoutePreview : UIView
@property(nonatomic) MWMDrivingOptionsState drivingOptionsState;
@property(weak, nonatomic) id<MWMRoutePreviewDelegate> delegate;
- (void)addToView:(UIView *)superview;
- (void)remove;
- (void)statePrepare;
- (void)selectRouter:(MWMRouterType)routerType;
- (void)router:(MWMRouterType)routerType setState:(MWMCircularProgressState)state;
- (void)router:(MWMRouterType)routerType setProgress:(CGFloat)progress;
@end

View file

@ -0,0 +1,215 @@
#import "MWMRoutePreview.h"
#import "MWMCircularProgress.h"
#import "MWMLocationManager.h"
#import "MWMRouter.h"
#import "UIButton+Orientation.h"
#import "UIImageView+Coloring.h"
#import "SwiftBridge.h"
#include "platform/platform.hpp"
static CGFloat const kDrivingOptionsHeight = 48;
@interface MWMRoutePreview ()<MWMCircularProgressProtocol>
@property(nonatomic) BOOL isVisible;
@property(nonatomic) BOOL actualVisibilityValue;
@property(weak, nonatomic) IBOutlet UIButton * backButton;
@property(weak, nonatomic) IBOutlet UIView * bicycle;
@property(weak, nonatomic) IBOutlet UIView * contentView;
@property(weak, nonatomic) IBOutlet UIView * pedestrian;
@property(weak, nonatomic) IBOutlet UIView * publicTransport;
@property(weak, nonatomic) IBOutlet UIView * ruler;
@property(weak, nonatomic) IBOutlet UIView * vehicle;
@property(strong, nonatomic) IBOutlet NSLayoutConstraint * drivingOptionHeightConstraint;
@property(strong, nonatomic) IBOutlet UIButton * drivingOptionsButton;
@end
@implementation MWMRoutePreview
{
std::map<MWMRouterType, MWMCircularProgress *> m_progresses;
}
- (void)awakeFromNib
{
[super awakeFromNib];
self.actualVisibilityValue = NO;
self.translatesAutoresizingMaskIntoConstraints = NO;
[self setupProgresses];
[self.backButton matchInterfaceOrientation];
self.drivingOptionHeightConstraint.constant = -kDrivingOptionsHeight;
[self applyContentViewShadow];
}
- (void)applyContentViewShadow {
self.contentView.layer.shadowOffset = CGSizeZero;
self.contentView.layer.shadowRadius = 2.0;
self.contentView.layer.shadowOpacity = 0.7;
self.contentView.layer.shadowColor = UIColor.blackColor.CGColor;
[self resizeShadow];
}
- (void)layoutSubviews {
[super layoutSubviews];
[self.vehicle setNeedsLayout];
[self resizeShadow];
}
- (void)resizeShadow {
CGFloat shadowSize = 1.0;
CGRect contentFrame = self.contentView.bounds;
CGRect shadowFrame = CGRectMake(contentFrame.origin.x - shadowSize,
contentFrame.size.height,
contentFrame.size.width + (2 * shadowSize),
shadowSize);
self.contentView.layer.shadowPath = [UIBezierPath bezierPathWithRect: shadowFrame].CGPath;
}
- (void)setupProgresses
{
[self addProgress:self.vehicle imageName:@"ic_car" routerType:MWMRouterTypeVehicle];
[self addProgress:self.pedestrian imageName:@"ic_pedestrian" routerType:MWMRouterTypePedestrian];
[self addProgress:self.publicTransport
imageName:@"ic_train"
routerType:MWMRouterTypePublicTransport];
[self addProgress:self.bicycle imageName:@"ic_bike" routerType:MWMRouterTypeBicycle];
[self addProgress:self.ruler imageName:@"ic_ruler_route" routerType:MWMRouterTypeRuler];
}
- (void)addProgress:(UIView *)parentView
imageName:(NSString *)imageName
routerType:(MWMRouterType)routerType
{
MWMCircularProgress * progress = [[MWMCircularProgress alloc] initWithParentView:parentView];
MWMCircularProgressStateVec imageStates = @[@(MWMCircularProgressStateNormal),
@(MWMCircularProgressStateProgress), @(MWMCircularProgressStateSpinner)];
[progress setImageName:imageName forStates:imageStates];
[progress setImageName:[imageName stringByAppendingString:@"_selected"] forStates:@[@(MWMCircularProgressStateSelected), @(MWMCircularProgressStateCompleted)]];
[progress setImageName:@"ic_error" forStates:@[@(MWMCircularProgressStateFailed)]];
[progress setColoring:MWMButtonColoringWhiteText
forStates:@[@(MWMCircularProgressStateFailed), @(MWMCircularProgressStateSelected),
@(MWMCircularProgressStateProgress), @(MWMCircularProgressStateSpinner),
@(MWMCircularProgressStateCompleted)]];
[progress setSpinnerBackgroundColor:UIColor.clearColor];
[progress setColor:UIColor.whiteColor
forStates:@[@(MWMCircularProgressStateProgress), @(MWMCircularProgressStateSpinner)]];
progress.delegate = self;
m_progresses[routerType] = progress;
}
- (void)statePrepare
{
for (auto const & progress : m_progresses)
progress.second.state = MWMCircularProgressStateNormal;
}
- (void)selectRouter:(MWMRouterType)routerType
{
for (auto const & progress : m_progresses)
progress.second.state = MWMCircularProgressStateNormal;
m_progresses[routerType].state = MWMCircularProgressStateSelected;
}
- (void)router:(MWMRouterType)routerType setState:(MWMCircularProgressState)state
{
m_progresses[routerType].state = state;
}
- (void)router:(MWMRouterType)routerType setProgress:(CGFloat)progress
{
m_progresses[routerType].progress = progress;
}
- (IBAction)onDrivingOptions:(UIButton *)sender {
[self.delegate routePreviewDidPressDrivingOptions:self];
}
#pragma mark - MWMCircularProgressProtocol
- (void)progressButtonPressed:(nonnull MWMCircularProgress *)progress
{
MWMCircularProgressState const s = progress.state;
if (s == MWMCircularProgressStateSelected || s == MWMCircularProgressStateCompleted)
return;
for (auto const & prg : m_progresses)
{
if (prg.second != progress)
continue;
auto const routerType = prg.first;
if ([MWMRouter type] == routerType)
return;
[self selectRouter:routerType];
[MWMRouter setType:routerType];
[MWMRouter rebuildWithBestRouter:NO];
}
}
- (void)addToView:(UIView *)superview
{
NSAssert(superview != nil, @"Superview can't be nil");
if ([superview.subviews containsObject:self])
return;
[superview addSubview:self];
[self setupConstraints];
self.actualVisibilityValue = YES;
dispatch_async(dispatch_get_main_queue(), ^{
self.isVisible = YES;
});
}
- (void)remove {
self.actualVisibilityValue = NO;
self.isVisible = NO;
}
- (void)setupConstraints {}
- (void)setDrivingOptionsState:(MWMDrivingOptionsState)state {
_drivingOptionsState = state;
[self layoutIfNeeded];
self.drivingOptionHeightConstraint.constant =
(state == MWMDrivingOptionsStateNone) ? -kDrivingOptionsHeight : 0;
[UIView animateWithDuration:kDefaultAnimationDuration animations:^{
[self layoutIfNeeded];
}];
if (state == MWMDrivingOptionsStateDefine) {
[self.drivingOptionsButton setImagePadding:0.0];
[self.drivingOptionsButton setImage:nil
forState:UIControlStateNormal];
[self.drivingOptionsButton setTitle:L(@"define_to_avoid_btn").uppercaseString
forState:UIControlStateNormal];
} else if (state == MWMDrivingOptionsStateChange) {
[self.drivingOptionsButton setImagePadding:5.0];
[self.drivingOptionsButton setImage:[UIImage imageNamed:@"ic_options_warning"]
forState:UIControlStateNormal];
[self.drivingOptionsButton setTitle:L(@"change_driving_options_btn").uppercaseString
forState:UIControlStateNormal];
}
}
#pragma mark - Properties
- (void)setIsVisible:(BOOL)isVisible
{
if (isVisible != self.actualVisibilityValue) { return; }
_isVisible = isVisible;
auto sv = self.superview;
[sv setNeedsLayout];
[UIView animateWithDuration:kDefaultAnimationDuration
animations:^{
[sv layoutIfNeeded];
}
completion:^(BOOL finished) {
if (!self.isVisible)
[self removeFromSuperview];
}];
}
@end

View file

@ -0,0 +1,5 @@
#import "MWMRoutePreview.h"
@interface MWMiPadRoutePreview : MWMRoutePreview
@end

View file

@ -0,0 +1,97 @@
#import "MWMiPadRoutePreview.h"
#import "MWMRouter.h"
#import "MWMAvailableAreaAffectDirection.h"
@interface MWMRoutePreview ()
@property(nonatomic) BOOL isVisible;
@end
@interface MWMiPadRoutePreview ()
@property(nonatomic) NSLayoutConstraint * horizontalConstraint;
@end
@implementation MWMiPadRoutePreview
- (void)setupConstraints
{
UIView * sv = self.superview;
if (sv)
{
[self.topAnchor constraintEqualToAnchor:sv.topAnchor].active = YES;
[self.bottomAnchor constraintEqualToAnchor:sv.bottomAnchor].active = YES;
self.horizontalConstraint = [self.trailingAnchor constraintEqualToAnchor:sv.leadingAnchor];
self.horizontalConstraint.active = YES;
}
}
- (void)setIsVisible:(BOOL)isVisible
{
self.horizontalConstraint.active = NO;
UIView * sv = self.superview;
if (sv)
{
NSLayoutXAxisAnchor * selfAnchor = isVisible ? self.leadingAnchor : self.trailingAnchor;
self.horizontalConstraint = [selfAnchor constraintEqualToAnchor:sv.leadingAnchor];
self.horizontalConstraint.active = YES;
}
[super setIsVisible:isVisible];
}
#pragma mark - SolidTouchView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {}
#pragma mark - AvailableArea / VisibleArea
- (MWMAvailableAreaAffectDirections)visibleAreaAffectDirections
{
return MWMAvailableAreaAffectDirectionsLeft;
}
#pragma mark - AvailableArea / PlacePageArea
- (MWMAvailableAreaAffectDirections)placePageAreaAffectDirections
{
return MWMAvailableAreaAffectDirectionsLeft;
}
#pragma mark - AvailableArea / WidgetsArea
- (MWMAvailableAreaAffectDirections)widgetsAreaAffectDirections
{
return MWMAvailableAreaAffectDirectionsLeft;
}
#pragma mark - AvailableArea / SideButtonsArea
- (MWMAvailableAreaAffectDirections)sideButtonsAreaAffectDirections
{
return MWMAvailableAreaAffectDirectionsLeft;
}
#pragma mark - AvailableArea / TrafficButtonArea
- (MWMAvailableAreaAffectDirections)trafficButtonAreaAffectDirections
{
return MWMAvailableAreaAffectDirectionsLeft;
}
#pragma mark - AvailableArea / NavigationInfoArea
- (MWMAvailableAreaAffectDirections)navigationInfoAreaAffectDirections
{
return MWMAvailableAreaAffectDirectionsLeft;
}
#pragma mark - AvailableArea / TrackRecordingButtonArea
- (MWMAvailableAreaAffectDirections)trackRecordingButtonAreaAffectDirections
{
return MWMAvailableAreaAffectDirectionsRight;
}
@end

View file

@ -0,0 +1,469 @@
<?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="retina4_7" orientation="portrait" 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" customClass="MWMNavigationDashboardManager">
<connections>
<outlet property="baseRoutePreviewStatus" destination="87p-Qg-8f3" id="ddh-nm-Ux9"/>
<outlet property="goButtonsContainer" destination="gcR-zj-b7P" id="RuF-Qu-ahX"/>
<outlet property="routePreview" destination="u2u-Vb-2eH" id="VZw-5q-2P6"/>
<outlet property="showRouteManagerButton" destination="NAs-km-8uw" id="pJx-0l-Skz"/>
<outlet property="transportRoutePreviewStatus" destination="FXb-tH-ZTF" id="P0g-ys-6wW"/>
<outletCollection property="goButtons" destination="4IJ-pR-Ztp" id="ePc-jh-SfE"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="u2u-Vb-2eH" customClass="MWMiPadRoutePreview">
<rect key="frame" x="0.0" y="0.0" width="320" height="667"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3zr-me-gbI">
<rect key="frame" x="0.0" y="0.0" width="320" height="20"/>
<color key="backgroundColor" red="0.12156862745098039" green="0.59999999999999998" blue="0.32156862745098036" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="0AS-cy-xIW"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="PrimaryBackground"/>
</userDefinedRuntimeAttributes>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="l7E-Ns-2Nn" userLabel="Options View">
<rect key="frame" x="0.0" y="68" width="320" height="48"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Opi-yT-xIZ">
<rect key="frame" x="0.0" y="0.0" width="320" height="48"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="12"/>
<state key="normal" title="DEFINE ROADS TO AVOID" image="ic_options_warning"/>
<connections>
<action selector="onDrivingOptions:" destination="u2u-Vb-2eH" eventType="touchUpInside" id="x2p-AW-91V"/>
</connections>
</button>
</subviews>
<viewLayoutGuide key="safeArea" id="THL-V3-3xS"/>
<color key="backgroundColor" red="0.1215686275" green="0.59999999999999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="Opi-yT-xIZ" firstAttribute="centerX" secondItem="l7E-Ns-2Nn" secondAttribute="centerX" id="5DK-9d-8qb"/>
<constraint firstItem="Opi-yT-xIZ" firstAttribute="centerY" secondItem="l7E-Ns-2Nn" secondAttribute="centerY" id="JFn-Vg-Wby"/>
<constraint firstItem="THL-V3-3xS" firstAttribute="bottom" secondItem="Opi-yT-xIZ" secondAttribute="bottom" id="LbO-Ze-Pwl"/>
<constraint firstItem="Opi-yT-xIZ" firstAttribute="top" secondItem="THL-V3-3xS" secondAttribute="top" id="OnE-Yh-Inw"/>
<constraint firstAttribute="trailing" secondItem="Opi-yT-xIZ" secondAttribute="trailing" id="aCy-jn-U2B"/>
<constraint firstAttribute="height" constant="48" id="mCi-1V-xX4"/>
<constraint firstItem="Opi-yT-xIZ" firstAttribute="leading" secondItem="l7E-Ns-2Nn" secondAttribute="leading" id="vdk-Ef-Kk1"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="SecondaryBackground"/>
</userDefinedRuntimeAttributes>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3s2-BV-X5i" customClass="SolidTouchView">
<rect key="frame" x="0.0" y="20" width="320" height="48"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="a4n-5I-PN3" userLabel="Back">
<rect key="frame" x="0.0" y="0.0" width="48" height="48"/>
<accessibility key="accessibilityConfiguration" identifier="goBackButton"/>
<constraints>
<constraint firstAttribute="width" secondItem="a4n-5I-PN3" secondAttribute="height" multiplier="1:1" id="eKl-2V-DBC"/>
</constraints>
<state key="normal" image="ic_nav_bar_back"/>
<connections>
<action selector="stopRoutingButtonAction" destination="-1" eventType="touchUpInside" id="TRy-DB-zyD"/>
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sXu-tl-a0m" userLabel="Buttons Box">
<rect key="frame" x="62" y="4" width="196" height="40"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jDl-pu-eov">
<rect key="frame" x="6" y="0.0" width="40" height="40"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="routeAuto"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="1qk-1F-lIB"/>
<constraint firstAttribute="width" secondItem="jDl-pu-eov" secondAttribute="height" multiplier="1:1" id="Yqu-oc-yrg"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="VhE-hA-Leo">
<rect key="frame" x="54" y="0.0" width="40" height="40"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<accessibility key="accessibilityConfiguration" identifier="routePedestrian"/>
<constraints>
<constraint firstAttribute="width" secondItem="VhE-hA-Leo" secondAttribute="height" multiplier="1:1" id="6lS-2K-Gki"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Eu3-bT-Dom">
<rect key="frame" x="102" y="0.0" width="40" height="40"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="routeAuto"/>
<constraints>
<constraint firstAttribute="width" secondItem="Eu3-bT-Dom" secondAttribute="height" multiplier="1:1" id="iam-D8-1uB"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="yiM-fM-sSS" userLabel="Bicycle">
<rect key="frame" x="150" y="0.0" width="40" height="40"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="routeBicycle"/>
<constraints>
<constraint firstAttribute="width" secondItem="yiM-fM-sSS" secondAttribute="height" multiplier="1:1" id="1N1-y5-Mwc"/>
</constraints>
</view>
<view contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="f6i-lw-K3R" userLabel="Ruler">
<rect key="frame" x="198" y="0.0" width="40" height="40"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<accessibility key="accessibilityConfiguration" identifier="routeRuler"/>
<constraints>
<constraint firstAttribute="width" secondItem="f6i-lw-K3R" secondAttribute="height" multiplier="1:1" id="iex-7v-w3k"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="yiM-fM-sSS" secondAttribute="trailing" priority="750" constant="6" id="5D1-qi-hgK"/>
<constraint firstItem="VhE-hA-Leo" firstAttribute="leading" secondItem="jDl-pu-eov" secondAttribute="trailing" constant="8" id="7Gp-G4-bFf"/>
<constraint firstItem="f6i-lw-K3R" firstAttribute="top" secondItem="sXu-tl-a0m" secondAttribute="top" id="BWr-hr-pwo"/>
<constraint firstAttribute="trailing" secondItem="f6i-lw-K3R" secondAttribute="trailing" constant="6" id="Cd3-ev-uFS"/>
<constraint firstItem="Eu3-bT-Dom" firstAttribute="top" secondItem="sXu-tl-a0m" secondAttribute="top" id="EzA-7U-VtV"/>
<constraint firstAttribute="bottom" secondItem="jDl-pu-eov" secondAttribute="bottom" id="GaQ-fH-vHQ"/>
<constraint firstAttribute="bottom" secondItem="Eu3-bT-Dom" secondAttribute="bottom" id="TKM-OQ-dSV"/>
<constraint firstItem="f6i-lw-K3R" firstAttribute="leading" secondItem="yiM-fM-sSS" secondAttribute="trailing" constant="8" id="Zs5-bj-AR6"/>
<constraint firstItem="Eu3-bT-Dom" firstAttribute="leading" secondItem="VhE-hA-Leo" secondAttribute="trailing" constant="8" id="a29-bx-wGD"/>
<constraint firstItem="jDl-pu-eov" firstAttribute="leading" secondItem="sXu-tl-a0m" secondAttribute="leading" constant="6" id="kNf-mc-JqH"/>
<constraint firstAttribute="bottom" secondItem="yiM-fM-sSS" secondAttribute="bottom" id="mYY-Hb-4eW"/>
<constraint firstItem="yiM-fM-sSS" firstAttribute="leading" secondItem="Eu3-bT-Dom" secondAttribute="trailing" constant="8" id="obj-Bs-U8h"/>
<constraint firstItem="yiM-fM-sSS" firstAttribute="top" secondItem="sXu-tl-a0m" secondAttribute="top" id="pRA-lu-I23"/>
<constraint firstItem="VhE-hA-Leo" firstAttribute="top" secondItem="sXu-tl-a0m" secondAttribute="top" id="pt4-Mg-zye"/>
<constraint firstAttribute="bottom" secondItem="f6i-lw-K3R" secondAttribute="bottom" id="qTV-BL-cX4"/>
<constraint firstAttribute="bottom" secondItem="VhE-hA-Leo" secondAttribute="bottom" id="scR-oW-NgF"/>
<constraint firstItem="jDl-pu-eov" firstAttribute="top" secondItem="sXu-tl-a0m" secondAttribute="top" id="sf1-S5-kmZ"/>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="Cd3-ev-uFS"/>
<exclude reference="qTV-BL-cX4"/>
<exclude reference="BWr-hr-pwo"/>
<exclude reference="Zs5-bj-AR6"/>
</mask>
</variation>
</view>
</subviews>
<color key="backgroundColor" red="0.1215686275" green="0.59999999999999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="sXu-tl-a0m" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="a4n-5I-PN3" secondAttribute="trailing" constant="4" id="2EB-6T-LIW"/>
<constraint firstAttribute="width" constant="320" id="80z-it-OLr"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="sXu-tl-a0m" secondAttribute="trailing" constant="4" id="Hj0-Nl-uaa"/>
<constraint firstAttribute="bottom" secondItem="a4n-5I-PN3" secondAttribute="bottom" id="J5F-jB-qyT"/>
<constraint firstItem="sXu-tl-a0m" firstAttribute="centerX" secondItem="3s2-BV-X5i" secondAttribute="centerX" priority="750" id="OeL-MY-Mn5"/>
<constraint firstItem="sXu-tl-a0m" firstAttribute="top" secondItem="3s2-BV-X5i" secondAttribute="top" constant="4" id="Q5o-OR-FCh"/>
<constraint firstItem="a4n-5I-PN3" firstAttribute="leading" secondItem="3s2-BV-X5i" secondAttribute="leading" id="Wxp-Yl-6SC"/>
<constraint firstItem="a4n-5I-PN3" firstAttribute="top" secondItem="3s2-BV-X5i" secondAttribute="top" id="ppW-Pq-6rh"/>
<constraint firstAttribute="bottom" secondItem="sXu-tl-a0m" secondAttribute="bottom" constant="4" id="wEf-uh-sfy"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="PrimaryBackground"/>
</userDefinedRuntimeAttributes>
</view>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="87p-Qg-8f3" customClass="MWMBaseRoutePreviewStatus">
<rect key="frame" x="0.0" y="152" width="320" height="180"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QEP-6s-YTM" userLabel="Error Box">
<rect key="frame" x="0.0" y="0.0" width="320" height="64"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Planning..." lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="14" translatesAutoresizingMaskIntoConstraints="NO" id="VNi-4g-9gz" userLabel="Error">
<rect key="frame" x="120" y="22" width="80" height="20"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<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="localizedText" value="routing_planning_error"/>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:redText"/>
</userDefinedRuntimeAttributes>
</label>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="64" id="DuS-FX-e47"/>
<constraint firstItem="VNi-4g-9gz" firstAttribute="centerY" secondItem="QEP-6s-YTM" secondAttribute="centerY" id="bfa-sc-ifi"/>
<constraint firstItem="VNi-4g-9gz" firstAttribute="centerX" secondItem="QEP-6s-YTM" secondAttribute="centerX" id="iKJ-3u-Al9"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Rqr-bu-crx" userLabel="Results Box">
<rect key="frame" x="0.0" y="0.0" width="320" height="64"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="results" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zml-eW-DsI">
<rect key="frame" x="16" y="14" width="248" height="20"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<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="regular17:blackSecondaryText"/>
</userDefinedRuntimeAttributes>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Arrive at 12:24" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="oec-Ee-6ha">
<rect key="frame" x="16" y="38" width="248" height="17"/>
<fontDescription key="fontDescription" type="system" weight="medium" 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="medium14:blackSecondaryText"/>
</userDefinedRuntimeAttributes>
</label>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="oec-Ee-6ha" firstAttribute="leading" secondItem="zml-eW-DsI" secondAttribute="leading" id="Am2-25-Zn9"/>
<constraint firstItem="zml-eW-DsI" firstAttribute="leading" secondItem="Rqr-bu-crx" secondAttribute="leading" constant="16" id="QNo-az-LlD"/>
<constraint firstAttribute="height" constant="64" id="bHW-iR-rAA"/>
<constraint firstItem="zml-eW-DsI" firstAttribute="top" secondItem="Rqr-bu-crx" secondAttribute="top" constant="14" id="c1k-EY-nwQ"/>
<constraint firstItem="oec-Ee-6ha" firstAttribute="top" secondItem="zml-eW-DsI" secondAttribute="bottom" constant="4" id="lV9-S0-yy4"/>
<constraint firstItem="oec-Ee-6ha" firstAttribute="trailing" secondItem="zml-eW-DsI" secondAttribute="trailing" id="rfO-2Z-g12"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6iM-A6-4JK" userLabel="Height Box">
<rect key="frame" x="0.0" y="64" width="320" height="68"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rXP-rM-9cx">
<rect key="frame" x="268" y="-48.5" width="36" height="17"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="260" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JtS-om-arD">
<rect key="frame" x="0.0" y="0.0" width="36" height="17"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium14:linkBlueText"/>
</userDefinedRuntimeAttributes>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="JtS-om-arD" firstAttribute="leading" secondItem="rXP-rM-9cx" secondAttribute="leading" id="6FT-YG-m3k"/>
<constraint firstAttribute="bottom" secondItem="JtS-om-arD" secondAttribute="bottom" id="B8p-TU-ROs"/>
<constraint firstAttribute="trailing" secondItem="JtS-om-arD" secondAttribute="trailing" id="CK8-gu-qaf"/>
<constraint firstItem="JtS-om-arD" firstAttribute="top" secondItem="rXP-rM-9cx" secondAttribute="top" id="szb-xc-aoe"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Apa-nH-KWJ">
<rect key="frame" x="16" y="14" width="288" height="40"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="zhI-92-6JG">
<rect key="frame" x="0.0" y="0.0" width="288" height="40"/>
</imageView>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="zhI-92-6JG" firstAttribute="top" secondItem="Apa-nH-KWJ" secondAttribute="top" id="7CP-bg-Id3"/>
<constraint firstAttribute="bottom" secondItem="zhI-92-6JG" secondAttribute="bottom" id="LeB-Rl-mPU"/>
<constraint firstItem="zhI-92-6JG" firstAttribute="leading" secondItem="Apa-nH-KWJ" secondAttribute="leading" id="OuL-Na-mKo"/>
<constraint firstAttribute="trailing" secondItem="zhI-92-6JG" secondAttribute="trailing" id="y82-L8-vTL"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="Apa-nH-KWJ" secondAttribute="bottom" constant="14" id="8P4-RM-XZb"/>
<constraint firstAttribute="height" constant="68" id="Fl4-Tb-OER"/>
<constraint firstItem="Apa-nH-KWJ" firstAttribute="top" secondItem="6iM-A6-4JK" secondAttribute="top" constant="14" id="TNG-pv-uE8"/>
<constraint firstAttribute="trailing" secondItem="Apa-nH-KWJ" secondAttribute="trailing" constant="16" id="XSN-g6-2W4"/>
<constraint firstItem="Apa-nH-KWJ" firstAttribute="leading" secondItem="6iM-A6-4JK" secondAttribute="leading" constant="16" id="cR5-Kq-pEJ"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="EY6-SY-ner">
<rect key="frame" x="0.0" y="132" width="320" height="48"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LyQ-pQ-XyG">
<rect key="frame" x="16" y="0.0" width="304" height="1"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="ZFh-zw-f9W"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
</userDefinedRuntimeAttributes>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NAs-km-8uw">
<rect key="frame" x="20" y="12" width="119" height="24"/>
<state key="normal" title="ManageRoute" image="ic_24px_manager"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium14:MWMBlack"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="showRouteManager" destination="-1" eventType="touchUpInside" id="eOk-zR-QEb"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="LyQ-pQ-XyG" secondAttribute="trailing" id="Inl-w6-cF4"/>
<constraint firstAttribute="height" constant="48" id="akp-Zy-mlV"/>
<constraint firstItem="LyQ-pQ-XyG" firstAttribute="top" secondItem="EY6-SY-ner" secondAttribute="top" id="cox-ec-1O6"/>
<constraint firstItem="NAs-km-8uw" firstAttribute="leading" secondItem="EY6-SY-ner" secondAttribute="leading" constant="20" id="dQV-wI-Rs3"/>
<constraint firstItem="NAs-km-8uw" firstAttribute="centerY" secondItem="EY6-SY-ner" secondAttribute="centerY" id="f3q-59-sF0"/>
<constraint firstItem="LyQ-pQ-XyG" firstAttribute="leading" secondItem="EY6-SY-ner" secondAttribute="leading" constant="16" id="fSG-dw-XFn"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" priority="100" constant="64" id="4qT-ha-p75"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="Rqr-bu-crx" secondAttribute="bottom" id="82W-n1-9wJ"/>
<constraint firstItem="EY6-SY-ner" firstAttribute="top" relation="greaterThanOrEqual" secondItem="Rqr-bu-crx" secondAttribute="bottom" id="8we-Hd-BFi"/>
<constraint firstItem="EY6-SY-ner" firstAttribute="top" secondItem="87p-Qg-8f3" secondAttribute="top" priority="100" id="DLg-g3-d3N"/>
<constraint firstItem="Rqr-bu-crx" firstAttribute="leading" secondItem="87p-Qg-8f3" secondAttribute="leading" id="Fkj-U2-xyQ"/>
<constraint firstItem="6iM-A6-4JK" firstAttribute="top" secondItem="Rqr-bu-crx" secondAttribute="bottom" id="HfZ-Ca-Mmj"/>
<constraint firstItem="QEP-6s-YTM" firstAttribute="top" secondItem="87p-Qg-8f3" secondAttribute="top" id="LrP-YM-0DL"/>
<constraint firstItem="Rqr-bu-crx" firstAttribute="top" secondItem="87p-Qg-8f3" secondAttribute="top" id="N7H-ZL-K7y"/>
<constraint firstAttribute="trailing" secondItem="Rqr-bu-crx" secondAttribute="trailing" id="QH4-pv-oKn"/>
<constraint firstAttribute="trailing" secondItem="QEP-6s-YTM" secondAttribute="trailing" id="QOg-C7-dgi"/>
<constraint firstItem="rXP-rM-9cx" firstAttribute="centerY" secondItem="zml-eW-DsI" secondAttribute="centerY" id="SAf-Qy-9uj"/>
<constraint firstItem="EY6-SY-ner" firstAttribute="top" relation="greaterThanOrEqual" secondItem="6iM-A6-4JK" secondAttribute="bottom" id="Szo-VK-580"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="6iM-A6-4JK" secondAttribute="bottom" id="a6M-h3-DzB"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="QEP-6s-YTM" secondAttribute="bottom" id="c3U-eM-8ig"/>
<constraint firstItem="rXP-rM-9cx" firstAttribute="leading" secondItem="zml-eW-DsI" secondAttribute="trailing" constant="4" id="fqI-7g-oBJ"/>
<constraint firstItem="EY6-SY-ner" firstAttribute="leading" secondItem="87p-Qg-8f3" secondAttribute="leading" id="iIn-NA-fpZ"/>
<constraint firstAttribute="trailing" secondItem="6iM-A6-4JK" secondAttribute="trailing" id="kek-Gu-EN2"/>
<constraint firstItem="Rqr-bu-crx" firstAttribute="trailing" secondItem="rXP-rM-9cx" secondAttribute="trailing" constant="16" id="mHh-IV-b00"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="EY6-SY-ner" secondAttribute="bottom" id="n6D-xw-Kge"/>
<constraint firstItem="6iM-A6-4JK" firstAttribute="leading" secondItem="87p-Qg-8f3" secondAttribute="leading" id="oeU-OH-ezY"/>
<constraint firstAttribute="trailing" secondItem="EY6-SY-ner" secondAttribute="trailing" id="u9T-89-RZi"/>
<constraint firstItem="QEP-6s-YTM" firstAttribute="leading" secondItem="87p-Qg-8f3" secondAttribute="leading" id="x6v-27-R7O"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="RouteBasePreview"/>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="arriveLabel" destination="oec-Ee-6ha" id="FLF-8X-PAf"/>
<outlet property="errorBox" destination="QEP-6s-YTM" id="cV6-hZ-IMv"/>
<outlet property="errorBoxBottom" destination="c3U-eM-8ig" id="oyN-uq-qC5"/>
<outlet property="errorLabel" destination="VNi-4g-9gz" id="BPc-Ib-vTt"/>
<outlet property="heightBox" destination="6iM-A6-4JK" id="pYC-Jp-UQT"/>
<outlet property="heightBoxBottom" destination="a6M-h3-DzB" id="qEw-fx-cle"/>
<outlet property="heightBoxBottomManageRouteBoxTop" destination="Szo-VK-580" id="niN-mR-8WR"/>
<outlet property="heightProfileElevationHeight" destination="JtS-om-arD" id="a8J-z7-RsP"/>
<outlet property="heightProfileImage" destination="zhI-92-6JG" id="MKv-4F-F9A"/>
<outlet property="manageRouteBox" destination="EY6-SY-ner" id="Yhf-jH-avh"/>
<outlet property="manageRouteBoxBottom" destination="n6D-xw-Kge" id="07u-YL-OU1"/>
<outlet property="manageRouteButtonRegular" destination="NAs-km-8uw" id="nTT-uS-9ud"/>
<outlet property="resultLabel" destination="zml-eW-DsI" id="lP5-pC-IIe"/>
<outlet property="resultsBox" destination="Rqr-bu-crx" id="iaF-Im-C5p"/>
<outlet property="resultsBoxBottom" destination="82W-n1-9wJ" id="RLp-VX-RD7"/>
</connections>
</view>
<view contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="FXb-tH-ZTF" customClass="MWMTransportRoutePreviewStatus">
<rect key="frame" x="0.0" y="152" width="320" height="80"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tuZ-25-ltG">
<rect key="frame" x="16" y="12" width="42" height="20"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<collectionView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" ambiguous="YES" alwaysBounceVertical="YES" alwaysBounceHorizontal="YES" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" delaysContentTouches="NO" canCancelContentTouches="NO" bouncesZoom="NO" dataMode="none" prefetchingEnabled="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8Ey-lL-uzF" customClass="TransportTransitStepsCollectionView" customModule="CoMaps" customModuleProvider="target">
<rect key="frame" x="16" y="44" width="288" height="20"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="4NI-xp-o3L"/>
</constraints>
<collectionViewLayout key="collectionViewLayout" id="PJv-Hi-Xu3" customClass="TransportTransitFlowLayout" customModule="CoMaps" customModuleProvider="target"/>
</collectionView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="tuZ-25-ltG" firstAttribute="top" secondItem="FXb-tH-ZTF" secondAttribute="top" constant="12" id="1Aj-rm-MMr"/>
<constraint firstItem="8Ey-lL-uzF" firstAttribute="top" secondItem="tuZ-25-ltG" secondAttribute="bottom" constant="12" id="CKR-P5-B2a"/>
<constraint firstItem="tuZ-25-ltG" firstAttribute="leading" secondItem="FXb-tH-ZTF" secondAttribute="leading" constant="16" id="GJC-Gt-255"/>
<constraint firstAttribute="bottom" secondItem="8Ey-lL-uzF" secondAttribute="bottom" constant="16" id="KxL-xf-eVL"/>
<constraint firstAttribute="trailing" secondItem="8Ey-lL-uzF" secondAttribute="trailing" constant="16" id="ejR-Po-Dxm"/>
<constraint firstItem="8Ey-lL-uzF" firstAttribute="leading" secondItem="tuZ-25-ltG" secondAttribute="leading" id="gkU-xQ-hb2"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" priority="100" constant="40" id="lLi-5o-PDI"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="RouteBasePreview"/>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="etaLabel" destination="tuZ-25-ltG" id="Rwo-UC-mvq"/>
<outlet property="stepsCollectionView" destination="8Ey-lL-uzF" id="qrI-Cw-rAJ"/>
<outlet property="stepsCollectionViewHeight" destination="4NI-xp-o3L" id="Z11-UU-Mca"/>
</connections>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="gcR-zj-b7P">
<rect key="frame" x="0.0" y="619" width="320" height="48"/>
<subviews>
<button hidden="YES" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="tailTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4IJ-pR-Ztp" userLabel="Go" customClass="MWMRouteStartButton">
<rect key="frame" x="0.0" y="0.0" width="320" height="48"/>
<accessibility key="accessibilityConfiguration" identifier="goButton"/>
<inset key="contentEdgeInsets" minX="8" minY="0.0" maxX="8" maxY="0.0"/>
<state key="normal" title="Start"/>
<state key="selected">
<color key="titleColor" red="0.0" green="0.0" blue="0.0" alpha="0.26000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="FlatNormalButtonBig"/>
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="p2p_start"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="routingStartTouchUpInside" destination="-1" eventType="touchUpInside" id="S0p-CT-EfI"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.80000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="48" id="5GN-4g-vV1"/>
<constraint firstAttribute="trailing" secondItem="4IJ-pR-Ztp" secondAttribute="trailing" id="ML1-Xc-dlH"/>
<constraint firstItem="4IJ-pR-Ztp" firstAttribute="top" secondItem="gcR-zj-b7P" secondAttribute="top" id="O3s-Ji-gI9"/>
<constraint firstItem="4IJ-pR-Ztp" firstAttribute="leading" secondItem="gcR-zj-b7P" secondAttribute="leading" id="VKp-pp-FKT"/>
<constraint firstAttribute="bottom" secondItem="4IJ-pR-Ztp" secondAttribute="bottom" id="iof-B4-bI3"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="RouteBasePreview"/>
</userDefinedRuntimeAttributes>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="rhh-dY-ZHT"/>
<color key="backgroundColor" red="0.96078431372549022" green="0.96078431372549022" blue="0.96078431372549022" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="gcR-zj-b7P" firstAttribute="leading" secondItem="u2u-Vb-2eH" secondAttribute="leading" id="1RD-kG-W2O"/>
<constraint firstAttribute="trailing" secondItem="87p-Qg-8f3" secondAttribute="trailing" id="5wh-lx-rUz"/>
<constraint firstItem="3s2-BV-X5i" firstAttribute="top" secondItem="3zr-me-gbI" secondAttribute="bottom" id="6c0-tH-qlJ"/>
<constraint firstItem="87p-Qg-8f3" firstAttribute="top" secondItem="l7E-Ns-2Nn" secondAttribute="bottom" constant="36" id="8nt-tf-wyq"/>
<constraint firstItem="FXb-tH-ZTF" firstAttribute="top" secondItem="l7E-Ns-2Nn" secondAttribute="bottom" constant="36" id="9Bz-NL-px6"/>
<constraint firstItem="87p-Qg-8f3" firstAttribute="leading" secondItem="u2u-Vb-2eH" secondAttribute="leading" id="9bA-9R-pwq"/>
<constraint firstItem="l7E-Ns-2Nn" firstAttribute="leading" secondItem="3s2-BV-X5i" secondAttribute="leading" id="By5-zt-x6R"/>
<constraint firstItem="l7E-Ns-2Nn" firstAttribute="top" secondItem="3s2-BV-X5i" secondAttribute="bottom" id="Cgo-cU-dTy"/>
<constraint firstItem="FXb-tH-ZTF" firstAttribute="leading" secondItem="u2u-Vb-2eH" secondAttribute="leading" id="Mmr-RH-iDm"/>
<constraint firstAttribute="trailing" secondItem="3zr-me-gbI" secondAttribute="trailing" id="OZ2-dP-mna"/>
<constraint firstAttribute="bottom" secondItem="gcR-zj-b7P" secondAttribute="bottom" id="QYs-r4-Jyl"/>
<constraint firstItem="l7E-Ns-2Nn" firstAttribute="trailing" secondItem="3s2-BV-X5i" secondAttribute="trailing" id="YI5-Hs-HZl"/>
<constraint firstItem="3s2-BV-X5i" firstAttribute="leading" secondItem="u2u-Vb-2eH" secondAttribute="leading" id="e90-R4-YQR"/>
<constraint firstItem="3zr-me-gbI" firstAttribute="top" secondItem="u2u-Vb-2eH" secondAttribute="top" id="hE5-50-mMA"/>
<constraint firstAttribute="trailing" secondItem="3s2-BV-X5i" secondAttribute="trailing" id="lnu-7l-aPG"/>
<constraint firstAttribute="trailing" secondItem="gcR-zj-b7P" secondAttribute="trailing" id="n9j-dG-kOB"/>
<constraint firstAttribute="trailing" secondItem="FXb-tH-ZTF" secondAttribute="trailing" id="sFK-Dz-Ta2"/>
<constraint firstItem="3zr-me-gbI" firstAttribute="leading" secondItem="u2u-Vb-2eH" secondAttribute="leading" id="xgY-7G-jgv"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="RoutePreview"/>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="backButton" destination="a4n-5I-PN3" id="42W-5B-Z8i"/>
<outlet property="bicycle" destination="yiM-fM-sSS" id="pMf-6J-9Gd"/>
<outlet property="contentView" destination="3s2-BV-X5i" id="mp7-Qq-drU"/>
<outlet property="drivingOptionHeightConstraint" destination="Cgo-cU-dTy" id="GVj-A8-kR1"/>
<outlet property="drivingOptionsButton" destination="Opi-yT-xIZ" id="tMG-Af-HEo"/>
<outlet property="pedestrian" destination="VhE-hA-Leo" id="R3O-th-Jw0"/>
<outlet property="publicTransport" destination="Eu3-bT-Dom" id="XNh-uW-Kog"/>
<outlet property="ruler" destination="f6i-lw-K3R" id="s0G-le-Kbz"/>
<outlet property="vehicle" destination="jDl-pu-eov" id="awM-KI-2xO"/>
</connections>
<point key="canvasLocation" x="448" y="573.46326836581716"/>
</view>
</objects>
<resources>
<image name="ic_24px_manager" width="24" height="24"/>
<image name="ic_nav_bar_back" width="14" height="22"/>
<image name="ic_options_warning" width="24" height="24"/>
</resources>
</document>

View file

@ -0,0 +1,5 @@
#import "MWMRoutePreview.h"
@interface MWMiPhoneRoutePreview : MWMRoutePreview
@end

View file

@ -0,0 +1,78 @@
#import "MWMiPhoneRoutePreview.h"
#import "MWMAvailableAreaAffectDirection.h"
@interface MWMRoutePreview ()
@property(nonatomic) BOOL isVisible;
@end
@interface MWMiPhoneRoutePreview ()
@property(weak, nonatomic) IBOutlet UIButton * backButton;
@property(nonatomic) NSLayoutConstraint * verticalConstraint;
@end
@implementation MWMiPhoneRoutePreview
- (void)setupConstraints
{
UIView * sv = self.superview;
[self.leadingAnchor constraintEqualToAnchor:sv.leadingAnchor].active = YES;
[self.trailingAnchor constraintEqualToAnchor:sv.trailingAnchor].active = YES;
self.verticalConstraint = [self.bottomAnchor constraintEqualToAnchor:sv.topAnchor];
self.verticalConstraint.active = YES;
NSLayoutXAxisAnchor * backLeadingAnchor = sv.leadingAnchor;
backLeadingAnchor = sv.safeAreaLayoutGuide.leadingAnchor;
[self.backButton.leadingAnchor constraintEqualToAnchor:backLeadingAnchor].active = YES;
[sv layoutIfNeeded];
}
- (void)setIsVisible:(BOOL)isVisible
{
UIView * sv = self.superview;
if (!sv)
return;
self.verticalConstraint.active = NO;
NSLayoutYAxisAnchor * topAnchor = sv.topAnchor;
NSLayoutYAxisAnchor * selfAnchor = isVisible ? self.topAnchor : self.bottomAnchor;
CGFloat constant = 0;
if (isVisible)
{
topAnchor = sv.topAnchor;
}
self.verticalConstraint = [selfAnchor constraintEqualToAnchor:topAnchor constant:constant];
self.verticalConstraint.active = YES;
[super setIsVisible:isVisible];
}
#pragma mark - AvailableArea / VisibleArea
- (MWMAvailableAreaAffectDirections)visibleAreaAffectDirections
{
return MWMAvailableAreaAffectDirectionsTop;
}
#pragma mark - AvailableArea / SideButtonsArea
- (MWMAvailableAreaAffectDirections)sideButtonsAreaAffectDirections
{
return MWMAvailableAreaAffectDirectionsTop;
}
#pragma mark - AvailableArea / TrafficButtonArea
- (MWMAvailableAreaAffectDirections)trafficButtonAreaAffectDirections
{
return MWMAvailableAreaAffectDirectionsTop;
}
#pragma mark - AvailableArea / TrackRecordingButtonArea
- (MWMAvailableAreaAffectDirections)trackRecordingButtonAreaAffectDirections
{
return MWMAvailableAreaAffectDirectionsTop;
}
@end

View file

@ -0,0 +1,504 @@
<?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="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
<capability name="Image references" minToolsVersion="12.0"/>
<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="MWMNavigationDashboardManager">
<connections>
<outlet property="baseRoutePreviewStatus" destination="hIE-BJ-nFm" id="Kwy-sL-krz"/>
<outlet property="routePreview" destination="aNH-vh-DPz" id="ORz-pz-7sV"/>
<outlet property="transportRoutePreviewStatus" destination="iWi-pM-AJF" id="ah9-rm-QeN"/>
<outletCollection property="goButtons" destination="CQB-xn-DSM" id="FPU-6h-5CN"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="aNH-vh-DPz" customClass="MWMiPhoneRoutePreview">
<rect key="frame" x="0.0" y="0.0" width="320" height="96"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ib5-qh-Cmo">
<rect key="frame" x="0.0" y="48" width="320" height="48"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ZXA-Og-q2I">
<rect key="frame" x="0.0" y="0.0" width="320" height="48"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="12"/>
<state key="normal" title="DEFINE ROADS TO AVOID" image="ic_options_warning"/>
<connections>
<action selector="onDrivingOptions:" destination="aNH-vh-DPz" eventType="touchUpInside" id="jR1-dk-nNj"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="0.12156862745098039" green="0.59999999999999998" blue="0.32156862745098036" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="ZXA-Og-q2I" secondAttribute="trailing" id="9bx-3j-h0U"/>
<constraint firstItem="ZXA-Og-q2I" firstAttribute="leading" secondItem="Ib5-qh-Cmo" secondAttribute="leading" id="IYq-Pa-Tai"/>
<constraint firstItem="ZXA-Og-q2I" firstAttribute="centerX" secondItem="Ib5-qh-Cmo" secondAttribute="centerX" id="Uq2-QA-bWZ"/>
<constraint firstAttribute="bottom" secondItem="ZXA-Og-q2I" secondAttribute="bottom" id="ga1-Mh-GwC"/>
<constraint firstItem="ZXA-Og-q2I" firstAttribute="centerY" secondItem="Ib5-qh-Cmo" secondAttribute="centerY" id="l5z-Dg-joz"/>
<constraint firstAttribute="height" constant="48" id="l8l-Ii-g5U"/>
<constraint firstItem="ZXA-Og-q2I" firstAttribute="top" secondItem="Ib5-qh-Cmo" secondAttribute="top" id="oca-09-5v4"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="SecondaryBackground"/>
</userDefinedRuntimeAttributes>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tRj-9k-ciR">
<rect key="frame" x="0.0" y="0.0" width="320" height="20"/>
<color key="backgroundColor" red="0.1215686275" green="0.59999999999999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="PrimaryBackground"/>
</userDefinedRuntimeAttributes>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="WqK-Yb-PmP" customClass="SolidTouchView">
<rect key="frame" x="0.0" y="20" width="320" height="28"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wpf-tw-Coz" userLabel="Back">
<rect key="frame" x="0.0" y="0.0" width="28" height="28"/>
<accessibility key="accessibilityConfiguration" identifier="goBackButton"/>
<constraints>
<constraint firstAttribute="width" secondItem="wpf-tw-Coz" secondAttribute="height" multiplier="1:1" id="CvM-v4-Hlm"/>
</constraints>
<state key="normal" image="ic_nav_bar_back"/>
<connections>
<action selector="stopRoutingButtonAction" destination="-1" eventType="touchUpInside" id="64T-4A-qPD"/>
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oQc-l8-sZH" userLabel="Buttons Box">
<rect key="frame" x="78" y="4" width="164" height="20"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="cZF-Ha-2tB">
<rect key="frame" x="6" y="0.0" width="40" height="40"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="routeAuto"/>
<constraints>
<constraint firstAttribute="width" secondItem="cZF-Ha-2tB" secondAttribute="height" multiplier="1:1" id="grh-9Q-TnW"/>
<constraint firstAttribute="height" constant="40" id="mp7-b4-xot">
<variation key="heightClass=compact" constant="36"/>
</constraint>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6D3-QF-6wm">
<rect key="frame" x="54" y="0.0" width="20" height="20"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<accessibility key="accessibilityConfiguration" identifier="routePedestrian"/>
<constraints>
<constraint firstAttribute="width" secondItem="6D3-QF-6wm" secondAttribute="height" multiplier="1:1" id="zmQ-m7-kqG"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="yIt-eq-pV5">
<rect key="frame" x="82" y="0.0" width="20" height="20"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="routeAuto"/>
<constraints>
<constraint firstAttribute="width" secondItem="yIt-eq-pV5" secondAttribute="height" multiplier="1:1" id="G9H-0c-fBM"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="FuO-c6-y9C" userLabel="Bicycle">
<rect key="frame" x="110" y="0.0" width="20" height="20"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="routeBicycle"/>
<constraints>
<constraint firstAttribute="width" secondItem="FuO-c6-y9C" secondAttribute="height" multiplier="1:1" id="2Fj-re-jjm"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="15Q-ZN-NzE" userLabel="Ruler">
<rect key="frame" x="138" y="0.0" width="20" height="20"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<accessibility key="accessibilityConfiguration" identifier="routeRuler"/>
<constraints>
<constraint firstAttribute="width" secondItem="15Q-ZN-NzE" secondAttribute="height" multiplier="1:1" id="8os-Sl-i57"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="6D3-QF-6wm" secondAttribute="bottom" id="6FS-Pq-Ued"/>
<constraint firstItem="FuO-c6-y9C" firstAttribute="top" secondItem="oQc-l8-sZH" secondAttribute="top" id="A7a-5f-vcS"/>
<constraint firstAttribute="bottom" secondItem="cZF-Ha-2tB" secondAttribute="bottom" id="CUK-cL-Kgn"/>
<constraint firstAttribute="bottom" secondItem="15Q-ZN-NzE" secondAttribute="bottom" id="D1r-cA-jTR"/>
<constraint firstItem="cZF-Ha-2tB" firstAttribute="top" secondItem="oQc-l8-sZH" secondAttribute="top" id="Gph-Eh-TU4"/>
<constraint firstItem="yIt-eq-pV5" firstAttribute="top" secondItem="oQc-l8-sZH" secondAttribute="top" id="K1X-SP-LcQ"/>
<constraint firstItem="cZF-Ha-2tB" firstAttribute="leading" secondItem="oQc-l8-sZH" secondAttribute="leading" constant="6" id="MjU-UA-Kax"/>
<constraint firstAttribute="trailing" secondItem="FuO-c6-y9C" secondAttribute="trailing" priority="750" constant="6" id="Mnf-65-B96"/>
<constraint firstItem="yIt-eq-pV5" firstAttribute="leading" secondItem="6D3-QF-6wm" secondAttribute="trailing" constant="8" id="SZ8-HG-7Hp">
<variation key="heightClass=compact" constant="16"/>
</constraint>
<constraint firstItem="FuO-c6-y9C" firstAttribute="leading" secondItem="yIt-eq-pV5" secondAttribute="trailing" constant="8" id="SZW-Df-ewG">
<variation key="heightClass=compact" constant="16"/>
</constraint>
<constraint firstAttribute="trailing" secondItem="15Q-ZN-NzE" secondAttribute="trailing" constant="6" id="WHK-N0-dCc"/>
<constraint firstItem="15Q-ZN-NzE" firstAttribute="leading" secondItem="FuO-c6-y9C" secondAttribute="trailing" constant="8" id="Wve-9m-8zL">
<variation key="heightClass=compact" constant="16"/>
</constraint>
<constraint firstAttribute="bottom" secondItem="FuO-c6-y9C" secondAttribute="bottom" id="cFg-em-TQM"/>
<constraint firstAttribute="bottom" secondItem="yIt-eq-pV5" secondAttribute="bottom" id="f1O-ox-6s4"/>
<constraint firstItem="6D3-QF-6wm" firstAttribute="top" secondItem="oQc-l8-sZH" secondAttribute="top" id="heq-kc-Kpz"/>
<constraint firstItem="15Q-ZN-NzE" firstAttribute="top" secondItem="oQc-l8-sZH" secondAttribute="top" id="my7-I5-Qu3"/>
<constraint firstItem="6D3-QF-6wm" firstAttribute="leading" secondItem="cZF-Ha-2tB" secondAttribute="trailing" constant="8" id="ogb-pD-gIt">
<variation key="heightClass=compact" constant="16"/>
</constraint>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.12156862745098039" green="0.59999999999999998" blue="0.32156862745098036" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="wpf-tw-Coz" secondAttribute="bottom" id="Rvh-ZU-Cog"/>
<constraint firstItem="oQc-l8-sZH" firstAttribute="top" secondItem="WqK-Yb-PmP" secondAttribute="top" constant="4" id="SYq-dv-9hN">
<variation key="heightClass=compact" constant="2"/>
</constraint>
<constraint firstAttribute="bottom" secondItem="oQc-l8-sZH" secondAttribute="bottom" constant="4" id="VR6-xa-YSc">
<variation key="heightClass=compact" constant="2"/>
</constraint>
<constraint firstItem="wpf-tw-Coz" firstAttribute="leading" secondItem="WqK-Yb-PmP" secondAttribute="leading" priority="800" id="bK5-4Q-Rv9"/>
<constraint firstItem="wpf-tw-Coz" firstAttribute="top" secondItem="WqK-Yb-PmP" secondAttribute="top" id="cPc-fO-vAa"/>
<constraint firstItem="oQc-l8-sZH" firstAttribute="centerX" secondItem="WqK-Yb-PmP" secondAttribute="centerX" priority="750" id="ltA-tm-z3P"/>
<constraint firstItem="oQc-l8-sZH" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="wpf-tw-Coz" secondAttribute="trailing" constant="4" id="nBk-gt-mzb"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="oQc-l8-sZH" secondAttribute="trailing" constant="4" id="yBS-u8-1oX"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="PrimaryBackground"/>
</userDefinedRuntimeAttributes>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="h9I-OO-tom"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="tRj-9k-ciR" secondAttribute="trailing" id="7jY-DP-PkA"/>
<constraint firstItem="h9I-OO-tom" firstAttribute="trailing" secondItem="Ib5-qh-Cmo" secondAttribute="trailing" id="BQK-qP-OHs"/>
<constraint firstItem="WqK-Yb-PmP" firstAttribute="top" secondItem="h9I-OO-tom" secondAttribute="top" id="DDa-WE-Bpd"/>
<constraint firstItem="Ib5-qh-Cmo" firstAttribute="top" secondItem="WqK-Yb-PmP" secondAttribute="bottom" id="Oxx-fO-ZLa"/>
<constraint firstItem="Ib5-qh-Cmo" firstAttribute="leading" secondItem="h9I-OO-tom" secondAttribute="leading" id="UJ9-p8-11L"/>
<constraint firstAttribute="top" secondItem="tRj-9k-ciR" secondAttribute="top" id="eyS-x2-mYX"/>
<constraint firstItem="tRj-9k-ciR" firstAttribute="leading" secondItem="aNH-vh-DPz" secondAttribute="leading" id="gxu-Mc-DSI"/>
<constraint firstAttribute="trailing" secondItem="WqK-Yb-PmP" secondAttribute="trailing" id="hui-oa-rbV"/>
<constraint firstItem="WqK-Yb-PmP" firstAttribute="leading" secondItem="aNH-vh-DPz" secondAttribute="leading" id="rQB-KG-9Kg"/>
<constraint firstItem="h9I-OO-tom" firstAttribute="bottom" secondItem="Ib5-qh-Cmo" secondAttribute="bottom" id="wQH-cW-zFs"/>
<constraint firstItem="WqK-Yb-PmP" firstAttribute="top" secondItem="tRj-9k-ciR" secondAttribute="bottom" id="zoU-RG-pIh"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="backButton" destination="wpf-tw-Coz" id="33r-Ij-AuQ"/>
<outlet property="bicycle" destination="FuO-c6-y9C" id="UR2-pF-Amb"/>
<outlet property="contentView" destination="WqK-Yb-PmP" id="4ph-Dm-EFr"/>
<outlet property="drivingOptionHeightConstraint" destination="Oxx-fO-ZLa" id="JOa-ih-oHZ"/>
<outlet property="drivingOptionsButton" destination="ZXA-Og-q2I" id="IZf-0l-IIV"/>
<outlet property="pedestrian" destination="6D3-QF-6wm" id="bdh-zx-9LW"/>
<outlet property="publicTransport" destination="yIt-eq-pV5" id="yIX-eM-Hrs"/>
<outlet property="ruler" destination="15Q-ZN-NzE" id="Hel-ic-Opt"/>
<outlet property="vehicle" destination="cZF-Ha-2tB" id="QP3-tU-nfO"/>
</connections>
<point key="canvasLocation" x="448" y="364.31784107946032"/>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hIE-BJ-nFm" customClass="MWMBaseRoutePreviewStatus">
<rect key="frame" x="0.0" y="0.0" width="320" height="152"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="o7e-Ce-Flg">
<rect key="frame" x="-100" y="20" width="520" height="232"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="RouteBasePreview"/>
</userDefinedRuntimeAttributes>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fD2-1N-x27" userLabel="Error Box">
<rect key="frame" x="0.0" y="0.0" width="320" height="48"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Planning..." lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="14" translatesAutoresizingMaskIntoConstraints="NO" id="vGz-fB-9Oi" userLabel="Error">
<rect key="frame" x="120" y="14" width="80" height="20"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<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="localizedText" value="routing_planning_error"/>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:redText"/>
</userDefinedRuntimeAttributes>
</label>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="vGz-fB-9Oi" firstAttribute="centerX" secondItem="fD2-1N-x27" secondAttribute="centerX" id="GDN-cJ-f4O"/>
<constraint firstItem="vGz-fB-9Oi" firstAttribute="centerY" secondItem="fD2-1N-x27" secondAttribute="centerY" id="i8z-Bu-10Z"/>
<constraint firstAttribute="height" constant="48" id="uRP-aY-61z"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Tai-sE-6DC" userLabel="Results Box">
<rect key="frame" x="0.0" y="0.0" width="320" height="48"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="results" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" minimumFontSize="12" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="sjQ-Sc-mtN">
<rect key="frame" x="16" y="10" width="51" height="28"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<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="regular17:blackSecondaryText"/>
</userDefinedRuntimeAttributes>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Zzm-Yo-BvL">
<rect key="frame" x="19" y="8" width="119" height="32"/>
<state key="normal" title="ManageRoute" image="ic_24px_manager"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium14:MWMBlack"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="showRouteManager" destination="-1" eventType="touchUpInside" id="KZh-LJ-k72"/>
</connections>
</button>
<button hidden="YES" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="tailTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="CQB-xn-DSM" userLabel="Go" customClass="MWMRouteStartButton">
<rect key="frame" x="228" y="8" width="80" height="32"/>
<accessibility key="accessibilityConfiguration" identifier="goButton"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="80" id="5fJ-hA-Bme"/>
<constraint firstAttribute="height" constant="32" id="b2e-N4-xY6"/>
</constraints>
<inset key="contentEdgeInsets" minX="8" minY="0.0" maxX="8" maxY="0.0"/>
<state key="normal" title="Start"/>
<state key="selected">
<color key="titleColor" red="0.0" green="0.0" blue="0.0" alpha="0.26000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="FlatNormalButton:regular17"/>
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="p2p_start"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="routingStartTouchUpInside" destination="-1" eventType="touchUpInside" id="IWD-gV-wDp"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hoe-y0-brm" userLabel="Save Route As Track Button Compact">
<rect key="frame" x="150" y="12" width="58" height="24"/>
<state key="normal" title="Save">
<imageReference key="image" image="ic24PxImport" symbolScale="small"/>
<preferredSymbolConfiguration key="preferredSymbolConfiguration" scale="small"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium14:MWMBlack"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="saveRouteAsTrack:" destination="-1" eventType="touchUpInside" id="6eG-56-Dxp"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="CQB-xn-DSM" secondAttribute="trailing" constant="20" id="5rE-ss-qWE"/>
<constraint firstItem="CQB-xn-DSM" firstAttribute="leading" secondItem="hoe-y0-brm" secondAttribute="trailing" constant="20" id="6Lz-mp-yW2"/>
<constraint firstItem="sjQ-Sc-mtN" firstAttribute="centerY" secondItem="Tai-sE-6DC" secondAttribute="centerY" id="Aer-5j-lt1"/>
<constraint firstAttribute="trailing" secondItem="CQB-xn-DSM" secondAttribute="trailing" constant="12" id="Any-Qx-9mT"/>
<constraint firstItem="Zzm-Yo-BvL" firstAttribute="bottom" secondItem="CQB-xn-DSM" secondAttribute="bottom" id="V0p-f2-5K1"/>
<constraint firstItem="sjQ-Sc-mtN" firstAttribute="top" secondItem="Tai-sE-6DC" secondAttribute="top" constant="10" id="Vhr-Mv-2aa"/>
<constraint firstItem="CQB-xn-DSM" firstAttribute="centerY" secondItem="Tai-sE-6DC" secondAttribute="centerY" id="XI7-0e-bgf"/>
<constraint firstItem="CQB-xn-DSM" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="sjQ-Sc-mtN" secondAttribute="trailing" constant="16" id="Xqn-Rd-gR3"/>
<constraint firstItem="hoe-y0-brm" firstAttribute="leading" secondItem="Zzm-Yo-BvL" secondAttribute="trailing" constant="12" id="YnF-gq-lzg"/>
<constraint firstItem="Zzm-Yo-BvL" firstAttribute="top" secondItem="CQB-xn-DSM" secondAttribute="top" id="cyE-HM-Ovf"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="48" id="dZS-mi-2sg"/>
<constraint firstItem="sjQ-Sc-mtN" firstAttribute="leading" secondItem="Tai-sE-6DC" secondAttribute="leading" constant="16" id="gdz-UZ-QkO"/>
<constraint firstItem="CQB-xn-DSM" firstAttribute="centerY" secondItem="Zzm-Yo-BvL" secondAttribute="centerY" id="oMX-9t-8XI"/>
<constraint firstAttribute="bottom" secondItem="sjQ-Sc-mtN" secondAttribute="bottom" constant="10" id="pnl-MH-dtY"/>
<constraint firstAttribute="height" relation="lessThanOrEqual" constant="96" id="tqy-f7-E4I"/>
<constraint firstItem="hoe-y0-brm" firstAttribute="centerY" secondItem="Zzm-Yo-BvL" secondAttribute="centerY" id="via-QU-3KT"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="FBs-iT-nWY" userLabel="Height Box">
<rect key="frame" x="0.0" y="48" width="320" height="60"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Z94-gs-dwB">
<rect key="frame" x="16" y="14" width="288" height="32"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="OqA-sS-wmI">
<rect key="frame" x="0.0" y="0.0" width="288" height="32"/>
</imageView>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="OqA-sS-wmI" firstAttribute="leading" secondItem="Z94-gs-dwB" secondAttribute="leading" id="53L-rq-Bau"/>
<constraint firstItem="OqA-sS-wmI" firstAttribute="top" secondItem="Z94-gs-dwB" secondAttribute="top" id="at0-E7-wDb"/>
<constraint firstAttribute="trailing" secondItem="OqA-sS-wmI" secondAttribute="trailing" id="ooX-y2-4kT"/>
<constraint firstAttribute="bottom" secondItem="OqA-sS-wmI" secondAttribute="bottom" id="wy6-zt-Lfz"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="Z94-gs-dwB" secondAttribute="bottom" constant="14" id="GiP-PC-EQA"/>
<constraint firstAttribute="height" constant="60" id="Isf-h4-Hen"/>
<constraint firstItem="Z94-gs-dwB" firstAttribute="leading" secondItem="FBs-iT-nWY" secondAttribute="leading" constant="16" id="h5e-gY-AhN"/>
<constraint firstAttribute="trailing" secondItem="Z94-gs-dwB" secondAttribute="trailing" constant="16" id="xNl-p8-3J5"/>
<constraint firstItem="Z94-gs-dwB" firstAttribute="top" secondItem="FBs-iT-nWY" secondAttribute="top" constant="14" id="zAG-Jd-mUl"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fzb-1W-zFB">
<rect key="frame" x="0.0" y="108" width="320" height="44"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Yia-YS-2aZ">
<rect key="frame" x="-100" y="0.0" width="520" height="144"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="K37-2W-GE8">
<rect key="frame" x="20" y="10" width="119" height="24"/>
<state key="normal" title="ManageRoute" image="ic_24px_manager"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium14:MWMBlack"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="showRouteManager" destination="-1" eventType="touchUpInside" id="Nvr-ZO-h84"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aCu-AO-AFN">
<rect key="frame" x="155" y="10" width="58" height="24"/>
<state key="normal" title="Save">
<imageReference key="image" image="ic24PxImport" symbolScale="small"/>
<preferredSymbolConfiguration key="preferredSymbolConfiguration" scale="small"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium14:MWMBlack"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="saveRouteAsTrack:" destination="-1" eventType="touchUpInside" id="xQ9-QD-abF"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="K37-2W-GE8" firstAttribute="centerY" secondItem="fzb-1W-zFB" secondAttribute="centerY" id="2ok-5H-yIR"/>
<constraint firstItem="aCu-AO-AFN" firstAttribute="centerY" secondItem="K37-2W-GE8" secondAttribute="centerY" id="6hJ-pH-kXM"/>
<constraint firstAttribute="trailing" secondItem="Yia-YS-2aZ" secondAttribute="trailing" constant="-100" id="Bzr-Sg-XK6"/>
<constraint firstAttribute="height" constant="44" id="P98-xe-1Mu"/>
<constraint firstItem="Yia-YS-2aZ" firstAttribute="leading" secondItem="fzb-1W-zFB" secondAttribute="leading" constant="-100" id="PVG-Ef-KEF"/>
<constraint firstItem="K37-2W-GE8" firstAttribute="leading" secondItem="fzb-1W-zFB" secondAttribute="leading" constant="20" id="UJ8-Kj-MhJ"/>
<constraint firstAttribute="bottom" secondItem="Yia-YS-2aZ" secondAttribute="bottom" constant="-100" id="YkC-k1-TjV"/>
<constraint firstItem="Yia-YS-2aZ" firstAttribute="top" secondItem="fzb-1W-zFB" secondAttribute="top" id="ejL-T8-2eh"/>
<constraint firstItem="aCu-AO-AFN" firstAttribute="leading" secondItem="K37-2W-GE8" secondAttribute="trailing" constant="16" id="iHG-Mn-z3w"/>
</constraints>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="Mhb-kh-JDR"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="FBs-iT-nWY" secondAttribute="bottom" id="0pP-L9-PjJ"/>
<constraint firstItem="fzb-1W-zFB" firstAttribute="top" relation="greaterThanOrEqual" secondItem="FBs-iT-nWY" secondAttribute="bottom" id="939-g8-UFV"/>
<constraint firstItem="Mhb-kh-JDR" firstAttribute="trailing" secondItem="o7e-Ce-Flg" secondAttribute="trailing" constant="-100" id="AkP-e9-DhZ"/>
<constraint firstAttribute="trailing" secondItem="fD2-1N-x27" secondAttribute="trailing" id="Alb-Vc-3pV"/>
<constraint firstItem="Tai-sE-6DC" firstAttribute="top" secondItem="hIE-BJ-nFm" secondAttribute="top" id="D1o-4y-GV6"/>
<constraint firstItem="o7e-Ce-Flg" firstAttribute="leading" secondItem="Mhb-kh-JDR" secondAttribute="leading" constant="-100" id="H9m-LI-nmf"/>
<constraint firstAttribute="height" priority="100" constant="48" id="HTC-IH-1gA"/>
<constraint firstItem="fD2-1N-x27" firstAttribute="leading" secondItem="hIE-BJ-nFm" secondAttribute="leading" id="OkZ-n1-6M1"/>
<constraint firstItem="fzb-1W-zFB" firstAttribute="top" secondItem="hIE-BJ-nFm" secondAttribute="top" priority="100" id="PEO-jz-737"/>
<constraint firstItem="Mhb-kh-JDR" firstAttribute="bottom" secondItem="o7e-Ce-Flg" secondAttribute="bottom" constant="-100" id="Qif-Nu-kov"/>
<constraint firstItem="fzb-1W-zFB" firstAttribute="leading" secondItem="hIE-BJ-nFm" secondAttribute="leading" id="Tfi-eh-yc6"/>
<constraint firstItem="FBs-iT-nWY" firstAttribute="top" secondItem="Tai-sE-6DC" secondAttribute="bottom" id="UK2-a3-v7h"/>
<constraint firstItem="o7e-Ce-Flg" firstAttribute="top" secondItem="Mhb-kh-JDR" secondAttribute="top" id="WUb-NN-KSO"/>
<constraint firstAttribute="trailing" secondItem="Tai-sE-6DC" secondAttribute="trailing" id="a1W-Je-BvX"/>
<constraint firstAttribute="trailing" secondItem="FBs-iT-nWY" secondAttribute="trailing" id="aFh-Oh-dMW"/>
<constraint firstItem="Tai-sE-6DC" firstAttribute="leading" secondItem="hIE-BJ-nFm" secondAttribute="leading" id="k8m-8C-aK9"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="fzb-1W-zFB" secondAttribute="bottom" id="kPq-9v-D4c"/>
<constraint firstItem="fD2-1N-x27" firstAttribute="top" secondItem="hIE-BJ-nFm" secondAttribute="top" id="rcc-1j-0Af"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="Tai-sE-6DC" secondAttribute="bottom" id="trf-mi-xeb"/>
<constraint firstItem="FBs-iT-nWY" firstAttribute="leading" secondItem="hIE-BJ-nFm" secondAttribute="leading" id="vEM-Q6-T7g"/>
<constraint firstAttribute="trailing" secondItem="fzb-1W-zFB" secondAttribute="trailing" id="vuc-5v-tGe"/>
<constraint firstItem="fzb-1W-zFB" firstAttribute="top" relation="greaterThanOrEqual" secondItem="Tai-sE-6DC" secondAttribute="bottom" id="x6f-8l-CgL"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="fD2-1N-x27" secondAttribute="bottom" id="x7F-TL-lJx"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="errorBox" destination="fD2-1N-x27" id="IV5-eS-JLl"/>
<outlet property="errorBoxBottom" destination="x7F-TL-lJx" id="W6w-05-Dll"/>
<outlet property="errorLabel" destination="vGz-fB-9Oi" id="Ojx-cU-Jpc"/>
<outlet property="heightBox" destination="FBs-iT-nWY" id="1Vi-nB-gGz"/>
<outlet property="heightBoxBottom" destination="0pP-L9-PjJ" id="BP0-zq-7kV"/>
<outlet property="heightBoxBottomManageRouteBoxTop" destination="939-g8-UFV" id="gmW-wu-meb"/>
<outlet property="heightProfileImage" destination="OqA-sS-wmI" id="NdN-LR-j7e"/>
<outlet property="manageRouteBox" destination="fzb-1W-zFB" id="yqc-27-U2m"/>
<outlet property="manageRouteBoxBackground" destination="Yia-YS-2aZ" id="omu-vJ-Nub"/>
<outlet property="manageRouteBoxBottom" destination="kPq-9v-D4c" id="FPP-Hc-DGv"/>
<outlet property="manageRouteButtonCompact" destination="Zzm-Yo-BvL" id="MAs-VL-pR0"/>
<outlet property="manageRouteButtonRegular" destination="K37-2W-GE8" id="I40-pl-MM4"/>
<outlet property="resultLabel" destination="sjQ-Sc-mtN" id="GLa-P4-7XO"/>
<outlet property="resultsBox" destination="Tai-sE-6DC" id="l4p-m2-z4z"/>
<outlet property="resultsBoxBottom" destination="trf-mi-xeb" id="tdb-sa-2ak"/>
<outlet property="saveRouteAsTrackButtonCompact" destination="hoe-y0-brm" id="gl1-BI-hc5"/>
<outlet property="saveRouteAsTrackButtonRegular" destination="aCu-AO-AFN" id="jpf-Pd-BOv"/>
</connections>
<point key="canvasLocation" x="448" y="520.83958020989508"/>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="iWi-pM-AJF" customClass="MWMTransportRoutePreviewStatus">
<rect key="frame" x="0.0" y="0.0" width="320" height="80"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uA3-5h-DWb">
<rect key="frame" x="-100" y="0.0" width="520" height="180"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="RouteBasePreview"/>
</userDefinedRuntimeAttributes>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Iu4-M8-t6g">
<rect key="frame" x="16" y="12" width="41.5" height="20.5"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="qgy-7x-cjR"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GZd-C3-csB">
<rect key="frame" x="16" y="48.5" width="288" height="15.5"/>
<subviews>
<collectionView userInteractionEnabled="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" alwaysBounceHorizontal="YES" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" delaysContentTouches="NO" canCancelContentTouches="NO" bouncesZoom="NO" dataMode="none" prefetchingEnabled="NO" translatesAutoresizingMaskIntoConstraints="NO" id="RVh-LF-kSn" customClass="TransportTransitStepsCollectionView" customModule="CoMaps" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="288" height="20"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="eGu-Mr-auv"/>
</constraints>
<collectionViewLayout key="collectionViewLayout" id="d3P-nT-IFD" customClass="TransportTransitFlowLayout" customModule="CoMaps" customModuleProvider="target"/>
</collectionView>
</subviews>
<constraints>
<constraint firstAttribute="height" relation="lessThanOrEqual" constant="100" id="IGc-CI-aOh"/>
<constraint firstAttribute="height" secondItem="RVh-LF-kSn" secondAttribute="height" priority="500" id="NQX-zx-17G"/>
<constraint firstItem="RVh-LF-kSn" firstAttribute="width" secondItem="gf4-x6-D2B" secondAttribute="width" id="d10-d7-7DD"/>
<constraint firstItem="RVh-LF-kSn" firstAttribute="leading" secondItem="k3L-Ml-bDX" secondAttribute="leading" id="fhs-TD-7km"/>
<constraint firstItem="RVh-LF-kSn" firstAttribute="trailing" secondItem="k3L-Ml-bDX" secondAttribute="trailing" id="jZG-5W-KKC"/>
<constraint firstItem="RVh-LF-kSn" firstAttribute="top" secondItem="k3L-Ml-bDX" secondAttribute="top" id="lDC-Pw-RQO"/>
<constraint firstItem="RVh-LF-kSn" firstAttribute="bottom" secondItem="k3L-Ml-bDX" secondAttribute="bottom" id="yz7-c7-AZq"/>
</constraints>
<viewLayoutGuide key="contentLayoutGuide" id="k3L-Ml-bDX"/>
<viewLayoutGuide key="frameLayoutGuide" id="gf4-x6-D2B"/>
</scrollView>
</subviews>
<viewLayoutGuide key="safeArea" id="YJK-Xe-9oN"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="GZd-C3-csB" secondAttribute="bottom" constant="16" id="3OD-DZ-eZI"/>
<constraint firstItem="Iu4-M8-t6g" firstAttribute="leading" secondItem="iWi-pM-AJF" secondAttribute="leading" constant="16" id="5Ge-fx-3pw"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="40" id="CQk-tf-wu9"/>
<constraint firstAttribute="trailing" secondItem="GZd-C3-csB" secondAttribute="trailing" constant="16" id="FUK-GV-FoZ"/>
<constraint firstItem="GZd-C3-csB" firstAttribute="top" secondItem="Iu4-M8-t6g" secondAttribute="bottom" constant="16" id="JrG-NF-7zG"/>
<constraint firstItem="Iu4-M8-t6g" firstAttribute="top" secondItem="iWi-pM-AJF" secondAttribute="top" constant="12" id="Zyw-PT-55a"/>
<constraint firstAttribute="bottom" secondItem="uA3-5h-DWb" secondAttribute="bottom" constant="-100" id="jN1-5h-bPC"/>
<constraint firstItem="GZd-C3-csB" firstAttribute="leading" secondItem="Iu4-M8-t6g" secondAttribute="leading" id="ktT-ZB-5HK"/>
<constraint firstItem="uA3-5h-DWb" firstAttribute="top" secondItem="iWi-pM-AJF" secondAttribute="top" id="nhC-d5-VpO"/>
<constraint firstAttribute="trailing" secondItem="uA3-5h-DWb" secondAttribute="trailing" constant="-100" id="wP2-ul-QVl"/>
<constraint firstItem="uA3-5h-DWb" firstAttribute="leading" secondItem="iWi-pM-AJF" secondAttribute="leading" constant="-100" id="zCP-aF-HvA"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="etaLabel" destination="Iu4-M8-t6g" id="hJe-KS-ctT"/>
<outlet property="stepsCollectionScrollView" destination="GZd-C3-csB" id="fFk-g4-SOl"/>
<outlet property="stepsCollectionView" destination="RVh-LF-kSn" id="3uG-nE-mt3"/>
<outlet property="stepsCollectionViewHeight" destination="eGu-Mr-auv" id="K2J-yO-yQ5"/>
</connections>
<point key="canvasLocation" x="449" y="737"/>
</view>
</objects>
<resources>
<image name="ic24PxImport" width="24" height="24"/>
<image name="ic_24px_manager" width="24" height="24"/>
<image name="ic_nav_bar_back" width="14" height="22"/>
<image name="ic_options_warning" width="24" height="24"/>
</resources>
</document>

View file

@ -0,0 +1,8 @@
typedef NS_ENUM(NSInteger, MWMRouteManagerPointType) {
MWMRouteManagerPointTypeStart,
MWMRouteManagerPointTypeA,
MWMRouteManagerPointTypeB,
MWMRouteManagerPointTypeC,
MWMRouteManagerPointTypeFinish,
MWMRouteManagerPointTypeMyPosition
};

View file

@ -0,0 +1,94 @@
final class RouteManagerCell: MWMTableViewCell {
@IBOutlet private weak var typeImage: UIImageView!
@IBOutlet private weak var titleLabel: UILabel!
@IBOutlet weak var subtitleLabel: UILabel!
@IBOutlet private weak var dragImage: UIImageView! {
didSet {
dragImage.image = #imageLiteral(resourceName: "ic_route_manager_move")
dragImage.tintColor = UIColor.blackHintText()
}
}
@IBOutlet private weak var separator1: UIView! {
didSet {
separator1.layer.cornerRadius = 2
}
}
@IBOutlet weak var separator2: UIView!
private var index: Int!
private var model: MWMRoutePoint!
@IBOutlet var subtitleConstraints: [NSLayoutConstraint]!
override var snapshot: UIView {
let skipViews: [UIView] = [typeImage, separator1, separator2]
skipViews.forEach { $0.isHidden = true }
let snapshot = super.snapshot
setStyle(.background)
skipViews.forEach { $0.isHidden = false }
return snapshot
}
func set(model: MWMRoutePoint, atIndex index: Int) {
self.model = model
self.index = index
setupTypeImage()
setupLabels()
setupSeparators()
}
private func setupTypeImage() {
if model.isMyPosition && index == 0 {
typeImage.image = #imageLiteral(resourceName: "ic_route_manager_my_position")
typeImage.tintColor = UIColor.linkBlue()
} else {
switch model.type {
case .start:
typeImage.image = #imageLiteral(resourceName: "ic_route_manager_start")
typeImage.tintColor = UIColor.linkBlue()
case .intermediate:
let i = model.intermediateIndex + 1
// TODO: Properly support more than 20 icons.
var iconName = "route-point-20"
if (i >= 1 && i < 20) {
iconName = "route-point-" + String(i)
}
typeImage.image = #imageLiteral(resourceName: iconName)
typeImage.tintColor = UIColor.primary()
case .finish:
typeImage.image = #imageLiteral(resourceName: "ic_route_manager_finish")
typeImage.tintColor = UIColor.blackPrimaryText()
}
}
}
private func setupLabels() {
let subtitle: String?
if model.isMyPosition && index != 0 {
titleLabel.text = model.latLonString
subtitle = model.title
} else {
titleLabel.text = model.title
subtitle = model.subtitle
}
var subtitleConstraintsActive = false
if let subtitle = subtitle, !subtitle.isEmpty {
subtitleLabel.text = subtitle
subtitleConstraintsActive = true
}
subtitleLabel.isHidden = !subtitleConstraintsActive
subtitleConstraints.forEach { $0.isActive = subtitleConstraintsActive }
}
private func setupSeparators() {
let isSeparatorsHidden = model.type == .finish
separator1.isHidden = isSeparatorsHidden
separator2.isHidden = isSeparatorsHidden
}
override func applyTheme() {
super.applyTheme()
self.setupTypeImage()
}
}

View file

@ -0,0 +1,109 @@
<?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" useSafeAreas="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="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="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="RouteManagerCell" customModule="CoMaps" customModuleProvider="target" propertyAccessControl="none">
<rect key="frame" x="0.0" y="0.0" width="320" height="66"/>
<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="66"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="We3-dE-JYe">
<rect key="frame" x="16" y="21" width="24" height="24"/>
<constraints>
<constraint firstAttribute="width" secondItem="We3-dE-JYe" secondAttribute="height" multiplier="1:1" id="LD8-kb-Haz"/>
<constraint firstAttribute="width" constant="24" id="jtH-Dc-l1B"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="E8T-WR-b4P">
<rect key="frame" x="60" y="12" width="212" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular16:blackPrimaryText"/>
</userDefinedRuntimeAttributes>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ec0-Sc-NrV">
<rect key="frame" x="60" y="36.5" width="212" height="17.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular16:blackSecondaryText"/>
</userDefinedRuntimeAttributes>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="a6S-xz-wDs">
<rect key="frame" x="280" y="21" width="24" height="24"/>
<constraints>
<constraint firstAttribute="width" constant="24" id="4YZ-4p-E4H"/>
<constraint firstAttribute="width" secondItem="a6S-xz-wDs" secondAttribute="height" multiplier="1:1" id="QCB-eE-cuO"/>
</constraints>
</imageView>
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="e4S-qN-tx8">
<rect key="frame" x="26" y="62" width="4" height="4"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" secondItem="e4S-qN-tx8" secondAttribute="height" multiplier="1:1" id="Fec-Pg-5YG"/>
<constraint firstAttribute="width" constant="4" id="bHk-pR-fsG"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
</userDefinedRuntimeAttributes>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="E4r-3f-9l2">
<rect key="frame" x="60" y="65" width="260" height="1"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="iTg-i0-sIJ"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
</userDefinedRuntimeAttributes>
</view>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="a6S-xz-wDs" secondAttribute="trailing" constant="16" id="2b1-Ud-Ffh"/>
<constraint firstItem="ec0-Sc-NrV" firstAttribute="leading" secondItem="E8T-WR-b4P" secondAttribute="leading" id="Bqj-Da-466"/>
<constraint firstItem="E8T-WR-b4P" firstAttribute="leading" secondItem="We3-dE-JYe" secondAttribute="trailing" constant="20" id="Ezt-Qw-m9Q"/>
<constraint firstAttribute="bottom" secondItem="ec0-Sc-NrV" secondAttribute="bottom" constant="12" id="OFV-X4-OWD"/>
<constraint firstItem="E4r-3f-9l2" firstAttribute="leading" secondItem="E8T-WR-b4P" secondAttribute="leading" id="S3g-BB-Afo"/>
<constraint firstItem="We3-dE-JYe" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="V68-v7-Y2a"/>
<constraint firstAttribute="bottom" secondItem="e4S-qN-tx8" secondAttribute="bottom" id="Yah-Vt-8Jb"/>
<constraint firstItem="ec0-Sc-NrV" firstAttribute="top" secondItem="E8T-WR-b4P" secondAttribute="bottom" constant="4" id="cbi-Y9-8Be"/>
<constraint firstAttribute="bottom" secondItem="E4r-3f-9l2" secondAttribute="bottom" id="gFy-Bp-fJL"/>
<constraint firstItem="ec0-Sc-NrV" firstAttribute="trailing" secondItem="E8T-WR-b4P" secondAttribute="trailing" id="jGc-UQ-TLl"/>
<constraint firstItem="We3-dE-JYe" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="16" id="m1w-0i-ELt"/>
<constraint firstItem="e4S-qN-tx8" firstAttribute="centerX" secondItem="We3-dE-JYe" secondAttribute="centerX" id="neB-SX-zce"/>
<constraint firstItem="a6S-xz-wDs" firstAttribute="leading" secondItem="E8T-WR-b4P" secondAttribute="trailing" constant="8" id="q6T-54-sGO"/>
<constraint firstAttribute="trailing" secondItem="E4r-3f-9l2" secondAttribute="trailing" id="s0v-JX-fRN"/>
<constraint firstAttribute="bottom" secondItem="E8T-WR-b4P" secondAttribute="bottom" priority="500" constant="14" id="s4P-2g-gnj"/>
<constraint firstItem="E8T-WR-b4P" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" priority="500" constant="14" id="uVb-l1-ce1"/>
<constraint firstItem="E8T-WR-b4P" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="12" id="vdO-bJ-CRg"/>
<constraint firstItem="a6S-xz-wDs" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="w85-cS-Uky"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="Kac-Pe-J2L"/>
<connections>
<outlet property="dragImage" destination="a6S-xz-wDs" id="zxX-KV-uIQ"/>
<outlet property="separator1" destination="e4S-qN-tx8" id="tXc-oD-GpR"/>
<outlet property="separator2" destination="E4r-3f-9l2" id="dVo-Qx-Nf1"/>
<outlet property="subtitleLabel" destination="ec0-Sc-NrV" id="XfG-GI-QSP"/>
<outlet property="titleLabel" destination="E8T-WR-b4P" id="hNR-bg-Aik"/>
<outlet property="typeImage" destination="We3-dE-JYe" id="yzw-GT-nXU"/>
<outletCollection property="subtitleConstraints" destination="vdO-bJ-CRg" collectionClass="NSMutableArray" id="YBl-7I-Qow"/>
<outletCollection property="subtitleConstraints" destination="OFV-X4-OWD" collectionClass="NSMutableArray" id="0A7-Qz-hws"/>
</connections>
<point key="canvasLocation" x="139" y="154"/>
</tableViewCell>
</objects>
</document>

View file

@ -0,0 +1,88 @@
final class RouteManagerDimView: UIView {
@IBOutlet private weak var image: UIImageView!
@IBOutlet private weak var label: UILabel!
@IBOutlet private weak var messageView: UIView!
@IBOutlet private weak var messageViewContainer: UIView!
@IBOutlet private var messageViewVerticalCenter: NSLayoutConstraint!
@IBOutlet private var labelVerticalCenter: NSLayoutConstraint!
enum State {
case visible
case binOpenned
case hidden
}
var state = State.hidden {
didSet {
guard state != oldValue else { return }
switch state {
case .visible:
isVisible = true
image.image = #imageLiteral(resourceName: "ic_route_manager_trash")
case .binOpenned:
isVisible = true
image.image = #imageLiteral(resourceName: "ic_route_manager_trash_open")
case .hidden:
isVisible = false
}
}
}
var binDropPoint: CGPoint {
return convert(image.isHidden ? label.center : image.center, from: messageView)
}
private var isVisible = false {
didSet {
guard isVisible != oldValue else { return }
let componentsAlpha: CGFloat = 0.5
setStyle(.blackStatusBarBackground)
alpha = isVisible ? 0 : 1
image.alpha = isVisible ? 0 : componentsAlpha
label.alpha = isVisible ? 0 : componentsAlpha
UIView.animate(withDuration: kDefaultAnimationDuration,
animations: {
self.alpha = self.isVisible ? 1 : 0
self.image.alpha = self.isVisible ? componentsAlpha : 0
self.label.alpha = self.isVisible ? componentsAlpha : 0
},
completion: { _ in
self.alpha = 1
if !self.isVisible {
self.backgroundColor = UIColor.clear
}
})
setNeedsLayout()
}
}
func setViews(container: UIView, controller: UIView, manager: UIView) {
alpha = 0
alternative(iPhone: {
controller.insertSubview(self, at: 0)
NSLayoutConstraint(item: self, attribute: .left, relatedBy: .equal, toItem: self.messageViewContainer, attribute: .left, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: manager, attribute: .top, relatedBy: .equal, toItem: self.messageViewContainer, attribute: .bottom, multiplier: 1, constant: 0).isActive = true
},
iPad: {
container.insertSubview(self, at: 0)
NSLayoutConstraint(item: self, attribute: .bottom, relatedBy: .equal, toItem: self.messageViewContainer, attribute: .bottom, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: controller, attribute: .right, relatedBy: .equal, toItem: self.messageViewContainer, attribute: .left, multiplier: 1, constant: 0).isActive = true
})()
NSLayoutConstraint(item: container, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: container, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: container, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: container, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1, constant: 0).isActive = true
}
override func layoutSubviews() {
super.layoutSubviews()
let isImageHidden = messageView.height > messageViewContainer.height
image.isHidden = isImageHidden
messageViewVerticalCenter.isActive = !isImageHidden
labelVerticalCenter.isActive = isImageHidden
}
}

View file

@ -0,0 +1,11 @@
final class RouteManagerFooterView: UIView {
@IBOutlet private weak var cancelButton: UIButton!
@IBOutlet private weak var planButton: UIButton!
@IBOutlet weak var separator: UIView!
@IBOutlet weak var background: UIView!
var isPlanButtonEnabled = true {
didSet {
planButton.isEnabled = isPlanButtonEnabled
}
}
}

View file

@ -0,0 +1,34 @@
final class RouteManagerHeaderView: UIView {
@IBOutlet private weak var titleLabel: UILabel!
@IBOutlet private weak var addLocationButton: UIButton! {
didSet {
// TODO(igrechuhin): Uncomment when start_from_my_position translation is ready.
// addLocationButton.setTitle(L("start_from_my_position"), for: .normal)
// addLocationButton.setTitleColor(UIColor.linkBlue(), for: .normal)
// addLocationButton.setTitleColor(UIColor.buttonDisabledBlueText(), for: .disabled)
// addLocationButton.tintColor = UIColor.linkBlue()
//
// let flipLR = CGAffineTransform(scaleX: -1.0, y: 1.0)
// addLocationButton.transform = flipLR
// addLocationButton.titleLabel?.transform = flipLR
// addLocationButton.imageView?.transform = flipLR
}
}
@IBOutlet weak var separator: UIView!
var isLocationButtonEnabled = true {
didSet {
addLocationButton.isEnabled = isLocationButtonEnabled
addLocationButton.tintColor = isLocationButtonEnabled ? UIColor.linkBlue() : UIColor.buttonDisabledBlueText()
}
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func applyTheme() {
super.applyTheme()
addLocationButton.tintColor = isLocationButtonEnabled ? UIColor.linkBlue() : UIColor.buttonDisabledBlueText()
}
}

View file

@ -0,0 +1,36 @@
final class RouteManagerPresentationController: UIPresentationController {
override func containerViewWillLayoutSubviews() {
super.containerViewWillLayoutSubviews()
(presentedViewController as? RouteManagerViewController)?.chromeView.frame = containerView!.bounds
presentedView?.frame = frameOfPresentedViewInContainerView
}
override func presentationTransitionWillBegin() {
super.presentationTransitionWillBegin()
guard let presentedViewController = presentedViewController as? RouteManagerViewController,
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? RouteManagerViewController,
let coordinator = presentedViewController.transitionCoordinator,
let presentedView = presentedView else { return }
coordinator.animate(alongsideTransition: { _ in
presentedViewController.chromeView.alpha = 0
}, completion: { _ in
presentedView.removeFromSuperview()
})
}
}

View file

@ -0,0 +1,31 @@
final class RouteManagerTableView: UITableView {
@IBOutlet private weak var tableViewHeight: NSLayoutConstraint!
enum HeightUpdateStyle {
case animated
case deferred
case off
}
var heightUpdateStyle = HeightUpdateStyle.deferred
private var scheduledUpdate: DispatchWorkItem?
override var contentSize: CGSize {
didSet {
guard contentSize != oldValue else { return }
scheduledUpdate?.cancel()
let update = { [weak self] in
guard let s = self else { return }
s.tableViewHeight.constant = s.contentSize.height
}
switch heightUpdateStyle {
case .animated: superview?.animateConstraints(animations: update)
case .deferred:
scheduledUpdate = DispatchWorkItem(block: update)
DispatchQueue.main.async(execute: scheduledUpdate!)
case .off: break
}
}
}
}

View file

@ -0,0 +1,35 @@
final class RouteManagerTransitioning: 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)
})
}
}

View file

@ -0,0 +1,19 @@
@objc(MWMRouteManagerTransitioningManager)
final class RouteManagerTransitioningManager: NSObject, UIViewControllerTransitioningDelegate {
override init() {
super.init()
}
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source _: UIViewController) -> UIPresentationController? {
RouteManagerPresentationController(presentedViewController: presented,
presenting: presenting)
}
func animationController(forPresented _: UIViewController, presenting _: UIViewController, source _: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return RouteManagerTransitioning(isPresentation: true)
}
func animationController(forDismissed _: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return RouteManagerTransitioning(isPresentation: false)
}
}

View file

@ -0,0 +1,257 @@
@objc(MWMRouteManagerViewController)
final class RouteManagerViewController: MWMViewController, UITableViewDataSource, UITableViewDelegate {
let viewModel: RouteManagerViewModelProtocol
@IBOutlet private var dimView: RouteManagerDimView!
@IBOutlet private weak var footerViewHeight: NSLayoutConstraint!
@IBOutlet private weak var headerView: RouteManagerHeaderView!
@IBOutlet private weak var footerView: RouteManagerFooterView!
@IBOutlet private weak var headerViewHeight: NSLayoutConstraint!
@IBOutlet private weak var managerView: UIView!
@IBOutlet private weak var managerWidth: NSLayoutConstraint!
@IBOutlet private weak var minManagerTopOffset: NSLayoutConstraint!
@IBOutlet private weak var tableView: RouteManagerTableView!
lazy var chromeView: UIView = {
let view = UIView()
view.setStyle(.blackStatusBarBackground)
return view
}()
weak var containerView: UIView!
private var canDeleteRow: Bool { return viewModel.routePoints.count > 2 }
final class DragCell {
weak var controller: RouteManagerViewController!
let snapshot: UIView
var indexPath: IndexPath
init(controller: RouteManagerViewController, cell: UITableViewCell, dragPoint: CGPoint, indexPath: IndexPath) {
self.controller = controller
snapshot = cell.snapshot
self.indexPath = indexPath
addSubView(cell: cell, dragPoint: dragPoint)
controller.tableView.heightUpdateStyle = .off
if controller.canDeleteRow {
controller.dimView.state = .visible
}
}
private func addSubView(cell: UITableViewCell, dragPoint: CGPoint) {
let view = controller.containerView!
view.addSubview(snapshot)
snapshot.center = view.convert(cell.center, from: controller.tableView)
cell.isHidden = true
UIView.animate(withDuration: kDefaultAnimationDuration,
animations: { [snapshot] in
snapshot.center = dragPoint
let scaleFactor: CGFloat = 1.05
snapshot.transform = CGAffineTransform(scaleX: scaleFactor, y: scaleFactor)
})
}
func move(dragPoint: CGPoint, indexPath: IndexPath?, inManagerView: Bool) {
snapshot.center = dragPoint
if controller.canDeleteRow {
controller.dimView.state = inManagerView ? .visible : .binOpenned
}
guard let newIP = indexPath else { return }
let tv = controller.tableView!
let cell = tv.cellForRow(at: newIP)
let canMoveCell: Bool
if let cell = cell {
let (centerX, centerY) = (snapshot.width / 2, snapshot.height / 2)
canMoveCell = cell.point(inside: cell.convert(CGPoint(x: centerX, y: 1.5 * centerY), from: snapshot), with: nil) &&
cell.point(inside: cell.convert(CGPoint(x: centerX, y: 0.5 * centerY), from: snapshot), with: nil)
} else {
canMoveCell = true
}
guard canMoveCell else { return }
let currentIP = self.indexPath
if newIP != currentIP {
let (currentRow, newRow) = (currentIP.row, newIP.row)
controller.viewModel.movePoint(at: currentRow, to: newRow)
tv.moveRow(at: currentIP, to: newIP)
let reloadRows = (min(currentRow, newRow) ... max(currentRow, newRow)).map { IndexPath(row: $0, section: 0) }
tv.reloadRows(at: reloadRows, with: .fade)
tv.cellForRow(at: newIP)?.isHidden = true
self.indexPath = newIP
}
}
func drop(inManagerView: Bool) {
let removeSnapshot = {
self.snapshot.removeFromSuperview()
self.controller.dimView.state = .hidden
}
let containerView = controller.containerView!
let tv = controller.tableView!
if inManagerView || !controller.canDeleteRow {
let dropCenter = tv.cellForRow(at: indexPath)?.center ?? snapshot.center
UIView.animate(withDuration: kDefaultAnimationDuration,
animations: { [snapshot] in
snapshot.center = containerView.convert(dropCenter, from: tv)
snapshot.transform = CGAffineTransform.identity
},
completion: { [indexPath] _ in
tv.reloadRows(at: [indexPath], with: .none)
removeSnapshot()
})
} else {
tv.heightUpdateStyle = .animated
tv.update {
controller.viewModel.deletePoint(at: indexPath.row)
tv.deleteRows(at: [indexPath], with: .automatic)
}
let dimView = controller.dimView!
UIView.animate(withDuration: kDefaultAnimationDuration,
animations: { [snapshot] in
snapshot.center = containerView.convert(dimView.binDropPoint, from: dimView)
let scaleFactor: CGFloat = 0.2
snapshot.transform = CGAffineTransform(scaleX: scaleFactor, y: scaleFactor)
},
completion: { _ in
removeSnapshot()
})
}
}
deinit {
controller.tableView.heightUpdateStyle = .deferred
}
}
var dragCell: DragCell?
@objc init(viewModel: RouteManagerViewModelProtocol) {
self.viewModel = viewModel
super.init(nibName: toString(type(of: self)), bundle: nil)
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
setupTableView()
setupLayout()
viewModel.refreshControlsCallback = { [unowned viewModel, unowned self] in
let points = viewModel.routePoints
self.footerView.isPlanButtonEnabled = points.count >= 2
self.headerView.isLocationButtonEnabled = true
points.forEach {
if $0.isMyPosition {
self.headerView.isLocationButtonEnabled = false
}
}
}
viewModel.refreshControlsCallback()
viewModel.reloadCallback = { [tableView] in
tableView?.reloadSections(IndexSet(integer: 0), with: .fade)
}
viewModel.startTransaction()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
dimView.setViews(container: containerView, controller: view, manager: managerView)
containerView.insertSubview(chromeView, at: 0)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if preferredContentSize != managerView.size {
preferredContentSize = managerView.size
}
}
private func setupLayout() {
alternative(iPhone: { self.managerWidth.isActive = false },
iPad: { self.minManagerTopOffset.isActive = false })()
}
private func setupTableView() {
tableView.registerNib(cellClass: RouteManagerCell.self)
tableView.estimatedRowHeight = 48
tableView.rowHeight = UITableView.automaticDimension
}
@IBAction func onCancel() {
viewModel.cancelTransaction()
dismiss(animated: true, completion: nil)
}
@IBAction func onPlan() {
viewModel.finishTransaction()
dismiss(animated: true, completion: nil)
}
@IBAction func onAdd() {
viewModel.addLocationPoint()
tableView.heightUpdateStyle = .off
tableView.update({
tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .fade)
}, completion: {
self.tableView.heightUpdateStyle = .deferred
})
}
@IBAction private func gestureRecognized(_ longPress: UIGestureRecognizer) {
let locationInView = gestureLocation(longPress, in: containerView)
let locationInTableView = gestureLocation(longPress, in: tableView)
switch longPress.state {
case .began:
guard let indexPath = tableView.indexPathForRow(at: locationInTableView),
let cell = tableView.cellForRow(at: indexPath) else { return }
dragCell = DragCell(controller: self, cell: cell, dragPoint: locationInView, indexPath: indexPath)
case .changed:
guard let dragCell = dragCell else { return }
let indexPath = tableView.indexPathForRow(at: locationInTableView)
let inManagerView = managerView.point(inside: gestureLocation(longPress, in: managerView), with: nil)
dragCell.move(dragPoint: locationInView, indexPath: indexPath, inManagerView: inManagerView)
default:
guard let dragCell = dragCell else { return }
let inManagerView = managerView.point(inside: gestureLocation(longPress, in: managerView), with: nil)
dragCell.drop(inManagerView: inManagerView)
self.dragCell = nil
}
}
private func gestureLocation(_ gestureRecognizer: UIGestureRecognizer, in view: UIView) -> CGPoint {
var location = gestureRecognizer.location(in: view)
iPhoneSpecific { location.x = view.width / 2 }
return location
}
// MARK: - UITableViewDataSource
func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int {
return viewModel.routePoints.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withCellClass: RouteManagerCell.self, indexPath: indexPath) as! RouteManagerCell
let row = indexPath.row
cell.set(model: viewModel.routePoints[row], atIndex: row)
return cell
}
func tableView(_ tableView: UITableView, commit _: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
viewModel.deletePoint(at: indexPath.row)
}
// MARK: - UITableViewDelegate
func tableView(_: UITableView, editingStyleForRowAt _: IndexPath) -> UITableViewCell.EditingStyle {
return canDeleteRow ? .delete : .none
}
}

View file

@ -0,0 +1,293 @@
<?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="MWMRouteManagerViewController">
<connections>
<outlet property="dimView" destination="3y4-jh-JUF" id="Eto-KX-XRu"/>
<outlet property="footerView" destination="mqc-M4-MPb" id="8im-uy-Rgm"/>
<outlet property="footerViewHeight" destination="1qG-Md-L9w" id="ffF-cw-Lp0"/>
<outlet property="headerView" destination="s0L-ul-gog" id="X5t-Xe-g5Y"/>
<outlet property="headerViewHeight" destination="e4y-UI-hxr" id="ERb-fS-AcU"/>
<outlet property="managerView" destination="i5M-Pr-FkT" id="zz4-k2-Tf1"/>
<outlet property="managerWidth" destination="6Nm-Md-fZo" id="7X5-IX-4cy"/>
<outlet property="minManagerTopOffset" destination="sYB-lu-PhM" id="C8h-tU-enB"/>
<outlet property="tableView" destination="Zqd-YQ-ksD" id="ROT-An-z3D"/>
<outlet property="view" destination="TKX-qe-h4F" id="ORc-Ju-S5N"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3y4-jh-JUF" customClass="RouteManagerDimView" customModule="CoMaps" customModuleProvider="target" propertyAccessControl="none">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<subviews>
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TC7-xW-EwE">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kTd-fr-ia7">
<rect key="frame" x="186" y="380" width="42" height="136.5"/>
<subviews>
<imageView userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_route_manager_trash" translatesAutoresizingMaskIntoConstraints="NO" id="ZEN-kK-2CA">
<rect key="frame" x="-3" y="40" width="48" height="48"/>
<constraints>
<constraint firstAttribute="width" constant="48" id="QJJ-DB-WF7"/>
<constraint firstAttribute="width" secondItem="ZEN-kK-2CA" secondAttribute="height" multiplier="1:1" id="YdZ-dM-iEr"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" alpha="0.0" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1ks-iR-Wia">
<rect key="frame" x="0.0" y="96" width="42" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular18:whiteText"/>
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="planning_route_remove_title"/>
</userDefinedRuntimeAttributes>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<accessibility key="accessibilityConfiguration" identifier="MessageView"/>
<constraints>
<constraint firstItem="1ks-iR-Wia" firstAttribute="leading" secondItem="kTd-fr-ia7" secondAttribute="leading" id="OZ4-Jc-PiA"/>
<constraint firstAttribute="bottom" secondItem="1ks-iR-Wia" secondAttribute="bottom" constant="20" id="PXJ-Xl-pHA"/>
<constraint firstItem="1ks-iR-Wia" firstAttribute="top" secondItem="ZEN-kK-2CA" secondAttribute="bottom" constant="8" id="iOV-1C-4Ak"/>
<constraint firstItem="ZEN-kK-2CA" firstAttribute="top" secondItem="kTd-fr-ia7" secondAttribute="top" constant="40" id="jma-ai-kFd"/>
<constraint firstItem="ZEN-kK-2CA" firstAttribute="centerX" secondItem="kTd-fr-ia7" secondAttribute="centerX" id="kue-ke-ivS"/>
<constraint firstAttribute="trailing" secondItem="1ks-iR-Wia" secondAttribute="trailing" id="oTF-hI-m7B"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<accessibility key="accessibilityConfiguration" identifier="MessageContainerView"/>
<constraints>
<constraint firstItem="kTd-fr-ia7" firstAttribute="centerX" secondItem="TC7-xW-EwE" secondAttribute="centerX" id="3Xc-xT-Z2O"/>
<constraint firstItem="kTd-fr-ia7" firstAttribute="centerY" secondItem="TC7-xW-EwE" secondAttribute="centerY" priority="700" id="EcF-uM-iaP"/>
<constraint firstItem="1ks-iR-Wia" firstAttribute="centerY" secondItem="TC7-xW-EwE" secondAttribute="centerY" priority="600" id="F2s-Or-gaN"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="72" id="niX-df-uVo"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<accessibility key="accessibilityConfiguration" identifier="DimView"/>
<gestureRecognizers/>
<constraints>
<constraint firstAttribute="bottom" secondItem="TC7-xW-EwE" secondAttribute="bottom" placeholder="YES" id="2x4-NF-KU5"/>
<constraint firstItem="TC7-xW-EwE" firstAttribute="leading" secondItem="3y4-jh-JUF" secondAttribute="leading" placeholder="YES" id="D86-wV-E74"/>
<constraint firstAttribute="trailing" secondItem="TC7-xW-EwE" secondAttribute="trailing" id="kvp-My-OQZ"/>
<constraint firstItem="TC7-xW-EwE" firstAttribute="top" secondItem="3y4-jh-JUF" secondAttribute="top" id="lfE-MZ-Cvp"/>
</constraints>
<viewLayoutGuide key="safeArea" id="aNC-Xj-cAv"/>
<connections>
<outlet property="image" destination="ZEN-kK-2CA" id="zkv-Ag-qOp"/>
<outlet property="label" destination="1ks-iR-Wia" id="jsa-mi-iZ2"/>
<outlet property="labelVerticalCenter" destination="F2s-Or-gaN" id="Ztj-AC-Re4"/>
<outlet property="messageView" destination="kTd-fr-ia7" id="dq4-iL-VA4"/>
<outlet property="messageViewContainer" destination="TC7-xW-EwE" id="gfd-5J-Tc3"/>
<outlet property="messageViewVerticalCenter" destination="EcF-uM-iaP" id="zyb-VY-6pA"/>
<outletCollection property="gestureRecognizers" destination="deN-Z9-rbS" appends="YES" id="Mcr-eR-ObL"/>
</connections>
<point key="canvasLocation" x="288" y="-49"/>
</view>
<view contentMode="scaleToFill" id="TKX-qe-h4F" propertyAccessControl="none">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<subviews>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="758" width="414" height="138"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="s0L-ul-gog" customClass="RouteManagerHeaderView" customModule="CoMaps" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="414" height="48"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="760" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dQh-uc-65J">
<rect key="frame" x="16" y="14" width="42" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="bold22:blackPrimaryText"/>
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="planning_route_manage_route"/>
</userDefinedRuntimeAttributes>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0Fo-Ae-QrQ">
<rect key="frame" x="370" y="0.0" width="28" height="48"/>
<inset key="contentEdgeInsets" minX="8" minY="0.0" maxX="0.0" maxY="0.0"/>
<inset key="imageEdgeInsets" minX="-8" minY="0.0" maxX="0.0" maxY="0.0"/>
<state key="normal" image="ic_get_position"/>
<connections>
<action selector="onAdd" destination="-1" eventType="touchUpInside" id="zGx-NU-Hzq"/>
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="MJB-bK-y5c">
<rect key="frame" x="16" y="47" width="398" height="1"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="aOa-8P-yGw"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
</userDefinedRuntimeAttributes>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="MJB-bK-y5c" firstAttribute="leading" secondItem="s0L-ul-gog" secondAttribute="leading" constant="16" id="0en-v6-Ij2"/>
<constraint firstItem="0Fo-Ae-QrQ" firstAttribute="top" secondItem="s0L-ul-gog" secondAttribute="top" id="8Bf-WP-AF2"/>
<constraint firstAttribute="bottom" secondItem="0Fo-Ae-QrQ" secondAttribute="bottom" id="BjQ-l3-bKd"/>
<constraint firstAttribute="trailing" secondItem="MJB-bK-y5c" secondAttribute="trailing" id="F3U-0x-OCM"/>
<constraint firstItem="dQh-uc-65J" firstAttribute="centerY" secondItem="s0L-ul-gog" secondAttribute="centerY" id="GNV-n4-tfI"/>
<constraint firstItem="0Fo-Ae-QrQ" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="dQh-uc-65J" secondAttribute="trailing" constant="8" id="Zke-bf-VcT"/>
<constraint firstAttribute="height" constant="48" id="e4y-UI-hxr"/>
<constraint firstAttribute="bottom" secondItem="MJB-bK-y5c" secondAttribute="bottom" id="qQD-HE-tWa"/>
<constraint firstItem="dQh-uc-65J" firstAttribute="leading" secondItem="s0L-ul-gog" secondAttribute="leading" constant="16" id="qSi-Cf-iFz"/>
<constraint firstAttribute="trailing" secondItem="0Fo-Ae-QrQ" secondAttribute="trailing" constant="16" id="zfn-hK-yiy"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="addLocationButton" destination="0Fo-Ae-QrQ" id="svZ-xA-Txe"/>
<outlet property="separator" destination="MJB-bK-y5c" id="xZC-xh-fJh"/>
<outlet property="titleLabel" destination="dQh-uc-65J" id="xKP-bY-mQP"/>
</connections>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="mqc-M4-MPb" customClass="RouteManagerFooterView" customModule="CoMaps" customModuleProvider="target">
<rect key="frame" x="0.0" y="48" width="414" height="48"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="or0-Gy-0su">
<rect key="frame" x="-100" y="0.0" width="614" height="148"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="BlackOpaqueBackground"/>
</userDefinedRuntimeAttributes>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="pjx-6o-NWS">
<rect key="frame" x="0.0" y="0.0" width="414" height="1"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="jFT-3E-Yrc"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
</userDefinedRuntimeAttributes>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2WE-hS-hzl">
<rect key="frame" x="16" y="0.0" width="46" height="48"/>
<state key="normal" title="Button"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="FlatNormalTransButton"/>
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="cancel"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="onCancel" destination="-1" eventType="touchUpInside" id="Lzk-mL-6Jg"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="u3h-0s-UBo">
<rect key="frame" x="352" y="0.0" width="46" height="48"/>
<state key="normal" title="Button"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="FlatNormalTransButton"/>
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="button_plan"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="onPlan" destination="-1" eventType="touchUpInside" id="M1B-dr-vZS"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="48" id="1qG-Md-L9w"/>
<constraint firstAttribute="trailing" secondItem="pjx-6o-NWS" secondAttribute="trailing" id="2pz-i3-nqy"/>
<constraint firstItem="2WE-hS-hzl" firstAttribute="leading" secondItem="mqc-M4-MPb" secondAttribute="leading" constant="16" id="5pP-TA-d4M"/>
<constraint firstItem="2WE-hS-hzl" firstAttribute="top" secondItem="mqc-M4-MPb" secondAttribute="top" id="CmY-Dg-Oyj"/>
<constraint firstAttribute="bottom" secondItem="or0-Gy-0su" secondAttribute="bottom" constant="-100" id="Df9-Xs-6pB"/>
<constraint firstAttribute="bottom" secondItem="u3h-0s-UBo" secondAttribute="bottom" id="GPn-YG-Q3q"/>
<constraint firstItem="pjx-6o-NWS" firstAttribute="top" secondItem="mqc-M4-MPb" secondAttribute="top" id="JBh-gl-9Zv"/>
<constraint firstItem="or0-Gy-0su" firstAttribute="leading" secondItem="mqc-M4-MPb" secondAttribute="leading" constant="-100" id="Nrk-kP-4Bh"/>
<constraint firstAttribute="trailing" secondItem="u3h-0s-UBo" secondAttribute="trailing" constant="16" id="QGG-zB-H6Q"/>
<constraint firstAttribute="bottom" secondItem="2WE-hS-hzl" secondAttribute="bottom" id="TJH-n6-XZn"/>
<constraint firstItem="pjx-6o-NWS" firstAttribute="leading" secondItem="mqc-M4-MPb" secondAttribute="leading" id="isd-2j-Shn"/>
<constraint firstItem="u3h-0s-UBo" firstAttribute="top" secondItem="mqc-M4-MPb" secondAttribute="top" id="jC7-jG-YeX"/>
<constraint firstAttribute="trailing" secondItem="or0-Gy-0su" secondAttribute="trailing" constant="-100" id="zfL-pn-451"/>
<constraint firstItem="or0-Gy-0su" firstAttribute="top" secondItem="mqc-M4-MPb" secondAttribute="top" id="ztB-hC-yRP"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="background" destination="or0-Gy-0su" id="aFx-YJ-deT"/>
<outlet property="cancelButton" destination="2WE-hS-hzl" id="8op-cA-N4M"/>
<outlet property="planButton" destination="u3h-0s-UBo" id="Fb9-pg-kh9"/>
<outlet property="separator" destination="pjx-6o-NWS" id="EKT-Ba-Lcy"/>
</connections>
</view>
<tableView clipsSubviews="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="48" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="Zqd-YQ-ksD" customClass="RouteManagerTableView" customModule="CoMaps" customModuleProvider="target">
<rect key="frame" x="0.0" y="48" width="414" height="0.0"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<gestureRecognizers/>
<constraints>
<constraint firstAttribute="height" priority="750" id="69R-bB-eQ7"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="TableView"/>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="dataSource" destination="-1" id="RZu-Qk-qvN"/>
<outlet property="delegate" destination="-1" id="QdP-tL-Afh"/>
<outlet property="tableViewHeight" destination="69R-bB-eQ7" id="iLN-YX-m5u"/>
<outletCollection property="gestureRecognizers" destination="iVR-Au-pTp" appends="YES" id="abb-Ql-z0p"/>
</connections>
</tableView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="ManagerView"/>
<constraints>
<constraint firstAttribute="width" priority="750" constant="375" id="6Nm-Md-fZo"/>
<constraint firstItem="Zqd-YQ-ksD" firstAttribute="top" secondItem="s0L-ul-gog" secondAttribute="bottom" id="9N3-Ti-W95"/>
<constraint firstItem="mqc-M4-MPb" firstAttribute="top" secondItem="Zqd-YQ-ksD" secondAttribute="bottom" id="A2s-eg-1CW"/>
<constraint firstAttribute="bottomMargin" secondItem="mqc-M4-MPb" secondAttribute="bottom" id="JoD-oZ-xgY"/>
<constraint firstAttribute="trailingMargin" secondItem="s0L-ul-gog" secondAttribute="trailing" constant="-8" id="Ndx-K2-qQ5"/>
<constraint firstAttribute="trailingMargin" secondItem="mqc-M4-MPb" secondAttribute="trailing" constant="-8" id="WgX-mu-BCG"/>
<constraint firstAttribute="trailing" secondItem="Zqd-YQ-ksD" secondAttribute="trailing" id="XkJ-fe-lyj"/>
<constraint firstItem="s0L-ul-gog" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leadingMargin" constant="-8" id="aDI-kT-6Ao"/>
<constraint firstItem="Zqd-YQ-ksD" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" id="cgy-zG-Qpu"/>
<constraint firstItem="mqc-M4-MPb" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leadingMargin" constant="-8" id="gvJ-kF-Ylk"/>
<constraint firstItem="s0L-ul-gog" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" id="ub9-gl-pdn"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
</userDefinedRuntimeAttributes>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<accessibility key="accessibilityConfiguration" identifier="ControllerView"/>
<gestureRecognizers/>
<constraints>
<constraint firstAttribute="trailing" secondItem="i5M-Pr-FkT" secondAttribute="trailing" id="Bnz-SA-yXA"/>
<constraint firstItem="i5M-Pr-FkT" firstAttribute="leading" secondItem="TKX-qe-h4F" secondAttribute="leading" id="Pui-FF-qOT"/>
<constraint firstAttribute="bottom" secondItem="i5M-Pr-FkT" secondAttribute="bottom" id="qXL-Hf-8X6"/>
<constraint firstItem="i5M-Pr-FkT" firstAttribute="top" relation="greaterThanOrEqual" secondItem="TKX-qe-h4F" secondAttribute="top" constant="72" id="sYB-lu-PhM"/>
</constraints>
<viewLayoutGuide key="safeArea" id="hNI-dt-MwJ"/>
<point key="canvasLocation" x="-239.5" y="-48.5"/>
</view>
<tapGestureRecognizer id="deN-Z9-rbS" userLabel="Cancel Tap Gesture Recognizer">
<connections>
<action selector="onCancel" destination="-1" id="QyM-e1-7JM"/>
</connections>
</tapGestureRecognizer>
<pongPressGestureRecognizer allowableMovement="10" minimumPressDuration="0.10000000000000001" id="iVR-Au-pTp">
<connections>
<action selector="gestureRecognized:" destination="-1" id="osy-Ks-njV"/>
</connections>
</pongPressGestureRecognizer>
</objects>
<resources>
<image name="ic_get_position" width="20" height="20"/>
<image name="ic_route_manager_trash" width="24" height="24"/>
</resources>
</document>

View file

@ -0,0 +1,32 @@
@objc(MWMRouteManagerViewModel)
final class RouteManagerViewModel: NSObject, RouteManagerViewModelProtocol {
var routePoints: [MWMRoutePoint] { return MWMRouter.points() }
var refreshControlsCallback: (() -> Void)!
var reloadCallback: (() -> Void)!
func startTransaction() { MWMRouter.openRouteManagerTransaction() }
func finishTransaction() {
MWMRouter.applyRouteManagerTransaction()
MWMRouter.rebuild(withBestRouter: false)
}
func cancelTransaction() { MWMRouter.cancelRouteManagerTransaction() }
func addLocationPoint() {
MWMRouter.addPoint(MWMRoutePoint(lastLocationAndType: .start, intermediateIndex: 0))
MWMRouter.updatePreviewMode()
refreshControlsCallback()
}
func movePoint(at index: Int, to newIndex: Int) {
MWMRouter.movePoint(at: index, to: newIndex)
MWMRouter.updatePreviewMode()
refreshControlsCallback()
}
func deletePoint(at index: Int) {
MWMRouter.removePoint(routePoints[index])
MWMRouter.updatePreviewMode()
refreshControlsCallback()
reloadCallback()
}
}

View file

@ -0,0 +1,15 @@
@objc(MWMRouteManagerViewModelProtocol)
protocol RouteManagerViewModelProtocol: AnyObject {
var routePoints: [MWMRoutePoint] { get }
var refreshControlsCallback: (() -> Void)! { get set }
var reloadCallback: (() -> Void)! { get set }
func startTransaction()
func finishTransaction()
func cancelTransaction()
func addLocationPoint()
func movePoint(at index: Int, to newIndex: Int)
func deletePoint(at index: Int)
}

View file

@ -0,0 +1,194 @@
@objc(MWMBaseRoutePreviewStatus)
final class BaseRoutePreviewStatus: SolidTouchView {
@IBOutlet private weak var errorBox: UIView!
@IBOutlet private weak var resultsBox: UIView!
@IBOutlet private weak var heightBox: UIView!
@IBOutlet private weak var manageRouteBox: UIView!
@IBOutlet weak var manageRouteBoxBackground: UIView! {
didSet {
manageRouteBoxBackground.setStyle(.blackOpaqueBackground)
}
}
@IBOutlet private weak var errorLabel: UILabel!
@IBOutlet private weak var resultLabel: UILabel!
@IBOutlet private weak var arriveLabel: UILabel?
@IBOutlet private weak var heightProfileImage: UIImageView!
@IBOutlet private weak var heightProfileElevationHeight: UILabel?
@IBOutlet private weak var manageRouteButtonRegular: UIButton! {
didSet {
configManageRouteButton(manageRouteButtonRegular)
}
}
@IBOutlet private weak var manageRouteButtonCompact: UIButton? {
didSet {
configManageRouteButton(manageRouteButtonCompact!)
}
}
@IBOutlet private weak var saveRouteAsTrackButtonRegular: UIButton! {
didSet {
configSaveRouteAsTrackButton(saveRouteAsTrackButtonRegular)
}
}
@IBOutlet private weak var saveRouteAsTrackButtonCompact: UIButton! {
didSet {
configSaveRouteAsTrackButton(saveRouteAsTrackButtonCompact)
}
}
@IBOutlet private var errorBoxBottom: NSLayoutConstraint!
@IBOutlet private var resultsBoxBottom: NSLayoutConstraint!
@IBOutlet private var heightBoxBottom: NSLayoutConstraint!
@IBOutlet private var manageRouteBoxBottom: NSLayoutConstraint!
@IBOutlet private var heightBoxBottomManageRouteBoxTop: NSLayoutConstraint!
@objc weak var ownerView: UIView!
weak var navigationInfo: MWMNavigationDashboardEntity?
static let elevationAttributes: [NSAttributedString.Key: Any] =
[
.foregroundColor: UIColor.linkBlue(),
.font: UIFont.medium14()
]
var elevation: NSAttributedString? {
didSet {
updateResultsLabel()
}
}
private var isVisible = false {
didSet {
addView()
isHidden = !isVisible
}
}
private func addView() {
guard superview != ownerView else { return }
ownerView.addSubview(self)
let lg = ownerView.safeAreaLayoutGuide
leadingAnchor.constraint(equalTo: lg.leadingAnchor).isActive = true
trailingAnchor.constraint(equalTo: lg.trailingAnchor).isActive = true
bottomAnchor.constraint(equalTo: lg.bottomAnchor).isActive = true
ownerView.layoutIfNeeded()
}
private func updateHeight() {
DispatchQueue.main.async {
self.animateConstraints(animations: {
self.errorBoxBottom.isActive = !self.errorBox.isHidden
self.resultsBoxBottom.isActive = !self.resultsBox.isHidden
self.heightBoxBottom.isActive = !self.heightBox.isHidden
self.heightBoxBottomManageRouteBoxTop.isActive = !self.heightBox.isHidden
self.manageRouteBoxBottom.isActive = !self.manageRouteBox.isHidden
})
}
}
private func configManageRouteButton(_ button: UIButton) {
button.setImagePadding(8)
button.setTitle(L("planning_route_manage_route"), for: .normal)
}
private func configSaveRouteAsTrackButton(_ button: UIButton) {
button.setImagePadding(8)
button.setTitle(L("save"), for: .normal)
button.setTitle(L("saved"), for: .disabled)
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
updateManageRouteVisibility()
updateHeight()
updateResultsLabel()
}
private func updateManageRouteVisibility() {
let isCompact = traitCollection.verticalSizeClass == .compact
manageRouteBox.isHidden = isCompact || resultsBox.isHidden
manageRouteButtonCompact?.isHidden = !isCompact
saveRouteAsTrackButtonCompact.isHidden = !isCompact
}
@objc func hide() {
isVisible = false
}
@objc func showError(message: String) {
isVisible = true
errorBox.isHidden = false
resultsBox.isHidden = true
heightBox.isHidden = true
manageRouteBox.isHidden = true
errorLabel.text = message
updateHeight()
}
@objc func showReady() {
isVisible = true
errorBox.isHidden = true
resultsBox.isHidden = false
elevation = nil
if MWMRouter.hasRouteAltitude() {
heightBox.isHidden = false
MWMRouter.routeAltitudeImage(for: heightProfileImage.frame.size,
completion: { image, totalAscent, totalDescent in
self.heightProfileImage.image = image
if let totalAscent = totalAscent, let totalDescent = totalDescent {
self.elevation = NSAttributedString(string: "\(totalAscent)\(totalDescent)", attributes: BaseRoutePreviewStatus.elevationAttributes)
}
})
} else {
heightBox.isHidden = true
}
setRouteAsTrackButtonEnabled(true)
updateManageRouteVisibility()
updateHeight()
}
@objc func setRouteSaved(_ isSaved: Bool) {
setRouteAsTrackButtonEnabled(!isSaved)
}
private func setRouteAsTrackButtonEnabled(_ isEnabled: Bool) {
saveRouteAsTrackButtonRegular.isEnabled = isEnabled
saveRouteAsTrackButtonCompact.isEnabled = isEnabled
}
private func updateResultsLabel() {
guard let info = navigationInfo else { return }
if let result = info.estimate().mutableCopy() as? NSMutableAttributedString {
if let elevation = self.elevation {
result.append(MWMNavigationDashboardEntity.estimateDot())
result.append(elevation)
}
resultLabel.attributedText = result
}
}
@objc func onNavigationInfoUpdated(_ info: MWMNavigationDashboardEntity) {
navigationInfo = info
updateResultsLabel()
arriveLabel?.text = String(format: L("routing_arrive"), arguments: [info.arrival])
}
override var sideButtonsAreaAffectDirections: MWMAvailableAreaAffectDirections {
return .bottom
}
override var visibleAreaAffectDirections: MWMAvailableAreaAffectDirections {
return .bottom
}
override var widgetsAreaAffectDirections: MWMAvailableAreaAffectDirections {
return .bottom
}
}

View file

@ -0,0 +1,93 @@
@objc(MWMTransportRoutePreviewStatus)
final class TransportRoutePreviewStatus: SolidTouchView {
@IBOutlet private weak var etaLabel: UILabel!
@IBOutlet private weak var stepsCollectionView: TransportTransitStepsCollectionView!
@IBOutlet private weak var stepsCollectionViewHeight: NSLayoutConstraint!
@IBOutlet private weak var stepsCollectionScrollView: UIScrollView?
@objc weak var ownerView: UIView!
weak var navigationInfo: MWMNavigationDashboardEntity?
private var isVisible = false {
didSet {
guard isVisible != oldValue else { return }
if isVisible {
addView()
} else {
self.removeFromSuperview()
}
}
}
private func addView() {
guard superview != ownerView else { return }
ownerView.addSubview(self)
let lg = ownerView.safeAreaLayoutGuide
leadingAnchor.constraint(equalTo: lg.leadingAnchor).isActive = true
trailingAnchor.constraint(equalTo: lg.trailingAnchor).isActive = true
bottomAnchor.constraint(equalTo: lg.bottomAnchor).isActive = true
}
@objc func hide() {
isVisible = false
}
@objc func showReady() {
isVisible = true
updateHeight()
}
@objc func onNavigationInfoUpdated(_ info: MWMNavigationDashboardEntity, prependDistance: Bool) {
navigationInfo = info
if (prependDistance) {
let labelText = NSMutableAttributedString(string: NSLocalizedString("placepage_distance", comment: "") + ": ")
labelText.append(info.estimate())
etaLabel.attributedText = labelText
}
else {
etaLabel.attributedText = info.estimate()
}
stepsCollectionView.steps = info.transitSteps
stepsCollectionView.isHidden = info.transitSteps.isEmpty
stepsCollectionScrollView?.isHidden = info.transitSteps.isEmpty
}
private func updateHeight() {
guard stepsCollectionViewHeight.constant != stepsCollectionView.contentSize.height else { return }
DispatchQueue.main.async {
self.animateConstraints(animations: {
self.stepsCollectionViewHeight.constant = self.stepsCollectionView.contentSize.height
})
if let sv = self.stepsCollectionScrollView {
let bottomOffset = CGPoint(x: 0, y: sv.contentSize.height - sv.bounds.size.height)
sv.setContentOffset(bottomOffset, animated: true)
}
}
}
override func layoutSubviews() {
super.layoutSubviews()
updateHeight()
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
updateHeight()
}
override var sideButtonsAreaAffectDirections: MWMAvailableAreaAffectDirections {
return .bottom
}
override var visibleAreaAffectDirections: MWMAvailableAreaAffectDirections {
return .bottom
}
override var widgetsAreaAffectDirections: MWMAvailableAreaAffectDirections {
return .bottom
}
}

View file

@ -0,0 +1,35 @@
final class TransportRuler: TransportTransitCell {
enum Config {
static var backgroundColor: UIColor { return UIColor.blackOpaque() }
static var imageColor: UIColor { return UIColor.blackSecondaryText() }
static var labelTextColor: UIColor { return .black }
static let labelTextFont = UIFont.bold12()
static let labelTrailing: CGFloat = 8
}
@IBOutlet private weak var background: UIView! {
didSet {
background.layer.setCornerRadius(.buttonSmall)
background.backgroundColor = Config.backgroundColor
}
}
@IBOutlet private weak var label: UILabel! {
didSet {
label.textColor = Config.labelTextColor
label.font = Config.labelTextFont
}
}
override class func estimatedCellSize(step: MWMRouterTransitStepInfo) -> CGSize {
let defaultSize = super.estimatedCellSize(step: step)
let labelText = step.distance + " " + step.distanceUnits;
let labelSize = labelText.size(width: CGFloat.greatestFiniteMagnitude, font: Config.labelTextFont, maxNumberOfLines: 1)
return CGSize(width: labelSize.width + Config.labelTrailing, height: defaultSize.height)
}
override func config(step: MWMRouterTransitStepInfo) {
label.isHidden = step.distance.isEmpty && step.distanceUnits.isEmpty
label.text = step.distance + " " + step.distanceUnits
}
}

View file

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/>
<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"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="TransportRuler" customModule="CoMaps" customModuleProvider="target" propertyAccessControl="all">
<rect key="frame" x="0.0" y="0.0" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="zFu-7m-DoW">
<rect key="frame" x="0.0" y="0.0" width="20" height="20"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="450" verticalCompressionResistancePriority="450" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="E5p-F1-lok">
<rect key="frame" x="-25.5" y="0.0" width="41.5" height="20"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="E5p-F1-lok" secondAttribute="trailing" priority="750" id="5NZ-eM-6di"/>
<constraint firstAttribute="bottom" secondItem="E5p-F1-lok" secondAttribute="bottom" id="Gu9-KZ-D9R"/>
<constraint firstAttribute="trailing" secondItem="E5p-F1-lok" secondAttribute="trailing" priority="250" constant="4" id="YNn-8w-SmG"/>
<constraint firstItem="E5p-F1-lok" firstAttribute="top" secondItem="zFu-7m-DoW" secondAttribute="top" id="ZPx-f9-8LV"/>
<constraint firstAttribute="height" constant="20" id="nQ0-T1-CvV"/>
</constraints>
</view>
</subviews>
</view>
<viewLayoutGuide key="safeArea" id="UN7-Tp-qio"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="zFu-7m-DoW" secondAttribute="bottom" id="3wL-hq-d5s"/>
<constraint firstItem="zFu-7m-DoW" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="JMg-4a-h4Z"/>
<constraint firstItem="zFu-7m-DoW" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="MOo-JD-4vU"/>
<constraint firstAttribute="trailing" secondItem="zFu-7m-DoW" secondAttribute="trailing" id="gmg-yt-uVD"/>
</constraints>
<size key="customSize" width="744" height="20"/>
<connections>
<outlet property="background" destination="zFu-7m-DoW" id="fh8-Ss-FeL"/>
<outlet property="label" destination="E5p-F1-lok" id="rsx-8D-Hvq"/>
</connections>
<point key="canvasLocation" x="612.79999999999995" y="48.575712143928037"/>
</collectionViewCell>
</objects>
</document>

View file

@ -0,0 +1,11 @@
class TransportTransitCell: UICollectionViewCell {
enum Config {
static let cellSize = CGSize(width: 20, height: 20)
}
class func estimatedCellSize(step _: MWMRouterTransitStepInfo) -> CGSize {
return Config.cellSize
}
func config(step _: MWMRouterTransitStepInfo) {}
}

View file

@ -0,0 +1,77 @@
final class TransportTransitFlowLayout: UICollectionViewLayout {
enum Config {
static let lineSpacing = CGFloat(8)
static let separator = TransportTransitSeparator.self
static let separatorKind = toString(separator)
static let separatorSize = CGSize(width: 16, height: 20)
static let minimumCellWidthAfterShrink = CGFloat(56)
}
private var cellsLayoutAttrs = [UICollectionViewLayoutAttributes]()
private var decoratorsLayoutAttrs = [UICollectionViewLayoutAttributes]()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
register(Config.separator, forDecorationViewOfKind: Config.separatorKind)
}
override var collectionViewContentSize: CGSize {
let width = collectionView?.frame.width ?? 0
let height = cellsLayoutAttrs.reduce(0) { return max($0, $1.frame.maxY) }
return CGSize(width: width, height: height)
}
override func prepare() {
super.prepare()
let cv = collectionView as! TransportTransitStepsCollectionView
let section = 0
let width = cv.width
var x = CGFloat(0)
var y = CGFloat(0)
cellsLayoutAttrs = []
decoratorsLayoutAttrs = []
for item in 0 ..< cv.numberOfItems(inSection: section) {
let ip = IndexPath(item: item, section: section)
if item != 0 {
let sepAttr = UICollectionViewLayoutAttributes(forDecorationViewOfKind: Config.separatorKind, with: ip)
sepAttr.frame = CGRect(origin: CGPoint(x: x, y: y), size: Config.separatorSize)
decoratorsLayoutAttrs.append(sepAttr)
x += Config.separatorSize.width
}
var cellSize = cv.estimatedCellSize(item: item)
let spaceLeft = width - x - Config.separatorSize.width
let minimumSpaceRequired = min(cellSize.width, Config.minimumCellWidthAfterShrink)
if spaceLeft < minimumSpaceRequired {
x = 0
y += Config.separatorSize.height + Config.lineSpacing
} else {
cellSize.width = min(cellSize.width, spaceLeft)
}
let cellAttr = UICollectionViewLayoutAttributes(forCellWith: ip)
cellAttr.frame = CGRect(origin: CGPoint(x: x, y: y), size: cellSize)
cellsLayoutAttrs.append(cellAttr)
x += cellSize.width
}
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return cellsLayoutAttrs.first(where: { $0.indexPath == indexPath })
}
override func layoutAttributesForDecorationView(ofKind _: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return decoratorsLayoutAttrs.first(where: { $0.indexPath == indexPath })
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var layoutAttrs = cellsLayoutAttrs.filter { $0.frame.intersects(rect) }
layoutAttrs.append(contentsOf: decoratorsLayoutAttrs.filter { $0.frame.intersects(rect) })
return layoutAttrs
}
}

View file

@ -0,0 +1,18 @@
final class TransportTransitIntermediatePoint: TransportTransitCell {
enum Config {
static var imageColor: UIColor { return UIColor.primary() }
}
@IBOutlet private weak var image: UIImageView!
override func config(step: MWMRouterTransitStepInfo) {
super.config(step: step)
let i = step.intermediateIndex + 1
// TODO: Properly support more than 20 icons.
var iconName = "route-point-20"
if (i >= 1 && i < 20) {
iconName = "route-point-" + String(i)
}
image.image = #imageLiteral(resourceName: iconName)
image.tintColor = Config.imageColor
}
}

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<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"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="TransportTransitIntermediatePoint" customModule="CoMaps" customModuleProvider="target" propertyAccessControl="all">
<rect key="frame" x="0.0" y="0.0" width="20" height="20"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="route-point-1" translatesAutoresizingMaskIntoConstraints="NO" id="kZf-7J-OZ5">
<rect key="frame" x="0.0" y="0.0" width="20" height="20"/>
<color key="tintColor" red="0.1215686275" green="0.59999999999999998" blue="0.32156862749999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="20" id="OkK-UD-TWr"/>
<constraint firstAttribute="height" constant="20" id="n8x-xG-gUb"/>
</constraints>
</imageView>
</subviews>
</view>
<constraints>
<constraint firstItem="kZf-7J-OZ5" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="5rb-Q4-qua"/>
<constraint firstAttribute="bottom" secondItem="kZf-7J-OZ5" secondAttribute="bottom" id="5uG-SP-l0m"/>
<constraint firstItem="kZf-7J-OZ5" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="KiE-6N-4Jp"/>
<constraint firstAttribute="trailing" secondItem="kZf-7J-OZ5" secondAttribute="trailing" id="mJI-lD-bsw"/>
</constraints>
<viewLayoutGuide key="safeArea" id="Aa6-gi-Bkw"/>
<size key="customSize" width="490" height="20"/>
<connections>
<outlet property="image" destination="kZf-7J-OZ5" id="b9M-uB-XoA"/>
</connections>
<point key="canvasLocation" x="270" y="54"/>
</collectionViewCell>
</objects>
<resources>
<image name="route-point-1" width="24" height="24"/>
</resources>
</document>

View file

@ -0,0 +1,21 @@
final class TransportTransitPedestrian: TransportTransitCell {
enum Config {
static var backgroundColor: UIColor { return UIColor.blackOpaque() }
static var imageColor: UIColor { return UIColor.blackSecondaryText() }
}
@IBOutlet private weak var background: UIView! {
didSet {
background.layer.setCornerRadius(.buttonSmall)
background.backgroundColor = Config.backgroundColor
}
}
@IBOutlet private weak var image: UIImageView! {
didSet {
image.image = UIImage(resource: .icWalk)
image.tintColor = Config.imageColor
image.contentMode = .scaleAspectFit
}
}
}

View file

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<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"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="TransportTransitPedestrian" customModule="CoMaps" customModuleProvider="target" propertyAccessControl="all">
<rect key="frame" x="0.0" y="0.0" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Shb-td-cXL">
<rect key="frame" x="0.0" y="0.0" width="20" height="20"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" insetsLayoutMarginsFromSafeArea="NO" image="ic_walk" translatesAutoresizingMaskIntoConstraints="NO" id="9hI-Y9-uRa">
<rect key="frame" x="3" y="3" width="14" height="14"/>
<constraints>
<constraint firstAttribute="width" constant="14" id="EeL-qS-rKg"/>
<constraint firstAttribute="height" constant="14" id="WCk-l4-hQF"/>
</constraints>
</imageView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="9hI-Y9-uRa" firstAttribute="centerY" secondItem="Shb-td-cXL" secondAttribute="centerY" id="IkJ-Yh-48o"/>
<constraint firstAttribute="height" constant="20" id="hEu-YE-u8j"/>
<constraint firstAttribute="width" constant="20" id="pxM-8S-VsE"/>
<constraint firstItem="9hI-Y9-uRa" firstAttribute="centerX" secondItem="Shb-td-cXL" secondAttribute="centerX" id="vZX-k3-HmM"/>
</constraints>
</view>
</subviews>
</view>
<constraints>
<constraint firstAttribute="trailing" secondItem="Shb-td-cXL" secondAttribute="trailing" id="JRQ-z7-d3U"/>
<constraint firstAttribute="bottom" secondItem="Shb-td-cXL" secondAttribute="bottom" id="nDY-rn-9J0"/>
<constraint firstItem="Shb-td-cXL" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="pu1-LW-wkd"/>
<constraint firstItem="Shb-td-cXL" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="ymH-iI-Ify"/>
</constraints>
<viewLayoutGuide key="safeArea" id="gmH-YP-0pW"/>
<size key="customSize" width="227" height="20"/>
<connections>
<outlet property="background" destination="Shb-td-cXL" id="YAM-iA-NYW"/>
<outlet property="image" destination="9hI-Y9-uRa" id="9i8-eW-XWO"/>
</connections>
<point key="canvasLocation" x="137.5" y="54"/>
</collectionViewCell>
</objects>
<resources>
<image name="ic_walk" width="24" height="24"/>
</resources>
</document>

View file

@ -0,0 +1,23 @@
final class TransportTransitSeparator: UICollectionReusableView {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
let image = UIImageView(image: #imageLiteral(resourceName: "ic_arrow"))
image.setStyle(.black)
image.contentMode = .scaleAspectFit
addSubview(image)
}
override func layoutSubviews() {
super.layoutSubviews()
subviews.first!.frame = bounds
}
}

View file

@ -0,0 +1,59 @@
final class TransportTransitStepsCollectionView: UICollectionView {
var steps: [MWMRouterTransitStepInfo] = [] {
didSet {
reloadData()
}
}
override var frame: CGRect {
didSet {
collectionViewLayout.invalidateLayout()
}
}
override var bounds: CGRect {
didSet {
collectionViewLayout.invalidateLayout()
}
}
override func awakeFromNib() {
super.awakeFromNib()
dataSource = self
[TransportTransitIntermediatePoint.self, TransportTransitPedestrian.self,
TransportTransitTrain.self, TransportRuler.self].forEach {
register(cellClass: $0)
}
}
private func cellClass(item: Int) -> TransportTransitCell.Type {
let step = steps[item]
switch step.type {
case .intermediatePoint: return TransportTransitIntermediatePoint.self
case .pedestrian: return TransportTransitPedestrian.self
case .train: fallthrough
case .subway: fallthrough
case .lightRail: fallthrough
case .monorail: return TransportTransitTrain.self
case .ruler: return TransportRuler.self
}
}
func estimatedCellSize(item: Int) -> CGSize {
return cellClass(item: item).estimatedCellSize(step: steps[item])
}
}
extension TransportTransitStepsCollectionView: UICollectionViewDataSource {
func collectionView(_: UICollectionView, numberOfItemsInSection _: Int) -> Int {
return steps.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let item = indexPath.item
let cellClass = self.cellClass(item: item)
let cell = collectionView.dequeueReusableCell(withCellClass: cellClass, indexPath: indexPath) as! TransportTransitCell
cell.config(step: steps[item])
return cell
}
}

View file

@ -0,0 +1,52 @@
final class TransportTransitTrain: TransportTransitCell {
enum Config {
static var labelTextColor: UIColor { return .white }
static let labelTextFont = UIFont.bold12()
static let labelTrailing: CGFloat = 4
}
@IBOutlet private weak var background: UIView! {
didSet {
background.layer.setCornerRadius(.buttonSmall)
}
}
@IBOutlet private weak var image: UIImageView!
@IBOutlet private weak var label: UILabel! {
didSet {
label.textColor = Config.labelTextColor
label.font = Config.labelTextFont
}
}
@IBOutlet private weak var labelTrailing: NSLayoutConstraint! {
didSet {
labelTrailing.constant = Config.labelTrailing
}
}
@IBOutlet private var emptyNumberTrailingOffset: NSLayoutConstraint!
override class func estimatedCellSize(step: MWMRouterTransitStepInfo) -> CGSize {
let defaultSize = super.estimatedCellSize(step: step)
let labelSize = step.number.size(width: CGFloat.greatestFiniteMagnitude, font: Config.labelTextFont, maxNumberOfLines: 1)
return CGSize(width: defaultSize.width + labelSize.width + Config.labelTrailing, height: defaultSize.height)
}
override func config(step: MWMRouterTransitStepInfo) {
switch step.type {
case .intermediatePoint: fallthrough
case .pedestrian: fatalError()
case .train: image.image = #imageLiteral(resourceName: "ic_20px_route_planning_train")
case .subway: image.image = #imageLiteral(resourceName: "ic_20px_route_planning_metro")
case .lightRail: image.image = #imageLiteral(resourceName: "ic_20px_route_planning_lightrail")
case .monorail: image.image = #imageLiteral(resourceName: "ic_20px_route_planning_monorail")
case .ruler: fatalError()
}
background.backgroundColor = step.color
emptyNumberTrailingOffset.isActive = step.number.isEmpty
label.isHidden = step.number.isEmpty
label.text = step.number
}
}

View file

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<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"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="TransportTransitTrain" customModule="CoMaps" customModuleProvider="target" propertyAccessControl="all">
<rect key="frame" x="0.0" y="0.0" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kDc-o2-JUp">
<rect key="frame" x="0.0" y="0.0" width="20" height="20"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="9d1-PP-h28">
<rect key="frame" x="0.0" y="0.0" width="20" height="20"/>
<constraints>
<constraint firstAttribute="width" secondItem="9d1-PP-h28" secondAttribute="height" multiplier="1:1" id="g45-u1-mQs"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="450" verticalCompressionResistancePriority="450" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0Eo-0D-mup">
<rect key="frame" x="20" y="0.0" width="0.0" height="20"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="9d1-PP-h28" firstAttribute="top" secondItem="kDc-o2-JUp" secondAttribute="top" id="3Nm-tA-OCb"/>
<constraint firstItem="0Eo-0D-mup" firstAttribute="leading" secondItem="9d1-PP-h28" secondAttribute="trailing" id="3sb-9G-W6c"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="0Eo-0D-mup" secondAttribute="trailing" priority="750" id="5NZ-eM-6di"/>
<constraint firstAttribute="bottom" secondItem="0Eo-0D-mup" secondAttribute="bottom" id="Gu9-KZ-D9R"/>
<constraint firstAttribute="trailing" secondItem="0Eo-0D-mup" secondAttribute="trailing" priority="250" constant="4" id="YNn-8w-SmG"/>
<constraint firstItem="0Eo-0D-mup" firstAttribute="top" secondItem="kDc-o2-JUp" secondAttribute="top" id="ZPx-f9-8LV"/>
<constraint firstItem="9d1-PP-h28" firstAttribute="leading" secondItem="kDc-o2-JUp" secondAttribute="leading" id="cQu-Lg-9xy"/>
<constraint firstAttribute="bottom" secondItem="9d1-PP-h28" secondAttribute="bottom" id="m4N-mH-YaE"/>
<constraint firstAttribute="trailing" secondItem="9d1-PP-h28" secondAttribute="trailing" id="mCa-Ah-9Xz"/>
<constraint firstAttribute="height" constant="20" id="nQ0-T1-CvV"/>
</constraints>
</view>
</subviews>
</view>
<constraints>
<constraint firstAttribute="bottom" secondItem="kDc-o2-JUp" secondAttribute="bottom" id="3wL-hq-d5s"/>
<constraint firstItem="kDc-o2-JUp" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="JMg-4a-h4Z"/>
<constraint firstItem="kDc-o2-JUp" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="MOo-JD-4vU"/>
<constraint firstAttribute="trailing" secondItem="kDc-o2-JUp" secondAttribute="trailing" id="gmg-yt-uVD"/>
</constraints>
<viewLayoutGuide key="safeArea" id="UN7-Tp-qio"/>
<size key="customSize" width="744" height="20"/>
<connections>
<outlet property="background" destination="kDc-o2-JUp" id="fh8-Ss-FeL"/>
<outlet property="emptyNumberTrailingOffset" destination="mCa-Ah-9Xz" id="k8k-Ca-u6s"/>
<outlet property="image" destination="9d1-PP-h28" id="Kpo-zR-osU"/>
<outlet property="label" destination="0Eo-0D-mup" id="Z15-fa-Wkx"/>
<outlet property="labelTrailing" destination="YNn-8w-SmG" id="lL1-5q-mjx"/>
</connections>
<point key="canvasLocation" x="383" y="54"/>
</collectionViewCell>
</objects>
</document>

View file

@ -0,0 +1,28 @@
@objc(MWMRouteStartButton)
final class RouteStartButton: UIButton {
@objc func statePrepare() {
isHidden = false
isEnabled = false
}
@objc func stateError() {
isHidden = true
isEnabled = false
}
@objc func stateReady() {
isHidden = false
isEnabled = true
}
@objc func stateHidden() {
isHidden = true
isEnabled = true
}
override func applyTheme() {
super.applyTheme()
setBackgroundImage(UIColor.linkBlue().getImage(), for: .normal)
setBackgroundImage(UIColor.linkBlueHighlighted().getImage(), for: .highlighted)
}
}