Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
168
iphone/Maps/UI/Downloader/AvailableMapsDataSource.swift
Normal file
168
iphone/Maps/UI/Downloader/AvailableMapsDataSource.swift
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
class AvailableMapsDataSource {
|
||||
struct Const {
|
||||
static let locationArrow = "➤"
|
||||
}
|
||||
|
||||
private let parentCountryId: String?
|
||||
|
||||
private var sections: [String]?
|
||||
private var sectionsContent: [String: [String]]?
|
||||
private var nearbySection: [String]?
|
||||
|
||||
fileprivate var searching = false
|
||||
fileprivate lazy var searchDataSource: IDownloaderDataSource = {
|
||||
SearchMapsDataSource()
|
||||
}()
|
||||
|
||||
init(_ parentCountryId: String? = nil, location: CLLocationCoordinate2D? = nil) {
|
||||
self.parentCountryId = parentCountryId
|
||||
let countryIds: [String]
|
||||
if let parentCountryId = parentCountryId {
|
||||
countryIds = Storage.shared().allCountries(withParent: parentCountryId)
|
||||
} else {
|
||||
countryIds = Storage.shared().allCountries()
|
||||
}
|
||||
configSections(countryIds, location: location)
|
||||
}
|
||||
|
||||
private func configSections(_ countryIds: [String], location: CLLocationCoordinate2D?) {
|
||||
let countries = countryIds.map {
|
||||
CountryIdAndName(countryId: $0, name: Storage.shared().name(forCountry: $0))
|
||||
}.sorted {
|
||||
$0.countryName.compare($1.countryName) == .orderedAscending
|
||||
}
|
||||
|
||||
sections = []
|
||||
sectionsContent = [:]
|
||||
|
||||
if let location = location, let nearbySection = Storage.shared().nearbyAvailableCountries(location) {
|
||||
sections?.append(Const.locationArrow)
|
||||
sectionsContent![Const.locationArrow] = nearbySection
|
||||
}
|
||||
|
||||
for country in countries {
|
||||
let section = parentCountryId == nil ? String(country.countryName.prefix(1)) : L("downloader_available_maps")
|
||||
if sections!.last != section {
|
||||
sections!.append(section)
|
||||
sectionsContent![section] = []
|
||||
}
|
||||
|
||||
var sectionCountries = sectionsContent![section]
|
||||
sectionCountries?.append(country.countryId)
|
||||
sectionsContent![section] = sectionCountries
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AvailableMapsDataSource: IDownloaderDataSource {
|
||||
var isEmpty: Bool {
|
||||
searching ? searchDataSource.isEmpty : false
|
||||
}
|
||||
|
||||
var title: String {
|
||||
guard let parentCountryId = parentCountryId else {
|
||||
return L("download_maps")
|
||||
}
|
||||
return Storage.shared().name(forCountry: parentCountryId)
|
||||
}
|
||||
|
||||
var isRoot: Bool {
|
||||
parentCountryId == nil
|
||||
}
|
||||
|
||||
var isSearching: Bool {
|
||||
searching
|
||||
}
|
||||
|
||||
func getParentCountryId() -> String {
|
||||
if parentCountryId != nil {
|
||||
return parentCountryId!
|
||||
}
|
||||
return Storage.shared().getRootId()
|
||||
}
|
||||
|
||||
func parentAttributes() -> MapNodeAttributes {
|
||||
guard let parentId = parentCountryId else {
|
||||
return Storage.shared().attributesForRoot()
|
||||
}
|
||||
return Storage.shared().attributes(forCountry: parentId)
|
||||
}
|
||||
|
||||
func numberOfSections() -> Int {
|
||||
searching ? searchDataSource.numberOfSections() : (sections?.count ?? 0)
|
||||
}
|
||||
|
||||
func numberOfItems(in section: Int) -> Int {
|
||||
if searching {
|
||||
return searchDataSource.numberOfItems(in: section)
|
||||
}
|
||||
let index = sections![section]
|
||||
return sectionsContent![index]!.count
|
||||
}
|
||||
|
||||
func item(at indexPath: IndexPath) -> MapNodeAttributes {
|
||||
if searching {
|
||||
return searchDataSource.item(at: indexPath)
|
||||
}
|
||||
let sectionIndex = sections![indexPath.section]
|
||||
let sectionItems = sectionsContent![sectionIndex]
|
||||
let countryId = sectionItems![indexPath.item]
|
||||
return Storage.shared().attributes(forCountry: countryId)
|
||||
}
|
||||
|
||||
func matchedName(at indexPath: IndexPath) -> String? {
|
||||
searching ? searchDataSource.matchedName(at: indexPath) : nil
|
||||
}
|
||||
|
||||
func title(for section: Int) -> String {
|
||||
if searching {
|
||||
return searchDataSource.title(for: section)
|
||||
}
|
||||
let title = sections![section]
|
||||
if title == Const.locationArrow {
|
||||
return L("downloader_near_me_subtitle")
|
||||
}
|
||||
return title
|
||||
}
|
||||
|
||||
func indexTitles() -> [String]? {
|
||||
if searching {
|
||||
return nil
|
||||
}
|
||||
if parentCountryId != nil {
|
||||
return nil
|
||||
}
|
||||
return sections
|
||||
}
|
||||
|
||||
func dataSourceFor(_ childId: String) -> IDownloaderDataSource {
|
||||
searching ? searchDataSource.dataSourceFor(childId) : AvailableMapsDataSource(childId)
|
||||
}
|
||||
|
||||
func reload(_ completion: () -> Void) {
|
||||
if searching {
|
||||
searchDataSource.reload(completion)
|
||||
}
|
||||
// do nothing.
|
||||
completion()
|
||||
}
|
||||
|
||||
func search(_ query: String, locale: String, update: @escaping (Bool) -> Void) {
|
||||
if query.isEmpty {
|
||||
cancelSearch()
|
||||
update(true)
|
||||
return
|
||||
}
|
||||
searchDataSource.search(query, locale: locale) { [weak self] (finished) in
|
||||
if finished {
|
||||
self?.searching = true
|
||||
update(finished)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancelSearch() {
|
||||
searching = false
|
||||
searchDataSource.cancelSearch()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#import "MWMTableViewCell.h"
|
||||
|
||||
@interface MWMMapDownloaderButtonTableViewCell : MWMTableViewCell
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#import "MWMMapDownloaderButtonTableViewCell.h"
|
||||
|
||||
@implementation MWMMapDownloaderButtonTableViewCell
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
[self config];
|
||||
}
|
||||
|
||||
- (void)prepareForReuse
|
||||
{
|
||||
[super prepareForReuse];
|
||||
[self config];
|
||||
}
|
||||
|
||||
- (void)config
|
||||
{
|
||||
if ([self respondsToSelector:@selector(setSeparatorInset:)])
|
||||
[self setSeparatorInset:UIEdgeInsetsZero];
|
||||
if ([self respondsToSelector:@selector(setLayoutMargins:)])
|
||||
[self setLayoutMargins:UIEdgeInsetsZero];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="0.0" shouldIndentWhileEditing="NO" reuseIdentifier="MWMMapDownloaderButtonTableViewCell" id="KGk-i7-Jjw" customClass="MWMMapDownloaderButtonTableViewCell" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Add Maps Button" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="dOW-mN-UY7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<color key="textColor" systemColor="linkColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:linkBlueText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="download_maps"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<inset key="separatorInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<point key="canvasLocation" x="139" y="155"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
<resources>
|
||||
<systemColor name="linkColor">
|
||||
<color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@interface MWMMapDownloaderCellHeader : UILabel
|
||||
|
||||
@end
|
||||
23
iphone/Maps/UI/Downloader/Cells/MWMMapDownloaderCellHeader.m
Normal file
23
iphone/Maps/UI/Downloader/Cells/MWMMapDownloaderCellHeader.m
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#import "MWMMapDownloaderCellHeader.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
@implementation MWMMapDownloaderCellHeader
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
[self setStyleNameAndApply:@"regular12:blackSecondaryText"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)drawTextInRect:(CGRect)rect
|
||||
{
|
||||
rect = UIEdgeInsetsInsetRect(rect, UIEdgeInsetsMake(0, 16, 0, 0));
|
||||
rect = UIEdgeInsetsInsetRect(rect, self.safeAreaInsets);
|
||||
[super drawTextInRect:rect];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#import "MWMMapDownloaderTableViewCell.h"
|
||||
|
||||
@interface MWMMapDownloaderLargeCountryTableViewCell : MWMMapDownloaderTableViewCell
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#import "MWMMapDownloaderLargeCountryTableViewCell.h"
|
||||
#import "MWMCircularProgress.h"
|
||||
|
||||
#import <CoreApi/MWMMapNodeAttributes.h>
|
||||
|
||||
@interface MWMMapDownloaderLargeCountryTableViewCell ()
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UILabel * mapsCount;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMMapDownloaderLargeCountryTableViewCell
|
||||
|
||||
#pragma mark - Config
|
||||
|
||||
- (void)config:(MWMMapNodeAttributes *)nodeAttrs searchQuery:(NSString *)searchQuery {
|
||||
[super config:nodeAttrs searchQuery:searchQuery];
|
||||
BOOL haveLocalMaps = (nodeAttrs.downloadedMwmCount != 0);
|
||||
NSString *ofMaps = haveLocalMaps ? [NSString stringWithFormat:L(@"downloader_of"), nodeAttrs.downloadedMwmCount, nodeAttrs.totalMwmCount] : @(nodeAttrs.totalMwmCount).stringValue;
|
||||
self.mapsCount.text = [NSString stringWithFormat:@"%@: %@", L(@"downloader_status_maps"), ofMaps];
|
||||
}
|
||||
|
||||
- (void)configProgress:(MWMMapNodeAttributes *)nodeAttrs {
|
||||
[super configProgress:nodeAttrs];
|
||||
if (nodeAttrs.nodeStatus == MWMMapNodeStatusPartly || nodeAttrs.nodeStatus == MWMMapNodeStatusNotDownloaded) {
|
||||
MWMCircularProgressStateVec affectedStates = @[@(MWMCircularProgressStateNormal), @(MWMCircularProgressStateSelected)];
|
||||
[self.progress setImageName:@"ic_folder" forStates:affectedStates];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="MWMMapDownloaderLargeCountryTableViewCell" rowHeight="62" id="4CW-jw-1JP" customClass="MWMMapDownloaderLargeCountryTableViewCell" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="62"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="4CW-jw-1JP" id="rFT-0r-5zy">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="62"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3NQ-tT-U9b">
|
||||
<rect key="frame" x="12" y="13" width="36" height="36"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="36" id="3vs-uR-5WA"/>
|
||||
<constraint firstAttribute="width" constant="36" id="WtB-IW-uuH"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="People's Republic of China" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cum-4j-JlQ">
|
||||
<rect key="frame" x="60" y="12" width="167" height="20"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.87" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="14 maps" lineBreakMode="wordWrap" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="851-fC-gEN">
|
||||
<rect key="frame" x="60" y="36" width="224" height="14"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.54000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular12:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="760" text="405 MB" textAlignment="right" lineBreakMode="wordWrap" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9Ni-mb-fA5">
|
||||
<rect key="frame" x="235" y="22.5" width="49" height="17"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_arrow_gray_right" translatesAutoresizingMaskIntoConstraints="NO" id="7HN-EK-QXO" userLabel="Arrow">
|
||||
<rect key="frame" x="284" y="17" width="28" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="28" id="OyB-rL-7rb"/>
|
||||
<constraint firstAttribute="height" constant="28" id="dNb-uE-TzF"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMGray"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="cum-4j-JlQ" firstAttribute="leading" secondItem="rFT-0r-5zy" secondAttribute="leading" constant="60" id="6W7-tW-rcf"/>
|
||||
<constraint firstAttribute="trailing" secondItem="9Ni-mb-fA5" secondAttribute="trailing" constant="36" id="C7I-fY-Gfw"/>
|
||||
<constraint firstItem="851-fC-gEN" firstAttribute="trailing" secondItem="9Ni-mb-fA5" secondAttribute="trailing" id="DJE-zo-NaQ"/>
|
||||
<constraint firstAttribute="trailing" secondItem="7HN-EK-QXO" secondAttribute="trailing" constant="8" id="EZJ-3g-VZ0"/>
|
||||
<constraint firstItem="3NQ-tT-U9b" firstAttribute="leading" secondItem="rFT-0r-5zy" secondAttribute="leading" constant="12" id="Km2-Bp-fFb"/>
|
||||
<constraint firstItem="851-fC-gEN" firstAttribute="leading" secondItem="cum-4j-JlQ" secondAttribute="leading" id="NHY-P5-axf"/>
|
||||
<constraint firstItem="cum-4j-JlQ" firstAttribute="top" secondItem="rFT-0r-5zy" secondAttribute="top" constant="12" id="SEx-Ax-stg"/>
|
||||
<constraint firstAttribute="bottom" secondItem="851-fC-gEN" secondAttribute="bottom" constant="12" id="ZwR-py-AZC"/>
|
||||
<constraint firstItem="851-fC-gEN" firstAttribute="top" secondItem="cum-4j-JlQ" secondAttribute="bottom" constant="4" id="aAE-b0-nwb"/>
|
||||
<constraint firstItem="9Ni-mb-fA5" firstAttribute="leading" secondItem="cum-4j-JlQ" secondAttribute="trailing" constant="8" id="hV0-8P-jwr"/>
|
||||
<constraint firstItem="9Ni-mb-fA5" firstAttribute="centerY" secondItem="rFT-0r-5zy" secondAttribute="centerY" id="tB8-k0-qYw"/>
|
||||
<constraint firstItem="7HN-EK-QXO" firstAttribute="centerY" secondItem="rFT-0r-5zy" secondAttribute="centerY" id="tjc-5f-EBn"/>
|
||||
<constraint firstItem="3NQ-tT-U9b" firstAttribute="centerY" secondItem="rFT-0r-5zy" secondAttribute="centerY" id="zd3-Ex-VJY"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<inset key="separatorInset" minX="60" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="downloadSize" destination="9Ni-mb-fA5" id="btQ-8t-pIV"/>
|
||||
<outlet property="mapsCount" destination="851-fC-gEN" id="dY0-zX-KS2"/>
|
||||
<outlet property="stateWrapper" destination="3NQ-tT-U9b" id="PJw-9U-8b3"/>
|
||||
<outlet property="title" destination="cum-4j-JlQ" id="f9c-KC-2dR"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="408.69565217391306" y="269.86607142857139"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="ic_arrow_gray_right" width="28" height="28"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#import "MWMMapDownloaderTableViewCell.h"
|
||||
|
||||
@interface MWMMapDownloaderPlaceTableViewCell : MWMMapDownloaderTableViewCell
|
||||
|
||||
@property (nonatomic) BOOL needDisplayArea;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
#import "MWMMapDownloaderPlaceTableViewCell.h"
|
||||
|
||||
#import <CoreApi/MWMMapNodeAttributes.h>
|
||||
|
||||
@interface MWMMapDownloaderPlaceTableViewCell ()
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UILabel *descriptionLabel;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *titleBottomOffset;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMMapDownloaderPlaceTableViewCell
|
||||
|
||||
#pragma mark - Config
|
||||
|
||||
- (void)config:(MWMMapNodeAttributes *)nodeAttrs searchQuery:(NSString *)searchQuery {
|
||||
[super config:nodeAttrs searchQuery:searchQuery];
|
||||
BOOL isDescriptionVisible = NO;
|
||||
NSDictionary *selectedAreaAttrs = @{NSFontAttributeName : [UIFont bold12]};
|
||||
NSDictionary *unselectedAreaAttrs = @{NSFontAttributeName : [UIFont regular12]};
|
||||
self.needDisplayArea = !nodeAttrs.hasParent;
|
||||
if (self.needDisplayArea && nodeAttrs.topmostParentInfo.count == 1) {
|
||||
isDescriptionVisible = nodeAttrs.hasParent;
|
||||
if (isDescriptionVisible) {
|
||||
self.descriptionLabel.attributedText = [self matchedString:nodeAttrs.topmostParentInfo[0].countryName
|
||||
selectedAttrs:selectedAreaAttrs
|
||||
unselectedAttrs:unselectedAreaAttrs];
|
||||
}
|
||||
}
|
||||
else if (nodeAttrs.nodeDescription.length > 0)
|
||||
{
|
||||
isDescriptionVisible = YES;
|
||||
self.descriptionLabel.attributedText = [self matchedString:nodeAttrs.nodeDescription
|
||||
selectedAttrs:selectedAreaAttrs
|
||||
unselectedAttrs:unselectedAreaAttrs];
|
||||
}
|
||||
self.descriptionLabel.hidden = !isDescriptionVisible;
|
||||
self.titleBottomOffset.priority =
|
||||
isDescriptionVisible ? UILayoutPriorityDefaultLow : UILayoutPriorityDefaultHigh;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="MWMMapDownloaderPlaceTableViewCell" rowHeight="62" id="1KI-85-wsU" customClass="MWMMapDownloaderPlaceTableViewCell" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="62"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="1KI-85-wsU" id="mRF-11-OKU">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="62"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Yb5-r1-Z2X">
|
||||
<rect key="frame" x="12" y="13" width="36" height="36"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="36" id="OjM-Wz-G8P"/>
|
||||
<constraint firstAttribute="width" constant="36" id="vYF-tP-Wtw"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="London" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Db-Yq-FlD">
|
||||
<rect key="frame" x="60" y="12" width="195" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" priority="750" constant="20" id="fnm-gO-0Fg"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.87" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="UK" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fev-4l-MY3">
|
||||
<rect key="frame" x="60" y="36" width="199" height="14"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.54000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular12:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="760" text="45 MB" textAlignment="right" lineBreakMode="wordWrap" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rqh-iy-Sx9">
|
||||
<rect key="frame" x="263" y="23" width="41" height="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="3Db-Yq-FlD" firstAttribute="leading" secondItem="mRF-11-OKU" secondAttribute="leading" constant="60" id="2CW-BR-iT1"/>
|
||||
<constraint firstItem="Yb5-r1-Z2X" firstAttribute="leading" secondItem="mRF-11-OKU" secondAttribute="leading" constant="12" id="GGp-H1-htH"/>
|
||||
<constraint firstItem="3Db-Yq-FlD" firstAttribute="top" secondItem="mRF-11-OKU" secondAttribute="top" constant="12" id="Jhy-gy-RcP"/>
|
||||
<constraint firstItem="rqh-iy-Sx9" firstAttribute="leading" secondItem="3Db-Yq-FlD" secondAttribute="trailing" constant="8" id="NhL-qc-Mcu"/>
|
||||
<constraint firstAttribute="trailing" secondItem="rqh-iy-Sx9" secondAttribute="trailing" constant="16" id="dil-Rw-64e"/>
|
||||
<constraint firstItem="fev-4l-MY3" firstAttribute="top" secondItem="3Db-Yq-FlD" secondAttribute="bottom" priority="500" constant="4" id="ePz-Xy-IoM"/>
|
||||
<constraint firstAttribute="bottom" secondItem="3Db-Yq-FlD" secondAttribute="bottom" priority="250" constant="12" id="ffv-VG-1xY"/>
|
||||
<constraint firstItem="Yb5-r1-Z2X" firstAttribute="centerY" secondItem="mRF-11-OKU" secondAttribute="centerY" id="g72-Dp-9Ky"/>
|
||||
<constraint firstItem="fev-4l-MY3" firstAttribute="leading" secondItem="3Db-Yq-FlD" secondAttribute="leading" id="kP7-cA-GeN"/>
|
||||
<constraint firstAttribute="bottom" secondItem="fev-4l-MY3" secondAttribute="bottom" priority="500" constant="12" id="mJE-YM-1uE"/>
|
||||
<constraint firstItem="rqh-iy-Sx9" firstAttribute="centerY" secondItem="mRF-11-OKU" secondAttribute="centerY" id="qZa-yl-a6W"/>
|
||||
<constraint firstItem="rqh-iy-Sx9" firstAttribute="leading" secondItem="fev-4l-MY3" secondAttribute="trailing" constant="4" id="rqB-DG-k9N"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<inset key="separatorInset" minX="60" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="descriptionLabel" destination="fev-4l-MY3" id="lyN-5S-iWE"/>
|
||||
<outlet property="downloadSize" destination="rqh-iy-Sx9" id="g9R-G7-dxQ"/>
|
||||
<outlet property="stateWrapper" destination="Yb5-r1-Z2X" id="iPW-N5-qJi"/>
|
||||
<outlet property="title" destination="3Db-Yq-FlD" id="qtt-YF-a0V"/>
|
||||
<outlet property="titleBottomOffset" destination="ffv-VG-1xY" id="0kL-3a-tKy"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="408.69565217391306" y="269.86607142857139"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#import "MWMMapDownloaderPlaceTableViewCell.h"
|
||||
|
||||
@interface MWMMapDownloaderSubplaceTableViewCell : MWMMapDownloaderPlaceTableViewCell
|
||||
|
||||
- (void)setSubplaceText:(NSString *)text;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#import "MWMMapDownloaderSubplaceTableViewCell.h"
|
||||
|
||||
@interface MWMMapDownloaderSubplaceTableViewCell ()
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UILabel *subPlace;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMMapDownloaderSubplaceTableViewCell
|
||||
|
||||
- (void)setSubplaceText:(NSString *)text {
|
||||
self.subPlace.attributedText = [self matchedString:text
|
||||
selectedAttrs:@{NSFontAttributeName : [UIFont bold14]}
|
||||
unselectedAttrs:@{NSFontAttributeName : [UIFont regular14]}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="MWMMapDownloaderSubplaceTableViewCell" rowHeight="82" id="1KI-85-wsU" customClass="MWMMapDownloaderSubplaceTableViewCell" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="82"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="1KI-85-wsU" id="mRF-11-OKU">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="82"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Yb5-r1-Z2X">
|
||||
<rect key="frame" x="12" y="23" width="36" height="36"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="36" id="OjM-Wz-G8P"/>
|
||||
<constraint firstAttribute="width" constant="36" id="vYF-tP-Wtw"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="760" text="Mossel Bay" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="x7m-Zm-8y6" propertyAccessControl="none">
|
||||
<rect key="frame" x="60" y="12" width="199" height="16.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.54000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="760" text="London" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Db-Yq-FlD">
|
||||
<rect key="frame" x="60" y="32.5" width="195" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" priority="750" constant="20" id="CiU-uC-JVj"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.87" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="UK" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fev-4l-MY3">
|
||||
<rect key="frame" x="60" y="56.5" width="199" height="14"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.54000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular12:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="760" text="45 MB" textAlignment="right" lineBreakMode="wordWrap" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rqh-iy-Sx9">
|
||||
<rect key="frame" x="263" y="33" width="41" height="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="3Db-Yq-FlD" firstAttribute="leading" secondItem="mRF-11-OKU" secondAttribute="leading" constant="60" id="4Sj-Yh-d4y"/>
|
||||
<constraint firstItem="3Db-Yq-FlD" firstAttribute="top" secondItem="x7m-Zm-8y6" secondAttribute="bottom" constant="4" id="AFW-TD-ADw"/>
|
||||
<constraint firstItem="x7m-Zm-8y6" firstAttribute="top" secondItem="mRF-11-OKU" secondAttribute="top" constant="12" id="E4o-jw-VEZ"/>
|
||||
<constraint firstItem="Yb5-r1-Z2X" firstAttribute="leading" secondItem="mRF-11-OKU" secondAttribute="leading" constant="12" id="GGp-H1-htH"/>
|
||||
<constraint firstItem="rqh-iy-Sx9" firstAttribute="leading" secondItem="x7m-Zm-8y6" secondAttribute="trailing" constant="4" id="GsC-fa-5kk"/>
|
||||
<constraint firstItem="rqh-iy-Sx9" firstAttribute="leading" secondItem="3Db-Yq-FlD" secondAttribute="trailing" constant="8" id="I7Q-yY-JMS"/>
|
||||
<constraint firstAttribute="bottom" secondItem="fev-4l-MY3" secondAttribute="bottom" priority="500" constant="12" id="Rnj-5e-Hfy"/>
|
||||
<constraint firstItem="fev-4l-MY3" firstAttribute="leading" secondItem="3Db-Yq-FlD" secondAttribute="leading" id="Yk6-jH-hKS"/>
|
||||
<constraint firstAttribute="bottom" secondItem="3Db-Yq-FlD" secondAttribute="bottom" priority="250" constant="12" id="Zic-It-hOL"/>
|
||||
<constraint firstAttribute="trailing" secondItem="rqh-iy-Sx9" secondAttribute="trailing" constant="16" id="dil-Rw-64e"/>
|
||||
<constraint firstItem="Yb5-r1-Z2X" firstAttribute="centerY" secondItem="mRF-11-OKU" secondAttribute="centerY" id="g72-Dp-9Ky"/>
|
||||
<constraint firstItem="rqh-iy-Sx9" firstAttribute="leading" secondItem="fev-4l-MY3" secondAttribute="trailing" constant="4" id="oqd-UU-h0T"/>
|
||||
<constraint firstItem="fev-4l-MY3" firstAttribute="top" secondItem="3Db-Yq-FlD" secondAttribute="bottom" constant="4" id="pwP-cL-stJ"/>
|
||||
<constraint firstItem="x7m-Zm-8y6" firstAttribute="leading" secondItem="3Db-Yq-FlD" secondAttribute="leading" id="pxg-tv-fNn"/>
|
||||
<constraint firstItem="rqh-iy-Sx9" firstAttribute="centerY" secondItem="mRF-11-OKU" secondAttribute="centerY" id="qZa-yl-a6W"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<inset key="separatorInset" minX="60" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="descriptionLabel" destination="fev-4l-MY3" id="1C2-OF-Vhu"/>
|
||||
<outlet property="downloadSize" destination="rqh-iy-Sx9" id="q4T-Pv-DrI"/>
|
||||
<outlet property="stateWrapper" destination="Yb5-r1-Z2X" id="eb8-Ut-gh5"/>
|
||||
<outlet property="subPlace" destination="x7m-Zm-8y6" id="63W-Bc-FdX"/>
|
||||
<outlet property="title" destination="3Db-Yq-FlD" id="JdN-yn-xLw"/>
|
||||
<outlet property="titleBottomOffset" destination="Zic-It-hOL" id="1gW-IS-6Fc"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="408.69565217391306" y="269.86607142857139"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
#import "MWMMapDownloaderMode.h"
|
||||
#import "MWMTableViewCell.h"
|
||||
|
||||
@class MWMCircularProgress;
|
||||
@class MWMMapNodeAttributes;
|
||||
@class MWMMapDownloaderTableViewCell;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol MWMMapDownloaderTableViewCellDelegate <NSObject>
|
||||
|
||||
- (void)mapDownloaderCellDidPressProgress:(MWMMapDownloaderTableViewCell *)cell;
|
||||
- (void)mapDownloaderCellDidLongPress:(MWMMapDownloaderTableViewCell *)cell;
|
||||
|
||||
@end
|
||||
|
||||
@interface MWMMapDownloaderTableViewCell : MWMTableViewCell
|
||||
|
||||
@property(nonatomic) MWMCircularProgress *progress;
|
||||
@property(weak, nonatomic) id<MWMMapDownloaderTableViewCellDelegate> delegate;
|
||||
@property(nonatomic) MWMMapDownloaderMode mode;
|
||||
@property(readonly, nonatomic) MWMMapNodeAttributes *nodeAttrs;
|
||||
|
||||
- (void)config:(MWMMapNodeAttributes *)nodeAttrs searchQuery:(nullable NSString *)searchQuery;
|
||||
- (void)configProgress:(MWMMapNodeAttributes *)nodeAttrs;
|
||||
- (void)setDownloadProgress:(CGFloat)progress;
|
||||
- (NSAttributedString *)matchedString:(NSString *)str
|
||||
selectedAttrs:(NSDictionary *)selectedAttrs
|
||||
unselectedAttrs:(NSDictionary *)unselectedAttrs;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
149
iphone/Maps/UI/Downloader/Cells/MWMMapDownloaderTableViewCell.m
Normal file
149
iphone/Maps/UI/Downloader/Cells/MWMMapDownloaderTableViewCell.m
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
#import "MWMMapDownloaderTableViewCell.h"
|
||||
#import "MWMCircularProgress.h"
|
||||
#import "NSString+Categories.h"
|
||||
|
||||
#import <CoreApi/MWMCommon.h>
|
||||
#import <CoreApi/MWMFrameworkHelper.h>
|
||||
#import <CoreApi/MWMMapNodeAttributes.h>
|
||||
|
||||
@interface MWMMapDownloaderTableViewCell () <MWMCircularProgressProtocol>
|
||||
|
||||
@property(copy, nonatomic) NSString *searchQuery;
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UIView *stateWrapper;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *title;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *downloadSize;
|
||||
|
||||
@property(strong, nonatomic) MWMMapNodeAttributes *nodeAttrs;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMMapDownloaderTableViewCell
|
||||
|
||||
- (void)awakeFromNib {
|
||||
[super awakeFromNib];
|
||||
UILongPressGestureRecognizer *lpGR = [[UILongPressGestureRecognizer alloc] initWithTarget:self
|
||||
action:@selector(onLongPress:)];
|
||||
[self addGestureRecognizer:lpGR];
|
||||
}
|
||||
|
||||
- (void)prepareForReuse {
|
||||
[super prepareForReuse];
|
||||
self.nodeAttrs = nil;
|
||||
}
|
||||
|
||||
- (void)onLongPress:(UILongPressGestureRecognizer *)sender {
|
||||
if (sender.state != UIGestureRecognizerStateBegan) {
|
||||
return;
|
||||
}
|
||||
[self.delegate mapDownloaderCellDidLongPress:self];
|
||||
}
|
||||
|
||||
#pragma mark - Search matching
|
||||
|
||||
- (NSAttributedString *)matchedString:(NSString *)str
|
||||
selectedAttrs:(NSDictionary *)selectedAttrs
|
||||
unselectedAttrs:(NSDictionary *)unselectedAttrs {
|
||||
NSMutableAttributedString *attrTitle = [[NSMutableAttributedString alloc] initWithString:str];
|
||||
[attrTitle addAttributes:unselectedAttrs range:NSMakeRange(0, str.length)];
|
||||
if (!self.searchQuery)
|
||||
return [attrTitle copy];
|
||||
for (NSValue *range in [str rangesOfString:self.searchQuery])
|
||||
[attrTitle addAttributes:selectedAttrs range:range.rangeValue];
|
||||
return [attrTitle copy];
|
||||
}
|
||||
|
||||
#pragma mark - Config
|
||||
|
||||
- (void)config:(MWMMapNodeAttributes *)nodeAttrs searchQuery:(NSString *)searchQuery {
|
||||
self.searchQuery = searchQuery;
|
||||
self.nodeAttrs = nodeAttrs;
|
||||
[self configProgress:nodeAttrs];
|
||||
|
||||
self.title.attributedText = [self matchedString:nodeAttrs.nodeName
|
||||
selectedAttrs:@{NSFontAttributeName: [UIFont bold17]}
|
||||
unselectedAttrs:@{NSFontAttributeName: [UIFont regular17]}];
|
||||
|
||||
uint64_t size = 0;
|
||||
BOOL isModeDownloaded = self.mode == MWMMapDownloaderModeDownloaded;
|
||||
|
||||
switch (nodeAttrs.nodeStatus) {
|
||||
case MWMMapNodeStatusUndefined:
|
||||
case MWMMapNodeStatusError:
|
||||
case MWMMapNodeStatusOnDiskOutOfDate:
|
||||
case MWMMapNodeStatusNotDownloaded:
|
||||
case MWMMapNodeStatusApplying:
|
||||
case MWMMapNodeStatusInQueue:
|
||||
case MWMMapNodeStatusPartly:
|
||||
size = isModeDownloaded ? nodeAttrs.downloadedSize : nodeAttrs.totalSize;
|
||||
break;
|
||||
case MWMMapNodeStatusDownloading:
|
||||
size = isModeDownloaded ? nodeAttrs.totalUpdateSizeBytes : nodeAttrs.totalSize - nodeAttrs.downloadingSize;
|
||||
break;
|
||||
case MWMMapNodeStatusOnDisk:
|
||||
size = isModeDownloaded ? nodeAttrs.totalSize : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
self.downloadSize.text = formattedSize(size);
|
||||
self.downloadSize.hidden = (size == 0);
|
||||
}
|
||||
|
||||
- (void)configProgress:(MWMMapNodeAttributes *)nodeAttrs {
|
||||
MWMCircularProgress *progress = self.progress;
|
||||
BOOL isModeDownloaded = self.mode == MWMMapDownloaderModeDownloaded;
|
||||
MWMButtonColoring coloring = isModeDownloaded ? MWMButtonColoringBlack : MWMButtonColoringBlue;
|
||||
switch (nodeAttrs.nodeStatus) {
|
||||
case MWMMapNodeStatusNotDownloaded:
|
||||
case MWMMapNodeStatusPartly: {
|
||||
MWMCircularProgressStateVec affectedStates = @[@(MWMCircularProgressStateNormal), @(MWMCircularProgressStateSelected)];
|
||||
[progress setImageName:@"ic_download" forStates:affectedStates];
|
||||
[progress setColoring:coloring forStates:affectedStates];
|
||||
progress.state = MWMCircularProgressStateNormal;
|
||||
break;
|
||||
}
|
||||
case MWMMapNodeStatusDownloading:
|
||||
progress.progress = kMaxProgress * nodeAttrs.downloadedSize / (isModeDownloaded ? nodeAttrs.totalUpdateSizeBytes : nodeAttrs.totalSize - nodeAttrs.downloadingSize);
|
||||
break;
|
||||
case MWMMapNodeStatusApplying:
|
||||
case MWMMapNodeStatusInQueue:
|
||||
progress.state = MWMCircularProgressStateSpinner;
|
||||
break;
|
||||
case MWMMapNodeStatusUndefined:
|
||||
case MWMMapNodeStatusError:
|
||||
progress.state = MWMCircularProgressStateFailed;
|
||||
break;
|
||||
case MWMMapNodeStatusOnDisk:
|
||||
progress.state = MWMCircularProgressStateCompleted;
|
||||
break;
|
||||
case MWMMapNodeStatusOnDiskOutOfDate: {
|
||||
MWMCircularProgressStateVec affectedStates = @[@(MWMCircularProgressStateNormal), @(MWMCircularProgressStateSelected)];
|
||||
[progress setImageName:@"ic_update" forStates:affectedStates];
|
||||
[progress setColoring:MWMButtonColoringOther forStates:affectedStates];
|
||||
progress.state = MWMCircularProgressStateNormal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setDownloadProgress:(CGFloat)progress {
|
||||
self.progress.progress = kMaxProgress * progress;
|
||||
}
|
||||
|
||||
#pragma mark - MWMCircularProgressProtocol
|
||||
|
||||
- (void)progressButtonPressed:(nonnull MWMCircularProgress *)progress {
|
||||
[self.delegate mapDownloaderCellDidPressProgress:self];
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (MWMCircularProgress *)progress {
|
||||
if (!_progress) {
|
||||
_progress = [MWMCircularProgress downloaderProgressForParentView:self.stateWrapper];
|
||||
_progress.delegate = self;
|
||||
}
|
||||
return _progress;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" horizontalCompressionResistancePriority="760" selectionStyle="default" indentationWidth="10" reuseIdentifier="MWMMapDownloaderTableViewCell" rowHeight="52" id="Igh-sI-4cU" customClass="MWMMapDownloaderTableViewCell" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="52"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Igh-sI-4cU" id="hYr-eg-wbg">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="52"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rTN-aw-5k7">
|
||||
<rect key="frame" x="12" y="8" width="36" height="36"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="36" id="H0T-d1-25M"/>
|
||||
<constraint firstAttribute="height" constant="36" id="aA3-Ok-RSD"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="300" verticalHuggingPriority="251" horizontalCompressionResistancePriority="800" text="30 MB" textAlignment="right" lineBreakMode="wordWrap" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lgA-X0-501">
|
||||
<rect key="frame" x="262.5" y="18" width="41.5" height="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="260" verticalHuggingPriority="249" horizontalCompressionResistancePriority="752" verticalCompressionResistancePriority="752" text="Algeria" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5kc-K7-7K8">
|
||||
<rect key="frame" x="60" y="16" width="194.5" height="20"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.87" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="lgA-X0-501" firstAttribute="centerY" secondItem="hYr-eg-wbg" secondAttribute="centerY" id="5QG-IM-adn"/>
|
||||
<constraint firstItem="5kc-K7-7K8" firstAttribute="leading" secondItem="hYr-eg-wbg" secondAttribute="leading" constant="60" id="6Dv-Pw-tTy"/>
|
||||
<constraint firstAttribute="trailing" secondItem="lgA-X0-501" secondAttribute="trailing" constant="16" id="Af9-0B-Y6q"/>
|
||||
<constraint firstItem="rTN-aw-5k7" firstAttribute="leading" secondItem="hYr-eg-wbg" secondAttribute="leading" constant="12" id="Jfc-5q-LZI"/>
|
||||
<constraint firstAttribute="bottom" secondItem="5kc-K7-7K8" secondAttribute="bottom" constant="16" id="ZLY-Tp-hnL"/>
|
||||
<constraint firstItem="lgA-X0-501" firstAttribute="leading" secondItem="5kc-K7-7K8" secondAttribute="trailing" constant="8" id="Zwe-tB-xNb"/>
|
||||
<constraint firstItem="5kc-K7-7K8" firstAttribute="top" secondItem="hYr-eg-wbg" secondAttribute="top" constant="16" id="anV-3S-XAR"/>
|
||||
<constraint firstItem="rTN-aw-5k7" firstAttribute="centerY" secondItem="hYr-eg-wbg" secondAttribute="centerY" id="tND-fx-OhO"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<inset key="separatorInset" minX="60" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="downloadSize" destination="lgA-X0-501" id="Dl4-r1-7lU"/>
|
||||
<outlet property="stateWrapper" destination="rTN-aw-5k7" id="3jJ-Zp-S0C"/>
|
||||
<outlet property="title" destination="5kc-K7-7K8" id="SDA-Yd-DD2"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="413.04347826086962" y="271.875"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
134
iphone/Maps/UI/Downloader/DownloadAllView/DownloadAllView.swift
Normal file
134
iphone/Maps/UI/Downloader/DownloadAllView/DownloadAllView.swift
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
import UIKit
|
||||
|
||||
protocol DownloadAllViewDelegate: AnyObject {
|
||||
func onDownloadButtonPressed()
|
||||
func onRetryButtonPressed()
|
||||
func onCancelButtonPressed()
|
||||
func onStateChanged(state: DownloadAllView.State)
|
||||
}
|
||||
|
||||
class DownloadAllView: UIView {
|
||||
enum State {
|
||||
case none
|
||||
case ready
|
||||
case error
|
||||
case dowloading
|
||||
}
|
||||
enum Style {
|
||||
case download
|
||||
case update
|
||||
}
|
||||
|
||||
@IBOutlet private var iconImageView: UIImageView!
|
||||
@IBOutlet private var title: UILabel!
|
||||
@IBOutlet private var downloadSizeLabel: UILabel!
|
||||
@IBOutlet private var stateWrapper: UIView!
|
||||
@IBOutlet private var downloadButton: UIButton!
|
||||
@IBOutlet private var titleCenterConstraint: NSLayoutConstraint!
|
||||
lazy private var progress: MWMCircularProgress = {
|
||||
let view = MWMCircularProgress.downloaderProgress(forParentView: stateWrapper)
|
||||
view.delegate = self
|
||||
return view
|
||||
}()
|
||||
|
||||
var isSizeHidden: Bool = false {
|
||||
didSet {
|
||||
if oldValue != isSizeHidden {
|
||||
updateView()
|
||||
}
|
||||
}
|
||||
}
|
||||
var style: Style = .download {
|
||||
didSet {
|
||||
if oldValue != style {
|
||||
updateView()
|
||||
}
|
||||
}
|
||||
}
|
||||
var state: State = .ready {
|
||||
didSet {
|
||||
if oldValue != state {
|
||||
updateView()
|
||||
delegate?.onStateChanged(state: state)
|
||||
}
|
||||
}
|
||||
}
|
||||
var downloadSize: UInt64 = 0 {
|
||||
didSet {
|
||||
downloadSizeLabel.text = formattedSize(downloadSize)
|
||||
}
|
||||
}
|
||||
var downloadProgress: CGFloat = 0 {
|
||||
didSet {
|
||||
self.progress.progress = downloadProgress
|
||||
}
|
||||
}
|
||||
weak var delegate: DownloadAllViewDelegate?
|
||||
|
||||
@IBAction func onDownloadButtonPress(_ sender: Any) {
|
||||
if state == .error {
|
||||
delegate?.onRetryButtonPressed()
|
||||
} else {
|
||||
delegate?.onDownloadButtonPressed()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateView() {
|
||||
let readyTitle: String
|
||||
let downloadingTitle: String
|
||||
let readyButtonTitle: String
|
||||
let errorTitle = L("country_status_download_failed")
|
||||
let errorButtonTitle = L("downloader_retry")
|
||||
|
||||
switch style {
|
||||
case .download:
|
||||
iconImageView.image = UIImage(named: "ic_download_all")
|
||||
readyTitle = L("downloader_download_all_button")
|
||||
downloadingTitle = L("downloader_loading_ios")
|
||||
readyButtonTitle = L("download_button")
|
||||
case .update:
|
||||
iconImageView.image = UIImage(named: "ic_update_all")
|
||||
readyTitle = L("downloader_update_maps")
|
||||
downloadingTitle = L("downloader_updating_ios")
|
||||
readyButtonTitle = L("downloader_update_all_button")
|
||||
}
|
||||
|
||||
titleCenterConstraint.priority = isSizeHidden ? .defaultHigh : .defaultLow
|
||||
downloadSizeLabel.isHidden = isSizeHidden
|
||||
|
||||
switch state {
|
||||
case .error:
|
||||
iconImageView.image = UIImage(named: "ic_download_error")
|
||||
title.text = errorTitle
|
||||
title.setFontStyleAndApply(.red)
|
||||
downloadButton.setTitle(errorButtonTitle, for: .normal)
|
||||
downloadButton.isHidden = false
|
||||
stateWrapper.isHidden = true
|
||||
progress.state = .spinner
|
||||
downloadSizeLabel.isHidden = false
|
||||
case .ready:
|
||||
title.text = readyTitle
|
||||
title.setFontStyleAndApply(.blackPrimary)
|
||||
downloadButton.setTitle(readyButtonTitle, for: .normal)
|
||||
downloadButton.isHidden = false
|
||||
stateWrapper.isHidden = true
|
||||
progress.state = .spinner
|
||||
downloadSizeLabel.isHidden = false
|
||||
case .dowloading:
|
||||
title.text = downloadingTitle
|
||||
title.setFontStyleAndApply(.blackPrimary)
|
||||
downloadButton.isHidden = true
|
||||
stateWrapper.isHidden = false
|
||||
progress.state = .spinner
|
||||
case .none:
|
||||
self.downloadButton.isHidden = true
|
||||
self.stateWrapper.isHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension DownloadAllView: MWMCircularProgressProtocol {
|
||||
func progressButtonPressed(_ progress: MWMCircularProgress) {
|
||||
delegate?.onCancelButtonPressed()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="iN0-l3-epB" customClass="DownloadAllView" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="64"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="cKg-JT-LQ3">
|
||||
<rect key="frame" x="16" y="20" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="24" id="IoI-GO-nG3"/>
|
||||
<constraint firstAttribute="height" constant="24" id="TMm-8V-jMo"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4vQ-dY-JAM">
|
||||
<rect key="frame" x="58" y="10" width="32" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" priority="750" constant="20" id="38P-x2-HCd"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.87" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="760" text="..." textAlignment="right" lineBreakMode="wordWrap" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mVA-td-yVW">
|
||||
<rect key="frame" x="58" y="38" width="12" height="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.66666666669999997" green="0.66666666669999997" blue="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NBt-lB-IvV">
|
||||
<rect key="frame" x="352" y="17" width="46" height="30"/>
|
||||
<state key="normal" title="Button"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="onDownloadButtonPress:" destination="iN0-l3-epB" eventType="touchUpInside" id="6kx-3L-wc2"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wlz-tD-b2M">
|
||||
<rect key="frame" x="354" y="14" width="36" height="36"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="36" id="C2G-Y6-VKF"/>
|
||||
<constraint firstAttribute="width" constant="36" id="Uo3-uS-WpU"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="mVA-td-yVW" firstAttribute="leading" secondItem="cKg-JT-LQ3" secondAttribute="trailing" constant="18" id="1J4-kU-lyG"/>
|
||||
<constraint firstItem="cKg-JT-LQ3" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="16" id="2AL-TB-z3U"/>
|
||||
<constraint firstItem="wlz-tD-b2M" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="8aq-IR-Ffz"/>
|
||||
<constraint firstItem="4vQ-dY-JAM" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" priority="500" constant="10" id="AOx-bc-GRy"/>
|
||||
<constraint firstItem="cKg-JT-LQ3" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="Ehd-uD-6HS"/>
|
||||
<constraint firstItem="4vQ-dY-JAM" firstAttribute="leading" secondItem="cKg-JT-LQ3" secondAttribute="trailing" constant="18" id="IH6-Jq-o9T"/>
|
||||
<constraint firstItem="NBt-lB-IvV" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="KUE-7v-eNY"/>
|
||||
<constraint firstAttribute="trailing" secondItem="NBt-lB-IvV" secondAttribute="trailing" constant="16" id="PgQ-HZ-Ui5"/>
|
||||
<constraint firstItem="4vQ-dY-JAM" firstAttribute="centerY" secondItem="cKg-JT-LQ3" secondAttribute="centerY" priority="250" id="gWz-3d-LHv"/>
|
||||
<constraint firstAttribute="bottom" secondItem="mVA-td-yVW" secondAttribute="bottom" constant="10" id="gj3-BP-k3L"/>
|
||||
<constraint firstAttribute="trailing" secondItem="wlz-tD-b2M" secondAttribute="trailing" constant="24" id="s3R-CP-1zA"/>
|
||||
</constraints>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="PressBackground"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="downloadButton" destination="NBt-lB-IvV" id="RDS-Sj-rmo"/>
|
||||
<outlet property="downloadSizeLabel" destination="mVA-td-yVW" id="G7H-Vl-OQQ"/>
|
||||
<outlet property="iconImageView" destination="cKg-JT-LQ3" id="lvM-rM-apH"/>
|
||||
<outlet property="stateWrapper" destination="wlz-tD-b2M" id="7XI-dP-yUS"/>
|
||||
<outlet property="title" destination="4vQ-dY-JAM" id="bZv-x2-pe5"/>
|
||||
<outlet property="titleCenterConstraint" destination="gWz-3d-LHv" id="6b1-Ah-2ns"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="137.68115942028987" y="-162.72321428571428"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
507
iphone/Maps/UI/Downloader/DownloadMapsViewController.swift
Normal file
507
iphone/Maps/UI/Downloader/DownloadMapsViewController.swift
Normal file
|
|
@ -0,0 +1,507 @@
|
|||
import UIKit
|
||||
|
||||
@objc(MWMDownloadMapsViewController)
|
||||
class DownloadMapsViewController: MWMViewController {
|
||||
// MARK: - Types
|
||||
|
||||
private enum NodeAction {
|
||||
case showOnMap
|
||||
case download
|
||||
case update
|
||||
case cancelDownload
|
||||
case retryDownload
|
||||
case delete
|
||||
}
|
||||
|
||||
private enum AllMapsButtonState {
|
||||
case none
|
||||
case download(String)
|
||||
case cancel(String)
|
||||
}
|
||||
|
||||
// MARK: - Outlets
|
||||
|
||||
@IBOutlet var tableView: UITableView!
|
||||
@IBOutlet var noMapsContainer: UIView!
|
||||
@IBOutlet var downloadAllViewContainer: UIView!
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
private var searchController = UISearchController(searchResultsController: nil)
|
||||
var dataSource: IDownloaderDataSource!
|
||||
@objc var mode: MWMMapDownloaderMode = .downloaded
|
||||
private var skipCountryEvent = false
|
||||
private var hasAddMapSection: Bool { dataSource.isRoot && mode == .downloaded }
|
||||
private let allMapsViewBottomOffsetConstant: CGFloat = 64
|
||||
|
||||
lazy var noSerchResultViewController: SearchNoResultsViewController = {
|
||||
let vc = storyboard!.instantiateViewController(ofType: SearchNoResultsViewController.self)
|
||||
view.insertSubview(vc.view, aboveSubview: tableView)
|
||||
vc.view.alignToSuperview()
|
||||
vc.view.isHidden = true
|
||||
addChild(vc)
|
||||
vc.didMove(toParent: self)
|
||||
return vc
|
||||
}()
|
||||
|
||||
lazy var downloadAllView: DownloadAllView = {
|
||||
let view = Bundle.main.load(viewClass: DownloadAllView.self)?.first as! DownloadAllView
|
||||
view.delegate = self
|
||||
downloadAllViewContainer.addSubview(view)
|
||||
downloadAllViewContainer.addSeparator()
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
view.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
|
||||
view.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
|
||||
view.topAnchor.constraint(equalTo: downloadAllViewContainer.topAnchor),
|
||||
view.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
|
||||
])
|
||||
return view
|
||||
}()
|
||||
|
||||
// MARK: - Methods
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
if dataSource == nil {
|
||||
switch mode {
|
||||
case .downloaded:
|
||||
dataSource = DownloadedMapsDataSource()
|
||||
case .available:
|
||||
dataSource = AvailableMapsDataSource(location: LocationManager.lastLocation()?.coordinate)
|
||||
@unknown default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
tableView.registerNib(cell: MWMMapDownloaderTableViewCell.self)
|
||||
tableView.registerNib(cell: MWMMapDownloaderPlaceTableViewCell.self)
|
||||
tableView.registerNib(cell: MWMMapDownloaderLargeCountryTableViewCell.self)
|
||||
tableView.registerNib(cell: MWMMapDownloaderSubplaceTableViewCell.self)
|
||||
tableView.registerNib(cell: MWMMapDownloaderButtonTableViewCell.self)
|
||||
title = dataSource.title
|
||||
if mode == .downloaded {
|
||||
let addMapsButton = button(with: UIImage(systemName: "plus"), action: #selector(onAddMaps))
|
||||
navigationItem.rightBarButtonItem = addMapsButton
|
||||
}
|
||||
noMapsContainer.isHidden = !dataSource.isEmpty || Storage.shared().downloadInProgress()
|
||||
extendedLayoutIncludesOpaqueBars = true
|
||||
searchController.searchBar.placeholder = L("downloader_search_field_hint")
|
||||
searchController.searchBar.delegate = self
|
||||
searchController.obscuresBackgroundDuringPresentation = false
|
||||
searchController.hidesNavigationBarDuringPresentation = alternativeSizeClass(iPhone: true, iPad: false)
|
||||
searchController.searchBar.applyTheme()
|
||||
navigationItem.searchController = searchController
|
||||
navigationItem.hidesSearchBarWhenScrolling = false
|
||||
|
||||
configButtons()
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
dataSource.reload {
|
||||
reloadData()
|
||||
noMapsContainer.isHidden = !dataSource.isEmpty || Storage.shared().downloadInProgress()
|
||||
}
|
||||
Storage.shared().add(self)
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
Storage.shared().remove(self)
|
||||
}
|
||||
|
||||
fileprivate func showChildren(_ nodeAttrs: MapNodeAttributes) {
|
||||
let vc = storyboard!.instantiateViewController(ofType: DownloadMapsViewController.self)
|
||||
vc.mode = dataSource.isSearching ? .available : mode
|
||||
vc.dataSource = dataSource.dataSourceFor(nodeAttrs.countryId)
|
||||
navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
|
||||
fileprivate func showActions(_ nodeAttrs: MapNodeAttributes, in cell: UITableViewCell) {
|
||||
let menuTitle = nodeAttrs.nodeName
|
||||
let multiparent = nodeAttrs.parentInfo.count > 1
|
||||
let message = dataSource.isRoot || multiparent ? nil : nodeAttrs.parentInfo.first?.countryName
|
||||
let actionSheet = UIAlertController(title: menuTitle, message: message, preferredStyle: .actionSheet)
|
||||
actionSheet.popoverPresentationController?.sourceView = cell
|
||||
actionSheet.popoverPresentationController?.sourceRect = cell.bounds
|
||||
|
||||
let actions: [NodeAction]
|
||||
switch nodeAttrs.nodeStatus {
|
||||
case .undefined:
|
||||
actions = []
|
||||
case .downloading, .applying, .inQueue:
|
||||
actions = [.cancelDownload]
|
||||
case .error:
|
||||
actions = nodeAttrs.downloadedMwmCount > 0 ? [.retryDownload, .delete] : [.retryDownload]
|
||||
case .onDiskOutOfDate:
|
||||
actions = [.showOnMap, .update, .delete]
|
||||
case .onDisk:
|
||||
actions = [.showOnMap, .delete]
|
||||
case .notDownloaded:
|
||||
actions = [.download]
|
||||
case .partly:
|
||||
actions = [.download, .delete]
|
||||
@unknown default:
|
||||
fatalError()
|
||||
}
|
||||
|
||||
addActions(actions, for: nodeAttrs, to: actionSheet)
|
||||
actionSheet.addAction(UIAlertAction(title: L("cancel"), style: .cancel))
|
||||
present(actionSheet, animated: true)
|
||||
}
|
||||
|
||||
private func addActions(_ actions: [NodeAction], for nodeAttrs: MapNodeAttributes, to actionSheet: UIAlertController) {
|
||||
actions.forEach { [unowned self] in
|
||||
let action: UIAlertAction
|
||||
switch $0 {
|
||||
case .showOnMap:
|
||||
action = UIAlertAction(title: L("zoom_to_country"), style: .default, handler: { _ in
|
||||
Storage.shared().showNode(nodeAttrs.countryId)
|
||||
self.navigationController?.popToRootViewController(animated: true)
|
||||
})
|
||||
case .download:
|
||||
let prefix = nodeAttrs.totalMwmCount == 1 ? L("downloader_download_map") : L("downloader_download_all_button")
|
||||
action = UIAlertAction(title: "\(prefix) (\(formattedSize(nodeAttrs.totalSize)))",
|
||||
style: .default,
|
||||
handler: { _ in
|
||||
Storage.shared().downloadNode(nodeAttrs.countryId)
|
||||
})
|
||||
case .update:
|
||||
let size = formattedSize(nodeAttrs.totalUpdateSizeBytes)
|
||||
let title = "\(L("downloader_status_outdated")) \(size)"
|
||||
action = UIAlertAction(title: title, style: .default, handler: { _ in
|
||||
Storage.shared().updateNode(nodeAttrs.countryId)
|
||||
})
|
||||
case .cancelDownload:
|
||||
action = UIAlertAction(title: L("cancel_download"), style: .destructive, handler: { _ in
|
||||
Storage.shared().cancelDownloadNode(nodeAttrs.countryId)
|
||||
})
|
||||
case .retryDownload:
|
||||
action = UIAlertAction(title: L("downloader_retry"), style: .destructive, handler: { _ in
|
||||
Storage.shared().retryDownloadNode(nodeAttrs.countryId)
|
||||
})
|
||||
case .delete:
|
||||
action = UIAlertAction(title: L("downloader_delete_map"), style: .destructive, handler: { _ in
|
||||
Storage.shared().deleteNode(nodeAttrs.countryId)
|
||||
})
|
||||
}
|
||||
actionSheet.addAction(action)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func reloadData() {
|
||||
tableView.reloadData()
|
||||
configButtons()
|
||||
}
|
||||
|
||||
fileprivate func configButtons() {
|
||||
downloadAllView.state = .none
|
||||
downloadAllView.isSizeHidden = false
|
||||
let parentAttributes = dataSource.parentAttributes()
|
||||
let error = parentAttributes.nodeStatus == .error || parentAttributes.nodeStatus == .undefined
|
||||
let downloading = parentAttributes.nodeStatus == .downloading || parentAttributes.nodeStatus == .inQueue || parentAttributes.nodeStatus == .applying
|
||||
switch mode {
|
||||
case .available:
|
||||
if dataSource.isRoot {
|
||||
break
|
||||
}
|
||||
if error {
|
||||
downloadAllView.state = .error
|
||||
} else if downloading {
|
||||
downloadAllView.state = .dowloading
|
||||
} else if parentAttributes.downloadedMwmCount < parentAttributes.totalMwmCount {
|
||||
downloadAllView.state = .ready
|
||||
downloadAllView.style = .download
|
||||
downloadAllView.downloadSize = parentAttributes.totalSize - parentAttributes.downloadedSize
|
||||
}
|
||||
case .downloaded:
|
||||
let isUpdate = parentAttributes.totalUpdateSizeBytes > 0
|
||||
let size = isUpdate ? parentAttributes.totalUpdateSizeBytes : parentAttributes.downloadingSize
|
||||
if error {
|
||||
downloadAllView.state = dataSource.isRoot ? .none : .error
|
||||
downloadAllView.downloadSize = parentAttributes.downloadingSize
|
||||
} else if downloading && dataSource is DownloadedMapsDataSource {
|
||||
downloadAllView.state = .dowloading
|
||||
if dataSource.isRoot {
|
||||
downloadAllView.style = .download
|
||||
downloadAllView.isSizeHidden = true
|
||||
}
|
||||
} else if isUpdate {
|
||||
downloadAllView.state = .ready
|
||||
downloadAllView.style = .update
|
||||
downloadAllView.downloadSize = size
|
||||
}
|
||||
@unknown default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func onAddMaps() {
|
||||
let vc = storyboard!.instantiateViewController(ofType: DownloadMapsViewController.self)
|
||||
if !dataSource.isRoot {
|
||||
vc.dataSource = AvailableMapsDataSource(dataSource.getParentCountryId())
|
||||
}
|
||||
vc.mode = .available
|
||||
navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
|
||||
extension DownloadMapsViewController: UITableViewDataSource {
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
dataSource.numberOfSections() + (hasAddMapSection ? 1 : 0)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
if hasAddMapSection && section == dataSource.numberOfSections() {
|
||||
return 1
|
||||
}
|
||||
return dataSource.numberOfItems(in: section)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
if hasAddMapSection && indexPath.section == dataSource.numberOfSections() {
|
||||
let cellType = MWMMapDownloaderButtonTableViewCell.self
|
||||
let buttonCell = tableView.dequeueReusableCell(cell: cellType, indexPath: indexPath)
|
||||
return buttonCell
|
||||
}
|
||||
|
||||
let nodeAttrs = dataSource.item(at: indexPath)
|
||||
let cell: MWMMapDownloaderTableViewCell
|
||||
if nodeAttrs.hasChildren {
|
||||
let cellType = MWMMapDownloaderLargeCountryTableViewCell.self
|
||||
let largeCountryCell = tableView.dequeueReusableCell(cell: cellType, indexPath: indexPath)
|
||||
cell = largeCountryCell
|
||||
} else if let matchedName = dataSource.matchedName(at: indexPath), matchedName != nodeAttrs.nodeName {
|
||||
let cellType = MWMMapDownloaderSubplaceTableViewCell.self
|
||||
let subplaceCell = tableView.dequeueReusableCell(cell: cellType, indexPath: indexPath)
|
||||
subplaceCell.setSubplaceText(matchedName)
|
||||
cell = subplaceCell
|
||||
} else if !nodeAttrs.hasParent {
|
||||
let cellType = MWMMapDownloaderTableViewCell.self
|
||||
let downloaderCell = tableView.dequeueReusableCell(cell: cellType, indexPath: indexPath)
|
||||
cell = downloaderCell
|
||||
} else {
|
||||
let cellType = MWMMapDownloaderPlaceTableViewCell.self
|
||||
let placeCell = tableView.dequeueReusableCell(cell: cellType, indexPath: indexPath)
|
||||
cell = placeCell
|
||||
}
|
||||
cell.mode = dataSource.isSearching ? .available : mode
|
||||
cell.config(nodeAttrs, searchQuery: searchController.searchBar.text)
|
||||
cell.delegate = self
|
||||
return cell
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
dataSource.title(for: section)
|
||||
}
|
||||
|
||||
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
|
||||
dataSource.indexTitles()
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
|
||||
index
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
||||
if indexPath.section == dataSource.numberOfSections() {
|
||||
return false
|
||||
}
|
||||
let nodeAttrs = dataSource.item(at: indexPath)
|
||||
switch nodeAttrs.nodeStatus {
|
||||
case .onDisk, .onDiskOutOfDate, .partly:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
|
||||
if editingStyle == .delete {
|
||||
let nodeAttrs = dataSource.item(at: indexPath)
|
||||
Storage.shared().deleteNode(nodeAttrs.countryId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
|
||||
extension DownloadMapsViewController: UITableViewDelegate {
|
||||
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
let headerView = MWMMapDownloaderCellHeader()
|
||||
if section != dataSource.numberOfSections() {
|
||||
headerView.text = dataSource.title(for: section)
|
||||
}
|
||||
return headerView
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
28
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
|
||||
section == dataSource.numberOfSections() - 1 ? 68 : 0
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
if indexPath.section == dataSource.numberOfSections() {
|
||||
onAddMaps()
|
||||
return
|
||||
}
|
||||
let nodeAttrs = dataSource.item(at: indexPath)
|
||||
if nodeAttrs.hasChildren {
|
||||
showChildren(dataSource.item(at: indexPath))
|
||||
return
|
||||
}
|
||||
showActions(nodeAttrs, in: tableView.cellForRow(at: indexPath)!)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIScrollViewDelegate
|
||||
|
||||
extension DownloadMapsViewController: UIScrollViewDelegate {
|
||||
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||
searchController.searchBar.resignFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MWMMapDownloaderTableViewCellDelegate
|
||||
|
||||
extension DownloadMapsViewController: MWMMapDownloaderTableViewCellDelegate {
|
||||
func mapDownloaderCellDidPressProgress(_ cell: MWMMapDownloaderTableViewCell) {
|
||||
guard let indexPath = tableView.indexPath(for: cell) else { return }
|
||||
let nodeAttrs = dataSource.item(at: indexPath)
|
||||
switch nodeAttrs.nodeStatus {
|
||||
case .undefined, .error:
|
||||
Storage.shared().retryDownloadNode(nodeAttrs.countryId)
|
||||
case .downloading, .applying, .inQueue:
|
||||
Storage.shared().cancelDownloadNode(nodeAttrs.countryId)
|
||||
case .onDiskOutOfDate:
|
||||
Storage.shared().updateNode(nodeAttrs.countryId)
|
||||
case .onDisk:
|
||||
// do nothing
|
||||
break
|
||||
case .notDownloaded, .partly:
|
||||
if nodeAttrs.hasChildren {
|
||||
showChildren(nodeAttrs)
|
||||
} else {
|
||||
Storage.shared().downloadNode(nodeAttrs.countryId)
|
||||
}
|
||||
@unknown default:
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
func mapDownloaderCellDidLongPress(_ cell: MWMMapDownloaderTableViewCell) {
|
||||
guard let indexPath = tableView.indexPath(for: cell) else { return }
|
||||
let nodeAttrs = dataSource.item(at: indexPath)
|
||||
showActions(nodeAttrs, in: cell)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - StorageObserver
|
||||
|
||||
extension DownloadMapsViewController: StorageObserver {
|
||||
func processCountryEvent(_ countryId: String) {
|
||||
if skipCountryEvent && countryId == dataSource.getParentCountryId() {
|
||||
return
|
||||
}
|
||||
dataSource.reload {
|
||||
reloadData()
|
||||
noMapsContainer.isHidden = !dataSource.isEmpty || Storage.shared().downloadInProgress()
|
||||
}
|
||||
if countryId == dataSource.getParentCountryId() {
|
||||
configButtons()
|
||||
}
|
||||
|
||||
for cell in tableView.visibleCells {
|
||||
guard let downloaderCell = cell as? MWMMapDownloaderTableViewCell else { continue }
|
||||
if downloaderCell.nodeAttrs.countryId != countryId { continue }
|
||||
guard let indexPath = tableView.indexPath(for: downloaderCell) else { return }
|
||||
downloaderCell.config(dataSource.item(at: indexPath), searchQuery: searchController.searchBar.text)
|
||||
}
|
||||
}
|
||||
|
||||
func processCountry(_ countryId: String, downloadedBytes: UInt64, totalBytes: UInt64) {
|
||||
for cell in tableView.visibleCells {
|
||||
guard let downloaderCell = cell as? MWMMapDownloaderTableViewCell else { continue }
|
||||
if downloaderCell.nodeAttrs.countryId != countryId { continue }
|
||||
downloaderCell.setDownloadProgress(CGFloat(downloadedBytes) / CGFloat(totalBytes))
|
||||
}
|
||||
|
||||
if countryId == dataSource.getParentCountryId() {
|
||||
downloadAllView.downloadProgress = CGFloat(downloadedBytes) / CGFloat(totalBytes)
|
||||
downloadAllView.downloadSize = totalBytes
|
||||
} else if dataSource.isRoot && dataSource is DownloadedMapsDataSource {
|
||||
downloadAllView.state = .dowloading
|
||||
downloadAllView.isSizeHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UISearchBarDelegate
|
||||
|
||||
extension DownloadMapsViewController: UISearchBarDelegate {
|
||||
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
|
||||
searchBar.text = nil
|
||||
searchBar.resignFirstResponder()
|
||||
dataSource.cancelSearch()
|
||||
reloadData()
|
||||
noSerchResultViewController.view.isHidden = true
|
||||
}
|
||||
|
||||
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
|
||||
let locale = searchBar.textInputMode?.primaryLanguage
|
||||
dataSource.search(searchText, locale: locale ?? "") { [weak self] finished in
|
||||
guard let self = self else { return }
|
||||
self.reloadData()
|
||||
self.noSerchResultViewController.view.isHidden = !self.dataSource.isEmpty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - DownloadAllViewDelegate
|
||||
|
||||
extension DownloadMapsViewController: DownloadAllViewDelegate {
|
||||
func onStateChanged(state: DownloadAllView.State) {
|
||||
if state == .none {
|
||||
downloadAllViewContainer.isHidden = true
|
||||
tableView.contentInset = UIEdgeInsets.zero
|
||||
} else {
|
||||
downloadAllViewContainer.isHidden = false
|
||||
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: allMapsViewBottomOffsetConstant, right: 0)
|
||||
}
|
||||
}
|
||||
|
||||
func onDownloadButtonPressed() {
|
||||
skipCountryEvent = true
|
||||
let id = dataSource.getParentCountryId()
|
||||
if mode == .downloaded {
|
||||
Storage.shared().updateNode(id)
|
||||
} else {
|
||||
Storage.shared().downloadNode(id)
|
||||
}
|
||||
skipCountryEvent = false
|
||||
processCountryEvent(id)
|
||||
}
|
||||
|
||||
func onRetryButtonPressed() {
|
||||
skipCountryEvent = true
|
||||
let id = dataSource.getParentCountryId()
|
||||
Storage.shared().retryDownloadNode(id)
|
||||
skipCountryEvent = false
|
||||
processCountryEvent(id)
|
||||
}
|
||||
|
||||
func onCancelButtonPressed() {
|
||||
skipCountryEvent = true
|
||||
let id = dataSource.getParentCountryId()
|
||||
Storage.shared().cancelDownloadNode(id)
|
||||
skipCountryEvent = false
|
||||
processCountryEvent(id)
|
||||
reloadData()
|
||||
}
|
||||
}
|
||||
139
iphone/Maps/UI/Downloader/DownloadedMapsDataSource.swift
Normal file
139
iphone/Maps/UI/Downloader/DownloadedMapsDataSource.swift
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
class DownloadedMapsDataSource {
|
||||
private let parentCountryId: String?
|
||||
private var countryIds: [String]
|
||||
|
||||
fileprivate var searching = false
|
||||
fileprivate lazy var searchDataSource: IDownloaderDataSource = {
|
||||
SearchMapsDataSource()
|
||||
}()
|
||||
|
||||
init(_ parentId: String? = nil) {
|
||||
self.parentCountryId = parentId
|
||||
countryIds = DownloadedMapsDataSource.loadData(parentId)
|
||||
}
|
||||
|
||||
private class func loadData(_ parentId: String?) -> [String] {
|
||||
let countryIds: [String]
|
||||
if let parentId = parentId {
|
||||
countryIds = Storage.shared().downloadedCountries(withParent: parentId)
|
||||
} else {
|
||||
countryIds = Storage.shared().downloadedCountries()
|
||||
}
|
||||
|
||||
return countryIds.map {
|
||||
CountryIdAndName(countryId: $0, name: Storage.shared().name(forCountry: $0))
|
||||
}.sorted {
|
||||
$0.countryName.compare($1.countryName) == .orderedAscending
|
||||
}.map {
|
||||
$0.countryId
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func reloadData() {
|
||||
countryIds = DownloadedMapsDataSource.loadData(parentCountryId)
|
||||
}
|
||||
}
|
||||
|
||||
extension DownloadedMapsDataSource: IDownloaderDataSource {
|
||||
var isEmpty: Bool {
|
||||
return searching ? searchDataSource.isEmpty : countryIds.isEmpty
|
||||
}
|
||||
|
||||
var title: String {
|
||||
guard let parentCountryId = parentCountryId else {
|
||||
return L("download_maps")
|
||||
}
|
||||
return Storage.shared().name(forCountry: parentCountryId)
|
||||
}
|
||||
|
||||
var isRoot: Bool {
|
||||
parentCountryId == nil
|
||||
}
|
||||
|
||||
var isSearching: Bool {
|
||||
searching
|
||||
}
|
||||
|
||||
func getParentCountryId() -> String {
|
||||
if parentCountryId != nil {
|
||||
return parentCountryId!
|
||||
}
|
||||
return Storage.shared().getRootId()
|
||||
}
|
||||
|
||||
func parentAttributes() -> MapNodeAttributes {
|
||||
guard let parentId = parentCountryId else {
|
||||
return Storage.shared().attributesForRoot()
|
||||
}
|
||||
return Storage.shared().attributes(forCountry: parentId)
|
||||
}
|
||||
|
||||
func numberOfSections() -> Int {
|
||||
searching ? searchDataSource.numberOfSections() : 1
|
||||
}
|
||||
|
||||
func numberOfItems(in section: Int) -> Int {
|
||||
searching ? searchDataSource.numberOfItems(in: section) : countryIds.count
|
||||
}
|
||||
|
||||
func item(at indexPath: IndexPath) -> MapNodeAttributes {
|
||||
if searching {
|
||||
return searchDataSource.item(at: indexPath)
|
||||
}
|
||||
guard indexPath.section == 0 else { fatalError() }
|
||||
let countryId = countryIds[indexPath.item]
|
||||
return Storage.shared().attributes(forCountry: countryId)
|
||||
}
|
||||
|
||||
func matchedName(at indexPath: IndexPath) -> String? {
|
||||
searching ? searchDataSource.matchedName(at: indexPath) : nil
|
||||
}
|
||||
|
||||
func title(for section: Int) -> String {
|
||||
if searching {
|
||||
return searchDataSource.title(for: section)
|
||||
}
|
||||
if let parentCountryId = parentCountryId {
|
||||
let attributes = Storage.shared().attributes(forCountry: parentCountryId)
|
||||
return Storage.shared().name(forCountry: parentCountryId) + " (\(formattedSize(attributes.downloadedSize)))"
|
||||
}
|
||||
let attributes = Storage.shared().attributesForRoot()
|
||||
return L("downloader_downloaded_subtitle") + " (\(formattedSize(attributes.downloadedSize)))"
|
||||
}
|
||||
|
||||
func indexTitles() -> [String]? {
|
||||
nil
|
||||
}
|
||||
|
||||
func dataSourceFor(_ childId: String) -> IDownloaderDataSource {
|
||||
searching ? searchDataSource.dataSourceFor(childId) : DownloadedMapsDataSource(childId)
|
||||
}
|
||||
|
||||
func reload(_ completion: () -> Void) {
|
||||
if searching {
|
||||
searchDataSource.reload(completion)
|
||||
return
|
||||
}
|
||||
reloadData()
|
||||
completion()
|
||||
}
|
||||
|
||||
func search(_ query: String, locale: String, update: @escaping (Bool) -> Void) {
|
||||
if query.isEmpty {
|
||||
cancelSearch()
|
||||
update(true)
|
||||
return
|
||||
}
|
||||
searchDataSource.search(query, locale: locale) { [weak self] (finished) in
|
||||
if finished {
|
||||
self?.searching = true
|
||||
update(finished)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancelSearch() {
|
||||
searching = false
|
||||
searchDataSource.cancelSearch()
|
||||
}
|
||||
}
|
||||
18
iphone/Maps/UI/Downloader/IDownloaderDataSource.swift
Normal file
18
iphone/Maps/UI/Downloader/IDownloaderDataSource.swift
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
protocol IDownloaderDataSource {
|
||||
var isEmpty: Bool { get }
|
||||
var title: String { get }
|
||||
var isRoot: Bool { get }
|
||||
var isSearching: Bool { get }
|
||||
func getParentCountryId() -> String
|
||||
func parentAttributes() -> MapNodeAttributes
|
||||
func numberOfSections() -> Int
|
||||
func numberOfItems(in section: Int) -> Int
|
||||
func item(at indexPath: IndexPath) -> MapNodeAttributes
|
||||
func matchedName(at indexPath: IndexPath) -> String?
|
||||
func title(for section: Int) -> String
|
||||
func indexTitles() -> [String]?
|
||||
func dataSourceFor(_ childId: String) -> IDownloaderDataSource
|
||||
func reload(_ completion: () -> Void)
|
||||
func search(_ query: String, locale: String, update: @escaping (_ completed: Bool) -> Void)
|
||||
func cancelSearch()
|
||||
}
|
||||
4
iphone/Maps/UI/Downloader/MWMMapDownloaderMode.h
Normal file
4
iphone/Maps/UI/Downloader/MWMMapDownloaderMode.h
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
typedef NS_ENUM(NSUInteger, MWMMapDownloaderMode) {
|
||||
MWMMapDownloaderModeDownloaded,
|
||||
MWMMapDownloaderModeAvailable
|
||||
};
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
@objc(MWMDownloaderNoResultsEmbedViewController)
|
||||
final class DownloaderNoResultsEmbed: UINavigationController {
|
||||
|
||||
@objc(MWMDownloaderNoResultsScreen)
|
||||
enum Screen: Int {
|
||||
|
||||
case noMaps
|
||||
case noSearchResults
|
||||
}
|
||||
|
||||
@objc var screen = Screen.noMaps {
|
||||
didSet {
|
||||
let controller: MWMViewController
|
||||
switch screen {
|
||||
case .noMaps: controller = MWMNoMapsViewController.controller()
|
||||
case .noSearchResults: controller = SearchNoResultsViewController.controller
|
||||
}
|
||||
setViewControllers([controller], animated: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
3
iphone/Maps/UI/Downloader/NoMaps/MWMNoMapsView.h
Normal file
3
iphone/Maps/UI/Downloader/NoMaps/MWMNoMapsView.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
@interface MWMNoMapsView : SolidTouchView
|
||||
|
||||
@end
|
||||
81
iphone/Maps/UI/Downloader/NoMaps/MWMNoMapsView.m
Normal file
81
iphone/Maps/UI/Downloader/NoMaps/MWMNoMapsView.m
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#import "MWMNoMapsView.h"
|
||||
#import "MWMKeyboard.h"
|
||||
|
||||
@interface MWMNoMapsView ()<MWMKeyboardObserver>
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UIImageView * image;
|
||||
@property(weak, nonatomic) IBOutlet UILabel * title;
|
||||
@property(weak, nonatomic) IBOutlet UILabel * text;
|
||||
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * containerWidth;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * containerHeight;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * containerTopOffset;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * containerBottomOffset;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * imageMinHeight;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * imageHeight;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * titleImageOffset;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * titleTopOffset;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * textTopOffset;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMNoMapsView
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
if (!IPAD)
|
||||
{
|
||||
self.containerWidth.active = NO;
|
||||
self.containerHeight.active = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.containerTopOffset.active = NO;
|
||||
}
|
||||
[MWMKeyboard addObserver:self];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
[self configForSize:self.frame.size];
|
||||
}
|
||||
|
||||
- (void)configForSize:(CGSize)size
|
||||
{
|
||||
CGSize iPadSize = CGSizeMake(520, 600);
|
||||
CGSize newSize = IPAD ? iPadSize : size;
|
||||
CGFloat width = newSize.width;
|
||||
CGFloat height = newSize.height;
|
||||
BOOL hideImage = (self.imageHeight.multiplier * height <= self.imageMinHeight.constant);
|
||||
if (hideImage)
|
||||
{
|
||||
self.titleImageOffset.priority = UILayoutPriorityDefaultLow;
|
||||
self.title.hidden = self.title.minY < self.titleTopOffset.constant;
|
||||
self.text.hidden = self.text.minY < self.textTopOffset.constant;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.titleImageOffset.priority = UILayoutPriorityDefaultHigh;
|
||||
self.title.hidden = NO;
|
||||
self.text.hidden = NO;
|
||||
}
|
||||
self.image.hidden = hideImage;
|
||||
if (IPAD)
|
||||
{
|
||||
self.containerWidth.constant = width;
|
||||
self.containerHeight.constant = height;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - MWMKeyboard
|
||||
|
||||
- (void)onKeyboardAnimation
|
||||
{
|
||||
self.containerBottomOffset.constant = [MWMKeyboard keyboardHeight];
|
||||
[self.superview layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (void)onKeyboardWillAnimate { [self.superview layoutIfNeeded]; }
|
||||
@end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#import "MWMViewController.h"
|
||||
|
||||
@interface MWMNoMapsViewController : MWMViewController
|
||||
|
||||
+ (MWMNoMapsViewController *)controller NS_SWIFT_NAME(controller());
|
||||
|
||||
@end
|
||||
19
iphone/Maps/UI/Downloader/NoMaps/MWMNoMapsViewController.mm
Normal file
19
iphone/Maps/UI/Downloader/NoMaps/MWMNoMapsViewController.mm
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#import "MWMNoMapsViewController.h"
|
||||
#import "MWMMapDownloaderMode.h"
|
||||
#import "MWMMapViewControlsManager.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
@implementation MWMNoMapsViewController
|
||||
|
||||
+ (MWMNoMapsViewController *)controller
|
||||
{
|
||||
auto storyboard = [UIStoryboard instance:MWMStoryboardMain];
|
||||
return [storyboard instantiateViewControllerWithIdentifier:[self className]];
|
||||
}
|
||||
|
||||
- (IBAction)downloadMaps
|
||||
{
|
||||
[[MWMMapViewControlsManager manager] actionDownloadMaps:MWMMapDownloaderModeAvailable];
|
||||
}
|
||||
|
||||
@end
|
||||
84
iphone/Maps/UI/Downloader/SearchMapsDataSource.swift
Normal file
84
iphone/Maps/UI/Downloader/SearchMapsDataSource.swift
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
class SearchMapsDataSource {
|
||||
typealias SearchCallback = (Bool) -> Void
|
||||
|
||||
fileprivate var searchResults: [MapSearchResult] = []
|
||||
fileprivate var searchId = 0
|
||||
fileprivate var onUpdate: SearchCallback?
|
||||
}
|
||||
|
||||
extension SearchMapsDataSource: IDownloaderDataSource {
|
||||
var isEmpty: Bool {
|
||||
searchResults.isEmpty
|
||||
}
|
||||
|
||||
var title: String {
|
||||
""
|
||||
}
|
||||
|
||||
var isRoot: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
var isSearching: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
func numberOfSections() -> Int {
|
||||
1
|
||||
}
|
||||
|
||||
func getParentCountryId() -> String {
|
||||
return Storage.shared().getRootId()
|
||||
}
|
||||
|
||||
func parentAttributes() -> MapNodeAttributes {
|
||||
return Storage.shared().attributesForRoot()
|
||||
}
|
||||
|
||||
func numberOfItems(in section: Int) -> Int {
|
||||
searchResults.count
|
||||
}
|
||||
|
||||
func item(at indexPath: IndexPath) -> MapNodeAttributes {
|
||||
Storage.shared().attributes(forCountry: searchResults[indexPath.item].countryId)
|
||||
}
|
||||
|
||||
func matchedName(at indexPath: IndexPath) -> String? {
|
||||
searchResults[indexPath.item].matchedName
|
||||
}
|
||||
|
||||
func title(for section: Int) -> String {
|
||||
L("downloader_search_results")
|
||||
}
|
||||
|
||||
func indexTitles() -> [String]? {
|
||||
nil
|
||||
}
|
||||
|
||||
func dataSourceFor(_ childId: String) -> IDownloaderDataSource {
|
||||
AvailableMapsDataSource(childId)
|
||||
}
|
||||
|
||||
func reload(_ completion: () -> Void) {
|
||||
completion()
|
||||
}
|
||||
|
||||
func search(_ query: String, locale: String, update: @escaping SearchCallback) {
|
||||
searchId += 1
|
||||
onUpdate = update
|
||||
FrameworkHelper.search(inDownloader: query, inputLocale: locale) { [weak self, searchId] (results, finished) in
|
||||
if searchId != self?.searchId {
|
||||
return
|
||||
}
|
||||
self?.searchResults = results
|
||||
if results.count > 0 || finished {
|
||||
self?.onUpdate?(finished)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancelSearch() {
|
||||
searchResults = []
|
||||
onUpdate = nil
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue