Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
37
iphone/Maps/Classes/Components/BorderedButton.swift
Normal file
37
iphone/Maps/Classes/Components/BorderedButton.swift
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
@objc(MWMBorderedButton)
|
||||
final class BorderedButton: UIButton {
|
||||
|
||||
private var borderColor: UIColor?
|
||||
private var borderHighlightedColor: UIColor?
|
||||
private var borderDisabledColor: UIColor?
|
||||
|
||||
@objc func setBorderColor(_ color: UIColor) {
|
||||
borderColor = color
|
||||
}
|
||||
|
||||
@objc func setBorderHighlightedColor(_ color: UIColor) {
|
||||
borderHighlightedColor = color
|
||||
}
|
||||
|
||||
@objc func setBorderDisabledColor(_ color: UIColor) {
|
||||
borderDisabledColor = color
|
||||
}
|
||||
|
||||
private func updateBorder() {
|
||||
if !isEnabled {
|
||||
layer.borderColor = borderDisabledColor?.cgColor ?? titleColor(for: .disabled)?.cgColor
|
||||
} else if isHighlighted {
|
||||
layer.borderColor = borderHighlightedColor?.cgColor ?? titleColor(for: .highlighted)?.cgColor
|
||||
} else {
|
||||
layer.borderColor = borderColor?.cgColor ?? titleColor(for: .normal)?.cgColor
|
||||
}
|
||||
}
|
||||
|
||||
override var isEnabled: Bool {
|
||||
didSet { updateBorder() }
|
||||
}
|
||||
|
||||
override var isHighlighted: Bool {
|
||||
didSet { updateBorder() }
|
||||
}
|
||||
}
|
||||
112
iphone/Maps/Classes/Components/Checkmark.swift
Normal file
112
iphone/Maps/Classes/Components/Checkmark.swift
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
class Checkmark: UIControl {
|
||||
|
||||
private let imageView = UIImageView(frame: .zero)
|
||||
|
||||
@IBInspectable
|
||||
var offImage: UIImage? {
|
||||
didSet {
|
||||
updateImage(animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var onImage: UIImage? {
|
||||
didSet {
|
||||
updateImage(animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var offTintColor: UIColor? {
|
||||
didSet {
|
||||
updateTintColor()
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var onTintColor: UIColor? {
|
||||
didSet {
|
||||
updateTintColor()
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var isChecked: Bool = false {
|
||||
didSet {
|
||||
updateImage(animated: true)
|
||||
updateTintColor()
|
||||
}
|
||||
}
|
||||
|
||||
override var isHighlighted: Bool {
|
||||
didSet {
|
||||
imageView.tintColor = isHighlighted ? tintColor.blending(with: UIColor(white: 0, alpha: 0.5)) : nil
|
||||
}
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
initViews()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
initViews()
|
||||
}
|
||||
|
||||
private func initViews() {
|
||||
addSubview(imageView)
|
||||
addTarget(self, action: #selector(onTouch), for: .touchUpInside)
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
imageView.sizeToFit()
|
||||
|
||||
var left: CGFloat = 0;
|
||||
var top: CGFloat = 0;
|
||||
var width: CGFloat = imageView.width;
|
||||
var height: CGFloat = imageView.height;
|
||||
|
||||
switch contentHorizontalAlignment {
|
||||
case .right: fallthrough
|
||||
case .trailing:
|
||||
left = floor(bounds.width - imageView.width)
|
||||
case .center:
|
||||
left = floor((bounds.width - width) / 2)
|
||||
case .fill:
|
||||
width = bounds.width
|
||||
default:
|
||||
left = 0
|
||||
}
|
||||
|
||||
switch contentVerticalAlignment {
|
||||
case .top:
|
||||
top = 0
|
||||
case .bottom:
|
||||
top = floor(bounds.height - height)
|
||||
case .center:
|
||||
top = floor((bounds.height - height) / 2)
|
||||
case .fill:
|
||||
height = bounds.height
|
||||
@unknown default:
|
||||
fatalError("Unexpected case in contentVerticalAlignment switch")
|
||||
}
|
||||
|
||||
imageView.frame = CGRect(x: left, y: top, width: width, height: height)
|
||||
}
|
||||
|
||||
@objc func onTouch() {
|
||||
isChecked = !isChecked
|
||||
sendActions(for: .valueChanged)
|
||||
}
|
||||
|
||||
private func updateImage(animated: Bool) {
|
||||
self.imageView.image = self.isChecked ? self.onImage : self.offImage
|
||||
}
|
||||
|
||||
private func updateTintColor() {
|
||||
tintColor = isChecked ? onTintColor : offTintColor
|
||||
}
|
||||
}
|
||||
48
iphone/Maps/Classes/Components/CopyableLabel.swift
Normal file
48
iphone/Maps/Classes/Components/CopyableLabel.swift
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// Label shows copy popup menu on tap or long tap.
|
||||
class CopyableLabel: UILabel {
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.sharedInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
self.sharedInit()
|
||||
}
|
||||
|
||||
private func sharedInit() {
|
||||
self.isUserInteractionEnabled = true
|
||||
self.gestureRecognizers = [
|
||||
UILongPressGestureRecognizer(target: self, action: #selector(self.showMenu)),
|
||||
UITapGestureRecognizer(target: self, action: #selector(self.showMenu))
|
||||
]
|
||||
}
|
||||
|
||||
@objc func showMenu(_ recognizer: UILongPressGestureRecognizer) {
|
||||
self.becomeFirstResponder()
|
||||
|
||||
let menu = UIMenuController.shared
|
||||
let locationOfTouchInLabel = recognizer.location(in: self)
|
||||
|
||||
if !menu.isMenuVisible {
|
||||
var rect = bounds
|
||||
rect.origin = locationOfTouchInLabel
|
||||
rect.size = CGSize(width: 1, height: 1)
|
||||
menu.showMenu(from: self, rect: rect)
|
||||
}
|
||||
}
|
||||
|
||||
override func copy(_ sender: Any?) {
|
||||
UIPasteboard.general.string = text
|
||||
UIMenuController.shared.hideMenu()
|
||||
}
|
||||
|
||||
override var canBecomeFirstResponder: Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
|
||||
return action == #selector(UIResponderStandardEditActions.copy)
|
||||
}
|
||||
}
|
||||
42
iphone/Maps/Classes/Components/DimBackground.swift
Normal file
42
iphone/Maps/Classes/Components/DimBackground.swift
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
@objc(MWMDimBackground)
|
||||
final class DimBackground: SolidTouchView {
|
||||
private let mainView: UIView
|
||||
private var tapAction: () -> Void
|
||||
|
||||
@objc init(mainView: UIView, tapAction: @escaping () -> Void) {
|
||||
self.mainView = mainView
|
||||
self.tapAction = tapAction
|
||||
super.init(frame: mainView.superview!.bounds)
|
||||
setStyle(.fadeBackground)
|
||||
autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTap)))
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc func setVisible(_ visible: Bool, completion: (() -> Void)?) {
|
||||
if visible {
|
||||
let sv = mainView.superview!
|
||||
frame = sv.bounds
|
||||
sv.insertSubview(self, belowSubview: mainView)
|
||||
alpha = 0
|
||||
} else {
|
||||
alpha = 0.8
|
||||
}
|
||||
UIView.animate(withDuration: kDefaultAnimationDuration,
|
||||
animations: { self.alpha = visible ? 0.8 : 0 },
|
||||
completion: { _ in
|
||||
if !visible {
|
||||
self.removeFromSuperview()
|
||||
}
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
|
||||
@objc
|
||||
private func onTap() {
|
||||
tapAction()
|
||||
}
|
||||
}
|
||||
13
iphone/Maps/Classes/Components/LeftAlignedIconButton.swift
Normal file
13
iphone/Maps/Classes/Components/LeftAlignedIconButton.swift
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
@IBDesignable
|
||||
class LeftAlignedIconButton: UIButton {
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
contentHorizontalAlignment = .left
|
||||
let availableSpace = bounds.inset(by: contentEdgeInsets)
|
||||
let imageWidth = imageView?.frame.width ?? 0
|
||||
let titleWidth = titleLabel?.frame.width ?? 0
|
||||
let availableWidth = availableSpace.width - imageEdgeInsets.right - imageWidth * 2 - titleWidth
|
||||
titleEdgeInsets = UIEdgeInsets(top: 0, left: floor(availableWidth) / 2, bottom: 0, right: 0)
|
||||
}
|
||||
}
|
||||
|
||||
5
iphone/Maps/Classes/Components/LinkTextView.swift
Normal file
5
iphone/Maps/Classes/Components/LinkTextView.swift
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
class LinkTextView : UITextView {
|
||||
override var canBecomeFirstResponder: Bool {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
11
iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.h
Normal file
11
iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#include "geometry/point2d.hpp"
|
||||
|
||||
@interface MWMAddPlaceNavigationBar : SolidTouchView
|
||||
|
||||
+ (void)showInSuperview:(UIView *)superview
|
||||
isBusiness:(BOOL)isBusiness
|
||||
position:(m2::PointD const *)optionalPosition
|
||||
doneBlock:(MWMVoidBlock)done
|
||||
cancelBlock:(MWMVoidBlock)cancel;
|
||||
|
||||
@end
|
||||
76
iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.mm
Normal file
76
iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.mm
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#import "MWMAddPlaceNavigationBar.h"
|
||||
|
||||
#include <CoreApi/Framework.h>
|
||||
|
||||
@interface MWMAddPlaceNavigationBar ()
|
||||
|
||||
@property(copy, nonatomic) MWMVoidBlock doneBlock;
|
||||
@property(copy, nonatomic) MWMVoidBlock cancelBlock;
|
||||
@property(assign, nonatomic) NSLayoutConstraint* topConstraint;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMAddPlaceNavigationBar
|
||||
|
||||
+ (void)showInSuperview:(UIView *)superview
|
||||
isBusiness:(BOOL)isBusiness
|
||||
position:(m2::PointD const *)optionalPosition
|
||||
doneBlock:(MWMVoidBlock)done
|
||||
cancelBlock:(MWMVoidBlock)cancel
|
||||
{
|
||||
MWMAddPlaceNavigationBar * navBar =
|
||||
[NSBundle.mainBundle loadNibNamed:self.className owner:nil options:nil].firstObject;
|
||||
navBar.width = superview.width;
|
||||
navBar.doneBlock = done;
|
||||
navBar.cancelBlock = cancel;
|
||||
navBar.translatesAutoresizingMaskIntoConstraints = false;
|
||||
|
||||
[superview addSubview:navBar];
|
||||
navBar.topConstraint = [navBar.topAnchor constraintEqualToAnchor:superview.topAnchor];
|
||||
navBar.topConstraint.active = true;
|
||||
navBar.topConstraint.constant = -navBar.height;
|
||||
[navBar.trailingAnchor constraintEqualToAnchor:superview.trailingAnchor].active = true;
|
||||
[navBar.leadingAnchor constraintEqualToAnchor:superview.leadingAnchor].active = true;
|
||||
[navBar show:isBusiness position:optionalPosition];
|
||||
}
|
||||
|
||||
- (void)show:(BOOL)enableBounds position:(m2::PointD const *)optionalPosition
|
||||
{
|
||||
auto & f = GetFramework();
|
||||
f.EnableChoosePositionMode(true /* enable */, enableBounds, optionalPosition);
|
||||
f.BlockTapEvents(true);
|
||||
|
||||
[UIView animateWithDuration:kDefaultAnimationDuration animations:^
|
||||
{
|
||||
self.topConstraint.constant = 0;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)dismissWithBlock:(MWMVoidBlock)block
|
||||
{
|
||||
auto & f = GetFramework();
|
||||
f.EnableChoosePositionMode(false /* enable */, false /* enableBounds */, nullptr /* optionalPosition */);
|
||||
f.BlockTapEvents(false);
|
||||
|
||||
[UIView animateWithDuration:kDefaultAnimationDuration animations:^
|
||||
{
|
||||
self.topConstraint.constant = -self.height;
|
||||
}
|
||||
completion:^(BOOL finished)
|
||||
{
|
||||
[self removeFromSuperview];
|
||||
block();
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)doneTap
|
||||
{
|
||||
[self dismissWithBlock:self.doneBlock];
|
||||
}
|
||||
|
||||
- (IBAction)cancelTap
|
||||
{
|
||||
[self dismissWithBlock:self.cancelBlock];
|
||||
}
|
||||
|
||||
@end
|
||||
137
iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.xib
Normal file
137
iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.xib
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="24128" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24063"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="BJ6-My-yAy" customClass="MWMAddPlaceNavigationBar" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="100"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IhK-ce-TXk">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="64"/>
|
||||
<color key="backgroundColor" red="0.1176470588" green="0.58823529409999997" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="PrimaryBackground"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ZKD-Be-eMp">
|
||||
<rect key="frame" x="0.0" y="20" width="320" height="44"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" horizontalCompressionResistancePriority="1000" contentHorizontalAlignment="right" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="zs9-MX-bIe">
|
||||
<rect key="frame" x="258" y="12" width="54" height="19"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="19" id="psK-YU-lUF"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="Готово"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="NavigationBarItem"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="continue_button"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="doneTap" destination="BJ6-My-yAy" eventType="touchUpInside" id="hS1-Tz-6Zo"/>
|
||||
</connections>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="1000" text="Местоположение" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8So-22-JS1">
|
||||
<rect key="frame" x="90" y="12" width="140" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="JFT-iW-rJY"/>
|
||||
<constraint firstAttribute="height" constant="20" id="mun-d1-wdT"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<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="NavigationBarItem"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="editor_add_select_location"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" horizontalCompressionResistancePriority="1000" contentHorizontalAlignment="left" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="boX-sU-7DW">
|
||||
<rect key="frame" x="8" y="12" width="62" height="19"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="19" id="KDz-RG-UH2"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="Отмена"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="NavigationBarItem"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="cancel"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="cancelTap" destination="BJ6-My-yAy" eventType="touchUpInside" id="f2O-El-juu"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.1176470588" green="0.58823529409999997" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="5VI-KR-aXh"/>
|
||||
<constraint firstItem="zs9-MX-bIe" firstAttribute="top" secondItem="ZKD-Be-eMp" secondAttribute="top" constant="12" id="BNr-Gr-o3j"/>
|
||||
<constraint firstItem="8So-22-JS1" firstAttribute="top" secondItem="ZKD-Be-eMp" secondAttribute="top" constant="12" id="CCm-fM-FcD"/>
|
||||
<constraint firstItem="boX-sU-7DW" firstAttribute="top" secondItem="ZKD-Be-eMp" secondAttribute="top" constant="12" id="Ofu-8n-bBo"/>
|
||||
<constraint firstItem="boX-sU-7DW" firstAttribute="leading" secondItem="ZKD-Be-eMp" secondAttribute="leading" constant="8" id="Sbg-dn-iro"/>
|
||||
<constraint firstAttribute="trailing" secondItem="zs9-MX-bIe" secondAttribute="trailing" constant="8" id="jiJ-gk-0bx"/>
|
||||
<constraint firstItem="8So-22-JS1" firstAttribute="centerX" secondItem="ZKD-Be-eMp" secondAttribute="centerX" id="m6T-st-PPz"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="PrimaryBackground"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="UsM-Al-MCC">
|
||||
<rect key="frame" x="0.0" y="64" width="320" height="56"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Потяните карту, чтобы выбрать правильное местоположение места. " lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="R6E-kO-6Lb">
|
||||
<rect key="frame" x="14" y="16" width="292" height="30"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" systemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular12:blackSecondaryText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="editor_focus_map_on_location"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.96078431372549022" green="0.96078431372549022" blue="0.96078431372549022" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="R6E-kO-6Lb" firstAttribute="leading" secondItem="UsM-Al-MCC" secondAttribute="leadingMargin" constant="6" id="F0k-Ua-uzh"/>
|
||||
<constraint firstItem="R6E-kO-6Lb" firstAttribute="top" secondItem="UsM-Al-MCC" secondAttribute="topMargin" constant="8" id="G1O-ji-PsY"/>
|
||||
<constraint firstAttribute="height" constant="56" id="RVj-Yg-4Hc"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="R6E-kO-6Lb" secondAttribute="trailing" constant="6" id="cRp-da-FUC"/>
|
||||
<constraint firstAttribute="bottomMargin" secondItem="R6E-kO-6Lb" secondAttribute="bottom" constant="2" id="e1g-Jr-sY9"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MenuBackground"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="tvs-e8-vSU"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="top" secondItem="IhK-ce-TXk" secondAttribute="top" id="B9q-oF-68E"/>
|
||||
<constraint firstItem="UsM-Al-MCC" firstAttribute="leading" secondItem="BJ6-My-yAy" secondAttribute="leading" id="C3j-SM-MSO"/>
|
||||
<constraint firstItem="ZKD-Be-eMp" firstAttribute="leading" secondItem="BJ6-My-yAy" secondAttribute="leading" id="Ckp-sr-B7c"/>
|
||||
<constraint firstItem="ZKD-Be-eMp" firstAttribute="top" secondItem="tvs-e8-vSU" secondAttribute="top" id="Drs-L4-75n"/>
|
||||
<constraint firstItem="IhK-ce-TXk" firstAttribute="leading" secondItem="tvs-e8-vSU" secondAttribute="leading" id="E1h-R5-QaY"/>
|
||||
<constraint firstAttribute="bottom" secondItem="UsM-Al-MCC" secondAttribute="bottom" id="Eii-Wt-WAj"/>
|
||||
<constraint firstItem="tvs-e8-vSU" firstAttribute="trailing" secondItem="IhK-ce-TXk" secondAttribute="trailing" id="FcR-B0-Kwb"/>
|
||||
<constraint firstAttribute="trailing" secondItem="UsM-Al-MCC" secondAttribute="trailing" id="FrY-zu-zwd"/>
|
||||
<constraint firstItem="UsM-Al-MCC" firstAttribute="top" secondItem="IhK-ce-TXk" secondAttribute="bottom" id="Tfo-lj-gPb"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ZKD-Be-eMp" secondAttribute="trailing" id="Yrh-qN-0sk"/>
|
||||
<constraint firstItem="IhK-ce-TXk" firstAttribute="bottom" secondItem="ZKD-Be-eMp" secondAttribute="bottom" id="fae-mk-c1a"/>
|
||||
</constraints>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="321.60000000000002" y="248.27586206896552"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<systemColor name="darkTextColor">
|
||||
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
17
iphone/Maps/Classes/Components/MWMButton.h
Normal file
17
iphone/Maps/Classes/Components/MWMButton.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
typedef NS_ENUM(NSUInteger, MWMButtonColoring)
|
||||
{
|
||||
MWMButtonColoringOther,
|
||||
MWMButtonColoringBlue,
|
||||
MWMButtonColoringBlack,
|
||||
MWMButtonColoringWhite,
|
||||
MWMButtonColoringWhiteText,
|
||||
MWMButtonColoringGray,
|
||||
MWMButtonColoringRed
|
||||
};
|
||||
|
||||
@interface MWMButton : UIButton
|
||||
|
||||
@property (copy, nonatomic) NSString * imageName;
|
||||
@property (nonatomic) MWMButtonColoring coloring;
|
||||
|
||||
@end
|
||||
153
iphone/Maps/Classes/Components/MWMButton.m
Normal file
153
iphone/Maps/Classes/Components/MWMButton.m
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
#import "MWMButton.h"
|
||||
#import "SwiftBridge.h"
|
||||
static NSString * const kDefaultPattern = @"%@_%@";
|
||||
static NSString * const kHighlightedPattern = @"%@_highlighted_%@";
|
||||
static NSString * const kSelectedPattern = @"%@_selected_%@";
|
||||
|
||||
@implementation MWMButton
|
||||
|
||||
- (void)setImageName:(NSString *)imageName
|
||||
{
|
||||
_imageName = imageName;
|
||||
[self setDefaultImages];
|
||||
}
|
||||
|
||||
// This method is overridden by MWMButtonRenderer.swift
|
||||
//- (void)applyTheme
|
||||
//{
|
||||
// [self changeColoringToOpposite];
|
||||
// [super applyTheme];
|
||||
//}
|
||||
|
||||
- (void)setColoring:(MWMButtonColoring)coloring
|
||||
{
|
||||
_coloring = coloring;
|
||||
[self setEnabled:self.enabled];
|
||||
}
|
||||
|
||||
- (void)changeColoringToOpposite
|
||||
{
|
||||
if (self.coloring == MWMButtonColoringOther)
|
||||
{
|
||||
if (self.imageName)
|
||||
{
|
||||
[self setDefaultImages];
|
||||
self.imageView.image = [self imageForState:self.state];
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (self.state == UIControlStateNormal)
|
||||
[self setDefaultTintColor];
|
||||
else if (self.state == UIControlStateHighlighted)
|
||||
[self setHighlighted:YES];
|
||||
else if (self.state == UIControlStateSelected)
|
||||
[self setSelected:YES];
|
||||
}
|
||||
|
||||
- (void)setDefaultImages
|
||||
{
|
||||
NSString * postfix = [UIColor isNightMode] ? @"dark" : @"light";
|
||||
[self setImage:[UIImage imageNamed:[NSString stringWithFormat:kDefaultPattern, self.imageName, postfix]] forState:UIControlStateNormal];
|
||||
[self setImage:[UIImage imageNamed:[NSString stringWithFormat:kHighlightedPattern, self.imageName, postfix]] forState:UIControlStateHighlighted];
|
||||
[self setImage:[UIImage imageNamed:[NSString stringWithFormat:kSelectedPattern, self.imageName, postfix]] forState:UIControlStateSelected];
|
||||
}
|
||||
|
||||
- (void)setHighlighted:(BOOL)highlighted
|
||||
{
|
||||
[super setHighlighted:highlighted];
|
||||
if (highlighted)
|
||||
{
|
||||
switch (self.coloring)
|
||||
{
|
||||
case MWMButtonColoringBlue:
|
||||
self.tintColor = [UIColor linkBlueHighlighted];
|
||||
break;
|
||||
case MWMButtonColoringBlack:
|
||||
self.tintColor = [UIColor blackHintText];
|
||||
break;
|
||||
case MWMButtonColoringGray:
|
||||
self.tintColor = [UIColor blackDividers];
|
||||
break;
|
||||
case MWMButtonColoringWhiteText:
|
||||
self.tintColor = [UIColor whitePrimaryTextHighlighted];
|
||||
break;
|
||||
case MWMButtonColoringRed:
|
||||
self.tintColor = [UIColor buttonRed];
|
||||
break;
|
||||
case MWMButtonColoringWhite:
|
||||
case MWMButtonColoringOther:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self.selected)
|
||||
[self setSelected:YES];
|
||||
else
|
||||
[self setEnabled:self.enabled];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setSelected:(BOOL)selected
|
||||
{
|
||||
[super setSelected:selected];
|
||||
if (selected)
|
||||
{
|
||||
switch (self.coloring)
|
||||
{
|
||||
case MWMButtonColoringBlack:
|
||||
self.tintColor = [UIColor linkBlue];
|
||||
break;
|
||||
case MWMButtonColoringWhite:
|
||||
case MWMButtonColoringWhiteText:
|
||||
case MWMButtonColoringBlue:
|
||||
case MWMButtonColoringOther:
|
||||
case MWMButtonColoringGray:
|
||||
case MWMButtonColoringRed:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setEnabled:self.enabled];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setEnabled:(BOOL)enabled
|
||||
{
|
||||
[super setEnabled:enabled];
|
||||
if (!enabled)
|
||||
self.tintColor = [UIColor lightGrayColor];
|
||||
else
|
||||
[self setDefaultTintColor];
|
||||
}
|
||||
|
||||
- (void)setDefaultTintColor
|
||||
{
|
||||
switch (self.coloring)
|
||||
{
|
||||
case MWMButtonColoringBlack:
|
||||
self.tintColor = [UIColor blackSecondaryText];
|
||||
break;
|
||||
case MWMButtonColoringWhite:
|
||||
self.tintColor = [UIColor white];
|
||||
break;
|
||||
case MWMButtonColoringWhiteText:
|
||||
self.tintColor = [UIColor whitePrimaryText];
|
||||
break;
|
||||
case MWMButtonColoringBlue:
|
||||
self.tintColor = [UIColor linkBlue];
|
||||
break;
|
||||
case MWMButtonColoringGray:
|
||||
self.tintColor = [UIColor blackHintText];
|
||||
break;
|
||||
case MWMButtonColoringRed:
|
||||
self.tintColor = [UIColor red];
|
||||
break;
|
||||
case MWMButtonColoringOther:
|
||||
self.imageView.image = [self imageForState:UIControlStateNormal];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#import "MWMController.h"
|
||||
|
||||
@interface MWMCollectionViewController : UICollectionViewController<MWMController>
|
||||
|
||||
@end
|
||||
33
iphone/Maps/Classes/Components/MWMCollectionViewController.m
Normal file
33
iphone/Maps/Classes/Components/MWMCollectionViewController.m
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#import "MWMCollectionViewController.h"
|
||||
#import "MWMAlertViewController.h"
|
||||
#import "MapViewController.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
@interface MWMCollectionViewController ()
|
||||
|
||||
@property(nonatomic, readwrite) MWMAlertViewController * alertController;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMCollectionViewController
|
||||
|
||||
- (BOOL)prefersStatusBarHidden { return NO; }
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.collectionView.styleName = @"PressBackground";
|
||||
[self.navigationController.navigationBar setTranslucent:NO];
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (BOOL)hasNavigationBar { return YES; }
|
||||
- (MWMAlertViewController *)alertController
|
||||
{
|
||||
if (!_alertController)
|
||||
_alertController = [[MWMAlertViewController alloc] initWithViewController:self];
|
||||
return _alertController;
|
||||
}
|
||||
|
||||
@end
|
||||
9
iphone/Maps/Classes/Components/MWMController.h
Normal file
9
iphone/Maps/Classes/Components/MWMController.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
@class MWMAlertViewController;
|
||||
|
||||
@protocol MWMController <NSObject>
|
||||
|
||||
@property (nonatomic, readonly) BOOL hasNavigationBar;
|
||||
|
||||
@property (nonatomic, readonly) MWMAlertViewController * alertController;
|
||||
|
||||
@end
|
||||
5
iphone/Maps/Classes/Components/MWMMailViewController.h
Normal file
5
iphone/Maps/Classes/Components/MWMMailViewController.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#import <MessageUI/MessageUI.h>
|
||||
|
||||
@interface MWMMailViewController : MFMailComposeViewController
|
||||
|
||||
@end
|
||||
18
iphone/Maps/Classes/Components/MWMMailViewController.m
Normal file
18
iphone/Maps/Classes/Components/MWMMailViewController.m
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#import "MWMMailViewController.h"
|
||||
|
||||
@implementation MWMMailViewController
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.navigationBar.tintColor = UIColor.whiteColor;
|
||||
self.navigationBar.barTintColor = UIColor.primary;
|
||||
}
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
||||
{
|
||||
return UIStatusBarStyleLightContent;
|
||||
}
|
||||
|
||||
- (UIViewController *)childViewControllerForStatusBarStyle { return nil; }
|
||||
@end
|
||||
3
iphone/Maps/Classes/Components/MWMNavigationController.h
Normal file
3
iphone/Maps/Classes/Components/MWMNavigationController.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
@interface MWMNavigationController : UINavigationController
|
||||
|
||||
@end
|
||||
86
iphone/Maps/Classes/Components/MWMNavigationController.m
Normal file
86
iphone/Maps/Classes/Components/MWMNavigationController.m
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#import "MWMNavigationController.h"
|
||||
#import "MWMController.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
#import <SafariServices/SafariServices.h>
|
||||
|
||||
@interface MWMNavigationController () <UINavigationControllerDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMNavigationController
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
||||
{
|
||||
return UIStatusBarStyleLightContent;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.delegate = self;
|
||||
self.navigationItem.leftBarButtonItem.tintColor = [UIColor whitePrimaryText];
|
||||
self.navigationItem.rightBarButtonItem.tintColor = [UIColor whitePrimaryText];
|
||||
|
||||
[MWMThemeManager invalidate];
|
||||
}
|
||||
|
||||
- (void)navigationController:(UINavigationController *)navigationController
|
||||
willShowViewController:(UIViewController *)viewController
|
||||
animated:(BOOL)animated
|
||||
{
|
||||
if ([viewController isKindOfClass:[SFSafariViewController class]])
|
||||
{
|
||||
[navigationController setNavigationBarHidden:YES animated:animated];
|
||||
return;
|
||||
}
|
||||
|
||||
if ([viewController conformsToProtocol:@protocol(MWMController)]) {
|
||||
id<MWMController> vc = (id<MWMController>)viewController;
|
||||
[navigationController setNavigationBarHidden:!vc.hasNavigationBar animated:animated];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
|
||||
{
|
||||
UIViewController * topVC = self.viewControllers.lastObject;
|
||||
[self setupNavigationBackButtonItemFor:topVC];
|
||||
[super pushViewController:viewController animated:animated];
|
||||
}
|
||||
|
||||
- (void)setViewControllers:(NSArray<UIViewController *> *)viewControllers animated:(BOOL)animated {
|
||||
[viewControllers enumerateObjectsUsingBlock:^(UIViewController * vc, NSUInteger idx, BOOL * stop) {
|
||||
if (idx == viewControllers.count - 1)
|
||||
return;
|
||||
[self setupNavigationBackButtonItemFor:vc];
|
||||
}];
|
||||
[super setViewControllers:viewControllers animated:animated];
|
||||
}
|
||||
|
||||
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
|
||||
{
|
||||
[super traitCollectionDidChange: previousTraitCollection];
|
||||
// Update the app theme when the device appearance is changing.
|
||||
if ((self.traitCollection.verticalSizeClass != previousTraitCollection.verticalSizeClass)
|
||||
|| (self.traitCollection.horizontalSizeClass != previousTraitCollection.horizontalSizeClass) || (self.traitCollection.userInterfaceStyle != previousTraitCollection.userInterfaceStyle)) {
|
||||
[MWMThemeManager invalidate];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)shouldAutorotate
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)setupNavigationBackButtonItemFor:(UIViewController *)viewController {
|
||||
if (@available(iOS 14.0, *)) {
|
||||
viewController.navigationItem.backButtonDisplayMode = UINavigationItemBackButtonDisplayModeMinimal;
|
||||
} else {
|
||||
viewController.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@""
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:nil
|
||||
action:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
3
iphone/Maps/Classes/Components/MWMStartButton.h
Normal file
3
iphone/Maps/Classes/Components/MWMStartButton.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
@interface MWMStartButton : UIButton
|
||||
|
||||
@end
|
||||
5
iphone/Maps/Classes/Components/MWMStartButton.m
Normal file
5
iphone/Maps/Classes/Components/MWMStartButton.m
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#import "MWMStartButton.h"
|
||||
|
||||
@implementation MWMStartButton
|
||||
|
||||
@end
|
||||
3
iphone/Maps/Classes/Components/MWMStopButton.h
Normal file
3
iphone/Maps/Classes/Components/MWMStopButton.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
@interface MWMStopButton : UIButton
|
||||
|
||||
@end
|
||||
5
iphone/Maps/Classes/Components/MWMStopButton.m
Normal file
5
iphone/Maps/Classes/Components/MWMStopButton.m
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#import "MWMStopButton.h"
|
||||
|
||||
@implementation MWMStopButton
|
||||
|
||||
@end
|
||||
15
iphone/Maps/Classes/Components/MWMTableViewController.h
Normal file
15
iphone/Maps/Classes/Components/MWMTableViewController.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#import "MWMController.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MWMTableViewController : UITableViewController <MWMController>
|
||||
|
||||
@end
|
||||
|
||||
@interface UITableView (MWMTableViewController)
|
||||
|
||||
- (UITableViewCell *)dequeueDefaultCellForIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
86
iphone/Maps/Classes/Components/MWMTableViewController.m
Normal file
86
iphone/Maps/Classes/Components/MWMTableViewController.m
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#import "MapsAppDelegate.h"
|
||||
#import "MapViewController.h"
|
||||
#import "MWMAlertViewController.h"
|
||||
#import "MWMTableViewCell.h"
|
||||
#import "MWMTableViewController.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
static CGFloat const kMaxEstimatedTableViewCellHeight = 100.0;
|
||||
|
||||
@interface MWMTableViewController ()
|
||||
|
||||
@property (nonatomic, readwrite) MWMAlertViewController * alertController;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMTableViewController
|
||||
|
||||
- (BOOL)prefersStatusBarHidden
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
|
||||
{
|
||||
[self fixHeaderAndFooterFontsInDarkMode:view];
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section
|
||||
{
|
||||
[self fixHeaderAndFooterFontsInDarkMode:view];
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return UITableViewAutomaticDimension;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return kMaxEstimatedTableViewCellHeight;
|
||||
}
|
||||
|
||||
// Fix table section header font color for all tables, including Setting and Route Options.
|
||||
- (void)fixHeaderAndFooterFontsInDarkMode:(UIView*)headerView {
|
||||
if ([headerView isKindOfClass: [UITableViewHeaderFooterView class]]) {
|
||||
UITableViewHeaderFooterView* header = (UITableViewHeaderFooterView *)headerView;
|
||||
header.textLabel.textColor = [UIColor blackSecondaryText];
|
||||
if (self.tableView.style == UITableViewStyleGrouped) {
|
||||
header.detailTextLabel.textColor = [UIColor blackSecondaryText];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.tableView.insetsContentViewsToSafeArea = YES;
|
||||
self.tableView.styleName = @"TableView:PressBackground";
|
||||
[self.navigationController.navigationBar setTranslucent:NO];
|
||||
[self.tableView registerClass:[MWMTableViewCell class]
|
||||
forCellReuseIdentifier:[UITableViewCell className]];
|
||||
[self.tableView registerClass:[MWMTableViewSubtitleCell class]
|
||||
forCellReuseIdentifier:[MWMTableViewSubtitleCell className]];
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (BOOL)hasNavigationBar
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (MWMAlertViewController *)alertController
|
||||
{
|
||||
if (!_alertController)
|
||||
_alertController = [[MWMAlertViewController alloc] initWithViewController:self];
|
||||
return _alertController;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation UITableView (MWMTableViewController)
|
||||
|
||||
- (UITableViewCell *)dequeueDefaultCellForIndexPath:(NSIndexPath *)indexPath {
|
||||
return [self dequeueReusableCellWithIdentifier:UITableViewCell.className forIndexPath:indexPath];
|
||||
}
|
||||
|
||||
@end
|
||||
4
iphone/Maps/Classes/Components/MWMViewController.h
Normal file
4
iphone/Maps/Classes/Components/MWMViewController.h
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#import "MWMController.h"
|
||||
|
||||
@interface MWMViewController : UIViewController <MWMController>
|
||||
@end
|
||||
40
iphone/Maps/Classes/Components/MWMViewController.m
Normal file
40
iphone/Maps/Classes/Components/MWMViewController.m
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#import "MapsAppDelegate.h"
|
||||
#import "MapViewController.h"
|
||||
#import "MWMAlertViewController.h"
|
||||
#import "MWMViewController.h"
|
||||
|
||||
|
||||
@interface MWMViewController ()
|
||||
|
||||
@property (nonatomic, readwrite) MWMAlertViewController * alertController;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMViewController
|
||||
|
||||
- (BOOL)prefersStatusBarHidden
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
[self.navigationController.navigationBar setTranslucent:NO];
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (BOOL)hasNavigationBar
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (MWMAlertViewController *)alertController
|
||||
{
|
||||
if (!_alertController)
|
||||
_alertController = [[MWMAlertViewController alloc] initWithViewController:self];
|
||||
return _alertController;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
final class AlertPresentationController: DimmedModalPresentationController {
|
||||
override var frameOfPresentedViewInContainerView: CGRect {
|
||||
let f = super.frameOfPresentedViewInContainerView
|
||||
let s = presentedViewController.view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
|
||||
let r = CGRect(x: 0, y: 0, width: s.width, height: s.height)
|
||||
return r.offsetBy(dx: (f.width - r.width) / 2, dy: (f.height - r.height) / 2)
|
||||
}
|
||||
|
||||
override func presentationTransitionWillBegin() {
|
||||
super.presentationTransitionWillBegin()
|
||||
presentedViewController.view.layer.setCornerRadius(.modalSheet)
|
||||
presentedViewController.view.clipsToBounds = true
|
||||
guard let containerView = containerView, let presentedView = presentedView else { return }
|
||||
containerView.addSubview(presentedView)
|
||||
presentedView.center = containerView.center
|
||||
presentedView.frame = frameOfPresentedViewInContainerView
|
||||
presentedView.autoresizingMask = [.flexibleLeftMargin, .flexibleTopMargin, .flexibleRightMargin, .flexibleBottomMargin]
|
||||
}
|
||||
|
||||
override func dismissalTransitionDidEnd(_ completed: Bool) {
|
||||
super.presentationTransitionDidEnd(completed)
|
||||
guard let presentedView = presentedView else { return }
|
||||
if completed {
|
||||
presentedView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
final class CoverVerticalDismissalAnimator: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
func transitionDuration(using transitionContext: 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 originFrame = transitionContext.finalFrame(for: toVC)
|
||||
let finalFrame = originFrame.offsetBy(dx: 0, dy: originFrame.height)
|
||||
UIView.animate(withDuration: transitionDuration(using: transitionContext),
|
||||
animations: {
|
||||
fromVC.view.frame = finalFrame
|
||||
}) { finished in
|
||||
fromVC.view.removeFromSuperview()
|
||||
transitionContext.completeTransition(finished)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
final class CoverVerticalModalTransitioning: NSObject, UIViewControllerTransitioningDelegate {
|
||||
private var height: CGFloat
|
||||
init(presentationHeight: CGFloat) {
|
||||
height = presentationHeight
|
||||
}
|
||||
|
||||
func animationController(forPresented presented: UIViewController,
|
||||
presenting: UIViewController,
|
||||
source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return CoverVerticalPresentationAnimator()
|
||||
}
|
||||
|
||||
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return CoverVerticalDismissalAnimator()
|
||||
}
|
||||
|
||||
func presentationController(forPresented presented: UIViewController,
|
||||
presenting: UIViewController?,
|
||||
source: UIViewController) -> UIPresentationController? {
|
||||
return PresentationController(presentedViewController: presented, presenting: presenting, presentationHeight: height)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate final class PresentationController: DimmedModalPresentationController {
|
||||
private var height: CGFloat
|
||||
init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, presentationHeight: CGFloat) {
|
||||
height = presentationHeight
|
||||
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
|
||||
}
|
||||
|
||||
required init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, cancellable: Bool = true) {
|
||||
fatalError("init(presentedViewController:presenting:cancellable:) has not been implemented")
|
||||
}
|
||||
|
||||
override var frameOfPresentedViewInContainerView: CGRect {
|
||||
let f = super.frameOfPresentedViewInContainerView
|
||||
return CGRect(x: 0, y: f.height - height, width: f.width, height: height)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
final class CoverVerticalPresentationAnimator: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
return kDefaultAnimationDuration
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
guard let toVC = transitionContext.viewController(forKey: .to) else { return }
|
||||
|
||||
let containerView = transitionContext.containerView
|
||||
let finalFrame = transitionContext.finalFrame(for: toVC)
|
||||
let originFrame = finalFrame.offsetBy(dx: 0, dy: finalFrame.height)
|
||||
|
||||
containerView.addSubview(toVC.view)
|
||||
toVC.view.frame = originFrame
|
||||
toVC.view.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
|
||||
UIView.animate(withDuration: transitionDuration(using: transitionContext),
|
||||
animations: {
|
||||
toVC.view.frame = finalFrame
|
||||
}) { transitionContext.completeTransition($0) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
class DimmedModalPresentationController: UIPresentationController {
|
||||
private lazy var onTapGr: UITapGestureRecognizer = {
|
||||
return UITapGestureRecognizer(target: self, action: #selector(onTap))
|
||||
}()
|
||||
|
||||
private lazy var dimView: UIView = {
|
||||
let view = UIView()
|
||||
view.setStyle(.blackStatusBarBackground)
|
||||
if isCancellable {
|
||||
view.addGestureRecognizer(onTapGr)
|
||||
}
|
||||
return view
|
||||
}()
|
||||
|
||||
let isCancellable: Bool
|
||||
|
||||
required init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, cancellable: Bool = true) {
|
||||
isCancellable = cancellable
|
||||
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
|
||||
}
|
||||
|
||||
@objc private func onTap() {
|
||||
presentingViewController.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
override func presentationTransitionWillBegin() {
|
||||
guard let containerView = containerView else { return }
|
||||
containerView.addSubview(dimView)
|
||||
dimView.frame = containerView.bounds
|
||||
dimView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
dimView.alpha = 0
|
||||
presentingViewController.transitionCoordinator?.animate(alongsideTransition: { _ in
|
||||
self.dimView.alpha = 1
|
||||
})
|
||||
}
|
||||
|
||||
override func presentationTransitionDidEnd(_ completed: Bool) {
|
||||
if !completed { dimView.removeFromSuperview() }
|
||||
}
|
||||
|
||||
override func dismissalTransitionWillBegin() {
|
||||
presentingViewController.transitionCoordinator?.animate(alongsideTransition: { _ in
|
||||
self.dimView.alpha = 0
|
||||
})
|
||||
}
|
||||
|
||||
override func dismissalTransitionDidEnd(_ completed: Bool) {
|
||||
if completed { dimView.removeFromSuperview() }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
final class FadeInAnimatedTransitioning: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
return kDefaultAnimationDuration
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
guard let presentedView = transitionContext.view(forKey: .to) else { return }
|
||||
|
||||
presentedView.alpha = 0
|
||||
UIView.animate(withDuration: transitionDuration(using: transitionContext),
|
||||
animations: {
|
||||
presentedView.alpha = 1
|
||||
}) { transitionContext.completeTransition($0) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
final class FadeOutAnimatedTransitioning: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
return kDefaultAnimationDuration
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
guard let presentedView = transitionContext.view(forKey: .from) else { return }
|
||||
UIView.animate(withDuration: transitionDuration(using: transitionContext),
|
||||
animations: {
|
||||
presentedView.alpha = 0
|
||||
}) { finished in
|
||||
transitionContext.completeTransition(finished)
|
||||
}
|
||||
}
|
||||
}
|
||||
26
iphone/Maps/Classes/Components/Modal/FadeTransitioning.swift
Normal file
26
iphone/Maps/Classes/Components/Modal/FadeTransitioning.swift
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
class FadeTransitioning<T: DimmedModalPresentationController>: NSObject, UIViewControllerTransitioningDelegate {
|
||||
let presentedTransitioning = FadeInAnimatedTransitioning()
|
||||
let dismissedTransitioning = FadeOutAnimatedTransitioning()
|
||||
let isCancellable: Bool
|
||||
|
||||
init(cancellable: Bool = true) {
|
||||
isCancellable = cancellable
|
||||
super.init()
|
||||
}
|
||||
|
||||
func animationController(forPresented presented: UIViewController,
|
||||
presenting: UIViewController,
|
||||
source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return presentedTransitioning
|
||||
}
|
||||
|
||||
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return dismissedTransitioning
|
||||
}
|
||||
|
||||
func presentationController(forPresented presented: UIViewController,
|
||||
presenting: UIViewController?,
|
||||
source: UIViewController) -> UIPresentationController? {
|
||||
return T(presentedViewController: presented, presenting: presenting, cancellable: isCancellable)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
final class IPadModalPresentationController: DimmedModalPresentationController {
|
||||
override var frameOfPresentedViewInContainerView: CGRect {
|
||||
guard let containerView = containerView else { return CGRect.zero }
|
||||
let screenSize = UIScreen.main.bounds
|
||||
let contentSize = presentedViewController.preferredContentSize
|
||||
let r = alternative(iPhone: containerView.bounds,
|
||||
iPad: CGRect(x: screenSize.width/2 - contentSize.width/2,
|
||||
y: screenSize.height/2 - contentSize.height/2,
|
||||
width: contentSize.width,
|
||||
height: contentSize.height))
|
||||
return r
|
||||
}
|
||||
|
||||
override func containerViewWillLayoutSubviews() {
|
||||
presentedView?.frame = frameOfPresentedViewInContainerView
|
||||
}
|
||||
|
||||
override func presentationTransitionWillBegin() {
|
||||
super.presentationTransitionWillBegin()
|
||||
presentedViewController.view.layer.setCornerRadius(.buttonDefault)
|
||||
presentedViewController.view.clipsToBounds = true
|
||||
guard let containerView = containerView, let presentedView = presentedView else { return }
|
||||
containerView.addSubview(presentedView)
|
||||
presentedView.frame = frameOfPresentedViewInContainerView
|
||||
}
|
||||
|
||||
override func dismissalTransitionDidEnd(_ completed: Bool) {
|
||||
super.presentationTransitionDidEnd(completed)
|
||||
guard let presentedView = presentedView else { return }
|
||||
if completed {
|
||||
presentedView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
final class PromoBookingPresentationController: DimmedModalPresentationController {
|
||||
let sideMargin: CGFloat = 32.0
|
||||
let maxWidth: CGFloat = 310.0
|
||||
|
||||
override var frameOfPresentedViewInContainerView: CGRect {
|
||||
let f = super.frameOfPresentedViewInContainerView
|
||||
let estimatedWidth = min(maxWidth, f.width - (sideMargin * 2.0))
|
||||
let s = presentedViewController.view.systemLayoutSizeFitting(CGSize(width: estimatedWidth, height: f.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow)
|
||||
let r = CGRect(x: (f.width - s.width) / 2, y: (f.height - s.height) / 2, width: s.width, height: s.height)
|
||||
return r
|
||||
}
|
||||
|
||||
override func containerViewWillLayoutSubviews() {
|
||||
presentedView?.frame = frameOfPresentedViewInContainerView
|
||||
}
|
||||
|
||||
override func presentationTransitionWillBegin() {
|
||||
super.presentationTransitionWillBegin()
|
||||
presentedViewController.view.layer.setCornerRadius(.buttonDefault)
|
||||
presentedViewController.view.clipsToBounds = true
|
||||
guard let containerView = containerView, let presentedView = presentedView else { return }
|
||||
containerView.addSubview(presentedView)
|
||||
presentedView.frame = frameOfPresentedViewInContainerView
|
||||
}
|
||||
|
||||
override func dismissalTransitionDidEnd(_ completed: Bool) {
|
||||
super.presentationTransitionDidEnd(completed)
|
||||
guard let presentedView = presentedView else { return }
|
||||
if completed {
|
||||
presentedView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
316
iphone/Maps/Classes/Components/TabView/TabView.swift
Normal file
316
iphone/Maps/Classes/Components/TabView/TabView.swift
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
fileprivate class ContentCell: UICollectionViewCell {
|
||||
var view: UIView? {
|
||||
didSet {
|
||||
if let view = view, view != oldValue {
|
||||
oldValue?.removeFromSuperview()
|
||||
view.frame = contentView.bounds
|
||||
contentView.addSubview(view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
if let view = view {
|
||||
view.frame = contentView.bounds
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class HeaderCell: UICollectionViewCell {
|
||||
private let label = UILabel()
|
||||
private var selectedAttributes: [NSAttributedString.Key : Any] = [:]
|
||||
private var deselectedAttributes: [NSAttributedString.Key : Any] = [:]
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
contentView.addSubview(label)
|
||||
label.textAlignment = .center
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
contentView.addSubview(label)
|
||||
label.textAlignment = .center
|
||||
}
|
||||
|
||||
override var isSelected: Bool {
|
||||
didSet {
|
||||
label.attributedText = NSAttributedString(string: label.text ?? "",
|
||||
attributes: isSelected ? selectedAttributes : deselectedAttributes)
|
||||
}
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
label.attributedText = nil
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
label.frame = contentView.bounds
|
||||
}
|
||||
|
||||
func configureWith(selectedAttributes: [NSAttributedString.Key : Any],
|
||||
deselectedAttributes: [NSAttributedString.Key : Any],
|
||||
text: String) {
|
||||
self.selectedAttributes = selectedAttributes
|
||||
self.deselectedAttributes = deselectedAttributes
|
||||
label.attributedText = NSAttributedString(string: text.uppercased(),
|
||||
attributes: deselectedAttributes)
|
||||
}
|
||||
}
|
||||
|
||||
protocol TabViewDataSource: AnyObject {
|
||||
func numberOfPages(in tabView: TabView) -> Int
|
||||
func tabView(_ tabView: TabView, viewAt index: Int) -> UIView
|
||||
func tabView(_ tabView: TabView, titleAt index: Int) -> String?
|
||||
}
|
||||
|
||||
protocol TabViewDelegate: AnyObject {
|
||||
func tabView(_ tabView: TabView, didSelectTabAt index: Int)
|
||||
}
|
||||
|
||||
@objcMembers
|
||||
@objc(MWMTabView)
|
||||
class TabView: UIView {
|
||||
private enum CellId {
|
||||
static let content = "contentCell"
|
||||
static let header = "headerCell"
|
||||
}
|
||||
|
||||
private let tabsLayout = UICollectionViewFlowLayout()
|
||||
private let tabsContentLayout = UICollectionViewFlowLayout()
|
||||
private let tabsCollectionView: UICollectionView
|
||||
private let tabsContentCollectionView: UICollectionView
|
||||
private let headerView = UIView()
|
||||
private let slidingView = UIView()
|
||||
private var slidingViewLeft: NSLayoutConstraint!
|
||||
private var slidingViewWidth: NSLayoutConstraint!
|
||||
private lazy var pageCount = { return self.dataSource?.numberOfPages(in: self) ?? 0; }()
|
||||
var selectedIndex: Int?
|
||||
private var lastSelectedIndex: Int?
|
||||
|
||||
weak var dataSource: TabViewDataSource?
|
||||
weak var delegate: TabViewDelegate?
|
||||
|
||||
var barTintColor = UIColor.white {
|
||||
didSet {
|
||||
headerView.backgroundColor = barTintColor
|
||||
}
|
||||
}
|
||||
|
||||
var selectedHeaderTextAttributes: [NSAttributedString.Key : Any] = [
|
||||
.foregroundColor : UIColor.white,
|
||||
.font : UIFont.systemFont(ofSize: 14, weight: .semibold)
|
||||
] {
|
||||
didSet {
|
||||
tabsCollectionView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
var deselectedHeaderTextAttributes: [NSAttributedString.Key : Any] = [
|
||||
.foregroundColor : UIColor.gray,
|
||||
.font : UIFont.systemFont(ofSize: 14, weight: .semibold)
|
||||
] {
|
||||
didSet {
|
||||
tabsCollectionView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
var contentFrame: CGRect {
|
||||
safeAreaLayoutGuide.layoutFrame
|
||||
}
|
||||
|
||||
override var tintColor: UIColor! {
|
||||
didSet {
|
||||
slidingView.backgroundColor = tintColor
|
||||
}
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
tabsCollectionView = UICollectionView(frame: .zero, collectionViewLayout: tabsLayout)
|
||||
tabsContentCollectionView = UICollectionView(frame: .zero, collectionViewLayout: tabsContentLayout)
|
||||
super.init(frame: frame)
|
||||
configure()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
tabsCollectionView = UICollectionView(frame: .zero, collectionViewLayout: tabsLayout)
|
||||
tabsContentCollectionView = UICollectionView(frame: .zero, collectionViewLayout: tabsContentLayout)
|
||||
super.init(coder: aDecoder)
|
||||
configure()
|
||||
}
|
||||
|
||||
private func configure() {
|
||||
backgroundColor = .white
|
||||
|
||||
configureHeader()
|
||||
configureContent()
|
||||
|
||||
addSubview(tabsContentCollectionView)
|
||||
addSubview(headerView)
|
||||
|
||||
configureLayoutContraints()
|
||||
}
|
||||
|
||||
private func configureHeader() {
|
||||
tabsLayout.scrollDirection = .horizontal
|
||||
tabsLayout.minimumLineSpacing = 0
|
||||
tabsLayout.minimumInteritemSpacing = 0
|
||||
|
||||
tabsCollectionView.register(HeaderCell.self, forCellWithReuseIdentifier: CellId.header)
|
||||
tabsCollectionView.dataSource = self
|
||||
tabsCollectionView.delegate = self
|
||||
tabsCollectionView.backgroundColor = .clear
|
||||
|
||||
slidingView.backgroundColor = tintColor
|
||||
|
||||
headerView.backgroundColor = barTintColor
|
||||
headerView.addSubview(tabsCollectionView)
|
||||
headerView.addSubview(slidingView)
|
||||
headerView.addSeparator(.bottom)
|
||||
}
|
||||
|
||||
private func configureContent() {
|
||||
tabsContentLayout.scrollDirection = .horizontal
|
||||
tabsContentLayout.minimumLineSpacing = 0
|
||||
tabsContentLayout.minimumInteritemSpacing = 0
|
||||
|
||||
tabsContentCollectionView.register(ContentCell.self, forCellWithReuseIdentifier: CellId.content)
|
||||
tabsContentCollectionView.dataSource = self
|
||||
tabsContentCollectionView.delegate = self
|
||||
tabsContentCollectionView.isPagingEnabled = true
|
||||
tabsContentCollectionView.bounces = false
|
||||
tabsContentCollectionView.showsVerticalScrollIndicator = false
|
||||
tabsContentCollectionView.showsHorizontalScrollIndicator = false
|
||||
tabsContentCollectionView.backgroundColor = .clear
|
||||
}
|
||||
|
||||
private func configureLayoutContraints() {
|
||||
tabsCollectionView.translatesAutoresizingMaskIntoConstraints = false;
|
||||
tabsContentCollectionView.translatesAutoresizingMaskIntoConstraints = false
|
||||
headerView.translatesAutoresizingMaskIntoConstraints = false
|
||||
slidingView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
headerView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
|
||||
headerView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
|
||||
headerView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor).isActive = true
|
||||
headerView.heightAnchor.constraint(equalToConstant: 46).isActive = true
|
||||
|
||||
tabsContentCollectionView.leftAnchor.constraint(equalTo: safeAreaLayoutGuide.leftAnchor).isActive = true
|
||||
tabsContentCollectionView.rightAnchor.constraint(equalTo: safeAreaLayoutGuide.rightAnchor).isActive = true
|
||||
tabsContentCollectionView.topAnchor.constraint(equalTo: headerView.bottomAnchor).isActive = true
|
||||
tabsContentCollectionView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor).isActive = true
|
||||
|
||||
tabsCollectionView.leftAnchor.constraint(equalTo: headerView.leftAnchor).isActive = true
|
||||
tabsCollectionView.rightAnchor.constraint(equalTo: headerView.rightAnchor).isActive = true
|
||||
tabsCollectionView.topAnchor.constraint(equalTo: headerView.topAnchor).isActive = true
|
||||
tabsCollectionView.bottomAnchor.constraint(equalTo: slidingView.topAnchor).isActive = true
|
||||
|
||||
slidingView.heightAnchor.constraint(equalToConstant: 3).isActive = true
|
||||
slidingView.bottomAnchor.constraint(equalTo: headerView.bottomAnchor).isActive = true
|
||||
|
||||
slidingViewLeft = slidingView.leftAnchor.constraint(equalTo: safeAreaLayoutGuide.leftAnchor)
|
||||
slidingViewLeft.isActive = true
|
||||
slidingViewWidth = slidingView.widthAnchor.constraint(equalToConstant: 0)
|
||||
slidingViewWidth.isActive = true
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
tabsLayout.invalidateLayout()
|
||||
tabsContentLayout.invalidateLayout()
|
||||
super.layoutSubviews()
|
||||
assert(pageCount > 0)
|
||||
slidingViewWidth.constant = pageCount > 0 ? contentFrame.width / CGFloat(pageCount) : 0
|
||||
slidingViewLeft.constant = pageCount > 0 ? contentFrame.width / CGFloat(pageCount) * CGFloat(selectedIndex ?? 0) : 0
|
||||
tabsCollectionView.layoutIfNeeded()
|
||||
tabsContentCollectionView.layoutIfNeeded()
|
||||
if let selectedIndex = selectedIndex {
|
||||
tabsContentCollectionView.scrollToItem(at: IndexPath(item: selectedIndex, section: 0),
|
||||
at: .left,
|
||||
animated: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension TabView : UICollectionViewDataSource {
|
||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return pageCount
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
var cell = UICollectionViewCell()
|
||||
if collectionView == tabsContentCollectionView {
|
||||
cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellId.content, for: indexPath)
|
||||
if let contentCell = cell as? ContentCell {
|
||||
contentCell.view = dataSource?.tabView(self, viewAt: indexPath.item)
|
||||
}
|
||||
}
|
||||
|
||||
if collectionView == tabsCollectionView {
|
||||
cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellId.header, for: indexPath)
|
||||
if let headerCell = cell as? HeaderCell {
|
||||
let title = dataSource?.tabView(self, titleAt: indexPath.item) ?? ""
|
||||
headerCell.configureWith(selectedAttributes: selectedHeaderTextAttributes,
|
||||
deselectedAttributes: deselectedHeaderTextAttributes,
|
||||
text: title)
|
||||
if indexPath.item == selectedIndex {
|
||||
collectionView.selectItem(at: indexPath, animated: false, scrollPosition: [])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
extension TabView : UICollectionViewDelegateFlowLayout {
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if scrollView.contentSize.width > 0 {
|
||||
let scrollOffset = scrollView.contentOffset.x / scrollView.contentSize.width
|
||||
slidingViewLeft.constant = scrollOffset * contentFrame.width
|
||||
}
|
||||
}
|
||||
|
||||
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||
lastSelectedIndex = selectedIndex
|
||||
}
|
||||
|
||||
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||
selectedIndex = Int(round(scrollView.contentOffset.x / scrollView.bounds.width))
|
||||
if let selectedIndex = selectedIndex, selectedIndex != lastSelectedIndex {
|
||||
delegate?.tabView(self, didSelectTabAt: selectedIndex)
|
||||
}
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
if (collectionView == tabsCollectionView) {
|
||||
let isSelected = selectedIndex == indexPath.item
|
||||
if !isSelected {
|
||||
selectedIndex = indexPath.item
|
||||
tabsContentCollectionView.scrollToItem(at: indexPath, at: .left, animated: true)
|
||||
delegate?.tabView(self, didSelectTabAt: selectedIndex!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
|
||||
if (collectionView == tabsCollectionView) {
|
||||
collectionView.deselectItem(at: indexPath, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView,
|
||||
layout collectionViewLayout: UICollectionViewLayout,
|
||||
sizeForItemAt indexPath: IndexPath) -> CGSize {
|
||||
let bounds = collectionView.bounds.inset(by: collectionView.adjustedContentInset)
|
||||
|
||||
if collectionView == tabsContentCollectionView {
|
||||
return bounds.size
|
||||
} else {
|
||||
return CGSize(width: bounds.width / CGFloat(pageCount),
|
||||
height: bounds.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
class TabViewController: MWMViewController {
|
||||
var viewControllers: [UIViewController] = [] {
|
||||
didSet {
|
||||
viewControllers.forEach {
|
||||
self.addChild($0)
|
||||
$0.didMove(toParent: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tabView: TabView {
|
||||
get {
|
||||
return view as! TabView
|
||||
}
|
||||
}
|
||||
|
||||
override func loadView() {
|
||||
let v = TabView()
|
||||
v.dataSource = self
|
||||
view = v
|
||||
}
|
||||
}
|
||||
|
||||
extension TabViewController: TabViewDataSource {
|
||||
func numberOfPages(in tabView: TabView) -> Int {
|
||||
return viewControllers.count
|
||||
}
|
||||
|
||||
func tabView(_ tabView: TabView, viewAt index: Int) -> UIView {
|
||||
return viewControllers[index].view
|
||||
}
|
||||
|
||||
func tabView(_ tabView: TabView, titleAt index: Int) -> String? {
|
||||
return viewControllers[index].title
|
||||
}
|
||||
}
|
||||
88
iphone/Maps/Classes/Components/VerticallyAlignedButton.swift
Normal file
88
iphone/Maps/Classes/Components/VerticallyAlignedButton.swift
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
@IBDesignable
|
||||
class VerticallyAlignedButton: UIControl {
|
||||
@IBInspectable
|
||||
var image: UIImage? {
|
||||
didSet {
|
||||
imageView.image = image
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var title: String? {
|
||||
didSet {
|
||||
if localizedText == nil {
|
||||
titleLabel.text = title
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var localizedText: String? {
|
||||
didSet {
|
||||
if let localizedText = localizedText {
|
||||
titleLabel.text = L(localizedText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var spacing: CGFloat = 4 {
|
||||
didSet {
|
||||
spacingConstraint.constant = spacing
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var numberOfLines: Int {
|
||||
get {
|
||||
return titleLabel.numberOfLines
|
||||
}
|
||||
set {
|
||||
titleLabel.numberOfLines = newValue
|
||||
}
|
||||
}
|
||||
|
||||
private lazy var spacingConstraint: NSLayoutConstraint = {
|
||||
let spacingConstraint = titleLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: spacing)
|
||||
return spacingConstraint
|
||||
}()
|
||||
|
||||
lazy var titleLabel: UILabel = {
|
||||
let titleLabel = UILabel()
|
||||
titleLabel.textAlignment = .center
|
||||
titleLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
return titleLabel
|
||||
}()
|
||||
|
||||
lazy var imageView: UIImageView = {
|
||||
let imageView = UIImageView()
|
||||
imageView.contentMode = .scaleAspectFit
|
||||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
return imageView
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setupView()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
setupView()
|
||||
}
|
||||
|
||||
private func setupView() {
|
||||
addSubview(titleLabel)
|
||||
addSubview(imageView)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
imageView.topAnchor.constraint(equalTo: topAnchor),
|
||||
imageView.centerXAnchor.constraint(equalTo: centerXAnchor),
|
||||
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor),
|
||||
spacingConstraint,
|
||||
titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
titleLabel.topAnchor.constraint(equalTo: bottomAnchor)
|
||||
])
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue