Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
14
iphone/Maps/Categories/Bundle+Init.swift
Normal file
14
iphone/Maps/Categories/Bundle+Init.swift
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
extension Bundle {
|
||||
func load<T:UIView>(viewClass: T.Type, owner: Any? = nil, options: [AnyHashable: Any]? = nil) -> T? {
|
||||
return loadNibNamed(String(describing: viewClass), owner: owner, options: options as? [UINib.OptionsKey : Any])?.first as? T
|
||||
}
|
||||
|
||||
@objc func load(viewClass: AnyClass, owner: Any? = nil, options: [AnyHashable: Any]? = nil) -> [Any]? {
|
||||
return loadNibNamed(toString(viewClass), owner: owner, options: options as? [UINib.OptionsKey : Any])
|
||||
}
|
||||
|
||||
@objc func load(plist: String) -> Dictionary<String, AnyObject>? {
|
||||
guard let path = Bundle.main.path(forResource: plist, ofType: "plist") else { return nil }
|
||||
return NSDictionary(contentsOfFile: path) as? [String: AnyObject]
|
||||
}
|
||||
}
|
||||
16
iphone/Maps/Categories/CALayer+SetCorner.swift
Normal file
16
iphone/Maps/Categories/CALayer+SetCorner.swift
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
extension CALayer {
|
||||
func setCornerRadius(_ cornerRadius: CornerRadius,
|
||||
maskedCorners: CACornerMask? = nil) {
|
||||
self.cornerRadius = cornerRadius.value
|
||||
if let maskedCorners {
|
||||
self.maskedCorners = maskedCorners
|
||||
}
|
||||
cornerCurve = .continuous
|
||||
}
|
||||
}
|
||||
|
||||
extension CACornerMask {
|
||||
static var all: CACornerMask {
|
||||
return [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
||||
}
|
||||
}
|
||||
7
iphone/Maps/Categories/CLLocation+Mercator.h
Normal file
7
iphone/Maps/Categories/CLLocation+Mercator.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#include "geometry/point2d.hpp"
|
||||
|
||||
@interface CLLocation (Mercator)
|
||||
|
||||
- (m2::PointD)mercator;
|
||||
|
||||
@end
|
||||
8
iphone/Maps/Categories/CLLocation+Mercator.mm
Normal file
8
iphone/Maps/Categories/CLLocation+Mercator.mm
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#import "CLLocation+Mercator.h"
|
||||
#import "MWMLocationHelpers.h"
|
||||
|
||||
@implementation CLLocation (Mercator)
|
||||
|
||||
- (m2::PointD)mercator { return location_helpers::ToMercator(self.coordinate); }
|
||||
|
||||
@end
|
||||
19
iphone/Maps/Categories/MWMCategory+PlacesCountTitle.swift
Normal file
19
iphone/Maps/Categories/MWMCategory+PlacesCountTitle.swift
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import Foundation
|
||||
|
||||
extension BookmarkGroup {
|
||||
@objc func placesCountTitle() -> String {
|
||||
let bookmarks = String(format: L("bookmarks_places"), bookmarksCount)
|
||||
let tracks = String(format: L("tracks"), trackCount)
|
||||
|
||||
if (bookmarksCount == 0 && trackCount == 0) || (bookmarksCount > 0 && trackCount > 0) {
|
||||
return "\(bookmarks), \(tracks)"
|
||||
}
|
||||
|
||||
if (bookmarksCount > 0) {
|
||||
return bookmarks
|
||||
}
|
||||
|
||||
return tracks
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#include "geometry/point2d.hpp"
|
||||
|
||||
@interface MWMMapViewControlsManager (AddPlace)
|
||||
|
||||
- (void)addPlace:(BOOL)isBusiness position:(nullable m2::PointD const *)optionalPosition;
|
||||
|
||||
@end
|
||||
100
iphone/Maps/Categories/NSAttributedString+HTML.swift
Normal file
100
iphone/Maps/Categories/NSAttributedString+HTML.swift
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
extension NSAttributedString {
|
||||
@objc
|
||||
public class func string(withHtml htmlString:String, defaultAttributes attributes:[NSAttributedString.Key : Any]) -> NSAttributedString? {
|
||||
guard let data = htmlString.data(using: .utf8) else { return nil }
|
||||
guard let text = try? NSMutableAttributedString(data: data,
|
||||
options: [.documentType: NSAttributedString.DocumentType.html,
|
||||
.characterEncoding: String.Encoding.utf8.rawValue],
|
||||
documentAttributes: nil) else { return nil }
|
||||
text.addAttributes(attributes, range: NSMakeRange(0, text.length))
|
||||
return text
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension NSMutableAttributedString {
|
||||
@objc convenience init?(htmlString: String, baseFont: UIFont, paragraphStyle: NSParagraphStyle?, estimatedWidth: CGFloat = 0) {
|
||||
self.init(htmlString: htmlString, baseFont: baseFont)
|
||||
if let paragraphStyle = paragraphStyle {
|
||||
addAttribute(.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, length))
|
||||
}
|
||||
|
||||
guard estimatedWidth > 0 else { return }
|
||||
enumerateAttachments(estimatedWidth: estimatedWidth)
|
||||
}
|
||||
|
||||
@objc convenience init?(htmlString: String, baseFont: UIFont) {
|
||||
guard let data = htmlString.data(using: .utf8) else { return nil }
|
||||
|
||||
do {
|
||||
try self.init(data: data,
|
||||
options: [.documentType : NSAttributedString.DocumentType.html,
|
||||
.characterEncoding: String.Encoding.utf8.rawValue],
|
||||
documentAttributes: nil)
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
|
||||
enumerateFont(baseFont)
|
||||
}
|
||||
|
||||
@objc convenience init?(htmlString: String, baseFont: UIFont, estimatedWidth: CGFloat) {
|
||||
guard let data = htmlString.data(using: .utf8) else { return nil }
|
||||
|
||||
do {
|
||||
try self.init(data: data,
|
||||
options: [.documentType : NSAttributedString.DocumentType.html,
|
||||
.characterEncoding: String.Encoding.utf8.rawValue],
|
||||
documentAttributes: nil)
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
|
||||
enumerateFont(baseFont)
|
||||
|
||||
guard estimatedWidth > 0 else { return }
|
||||
|
||||
enumerateAttachments(estimatedWidth: estimatedWidth)
|
||||
}
|
||||
|
||||
func enumerateFont(_ baseFont: UIFont) {
|
||||
enumerateAttribute(.font, in: NSMakeRange(0, length), options: []) { (value, range, _) in
|
||||
if let font = value as? UIFont,
|
||||
let descriptor = baseFont.fontDescriptor.withSymbolicTraits(font.fontDescriptor.symbolicTraits) {
|
||||
let newFont = UIFont(descriptor: descriptor, size: baseFont.pointSize)
|
||||
addAttribute(.font, value: newFont, range: range)
|
||||
} else {
|
||||
addAttribute(.font, value: baseFont, range: range)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func enumerateAttachments(estimatedWidth: CGFloat) {
|
||||
enumerateAttribute(.attachment, in: NSMakeRange(0, length), options: []) { (value, range, _) in
|
||||
if let attachement = value as? NSTextAttachment,
|
||||
let image = attachement.image(forBounds: attachement.bounds, textContainer: NSTextContainer(), characterIndex: range.location) {
|
||||
if image.size.width > estimatedWidth || attachement.bounds.width > estimatedWidth {
|
||||
let resizedAttachment = NSTextAttachment()
|
||||
if image.size.width > estimatedWidth {
|
||||
let newImage = resizeImage(image: image, scale: estimatedWidth/image.size.width) ?? image
|
||||
resizedAttachment.image = newImage
|
||||
} else {
|
||||
resizedAttachment.image = image
|
||||
}
|
||||
addAttribute(.attachment, value: resizedAttachment, range: range)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func resizeImage(image: UIImage, scale: CGFloat) -> UIImage? {
|
||||
let newSize = CGSize(width: image.size.width*scale, height: image.size.height*scale)
|
||||
let rect = CGRect(origin: CGPoint.zero, size: newSize)
|
||||
|
||||
let renderer = UIGraphicsImageRenderer(size: newSize)
|
||||
let newImage = renderer.image { context in
|
||||
image.draw(in: rect)
|
||||
}
|
||||
return newImage
|
||||
}
|
||||
}
|
||||
11
iphone/Maps/Categories/NSDate+TimeDistance.h
Normal file
11
iphone/Maps/Categories/NSDate+TimeDistance.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSDate (TimeDistance)
|
||||
|
||||
- (NSInteger)daysToNow;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
14
iphone/Maps/Categories/NSDate+TimeDistance.m
Normal file
14
iphone/Maps/Categories/NSDate+TimeDistance.m
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#import "NSDate+TimeDistance.h"
|
||||
|
||||
@implementation NSDate (TimeDistance)
|
||||
|
||||
- (NSInteger)daysToNow {
|
||||
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay
|
||||
fromDate:self
|
||||
toDate:[NSDate date]
|
||||
options:NSCalendarWrapComponents];
|
||||
|
||||
return components.day;
|
||||
}
|
||||
|
||||
@end
|
||||
11
iphone/Maps/Categories/NSString+Categories.h
Normal file
11
iphone/Maps/Categories/NSString+Categories.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
@interface NSString (MapsMeSize)
|
||||
|
||||
- (CGSize)sizeWithDrawSize:(CGSize)size font:(UIFont *)font;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSString (MapsMeRanges)
|
||||
|
||||
- (NSArray<NSValue *> *)rangesOfString:(NSString *)aString;
|
||||
|
||||
@end
|
||||
36
iphone/Maps/Categories/NSString+Categories.m
Normal file
36
iphone/Maps/Categories/NSString+Categories.m
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#import "NSString+Categories.h"
|
||||
|
||||
@implementation NSString (MapsMeSize)
|
||||
|
||||
- (CGSize)sizeWithDrawSize:(CGSize)drawSize font:(UIFont *)font {
|
||||
CGRect rect = [self boundingRectWithSize:drawSize
|
||||
options:NSStringDrawingUsesLineFragmentOrigin
|
||||
attributes:@{NSFontAttributeName : font}
|
||||
context:nil];
|
||||
return CGRectIntegral(rect).size;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSString (MapsMeRanges)
|
||||
|
||||
- (NSArray<NSValue *> *)rangesOfString:(NSString *)aString {
|
||||
NSMutableArray *result = [NSMutableArray array];
|
||||
if (self.length == 0) {
|
||||
return [result copy];
|
||||
}
|
||||
|
||||
NSRange searchRange = NSMakeRange(0, self.length);
|
||||
while (searchRange.location < self.length) {
|
||||
searchRange.length = self.length - searchRange.location;
|
||||
NSRange foundRange = [self rangeOfString:aString options:NSCaseInsensitiveSearch range:searchRange];
|
||||
if (foundRange.location == NSNotFound) {
|
||||
break;
|
||||
}
|
||||
searchRange.location = foundRange.location + foundRange.length;
|
||||
[result addObject:[NSValue valueWithRange:foundRange]];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
29
iphone/Maps/Categories/String+BoundingRect.swift
Normal file
29
iphone/Maps/Categories/String+BoundingRect.swift
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
extension String {
|
||||
func size(width: CGFloat, font: UIFont, maxNumberOfLines: Int = 0) -> CGSize {
|
||||
if isEmpty {
|
||||
return CGSize.zero
|
||||
}
|
||||
let lineHeight = font.lineHeight
|
||||
let maximumHeight = maxNumberOfLines == 0 ? CGFloat.greatestFiniteMagnitude : lineHeight * CGFloat(maxNumberOfLines + 1)
|
||||
let constraintSize = CGSize(width: width, height: maximumHeight)
|
||||
let options: NSStringDrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading, .truncatesLastVisibleLine]
|
||||
let paragraph = NSMutableParagraphStyle()
|
||||
paragraph.lineBreakMode = .byWordWrapping
|
||||
paragraph.alignment = .natural
|
||||
let attributes = [
|
||||
NSAttributedString.Key.font: font,
|
||||
NSAttributedString.Key.paragraphStyle: paragraph,
|
||||
]
|
||||
var rect = (self as NSString).boundingRect(with: constraintSize, options: options, attributes: attributes, context: nil)
|
||||
var numberOfLines = ceil(rect.height / lineHeight)
|
||||
if maxNumberOfLines != 0 {
|
||||
if width - rect.width < font.ascender {
|
||||
rect.size.width = width - font.ascender
|
||||
numberOfLines += 1
|
||||
}
|
||||
numberOfLines = min(numberOfLines, CGFloat(maxNumberOfLines))
|
||||
}
|
||||
return CGSize(width: ceil(rect.width), height: ceil(numberOfLines * lineHeight))
|
||||
}
|
||||
}
|
||||
28
iphone/Maps/Categories/UIApplication+LoadingOverlay.swift
Normal file
28
iphone/Maps/Categories/UIApplication+LoadingOverlay.swift
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
extension UIApplication {
|
||||
private static let overlayViewController = LoadingOverlayViewController()
|
||||
|
||||
@objc
|
||||
func showLoadingOverlay(completion: (() -> Void)? = nil) {
|
||||
guard let window = (self.connectedScenes.filter { $0.activationState == .foregroundActive }.first(where: { $0 is UIWindowScene }) as? UIWindowScene)?.windows.first(where: { $0.isKeyWindow }) else {
|
||||
completion?()
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
UIApplication.overlayViewController.modalPresentationStyle = .overFullScreen
|
||||
UIApplication.overlayViewController.modalTransitionStyle = .crossDissolve
|
||||
if window.rootViewController?.presentedViewController != nil {
|
||||
window.rootViewController?.presentedViewController?.present(UIApplication.overlayViewController, animated: true, completion: completion)
|
||||
} else {
|
||||
window.rootViewController?.present(UIApplication.overlayViewController, animated: true, completion: completion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func hideLoadingOverlay(completion: (() -> Void)? = nil) {
|
||||
DispatchQueue.main.async {
|
||||
UIApplication.overlayViewController.dismiss(animated: true, completion: completion)
|
||||
}
|
||||
}
|
||||
}
|
||||
12
iphone/Maps/Categories/UIButton+ImagePadding.swift
Normal file
12
iphone/Maps/Categories/UIButton+ImagePadding.swift
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
extension UIButton {
|
||||
@objc func setImagePadding(_ padding: CGFloat) {
|
||||
let isRightToLeft = UIView.userInterfaceLayoutDirection(for: self.semanticContentAttribute) == .rightToLeft
|
||||
if isRightToLeft {
|
||||
self.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: padding)
|
||||
self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: -2 * padding)
|
||||
} else {
|
||||
self.contentEdgeInsets = UIEdgeInsets(top: 0, left: padding, bottom: 0, right: 0)
|
||||
self.imageEdgeInsets = UIEdgeInsets(top: 0, left: -2 * padding, bottom: 0, right: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
5
iphone/Maps/Categories/UIButton+Orientation.h
Normal file
5
iphone/Maps/Categories/UIButton+Orientation.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
@interface UIButton (Orientation)
|
||||
|
||||
- (void)matchInterfaceOrientation;
|
||||
|
||||
@end
|
||||
13
iphone/Maps/Categories/UIButton+Orientation.m
Normal file
13
iphone/Maps/Categories/UIButton+Orientation.m
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#import "UIButton+Orientation.h"
|
||||
|
||||
#import <CoreApi/MWMCommon.h>
|
||||
|
||||
@implementation UIButton (Orientation)
|
||||
|
||||
- (void)matchInterfaceOrientation
|
||||
{
|
||||
if (isInterfaceRightToLeft())
|
||||
self.imageView.transform = CGAffineTransformMakeScale(-1, 1);
|
||||
}
|
||||
|
||||
@end
|
||||
7
iphone/Maps/Categories/UIButton+RuntimeAttributes.h
Normal file
7
iphone/Maps/Categories/UIButton+RuntimeAttributes.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIButton (RuntimeAttributes)
|
||||
|
||||
@property(copy, nonatomic) NSString * localizedText;
|
||||
|
||||
@end
|
||||
18
iphone/Maps/Categories/UIButton+RuntimeAttributes.m
Normal file
18
iphone/Maps/Categories/UIButton+RuntimeAttributes.m
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#import <objc/runtime.h>
|
||||
#import "UIButton+RuntimeAttributes.h"
|
||||
|
||||
@implementation UIButton (RuntimeAttributes)
|
||||
|
||||
- (void)setLocalizedText:(NSString *)localizedText
|
||||
{
|
||||
[self setTitle:L(localizedText) forState:UIControlStateNormal];
|
||||
[self setTitle:L(localizedText) forState:UIControlStateDisabled];
|
||||
}
|
||||
|
||||
- (NSString *)localizedText
|
||||
{
|
||||
NSString * title = [self titleForState:UIControlStateNormal];
|
||||
return L(title);
|
||||
}
|
||||
|
||||
@end
|
||||
17
iphone/Maps/Categories/UICollectionView+Cells.swift
Normal file
17
iphone/Maps/Categories/UICollectionView+Cells.swift
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
extension UICollectionView {
|
||||
@objc func register(cellClass: AnyClass) {
|
||||
register(UINib(cellClass), forCellWithReuseIdentifier: toString(cellClass))
|
||||
}
|
||||
|
||||
@objc func dequeueReusableCell(withCellClass cellClass: AnyClass, indexPath: IndexPath) -> UICollectionViewCell {
|
||||
return dequeueReusableCell(withReuseIdentifier: toString(cellClass), for: indexPath)
|
||||
}
|
||||
|
||||
func register<Cell>(cell: Cell.Type) where Cell: UICollectionViewCell {
|
||||
register(cell, forCellWithReuseIdentifier: toString(cell))
|
||||
}
|
||||
|
||||
func dequeueReusableCell<Cell>(cell: Cell.Type, indexPath: IndexPath) -> Cell where Cell: UICollectionViewCell {
|
||||
return dequeueReusableCell(withReuseIdentifier: toString(cell), for: indexPath) as! Cell
|
||||
}
|
||||
}
|
||||
42
iphone/Maps/Categories/UIColor+MapsMeColor.h
Normal file
42
iphone/Maps/Categories/UIColor+MapsMeColor.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#import "UIColor+PartnerColor.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface UIColor (MapsMeColor)
|
||||
|
||||
+ (UIColor *)black;
|
||||
+ (UIColor *)blackPrimaryText;
|
||||
+ (UIColor *)blackSecondaryText;
|
||||
+ (UIColor *)blackHintText;
|
||||
+ (UIColor *)red;
|
||||
+ (UIColor *)white;
|
||||
+ (UIColor *)primary;
|
||||
+ (UIColor *)pressBackground;
|
||||
+ (UIColor *)linkBlue;
|
||||
+ (UIColor *)linkBlueHighlighted;
|
||||
+ (UIColor *)buttonRed;
|
||||
+ (UIColor *)blackDividers;
|
||||
+ (UIColor *)whitePrimaryText;
|
||||
+ (UIColor *)whitePrimaryTextHighlighted;
|
||||
+ (UIColor *)whiteHintText;
|
||||
+ (UIColor *)buttonDisabledBlueText;
|
||||
+ (UIColor *)blackOpaque;
|
||||
+ (UIColor *)bookingBackground;
|
||||
+ (UIColor *)opentableBackground;
|
||||
+ (UIColor *)transparentGreen;
|
||||
+ (UIColor *)speedLimitRed;
|
||||
+ (UIColor *)speedLimitGreen;
|
||||
+ (UIColor *)speedLimitWhite;
|
||||
+ (UIColor *)speedLimitLightGray;
|
||||
+ (UIColor *)speedLimitDarkGray;
|
||||
+ (UIColor *)carplayPlaceholderBackground;
|
||||
|
||||
+ (UIColor *)colorWithName:(NSString *)colorName;
|
||||
+ (UIColor *)colorFromHexString:(NSString *)hexString;
|
||||
|
||||
+ (void)setNightMode:(BOOL)mode;
|
||||
+ (BOOL)isNightMode;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
179
iphone/Maps/Categories/UIColor+MapsMeColor.m
Normal file
179
iphone/Maps/Categories/UIColor+MapsMeColor.m
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
#import "UIColorRoutines.h"
|
||||
#import "UIColor+MapsMeColor.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
static BOOL isNightMode = NO;
|
||||
|
||||
@implementation UIColor (MapsMeColor)
|
||||
|
||||
// hex string without #
|
||||
+ (UIColor *)colorFromHexString:(NSString *)hexString
|
||||
{
|
||||
unsigned rgbValue = 0;
|
||||
NSScanner *scanner = [NSScanner scannerWithString:hexString];
|
||||
[scanner setScanLocation:0];
|
||||
[scanner scanHexInt:&rgbValue];
|
||||
return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 blue:(rgbValue & 0xFF)/255.0 alpha:1.0];
|
||||
}
|
||||
|
||||
+ (void)setNightMode:(BOOL)mode
|
||||
{
|
||||
isNightMode = mode;
|
||||
}
|
||||
|
||||
+ (BOOL)isNightMode
|
||||
{
|
||||
return isNightMode;
|
||||
}
|
||||
|
||||
// Green color
|
||||
+ (UIColor *)primary
|
||||
{
|
||||
return StyleManager.shared.theme.colors.primary;
|
||||
}
|
||||
|
||||
// Use for opaque fullscreen
|
||||
+ (UIColor *)fadeBackground
|
||||
{
|
||||
return [UIColor colorWithWhite:0. alpha:alpha80];
|
||||
}
|
||||
|
||||
// Background color && press color
|
||||
+ (UIColor *)pressBackground
|
||||
{
|
||||
return StyleManager.shared.theme.colors.pressBackground;
|
||||
}
|
||||
// Red color (use for status closed in place page)
|
||||
+ (UIColor *)red
|
||||
{
|
||||
return StyleManager.shared.theme.colors.red;
|
||||
}
|
||||
|
||||
// Blue color (use for links and phone numbers)
|
||||
+ (UIColor *)linkBlue
|
||||
{
|
||||
return StyleManager.shared.theme.colors.linkBlue;
|
||||
}
|
||||
|
||||
+ (UIColor *)linkBlueHighlighted
|
||||
{
|
||||
return StyleManager.shared.theme.colors.linkBlueHighlighted;
|
||||
}
|
||||
|
||||
+ (UIColor *)linkBlueDark
|
||||
{
|
||||
return StyleManager.shared.theme.colors.linkBlueDark;
|
||||
}
|
||||
+ (UIColor *)buttonRed
|
||||
{
|
||||
return StyleManager.shared.theme.colors.buttonRed;
|
||||
}
|
||||
+ (UIColor *)black
|
||||
{
|
||||
return StyleManager.shared.theme.colors.black;
|
||||
}
|
||||
|
||||
+ (UIColor *)blackPrimaryText
|
||||
{
|
||||
return StyleManager.shared.theme.colors.blackPrimaryText;
|
||||
}
|
||||
|
||||
+ (UIColor *)blackSecondaryText
|
||||
{
|
||||
return StyleManager.shared.theme.colors.blackSecondaryText;
|
||||
}
|
||||
|
||||
+ (UIColor *)blackHintText
|
||||
{
|
||||
return StyleManager.shared.theme.colors.blackHintText;
|
||||
}
|
||||
|
||||
+ (UIColor *)blackDividers
|
||||
{
|
||||
return StyleManager.shared.theme.colors.blackDividers;
|
||||
}
|
||||
|
||||
+ (UIColor *)white
|
||||
{
|
||||
return StyleManager.shared.theme.colors.white;
|
||||
}
|
||||
|
||||
+ (UIColor *)whitePrimaryText
|
||||
{
|
||||
return [UIColor colorWithWhite:1. alpha:alpha87];
|
||||
}
|
||||
|
||||
+ (UIColor *)whitePrimaryTextHighlighted
|
||||
{
|
||||
// use only for highlighted colors!
|
||||
return [UIColor colorWithWhite:1. alpha:alpha30];
|
||||
}
|
||||
|
||||
+ (UIColor *)whiteHintText
|
||||
{
|
||||
return StyleManager.shared.theme.colors.whiteHintText;
|
||||
}
|
||||
|
||||
+ (UIColor *)buttonDisabledBlueText
|
||||
{
|
||||
return StyleManager.shared.theme.colors.buttonDisabledBlueText;
|
||||
}
|
||||
|
||||
+ (UIColor *)buttonHighlightedBlueText
|
||||
{
|
||||
return [UIColor colorWithRed:scaled(3.) green:scaled(122.) blue:scaled(255.) alpha:alpha54];
|
||||
}
|
||||
|
||||
+ (UIColor *)blackOpaque
|
||||
{
|
||||
return StyleManager.shared.theme.colors.blackOpaque;
|
||||
}
|
||||
|
||||
+ (UIColor *)carplayPlaceholderBackground
|
||||
{
|
||||
return StyleManager.shared.theme.colors.carplayPlaceholderBackground;
|
||||
}
|
||||
|
||||
+ (UIColor *)bookingBackground
|
||||
{
|
||||
return [UIColor colorWithRed:scaled(25.) green:scaled(69.) blue:scaled(125.) alpha:alpha100];
|
||||
}
|
||||
|
||||
+ (UIColor *)opentableBackground
|
||||
{
|
||||
return [UIColor colorWithRed:scaled(218.) green:scaled(55) blue:scaled(67) alpha:alpha100];
|
||||
}
|
||||
|
||||
+ (UIColor *)transparentGreen
|
||||
{
|
||||
return [UIColor colorWithRed:scaled(233) green:scaled(244) blue:scaled(233) alpha:alpha26];
|
||||
}
|
||||
|
||||
+ (UIColor *)colorWithName:(NSString *)colorName
|
||||
{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
return [[UIColor class] performSelector:NSSelectorFromString(colorName)];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
+ (UIColor *)speedLimitRed {
|
||||
return [UIColor colorWithRed:scaled(224) green:scaled(31) blue:scaled(31) alpha:alpha100];
|
||||
}
|
||||
|
||||
+ (UIColor *)speedLimitGreen {
|
||||
return [UIColor colorWithRed:scaled(1) green:scaled(104) blue:scaled(44) alpha:alpha100];
|
||||
}
|
||||
|
||||
+ (UIColor *)speedLimitWhite {
|
||||
return [UIColor colorWithRed:scaled(255) green:scaled(255) blue:scaled(255) alpha:alpha80];
|
||||
}
|
||||
|
||||
+ (UIColor *)speedLimitLightGray {
|
||||
return [UIColor colorWithRed:scaled(0) green:scaled(0) blue:scaled(0) alpha:alpha20];
|
||||
}
|
||||
|
||||
+ (UIColor *)speedLimitDarkGray {
|
||||
return [UIColor colorWithRed:scaled(51) green:scaled(51) blue:scaled(50) alpha:alpha100];
|
||||
}
|
||||
@end
|
||||
63
iphone/Maps/Categories/UIColor+Modifications.swift
Normal file
63
iphone/Maps/Categories/UIColor+Modifications.swift
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
extension UIColor {
|
||||
func blending(with color: UIColor) -> UIColor {
|
||||
var bgR: CGFloat = 0
|
||||
var bgG: CGFloat = 0
|
||||
var bgB: CGFloat = 0
|
||||
var bgA: CGFloat = 0
|
||||
|
||||
var fgR: CGFloat = 0
|
||||
var fgG: CGFloat = 0
|
||||
var fgB: CGFloat = 0
|
||||
var fgA: CGFloat = 0
|
||||
|
||||
self.getRed(&bgR, green: &bgG, blue: &bgB, alpha: &bgA)
|
||||
color.getRed(&fgR, green: &fgG, blue: &fgB, alpha: &fgA)
|
||||
|
||||
let r = fgA * fgR + (1 - fgA) * bgR
|
||||
let g = fgA * fgG + (1 - fgA) * bgG
|
||||
let b = fgA * fgB + (1 - fgA) * bgB
|
||||
|
||||
return UIColor(red: r, green: g, blue: b, alpha: bgA)
|
||||
}
|
||||
|
||||
func lighter(percent: CGFloat) -> UIColor {
|
||||
return colorWithBrightnessFactor(factor: 1 + percent)
|
||||
}
|
||||
|
||||
func darker(percent: CGFloat) -> UIColor {
|
||||
return colorWithBrightnessFactor(factor: 1 - percent)
|
||||
}
|
||||
|
||||
private func colorWithBrightnessFactor(factor: CGFloat) -> UIColor {
|
||||
var hue: CGFloat = 0
|
||||
var saturation: CGFloat = 0
|
||||
var brightness: CGFloat = 0
|
||||
var alpha: CGFloat = 0
|
||||
|
||||
if getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) {
|
||||
return UIColor(hue: hue, saturation: saturation, brightness: brightness * factor, alpha: alpha)
|
||||
} else {
|
||||
return self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension UIColor {
|
||||
func components() -> (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)? {
|
||||
var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
|
||||
return getRed(&r, green: &g, blue: &b, alpha: &a) ? (r,g,b,a) : nil
|
||||
}
|
||||
|
||||
static func intermediateColor( color1: UIColor, color2: UIColor, _ scale: CGFloat) -> UIColor? {
|
||||
guard let comp1 = color1.components(),
|
||||
let comp2 = color2.components() else {
|
||||
return nil
|
||||
}
|
||||
let scale = min(1, max(0, scale))
|
||||
let r = comp1.red + (comp2.red - comp1.red) * scale
|
||||
let g = comp1.green + (comp2.green - comp1.green) * scale
|
||||
let b = comp1.blue + (comp2.blue - comp1.blue) * scale
|
||||
let a = comp1.alpha + (comp2.alpha - comp1.alpha) * scale
|
||||
return UIColor(red: r, green: g, blue: b, alpha: a)
|
||||
}
|
||||
}
|
||||
18
iphone/Maps/Categories/UIColor+PartnerColor.h
Normal file
18
iphone/Maps/Categories/UIColor+PartnerColor.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// This file is autogenerated
|
||||
@interface UIColor (PartnerColor)
|
||||
|
||||
+ (UIColor *)partner1Background;
|
||||
+ (UIColor *)partner1TextColor;
|
||||
+ (UIColor *)partner2Background;
|
||||
+ (UIColor *)partner2TextColor;
|
||||
+ (UIColor *)partner3Background;
|
||||
+ (UIColor *)partner3TextColor;
|
||||
+ (UIColor *)partner18Background;
|
||||
+ (UIColor *)partner18TextColor;
|
||||
+ (UIColor *)partner19Background;
|
||||
+ (UIColor *)partner19TextColor;
|
||||
+ (UIColor *)partner20Background;
|
||||
+ (UIColor *)partner20TextColor;
|
||||
|
||||
@end
|
||||
|
||||
56
iphone/Maps/Categories/UIColor+PartnerColor.m
Normal file
56
iphone/Maps/Categories/UIColor+PartnerColor.m
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
// This file is autogenerated
|
||||
#import "UIColorRoutines.h"
|
||||
#import "UIColor+PartnerColor.h"
|
||||
|
||||
@implementation UIColor (PartnerColor)
|
||||
+ (UIColor *)partner1Background
|
||||
{
|
||||
return [UIColor colorWithRed:scaled(48) green:scaled(52) blue:scaled(56) alpha:1];
|
||||
}
|
||||
+ (UIColor *)partner1TextColor
|
||||
{
|
||||
return [UIColor colorWithRed:scaled(255) green:scaled(255) blue:scaled(255) alpha:1];
|
||||
}
|
||||
+ (UIColor *)partner2Background
|
||||
{
|
||||
return [UIColor colorWithRed:scaled(215) green:scaled(215) blue:scaled(215) alpha:1];
|
||||
}
|
||||
+ (UIColor *)partner2TextColor
|
||||
{
|
||||
return [UIColor colorWithRed:scaled(33) green:scaled(33) blue:scaled(33) alpha:1];
|
||||
}
|
||||
+ (UIColor *)partner3Background
|
||||
{
|
||||
return [UIColor colorWithRed:scaled(230) green:scaled(23) blue:scaled(23) alpha:1];
|
||||
}
|
||||
+ (UIColor *)partner3TextColor
|
||||
{
|
||||
return [UIColor colorWithRed:scaled(255) green:scaled(255) blue:scaled(255) alpha:1];
|
||||
}
|
||||
+ (UIColor *)partner18Background
|
||||
{
|
||||
return [UIColor colorWithRed:scaled(0) green:scaled(185) blue:scaled(86) alpha:100];
|
||||
}
|
||||
+ (UIColor *)partner18TextColor
|
||||
{
|
||||
return [UIColor colorWithRed:scaled(255) green:scaled(255) blue:scaled(255) alpha:100];
|
||||
}
|
||||
+ (UIColor *)partner19Background
|
||||
{
|
||||
return [UIColor colorWithRed:scaled(87) green:scaled(26) blue:scaled(140) alpha:100];
|
||||
}
|
||||
+ (UIColor *)partner19TextColor
|
||||
{
|
||||
return [UIColor colorWithRed:scaled(255) green:scaled(255) blue:scaled(255) alpha:100];
|
||||
}
|
||||
+ (UIColor *)partner20Background
|
||||
{
|
||||
return [UIColor colorWithRed:scaled(87) green:scaled(26) blue:scaled(140) alpha:100];
|
||||
}
|
||||
+ (UIColor *)partner20TextColor
|
||||
{
|
||||
return [UIColor colorWithRed:scaled(255) green:scaled(255) blue:scaled(255) alpha:100];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
16
iphone/Maps/Categories/UIColorRoutines.h
Normal file
16
iphone/Maps/Categories/UIColorRoutines.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
static CGFloat const alpha04 = 0.04;
|
||||
static CGFloat const alpha12 = 0.12;
|
||||
static CGFloat const alpha20 = 0.20;
|
||||
static CGFloat const alpha26 = 0.26;
|
||||
static CGFloat const alpha30 = 0.3;
|
||||
static CGFloat const alpha32 = 0.32;
|
||||
static CGFloat const alpha36 = 0.36;
|
||||
static CGFloat const alpha40 = 0.4;
|
||||
static CGFloat const alpha54 = 0.54;
|
||||
static CGFloat const alpha70 = 0.7;
|
||||
static CGFloat const alpha80 = 0.8;
|
||||
static CGFloat const alpha87 = 0.87;
|
||||
static CGFloat const alpha90 = 0.9;
|
||||
static CGFloat const alpha100 = 1.;
|
||||
|
||||
static inline CGFloat scaled(CGFloat f) { return f / 255.; };
|
||||
29
iphone/Maps/Categories/UIFont+MapsMeFonts.h
Normal file
29
iphone/Maps/Categories/UIFont+MapsMeFonts.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
NS_ASSUME_NONNULL_BEGIN
|
||||
@interface UIFont (MapsMeFonts)
|
||||
|
||||
+ (UIFont *)regular10;
|
||||
+ (UIFont *)regular12;
|
||||
+ (UIFont *)regular13;
|
||||
+ (UIFont *)regular14;
|
||||
+ (UIFont *)regular16;
|
||||
+ (UIFont *)regular17;
|
||||
+ (UIFont *)regular18;
|
||||
+ (UIFont *)regular24;
|
||||
+ (UIFont *)regular32;
|
||||
+ (UIFont *)regular52;
|
||||
+ (UIFont *)medium10;
|
||||
+ (UIFont *)medium14;
|
||||
+ (UIFont *)medium16;
|
||||
+ (UIFont *)medium17;
|
||||
+ (UIFont *)light12;
|
||||
+ (UIFont *)bold12;
|
||||
+ (UIFont *)bold14;
|
||||
+ (UIFont *)bold16;
|
||||
+ (UIFont *)bold17;
|
||||
+ (UIFont *)bold24;
|
||||
+ (UIFont *)bold28;
|
||||
+ (UIFont *)bold36;
|
||||
+ (UIFont *)semibold16;
|
||||
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
||||
27
iphone/Maps/Categories/UIFont+MapsMeFonts.m
Normal file
27
iphone/Maps/Categories/UIFont+MapsMeFonts.m
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
@implementation UIFont (MapsMeFonts)
|
||||
|
||||
+ (UIFont *)regular10 { return [UIFont systemFontOfSize:10]; }
|
||||
+ (UIFont *)regular12 { return [UIFont systemFontOfSize:12]; }
|
||||
+ (UIFont *)regular13 { return [UIFont systemFontOfSize:13]; }
|
||||
+ (UIFont *)regular14 { return [UIFont systemFontOfSize:14]; }
|
||||
+ (UIFont *)regular16 { return [UIFont systemFontOfSize:16]; }
|
||||
+ (UIFont *)regular17 { return [UIFont systemFontOfSize:17]; }
|
||||
+ (UIFont *)regular18 { return [UIFont systemFontOfSize:18]; }
|
||||
+ (UIFont *)regular24 { return [UIFont systemFontOfSize:24]; }
|
||||
+ (UIFont *)regular32 { return [UIFont systemFontOfSize:32]; }
|
||||
+ (UIFont *)regular52 { return [UIFont systemFontOfSize:52]; }
|
||||
+ (UIFont *)medium10 { return [UIFont systemFontOfSize:10 weight:UIFontWeightMedium]; }
|
||||
+ (UIFont *)medium14 { return [UIFont systemFontOfSize:14 weight:UIFontWeightMedium]; }
|
||||
+ (UIFont *)medium16 { return [UIFont systemFontOfSize:16 weight:UIFontWeightMedium]; }
|
||||
+ (UIFont *)medium17 { return [UIFont systemFontOfSize:17 weight:UIFontWeightMedium]; }
|
||||
+ (UIFont *)light12 { return [UIFont systemFontOfSize:12 weight:UIFontWeightLight]; }
|
||||
+ (UIFont *)bold12 { return [UIFont boldSystemFontOfSize:12]; }
|
||||
+ (UIFont *)bold14 { return [UIFont boldSystemFontOfSize:14]; }
|
||||
+ (UIFont *)bold16 { return [UIFont boldSystemFontOfSize:16]; }
|
||||
+ (UIFont *)bold17 { return [UIFont boldSystemFontOfSize:17]; }
|
||||
+ (UIFont *)bold24 { return [UIFont boldSystemFontOfSize:24]; }
|
||||
+ (UIFont *)bold28 { return [UIFont boldSystemFontOfSize:28]; }
|
||||
+ (UIFont *)bold36 { return [UIFont boldSystemFontOfSize:26]; }
|
||||
+ (UIFont *)semibold16 { return [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold]; }
|
||||
|
||||
@end
|
||||
9
iphone/Maps/Categories/UIImage+FilledWithColor.swift
Normal file
9
iphone/Maps/Categories/UIImage+FilledWithColor.swift
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
extension UIImage {
|
||||
static func filled(with color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) -> UIImage {
|
||||
let renderer = UIGraphicsImageRenderer(size: size)
|
||||
return renderer.image { context in
|
||||
color.setFill()
|
||||
context.fill(CGRect(origin: .zero, size: size))
|
||||
}
|
||||
}
|
||||
}
|
||||
5
iphone/Maps/Categories/UIImage+RGBAData.h
Normal file
5
iphone/Maps/Categories/UIImage+RGBAData.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
@interface UIImage (RGBAData)
|
||||
|
||||
+ (UIImage *)imageWithRGBAData:(NSData *)data width:(size_t)width height:(size_t)height;
|
||||
|
||||
@end
|
||||
36
iphone/Maps/Categories/UIImage+RGBAData.m
Normal file
36
iphone/Maps/Categories/UIImage+RGBAData.m
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#import "UIImage+RGBAData.h"
|
||||
|
||||
static void releaseCallback(void *info, const void *data, size_t size) {
|
||||
CFRelease((CFDataRef)info);
|
||||
}
|
||||
|
||||
@implementation UIImage (RGBAData)
|
||||
|
||||
+ (UIImage *)imageWithRGBAData:(NSData *)data width:(size_t)width height:(size_t)height {
|
||||
size_t bytesPerPixel = 4;
|
||||
size_t bitsPerComponent = 8;
|
||||
size_t bitsPerPixel = bitsPerComponent * bytesPerPixel;
|
||||
size_t bytesPerRow = bytesPerPixel * width;
|
||||
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaLast;
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CFDataRef cfData = (__bridge_retained CFDataRef)data;
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithData((void *)cfData,
|
||||
data.bytes,
|
||||
height * bytesPerRow,
|
||||
releaseCallback);
|
||||
|
||||
CGImageRef cgImage = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow,
|
||||
colorSpace, bitmapInfo, provider,
|
||||
NULL, YES, kCGRenderingIntentDefault);
|
||||
|
||||
UIImage *image = [UIImage imageWithCGImage:cgImage];
|
||||
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
CGDataProviderRelease(provider);
|
||||
CGImageRelease(cgImage);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@end
|
||||
17
iphone/Maps/Categories/UIImageView+Coloring.h
Normal file
17
iphone/Maps/Categories/UIImageView+Coloring.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
typedef NS_ENUM(NSUInteger, MWMImageColoring) {
|
||||
MWMImageColoringOther,
|
||||
MWMImageColoringBlue,
|
||||
MWMImageColoringBlack,
|
||||
MWMImageColoringWhite,
|
||||
MWMImageColoringGray,
|
||||
MWMImageColoringSeparator
|
||||
};
|
||||
|
||||
@interface UIImageView (Coloring)
|
||||
|
||||
@property(nonatomic) MWMImageColoring mwm_coloring;
|
||||
@property(copy, nonatomic) NSString * mwm_name;
|
||||
|
||||
- (void)changeColoringToOpposite;
|
||||
|
||||
@end
|
||||
101
iphone/Maps/Categories/UIImageView+Coloring.m
Normal file
101
iphone/Maps/Categories/UIImageView+Coloring.m
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#import "UIImageView+Coloring.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@implementation UIImageView (Coloring)
|
||||
|
||||
- (void)setMwm_name:(NSString *)mwm_name
|
||||
{
|
||||
objc_setAssociatedObject(self, @selector(mwm_name), mwm_name, OBJC_ASSOCIATION_COPY_NONATOMIC);
|
||||
self.image =
|
||||
[UIImage imageNamed:[NSString stringWithFormat:@"%@_%@", mwm_name,
|
||||
[UIColor isNightMode] ? @"dark" : @"light"]];
|
||||
}
|
||||
|
||||
- (NSString *)mwm_name { return objc_getAssociatedObject(self, @selector(mwm_name)); }
|
||||
- (void)setMwm_coloring:(MWMImageColoring)mwm_coloring
|
||||
{
|
||||
objc_setAssociatedObject(self, @selector(mwm_coloring), @(mwm_coloring),
|
||||
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
if (mwm_coloring == MWMImageColoringOther)
|
||||
return;
|
||||
[self applyColoring];
|
||||
}
|
||||
|
||||
- (MWMImageColoring)mwm_coloring
|
||||
{
|
||||
return [objc_getAssociatedObject(self, @selector(mwm_coloring)) integerValue];
|
||||
}
|
||||
|
||||
- (void)applyColoring
|
||||
{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
self.tintColor = [[UIColor class] performSelector:self.coloringSelector];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
- (void)changeColoringToOpposite
|
||||
{
|
||||
if (self.mwm_coloring == MWMImageColoringOther)
|
||||
{
|
||||
if (self.mwm_name)
|
||||
self.image = [UIImage
|
||||
imageNamed:[NSString stringWithFormat:@"%@_%@", self.mwm_name,
|
||||
[UIColor isNightMode] ? @"dark" : @"light"]];
|
||||
return;
|
||||
}
|
||||
[self applyColoring];
|
||||
}
|
||||
|
||||
- (SEL)coloringSelector
|
||||
{
|
||||
switch (self.mwm_coloring)
|
||||
{
|
||||
case MWMImageColoringWhite: return @selector(white);
|
||||
case MWMImageColoringBlack: return @selector(blackSecondaryText);
|
||||
case MWMImageColoringBlue: return @selector(linkBlue);
|
||||
case MWMImageColoringGray: return @selector(blackHintText);
|
||||
case MWMImageColoringOther: return @selector(white);
|
||||
case MWMImageColoringSeparator: return @selector(blackDividers);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setHighlighted:(BOOL)highlighted
|
||||
{
|
||||
switch (self.mwm_coloring)
|
||||
{
|
||||
case MWMImageColoringWhite:
|
||||
self.tintColor = highlighted ? [UIColor whiteHintText] : [UIColor white];
|
||||
break;
|
||||
case MWMImageColoringBlack:
|
||||
self.tintColor = highlighted ? [UIColor blackHintText] : [UIColor blackSecondaryText];
|
||||
break;
|
||||
case MWMImageColoringGray:
|
||||
self.tintColor = highlighted ? [UIColor blackSecondaryText] : [UIColor blackHintText];
|
||||
break;
|
||||
case MWMImageColoringOther:
|
||||
case MWMImageColoringBlue:
|
||||
case MWMImageColoringSeparator: break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setColoring:(NSString *)coloring
|
||||
{
|
||||
if ([coloring isEqualToString:@"MWMBlue"])
|
||||
self.mwm_coloring = MWMImageColoringBlue;
|
||||
else if ([coloring isEqualToString:@"MWMBlack"])
|
||||
self.mwm_coloring = MWMImageColoringBlack;
|
||||
else if ([coloring isEqualToString:@"MWMOther"])
|
||||
self.mwm_coloring = MWMImageColoringOther;
|
||||
else if ([coloring isEqualToString:@"MWMGray"])
|
||||
self.mwm_coloring = MWMImageColoringGray;
|
||||
else if ([coloring isEqualToString:@"MWMSeparator"])
|
||||
self.mwm_coloring = MWMImageColoringSeparator;
|
||||
else if ([coloring isEqualToString:@"MWMWhite"])
|
||||
self.mwm_coloring = MWMImageColoringWhite;
|
||||
else
|
||||
NSAssert(false, @"Incorrect UIImageView's coloring");
|
||||
}
|
||||
|
||||
@end
|
||||
89
iphone/Maps/Categories/UIKitCategories.h
Normal file
89
iphone/Maps/Categories/UIKitCategories.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#import <CoreApi/MWMTypes.h>
|
||||
|
||||
static inline CGPoint SubtractCGPoint(CGPoint p1, CGPoint p2)
|
||||
{
|
||||
return CGPointMake(p1.x - p2.x, p1.y - p2.y);
|
||||
}
|
||||
|
||||
static inline CGPoint AddCGPoint(CGPoint p1, CGPoint p2)
|
||||
{
|
||||
return CGPointMake(p1.x + p2.x, p1.y + p2.y);
|
||||
}
|
||||
|
||||
static inline CGPoint MultiplyCGPoint(CGPoint point, CGFloat multiplier)
|
||||
{
|
||||
return CGPointMake(point.x * multiplier, point.y * multiplier);
|
||||
}
|
||||
|
||||
static inline CGFloat LengthCGPoint(CGPoint point)
|
||||
{
|
||||
return (CGFloat)sqrt(point.x * point.x + point.y * point.y);
|
||||
}
|
||||
|
||||
@interface NSObject (Optimized)
|
||||
|
||||
+ (NSString *)className;
|
||||
- (void)performAfterDelay:(NSTimeInterval)delayInSec block:(MWMVoidBlock)block;
|
||||
|
||||
@end
|
||||
|
||||
@interface UIView (Coordinates)
|
||||
|
||||
@property (nonatomic) CGFloat minX;
|
||||
@property (nonatomic) CGFloat minY;
|
||||
@property (nonatomic) CGFloat midX;
|
||||
@property (nonatomic) CGFloat midY;
|
||||
@property (nonatomic) CGFloat maxX;
|
||||
@property (nonatomic) CGFloat maxY;
|
||||
@property (nonatomic) CGPoint origin;
|
||||
@property (nonatomic) CGFloat width;
|
||||
@property (nonatomic) CGFloat height;
|
||||
@property (nonatomic) CGSize size;
|
||||
|
||||
- (void)sizeToIntegralFit;
|
||||
|
||||
@end
|
||||
|
||||
@interface UIView (Refresh)
|
||||
|
||||
@end
|
||||
|
||||
@interface UIApplication (URLs)
|
||||
|
||||
- (void)rateApp;
|
||||
|
||||
@end
|
||||
|
||||
@interface SolidTouchView : UIView
|
||||
|
||||
@end
|
||||
|
||||
@interface SolidTouchImageView : UIImageView
|
||||
|
||||
@end
|
||||
|
||||
@interface UIViewController (Safari)
|
||||
|
||||
/// Open URL internally in SFSafariViewController. Returns NO (false) if the url id invalid.
|
||||
- (BOOL)openUrl:(NSString *)urlString;
|
||||
|
||||
/// Open URL externally in installed application (or in Safari if there are no appropriate application) if possible or internally in SFSafariViewController. Returns NO (false) if the url id invalid.
|
||||
///
|
||||
/// @param urlString: URL string to open.
|
||||
/// @param externally: If true, try to open URL in installed application or in Safari, otherwise open in internal browser without leaving the app.
|
||||
- (BOOL)openUrl:(NSString *)urlString externally:(BOOL)externally;
|
||||
|
||||
/// Open URL externally in installed application (or in Safari if there are no appropriate application) if possible or internally in SFSafariViewController. Returns NO (false) if the url id invalid.
|
||||
///
|
||||
/// @param urlString: URL string to open.
|
||||
/// @param externally: If true, try to open URL in installed application or in Safari, otherwise open in internal browser without leaving the app.
|
||||
/// @param skipEncoding: If true, extra URL encoding will be skipped
|
||||
- (BOOL)openUrl:(NSString *)urlString externally:(BOOL)externally skipEncoding:(BOOL)skipEncoding;
|
||||
|
||||
@end
|
||||
|
||||
@interface UIImage (ImageWithColor)
|
||||
|
||||
+ (UIImage *)imageWithColor:(UIColor *)color;
|
||||
|
||||
@end
|
||||
249
iphone/Maps/Categories/UIKitCategories.m
Normal file
249
iphone/Maps/Categories/UIKitCategories.m
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
#import "UIKitCategories.h"
|
||||
#import "UIButton+RuntimeAttributes.h"
|
||||
#import "UIImageView+Coloring.h"
|
||||
|
||||
#import <SafariServices/SafariServices.h>
|
||||
|
||||
@implementation NSObject (Optimized)
|
||||
|
||||
+ (NSString *)className { return NSStringFromClass(self); }
|
||||
- (void)performAfterDelay:(NSTimeInterval)delayInSec block:(MWMVoidBlock)block
|
||||
{
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSec * NSEC_PER_SEC)),
|
||||
dispatch_get_main_queue(), ^{
|
||||
block();
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation UIView (Coordinates)
|
||||
|
||||
- (void)setMidX:(CGFloat)midX { self.center = CGPointMake(midX, self.center.y); }
|
||||
- (CGFloat)midX { return self.center.x; }
|
||||
- (void)setMidY:(CGFloat)midY { self.center = CGPointMake(self.center.x, midY); }
|
||||
- (CGFloat)midY { return self.center.y; }
|
||||
- (void)setOrigin:(CGPoint)origin
|
||||
{
|
||||
self.frame = CGRectMake(origin.x, origin.y, self.frame.size.width, self.frame.size.height);
|
||||
}
|
||||
|
||||
- (CGPoint)origin { return self.frame.origin; }
|
||||
- (void)setMinX:(CGFloat)minX
|
||||
{
|
||||
self.frame = CGRectMake(minX, self.frame.origin.y, self.frame.size.width, self.frame.size.height);
|
||||
}
|
||||
|
||||
- (CGFloat)minX { return self.frame.origin.x; }
|
||||
- (void)setMinY:(CGFloat)minY
|
||||
{
|
||||
self.frame = CGRectMake(self.frame.origin.x, minY, self.frame.size.width, self.frame.size.height);
|
||||
}
|
||||
|
||||
- (CGFloat)minY { return self.frame.origin.y; }
|
||||
- (void)setMaxX:(CGFloat)maxX
|
||||
{
|
||||
self.frame = CGRectMake(maxX - self.frame.size.width, self.frame.origin.y, self.frame.size.width,
|
||||
self.frame.size.height);
|
||||
}
|
||||
|
||||
- (CGFloat)maxX { return self.frame.origin.x + self.frame.size.width; }
|
||||
- (void)setMaxY:(CGFloat)maxY
|
||||
{
|
||||
self.frame = CGRectMake(self.frame.origin.x, maxY - self.frame.size.height, self.frame.size.width,
|
||||
self.frame.size.height);
|
||||
}
|
||||
|
||||
- (CGFloat)maxY { return self.frame.origin.y + self.frame.size.height; }
|
||||
- (void)setWidth:(CGFloat)width
|
||||
{
|
||||
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, width, self.frame.size.height);
|
||||
}
|
||||
|
||||
- (CGFloat)width { return self.frame.size.width; }
|
||||
- (void)setHeight:(CGFloat)height
|
||||
{
|
||||
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, height);
|
||||
}
|
||||
|
||||
- (CGFloat)height { return self.frame.size.height; }
|
||||
- (CGSize)size { return self.frame.size; }
|
||||
- (void)setSize:(CGSize)size
|
||||
{
|
||||
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, size.width, size.height);
|
||||
}
|
||||
|
||||
- (void)sizeToIntegralFit
|
||||
{
|
||||
[self sizeToFit];
|
||||
self.frame = CGRectIntegral(self.frame);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation UIApplication (URLs)
|
||||
|
||||
- (void)rateApp
|
||||
{
|
||||
NSString * urlString = @"https://apps.apple.com/app/comaps/id6747180809?action=write-review";
|
||||
NSURL * url = [NSURL URLWithString:urlString];
|
||||
[self openURL:url options:@{} completionHandler:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation SolidTouchView
|
||||
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {}
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {}
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {}
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {}
|
||||
@end
|
||||
|
||||
@implementation UIView (Refresh)
|
||||
|
||||
@end
|
||||
|
||||
@implementation UITableViewCell (Refresh)
|
||||
|
||||
@end
|
||||
|
||||
@implementation UINavigationBar (Refresh)
|
||||
|
||||
@end
|
||||
|
||||
@implementation UILabel (Refresh)
|
||||
|
||||
@end
|
||||
|
||||
@implementation UISlider (Refresh)
|
||||
|
||||
@end
|
||||
|
||||
@implementation UISwitch (Refresh)
|
||||
|
||||
@end
|
||||
|
||||
@implementation UIButton (Refresh)
|
||||
|
||||
@end
|
||||
|
||||
@implementation UITextView (Refresh)
|
||||
|
||||
@end
|
||||
|
||||
@implementation UIImageView (Refresh)
|
||||
|
||||
@end
|
||||
|
||||
@implementation SolidTouchImageView
|
||||
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {}
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {}
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {}
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {}
|
||||
@end
|
||||
|
||||
@implementation UINavigationController (Autorotate)
|
||||
|
||||
- (BOOL)shouldAutorotate { return [self.viewControllers.lastObject shouldAutorotate]; }
|
||||
@end
|
||||
|
||||
@implementation UIViewController (Autorotate)
|
||||
|
||||
- (NSUInteger)supportedInterfaceOrientations { return UIInterfaceOrientationMaskAll; }
|
||||
@end
|
||||
|
||||
@interface UIViewController (SafariDelegateImpl)<SFSafariViewControllerDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation UIViewController (SafariDelegateImpl)
|
||||
|
||||
- (void)safariViewControllerDidFinish:(SFSafariViewController *)controller
|
||||
{
|
||||
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation UIViewController (Safari)
|
||||
|
||||
- (BOOL)openUrl:(NSString * _Nonnull)urlString
|
||||
{
|
||||
return [self openUrl:urlString externally:NO];
|
||||
}
|
||||
|
||||
- (BOOL)openUrl:(NSString *)urlString externally:(BOOL)externally
|
||||
{
|
||||
return [self openUrl:urlString externally:externally skipEncoding:NO];
|
||||
}
|
||||
|
||||
- (BOOL)openUrl:(NSString *)urlString externally:(BOOL)externally skipEncoding:(BOOL)skipEncoding
|
||||
{
|
||||
NSString * encoded = urlString;
|
||||
if (!skipEncoding && ![urlString canBeConvertedToEncoding:NSASCIIStringEncoding]) {
|
||||
// TODO: This is a temporary workaround to open cyrillic/non-ASCII URLs.
|
||||
// URLs in OSM are stored in UTF-8. NSURL constructor documentation says:
|
||||
// > Must be a URL that conforms to RFC 2396. This method parses URLString according to RFCs 1738 and 1808.
|
||||
// The right way to encode the URL string should be:
|
||||
// 1. Split the (non-ASCII) string into components (host, path, query, fragment, etc.)
|
||||
// 2. Encode each component separately (they have different allowed characters).
|
||||
// 3. Merge them back into the string and create NSURL.
|
||||
NSMutableCharacterSet * charset = [[NSMutableCharacterSet alloc] init];
|
||||
[charset formUnionWithCharacterSet:NSCharacterSet.URLHostAllowedCharacterSet];
|
||||
[charset formUnionWithCharacterSet:NSCharacterSet.URLPathAllowedCharacterSet];
|
||||
[charset formUnionWithCharacterSet:NSCharacterSet.URLQueryAllowedCharacterSet];
|
||||
[charset formUnionWithCharacterSet:NSCharacterSet.URLFragmentAllowedCharacterSet];
|
||||
[charset addCharactersInString:@"#;/?:@&=+$,"];
|
||||
encoded = [urlString stringByAddingPercentEncodingWithAllowedCharacters:charset];
|
||||
}
|
||||
|
||||
// Matrix has an url with two hashes which doesn't work for NSURL and NSURLComponent.
|
||||
NSRange const matrixUrl = [encoded rangeOfString:@"#/#"];
|
||||
if (matrixUrl.location != NSNotFound)
|
||||
encoded = [encoded stringByReplacingOccurrencesOfString:@"#/#" withString:@"#/%23"];
|
||||
NSURLComponents * urlc = [NSURLComponents componentsWithString:encoded];
|
||||
if (!urlc)
|
||||
{
|
||||
NSAssert(false, @"Invalid URL %@", urlString);
|
||||
return NO;
|
||||
}
|
||||
// Some links in OSM do not have a scheme: www.some.link
|
||||
if (!urlc.scheme)
|
||||
urlc.scheme = @"http";
|
||||
|
||||
NSURL * url = urlc.URL;
|
||||
if (externally && [UIApplication.sharedApplication canOpenURL:url])
|
||||
{
|
||||
[UIApplication.sharedApplication openURL:url options:@{} completionHandler:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
SFSafariViewController * svc = [[SFSafariViewController alloc] initWithURL:url];
|
||||
svc.delegate = self;
|
||||
[self.navigationController presentViewController:svc animated:YES completion:nil];
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation UIImage (ImageWithColor)
|
||||
|
||||
+ (UIImage *)imageWithColor:(UIColor *)color
|
||||
{
|
||||
CGRect rect = CGRectMake(0.0, 0.0, 1.0, 1.0);
|
||||
UIGraphicsBeginImageContext(rect.size);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
CGContextSetFillColorWithColor(context, color.CGColor);
|
||||
CGContextFillRect(context, rect);
|
||||
|
||||
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
extension UILabel {
|
||||
var numberOfVisibleLines: Int {
|
||||
let textSize = CGSize(width: frame.size.width, height: CGFloat(MAXFLOAT))
|
||||
let rowHeight = sizeThatFits(textSize).height.rounded()
|
||||
let charHeight = font.pointSize.rounded()
|
||||
return Int((rowHeight / charHeight).rounded())
|
||||
}
|
||||
}
|
||||
3
iphone/Maps/Categories/UILabel+RuntimeAttributes.h
Normal file
3
iphone/Maps/Categories/UILabel+RuntimeAttributes.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
@interface UILabel (RuntimeAttributes)
|
||||
@property (copy, nonatomic) NSString * localizedText;
|
||||
@end
|
||||
17
iphone/Maps/Categories/UILabel+RuntimeAttributes.m
Normal file
17
iphone/Maps/Categories/UILabel+RuntimeAttributes.m
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#import "UILabel+RuntimeAttributes.h"
|
||||
|
||||
// Runtime attributes for setting localized text in Xib.
|
||||
|
||||
@implementation UILabel (RuntimeAttributes)
|
||||
|
||||
- (void)setLocalizedText:(NSString *)localizedText
|
||||
{
|
||||
self.text = L(localizedText);
|
||||
}
|
||||
- (NSString *)localizedText
|
||||
{
|
||||
NSString * text = self.text;
|
||||
return L(text);
|
||||
}
|
||||
|
||||
@end
|
||||
5
iphone/Maps/Categories/UINib+Init.swift
Normal file
5
iphone/Maps/Categories/UINib+Init.swift
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
extension UINib {
|
||||
@objc convenience init(_ viewClass: AnyClass, bundle: Bundle? = nil) {
|
||||
self.init(nibName: toString(viewClass), bundle: bundle)
|
||||
}
|
||||
}
|
||||
45
iphone/Maps/Categories/UITableView+Cells.swift
Normal file
45
iphone/Maps/Categories/UITableView+Cells.swift
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
extension UITableView {
|
||||
@objc func registerNib(cellClass: AnyClass) {
|
||||
register(UINib(cellClass), forCellReuseIdentifier: toString(cellClass))
|
||||
}
|
||||
|
||||
@objc func registerClass(cellClass: AnyClass) {
|
||||
register(cellClass, forCellReuseIdentifier: toString(cellClass))
|
||||
}
|
||||
|
||||
@objc func dequeueReusableCell(withCellClass cellClass: AnyClass) -> UITableViewCell? {
|
||||
return dequeueReusableCell(withIdentifier: toString(cellClass))
|
||||
}
|
||||
|
||||
@objc func dequeueReusableCell(withCellClass cellClass: AnyClass, indexPath: IndexPath) -> UITableViewCell {
|
||||
return dequeueReusableCell(withIdentifier: toString(cellClass), for: indexPath)
|
||||
}
|
||||
|
||||
func registerNib<Cell>(cell: Cell.Type) where Cell: UITableViewCell {
|
||||
register(UINib(cell), forCellReuseIdentifier: toString(cell))
|
||||
}
|
||||
|
||||
func registerNibs<Cell>(_ cells: [Cell.Type]) where Cell: UITableViewCell {
|
||||
cells.forEach { registerNib(cell: $0) }
|
||||
}
|
||||
|
||||
func register<Cell>(cell: Cell.Type) where Cell: UITableViewCell {
|
||||
register(cell, forCellReuseIdentifier: toString(cell))
|
||||
}
|
||||
|
||||
func dequeueReusableCell<Cell>(cell: Cell.Type) -> Cell? where Cell: UITableViewCell {
|
||||
return dequeueReusableCell(withIdentifier: toString(cell)) as? Cell
|
||||
}
|
||||
|
||||
func dequeueReusableCell<Cell>(cell: Cell.Type, indexPath: IndexPath) -> Cell where Cell: UITableViewCell {
|
||||
return dequeueReusableCell(withIdentifier: toString(cell), for: indexPath) as! Cell
|
||||
}
|
||||
|
||||
func registerNibForHeaderFooterView<View>(_ view: View.Type) where View: UIView {
|
||||
register(UINib(view), forHeaderFooterViewReuseIdentifier: toString(view))
|
||||
}
|
||||
|
||||
func dequeueReusableHeaderFooterView<View>(_ view: View.Type) -> View where View: UIView {
|
||||
return dequeueReusableHeaderFooterView(withIdentifier: toString(view)) as! View
|
||||
}
|
||||
}
|
||||
18
iphone/Maps/Categories/UITableView+Updates.swift
Normal file
18
iphone/Maps/Categories/UITableView+Updates.swift
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
extension UITableView {
|
||||
typealias Updates = () -> Void
|
||||
typealias Completion = () -> Void
|
||||
|
||||
@objc func update(_ updates: Updates) {
|
||||
performBatchUpdates(updates, completion: nil)
|
||||
}
|
||||
|
||||
@objc func update(_ updates: Updates, completion: @escaping Completion) {
|
||||
performBatchUpdates(updates, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
}
|
||||
|
||||
@objc func refresh() {
|
||||
update {}
|
||||
}
|
||||
}
|
||||
5
iphone/Maps/Categories/UITextField+RuntimeAttributes.h
Normal file
5
iphone/Maps/Categories/UITextField+RuntimeAttributes.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
@interface UITextField (RuntimeAttributes)
|
||||
|
||||
@property (copy, nonatomic) NSString * localizedPlaceholder;
|
||||
|
||||
@end
|
||||
16
iphone/Maps/Categories/UITextField+RuntimeAttributes.m
Normal file
16
iphone/Maps/Categories/UITextField+RuntimeAttributes.m
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#import "UITextField+RuntimeAttributes.h"
|
||||
|
||||
@implementation UITextField (RuntimeAttributes)
|
||||
|
||||
- (void)setLocalizedPlaceholder:(NSString *)placeholder
|
||||
{
|
||||
self.placeholder = L(placeholder);
|
||||
}
|
||||
|
||||
- (NSString *)localizedPlaceholder
|
||||
{
|
||||
NSString * placeholder = self.placeholder;
|
||||
return L(placeholder);
|
||||
}
|
||||
|
||||
@end
|
||||
11
iphone/Maps/Categories/UITextView+RuntimeAttributes.h
Normal file
11
iphone/Maps/Categories/UITextView+RuntimeAttributes.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#import "MWMTextView.h"
|
||||
|
||||
@interface UITextView (UITextView_RuntimeAttributes)
|
||||
|
||||
@end
|
||||
|
||||
@interface MWMTextView (RuntimeAttributes)
|
||||
|
||||
@property (copy, nonatomic) NSString * localizedPlaceholder;
|
||||
|
||||
@end
|
||||
27
iphone/Maps/Categories/UITextView+RuntimeAttributes.m
Normal file
27
iphone/Maps/Categories/UITextView+RuntimeAttributes.m
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#import "UITextView+RuntimeAttributes.h"
|
||||
|
||||
@implementation UITextView (RuntimeAttributes)
|
||||
|
||||
- (void)setLocalizedText:(NSString *)localizedText
|
||||
{
|
||||
self.text = L(localizedText);
|
||||
}
|
||||
|
||||
- (NSString *)localizedText
|
||||
{
|
||||
return L(self.text);
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation MWMTextView (RuntimeAttributes)
|
||||
|
||||
- (void)setLocalizedPlaceholder:(NSString *)localizedPlaceholder
|
||||
{
|
||||
self.placeholder = L(localizedPlaceholder);
|
||||
}
|
||||
|
||||
- (NSString *)localizedPlaceholder
|
||||
{
|
||||
return L(self.placeholder);
|
||||
}
|
||||
@end
|
||||
30
iphone/Maps/Categories/UIView+AddSeparator.swift
Normal file
30
iphone/Maps/Categories/UIView+AddSeparator.swift
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
extension UIView {
|
||||
enum SeparatorPosition {
|
||||
case top
|
||||
case bottom
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func addSeparator(_ position: SeparatorPosition = .top,
|
||||
thickness: CGFloat = 1.0,
|
||||
insets: UIEdgeInsets = .zero) -> UIView {
|
||||
let lineView = UIView()
|
||||
lineView.setStyleAndApply(.divider)
|
||||
lineView.isUserInteractionEnabled = false
|
||||
lineView.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(lineView)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
lineView.heightAnchor.constraint(equalToConstant: thickness),
|
||||
lineView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: insets.left),
|
||||
lineView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -insets.right),
|
||||
])
|
||||
switch position {
|
||||
case .top:
|
||||
lineView.topAnchor.constraint(equalTo: topAnchor, constant: insets.top).isActive = true
|
||||
case .bottom:
|
||||
lineView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -insets.bottom).isActive = true
|
||||
}
|
||||
return lineView
|
||||
}
|
||||
}
|
||||
40
iphone/Maps/Categories/UIView+Animation.swift
Normal file
40
iphone/Maps/Categories/UIView+Animation.swift
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
|
||||
extension UIView {
|
||||
@objc func animateConstraints(duration: TimeInterval,
|
||||
animations: @escaping () -> Void,
|
||||
completion: @escaping () -> Void) {
|
||||
setNeedsLayout()
|
||||
UIView.animate(withDuration: duration,
|
||||
animations: { [weak self] in
|
||||
animations()
|
||||
self?.layoutIfNeeded()
|
||||
},
|
||||
completion: { _ in completion() })
|
||||
}
|
||||
|
||||
@objc func animateConstraints(animations: @escaping () -> Void, completion: @escaping () -> Void) {
|
||||
animateConstraints(duration: kDefaultAnimationDuration, animations: animations, completion: completion)
|
||||
}
|
||||
|
||||
@objc func animateConstraints(animations: @escaping () -> Void) {
|
||||
animateConstraints(duration: kDefaultAnimationDuration, animations: animations, completion: {})
|
||||
}
|
||||
|
||||
@objc func startRotation(_ duration: TimeInterval = 1.0) {
|
||||
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
||||
rotationAnimation.toValue = Double.pi * 2
|
||||
rotationAnimation.duration = duration;
|
||||
rotationAnimation.isCumulative = true;
|
||||
rotationAnimation.repeatCount = Float.greatestFiniteMagnitude;
|
||||
rotationAnimation.isRemovedOnCompletion = false
|
||||
layer.add(rotationAnimation, forKey: "rotationAnimation")
|
||||
}
|
||||
|
||||
@objc func stopRotation() {
|
||||
layer.removeAnimation(forKey: "rotationAnimation")
|
||||
}
|
||||
|
||||
@objc var isRotating: Bool {
|
||||
return layer.animationKeys()?.contains("rotationAnimation") ?? false
|
||||
}
|
||||
}
|
||||
13
iphone/Maps/Categories/UIView+Coordinates.swift
Normal file
13
iphone/Maps/Categories/UIView+Coordinates.swift
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
extension UIView {
|
||||
func center(inContainerView containerView: UIView) -> CGPoint {
|
||||
guard let sv = superview else { return .zero }
|
||||
var centerPoint = center
|
||||
|
||||
if let scrollView = sv as? UIScrollView, scrollView.zoomScale != 1.0 {
|
||||
centerPoint.x += (scrollView.bounds.width - scrollView.contentSize.width) / 2.0 + scrollView.contentOffset.x
|
||||
centerPoint.y += (scrollView.bounds.height - scrollView.contentSize.height) / 2.0 + scrollView.contentOffset.y
|
||||
}
|
||||
return sv.convert(centerPoint, to: containerView)
|
||||
}
|
||||
}
|
||||
20
iphone/Maps/Categories/UIView+Hierarchy.swift
Normal file
20
iphone/Maps/Categories/UIView+Hierarchy.swift
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
extension UIView {
|
||||
@objc func hasSubview(viewClass: AnyClass) -> Bool {
|
||||
return !subviews.filter { type(of: $0) == viewClass }.isEmpty
|
||||
}
|
||||
|
||||
func clearTreeBackground() {
|
||||
backgroundColor = UIColor.clear
|
||||
subviews.forEach { $0.clearTreeBackground() }
|
||||
}
|
||||
|
||||
func alignToSuperview(_ insets: UIEdgeInsets = .zero) {
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
topAnchor.constraint(equalTo: superview!.topAnchor, constant: insets.top),
|
||||
leftAnchor.constraint(equalTo: superview!.leftAnchor, constant: insets.left),
|
||||
bottomAnchor.constraint(equalTo: superview!.bottomAnchor, constant: insets.bottom),
|
||||
rightAnchor.constraint(equalTo: superview!.rightAnchor, constant: insets.right)
|
||||
])
|
||||
}
|
||||
}
|
||||
25
iphone/Maps/Categories/UIView+Highlight.swift
Normal file
25
iphone/Maps/Categories/UIView+Highlight.swift
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
extension UIView {
|
||||
@objc
|
||||
func highlight() {
|
||||
let color = UIColor.linkBlueHighlighted().withAlphaComponent(0.2)
|
||||
let duration: TimeInterval = kDefaultAnimationDuration
|
||||
let overlayView = UIView(frame: bounds)
|
||||
overlayView.backgroundColor = color
|
||||
overlayView.alpha = 0
|
||||
overlayView.clipsToBounds = true
|
||||
overlayView.isUserInteractionEnabled = false
|
||||
overlayView.layer.cornerRadius = layer.cornerRadius
|
||||
overlayView.layer.maskedCorners = layer.maskedCorners
|
||||
addSubview(overlayView)
|
||||
|
||||
UIView.animate(withDuration: duration, delay: duration, options: .curveEaseInOut, animations: {
|
||||
overlayView.alpha = 1
|
||||
}) { _ in
|
||||
UIView.animate(withDuration: duration, delay: duration * 3, options: .curveEaseInOut, animations: {
|
||||
overlayView.alpha = 0
|
||||
}) { _ in
|
||||
overlayView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
iphone/Maps/Categories/UIView+Snapshot.swift
Normal file
22
iphone/Maps/Categories/UIView+Snapshot.swift
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
extension UIView {
|
||||
@objc var snapshot: UIView {
|
||||
guard let contents = layer.contents else {
|
||||
return snapshotView(afterScreenUpdates: true)!
|
||||
}
|
||||
let snapshot: UIView
|
||||
if let view = self as? UIImageView {
|
||||
snapshot = UIImageView(image: view.image)
|
||||
snapshot.bounds = view.bounds
|
||||
} else {
|
||||
snapshot = UIView(frame: frame)
|
||||
snapshot.layer.contents = contents
|
||||
snapshot.layer.bounds = layer.bounds
|
||||
}
|
||||
snapshot.layer.setCornerRadius(.custom(layer.cornerRadius))
|
||||
snapshot.layer.masksToBounds = layer.masksToBounds
|
||||
snapshot.contentMode = contentMode
|
||||
snapshot.transform = transform
|
||||
return snapshot
|
||||
}
|
||||
}
|
||||
12
iphone/Maps/Categories/UIViewController+Hierarchy.swift
Normal file
12
iphone/Maps/Categories/UIViewController+Hierarchy.swift
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
extension UIViewController {
|
||||
@objc static func topViewController() -> UIViewController {
|
||||
let window = UIApplication.shared.delegate!.window!!
|
||||
if var topController = window.rootViewController {
|
||||
while let presentedViewController = topController.presentedViewController {
|
||||
topController = presentedViewController
|
||||
}
|
||||
return topController
|
||||
}
|
||||
return (window.rootViewController as! UINavigationController).topViewController!
|
||||
}
|
||||
}
|
||||
8
iphone/Maps/Categories/UIViewController+Navigation.h
Normal file
8
iphone/Maps/Categories/UIViewController+Navigation.h
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
@interface UIViewController (Navigation)
|
||||
|
||||
- (void)goBack;
|
||||
|
||||
- (UIBarButtonItem *)buttonWithImage:(UIImage *)image action:(SEL)action;
|
||||
- (NSArray<UIBarButtonItem *> *)alignedNavBarButtonItems:(NSArray<UIBarButtonItem *> *)items;
|
||||
|
||||
@end
|
||||
30
iphone/Maps/Categories/UIViewController+Navigation.m
Normal file
30
iphone/Maps/Categories/UIViewController+Navigation.m
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#import "UIButton+Orientation.h"
|
||||
#import "UIViewController+Navigation.h"
|
||||
|
||||
static CGFloat const kButtonExtraWidth = 16.0;
|
||||
|
||||
@implementation UIViewController (Navigation)
|
||||
|
||||
- (UIBarButtonItem *)negativeSpacer
|
||||
{
|
||||
UIBarButtonItem * spacer =
|
||||
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
|
||||
target:nil
|
||||
action:nil];
|
||||
spacer.width = -kButtonExtraWidth;
|
||||
return spacer;
|
||||
}
|
||||
|
||||
- (UIBarButtonItem *)buttonWithImage:(UIImage *)image action:(SEL)action
|
||||
{
|
||||
return [[UIBarButtonItem alloc] initWithImage:image style:UIBarButtonItemStylePlain target:self action:action];
|
||||
}
|
||||
|
||||
- (NSArray<UIBarButtonItem *> *)alignedNavBarButtonItems:(NSArray<UIBarButtonItem *> *)items
|
||||
{
|
||||
return [@[ [self negativeSpacer] ] arrayByAddingObjectsFromArray:items];
|
||||
}
|
||||
|
||||
- (void)goBack { [self.navigationController popViewControllerAnimated:YES]; }
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
extension UIViewController {
|
||||
func alternativeSizeClass<T>(iPhone: @autoclosure () -> T, iPad: @autoclosure () -> T) -> T {
|
||||
isiPad ? iPad() : iPhone()
|
||||
}
|
||||
|
||||
func alternativeSizeClass(iPhone: () -> Void, iPad: () -> Void) {
|
||||
isiPad ? iPad() : iPhone()
|
||||
}
|
||||
}
|
||||
8
iphone/Maps/Categories/URL+Query.swift
Normal file
8
iphone/Maps/Categories/URL+Query.swift
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import Foundation
|
||||
|
||||
extension URL {
|
||||
func queryParams() -> [String : String]? {
|
||||
guard let urlComponents = URLComponents(url: self, resolvingAgainstBaseURL: false) else { return nil }
|
||||
return urlComponents.queryItems?.reduce(into: [:], { $0[$1.name] = $1.value })
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue