Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
38
iphone/Maps/Classes/CarPlay/CPConstants.swift
Normal file
38
iphone/Maps/Classes/CarPlay/CPConstants.swift
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import Foundation
|
||||
|
||||
struct CPConstants {
|
||||
struct TemplateKey {
|
||||
static let map = "map_type"
|
||||
static let alert = "alert_type"
|
||||
static let list = "list_type"
|
||||
}
|
||||
struct TemplateType {
|
||||
static let main = "main"
|
||||
static let navigation = "navigation"
|
||||
static let preview = "preview"
|
||||
static let previewAccepted = "preview_accepted"
|
||||
static let previewSettings = "preview_settings"
|
||||
static let redirectRoute = "redirect_route"
|
||||
static let restoreRoute = "restore_route"
|
||||
static let downloadMap = "download_map"
|
||||
}
|
||||
|
||||
struct ListItemType {
|
||||
static let history = "history"
|
||||
static let bookmarks = "bookmarks"
|
||||
static let bookmarkLists = "bookmark_lists"
|
||||
static let searchResults = "search_results"
|
||||
}
|
||||
|
||||
struct Maneuvers {
|
||||
static let primary = "primary"
|
||||
static let secondary = "secondary"
|
||||
}
|
||||
|
||||
struct Trip {
|
||||
static let start = "start_point"
|
||||
static let end = "end_point"
|
||||
static let errorCode = "error_code"
|
||||
static let missedCountries = "countries"
|
||||
}
|
||||
}
|
||||
7
iphone/Maps/Classes/CarPlay/CPViewPortState.swift
Normal file
7
iphone/Maps/Classes/CarPlay/CPViewPortState.swift
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import Foundation
|
||||
|
||||
enum CPViewPortState: Int {
|
||||
case `default`
|
||||
case preview
|
||||
case navigation
|
||||
}
|
||||
366
iphone/Maps/Classes/CarPlay/CarPlayRouter.swift
Normal file
366
iphone/Maps/Classes/CarPlay/CarPlayRouter.swift
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
import CarPlay
|
||||
import Contacts
|
||||
|
||||
protocol CarPlayRouterListener: AnyObject {
|
||||
func didCreateRoute(routeInfo: RouteInfo,
|
||||
trip: CPTrip)
|
||||
func didUpdateRouteInfo(_ routeInfo: RouteInfo, forTrip trip: CPTrip)
|
||||
func didFailureBuildRoute(forTrip trip: CPTrip, code: RouterResultCode, countries: [String])
|
||||
func routeDidFinish(_ trip: CPTrip)
|
||||
}
|
||||
|
||||
|
||||
@objc(MWMCarPlayRouter)
|
||||
final class CarPlayRouter: NSObject {
|
||||
private let listenerContainer: ListenerContainer<CarPlayRouterListener>
|
||||
private var routeSession: CPNavigationSession?
|
||||
private var initialSpeedCamSettings: SpeedCameraManagerMode
|
||||
var currentTrip: CPTrip? {
|
||||
return routeSession?.trip
|
||||
}
|
||||
var previewTrip: CPTrip?
|
||||
var speedCameraMode: SpeedCameraManagerMode {
|
||||
return RoutingManager.routingManager.speedCameraMode
|
||||
}
|
||||
|
||||
override init() {
|
||||
listenerContainer = ListenerContainer<CarPlayRouterListener>()
|
||||
initialSpeedCamSettings = RoutingManager.routingManager.speedCameraMode
|
||||
super.init()
|
||||
}
|
||||
|
||||
func addListener(_ listener: CarPlayRouterListener) {
|
||||
listenerContainer.addListener(listener)
|
||||
}
|
||||
|
||||
func removeListener(_ listener: CarPlayRouterListener) {
|
||||
listenerContainer.removeListener(listener)
|
||||
}
|
||||
|
||||
func subscribeToEvents() {
|
||||
RoutingManager.routingManager.add(self)
|
||||
}
|
||||
|
||||
func unsubscribeFromEvents() {
|
||||
RoutingManager.routingManager.remove(self)
|
||||
}
|
||||
|
||||
func completeRouteAndRemovePoints() {
|
||||
let manager = RoutingManager.routingManager
|
||||
manager.stopRoutingAndRemoveRoutePoints(true)
|
||||
manager.deleteSavedRoutePoints()
|
||||
manager.apply(routeType: .vehicle)
|
||||
previewTrip = nil
|
||||
}
|
||||
|
||||
func rebuildRoute() {
|
||||
guard let trip = previewTrip else { return }
|
||||
do {
|
||||
try RoutingManager.routingManager.buildRoute()
|
||||
} catch let error as NSError {
|
||||
listenerContainer.forEach({
|
||||
let code = RouterResultCode(rawValue: UInt(error.code)) ?? .internalError
|
||||
$0.didFailureBuildRoute(forTrip: trip, code: code, countries: [])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func buildRoute(trip: CPTrip) {
|
||||
completeRouteAndRemovePoints()
|
||||
previewTrip = trip
|
||||
guard let info = trip.userInfo as? [String: MWMRoutePoint] else {
|
||||
listenerContainer.forEach({
|
||||
$0.didFailureBuildRoute(forTrip: trip, code: .routeNotFound, countries: [])
|
||||
})
|
||||
return
|
||||
}
|
||||
guard let startPoint = info[CPConstants.Trip.start],
|
||||
let endPoint = info[CPConstants.Trip.end] else {
|
||||
listenerContainer.forEach({
|
||||
var code: RouterResultCode!
|
||||
if info[CPConstants.Trip.end] == nil {
|
||||
code = .endPointNotFound
|
||||
} else {
|
||||
code = .startPointNotFound
|
||||
}
|
||||
$0.didFailureBuildRoute(forTrip: trip, code: code, countries: [])
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
let manager = RoutingManager.routingManager
|
||||
manager.add(routePoint: startPoint)
|
||||
manager.add(routePoint: endPoint)
|
||||
|
||||
do {
|
||||
try manager.buildRoute()
|
||||
} catch let error as NSError {
|
||||
listenerContainer.forEach({
|
||||
let code = RouterResultCode(rawValue: UInt(error.code)) ?? .internalError
|
||||
$0.didFailureBuildRoute(forTrip: trip, code: code, countries: [])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func updateStartPointAndRebuild(trip: CPTrip) {
|
||||
let manager = RoutingManager.routingManager
|
||||
previewTrip = trip
|
||||
guard let info = trip.userInfo as? [String: MWMRoutePoint] else {
|
||||
listenerContainer.forEach({
|
||||
$0.didFailureBuildRoute(forTrip: trip, code: .routeNotFound, countries: [])
|
||||
})
|
||||
return
|
||||
}
|
||||
guard let startPoint = info[CPConstants.Trip.start] else {
|
||||
listenerContainer.forEach({
|
||||
$0.didFailureBuildRoute(forTrip: trip, code: .startPointNotFound, countries: [])
|
||||
})
|
||||
return
|
||||
}
|
||||
manager.add(routePoint: startPoint)
|
||||
manager.apply(routeType: .vehicle)
|
||||
do {
|
||||
try manager.buildRoute()
|
||||
} catch let error as NSError {
|
||||
listenerContainer.forEach({
|
||||
let code = RouterResultCode(rawValue: UInt(error.code)) ?? .internalError
|
||||
$0.didFailureBuildRoute(forTrip: trip, code: code, countries: [])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func startRoute() {
|
||||
let manager = RoutingManager.routingManager
|
||||
manager.startRoute()
|
||||
}
|
||||
|
||||
func setupCarPlaySpeedCameraMode() {
|
||||
if case .auto = initialSpeedCamSettings {
|
||||
RoutingManager.routingManager.speedCameraMode = .always
|
||||
}
|
||||
}
|
||||
|
||||
func setupInitialSpeedCameraMode() {
|
||||
RoutingManager.routingManager.speedCameraMode = initialSpeedCamSettings
|
||||
}
|
||||
|
||||
func updateSpeedCameraMode(_ mode: SpeedCameraManagerMode) {
|
||||
initialSpeedCamSettings = mode
|
||||
RoutingManager.routingManager.speedCameraMode = mode
|
||||
}
|
||||
|
||||
func restoreTripPreviewOnCarplay(beforeRootTemplateDidAppear: Bool) {
|
||||
guard MWMRouter.isRestoreProcessCompleted() else {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.restoreTripPreviewOnCarplay(beforeRootTemplateDidAppear: false)
|
||||
}
|
||||
return
|
||||
}
|
||||
let manager = RoutingManager.routingManager
|
||||
MWMRouter.hideNavigationMapControls()
|
||||
guard manager.isRoutingActive,
|
||||
let startPoint = manager.startPoint,
|
||||
let endPoint = manager.endPoint else {
|
||||
completeRouteAndRemovePoints()
|
||||
return
|
||||
}
|
||||
let trip = createTrip(startPoint: startPoint,
|
||||
endPoint: endPoint,
|
||||
routeInfo: manager.routeInfo)
|
||||
previewTrip = trip
|
||||
if manager.type != .vehicle {
|
||||
CarPlayService.shared.showRecoverRouteAlert(trip: trip, isTypeCorrect: false)
|
||||
return
|
||||
}
|
||||
if !startPoint.isMyPosition {
|
||||
CarPlayService.shared.showRecoverRouteAlert(trip: trip, isTypeCorrect: true)
|
||||
return
|
||||
}
|
||||
if beforeRootTemplateDidAppear {
|
||||
CarPlayService.shared.preparedToPreviewTrips = [trip]
|
||||
} else {
|
||||
CarPlayService.shared.preparePreview(trips: [trip])
|
||||
}
|
||||
}
|
||||
|
||||
func restoredNavigationSession() -> (CPTrip, RouteInfo)? {
|
||||
let manager = RoutingManager.routingManager
|
||||
if manager.isOnRoute,
|
||||
manager.type == .vehicle,
|
||||
let startPoint = manager.startPoint,
|
||||
let endPoint = manager.endPoint,
|
||||
let routeInfo = manager.routeInfo {
|
||||
MWMRouter.hideNavigationMapControls()
|
||||
let trip = createTrip(startPoint: startPoint,
|
||||
endPoint: endPoint,
|
||||
routeInfo: routeInfo)
|
||||
previewTrip = trip
|
||||
return (trip, routeInfo)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Navigation session management
|
||||
extension CarPlayRouter {
|
||||
func startNavigationSession(forTrip trip: CPTrip, template: CPMapTemplate) {
|
||||
routeSession = template.startNavigationSession(for: trip)
|
||||
routeSession?.pauseTrip(for: .loading, description: nil)
|
||||
updateUpcomingManeuvers()
|
||||
RoutingManager.routingManager.setOnNewTurnCallback { [weak self] in
|
||||
self?.updateUpcomingManeuvers()
|
||||
}
|
||||
}
|
||||
|
||||
func cancelTrip() {
|
||||
routeSession?.cancelTrip()
|
||||
routeSession = nil
|
||||
completeRouteAndRemovePoints()
|
||||
RoutingManager.routingManager.resetOnNewTurnCallback()
|
||||
}
|
||||
|
||||
func finishTrip() {
|
||||
routeSession?.finishTrip()
|
||||
routeSession = nil
|
||||
completeRouteAndRemovePoints()
|
||||
RoutingManager.routingManager.resetOnNewTurnCallback()
|
||||
}
|
||||
|
||||
func updateUpcomingManeuvers() {
|
||||
let maneuvers = createUpcomingManeuvers()
|
||||
routeSession?.upcomingManeuvers = maneuvers
|
||||
}
|
||||
|
||||
func updateEstimates() {
|
||||
guard let routeSession = routeSession,
|
||||
let routeInfo = RoutingManager.routingManager.routeInfo,
|
||||
let primaryManeuver = routeSession.upcomingManeuvers.first,
|
||||
let estimates = createEstimates(routeInfo) else {
|
||||
return
|
||||
}
|
||||
routeSession.updateEstimates(estimates, for: primaryManeuver)
|
||||
}
|
||||
|
||||
private func createEstimates(_ routeInfo: RouteInfo) -> CPTravelEstimates? {
|
||||
let measurement = Measurement(value: routeInfo.distanceToTurn, unit: routeInfo.turnUnits)
|
||||
return CPTravelEstimates(distanceRemaining: measurement, timeRemaining: 0.0)
|
||||
}
|
||||
|
||||
private func createUpcomingManeuvers() -> [CPManeuver] {
|
||||
guard let routeInfo = RoutingManager.routingManager.routeInfo else {
|
||||
return []
|
||||
}
|
||||
var maneuvers = [CPManeuver]()
|
||||
let primaryManeuver = CPManeuver()
|
||||
primaryManeuver.userInfo = CPConstants.Maneuvers.primary
|
||||
var instructionVariant = routeInfo.streetName
|
||||
if routeInfo.roundExitNumber != 0 {
|
||||
let ordinalExitNumber = NumberFormatter.localizedString(from: NSNumber(value: routeInfo.roundExitNumber),
|
||||
number: .ordinal)
|
||||
let exitNumber = String(format: L("carplay_roundabout_exit"),
|
||||
arguments: [ordinalExitNumber])
|
||||
instructionVariant = instructionVariant.isEmpty ? exitNumber : (exitNumber + ", " + instructionVariant)
|
||||
}
|
||||
primaryManeuver.instructionVariants = [instructionVariant]
|
||||
if let imageName = routeInfo.turnImageName,
|
||||
let symbol = UIImage(named: imageName) {
|
||||
primaryManeuver.symbolImage = symbol
|
||||
}
|
||||
if let estimates = createEstimates(routeInfo) {
|
||||
primaryManeuver.initialTravelEstimates = estimates
|
||||
}
|
||||
maneuvers.append(primaryManeuver)
|
||||
if let imageName = routeInfo.nextTurnImageName,
|
||||
let symbol = UIImage(named: imageName) {
|
||||
let secondaryManeuver = CPManeuver()
|
||||
secondaryManeuver.userInfo = CPConstants.Maneuvers.secondary
|
||||
secondaryManeuver.instructionVariants = [L("then_turn")]
|
||||
secondaryManeuver.symbolImage = symbol
|
||||
maneuvers.append(secondaryManeuver)
|
||||
}
|
||||
return maneuvers
|
||||
}
|
||||
|
||||
func createTrip(startPoint: MWMRoutePoint, endPoint: MWMRoutePoint, routeInfo: RouteInfo? = nil) -> CPTrip {
|
||||
let startPlacemark = MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: startPoint.latitude,
|
||||
longitude: startPoint.longitude))
|
||||
let endPlacemark = MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: endPoint.latitude,
|
||||
longitude: endPoint.longitude),
|
||||
addressDictionary: [CNPostalAddressStreetKey: endPoint.subtitle ?? ""])
|
||||
let startItem = MKMapItem(placemark: startPlacemark)
|
||||
let endItem = MKMapItem(placemark: endPlacemark)
|
||||
endItem.name = endPoint.title
|
||||
|
||||
let routeChoice = CPRouteChoice(summaryVariants: [" "], additionalInformationVariants: [], selectionSummaryVariants: [])
|
||||
routeChoice.userInfo = routeInfo
|
||||
|
||||
let trip = CPTrip(origin: startItem, destination: endItem, routeChoices: [routeChoice])
|
||||
trip.userInfo = [CPConstants.Trip.start: startPoint, CPConstants.Trip.end: endPoint]
|
||||
return trip
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - RoutingManagerListener implementation
|
||||
extension CarPlayRouter: RoutingManagerListener {
|
||||
func updateCameraInfo(isCameraOnRoute: Bool, speedLimitMps limit: Double) {
|
||||
CarPlayService.shared.updateCameraUI(isCameraOnRoute: isCameraOnRoute, speedLimitMps: limit < 0 ? nil : limit)
|
||||
}
|
||||
|
||||
func processRouteBuilderEvent(with code: RouterResultCode, countries: [String]) {
|
||||
guard let trip = previewTrip else {
|
||||
return
|
||||
}
|
||||
switch code {
|
||||
case .noError, .hasWarnings:
|
||||
let manager = RoutingManager.routingManager
|
||||
if manager.isRouteFinished {
|
||||
listenerContainer.forEach({
|
||||
$0.routeDidFinish(trip)
|
||||
})
|
||||
return
|
||||
}
|
||||
if let info = manager.routeInfo {
|
||||
previewTrip?.routeChoices.first?.userInfo = info
|
||||
if routeSession == nil {
|
||||
listenerContainer.forEach({
|
||||
$0.didCreateRoute(routeInfo: info,
|
||||
trip: trip)
|
||||
})
|
||||
} else {
|
||||
listenerContainer.forEach({
|
||||
$0.didUpdateRouteInfo(info, forTrip: trip)
|
||||
})
|
||||
updateUpcomingManeuvers()
|
||||
}
|
||||
}
|
||||
default:
|
||||
listenerContainer.forEach({
|
||||
$0.didFailureBuildRoute(forTrip: trip, code: code, countries: countries)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func didLocationUpdate(_ notifications: [String]) {
|
||||
guard let trip = previewTrip else { return }
|
||||
|
||||
let manager = RoutingManager.routingManager
|
||||
if manager.isRouteFinished {
|
||||
listenerContainer.forEach({
|
||||
$0.routeDidFinish(trip)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
guard let routeInfo = manager.routeInfo,
|
||||
manager.isRoutingActive else { return }
|
||||
listenerContainer.forEach({
|
||||
$0.didUpdateRouteInfo(routeInfo, forTrip: trip)
|
||||
})
|
||||
|
||||
let tts = MWMTextToSpeech.tts()!
|
||||
if manager.isOnRoute && tts.active {
|
||||
tts.playTurnNotifications(notifications)
|
||||
tts.playWarningSound()
|
||||
}
|
||||
}
|
||||
}
|
||||
805
iphone/Maps/Classes/CarPlay/CarPlayService.swift
Normal file
805
iphone/Maps/Classes/CarPlay/CarPlayService.swift
Normal file
|
|
@ -0,0 +1,805 @@
|
|||
import CarPlay
|
||||
import Contacts
|
||||
|
||||
@objc(MWMCarPlayService)
|
||||
final class CarPlayService: NSObject {
|
||||
@objc static let shared = CarPlayService()
|
||||
@objc var isCarplayActivated: Bool = false
|
||||
private var searchService: CarPlaySearchService?
|
||||
private var router: CarPlayRouter?
|
||||
private var window: CPWindow?
|
||||
private var interfaceController: CPInterfaceController?
|
||||
private var sessionConfiguration: CPSessionConfiguration?
|
||||
var currentPositionMode: MWMMyPositionMode = .pendingPosition
|
||||
var isSpeedCamActivated: Bool {
|
||||
set {
|
||||
router?.updateSpeedCameraMode(newValue ? .always: .never)
|
||||
}
|
||||
get {
|
||||
let mode: SpeedCameraManagerMode = router?.speedCameraMode ?? .never
|
||||
return mode == .always ? true : false
|
||||
}
|
||||
}
|
||||
var isKeyboardLimited: Bool {
|
||||
return sessionConfiguration?.limitedUserInterfaces.contains(.keyboard) ?? false
|
||||
}
|
||||
private var carplayVC: CarPlayMapViewController? {
|
||||
return window?.rootViewController as? CarPlayMapViewController
|
||||
}
|
||||
private var rootMapTemplate: CPMapTemplate? {
|
||||
return interfaceController?.rootTemplate as? CPMapTemplate
|
||||
}
|
||||
var preparedToPreviewTrips: [CPTrip] = []
|
||||
var isUserPanMap: Bool = false
|
||||
private var searchText = ""
|
||||
|
||||
@objc func setup(window: CPWindow, interfaceController: CPInterfaceController) {
|
||||
isCarplayActivated = true
|
||||
self.window = window
|
||||
self.interfaceController = interfaceController
|
||||
self.interfaceController?.delegate = self
|
||||
let configuration = CPSessionConfiguration(delegate: self)
|
||||
sessionConfiguration = configuration
|
||||
searchService = CarPlaySearchService()
|
||||
let router = CarPlayRouter()
|
||||
router.addListener(self)
|
||||
router.subscribeToEvents()
|
||||
router.setupCarPlaySpeedCameraMode()
|
||||
self.router = router
|
||||
MWMRouter.unsubscribeFromEvents()
|
||||
applyRootViewController()
|
||||
if let sessionData = router.restoredNavigationSession() {
|
||||
applyNavigationRootTemplate(trip: sessionData.0, routeInfo: sessionData.1)
|
||||
} else {
|
||||
applyBaseRootTemplate()
|
||||
router.restoreTripPreviewOnCarplay(beforeRootTemplateDidAppear: true)
|
||||
}
|
||||
updateContentStyle(configuration.contentStyle)
|
||||
FrameworkHelper.updatePositionArrowOffset(false, offset: (Int32(window.height * window.screen.scale)/3))
|
||||
|
||||
CarPlayWindowScaleAdjuster.updateAppearance(
|
||||
fromWindow: MapsAppDelegate.theApp().window,
|
||||
toWindow: window,
|
||||
isCarplayActivated: true
|
||||
)
|
||||
}
|
||||
|
||||
private var savedInterfaceController: CPInterfaceController?
|
||||
@objc func showOnPhone() {
|
||||
savedInterfaceController = interfaceController
|
||||
switchScreenToPhone()
|
||||
showPhoneModeAlert()
|
||||
}
|
||||
|
||||
@objc func showOnCarplay() {
|
||||
if let window, let savedInterfaceController {
|
||||
setup(window: window, interfaceController: savedInterfaceController)
|
||||
}
|
||||
}
|
||||
|
||||
private func showPhoneModeAlert() {
|
||||
let switchToCarAction = CPAlertAction(
|
||||
title: L("car_continue_in_the_car"),
|
||||
style: .default,
|
||||
handler: { [weak self] _ in
|
||||
self?.savedInterfaceController?.dismissTemplate(animated: false)
|
||||
self?.showOnCarplay()
|
||||
}
|
||||
)
|
||||
let alert = CPAlertTemplate(
|
||||
titleVariants: [L("car_used_on_the_phone_screen")],
|
||||
actions: [switchToCarAction]
|
||||
)
|
||||
savedInterfaceController?.dismissTemplate(animated: false)
|
||||
savedInterfaceController?.presentTemplate(alert, animated: false)
|
||||
}
|
||||
|
||||
private func switchScreenToPhone() {
|
||||
if let carplayVC = carplayVC {
|
||||
carplayVC.removeMapView()
|
||||
}
|
||||
if let mvc = MapViewController.shared() {
|
||||
mvc.disableCarPlayRepresentation()
|
||||
mvc.remove(self)
|
||||
}
|
||||
router?.removeListener(self)
|
||||
router?.unsubscribeFromEvents()
|
||||
router?.setupInitialSpeedCameraMode()
|
||||
MWMRouter.subscribeToEvents()
|
||||
isCarplayActivated = false
|
||||
if router?.currentTrip != nil {
|
||||
MWMRouter.showNavigationMapControls()
|
||||
} else if router?.previewTrip != nil {
|
||||
MWMRouter.rebuild(withBestRouter: true)
|
||||
}
|
||||
searchService = nil
|
||||
router = nil
|
||||
sessionConfiguration = nil
|
||||
interfaceController = nil
|
||||
ThemeManager.invalidate()
|
||||
FrameworkHelper.updatePositionArrowOffset(true, offset: 0)
|
||||
|
||||
if let window {
|
||||
CarPlayWindowScaleAdjuster.updateAppearance(
|
||||
fromWindow: window,
|
||||
toWindow: MapsAppDelegate.theApp().window,
|
||||
isCarplayActivated: false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func destroy() {
|
||||
if isCarplayActivated {
|
||||
switchScreenToPhone()
|
||||
}
|
||||
savedInterfaceController = nil
|
||||
}
|
||||
|
||||
@objc func interfaceStyle() -> UIUserInterfaceStyle {
|
||||
if let window = window,
|
||||
window.traitCollection.userInterfaceIdiom == .carPlay {
|
||||
return rootTemplateStyle == .dark ? .dark : .light
|
||||
}
|
||||
return .unspecified
|
||||
}
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
private func updateContentStyle(_ contentStyle: CPContentStyle) {
|
||||
rootTemplateStyle = contentStyle == .dark ? .dark : .light
|
||||
// Update the current map style in accordance with the CarPLay content theme.
|
||||
ThemeManager.invalidate()
|
||||
}
|
||||
|
||||
private var rootTemplateStyle: CPTripEstimateStyle = .light {
|
||||
didSet {
|
||||
(interfaceController?.rootTemplate as? CPMapTemplate)?.tripEstimateStyle = rootTemplateStyle
|
||||
}
|
||||
}
|
||||
|
||||
private func applyRootViewController() {
|
||||
guard let window = window else { return }
|
||||
let carplaySotyboard = UIStoryboard.instance(.carPlay)
|
||||
let carplayVC = carplaySotyboard.instantiateInitialViewController() as! CarPlayMapViewController
|
||||
window.rootViewController = carplayVC
|
||||
if let mapVC = MapViewController.shared() {
|
||||
currentPositionMode = mapVC.currentPositionMode
|
||||
mapVC.enableCarPlayRepresentation()
|
||||
carplayVC.addMapView(mapVC.mapView, mapButtonSafeAreaLayoutGuide: window.mapButtonSafeAreaLayoutGuide)
|
||||
mapVC.add(self)
|
||||
}
|
||||
}
|
||||
|
||||
private func applyBaseRootTemplate() {
|
||||
let mapTemplate = MapTemplateBuilder.buildBaseTemplate(positionMode: currentPositionMode)
|
||||
mapTemplate.mapDelegate = self
|
||||
mapTemplate.tripEstimateStyle = rootTemplateStyle
|
||||
interfaceController?.setRootTemplate(mapTemplate, animated: true)
|
||||
FrameworkHelper.rotateMap(0.0, animated: false)
|
||||
}
|
||||
|
||||
private func applyNavigationRootTemplate(trip: CPTrip, routeInfo: RouteInfo) {
|
||||
let mapTemplate = MapTemplateBuilder.buildNavigationTemplate()
|
||||
mapTemplate.mapDelegate = self
|
||||
interfaceController?.setRootTemplate(mapTemplate, animated: true)
|
||||
router?.startNavigationSession(forTrip: trip, template: mapTemplate)
|
||||
if let estimates = createEstimates(routeInfo: routeInfo) {
|
||||
mapTemplate.tripEstimateStyle = rootTemplateStyle
|
||||
mapTemplate.updateEstimates(estimates, for: trip)
|
||||
}
|
||||
|
||||
if let carplayVC = carplayVC {
|
||||
carplayVC.updateCurrentSpeed(routeInfo.speedMps, speedLimitMps: routeInfo.speedLimitMps)
|
||||
carplayVC.showSpeedControl()
|
||||
}
|
||||
}
|
||||
|
||||
func pushTemplate(_ templateToPush: CPTemplate, animated: Bool) {
|
||||
if let interfaceController = interfaceController {
|
||||
switch templateToPush {
|
||||
case let list as CPListTemplate:
|
||||
list.delegate = self
|
||||
case let search as CPSearchTemplate:
|
||||
search.delegate = self
|
||||
case let map as CPMapTemplate:
|
||||
map.mapDelegate = self
|
||||
default:
|
||||
break
|
||||
}
|
||||
interfaceController.pushTemplate(templateToPush, animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
func popTemplate(animated: Bool) {
|
||||
interfaceController?.popTemplate(animated: animated)
|
||||
}
|
||||
|
||||
func presentAlert(_ template: CPAlertTemplate, animated: Bool) {
|
||||
interfaceController?.dismissTemplate(animated: false)
|
||||
interfaceController?.presentTemplate(template, animated: animated)
|
||||
}
|
||||
|
||||
func cancelCurrentTrip() {
|
||||
router?.cancelTrip()
|
||||
if let carplayVC = carplayVC {
|
||||
carplayVC.hideSpeedControl()
|
||||
}
|
||||
updateMapTemplateUIToBase()
|
||||
}
|
||||
|
||||
func updateCameraUI(isCameraOnRoute: Bool, speedLimitMps limit: Double?) {
|
||||
if let carplayVC = carplayVC {
|
||||
carplayVC.updateCameraInfo(isCameraOnRoute: isCameraOnRoute, speedLimitMps: limit)
|
||||
}
|
||||
}
|
||||
|
||||
func updateMapTemplateUIToBase() {
|
||||
guard let mapTemplate = rootMapTemplate else {
|
||||
return
|
||||
}
|
||||
MapTemplateBuilder.configureBaseUI(mapTemplate: mapTemplate)
|
||||
if currentPositionMode == .pendingPosition {
|
||||
mapTemplate.leadingNavigationBarButtons = []
|
||||
} else if currentPositionMode == .follow || currentPositionMode == .followAndRotate {
|
||||
MapTemplateBuilder.setupDestinationButton(mapTemplate: mapTemplate)
|
||||
} else {
|
||||
MapTemplateBuilder.setupRecenterButton(mapTemplate: mapTemplate)
|
||||
}
|
||||
updateVisibleViewPortState(.default)
|
||||
FrameworkHelper.rotateMap(0.0, animated: true)
|
||||
}
|
||||
|
||||
func updateMapTemplateUIToTripFinished(_ trip: CPTrip) {
|
||||
guard let mapTemplate = rootMapTemplate else {
|
||||
return
|
||||
}
|
||||
mapTemplate.leadingNavigationBarButtons = []
|
||||
mapTemplate.trailingNavigationBarButtons = []
|
||||
mapTemplate.mapButtons = []
|
||||
let doneAction = CPAlertAction(title: L("done"), style: .default) { [unowned self] _ in
|
||||
self.updateMapTemplateUIToBase()
|
||||
}
|
||||
var subtitle = ""
|
||||
if let locationName = trip.destination.name {
|
||||
subtitle = locationName
|
||||
}
|
||||
if let address = trip.destination.placemark.postalAddress?.street {
|
||||
subtitle = subtitle + "\n" + address
|
||||
}
|
||||
|
||||
let alert = CPNavigationAlert(titleVariants: [L("trip_finished")],
|
||||
subtitleVariants: [subtitle],
|
||||
image: nil,
|
||||
primaryAction: doneAction,
|
||||
secondaryAction: nil,
|
||||
duration: 0)
|
||||
mapTemplate.present(navigationAlert: alert, animated: true)
|
||||
}
|
||||
|
||||
func updateVisibleViewPortState(_ state: CPViewPortState) {
|
||||
guard let carplayVC = carplayVC else {
|
||||
return
|
||||
}
|
||||
carplayVC.updateVisibleViewPortState(state)
|
||||
}
|
||||
|
||||
func updateRouteAfterChangingSettings() {
|
||||
router?.rebuildRoute()
|
||||
}
|
||||
|
||||
@objc func showNoMapAlert() {
|
||||
guard let mapTemplate = interfaceController?.topTemplate as? CPMapTemplate,
|
||||
let info = mapTemplate.userInfo as? MapInfo,
|
||||
info.type == CPConstants.TemplateType.main else {
|
||||
return
|
||||
}
|
||||
let alert = CPAlertTemplate(titleVariants: [L("download_map_carplay")], actions: [])
|
||||
alert.userInfo = [CPConstants.TemplateKey.alert: CPConstants.TemplateType.downloadMap]
|
||||
presentAlert(alert, animated: true)
|
||||
}
|
||||
|
||||
@objc func hideNoMapAlert() {
|
||||
if let presentedTemplate = interfaceController?.presentedTemplate,
|
||||
let info = presentedTemplate.userInfo as? [String: String],
|
||||
let alertType = info[CPConstants.TemplateKey.alert],
|
||||
alertType == CPConstants.TemplateType.downloadMap {
|
||||
interfaceController?.dismissTemplate(animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CPInterfaceControllerDelegate implementation
|
||||
extension CarPlayService: CPInterfaceControllerDelegate {
|
||||
func templateWillAppear(_ aTemplate: CPTemplate, animated: Bool) {
|
||||
guard let info = aTemplate.userInfo as? MapInfo else {
|
||||
return
|
||||
}
|
||||
switch info.type {
|
||||
case CPConstants.TemplateType.main:
|
||||
updateVisibleViewPortState(.default)
|
||||
case CPConstants.TemplateType.preview:
|
||||
updateVisibleViewPortState(.preview)
|
||||
case CPConstants.TemplateType.navigation:
|
||||
updateVisibleViewPortState(.navigation)
|
||||
case CPConstants.TemplateType.previewSettings:
|
||||
aTemplate.userInfo = MapInfo(type: CPConstants.TemplateType.preview)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func templateDidAppear(_ aTemplate: CPTemplate, animated: Bool) {
|
||||
guard let mapTemplate = aTemplate as? CPMapTemplate,
|
||||
let info = aTemplate.userInfo as? MapInfo else {
|
||||
return
|
||||
}
|
||||
if !preparedToPreviewTrips.isEmpty && info.type == CPConstants.TemplateType.main {
|
||||
preparePreview(trips: preparedToPreviewTrips)
|
||||
preparedToPreviewTrips = []
|
||||
return
|
||||
}
|
||||
|
||||
if info.type == CPConstants.TemplateType.preview, let trips = info.trips {
|
||||
showPreview(mapTemplate: mapTemplate, trips: trips)
|
||||
}
|
||||
}
|
||||
|
||||
func templateWillDisappear(_ aTemplate: CPTemplate, animated: Bool) {
|
||||
guard let info = aTemplate.userInfo as? MapInfo else {
|
||||
return
|
||||
}
|
||||
if info.type == CPConstants.TemplateType.preview {
|
||||
router?.completeRouteAndRemovePoints()
|
||||
}
|
||||
}
|
||||
|
||||
func templateDidDisappear(_ aTemplate: CPTemplate, animated: Bool) {
|
||||
guard !preparedToPreviewTrips.isEmpty,
|
||||
let info = aTemplate.userInfo as? [String: String],
|
||||
let alertType = info[CPConstants.TemplateKey.alert],
|
||||
alertType == CPConstants.TemplateType.redirectRoute ||
|
||||
alertType == CPConstants.TemplateType.restoreRoute else {
|
||||
return
|
||||
}
|
||||
preparePreview(trips: preparedToPreviewTrips)
|
||||
preparedToPreviewTrips = []
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CPSessionConfigurationDelegate implementation
|
||||
extension CarPlayService: CPSessionConfigurationDelegate {
|
||||
func sessionConfiguration(_ sessionConfiguration: CPSessionConfiguration,
|
||||
limitedUserInterfacesChanged limitedUserInterfaces: CPLimitableUserInterface) {
|
||||
|
||||
}
|
||||
@available(iOS 13.0, *)
|
||||
func sessionConfiguration(_ sessionConfiguration: CPSessionConfiguration,
|
||||
contentStyleChanged contentStyle: CPContentStyle) {
|
||||
// Handle the CarPlay content style changing triggered by the 'Always Show Dark Maps' toggle.
|
||||
updateContentStyle(contentStyle)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CPMapTemplateDelegate implementation
|
||||
extension CarPlayService: CPMapTemplateDelegate {
|
||||
public func mapTemplateDidShowPanningInterface(_ mapTemplate: CPMapTemplate) {
|
||||
isUserPanMap = false
|
||||
MapTemplateBuilder.configurePanUI(mapTemplate: mapTemplate)
|
||||
FrameworkHelper.stopLocationFollow()
|
||||
}
|
||||
|
||||
public func mapTemplateDidDismissPanningInterface(_ mapTemplate: CPMapTemplate) {
|
||||
if let info = mapTemplate.userInfo as? MapInfo,
|
||||
info.type == CPConstants.TemplateType.navigation {
|
||||
MapTemplateBuilder.configureNavigationUI(mapTemplate: mapTemplate)
|
||||
} else {
|
||||
MapTemplateBuilder.configureBaseUI(mapTemplate: mapTemplate)
|
||||
}
|
||||
FrameworkHelper.switchMyPositionMode()
|
||||
}
|
||||
|
||||
@objc(mapTemplate:panEndedWithDirection:)
|
||||
func mapTemplate(_ mapTemplate: CPMapTemplate, panEndedWith direction: Int) {
|
||||
var offset = UIOffset(horizontal: 0.0, vertical: 0.0)
|
||||
let offsetStep: CGFloat = 0.25
|
||||
let panDirection = CPMapTemplate.PanDirection(rawValue: direction)
|
||||
if panDirection.contains(.up) { offset.vertical -= offsetStep }
|
||||
if panDirection.contains(.down) { offset.vertical += offsetStep }
|
||||
if panDirection.contains(.left) { offset.horizontal += offsetStep }
|
||||
if panDirection.contains(.right) { offset.horizontal -= offsetStep }
|
||||
FrameworkHelper.moveMap(offset)
|
||||
isUserPanMap = true
|
||||
}
|
||||
|
||||
|
||||
@objc(mapTemplate:panWithDirection:)
|
||||
func mapTemplate(_ mapTemplate: CPMapTemplate, panWith direction: Int) {
|
||||
var offset = UIOffset(horizontal: 0.0, vertical: 0.0)
|
||||
let offsetStep: CGFloat = 0.1
|
||||
let panDirection = CPMapTemplate.PanDirection(rawValue: direction)
|
||||
if panDirection.contains(.up) { offset.vertical -= offsetStep }
|
||||
if panDirection.contains(.down) { offset.vertical += offsetStep }
|
||||
if panDirection.contains(.left) { offset.horizontal += offsetStep }
|
||||
if panDirection.contains(.right) { offset.horizontal -= offsetStep }
|
||||
FrameworkHelper.moveMap(offset)
|
||||
isUserPanMap = true
|
||||
}
|
||||
|
||||
func mapTemplate(_ mapTemplate: CPMapTemplate, didUpdatePanGestureWithTranslation translation: CGPoint, velocity: CGPoint) {
|
||||
let scaleFactor = self.carplayVC?.mapView?.contentScaleFactor ?? 1
|
||||
FrameworkHelper.scrollMap(toDistanceX:-scaleFactor * translation.x, andY:-scaleFactor * translation.y);
|
||||
}
|
||||
|
||||
func mapTemplate(_ mapTemplate: CPMapTemplate, startedTrip trip: CPTrip, using routeChoice: CPRouteChoice) {
|
||||
guard let info = routeChoice.userInfo as? RouteInfo else {
|
||||
if let info = routeChoice.userInfo as? [String: Any],
|
||||
let code = info[CPConstants.Trip.errorCode] as? RouterResultCode,
|
||||
let countries = info[CPConstants.Trip.missedCountries] as? [String] {
|
||||
showErrorAlert(code: code, countries: countries)
|
||||
}
|
||||
return
|
||||
}
|
||||
mapTemplate.userInfo = MapInfo(type: CPConstants.TemplateType.previewAccepted)
|
||||
mapTemplate.hideTripPreviews()
|
||||
|
||||
guard let router = router,
|
||||
let interfaceController = interfaceController,
|
||||
let rootMapTemplate = rootMapTemplate else {
|
||||
return
|
||||
}
|
||||
|
||||
MapTemplateBuilder.configureNavigationUI(mapTemplate: rootMapTemplate)
|
||||
|
||||
if interfaceController.templates.count > 1 {
|
||||
interfaceController.popToRootTemplate(animated: false)
|
||||
}
|
||||
router.startNavigationSession(forTrip: trip, template: rootMapTemplate)
|
||||
router.startRoute()
|
||||
if let estimates = createEstimates(routeInfo: info) {
|
||||
rootMapTemplate.updateEstimates(estimates, for: trip)
|
||||
}
|
||||
|
||||
if let carplayVC = carplayVC {
|
||||
carplayVC.updateCurrentSpeed(info.speedMps, speedLimitMps: info.speedLimitMps)
|
||||
carplayVC.showSpeedControl()
|
||||
}
|
||||
updateVisibleViewPortState(.navigation)
|
||||
}
|
||||
|
||||
func mapTemplate(_ mapTemplate: CPMapTemplate, displayStyleFor maneuver: CPManeuver) -> CPManeuverDisplayStyle {
|
||||
if let type = maneuver.userInfo as? String,
|
||||
type == CPConstants.Maneuvers.secondary {
|
||||
return .trailingSymbol
|
||||
}
|
||||
return .leadingSymbol
|
||||
}
|
||||
|
||||
func mapTemplate(_ mapTemplate: CPMapTemplate,
|
||||
selectedPreviewFor trip: CPTrip,
|
||||
using routeChoice: CPRouteChoice) {
|
||||
guard let previewTrip = router?.previewTrip, previewTrip == trip else {
|
||||
applyUndefinedEstimates(template: mapTemplate, trip: trip)
|
||||
router?.buildRoute(trip: trip)
|
||||
return
|
||||
}
|
||||
guard let info = routeChoice.userInfo as? RouteInfo,
|
||||
let estimates = createEstimates(routeInfo: info) else {
|
||||
applyUndefinedEstimates(template: mapTemplate, trip: trip)
|
||||
router?.rebuildRoute()
|
||||
return
|
||||
}
|
||||
mapTemplate.updateEstimates(estimates, for: trip)
|
||||
routeChoice.userInfo = nil
|
||||
router?.rebuildRoute()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - CPListTemplateDelegate implementation
|
||||
extension CarPlayService: CPListTemplateDelegate {
|
||||
func listTemplate(_ listTemplate: CPListTemplate, didSelect item: CPListItem, completionHandler: @escaping () -> Void) {
|
||||
if let userInfo = item.userInfo as? ListItemInfo {
|
||||
switch userInfo.type {
|
||||
case CPConstants.ListItemType.history:
|
||||
let locale = window?.textInputMode?.primaryLanguage ?? "en"
|
||||
guard let searchService = searchService else {
|
||||
completionHandler()
|
||||
return
|
||||
}
|
||||
searchService.searchText(item.text ?? "", forInputLocale: locale, completionHandler: { [weak self] results in
|
||||
guard let self = self else { return }
|
||||
let template = ListTemplateBuilder.buildListTemplate(for: .searchResults(results: results))
|
||||
completionHandler()
|
||||
self.pushTemplate(template, animated: true)
|
||||
})
|
||||
case CPConstants.ListItemType.bookmarkLists where userInfo.metadata is CategoryInfo:
|
||||
let metadata = userInfo.metadata as! CategoryInfo
|
||||
let template = ListTemplateBuilder.buildListTemplate(for: .bookmarks(category: metadata.category))
|
||||
completionHandler()
|
||||
pushTemplate(template, animated: true)
|
||||
case CPConstants.ListItemType.bookmarks where userInfo.metadata is BookmarkInfo:
|
||||
let metadata = userInfo.metadata as! BookmarkInfo
|
||||
let bookmark = MWMCarPlayBookmarkObject(bookmarkId: metadata.bookmarkId)
|
||||
preparePreview(forBookmark: bookmark)
|
||||
completionHandler()
|
||||
case CPConstants.ListItemType.searchResults where userInfo.metadata is SearchResultInfo:
|
||||
let metadata = userInfo.metadata as! SearchResultInfo
|
||||
preparePreviewForSearchResults(selectedRow: metadata.originalRow)
|
||||
completionHandler()
|
||||
default:
|
||||
completionHandler()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CPSearchTemplateDelegate implementation
|
||||
extension CarPlayService: CPSearchTemplateDelegate {
|
||||
func searchTemplate(_ searchTemplate: CPSearchTemplate, updatedSearchText searchText: String, completionHandler: @escaping ([CPListItem]) -> Void) {
|
||||
self.searchText = searchText
|
||||
let locale = window?.textInputMode?.primaryLanguage ?? "en"
|
||||
guard let searchService = searchService else {
|
||||
completionHandler([])
|
||||
return
|
||||
}
|
||||
searchService.searchText(self.searchText, forInputLocale: locale, completionHandler: { results in
|
||||
var items = [CPListItem]()
|
||||
for object in results {
|
||||
let item = CPListItem(text: object.title, detailText: object.address)
|
||||
item.userInfo = ListItemInfo(type: CPConstants.ListItemType.searchResults,
|
||||
metadata: SearchResultInfo(originalRow: object.originalRow))
|
||||
items.append(item)
|
||||
}
|
||||
completionHandler(items)
|
||||
})
|
||||
}
|
||||
|
||||
func searchTemplate(_ searchTemplate: CPSearchTemplate, selectedResult item: CPListItem, completionHandler: @escaping () -> Void) {
|
||||
searchService?.saveLastQuery()
|
||||
if let info = item.userInfo as? ListItemInfo,
|
||||
let metadata = info.metadata as? SearchResultInfo {
|
||||
preparePreviewForSearchResults(selectedRow: metadata.originalRow)
|
||||
}
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
func searchTemplateSearchButtonPressed(_ searchTemplate: CPSearchTemplate) {
|
||||
let locale = window?.textInputMode?.primaryLanguage ?? "en"
|
||||
guard let searchService = searchService else {
|
||||
return
|
||||
}
|
||||
searchService.searchText(searchText, forInputLocale: locale, completionHandler: { [weak self] results in
|
||||
guard let self = self else { return }
|
||||
let template = ListTemplateBuilder.buildListTemplate(for: .searchResults(results: results))
|
||||
self.pushTemplate(template, animated: true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CarPlayRouterListener implementation
|
||||
extension CarPlayService: CarPlayRouterListener {
|
||||
func didCreateRoute(routeInfo: RouteInfo, trip: CPTrip) {
|
||||
guard let currentTemplate = interfaceController?.topTemplate as? CPMapTemplate,
|
||||
let info = currentTemplate.userInfo as? MapInfo,
|
||||
info.type == CPConstants.TemplateType.preview else {
|
||||
return
|
||||
}
|
||||
if let estimates = createEstimates(routeInfo: routeInfo) {
|
||||
currentTemplate.updateEstimates(estimates, for: trip)
|
||||
}
|
||||
}
|
||||
|
||||
func didUpdateRouteInfo(_ routeInfo: RouteInfo, forTrip trip: CPTrip) {
|
||||
if let carplayVC = carplayVC {
|
||||
carplayVC.updateCurrentSpeed(routeInfo.speedMps, speedLimitMps: routeInfo.speedLimitMps)
|
||||
}
|
||||
guard let router = router,
|
||||
let template = rootMapTemplate else {
|
||||
return
|
||||
}
|
||||
router.updateEstimates()
|
||||
if let estimates = createEstimates(routeInfo: routeInfo) {
|
||||
template.updateEstimates(estimates, for: trip)
|
||||
}
|
||||
trip.routeChoices.first?.userInfo = routeInfo
|
||||
}
|
||||
|
||||
func didFailureBuildRoute(forTrip trip: CPTrip, code: RouterResultCode, countries: [String]) {
|
||||
guard let template = interfaceController?.topTemplate as? CPMapTemplate else { return }
|
||||
trip.routeChoices.first?.userInfo = [CPConstants.Trip.errorCode: code, CPConstants.Trip.missedCountries: countries]
|
||||
applyUndefinedEstimates(template: template, trip: trip)
|
||||
}
|
||||
|
||||
func routeDidFinish(_ trip: CPTrip) {
|
||||
if router?.currentTrip == nil { return }
|
||||
router?.finishTrip()
|
||||
if let carplayVC = carplayVC {
|
||||
carplayVC.hideSpeedControl()
|
||||
}
|
||||
updateMapTemplateUIToTripFinished(trip)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - LocationModeListener implementation
|
||||
extension CarPlayService: LocationModeListener {
|
||||
func processMyPositionStateModeEvent(_ mode: MWMMyPositionMode) {
|
||||
currentPositionMode = mode
|
||||
guard let rootMapTemplate = rootMapTemplate,
|
||||
let info = rootMapTemplate.userInfo as? MapInfo,
|
||||
info.type == CPConstants.TemplateType.main else {
|
||||
return
|
||||
}
|
||||
switch mode {
|
||||
case .follow, .followAndRotate:
|
||||
if !rootMapTemplate.isPanningInterfaceVisible {
|
||||
MapTemplateBuilder.setupDestinationButton(mapTemplate: rootMapTemplate)
|
||||
}
|
||||
case .notFollow:
|
||||
if !rootMapTemplate.isPanningInterfaceVisible {
|
||||
MapTemplateBuilder.setupRecenterButton(mapTemplate: rootMapTemplate)
|
||||
}
|
||||
case .pendingPosition, .notFollowNoPosition:
|
||||
rootMapTemplate.leadingNavigationBarButtons = []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Alerts and Trip Previews
|
||||
extension CarPlayService {
|
||||
func preparePreviewForSearchResults(selectedRow row: Int) {
|
||||
var results = searchService?.lastResults ?? []
|
||||
if let currentItemIndex = results.firstIndex(where: { $0.originalRow == row }) {
|
||||
let item = results.remove(at: currentItemIndex)
|
||||
results.insert(item, at: 0)
|
||||
} else {
|
||||
results.insert(MWMCarPlaySearchResultObject(forRow: row), at: 0)
|
||||
}
|
||||
if let router = router,
|
||||
let startPoint = MWMRoutePoint(lastLocationAndType: .start,
|
||||
intermediateIndex: 0) {
|
||||
let endPoints = results.compactMap({ MWMRoutePoint(cgPoint: $0.mercatorPoint,
|
||||
title: $0.title,
|
||||
subtitle: $0.address,
|
||||
type: .finish,
|
||||
intermediateIndex: 0) })
|
||||
let trips = endPoints.map({ router.createTrip(startPoint: startPoint, endPoint: $0) })
|
||||
if router.currentTrip == nil {
|
||||
preparePreview(trips: trips)
|
||||
} else {
|
||||
showRerouteAlert(trips: trips)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func preparePreview(forBookmark bookmark: MWMCarPlayBookmarkObject) {
|
||||
if let router = router,
|
||||
let startPoint = MWMRoutePoint(lastLocationAndType: .start,
|
||||
intermediateIndex: 0),
|
||||
let endPoint = MWMRoutePoint(cgPoint: bookmark.mercatorPoint,
|
||||
title: bookmark.prefferedName,
|
||||
subtitle: bookmark.address,
|
||||
type: .finish,
|
||||
intermediateIndex: 0) {
|
||||
let trip = router.createTrip(startPoint: startPoint, endPoint: endPoint)
|
||||
if router.currentTrip == nil {
|
||||
preparePreview(trips: [trip])
|
||||
} else {
|
||||
showRerouteAlert(trips: [trip])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func preparePreview(trips: [CPTrip]) {
|
||||
let mapTemplate = MapTemplateBuilder.buildTripPreviewTemplate(forTrips: trips)
|
||||
if let interfaceController = interfaceController {
|
||||
mapTemplate.mapDelegate = self
|
||||
|
||||
if interfaceController.templates.count > 1 {
|
||||
interfaceController.popToRootTemplate(animated: false)
|
||||
}
|
||||
interfaceController.pushTemplate(mapTemplate, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
func showPreview(mapTemplate: CPMapTemplate, trips: [CPTrip]) {
|
||||
let tripTextConfig = CPTripPreviewTextConfiguration(startButtonTitle: L("trip_start"),
|
||||
additionalRoutesButtonTitle: nil,
|
||||
overviewButtonTitle: nil)
|
||||
mapTemplate.showTripPreviews(trips, textConfiguration: tripTextConfig)
|
||||
}
|
||||
|
||||
func createEstimates(routeInfo: RouteInfo) -> CPTravelEstimates? {
|
||||
let measurement = Measurement(value: routeInfo.targetDistance, unit: routeInfo.targetUnits)
|
||||
return CPTravelEstimates(distanceRemaining: measurement, timeRemaining: routeInfo.timeToTarget)
|
||||
}
|
||||
|
||||
func applyUndefinedEstimates(template: CPMapTemplate, trip: CPTrip) {
|
||||
let measurement = Measurement(value: -1,
|
||||
unit: UnitLength.meters)
|
||||
let estimates = CPTravelEstimates(distanceRemaining: measurement,
|
||||
timeRemaining: -1)
|
||||
template.updateEstimates(estimates, for: trip)
|
||||
}
|
||||
|
||||
func showRerouteAlert(trips: [CPTrip]) {
|
||||
let yesAction = CPAlertAction(title: L("yes"), style: .default, handler: { [unowned self] _ in
|
||||
self.router?.cancelTrip()
|
||||
self.updateMapTemplateUIToBase()
|
||||
self.preparedToPreviewTrips = trips
|
||||
self.interfaceController?.dismissTemplate(animated: true)
|
||||
})
|
||||
let noAction = CPAlertAction(title: L("no"), style: .cancel, handler: { [unowned self] _ in
|
||||
self.interfaceController?.dismissTemplate(animated: true)
|
||||
})
|
||||
let alert = CPAlertTemplate(titleVariants: [L("redirect_route_alert")], actions: [noAction, yesAction])
|
||||
alert.userInfo = [CPConstants.TemplateKey.alert: CPConstants.TemplateType.redirectRoute]
|
||||
presentAlert(alert, animated: true)
|
||||
}
|
||||
|
||||
func showKeyboardAlert() {
|
||||
let okAction = CPAlertAction(title: L("ok"), style: .default, handler: { [unowned self] _ in
|
||||
self.interfaceController?.dismissTemplate(animated: true)
|
||||
})
|
||||
let alert = CPAlertTemplate(titleVariants: [L("keyboard_availability_alert")], actions: [okAction])
|
||||
presentAlert(alert, animated: true)
|
||||
}
|
||||
|
||||
func showErrorAlert(code: RouterResultCode, countries: [String]) {
|
||||
var titleVariants = [String]()
|
||||
switch code {
|
||||
case .noCurrentPosition:
|
||||
titleVariants = ["\(L("dialog_routing_check_gps_carplay"))"]
|
||||
case .startPointNotFound:
|
||||
titleVariants = ["\(L("dialog_routing_change_start_carplay"))"]
|
||||
case .endPointNotFound:
|
||||
titleVariants = ["\(L("dialog_routing_change_end_carplay"))"]
|
||||
case .routeNotFoundRedressRouteError,
|
||||
.routeNotFound,
|
||||
.inconsistentMWMandRoute:
|
||||
titleVariants = ["\(L("dialog_routing_unable_locate_route_carplay"))"]
|
||||
case .routeFileNotExist,
|
||||
.fileTooOld,
|
||||
.needMoreMaps,
|
||||
.pointsInDifferentMWM:
|
||||
titleVariants = ["\(L("dialog_routing_download_files_carplay"))"]
|
||||
case .internalError,
|
||||
.intermediatePointNotFound:
|
||||
titleVariants = ["\(L("dialog_routing_system_error_carplay"))"]
|
||||
case .noError,
|
||||
.cancelled,
|
||||
.hasWarnings,
|
||||
.transitRouteNotFoundNoNetwork,
|
||||
.transitRouteNotFoundTooLongPedestrian:
|
||||
return
|
||||
}
|
||||
|
||||
let okAction = CPAlertAction(title: L("ok"), style: .cancel, handler: { [unowned self] _ in
|
||||
self.interfaceController?.dismissTemplate(animated: true)
|
||||
})
|
||||
let alert = CPAlertTemplate(titleVariants: titleVariants, actions: [okAction])
|
||||
presentAlert(alert, animated: true)
|
||||
}
|
||||
|
||||
func showRecoverRouteAlert(trip: CPTrip, isTypeCorrect: Bool) {
|
||||
let yesAction = CPAlertAction(title: L("ok"), style: .default, handler: { [unowned self] _ in
|
||||
var info = trip.userInfo as? [String: MWMRoutePoint]
|
||||
|
||||
if let startPoint = MWMRoutePoint(lastLocationAndType: .start,
|
||||
intermediateIndex: 0) {
|
||||
info?[CPConstants.Trip.start] = startPoint
|
||||
}
|
||||
trip.userInfo = info
|
||||
self.preparedToPreviewTrips = [trip]
|
||||
self.router?.updateStartPointAndRebuild(trip: trip)
|
||||
self.interfaceController?.dismissTemplate(animated: true)
|
||||
})
|
||||
let noAction = CPAlertAction(title: L("cancel"), style: .cancel, handler: { [unowned self] _ in
|
||||
FrameworkHelper.rotateMap(0.0, animated: false)
|
||||
self.router?.completeRouteAndRemovePoints()
|
||||
self.interfaceController?.dismissTemplate(animated: true)
|
||||
})
|
||||
let title = isTypeCorrect ? L("dialog_routing_rebuild_from_current_location_carplay") : L("dialog_routing_rebuild_for_vehicle_carplay")
|
||||
let alert = CPAlertTemplate(titleVariants: [title], actions: [noAction, yesAction])
|
||||
alert.userInfo = [CPConstants.TemplateKey.alert: CPConstants.TemplateType.restoreRoute]
|
||||
presentAlert(alert, animated: true)
|
||||
}
|
||||
}
|
||||
51
iphone/Maps/Classes/CarPlay/CarPlayWindowScaleAdjuster.swift
Normal file
51
iphone/Maps/Classes/CarPlay/CarPlayWindowScaleAdjuster.swift
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
|
||||
enum CarPlayWindowScaleAdjuster {
|
||||
|
||||
static func updateAppearance(
|
||||
fromWindow sourceWindow: UIWindow,
|
||||
toWindow destinationWindow: UIWindow,
|
||||
isCarplayActivated: Bool
|
||||
) {
|
||||
|
||||
let sourceContentScale = sourceWindow.screen.scale;
|
||||
let destinationContentScale = destinationWindow.screen.scale;
|
||||
|
||||
if abs(sourceContentScale - destinationContentScale) > 0.1 {
|
||||
if isCarplayActivated {
|
||||
updateVisualScale(to: destinationContentScale)
|
||||
} else {
|
||||
updateVisualScaleToMain()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func updateVisualScale(to scale: CGFloat) {
|
||||
if isGraphicContextInitialized {
|
||||
mapViewController?.mapView.updateVisualScale(to: scale)
|
||||
} else {
|
||||
DispatchQueue.main.async {
|
||||
updateVisualScale(to: scale)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func updateVisualScaleToMain() {
|
||||
if isGraphicContextInitialized {
|
||||
mapViewController?.mapView.updateVisualScaleToMain()
|
||||
} else {
|
||||
DispatchQueue.main.async {
|
||||
updateVisualScaleToMain()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static var isGraphicContextInitialized: Bool {
|
||||
return mapViewController?.mapView.graphicContextInitialized ?? false
|
||||
}
|
||||
|
||||
private static var mapViewController: MapViewController? {
|
||||
return MapViewController.shared()
|
||||
}
|
||||
}
|
||||
93
iphone/Maps/Classes/CarPlay/CarplayPlaceholderView.swift
Normal file
93
iphone/Maps/Classes/CarPlay/CarplayPlaceholderView.swift
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
import Foundation
|
||||
|
||||
class CarplayPlaceholderView: UIView {
|
||||
private let containerView = UIView()
|
||||
private let imageView = UIImageView()
|
||||
private let descriptionLabel = UILabel()
|
||||
private let switchButton = UIButton(type: .system);
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
addSubview(containerView)
|
||||
|
||||
imageView.image = UIImage(named: "ic_carplay_activated")
|
||||
imageView.contentMode = .scaleAspectFit
|
||||
containerView.addSubview(imageView)
|
||||
|
||||
descriptionLabel.text = L("car_used_on_the_car_screen")
|
||||
descriptionLabel.font = UIFont.bold24()
|
||||
descriptionLabel.textAlignment = .center
|
||||
descriptionLabel.numberOfLines = 0
|
||||
containerView.addSubview(descriptionLabel)
|
||||
|
||||
switchButton.setTitle(L("car_continue_on_the_phone"), for: .normal)
|
||||
switchButton.addTarget(self, action: #selector(onSwitchButtonTap), for: .touchUpInside)
|
||||
switchButton.titleLabel?.font = UIFont.medium16()
|
||||
switchButton.titleLabel?.lineBreakMode = .byWordWrapping
|
||||
switchButton.titleLabel?.textAlignment = .center
|
||||
switchButton.layer.cornerRadius = 8
|
||||
containerView.addSubview(switchButton)
|
||||
|
||||
updateColors()
|
||||
setupConstraints()
|
||||
}
|
||||
|
||||
override func applyTheme() {
|
||||
super.applyTheme()
|
||||
updateColors()
|
||||
}
|
||||
|
||||
private func updateColors() {
|
||||
backgroundColor = UIColor.carplayPlaceholderBackground()
|
||||
descriptionLabel.textColor = UIColor.blackSecondaryText()
|
||||
switchButton.backgroundColor = UIColor.linkBlue()
|
||||
switchButton.setTitleColor(UIColor.whitePrimaryText(), for: .normal)
|
||||
}
|
||||
|
||||
@objc private func onSwitchButtonTap(_ sender: UIButton) {
|
||||
CarPlayService.shared.showOnPhone()
|
||||
}
|
||||
|
||||
private func setupConstraints() {
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
containerView.translatesAutoresizingMaskIntoConstraints = false
|
||||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
descriptionLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
switchButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
let horizontalPadding: CGFloat = 24
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
containerView.centerYAnchor.constraint(equalTo: centerYAnchor),
|
||||
containerView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor),
|
||||
containerView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
containerView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
containerView.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor),
|
||||
|
||||
imageView.topAnchor.constraint(equalTo: containerView.topAnchor),
|
||||
imageView.centerXAnchor.constraint(equalTo: centerXAnchor),
|
||||
imageView.widthAnchor.constraint(equalToConstant: 160),
|
||||
imageView.heightAnchor.constraint(equalToConstant: 160),
|
||||
|
||||
descriptionLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 32),
|
||||
descriptionLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: horizontalPadding),
|
||||
descriptionLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -horizontalPadding),
|
||||
|
||||
switchButton.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: 24),
|
||||
switchButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: horizontalPadding),
|
||||
switchButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -horizontalPadding),
|
||||
switchButton.heightAnchor.constraint(equalToConstant: 48),
|
||||
switchButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
|
||||
])
|
||||
}
|
||||
}
|
||||
13
iphone/Maps/Classes/CarPlay/MWMCarPlaySearchResultObject.h
Normal file
13
iphone/Maps/Classes/CarPlay/MWMCarPlaySearchResultObject.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MWMCarPlaySearchResultObject : NSObject
|
||||
@property(assign, nonatomic, readonly) NSInteger originalRow;
|
||||
@property(strong, nonatomic, readonly) NSString *title;
|
||||
@property(strong, nonatomic, readonly) NSString *address;
|
||||
@property(assign, nonatomic, readonly) CLLocationCoordinate2D coordinate;
|
||||
@property(assign, nonatomic, readonly) CGPoint mercatorPoint;
|
||||
|
||||
- (instancetype)initForRow:(NSInteger)row;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
43
iphone/Maps/Classes/CarPlay/MWMCarPlaySearchResultObject.mm
Normal file
43
iphone/Maps/Classes/CarPlay/MWMCarPlaySearchResultObject.mm
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#import "MWMCarPlaySearchResultObject.h"
|
||||
#import "MWMSearch.h"
|
||||
#import "SearchResult.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
#include "search/result.hpp"
|
||||
|
||||
#include "indexer/classificator.hpp"
|
||||
|
||||
#include "geometry/mercator.hpp"
|
||||
|
||||
#include "platform/localization.hpp"
|
||||
|
||||
@interface MWMCarPlaySearchResultObject()
|
||||
@property(assign, nonatomic, readwrite) NSInteger originalRow;
|
||||
@property(strong, nonatomic, readwrite) NSString *title;
|
||||
@property(strong, nonatomic, readwrite) NSString *address;
|
||||
@property(assign, nonatomic, readwrite) CLLocationCoordinate2D coordinate;
|
||||
@property(assign, nonatomic, readwrite) CGPoint mercatorPoint;
|
||||
@end
|
||||
|
||||
@implementation MWMCarPlaySearchResultObject
|
||||
|
||||
- (instancetype)initForRow:(NSInteger)row {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.originalRow = row;
|
||||
NSInteger containerIndex = [MWMSearch containerIndexWithRow:row];
|
||||
SearchItemType type = [MWMSearch resultTypeWithRow:row];
|
||||
if (type == SearchItemTypeRegular) {
|
||||
auto const & result = [MWMSearch resultWithContainerIndex:containerIndex];
|
||||
self.title = result.titleText;
|
||||
self.address = result.addressText;
|
||||
self.coordinate = result.coordinate;
|
||||
auto const pivot = mercator::FromLatLon(result.coordinate.latitude, result.coordinate.longitude);
|
||||
self.mercatorPoint = CGPointMake(pivot.x, pivot.y);
|
||||
return self;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
17
iphone/Maps/Classes/CarPlay/MWMCarPlaySearchService.h
Normal file
17
iphone/Maps/Classes/CarPlay/MWMCarPlaySearchService.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
NS_ASSUME_NONNULL_BEGIN
|
||||
@class MWMCarPlaySearchResultObject;
|
||||
|
||||
API_AVAILABLE(ios(12.0))
|
||||
NS_SWIFT_NAME(CarPlaySearchService)
|
||||
@interface MWMCarPlaySearchService : NSObject
|
||||
@property(strong, nonatomic, readonly) NSArray<MWMCarPlaySearchResultObject *> *lastResults;
|
||||
|
||||
- (instancetype)init;
|
||||
|
||||
- (void)searchText:(NSString *)text
|
||||
forInputLocale:(NSString *)inputLocale
|
||||
completionHandler:(void (^)(NSArray<MWMCarPlaySearchResultObject *> *searchResults))completionHandler;
|
||||
- (void)saveLastQuery;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
65
iphone/Maps/Classes/CarPlay/MWMCarPlaySearchService.mm
Normal file
65
iphone/Maps/Classes/CarPlay/MWMCarPlaySearchService.mm
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#import "MWMCarPlaySearchService.h"
|
||||
#import "MWMCarPlaySearchResultObject.h"
|
||||
#import "MWMSearch.h"
|
||||
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
API_AVAILABLE(ios(12.0))
|
||||
@interface MWMCarPlaySearchService ()<MWMSearchObserver>
|
||||
@property(strong, nonatomic, nullable) void (^completionHandler)(NSArray<MWMCarPlaySearchResultObject *> *searchResults);
|
||||
@property(strong, nonatomic, nullable) NSString *lastQuery;
|
||||
@property(strong, nonatomic, nullable) NSString *inputLocale;
|
||||
@property(strong, nonatomic, readwrite) NSArray<MWMCarPlaySearchResultObject *> *lastResults;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMCarPlaySearchService
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[MWMSearch addObserver:self];
|
||||
self.lastResults = @[];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)searchText:(NSString *)text
|
||||
forInputLocale:(NSString *)inputLocale
|
||||
completionHandler:(void (^)(NSArray<MWMCarPlaySearchResultObject *> *searchResults))completionHandler {
|
||||
self.lastQuery = text;
|
||||
self.inputLocale = inputLocale;
|
||||
self.lastResults = @[];
|
||||
self.completionHandler = completionHandler;
|
||||
/// @todo Didn't find pure category request in CarPlay.
|
||||
[MWMSearch setSearchMode:SearchModeEverywhere];
|
||||
SearchQuery * query = [[SearchQuery alloc] init:text locale:inputLocale source:SearchTextSourceTypedText];
|
||||
[MWMSearch searchQuery:query];
|
||||
}
|
||||
|
||||
- (void)saveLastQuery {
|
||||
if (self.lastQuery != nil && self.inputLocale != nil) {
|
||||
SearchQuery * query = [[SearchQuery alloc] init:self.lastQuery locale:self.inputLocale source:SearchTextSourceTypedText];
|
||||
[MWMSearch saveQuery:query];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - MWMSearchObserver
|
||||
|
||||
- (void)onSearchCompleted {
|
||||
void (^completionHandler)(NSArray<MWMCarPlaySearchResultObject *> *searchResults) = self.completionHandler;
|
||||
if (completionHandler == nil) { return; }
|
||||
|
||||
NSMutableArray<MWMCarPlaySearchResultObject *> *results = [NSMutableArray array];
|
||||
NSInteger count = [MWMSearch resultsCount];
|
||||
for (NSInteger row = 0; row < count; row++) {
|
||||
MWMCarPlaySearchResultObject *result = [[MWMCarPlaySearchResultObject alloc] initForRow:row];
|
||||
if (result != nil) { [results addObject:result]; }
|
||||
}
|
||||
|
||||
self.lastResults = results;
|
||||
completionHandler(results);
|
||||
self.completionHandler = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
import CarPlay
|
||||
|
||||
final class ListTemplateBuilder {
|
||||
enum ListTemplateType {
|
||||
case history
|
||||
case bookmarkLists
|
||||
case bookmarks(category: BookmarkGroup)
|
||||
case searchResults(results: [MWMCarPlaySearchResultObject])
|
||||
}
|
||||
|
||||
enum BarButtonType {
|
||||
case bookmarks
|
||||
case search
|
||||
}
|
||||
|
||||
// MARK: - CPListTemplate builder
|
||||
class func buildListTemplate(for type: ListTemplateType) -> CPListTemplate {
|
||||
var title = ""
|
||||
var trailingNavigationBarButtons = [CPBarButton]()
|
||||
switch type {
|
||||
case .history:
|
||||
title = L("pick_destination")
|
||||
let bookmarksButton = buildBarButton(type: .bookmarks) { _ in
|
||||
let listTemplate = ListTemplateBuilder.buildListTemplate(for: .bookmarkLists)
|
||||
CarPlayService.shared.pushTemplate(listTemplate, animated: true)
|
||||
}
|
||||
let searchButton = buildBarButton(type: .search) { _ in
|
||||
if CarPlayService.shared.isKeyboardLimited {
|
||||
CarPlayService.shared.showKeyboardAlert()
|
||||
} else {
|
||||
let searchTemplate = SearchTemplateBuilder.buildSearchTemplate()
|
||||
CarPlayService.shared.pushTemplate(searchTemplate, animated: true)
|
||||
}
|
||||
}
|
||||
trailingNavigationBarButtons = [searchButton, bookmarksButton]
|
||||
case .bookmarkLists:
|
||||
title = L("bookmarks")
|
||||
case .searchResults:
|
||||
title = L("search_results")
|
||||
case .bookmarks(let category):
|
||||
title = category.title
|
||||
}
|
||||
|
||||
let sections = buildSectionsForType(type)
|
||||
let template = CPListTemplate(title: title, sections: sections)
|
||||
template.trailingNavigationBarButtons = trailingNavigationBarButtons
|
||||
return template
|
||||
}
|
||||
|
||||
private class func buildSectionsForType(_ type: ListTemplateType) -> [CPListSection] {
|
||||
switch type {
|
||||
case .history:
|
||||
return buildHistorySections()
|
||||
case .bookmarks(let category):
|
||||
return buildBookmarksSections(categoryId: category.categoryId)
|
||||
case .bookmarkLists:
|
||||
return buildBookmarkListsSections()
|
||||
case .searchResults(let results):
|
||||
return buildSearchResultsSections(results)
|
||||
}
|
||||
}
|
||||
|
||||
private class func buildHistorySections() -> [CPListSection] {
|
||||
let searchQueries = FrameworkHelper.obtainLastSearchQueries()
|
||||
let items = searchQueries.map({ (text) -> CPListItem in
|
||||
let item = CPListItem(text: text, detailText: nil, image: UIImage(named: "recent"))
|
||||
item.userInfo = ListItemInfo(type: CPConstants.ListItemType.history,
|
||||
metadata: nil)
|
||||
return item
|
||||
})
|
||||
return [CPListSection(items: items)]
|
||||
}
|
||||
|
||||
private class func buildBookmarkListsSections() -> [CPListSection] {
|
||||
let bookmarkManager = BookmarksManager.shared()
|
||||
let categories = bookmarkManager.sortedUserCategories()
|
||||
let items: [CPListItem] = categories.compactMap({ category in
|
||||
if category.bookmarksCount == 0 { return nil }
|
||||
let placesString = category.placesCountTitle()
|
||||
let item = CPListItem(text: category.title, detailText: placesString)
|
||||
item.userInfo = ListItemInfo(type: CPConstants.ListItemType.bookmarkLists,
|
||||
metadata: CategoryInfo(category: category))
|
||||
return item
|
||||
})
|
||||
return [CPListSection(items: items)]
|
||||
}
|
||||
|
||||
private class func buildBookmarksSections(categoryId: MWMMarkGroupID) -> [CPListSection] {
|
||||
let bookmarkManager = BookmarksManager.shared()
|
||||
let bookmarks = bookmarkManager.bookmarks(forCategory: categoryId)
|
||||
var items = bookmarks.map({ (bookmark) -> CPListItem in
|
||||
let item = CPListItem(text: bookmark.prefferedName, detailText: bookmark.address)
|
||||
item.userInfo = ListItemInfo(type: CPConstants.ListItemType.bookmarks,
|
||||
metadata: BookmarkInfo(categoryId: categoryId,
|
||||
bookmarkId: bookmark.bookmarkId))
|
||||
return item
|
||||
})
|
||||
let maxItemCount = CPListTemplate.maximumItemCount - 1
|
||||
if items.count >= maxItemCount {
|
||||
items = Array(items.prefix(maxItemCount))
|
||||
let cropWarning = CPListItem(text: L("not_all_shown_bookmarks_carplay"), detailText: L("switch_to_phone_bookmarks_carplay"))
|
||||
cropWarning.isEnabled = false
|
||||
items.append(cropWarning)
|
||||
}
|
||||
return [CPListSection(items: items)]
|
||||
}
|
||||
|
||||
private class func buildSearchResultsSections(_ results: [MWMCarPlaySearchResultObject]) -> [CPListSection] {
|
||||
var items = [CPListItem]()
|
||||
for object in results {
|
||||
let item = CPListItem(text: object.title, detailText: object.address)
|
||||
item.userInfo = ListItemInfo(type: CPConstants.ListItemType.searchResults,
|
||||
metadata: SearchResultInfo(originalRow: object.originalRow))
|
||||
items.append(item)
|
||||
}
|
||||
return [CPListSection(items: items)]
|
||||
}
|
||||
|
||||
|
||||
// MARK: - CPBarButton builder
|
||||
private class func buildBarButton(type: BarButtonType, action: ((CPBarButton) -> Void)?) -> CPBarButton {
|
||||
switch type {
|
||||
case .bookmarks:
|
||||
return CPBarButton(image: UIImage(systemName: "list.star")!, handler: action)
|
||||
case .search:
|
||||
return CPBarButton(image: UIImage(systemName: "keyboard.fill")!, handler: action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
import CarPlay
|
||||
|
||||
final class MapTemplateBuilder {
|
||||
enum MapButtonType {
|
||||
case startPanning
|
||||
case zoomIn
|
||||
case zoomOut
|
||||
}
|
||||
enum BarButtonType {
|
||||
case dismissPaning
|
||||
case destination
|
||||
case recenter
|
||||
case settings
|
||||
case mute
|
||||
case unmute
|
||||
case redirectRoute
|
||||
case endRoute
|
||||
}
|
||||
|
||||
private enum Constants {
|
||||
static let carPlayGuidanceBackgroundColor = UIColor(46, 100, 51, 1.0)
|
||||
}
|
||||
|
||||
// MARK: - CPMapTemplate builders
|
||||
class func buildBaseTemplate(positionMode: MWMMyPositionMode) -> CPMapTemplate {
|
||||
let mapTemplate = CPMapTemplate()
|
||||
mapTemplate.hidesButtonsWithNavigationBar = false
|
||||
configureBaseUI(mapTemplate: mapTemplate)
|
||||
if positionMode == .pendingPosition {
|
||||
mapTemplate.leadingNavigationBarButtons = []
|
||||
} else if positionMode == .follow || positionMode == .followAndRotate {
|
||||
setupDestinationButton(mapTemplate: mapTemplate)
|
||||
} else {
|
||||
setupRecenterButton(mapTemplate: mapTemplate)
|
||||
}
|
||||
return mapTemplate
|
||||
}
|
||||
|
||||
class func buildNavigationTemplate() -> CPMapTemplate {
|
||||
let mapTemplate = CPMapTemplate()
|
||||
mapTemplate.hidesButtonsWithNavigationBar = false
|
||||
configureNavigationUI(mapTemplate: mapTemplate)
|
||||
return mapTemplate
|
||||
}
|
||||
|
||||
class func buildTripPreviewTemplate(forTrips trips: [CPTrip]) -> CPMapTemplate {
|
||||
let mapTemplate = CPMapTemplate()
|
||||
mapTemplate.userInfo = MapInfo(type: CPConstants.TemplateType.preview, trips: trips)
|
||||
mapTemplate.mapButtons = []
|
||||
mapTemplate.leadingNavigationBarButtons = []
|
||||
let settingsButton = buildBarButton(type: .settings) { _ in
|
||||
mapTemplate.userInfo = MapInfo(type: CPConstants.TemplateType.previewSettings)
|
||||
let gridTemplate = SettingsTemplateBuilder.buildGridTemplate()
|
||||
CarPlayService.shared.pushTemplate(gridTemplate, animated: true)
|
||||
}
|
||||
mapTemplate.trailingNavigationBarButtons = [settingsButton]
|
||||
return mapTemplate
|
||||
}
|
||||
|
||||
// MARK: - MapTemplate UI configs
|
||||
class func configureBaseUI(mapTemplate: CPMapTemplate) {
|
||||
mapTemplate.userInfo = MapInfo(type: CPConstants.TemplateType.main)
|
||||
let panningButton = buildMapButton(type: .startPanning) { _ in
|
||||
mapTemplate.showPanningInterface(animated: true)
|
||||
}
|
||||
let zoomInButton = buildMapButton(type: .zoomIn) { _ in
|
||||
FrameworkHelper.zoomMap(.in)
|
||||
}
|
||||
let zoomOutButton = buildMapButton(type: .zoomOut) { _ in
|
||||
FrameworkHelper.zoomMap(.out)
|
||||
}
|
||||
mapTemplate.mapButtons = [panningButton, zoomInButton, zoomOutButton]
|
||||
|
||||
let settingsButton = buildBarButton(type: .settings) { _ in
|
||||
let gridTemplate = SettingsTemplateBuilder.buildGridTemplate()
|
||||
CarPlayService.shared.pushTemplate(gridTemplate, animated: true)
|
||||
}
|
||||
mapTemplate.trailingNavigationBarButtons = [settingsButton]
|
||||
}
|
||||
|
||||
class func configurePanUI(mapTemplate: CPMapTemplate) {
|
||||
let zoomInButton = buildMapButton(type: .zoomIn) { _ in
|
||||
FrameworkHelper.zoomMap(.in)
|
||||
}
|
||||
let zoomOutButton = buildMapButton(type: .zoomOut) { _ in
|
||||
FrameworkHelper.zoomMap(.out)
|
||||
}
|
||||
mapTemplate.mapButtons = [zoomInButton, zoomOutButton]
|
||||
|
||||
let doneButton = buildBarButton(type: .dismissPaning) { _ in
|
||||
mapTemplate.dismissPanningInterface(animated: true)
|
||||
}
|
||||
mapTemplate.leadingNavigationBarButtons = []
|
||||
mapTemplate.trailingNavigationBarButtons = [doneButton]
|
||||
}
|
||||
|
||||
class func configureNavigationUI(mapTemplate: CPMapTemplate) {
|
||||
mapTemplate.userInfo = MapInfo(type: CPConstants.TemplateType.navigation)
|
||||
let panningButton = buildMapButton(type: .startPanning) { _ in
|
||||
mapTemplate.showPanningInterface(animated: true)
|
||||
}
|
||||
mapTemplate.mapButtons = [panningButton]
|
||||
setupMuteAndRedirectButtons(template: mapTemplate)
|
||||
let endButton = buildBarButton(type: .endRoute) { _ in
|
||||
CarPlayService.shared.cancelCurrentTrip()
|
||||
}
|
||||
mapTemplate.trailingNavigationBarButtons = [endButton]
|
||||
mapTemplate.guidanceBackgroundColor = Constants.carPlayGuidanceBackgroundColor
|
||||
}
|
||||
|
||||
// MARK: - Conditional navigation buttons
|
||||
class func setupDestinationButton(mapTemplate: CPMapTemplate) {
|
||||
let destinationButton = buildBarButton(type: .destination) { _ in
|
||||
let listTemplate = ListTemplateBuilder.buildListTemplate(for: .history)
|
||||
CarPlayService.shared.pushTemplate(listTemplate, animated: true)
|
||||
}
|
||||
mapTemplate.leadingNavigationBarButtons = [destinationButton]
|
||||
}
|
||||
|
||||
class func setupRecenterButton(mapTemplate: CPMapTemplate) {
|
||||
let recenterButton = buildBarButton(type: .recenter) { _ in
|
||||
FrameworkHelper.switchMyPositionMode()
|
||||
}
|
||||
mapTemplate.leadingNavigationBarButtons = [recenterButton]
|
||||
}
|
||||
|
||||
private class func setupMuteAndRedirectButtons(template: CPMapTemplate) {
|
||||
let redirectButton = buildBarButton(type: .redirectRoute) { _ in
|
||||
let listTemplate = ListTemplateBuilder.buildListTemplate(for: .history)
|
||||
CarPlayService.shared.pushTemplate(listTemplate, animated: true)
|
||||
}
|
||||
if MWMTextToSpeech.isTTSEnabled() {
|
||||
let muteButton = buildBarButton(type: .mute) { _ in
|
||||
MWMTextToSpeech.tts().active = false
|
||||
setupUnmuteAndRedirectButtons(template: template)
|
||||
}
|
||||
template.leadingNavigationBarButtons = [muteButton, redirectButton]
|
||||
} else {
|
||||
template.leadingNavigationBarButtons = [redirectButton]
|
||||
}
|
||||
}
|
||||
|
||||
private class func setupUnmuteAndRedirectButtons(template: CPMapTemplate) {
|
||||
let redirectButton = buildBarButton(type: .redirectRoute) { _ in
|
||||
let listTemplate = ListTemplateBuilder.buildListTemplate(for: .history)
|
||||
CarPlayService.shared.pushTemplate(listTemplate, animated: true)
|
||||
}
|
||||
if MWMTextToSpeech.isTTSEnabled() {
|
||||
let unmuteButton = buildBarButton(type: .unmute) { _ in
|
||||
MWMTextToSpeech.tts().active = true
|
||||
setupMuteAndRedirectButtons(template: template)
|
||||
}
|
||||
template.leadingNavigationBarButtons = [unmuteButton, redirectButton]
|
||||
} else {
|
||||
template.leadingNavigationBarButtons = [redirectButton]
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CPMapButton builder
|
||||
private class func buildMapButton(type: MapButtonType, action: ((CPMapButton) -> Void)?) -> CPMapButton {
|
||||
let button = CPMapButton(handler: action)
|
||||
switch type {
|
||||
case .startPanning:
|
||||
button.image = UIImage(systemName: "arrow.up.and.down.and.arrow.left.and.right")
|
||||
case .zoomIn:
|
||||
button.image = UIImage(systemName: "plus")
|
||||
case .zoomOut:
|
||||
button.image = UIImage(systemName: "minus")
|
||||
}
|
||||
// Remove code below once Apple has fixed its issue with the button background
|
||||
if #unavailable(iOS 26) {
|
||||
switch type {
|
||||
case .startPanning:
|
||||
button.focusedImage = UIImage(systemName: "smallcircle.filled.circle.fill")
|
||||
case .zoomIn:
|
||||
button.focusedImage = UIImage(systemName: "plus.circle.fill")
|
||||
case .zoomOut:
|
||||
button.focusedImage = UIImage(systemName: "minus.circle.fill")
|
||||
}
|
||||
}
|
||||
return button
|
||||
}
|
||||
|
||||
// MARK: - CPBarButton builder
|
||||
private class func buildBarButton(type: BarButtonType, action: ((CPBarButton) -> Void)?) -> CPBarButton {
|
||||
switch type {
|
||||
case .dismissPaning:
|
||||
return CPBarButton(title: L("done"), handler: action)
|
||||
case .destination:
|
||||
return CPBarButton(title: L("pick_destination"), handler: action)
|
||||
case .recenter:
|
||||
return CPBarButton(title: L("follow_my_position"), handler: action)
|
||||
case .settings:
|
||||
return CPBarButton(image: UIImage(systemName: "gearshape.fill")!, handler: action)
|
||||
case .mute:
|
||||
return CPBarButton(image: UIImage(systemName: "speaker.wave.3")!, handler: action)
|
||||
case .unmute:
|
||||
return CPBarButton(image: UIImage(systemName: "speaker.slash")!, handler: action)
|
||||
case .redirectRoute:
|
||||
return CPBarButton(image: UIImage(named: "ic_carplay_redirect_route")!, handler: action)
|
||||
case .endRoute:
|
||||
return CPBarButton(title: L("navigation_stop_button").capitalized, handler: action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import CarPlay
|
||||
|
||||
final class SearchTemplateBuilder {
|
||||
// MARK: - CPSearchTemplate builder
|
||||
class func buildSearchTemplate() -> CPSearchTemplate {
|
||||
let template = CPSearchTemplate()
|
||||
return template
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
import CarPlay
|
||||
|
||||
final class SettingsTemplateBuilder {
|
||||
// MARK: - CPGridTemplate builder
|
||||
class func buildGridTemplate() -> CPGridTemplate {
|
||||
let actions = SettingsTemplateBuilder.buildGridButtons()
|
||||
let gridTemplate = CPGridTemplate(title: L("settings"),
|
||||
gridButtons: actions)
|
||||
|
||||
return gridTemplate
|
||||
}
|
||||
|
||||
private class func buildGridButtons() -> [CPGridButton] {
|
||||
let options = RoutingOptions()
|
||||
return [createTollButton(options: options),
|
||||
createUnpavedButton(options: options),
|
||||
createPavedButton(options: options),
|
||||
createMotorwayButton(options: options),
|
||||
createFerryButton(options: options),
|
||||
createStepsButton(options: options),
|
||||
createSpeedcamButton()]
|
||||
}
|
||||
|
||||
// MARK: - CPGridButton builders
|
||||
private class func createTollButton(options: RoutingOptions) -> CPGridButton {
|
||||
var tollIconName = "tolls.circle"
|
||||
if options.avoidToll { tollIconName += ".slash" }
|
||||
let configuration = UIImage.SymbolConfiguration(textStyle: .title1)
|
||||
var image = UIImage(named: tollIconName, in: nil, with: configuration)!
|
||||
if #unavailable(iOS 26) {
|
||||
image = image.withTintColor(.white, renderingMode: .alwaysTemplate)
|
||||
image = UIImage(data: image.pngData()!)!.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
let tollButton = CPGridButton(titleVariants: [L("avoid_tolls")], image: image) { _ in
|
||||
options.avoidToll = !options.avoidToll
|
||||
options.save()
|
||||
CarPlayService.shared.updateRouteAfterChangingSettings()
|
||||
CarPlayService.shared.popTemplate(animated: true)
|
||||
}
|
||||
return tollButton
|
||||
}
|
||||
|
||||
private class func createUnpavedButton(options: RoutingOptions) -> CPGridButton {
|
||||
var unpavedIconName = "unpaved.circle"
|
||||
if options.avoidDirty && !options.avoidPaved { unpavedIconName += ".slash" }
|
||||
let configuration = UIImage.SymbolConfiguration(textStyle: .title1)
|
||||
var image = UIImage(named: unpavedIconName, in: nil, with: configuration)!
|
||||
if #unavailable(iOS 26) {
|
||||
image = image.withTintColor(.white, renderingMode: .alwaysTemplate)
|
||||
image = UIImage(data: image.pngData()!)!.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
let unpavedButton = CPGridButton(titleVariants: [L("avoid_unpaved")], image: image) { _ in
|
||||
options.avoidDirty = !options.avoidDirty
|
||||
if options.avoidDirty {
|
||||
options.avoidPaved = false
|
||||
}
|
||||
options.save()
|
||||
CarPlayService.shared.updateRouteAfterChangingSettings()
|
||||
CarPlayService.shared.popTemplate(animated: true)
|
||||
}
|
||||
unpavedButton.isEnabled = !options.avoidPaved
|
||||
return unpavedButton
|
||||
}
|
||||
|
||||
private class func createPavedButton(options: RoutingOptions) -> CPGridButton {
|
||||
var pavedIconName = "paved.circle"
|
||||
if options.avoidPaved && !options.avoidDirty { pavedIconName += ".slash" }
|
||||
let configuration = UIImage.SymbolConfiguration(textStyle: .title1)
|
||||
var image = UIImage(named: pavedIconName, in: nil, with: configuration)!
|
||||
if #unavailable(iOS 26) {
|
||||
image = image.withTintColor(.white, renderingMode: .alwaysTemplate)
|
||||
image = UIImage(data: image.pngData()!)!.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
let pavedButton = CPGridButton(titleVariants: [L("avoid_paved")], image: image) { _ in
|
||||
options.avoidPaved = !options.avoidPaved
|
||||
if options.avoidPaved {
|
||||
options.avoidDirty = false
|
||||
}
|
||||
options.save()
|
||||
CarPlayService.shared.updateRouteAfterChangingSettings()
|
||||
CarPlayService.shared.popTemplate(animated: true)
|
||||
}
|
||||
pavedButton.isEnabled = !options.avoidDirty
|
||||
return pavedButton
|
||||
}
|
||||
|
||||
private class func createMotorwayButton(options: RoutingOptions) -> CPGridButton {
|
||||
var motorwayIconName = "motorways.circle"
|
||||
if options.avoidMotorway { motorwayIconName += ".slash" }
|
||||
let configuration = UIImage.SymbolConfiguration(textStyle: .title1)
|
||||
var image = UIImage(named: motorwayIconName, in: nil, with: configuration)!
|
||||
if #unavailable(iOS 26) {
|
||||
image = image.withTintColor(.white, renderingMode: .alwaysTemplate)
|
||||
image = UIImage(data: image.pngData()!)!.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
let motorwayButton = CPGridButton(titleVariants: [L("avoid_motorways")], image: image) { _ in
|
||||
options.avoidMotorway = !options.avoidMotorway
|
||||
options.save()
|
||||
CarPlayService.shared.updateRouteAfterChangingSettings()
|
||||
CarPlayService.shared.popTemplate(animated: true)
|
||||
}
|
||||
return motorwayButton
|
||||
}
|
||||
|
||||
private class func createFerryButton(options: RoutingOptions) -> CPGridButton {
|
||||
var ferryIconName = "ferries.circle"
|
||||
if options.avoidFerry { ferryIconName += ".slash" }
|
||||
let configuration = UIImage.SymbolConfiguration(textStyle: .title1)
|
||||
var image = UIImage(named: ferryIconName, in: nil, with: configuration)!
|
||||
if #unavailable(iOS 26) {
|
||||
image = image.withTintColor(.white, renderingMode: .alwaysTemplate)
|
||||
image = UIImage(data: image.pngData()!)!.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
let ferryButton = CPGridButton(titleVariants: [L("avoid_ferry")], image: image) { _ in
|
||||
options.avoidFerry = !options.avoidFerry
|
||||
options.save()
|
||||
CarPlayService.shared.updateRouteAfterChangingSettings()
|
||||
CarPlayService.shared.popTemplate(animated: true)
|
||||
}
|
||||
return ferryButton
|
||||
}
|
||||
|
||||
private class func createStepsButton(options: RoutingOptions) -> CPGridButton {
|
||||
var stepsIconName = "steps.circle"
|
||||
if options.avoidSteps { stepsIconName += ".slash" }
|
||||
let configuration = UIImage.SymbolConfiguration(textStyle: .title1)
|
||||
var image = UIImage(named: stepsIconName, in: nil, with: configuration)!
|
||||
if #unavailable(iOS 26) {
|
||||
image = image.withTintColor(.white, renderingMode: .alwaysTemplate)
|
||||
image = UIImage(data: image.pngData()!)!.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
let stepsButton = CPGridButton(titleVariants: [L("avoid_steps")], image: image) { _ in
|
||||
options.avoidSteps = !options.avoidSteps
|
||||
options.save()
|
||||
CarPlayService.shared.updateRouteAfterChangingSettings()
|
||||
CarPlayService.shared.popTemplate(animated: true)
|
||||
}
|
||||
return stepsButton
|
||||
}
|
||||
|
||||
private class func createSpeedcamButton() -> CPGridButton {
|
||||
var speedcamIconName = "speedcamera"
|
||||
let isSpeedCamActivated = CarPlayService.shared.isSpeedCamActivated
|
||||
if !isSpeedCamActivated { speedcamIconName += ".slash" }
|
||||
let configuration = UIImage.SymbolConfiguration(textStyle: .title1)
|
||||
var image = UIImage(named: speedcamIconName, in: nil, with: configuration)!
|
||||
if #unavailable(iOS 26) {
|
||||
image = image.withTintColor(.white, renderingMode: .alwaysTemplate)
|
||||
image = UIImage(data: image.pngData()!)!.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
let speedButton = CPGridButton(titleVariants: [L("speedcams_alert_title_carplay_1"), L("speedcams_alert_title_carplay_2")], image: image) { _ in
|
||||
CarPlayService.shared.isSpeedCamActivated = !isSpeedCamActivated
|
||||
CarPlayService.shared.popTemplate(animated: true)
|
||||
}
|
||||
return speedButton
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
struct BookmarkInfo: InfoMetadata {
|
||||
let categoryId: UInt64
|
||||
let bookmarkId: UInt64
|
||||
|
||||
init(categoryId: UInt64, bookmarkId: UInt64) {
|
||||
self.categoryId = categoryId
|
||||
self.bookmarkId = bookmarkId
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
struct CategoryInfo: InfoMetadata {
|
||||
let category: BookmarkGroup
|
||||
|
||||
init(category: BookmarkGroup) {
|
||||
self.category = category
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
protocol InfoMetadata {}
|
||||
|
||||
struct ListItemInfo {
|
||||
let type: String
|
||||
let metadata: InfoMetadata?
|
||||
|
||||
init(type: String, metadata: InfoMetadata?) {
|
||||
self.type = type
|
||||
self.metadata = metadata
|
||||
}
|
||||
}
|
||||
9
iphone/Maps/Classes/CarPlay/Templates Data/MapInfo.swift
Normal file
9
iphone/Maps/Classes/CarPlay/Templates Data/MapInfo.swift
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
struct MapInfo {
|
||||
let type: String
|
||||
let trips: [CPTrip]?
|
||||
|
||||
init(type: String, trips: [CPTrip]? = nil) {
|
||||
self.type = type
|
||||
self.trips = trips
|
||||
}
|
||||
}
|
||||
57
iphone/Maps/Classes/CarPlay/Templates Data/RouteInfo.swift
Normal file
57
iphone/Maps/Classes/CarPlay/Templates Data/RouteInfo.swift
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
@objc(MWMRouteInfo)
|
||||
class RouteInfo: NSObject {
|
||||
let timeToTarget: TimeInterval
|
||||
let targetDistance: Double
|
||||
let targetUnits: UnitLength
|
||||
let distanceToTurn: Double
|
||||
let turnUnits: UnitLength
|
||||
let streetName: String
|
||||
let turnImageName: String?
|
||||
let nextTurnImageName: String?
|
||||
let speedMps: Double
|
||||
let speedLimitMps: Double?
|
||||
let roundExitNumber: Int
|
||||
|
||||
@objc init(timeToTarget: TimeInterval,
|
||||
targetDistance: Double,
|
||||
targetUnitsIndex: UInt8,
|
||||
distanceToTurn: Double,
|
||||
turnUnitsIndex: UInt8,
|
||||
streetName: String,
|
||||
turnImageName: String?,
|
||||
nextTurnImageName: String?,
|
||||
speedMps: Double,
|
||||
speedLimitMps: Double,
|
||||
roundExitNumber: Int) {
|
||||
self.timeToTarget = timeToTarget
|
||||
self.targetDistance = targetDistance
|
||||
self.targetUnits = RouteInfo.unitLength(for: targetUnitsIndex)
|
||||
self.distanceToTurn = distanceToTurn
|
||||
self.turnUnits = RouteInfo.unitLength(for: turnUnitsIndex)
|
||||
self.streetName = streetName;
|
||||
self.turnImageName = turnImageName
|
||||
self.nextTurnImageName = nextTurnImageName
|
||||
self.speedMps = speedMps
|
||||
// speedLimitMps >= 0 means known limited speed.
|
||||
self.speedLimitMps = speedLimitMps < 0 ? nil : speedLimitMps
|
||||
self.roundExitNumber = roundExitNumber
|
||||
}
|
||||
|
||||
|
||||
/// > Warning: Order of enum values MUST BE the same with
|
||||
/// > native ``Distance::Units`` enum (see platform/distance.hpp for details).
|
||||
class func unitLength(for targetUnitsIndex: UInt8) -> UnitLength {
|
||||
switch targetUnitsIndex {
|
||||
case 0:
|
||||
return .meters
|
||||
case 1:
|
||||
return .kilometers
|
||||
case 2:
|
||||
return .feet
|
||||
case 3:
|
||||
return .miles
|
||||
default:
|
||||
return .meters
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
struct SearchResultInfo: InfoMetadata {
|
||||
let originalRow: Int
|
||||
|
||||
init(originalRow: Int) {
|
||||
self.originalRow = originalRow
|
||||
}
|
||||
}
|
||||
18
iphone/Maps/Classes/CircleView.h
Normal file
18
iphone/Maps/Classes/CircleView.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface CircleView : UIView
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame andColor:(UIColor *)color;
|
||||
- (id)initWithFrame:(CGRect)frame andColor:(UIColor *)color andImageName:(nullable NSString *)imageName;
|
||||
|
||||
+ (UIImage *)createCircleImageWithFrameSize:(CGFloat)frameSize andDiameter:(CGFloat)diameter andColor:(UIColor *)color;
|
||||
+ (UIImage *)createCircleImageWithDiameter:(CGFloat)diameter andColor:(UIColor *)color;
|
||||
+ (UIImage *)createCircleImageWithDiameter:(CGFloat)diameter
|
||||
andColor:(UIColor *)color
|
||||
andImageName:(NSString *)imageName;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
80
iphone/Maps/Classes/CircleView.m
Normal file
80
iphone/Maps/Classes/CircleView.m
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
#import "CircleView.h"
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
@interface CircleView()
|
||||
|
||||
@property(nonatomic) UIColor *circleColor;
|
||||
@property(nonatomic) UIImage *image;
|
||||
|
||||
@end
|
||||
|
||||
@implementation CircleView
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame andColor:(UIColor *)color {
|
||||
return [self initWithFrame:frame andColor:color andImageName:nil];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame andColor:(UIColor *)color andImageName:(nullable NSString *)imageName {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
_circleColor = color;
|
||||
if (imageName)
|
||||
_image = [UIImage imageNamed:imageName];
|
||||
self.opaque = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect {
|
||||
CGContextRef ctx = UIGraphicsGetCurrentContext();
|
||||
CGContextAddEllipseInRect(ctx, rect);
|
||||
CGContextSetFillColor(ctx, CGColorGetComponents(self.circleColor.CGColor));
|
||||
CGContextFillPath(ctx);
|
||||
|
||||
if (self.image)
|
||||
[self.image drawInRect:CGRectMake(3, 3, rect.size.width - 6, rect.size.height - 6)];
|
||||
}
|
||||
|
||||
+ (UIImage *)createCircleImageWithFrameSize:(CGFloat)frameSize
|
||||
andDiameter:(CGFloat)diameter
|
||||
andColor:(UIColor *)color
|
||||
andImageName:(nullable NSString *)imageName {
|
||||
UIView *circleView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, frameSize, frameSize)];
|
||||
circleView.backgroundColor = UIColor.clearColor;
|
||||
CircleView *circle = [[self alloc] initWithFrame:CGRectMake(0.5, 0.5, diameter - 1, diameter - 1)
|
||||
andColor:color
|
||||
andImageName:imageName];
|
||||
circle.center = circleView.center;
|
||||
[circleView addSubview:circle];
|
||||
return [self imageWithView:circleView];
|
||||
}
|
||||
|
||||
+ (UIImage *)createCircleImageWithFrameSize:(CGFloat)frameSize
|
||||
andDiameter:(CGFloat)diameter
|
||||
andColor:(UIColor *)color {
|
||||
return [self createCircleImageWithFrameSize:frameSize andDiameter:diameter andColor:color andImageName:nil];
|
||||
}
|
||||
|
||||
+ (UIImage *)createCircleImageWithDiameter:(CGFloat)diameter andColor:(UIColor *)color {
|
||||
return [self createCircleImageWithFrameSize:diameter andDiameter:diameter andColor:color andImageName:nil];
|
||||
}
|
||||
|
||||
+ (UIImage *)createCircleImageWithDiameter:(CGFloat)diameter
|
||||
andColor:(UIColor *)color
|
||||
andImageName:(NSString *)imageName {
|
||||
return [self createCircleImageWithFrameSize:diameter andDiameter:diameter andColor:color andImageName:imageName];
|
||||
}
|
||||
|
||||
+ (UIImage *)imageWithView:(UIView *)view {
|
||||
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:view.bounds.size];
|
||||
|
||||
UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
|
||||
[view.layer renderInContext:rendererContext.CGContext];
|
||||
}];
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@end
|
||||
37
iphone/Maps/Classes/Components/BorderedButton.swift
Normal file
37
iphone/Maps/Classes/Components/BorderedButton.swift
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
@objc(MWMBorderedButton)
|
||||
final class BorderedButton: UIButton {
|
||||
|
||||
private var borderColor: UIColor?
|
||||
private var borderHighlightedColor: UIColor?
|
||||
private var borderDisabledColor: UIColor?
|
||||
|
||||
@objc func setBorderColor(_ color: UIColor) {
|
||||
borderColor = color
|
||||
}
|
||||
|
||||
@objc func setBorderHighlightedColor(_ color: UIColor) {
|
||||
borderHighlightedColor = color
|
||||
}
|
||||
|
||||
@objc func setBorderDisabledColor(_ color: UIColor) {
|
||||
borderDisabledColor = color
|
||||
}
|
||||
|
||||
private func updateBorder() {
|
||||
if !isEnabled {
|
||||
layer.borderColor = borderDisabledColor?.cgColor ?? titleColor(for: .disabled)?.cgColor
|
||||
} else if isHighlighted {
|
||||
layer.borderColor = borderHighlightedColor?.cgColor ?? titleColor(for: .highlighted)?.cgColor
|
||||
} else {
|
||||
layer.borderColor = borderColor?.cgColor ?? titleColor(for: .normal)?.cgColor
|
||||
}
|
||||
}
|
||||
|
||||
override var isEnabled: Bool {
|
||||
didSet { updateBorder() }
|
||||
}
|
||||
|
||||
override var isHighlighted: Bool {
|
||||
didSet { updateBorder() }
|
||||
}
|
||||
}
|
||||
112
iphone/Maps/Classes/Components/Checkmark.swift
Normal file
112
iphone/Maps/Classes/Components/Checkmark.swift
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
class Checkmark: UIControl {
|
||||
|
||||
private let imageView = UIImageView(frame: .zero)
|
||||
|
||||
@IBInspectable
|
||||
var offImage: UIImage? {
|
||||
didSet {
|
||||
updateImage(animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var onImage: UIImage? {
|
||||
didSet {
|
||||
updateImage(animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var offTintColor: UIColor? {
|
||||
didSet {
|
||||
updateTintColor()
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var onTintColor: UIColor? {
|
||||
didSet {
|
||||
updateTintColor()
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var isChecked: Bool = false {
|
||||
didSet {
|
||||
updateImage(animated: true)
|
||||
updateTintColor()
|
||||
}
|
||||
}
|
||||
|
||||
override var isHighlighted: Bool {
|
||||
didSet {
|
||||
imageView.tintColor = isHighlighted ? tintColor.blending(with: UIColor(white: 0, alpha: 0.5)) : nil
|
||||
}
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
initViews()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
initViews()
|
||||
}
|
||||
|
||||
private func initViews() {
|
||||
addSubview(imageView)
|
||||
addTarget(self, action: #selector(onTouch), for: .touchUpInside)
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
imageView.sizeToFit()
|
||||
|
||||
var left: CGFloat = 0;
|
||||
var top: CGFloat = 0;
|
||||
var width: CGFloat = imageView.width;
|
||||
var height: CGFloat = imageView.height;
|
||||
|
||||
switch contentHorizontalAlignment {
|
||||
case .right: fallthrough
|
||||
case .trailing:
|
||||
left = floor(bounds.width - imageView.width)
|
||||
case .center:
|
||||
left = floor((bounds.width - width) / 2)
|
||||
case .fill:
|
||||
width = bounds.width
|
||||
default:
|
||||
left = 0
|
||||
}
|
||||
|
||||
switch contentVerticalAlignment {
|
||||
case .top:
|
||||
top = 0
|
||||
case .bottom:
|
||||
top = floor(bounds.height - height)
|
||||
case .center:
|
||||
top = floor((bounds.height - height) / 2)
|
||||
case .fill:
|
||||
height = bounds.height
|
||||
@unknown default:
|
||||
fatalError("Unexpected case in contentVerticalAlignment switch")
|
||||
}
|
||||
|
||||
imageView.frame = CGRect(x: left, y: top, width: width, height: height)
|
||||
}
|
||||
|
||||
@objc func onTouch() {
|
||||
isChecked = !isChecked
|
||||
sendActions(for: .valueChanged)
|
||||
}
|
||||
|
||||
private func updateImage(animated: Bool) {
|
||||
self.imageView.image = self.isChecked ? self.onImage : self.offImage
|
||||
}
|
||||
|
||||
private func updateTintColor() {
|
||||
tintColor = isChecked ? onTintColor : offTintColor
|
||||
}
|
||||
}
|
||||
48
iphone/Maps/Classes/Components/CopyableLabel.swift
Normal file
48
iphone/Maps/Classes/Components/CopyableLabel.swift
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// Label shows copy popup menu on tap or long tap.
|
||||
class CopyableLabel: UILabel {
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.sharedInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
self.sharedInit()
|
||||
}
|
||||
|
||||
private func sharedInit() {
|
||||
self.isUserInteractionEnabled = true
|
||||
self.gestureRecognizers = [
|
||||
UILongPressGestureRecognizer(target: self, action: #selector(self.showMenu)),
|
||||
UITapGestureRecognizer(target: self, action: #selector(self.showMenu))
|
||||
]
|
||||
}
|
||||
|
||||
@objc func showMenu(_ recognizer: UILongPressGestureRecognizer) {
|
||||
self.becomeFirstResponder()
|
||||
|
||||
let menu = UIMenuController.shared
|
||||
let locationOfTouchInLabel = recognizer.location(in: self)
|
||||
|
||||
if !menu.isMenuVisible {
|
||||
var rect = bounds
|
||||
rect.origin = locationOfTouchInLabel
|
||||
rect.size = CGSize(width: 1, height: 1)
|
||||
menu.showMenu(from: self, rect: rect)
|
||||
}
|
||||
}
|
||||
|
||||
override func copy(_ sender: Any?) {
|
||||
UIPasteboard.general.string = text
|
||||
UIMenuController.shared.hideMenu()
|
||||
}
|
||||
|
||||
override var canBecomeFirstResponder: Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
|
||||
return action == #selector(UIResponderStandardEditActions.copy)
|
||||
}
|
||||
}
|
||||
42
iphone/Maps/Classes/Components/DimBackground.swift
Normal file
42
iphone/Maps/Classes/Components/DimBackground.swift
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
@objc(MWMDimBackground)
|
||||
final class DimBackground: SolidTouchView {
|
||||
private let mainView: UIView
|
||||
private var tapAction: () -> Void
|
||||
|
||||
@objc init(mainView: UIView, tapAction: @escaping () -> Void) {
|
||||
self.mainView = mainView
|
||||
self.tapAction = tapAction
|
||||
super.init(frame: mainView.superview!.bounds)
|
||||
setStyle(.fadeBackground)
|
||||
autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTap)))
|
||||
}
|
||||
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc func setVisible(_ visible: Bool, completion: (() -> Void)?) {
|
||||
if visible {
|
||||
let sv = mainView.superview!
|
||||
frame = sv.bounds
|
||||
sv.insertSubview(self, belowSubview: mainView)
|
||||
alpha = 0
|
||||
} else {
|
||||
alpha = 0.8
|
||||
}
|
||||
UIView.animate(withDuration: kDefaultAnimationDuration,
|
||||
animations: { self.alpha = visible ? 0.8 : 0 },
|
||||
completion: { _ in
|
||||
if !visible {
|
||||
self.removeFromSuperview()
|
||||
}
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
|
||||
@objc
|
||||
private func onTap() {
|
||||
tapAction()
|
||||
}
|
||||
}
|
||||
13
iphone/Maps/Classes/Components/LeftAlignedIconButton.swift
Normal file
13
iphone/Maps/Classes/Components/LeftAlignedIconButton.swift
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
@IBDesignable
|
||||
class LeftAlignedIconButton: UIButton {
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
contentHorizontalAlignment = .left
|
||||
let availableSpace = bounds.inset(by: contentEdgeInsets)
|
||||
let imageWidth = imageView?.frame.width ?? 0
|
||||
let titleWidth = titleLabel?.frame.width ?? 0
|
||||
let availableWidth = availableSpace.width - imageEdgeInsets.right - imageWidth * 2 - titleWidth
|
||||
titleEdgeInsets = UIEdgeInsets(top: 0, left: floor(availableWidth) / 2, bottom: 0, right: 0)
|
||||
}
|
||||
}
|
||||
|
||||
5
iphone/Maps/Classes/Components/LinkTextView.swift
Normal file
5
iphone/Maps/Classes/Components/LinkTextView.swift
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
class LinkTextView : UITextView {
|
||||
override var canBecomeFirstResponder: Bool {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
11
iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.h
Normal file
11
iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#include "geometry/point2d.hpp"
|
||||
|
||||
@interface MWMAddPlaceNavigationBar : SolidTouchView
|
||||
|
||||
+ (void)showInSuperview:(UIView *)superview
|
||||
isBusiness:(BOOL)isBusiness
|
||||
position:(m2::PointD const *)optionalPosition
|
||||
doneBlock:(MWMVoidBlock)done
|
||||
cancelBlock:(MWMVoidBlock)cancel;
|
||||
|
||||
@end
|
||||
76
iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.mm
Normal file
76
iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.mm
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#import "MWMAddPlaceNavigationBar.h"
|
||||
|
||||
#include <CoreApi/Framework.h>
|
||||
|
||||
@interface MWMAddPlaceNavigationBar ()
|
||||
|
||||
@property(copy, nonatomic) MWMVoidBlock doneBlock;
|
||||
@property(copy, nonatomic) MWMVoidBlock cancelBlock;
|
||||
@property(assign, nonatomic) NSLayoutConstraint* topConstraint;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMAddPlaceNavigationBar
|
||||
|
||||
+ (void)showInSuperview:(UIView *)superview
|
||||
isBusiness:(BOOL)isBusiness
|
||||
position:(m2::PointD const *)optionalPosition
|
||||
doneBlock:(MWMVoidBlock)done
|
||||
cancelBlock:(MWMVoidBlock)cancel
|
||||
{
|
||||
MWMAddPlaceNavigationBar * navBar =
|
||||
[NSBundle.mainBundle loadNibNamed:self.className owner:nil options:nil].firstObject;
|
||||
navBar.width = superview.width;
|
||||
navBar.doneBlock = done;
|
||||
navBar.cancelBlock = cancel;
|
||||
navBar.translatesAutoresizingMaskIntoConstraints = false;
|
||||
|
||||
[superview addSubview:navBar];
|
||||
navBar.topConstraint = [navBar.topAnchor constraintEqualToAnchor:superview.topAnchor];
|
||||
navBar.topConstraint.active = true;
|
||||
navBar.topConstraint.constant = -navBar.height;
|
||||
[navBar.trailingAnchor constraintEqualToAnchor:superview.trailingAnchor].active = true;
|
||||
[navBar.leadingAnchor constraintEqualToAnchor:superview.leadingAnchor].active = true;
|
||||
[navBar show:isBusiness position:optionalPosition];
|
||||
}
|
||||
|
||||
- (void)show:(BOOL)enableBounds position:(m2::PointD const *)optionalPosition
|
||||
{
|
||||
auto & f = GetFramework();
|
||||
f.EnableChoosePositionMode(true /* enable */, enableBounds, optionalPosition);
|
||||
f.BlockTapEvents(true);
|
||||
|
||||
[UIView animateWithDuration:kDefaultAnimationDuration animations:^
|
||||
{
|
||||
self.topConstraint.constant = 0;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)dismissWithBlock:(MWMVoidBlock)block
|
||||
{
|
||||
auto & f = GetFramework();
|
||||
f.EnableChoosePositionMode(false /* enable */, false /* enableBounds */, nullptr /* optionalPosition */);
|
||||
f.BlockTapEvents(false);
|
||||
|
||||
[UIView animateWithDuration:kDefaultAnimationDuration animations:^
|
||||
{
|
||||
self.topConstraint.constant = -self.height;
|
||||
}
|
||||
completion:^(BOOL finished)
|
||||
{
|
||||
[self removeFromSuperview];
|
||||
block();
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)doneTap
|
||||
{
|
||||
[self dismissWithBlock:self.doneBlock];
|
||||
}
|
||||
|
||||
- (IBAction)cancelTap
|
||||
{
|
||||
[self dismissWithBlock:self.cancelBlock];
|
||||
}
|
||||
|
||||
@end
|
||||
137
iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.xib
Normal file
137
iphone/Maps/Classes/Components/MWMAddPlaceNavigationBar.xib
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="24128" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24063"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="BJ6-My-yAy" customClass="MWMAddPlaceNavigationBar" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="100"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IhK-ce-TXk">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="64"/>
|
||||
<color key="backgroundColor" red="0.1176470588" green="0.58823529409999997" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="PrimaryBackground"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ZKD-Be-eMp">
|
||||
<rect key="frame" x="0.0" y="20" width="320" height="44"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" horizontalCompressionResistancePriority="1000" contentHorizontalAlignment="right" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="zs9-MX-bIe">
|
||||
<rect key="frame" x="258" y="12" width="54" height="19"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="19" id="psK-YU-lUF"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="Готово"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="NavigationBarItem"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="continue_button"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="doneTap" destination="BJ6-My-yAy" eventType="touchUpInside" id="hS1-Tz-6Zo"/>
|
||||
</connections>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="1000" text="Местоположение" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8So-22-JS1">
|
||||
<rect key="frame" x="90" y="12" width="140" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="JFT-iW-rJY"/>
|
||||
<constraint firstAttribute="height" constant="20" id="mun-d1-wdT"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="NavigationBarItem"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="editor_add_select_location"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" horizontalCompressionResistancePriority="1000" contentHorizontalAlignment="left" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="boX-sU-7DW">
|
||||
<rect key="frame" x="8" y="12" width="62" height="19"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="19" id="KDz-RG-UH2"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="Отмена"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="NavigationBarItem"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="cancel"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="cancelTap" destination="BJ6-My-yAy" eventType="touchUpInside" id="f2O-El-juu"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.1176470588" green="0.58823529409999997" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="5VI-KR-aXh"/>
|
||||
<constraint firstItem="zs9-MX-bIe" firstAttribute="top" secondItem="ZKD-Be-eMp" secondAttribute="top" constant="12" id="BNr-Gr-o3j"/>
|
||||
<constraint firstItem="8So-22-JS1" firstAttribute="top" secondItem="ZKD-Be-eMp" secondAttribute="top" constant="12" id="CCm-fM-FcD"/>
|
||||
<constraint firstItem="boX-sU-7DW" firstAttribute="top" secondItem="ZKD-Be-eMp" secondAttribute="top" constant="12" id="Ofu-8n-bBo"/>
|
||||
<constraint firstItem="boX-sU-7DW" firstAttribute="leading" secondItem="ZKD-Be-eMp" secondAttribute="leading" constant="8" id="Sbg-dn-iro"/>
|
||||
<constraint firstAttribute="trailing" secondItem="zs9-MX-bIe" secondAttribute="trailing" constant="8" id="jiJ-gk-0bx"/>
|
||||
<constraint firstItem="8So-22-JS1" firstAttribute="centerX" secondItem="ZKD-Be-eMp" secondAttribute="centerX" id="m6T-st-PPz"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="PrimaryBackground"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="UsM-Al-MCC">
|
||||
<rect key="frame" x="0.0" y="64" width="320" height="56"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Потяните карту, чтобы выбрать правильное местоположение места. " lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="R6E-kO-6Lb">
|
||||
<rect key="frame" x="14" y="16" width="292" height="30"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" systemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular12:blackSecondaryText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="editor_focus_map_on_location"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.96078431372549022" green="0.96078431372549022" blue="0.96078431372549022" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="R6E-kO-6Lb" firstAttribute="leading" secondItem="UsM-Al-MCC" secondAttribute="leadingMargin" constant="6" id="F0k-Ua-uzh"/>
|
||||
<constraint firstItem="R6E-kO-6Lb" firstAttribute="top" secondItem="UsM-Al-MCC" secondAttribute="topMargin" constant="8" id="G1O-ji-PsY"/>
|
||||
<constraint firstAttribute="height" constant="56" id="RVj-Yg-4Hc"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="R6E-kO-6Lb" secondAttribute="trailing" constant="6" id="cRp-da-FUC"/>
|
||||
<constraint firstAttribute="bottomMargin" secondItem="R6E-kO-6Lb" secondAttribute="bottom" constant="2" id="e1g-Jr-sY9"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MenuBackground"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="tvs-e8-vSU"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="top" secondItem="IhK-ce-TXk" secondAttribute="top" id="B9q-oF-68E"/>
|
||||
<constraint firstItem="UsM-Al-MCC" firstAttribute="leading" secondItem="BJ6-My-yAy" secondAttribute="leading" id="C3j-SM-MSO"/>
|
||||
<constraint firstItem="ZKD-Be-eMp" firstAttribute="leading" secondItem="BJ6-My-yAy" secondAttribute="leading" id="Ckp-sr-B7c"/>
|
||||
<constraint firstItem="ZKD-Be-eMp" firstAttribute="top" secondItem="tvs-e8-vSU" secondAttribute="top" id="Drs-L4-75n"/>
|
||||
<constraint firstItem="IhK-ce-TXk" firstAttribute="leading" secondItem="tvs-e8-vSU" secondAttribute="leading" id="E1h-R5-QaY"/>
|
||||
<constraint firstAttribute="bottom" secondItem="UsM-Al-MCC" secondAttribute="bottom" id="Eii-Wt-WAj"/>
|
||||
<constraint firstItem="tvs-e8-vSU" firstAttribute="trailing" secondItem="IhK-ce-TXk" secondAttribute="trailing" id="FcR-B0-Kwb"/>
|
||||
<constraint firstAttribute="trailing" secondItem="UsM-Al-MCC" secondAttribute="trailing" id="FrY-zu-zwd"/>
|
||||
<constraint firstItem="UsM-Al-MCC" firstAttribute="top" secondItem="IhK-ce-TXk" secondAttribute="bottom" id="Tfo-lj-gPb"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ZKD-Be-eMp" secondAttribute="trailing" id="Yrh-qN-0sk"/>
|
||||
<constraint firstItem="IhK-ce-TXk" firstAttribute="bottom" secondItem="ZKD-Be-eMp" secondAttribute="bottom" id="fae-mk-c1a"/>
|
||||
</constraints>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="321.60000000000002" y="248.27586206896552"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<systemColor name="darkTextColor">
|
||||
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
17
iphone/Maps/Classes/Components/MWMButton.h
Normal file
17
iphone/Maps/Classes/Components/MWMButton.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
typedef NS_ENUM(NSUInteger, MWMButtonColoring)
|
||||
{
|
||||
MWMButtonColoringOther,
|
||||
MWMButtonColoringBlue,
|
||||
MWMButtonColoringBlack,
|
||||
MWMButtonColoringWhite,
|
||||
MWMButtonColoringWhiteText,
|
||||
MWMButtonColoringGray,
|
||||
MWMButtonColoringRed
|
||||
};
|
||||
|
||||
@interface MWMButton : UIButton
|
||||
|
||||
@property (copy, nonatomic) NSString * imageName;
|
||||
@property (nonatomic) MWMButtonColoring coloring;
|
||||
|
||||
@end
|
||||
153
iphone/Maps/Classes/Components/MWMButton.m
Normal file
153
iphone/Maps/Classes/Components/MWMButton.m
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
#import "MWMButton.h"
|
||||
#import "SwiftBridge.h"
|
||||
static NSString * const kDefaultPattern = @"%@_%@";
|
||||
static NSString * const kHighlightedPattern = @"%@_highlighted_%@";
|
||||
static NSString * const kSelectedPattern = @"%@_selected_%@";
|
||||
|
||||
@implementation MWMButton
|
||||
|
||||
- (void)setImageName:(NSString *)imageName
|
||||
{
|
||||
_imageName = imageName;
|
||||
[self setDefaultImages];
|
||||
}
|
||||
|
||||
// This method is overridden by MWMButtonRenderer.swift
|
||||
//- (void)applyTheme
|
||||
//{
|
||||
// [self changeColoringToOpposite];
|
||||
// [super applyTheme];
|
||||
//}
|
||||
|
||||
- (void)setColoring:(MWMButtonColoring)coloring
|
||||
{
|
||||
_coloring = coloring;
|
||||
[self setEnabled:self.enabled];
|
||||
}
|
||||
|
||||
- (void)changeColoringToOpposite
|
||||
{
|
||||
if (self.coloring == MWMButtonColoringOther)
|
||||
{
|
||||
if (self.imageName)
|
||||
{
|
||||
[self setDefaultImages];
|
||||
self.imageView.image = [self imageForState:self.state];
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (self.state == UIControlStateNormal)
|
||||
[self setDefaultTintColor];
|
||||
else if (self.state == UIControlStateHighlighted)
|
||||
[self setHighlighted:YES];
|
||||
else if (self.state == UIControlStateSelected)
|
||||
[self setSelected:YES];
|
||||
}
|
||||
|
||||
- (void)setDefaultImages
|
||||
{
|
||||
NSString * postfix = [UIColor isNightMode] ? @"dark" : @"light";
|
||||
[self setImage:[UIImage imageNamed:[NSString stringWithFormat:kDefaultPattern, self.imageName, postfix]] forState:UIControlStateNormal];
|
||||
[self setImage:[UIImage imageNamed:[NSString stringWithFormat:kHighlightedPattern, self.imageName, postfix]] forState:UIControlStateHighlighted];
|
||||
[self setImage:[UIImage imageNamed:[NSString stringWithFormat:kSelectedPattern, self.imageName, postfix]] forState:UIControlStateSelected];
|
||||
}
|
||||
|
||||
- (void)setHighlighted:(BOOL)highlighted
|
||||
{
|
||||
[super setHighlighted:highlighted];
|
||||
if (highlighted)
|
||||
{
|
||||
switch (self.coloring)
|
||||
{
|
||||
case MWMButtonColoringBlue:
|
||||
self.tintColor = [UIColor linkBlueHighlighted];
|
||||
break;
|
||||
case MWMButtonColoringBlack:
|
||||
self.tintColor = [UIColor blackHintText];
|
||||
break;
|
||||
case MWMButtonColoringGray:
|
||||
self.tintColor = [UIColor blackDividers];
|
||||
break;
|
||||
case MWMButtonColoringWhiteText:
|
||||
self.tintColor = [UIColor whitePrimaryTextHighlighted];
|
||||
break;
|
||||
case MWMButtonColoringRed:
|
||||
self.tintColor = [UIColor buttonRed];
|
||||
break;
|
||||
case MWMButtonColoringWhite:
|
||||
case MWMButtonColoringOther:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self.selected)
|
||||
[self setSelected:YES];
|
||||
else
|
||||
[self setEnabled:self.enabled];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setSelected:(BOOL)selected
|
||||
{
|
||||
[super setSelected:selected];
|
||||
if (selected)
|
||||
{
|
||||
switch (self.coloring)
|
||||
{
|
||||
case MWMButtonColoringBlack:
|
||||
self.tintColor = [UIColor linkBlue];
|
||||
break;
|
||||
case MWMButtonColoringWhite:
|
||||
case MWMButtonColoringWhiteText:
|
||||
case MWMButtonColoringBlue:
|
||||
case MWMButtonColoringOther:
|
||||
case MWMButtonColoringGray:
|
||||
case MWMButtonColoringRed:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setEnabled:self.enabled];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setEnabled:(BOOL)enabled
|
||||
{
|
||||
[super setEnabled:enabled];
|
||||
if (!enabled)
|
||||
self.tintColor = [UIColor lightGrayColor];
|
||||
else
|
||||
[self setDefaultTintColor];
|
||||
}
|
||||
|
||||
- (void)setDefaultTintColor
|
||||
{
|
||||
switch (self.coloring)
|
||||
{
|
||||
case MWMButtonColoringBlack:
|
||||
self.tintColor = [UIColor blackSecondaryText];
|
||||
break;
|
||||
case MWMButtonColoringWhite:
|
||||
self.tintColor = [UIColor white];
|
||||
break;
|
||||
case MWMButtonColoringWhiteText:
|
||||
self.tintColor = [UIColor whitePrimaryText];
|
||||
break;
|
||||
case MWMButtonColoringBlue:
|
||||
self.tintColor = [UIColor linkBlue];
|
||||
break;
|
||||
case MWMButtonColoringGray:
|
||||
self.tintColor = [UIColor blackHintText];
|
||||
break;
|
||||
case MWMButtonColoringRed:
|
||||
self.tintColor = [UIColor red];
|
||||
break;
|
||||
case MWMButtonColoringOther:
|
||||
self.imageView.image = [self imageForState:UIControlStateNormal];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#import "MWMController.h"
|
||||
|
||||
@interface MWMCollectionViewController : UICollectionViewController<MWMController>
|
||||
|
||||
@end
|
||||
33
iphone/Maps/Classes/Components/MWMCollectionViewController.m
Normal file
33
iphone/Maps/Classes/Components/MWMCollectionViewController.m
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#import "MWMCollectionViewController.h"
|
||||
#import "MWMAlertViewController.h"
|
||||
#import "MapViewController.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
@interface MWMCollectionViewController ()
|
||||
|
||||
@property(nonatomic, readwrite) MWMAlertViewController * alertController;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMCollectionViewController
|
||||
|
||||
- (BOOL)prefersStatusBarHidden { return NO; }
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.collectionView.styleName = @"PressBackground";
|
||||
[self.navigationController.navigationBar setTranslucent:NO];
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (BOOL)hasNavigationBar { return YES; }
|
||||
- (MWMAlertViewController *)alertController
|
||||
{
|
||||
if (!_alertController)
|
||||
_alertController = [[MWMAlertViewController alloc] initWithViewController:self];
|
||||
return _alertController;
|
||||
}
|
||||
|
||||
@end
|
||||
9
iphone/Maps/Classes/Components/MWMController.h
Normal file
9
iphone/Maps/Classes/Components/MWMController.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
@class MWMAlertViewController;
|
||||
|
||||
@protocol MWMController <NSObject>
|
||||
|
||||
@property (nonatomic, readonly) BOOL hasNavigationBar;
|
||||
|
||||
@property (nonatomic, readonly) MWMAlertViewController * alertController;
|
||||
|
||||
@end
|
||||
5
iphone/Maps/Classes/Components/MWMMailViewController.h
Normal file
5
iphone/Maps/Classes/Components/MWMMailViewController.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#import <MessageUI/MessageUI.h>
|
||||
|
||||
@interface MWMMailViewController : MFMailComposeViewController
|
||||
|
||||
@end
|
||||
18
iphone/Maps/Classes/Components/MWMMailViewController.m
Normal file
18
iphone/Maps/Classes/Components/MWMMailViewController.m
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#import "MWMMailViewController.h"
|
||||
|
||||
@implementation MWMMailViewController
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.navigationBar.tintColor = UIColor.whiteColor;
|
||||
self.navigationBar.barTintColor = UIColor.primary;
|
||||
}
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
||||
{
|
||||
return UIStatusBarStyleLightContent;
|
||||
}
|
||||
|
||||
- (UIViewController *)childViewControllerForStatusBarStyle { return nil; }
|
||||
@end
|
||||
3
iphone/Maps/Classes/Components/MWMNavigationController.h
Normal file
3
iphone/Maps/Classes/Components/MWMNavigationController.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
@interface MWMNavigationController : UINavigationController
|
||||
|
||||
@end
|
||||
86
iphone/Maps/Classes/Components/MWMNavigationController.m
Normal file
86
iphone/Maps/Classes/Components/MWMNavigationController.m
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#import "MWMNavigationController.h"
|
||||
#import "MWMController.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
#import <SafariServices/SafariServices.h>
|
||||
|
||||
@interface MWMNavigationController () <UINavigationControllerDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMNavigationController
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
||||
{
|
||||
return UIStatusBarStyleLightContent;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.delegate = self;
|
||||
self.navigationItem.leftBarButtonItem.tintColor = [UIColor whitePrimaryText];
|
||||
self.navigationItem.rightBarButtonItem.tintColor = [UIColor whitePrimaryText];
|
||||
|
||||
[MWMThemeManager invalidate];
|
||||
}
|
||||
|
||||
- (void)navigationController:(UINavigationController *)navigationController
|
||||
willShowViewController:(UIViewController *)viewController
|
||||
animated:(BOOL)animated
|
||||
{
|
||||
if ([viewController isKindOfClass:[SFSafariViewController class]])
|
||||
{
|
||||
[navigationController setNavigationBarHidden:YES animated:animated];
|
||||
return;
|
||||
}
|
||||
|
||||
if ([viewController conformsToProtocol:@protocol(MWMController)]) {
|
||||
id<MWMController> vc = (id<MWMController>)viewController;
|
||||
[navigationController setNavigationBarHidden:!vc.hasNavigationBar animated:animated];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
|
||||
{
|
||||
UIViewController * topVC = self.viewControllers.lastObject;
|
||||
[self setupNavigationBackButtonItemFor:topVC];
|
||||
[super pushViewController:viewController animated:animated];
|
||||
}
|
||||
|
||||
- (void)setViewControllers:(NSArray<UIViewController *> *)viewControllers animated:(BOOL)animated {
|
||||
[viewControllers enumerateObjectsUsingBlock:^(UIViewController * vc, NSUInteger idx, BOOL * stop) {
|
||||
if (idx == viewControllers.count - 1)
|
||||
return;
|
||||
[self setupNavigationBackButtonItemFor:vc];
|
||||
}];
|
||||
[super setViewControllers:viewControllers animated:animated];
|
||||
}
|
||||
|
||||
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
|
||||
{
|
||||
[super traitCollectionDidChange: previousTraitCollection];
|
||||
// Update the app theme when the device appearance is changing.
|
||||
if ((self.traitCollection.verticalSizeClass != previousTraitCollection.verticalSizeClass)
|
||||
|| (self.traitCollection.horizontalSizeClass != previousTraitCollection.horizontalSizeClass) || (self.traitCollection.userInterfaceStyle != previousTraitCollection.userInterfaceStyle)) {
|
||||
[MWMThemeManager invalidate];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)shouldAutorotate
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)setupNavigationBackButtonItemFor:(UIViewController *)viewController {
|
||||
if (@available(iOS 14.0, *)) {
|
||||
viewController.navigationItem.backButtonDisplayMode = UINavigationItemBackButtonDisplayModeMinimal;
|
||||
} else {
|
||||
viewController.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@""
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:nil
|
||||
action:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
3
iphone/Maps/Classes/Components/MWMStartButton.h
Normal file
3
iphone/Maps/Classes/Components/MWMStartButton.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
@interface MWMStartButton : UIButton
|
||||
|
||||
@end
|
||||
5
iphone/Maps/Classes/Components/MWMStartButton.m
Normal file
5
iphone/Maps/Classes/Components/MWMStartButton.m
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#import "MWMStartButton.h"
|
||||
|
||||
@implementation MWMStartButton
|
||||
|
||||
@end
|
||||
3
iphone/Maps/Classes/Components/MWMStopButton.h
Normal file
3
iphone/Maps/Classes/Components/MWMStopButton.h
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
@interface MWMStopButton : UIButton
|
||||
|
||||
@end
|
||||
5
iphone/Maps/Classes/Components/MWMStopButton.m
Normal file
5
iphone/Maps/Classes/Components/MWMStopButton.m
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#import "MWMStopButton.h"
|
||||
|
||||
@implementation MWMStopButton
|
||||
|
||||
@end
|
||||
15
iphone/Maps/Classes/Components/MWMTableViewController.h
Normal file
15
iphone/Maps/Classes/Components/MWMTableViewController.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#import "MWMController.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MWMTableViewController : UITableViewController <MWMController>
|
||||
|
||||
@end
|
||||
|
||||
@interface UITableView (MWMTableViewController)
|
||||
|
||||
- (UITableViewCell *)dequeueDefaultCellForIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
86
iphone/Maps/Classes/Components/MWMTableViewController.m
Normal file
86
iphone/Maps/Classes/Components/MWMTableViewController.m
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#import "MapsAppDelegate.h"
|
||||
#import "MapViewController.h"
|
||||
#import "MWMAlertViewController.h"
|
||||
#import "MWMTableViewCell.h"
|
||||
#import "MWMTableViewController.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
static CGFloat const kMaxEstimatedTableViewCellHeight = 100.0;
|
||||
|
||||
@interface MWMTableViewController ()
|
||||
|
||||
@property (nonatomic, readwrite) MWMAlertViewController * alertController;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMTableViewController
|
||||
|
||||
- (BOOL)prefersStatusBarHidden
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
|
||||
{
|
||||
[self fixHeaderAndFooterFontsInDarkMode:view];
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section
|
||||
{
|
||||
[self fixHeaderAndFooterFontsInDarkMode:view];
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return UITableViewAutomaticDimension;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return kMaxEstimatedTableViewCellHeight;
|
||||
}
|
||||
|
||||
// Fix table section header font color for all tables, including Setting and Route Options.
|
||||
- (void)fixHeaderAndFooterFontsInDarkMode:(UIView*)headerView {
|
||||
if ([headerView isKindOfClass: [UITableViewHeaderFooterView class]]) {
|
||||
UITableViewHeaderFooterView* header = (UITableViewHeaderFooterView *)headerView;
|
||||
header.textLabel.textColor = [UIColor blackSecondaryText];
|
||||
if (self.tableView.style == UITableViewStyleGrouped) {
|
||||
header.detailTextLabel.textColor = [UIColor blackSecondaryText];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.tableView.insetsContentViewsToSafeArea = YES;
|
||||
self.tableView.styleName = @"TableView:PressBackground";
|
||||
[self.navigationController.navigationBar setTranslucent:NO];
|
||||
[self.tableView registerClass:[MWMTableViewCell class]
|
||||
forCellReuseIdentifier:[UITableViewCell className]];
|
||||
[self.tableView registerClass:[MWMTableViewSubtitleCell class]
|
||||
forCellReuseIdentifier:[MWMTableViewSubtitleCell className]];
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (BOOL)hasNavigationBar
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (MWMAlertViewController *)alertController
|
||||
{
|
||||
if (!_alertController)
|
||||
_alertController = [[MWMAlertViewController alloc] initWithViewController:self];
|
||||
return _alertController;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation UITableView (MWMTableViewController)
|
||||
|
||||
- (UITableViewCell *)dequeueDefaultCellForIndexPath:(NSIndexPath *)indexPath {
|
||||
return [self dequeueReusableCellWithIdentifier:UITableViewCell.className forIndexPath:indexPath];
|
||||
}
|
||||
|
||||
@end
|
||||
4
iphone/Maps/Classes/Components/MWMViewController.h
Normal file
4
iphone/Maps/Classes/Components/MWMViewController.h
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#import "MWMController.h"
|
||||
|
||||
@interface MWMViewController : UIViewController <MWMController>
|
||||
@end
|
||||
40
iphone/Maps/Classes/Components/MWMViewController.m
Normal file
40
iphone/Maps/Classes/Components/MWMViewController.m
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#import "MapsAppDelegate.h"
|
||||
#import "MapViewController.h"
|
||||
#import "MWMAlertViewController.h"
|
||||
#import "MWMViewController.h"
|
||||
|
||||
|
||||
@interface MWMViewController ()
|
||||
|
||||
@property (nonatomic, readwrite) MWMAlertViewController * alertController;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMViewController
|
||||
|
||||
- (BOOL)prefersStatusBarHidden
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
[self.navigationController.navigationBar setTranslucent:NO];
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (BOOL)hasNavigationBar
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (MWMAlertViewController *)alertController
|
||||
{
|
||||
if (!_alertController)
|
||||
_alertController = [[MWMAlertViewController alloc] initWithViewController:self];
|
||||
return _alertController;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
final class AlertPresentationController: DimmedModalPresentationController {
|
||||
override var frameOfPresentedViewInContainerView: CGRect {
|
||||
let f = super.frameOfPresentedViewInContainerView
|
||||
let s = presentedViewController.view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
|
||||
let r = CGRect(x: 0, y: 0, width: s.width, height: s.height)
|
||||
return r.offsetBy(dx: (f.width - r.width) / 2, dy: (f.height - r.height) / 2)
|
||||
}
|
||||
|
||||
override func presentationTransitionWillBegin() {
|
||||
super.presentationTransitionWillBegin()
|
||||
presentedViewController.view.layer.setCornerRadius(.modalSheet)
|
||||
presentedViewController.view.clipsToBounds = true
|
||||
guard let containerView = containerView, let presentedView = presentedView else { return }
|
||||
containerView.addSubview(presentedView)
|
||||
presentedView.center = containerView.center
|
||||
presentedView.frame = frameOfPresentedViewInContainerView
|
||||
presentedView.autoresizingMask = [.flexibleLeftMargin, .flexibleTopMargin, .flexibleRightMargin, .flexibleBottomMargin]
|
||||
}
|
||||
|
||||
override func dismissalTransitionDidEnd(_ completed: Bool) {
|
||||
super.presentationTransitionDidEnd(completed)
|
||||
guard let presentedView = presentedView else { return }
|
||||
if completed {
|
||||
presentedView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
final class CoverVerticalDismissalAnimator: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
return kDefaultAnimationDuration
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
guard let fromVC = transitionContext.viewController(forKey: .from),
|
||||
let toVC = transitionContext.viewController(forKey: .to)
|
||||
else { return }
|
||||
|
||||
let originFrame = transitionContext.finalFrame(for: toVC)
|
||||
let finalFrame = originFrame.offsetBy(dx: 0, dy: originFrame.height)
|
||||
UIView.animate(withDuration: transitionDuration(using: transitionContext),
|
||||
animations: {
|
||||
fromVC.view.frame = finalFrame
|
||||
}) { finished in
|
||||
fromVC.view.removeFromSuperview()
|
||||
transitionContext.completeTransition(finished)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
final class CoverVerticalModalTransitioning: NSObject, UIViewControllerTransitioningDelegate {
|
||||
private var height: CGFloat
|
||||
init(presentationHeight: CGFloat) {
|
||||
height = presentationHeight
|
||||
}
|
||||
|
||||
func animationController(forPresented presented: UIViewController,
|
||||
presenting: UIViewController,
|
||||
source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return CoverVerticalPresentationAnimator()
|
||||
}
|
||||
|
||||
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return CoverVerticalDismissalAnimator()
|
||||
}
|
||||
|
||||
func presentationController(forPresented presented: UIViewController,
|
||||
presenting: UIViewController?,
|
||||
source: UIViewController) -> UIPresentationController? {
|
||||
return PresentationController(presentedViewController: presented, presenting: presenting, presentationHeight: height)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate final class PresentationController: DimmedModalPresentationController {
|
||||
private var height: CGFloat
|
||||
init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, presentationHeight: CGFloat) {
|
||||
height = presentationHeight
|
||||
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
|
||||
}
|
||||
|
||||
required init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, cancellable: Bool = true) {
|
||||
fatalError("init(presentedViewController:presenting:cancellable:) has not been implemented")
|
||||
}
|
||||
|
||||
override var frameOfPresentedViewInContainerView: CGRect {
|
||||
let f = super.frameOfPresentedViewInContainerView
|
||||
return CGRect(x: 0, y: f.height - height, width: f.width, height: height)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
final class CoverVerticalPresentationAnimator: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
return kDefaultAnimationDuration
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
guard let toVC = transitionContext.viewController(forKey: .to) else { return }
|
||||
|
||||
let containerView = transitionContext.containerView
|
||||
let finalFrame = transitionContext.finalFrame(for: toVC)
|
||||
let originFrame = finalFrame.offsetBy(dx: 0, dy: finalFrame.height)
|
||||
|
||||
containerView.addSubview(toVC.view)
|
||||
toVC.view.frame = originFrame
|
||||
toVC.view.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
|
||||
UIView.animate(withDuration: transitionDuration(using: transitionContext),
|
||||
animations: {
|
||||
toVC.view.frame = finalFrame
|
||||
}) { transitionContext.completeTransition($0) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
class DimmedModalPresentationController: UIPresentationController {
|
||||
private lazy var onTapGr: UITapGestureRecognizer = {
|
||||
return UITapGestureRecognizer(target: self, action: #selector(onTap))
|
||||
}()
|
||||
|
||||
private lazy var dimView: UIView = {
|
||||
let view = UIView()
|
||||
view.setStyle(.blackStatusBarBackground)
|
||||
if isCancellable {
|
||||
view.addGestureRecognizer(onTapGr)
|
||||
}
|
||||
return view
|
||||
}()
|
||||
|
||||
let isCancellable: Bool
|
||||
|
||||
required init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, cancellable: Bool = true) {
|
||||
isCancellable = cancellable
|
||||
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
|
||||
}
|
||||
|
||||
@objc private func onTap() {
|
||||
presentingViewController.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
override func presentationTransitionWillBegin() {
|
||||
guard let containerView = containerView else { return }
|
||||
containerView.addSubview(dimView)
|
||||
dimView.frame = containerView.bounds
|
||||
dimView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
dimView.alpha = 0
|
||||
presentingViewController.transitionCoordinator?.animate(alongsideTransition: { _ in
|
||||
self.dimView.alpha = 1
|
||||
})
|
||||
}
|
||||
|
||||
override func presentationTransitionDidEnd(_ completed: Bool) {
|
||||
if !completed { dimView.removeFromSuperview() }
|
||||
}
|
||||
|
||||
override func dismissalTransitionWillBegin() {
|
||||
presentingViewController.transitionCoordinator?.animate(alongsideTransition: { _ in
|
||||
self.dimView.alpha = 0
|
||||
})
|
||||
}
|
||||
|
||||
override func dismissalTransitionDidEnd(_ completed: Bool) {
|
||||
if completed { dimView.removeFromSuperview() }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
final class FadeInAnimatedTransitioning: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
return kDefaultAnimationDuration
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
guard let presentedView = transitionContext.view(forKey: .to) else { return }
|
||||
|
||||
presentedView.alpha = 0
|
||||
UIView.animate(withDuration: transitionDuration(using: transitionContext),
|
||||
animations: {
|
||||
presentedView.alpha = 1
|
||||
}) { transitionContext.completeTransition($0) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
final class FadeOutAnimatedTransitioning: NSObject, UIViewControllerAnimatedTransitioning {
|
||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
return kDefaultAnimationDuration
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||
guard let presentedView = transitionContext.view(forKey: .from) else { return }
|
||||
UIView.animate(withDuration: transitionDuration(using: transitionContext),
|
||||
animations: {
|
||||
presentedView.alpha = 0
|
||||
}) { finished in
|
||||
transitionContext.completeTransition(finished)
|
||||
}
|
||||
}
|
||||
}
|
||||
26
iphone/Maps/Classes/Components/Modal/FadeTransitioning.swift
Normal file
26
iphone/Maps/Classes/Components/Modal/FadeTransitioning.swift
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
class FadeTransitioning<T: DimmedModalPresentationController>: NSObject, UIViewControllerTransitioningDelegate {
|
||||
let presentedTransitioning = FadeInAnimatedTransitioning()
|
||||
let dismissedTransitioning = FadeOutAnimatedTransitioning()
|
||||
let isCancellable: Bool
|
||||
|
||||
init(cancellable: Bool = true) {
|
||||
isCancellable = cancellable
|
||||
super.init()
|
||||
}
|
||||
|
||||
func animationController(forPresented presented: UIViewController,
|
||||
presenting: UIViewController,
|
||||
source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return presentedTransitioning
|
||||
}
|
||||
|
||||
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
return dismissedTransitioning
|
||||
}
|
||||
|
||||
func presentationController(forPresented presented: UIViewController,
|
||||
presenting: UIViewController?,
|
||||
source: UIViewController) -> UIPresentationController? {
|
||||
return T(presentedViewController: presented, presenting: presenting, cancellable: isCancellable)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
final class IPadModalPresentationController: DimmedModalPresentationController {
|
||||
override var frameOfPresentedViewInContainerView: CGRect {
|
||||
guard let containerView = containerView else { return CGRect.zero }
|
||||
let screenSize = UIScreen.main.bounds
|
||||
let contentSize = presentedViewController.preferredContentSize
|
||||
let r = alternative(iPhone: containerView.bounds,
|
||||
iPad: CGRect(x: screenSize.width/2 - contentSize.width/2,
|
||||
y: screenSize.height/2 - contentSize.height/2,
|
||||
width: contentSize.width,
|
||||
height: contentSize.height))
|
||||
return r
|
||||
}
|
||||
|
||||
override func containerViewWillLayoutSubviews() {
|
||||
presentedView?.frame = frameOfPresentedViewInContainerView
|
||||
}
|
||||
|
||||
override func presentationTransitionWillBegin() {
|
||||
super.presentationTransitionWillBegin()
|
||||
presentedViewController.view.layer.setCornerRadius(.buttonDefault)
|
||||
presentedViewController.view.clipsToBounds = true
|
||||
guard let containerView = containerView, let presentedView = presentedView else { return }
|
||||
containerView.addSubview(presentedView)
|
||||
presentedView.frame = frameOfPresentedViewInContainerView
|
||||
}
|
||||
|
||||
override func dismissalTransitionDidEnd(_ completed: Bool) {
|
||||
super.presentationTransitionDidEnd(completed)
|
||||
guard let presentedView = presentedView else { return }
|
||||
if completed {
|
||||
presentedView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
final class PromoBookingPresentationController: DimmedModalPresentationController {
|
||||
let sideMargin: CGFloat = 32.0
|
||||
let maxWidth: CGFloat = 310.0
|
||||
|
||||
override var frameOfPresentedViewInContainerView: CGRect {
|
||||
let f = super.frameOfPresentedViewInContainerView
|
||||
let estimatedWidth = min(maxWidth, f.width - (sideMargin * 2.0))
|
||||
let s = presentedViewController.view.systemLayoutSizeFitting(CGSize(width: estimatedWidth, height: f.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow)
|
||||
let r = CGRect(x: (f.width - s.width) / 2, y: (f.height - s.height) / 2, width: s.width, height: s.height)
|
||||
return r
|
||||
}
|
||||
|
||||
override func containerViewWillLayoutSubviews() {
|
||||
presentedView?.frame = frameOfPresentedViewInContainerView
|
||||
}
|
||||
|
||||
override func presentationTransitionWillBegin() {
|
||||
super.presentationTransitionWillBegin()
|
||||
presentedViewController.view.layer.setCornerRadius(.buttonDefault)
|
||||
presentedViewController.view.clipsToBounds = true
|
||||
guard let containerView = containerView, let presentedView = presentedView else { return }
|
||||
containerView.addSubview(presentedView)
|
||||
presentedView.frame = frameOfPresentedViewInContainerView
|
||||
}
|
||||
|
||||
override func dismissalTransitionDidEnd(_ completed: Bool) {
|
||||
super.presentationTransitionDidEnd(completed)
|
||||
guard let presentedView = presentedView else { return }
|
||||
if completed {
|
||||
presentedView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
316
iphone/Maps/Classes/Components/TabView/TabView.swift
Normal file
316
iphone/Maps/Classes/Components/TabView/TabView.swift
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
fileprivate class ContentCell: UICollectionViewCell {
|
||||
var view: UIView? {
|
||||
didSet {
|
||||
if let view = view, view != oldValue {
|
||||
oldValue?.removeFromSuperview()
|
||||
view.frame = contentView.bounds
|
||||
contentView.addSubview(view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
if let view = view {
|
||||
view.frame = contentView.bounds
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate class HeaderCell: UICollectionViewCell {
|
||||
private let label = UILabel()
|
||||
private var selectedAttributes: [NSAttributedString.Key : Any] = [:]
|
||||
private var deselectedAttributes: [NSAttributedString.Key : Any] = [:]
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
contentView.addSubview(label)
|
||||
label.textAlignment = .center
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
contentView.addSubview(label)
|
||||
label.textAlignment = .center
|
||||
}
|
||||
|
||||
override var isSelected: Bool {
|
||||
didSet {
|
||||
label.attributedText = NSAttributedString(string: label.text ?? "",
|
||||
attributes: isSelected ? selectedAttributes : deselectedAttributes)
|
||||
}
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
label.attributedText = nil
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
label.frame = contentView.bounds
|
||||
}
|
||||
|
||||
func configureWith(selectedAttributes: [NSAttributedString.Key : Any],
|
||||
deselectedAttributes: [NSAttributedString.Key : Any],
|
||||
text: String) {
|
||||
self.selectedAttributes = selectedAttributes
|
||||
self.deselectedAttributes = deselectedAttributes
|
||||
label.attributedText = NSAttributedString(string: text.uppercased(),
|
||||
attributes: deselectedAttributes)
|
||||
}
|
||||
}
|
||||
|
||||
protocol TabViewDataSource: AnyObject {
|
||||
func numberOfPages(in tabView: TabView) -> Int
|
||||
func tabView(_ tabView: TabView, viewAt index: Int) -> UIView
|
||||
func tabView(_ tabView: TabView, titleAt index: Int) -> String?
|
||||
}
|
||||
|
||||
protocol TabViewDelegate: AnyObject {
|
||||
func tabView(_ tabView: TabView, didSelectTabAt index: Int)
|
||||
}
|
||||
|
||||
@objcMembers
|
||||
@objc(MWMTabView)
|
||||
class TabView: UIView {
|
||||
private enum CellId {
|
||||
static let content = "contentCell"
|
||||
static let header = "headerCell"
|
||||
}
|
||||
|
||||
private let tabsLayout = UICollectionViewFlowLayout()
|
||||
private let tabsContentLayout = UICollectionViewFlowLayout()
|
||||
private let tabsCollectionView: UICollectionView
|
||||
private let tabsContentCollectionView: UICollectionView
|
||||
private let headerView = UIView()
|
||||
private let slidingView = UIView()
|
||||
private var slidingViewLeft: NSLayoutConstraint!
|
||||
private var slidingViewWidth: NSLayoutConstraint!
|
||||
private lazy var pageCount = { return self.dataSource?.numberOfPages(in: self) ?? 0; }()
|
||||
var selectedIndex: Int?
|
||||
private var lastSelectedIndex: Int?
|
||||
|
||||
weak var dataSource: TabViewDataSource?
|
||||
weak var delegate: TabViewDelegate?
|
||||
|
||||
var barTintColor = UIColor.white {
|
||||
didSet {
|
||||
headerView.backgroundColor = barTintColor
|
||||
}
|
||||
}
|
||||
|
||||
var selectedHeaderTextAttributes: [NSAttributedString.Key : Any] = [
|
||||
.foregroundColor : UIColor.white,
|
||||
.font : UIFont.systemFont(ofSize: 14, weight: .semibold)
|
||||
] {
|
||||
didSet {
|
||||
tabsCollectionView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
var deselectedHeaderTextAttributes: [NSAttributedString.Key : Any] = [
|
||||
.foregroundColor : UIColor.gray,
|
||||
.font : UIFont.systemFont(ofSize: 14, weight: .semibold)
|
||||
] {
|
||||
didSet {
|
||||
tabsCollectionView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
var contentFrame: CGRect {
|
||||
safeAreaLayoutGuide.layoutFrame
|
||||
}
|
||||
|
||||
override var tintColor: UIColor! {
|
||||
didSet {
|
||||
slidingView.backgroundColor = tintColor
|
||||
}
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
tabsCollectionView = UICollectionView(frame: .zero, collectionViewLayout: tabsLayout)
|
||||
tabsContentCollectionView = UICollectionView(frame: .zero, collectionViewLayout: tabsContentLayout)
|
||||
super.init(frame: frame)
|
||||
configure()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
tabsCollectionView = UICollectionView(frame: .zero, collectionViewLayout: tabsLayout)
|
||||
tabsContentCollectionView = UICollectionView(frame: .zero, collectionViewLayout: tabsContentLayout)
|
||||
super.init(coder: aDecoder)
|
||||
configure()
|
||||
}
|
||||
|
||||
private func configure() {
|
||||
backgroundColor = .white
|
||||
|
||||
configureHeader()
|
||||
configureContent()
|
||||
|
||||
addSubview(tabsContentCollectionView)
|
||||
addSubview(headerView)
|
||||
|
||||
configureLayoutContraints()
|
||||
}
|
||||
|
||||
private func configureHeader() {
|
||||
tabsLayout.scrollDirection = .horizontal
|
||||
tabsLayout.minimumLineSpacing = 0
|
||||
tabsLayout.minimumInteritemSpacing = 0
|
||||
|
||||
tabsCollectionView.register(HeaderCell.self, forCellWithReuseIdentifier: CellId.header)
|
||||
tabsCollectionView.dataSource = self
|
||||
tabsCollectionView.delegate = self
|
||||
tabsCollectionView.backgroundColor = .clear
|
||||
|
||||
slidingView.backgroundColor = tintColor
|
||||
|
||||
headerView.backgroundColor = barTintColor
|
||||
headerView.addSubview(tabsCollectionView)
|
||||
headerView.addSubview(slidingView)
|
||||
headerView.addSeparator(.bottom)
|
||||
}
|
||||
|
||||
private func configureContent() {
|
||||
tabsContentLayout.scrollDirection = .horizontal
|
||||
tabsContentLayout.minimumLineSpacing = 0
|
||||
tabsContentLayout.minimumInteritemSpacing = 0
|
||||
|
||||
tabsContentCollectionView.register(ContentCell.self, forCellWithReuseIdentifier: CellId.content)
|
||||
tabsContentCollectionView.dataSource = self
|
||||
tabsContentCollectionView.delegate = self
|
||||
tabsContentCollectionView.isPagingEnabled = true
|
||||
tabsContentCollectionView.bounces = false
|
||||
tabsContentCollectionView.showsVerticalScrollIndicator = false
|
||||
tabsContentCollectionView.showsHorizontalScrollIndicator = false
|
||||
tabsContentCollectionView.backgroundColor = .clear
|
||||
}
|
||||
|
||||
private func configureLayoutContraints() {
|
||||
tabsCollectionView.translatesAutoresizingMaskIntoConstraints = false;
|
||||
tabsContentCollectionView.translatesAutoresizingMaskIntoConstraints = false
|
||||
headerView.translatesAutoresizingMaskIntoConstraints = false
|
||||
slidingView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
headerView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
|
||||
headerView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
|
||||
headerView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor).isActive = true
|
||||
headerView.heightAnchor.constraint(equalToConstant: 46).isActive = true
|
||||
|
||||
tabsContentCollectionView.leftAnchor.constraint(equalTo: safeAreaLayoutGuide.leftAnchor).isActive = true
|
||||
tabsContentCollectionView.rightAnchor.constraint(equalTo: safeAreaLayoutGuide.rightAnchor).isActive = true
|
||||
tabsContentCollectionView.topAnchor.constraint(equalTo: headerView.bottomAnchor).isActive = true
|
||||
tabsContentCollectionView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor).isActive = true
|
||||
|
||||
tabsCollectionView.leftAnchor.constraint(equalTo: headerView.leftAnchor).isActive = true
|
||||
tabsCollectionView.rightAnchor.constraint(equalTo: headerView.rightAnchor).isActive = true
|
||||
tabsCollectionView.topAnchor.constraint(equalTo: headerView.topAnchor).isActive = true
|
||||
tabsCollectionView.bottomAnchor.constraint(equalTo: slidingView.topAnchor).isActive = true
|
||||
|
||||
slidingView.heightAnchor.constraint(equalToConstant: 3).isActive = true
|
||||
slidingView.bottomAnchor.constraint(equalTo: headerView.bottomAnchor).isActive = true
|
||||
|
||||
slidingViewLeft = slidingView.leftAnchor.constraint(equalTo: safeAreaLayoutGuide.leftAnchor)
|
||||
slidingViewLeft.isActive = true
|
||||
slidingViewWidth = slidingView.widthAnchor.constraint(equalToConstant: 0)
|
||||
slidingViewWidth.isActive = true
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
tabsLayout.invalidateLayout()
|
||||
tabsContentLayout.invalidateLayout()
|
||||
super.layoutSubviews()
|
||||
assert(pageCount > 0)
|
||||
slidingViewWidth.constant = pageCount > 0 ? contentFrame.width / CGFloat(pageCount) : 0
|
||||
slidingViewLeft.constant = pageCount > 0 ? contentFrame.width / CGFloat(pageCount) * CGFloat(selectedIndex ?? 0) : 0
|
||||
tabsCollectionView.layoutIfNeeded()
|
||||
tabsContentCollectionView.layoutIfNeeded()
|
||||
if let selectedIndex = selectedIndex {
|
||||
tabsContentCollectionView.scrollToItem(at: IndexPath(item: selectedIndex, section: 0),
|
||||
at: .left,
|
||||
animated: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension TabView : UICollectionViewDataSource {
|
||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return pageCount
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
var cell = UICollectionViewCell()
|
||||
if collectionView == tabsContentCollectionView {
|
||||
cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellId.content, for: indexPath)
|
||||
if let contentCell = cell as? ContentCell {
|
||||
contentCell.view = dataSource?.tabView(self, viewAt: indexPath.item)
|
||||
}
|
||||
}
|
||||
|
||||
if collectionView == tabsCollectionView {
|
||||
cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellId.header, for: indexPath)
|
||||
if let headerCell = cell as? HeaderCell {
|
||||
let title = dataSource?.tabView(self, titleAt: indexPath.item) ?? ""
|
||||
headerCell.configureWith(selectedAttributes: selectedHeaderTextAttributes,
|
||||
deselectedAttributes: deselectedHeaderTextAttributes,
|
||||
text: title)
|
||||
if indexPath.item == selectedIndex {
|
||||
collectionView.selectItem(at: indexPath, animated: false, scrollPosition: [])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
extension TabView : UICollectionViewDelegateFlowLayout {
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if scrollView.contentSize.width > 0 {
|
||||
let scrollOffset = scrollView.contentOffset.x / scrollView.contentSize.width
|
||||
slidingViewLeft.constant = scrollOffset * contentFrame.width
|
||||
}
|
||||
}
|
||||
|
||||
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||
lastSelectedIndex = selectedIndex
|
||||
}
|
||||
|
||||
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||
selectedIndex = Int(round(scrollView.contentOffset.x / scrollView.bounds.width))
|
||||
if let selectedIndex = selectedIndex, selectedIndex != lastSelectedIndex {
|
||||
delegate?.tabView(self, didSelectTabAt: selectedIndex)
|
||||
}
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
if (collectionView == tabsCollectionView) {
|
||||
let isSelected = selectedIndex == indexPath.item
|
||||
if !isSelected {
|
||||
selectedIndex = indexPath.item
|
||||
tabsContentCollectionView.scrollToItem(at: indexPath, at: .left, animated: true)
|
||||
delegate?.tabView(self, didSelectTabAt: selectedIndex!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
|
||||
if (collectionView == tabsCollectionView) {
|
||||
collectionView.deselectItem(at: indexPath, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView,
|
||||
layout collectionViewLayout: UICollectionViewLayout,
|
||||
sizeForItemAt indexPath: IndexPath) -> CGSize {
|
||||
let bounds = collectionView.bounds.inset(by: collectionView.adjustedContentInset)
|
||||
|
||||
if collectionView == tabsContentCollectionView {
|
||||
return bounds.size
|
||||
} else {
|
||||
return CGSize(width: bounds.width / CGFloat(pageCount),
|
||||
height: bounds.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
class TabViewController: MWMViewController {
|
||||
var viewControllers: [UIViewController] = [] {
|
||||
didSet {
|
||||
viewControllers.forEach {
|
||||
self.addChild($0)
|
||||
$0.didMove(toParent: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tabView: TabView {
|
||||
get {
|
||||
return view as! TabView
|
||||
}
|
||||
}
|
||||
|
||||
override func loadView() {
|
||||
let v = TabView()
|
||||
v.dataSource = self
|
||||
view = v
|
||||
}
|
||||
}
|
||||
|
||||
extension TabViewController: TabViewDataSource {
|
||||
func numberOfPages(in tabView: TabView) -> Int {
|
||||
return viewControllers.count
|
||||
}
|
||||
|
||||
func tabView(_ tabView: TabView, viewAt index: Int) -> UIView {
|
||||
return viewControllers[index].view
|
||||
}
|
||||
|
||||
func tabView(_ tabView: TabView, titleAt index: Int) -> String? {
|
||||
return viewControllers[index].title
|
||||
}
|
||||
}
|
||||
88
iphone/Maps/Classes/Components/VerticallyAlignedButton.swift
Normal file
88
iphone/Maps/Classes/Components/VerticallyAlignedButton.swift
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
@IBDesignable
|
||||
class VerticallyAlignedButton: UIControl {
|
||||
@IBInspectable
|
||||
var image: UIImage? {
|
||||
didSet {
|
||||
imageView.image = image
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var title: String? {
|
||||
didSet {
|
||||
if localizedText == nil {
|
||||
titleLabel.text = title
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var localizedText: String? {
|
||||
didSet {
|
||||
if let localizedText = localizedText {
|
||||
titleLabel.text = L(localizedText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var spacing: CGFloat = 4 {
|
||||
didSet {
|
||||
spacingConstraint.constant = spacing
|
||||
}
|
||||
}
|
||||
|
||||
@IBInspectable
|
||||
var numberOfLines: Int {
|
||||
get {
|
||||
return titleLabel.numberOfLines
|
||||
}
|
||||
set {
|
||||
titleLabel.numberOfLines = newValue
|
||||
}
|
||||
}
|
||||
|
||||
private lazy var spacingConstraint: NSLayoutConstraint = {
|
||||
let spacingConstraint = titleLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: spacing)
|
||||
return spacingConstraint
|
||||
}()
|
||||
|
||||
lazy var titleLabel: UILabel = {
|
||||
let titleLabel = UILabel()
|
||||
titleLabel.textAlignment = .center
|
||||
titleLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
return titleLabel
|
||||
}()
|
||||
|
||||
lazy var imageView: UIImageView = {
|
||||
let imageView = UIImageView()
|
||||
imageView.contentMode = .scaleAspectFit
|
||||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
return imageView
|
||||
}()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setupView()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
setupView()
|
||||
}
|
||||
|
||||
private func setupView() {
|
||||
addSubview(titleLabel)
|
||||
addSubview(imageView)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
imageView.topAnchor.constraint(equalTo: topAnchor),
|
||||
imageView.centerXAnchor.constraint(equalTo: centerXAnchor),
|
||||
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor),
|
||||
spacingConstraint,
|
||||
titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
titleLabel.topAnchor.constraint(equalTo: bottomAnchor)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#import "MWMAlert+CPP.h"
|
||||
#import "MWMAlertViewController.h"
|
||||
|
||||
#include "routing/router.hpp"
|
||||
#include "routing/routing_callbacks.hpp"
|
||||
#include "storage/storage.hpp"
|
||||
#include "storage/storage_defines.hpp"
|
||||
|
||||
@interface MWMAlertViewController (CPP)
|
||||
|
||||
- (void)presentAlert:(routing::RouterResultCode)type;
|
||||
- (void)presentDownloaderAlertWithCountries:(storage::CountriesSet const &)countries
|
||||
code:(routing::RouterResultCode)code
|
||||
cancelBlock:(nonnull MWMVoidBlock)cancelBlock
|
||||
downloadBlock:(nonnull MWMDownloadBlock)downloadBlock
|
||||
downloadCompleteBlock:(nonnull MWMVoidBlock)downloadCompleteBlock;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
#import "MWMAlert.h"
|
||||
#import "MWMMobileInternetAlert.h"
|
||||
#import "MWMViewController.h"
|
||||
|
||||
@interface MWMAlertViewController : MWMViewController
|
||||
|
||||
+ (nonnull MWMAlertViewController *)activeAlertController;
|
||||
|
||||
@property(weak, nonatomic, readonly) UIViewController *_Null_unspecified ownerViewController;
|
||||
|
||||
- (nonnull instancetype)initWithViewController:(nonnull UIViewController *)viewController;
|
||||
- (void)presentPoint2PointAlertWithOkBlock:(nonnull MWMVoidBlock)okBlock needToRebuild:(BOOL)needToRebuild;
|
||||
- (void)presentRoutingDisclaimerAlertWithOkBlock:(nonnull nonnull MWMVoidBlock)block;
|
||||
- (void)presentDisabledLocationAlert;
|
||||
- (void)presentLocationAlertWithCancelBlock:(MWMVoidBlock _Nonnull )cancelBlock;
|
||||
- (void)presentLocationServicesDisabledAlert;
|
||||
- (void)presentLocationServiceNotSupportedAlert;
|
||||
- (void)presentNoConnectionAlert;
|
||||
- (void)presentDeleteMapProhibitedAlert;
|
||||
- (void)presentUnsavedEditsAlertWithOkBlock:(nonnull MWMVoidBlock)okBlock;
|
||||
- (void)presentNoWiFiAlertWithOkBlock:(nullable MWMVoidBlock)okBlock andCancelBlock:(nullable MWMVoidBlock)cancelBlock;
|
||||
- (void)presentIncorrectFeauturePositionAlert;
|
||||
- (void)presentNotEnoughSpaceAlert;
|
||||
- (void)presentInvalidUserNameOrPasswordAlert;
|
||||
- (void)presentDownloaderNoConnectionAlertWithOkBlock:(nonnull MWMVoidBlock)okBlock
|
||||
cancelBlock:(nonnull MWMVoidBlock)cancelBlock;
|
||||
- (void)presentDownloaderNotEnoughSpaceAlert;
|
||||
- (void)presentDownloaderInternalErrorAlertWithOkBlock:(nonnull MWMVoidBlock)okBlock
|
||||
cancelBlock:(nonnull MWMVoidBlock)cancelBlock;
|
||||
- (void)presentPlaceDoesntExistAlertWithBlock:(nonnull MWMStringBlock)block;
|
||||
- (void)presentResetChangesAlertWithBlock:(nonnull MWMVoidBlock)block;
|
||||
- (void)presentDeleteFeatureAlertWithBlock:(nonnull MWMVoidBlock)block;
|
||||
- (void)presentPersonalInfoWarningAlertWithBlock:(nonnull MWMVoidBlock)block;
|
||||
- (void)presentTrackWarningAlertWithCancelBlock:(nonnull MWMVoidBlock)block;
|
||||
- (void)presentMobileInternetAlertWithBlock:(nonnull MWMMobileInternetAlertCompletionBlock)block;
|
||||
- (void)presentInfoAlert:(nonnull NSString *)title text:(nonnull NSString *)text;
|
||||
- (void)presentInfoAlert:(nonnull NSString *)title;
|
||||
- (void)presentCreateBookmarkCategoryAlertWithMaxCharacterNum:(NSUInteger)max
|
||||
minCharacterNum:(NSUInteger)min
|
||||
callback:(nonnull MWMCheckStringBlock)callback;
|
||||
|
||||
- (void)presentBookmarkConversionErrorAlert;
|
||||
- (void)presentBugReportAlertWithTitle:(nonnull NSString *)title;
|
||||
|
||||
- (void)presentDefaultAlertWithTitle:(nonnull NSString *)title
|
||||
message:(nullable NSString *)message
|
||||
rightButtonTitle:(nonnull NSString *)rightButtonTitle
|
||||
leftButtonTitle:(nullable NSString *)leftButtonTitle
|
||||
rightButtonAction:(nullable MWMVoidBlock)action;
|
||||
|
||||
- (void)closeAlert:(nullable MWMVoidBlock)completion;
|
||||
|
||||
- (nonnull instancetype)init __attribute__((unavailable("call -initWithViewController: instead!")));
|
||||
+ (nonnull instancetype)new __attribute__((unavailable("call -initWithViewController: instead!")));
|
||||
- (nonnull instancetype)initWithCoder:(nonnull NSCoder *)aDecoder
|
||||
__attribute__((unavailable("call -initWithViewController: instead!")));
|
||||
- (nonnull instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
|
||||
bundle:(nullable NSBundle *)nibBundleOrNil
|
||||
__attribute__((unavailable("call -initWithViewController: instead!")));
|
||||
@end
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
#import "MWMAlertViewController+CPP.h"
|
||||
#import "MWMController.h"
|
||||
#import "MWMDownloadTransitMapAlert.h"
|
||||
#import "MWMLocationAlert.h"
|
||||
#import "MapViewController.h"
|
||||
#import "MapsAppDelegate.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
static NSString *const kAlertControllerNibIdentifier = @"MWMAlertViewController";
|
||||
|
||||
@interface MWMAlertViewController () <UIGestureRecognizerDelegate>
|
||||
|
||||
@property(weak, nonatomic, readwrite) UIViewController *ownerViewController;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMAlertViewController
|
||||
|
||||
+ (nonnull MWMAlertViewController *)activeAlertController {
|
||||
UIViewController *tvc = [MapViewController sharedController];
|
||||
ASSERT([tvc conformsToProtocol:@protocol(MWMController)], ());
|
||||
UIViewController<MWMController> *mwmController = static_cast<UIViewController<MWMController> *>(tvc);
|
||||
return mwmController.alertController;
|
||||
}
|
||||
|
||||
- (nonnull instancetype)initWithViewController:(nonnull UIViewController *)viewController {
|
||||
self = [super initWithNibName:kAlertControllerNibIdentifier bundle:nil];
|
||||
if (self)
|
||||
_ownerViewController = viewController;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewWillTransitionToSize:(CGSize)size
|
||||
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
|
||||
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
|
||||
auto const orient = size.width > size.height ? UIInterfaceOrientationLandscapeLeft : UIInterfaceOrientationPortrait;
|
||||
[coordinator
|
||||
animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
|
||||
for (MWMAlert *alert in self.view.subviews)
|
||||
[alert rotate:orient duration:context.transitionDuration];
|
||||
}
|
||||
completion:^(id<UIViewControllerTransitionCoordinatorContext> context){
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)presentLocationAlertWithCancelBlock:(MWMVoidBlock)cancelBlock {
|
||||
[self displayAlert:[MWMAlert locationAlertWithCancelBlock:cancelBlock]];
|
||||
}
|
||||
- (void)presentPoint2PointAlertWithOkBlock:(nonnull MWMVoidBlock)okBlock needToRebuild:(BOOL)needToRebuild {
|
||||
[self displayAlert:[MWMAlert point2PointAlertWithOkBlock:okBlock needToRebuild:needToRebuild]];
|
||||
}
|
||||
|
||||
- (void)presentLocationServiceNotSupportedAlert {
|
||||
[self displayAlert:[MWMAlert locationServiceNotSupportedAlert]];
|
||||
}
|
||||
- (void)presentNoConnectionAlert {
|
||||
[self displayAlert:[MWMAlert noConnectionAlert]];
|
||||
}
|
||||
|
||||
- (void)presentDeleteMapProhibitedAlert {
|
||||
[self displayAlert:[MWMAlert deleteMapProhibitedAlert]];
|
||||
}
|
||||
- (void)presentUnsavedEditsAlertWithOkBlock:(nonnull MWMVoidBlock)okBlock {
|
||||
[self displayAlert:[MWMAlert unsavedEditsAlertWithOkBlock:okBlock]];
|
||||
}
|
||||
|
||||
- (void)presentNoWiFiAlertWithOkBlock:(nullable MWMVoidBlock)okBlock andCancelBlock:(MWMVoidBlock)cancelBlock {
|
||||
[self displayAlert:[MWMAlert noWiFiAlertWithOkBlock:okBlock andCancelBlock:cancelBlock]];
|
||||
}
|
||||
|
||||
- (void)presentIncorrectFeauturePositionAlert {
|
||||
[self displayAlert:[MWMAlert incorrectFeaturePositionAlert]];
|
||||
}
|
||||
|
||||
- (void)presentNotEnoughSpaceAlert {
|
||||
[self displayAlert:[MWMAlert notEnoughSpaceAlert]];
|
||||
}
|
||||
- (void)presentInvalidUserNameOrPasswordAlert {
|
||||
[self displayAlert:[MWMAlert invalidUserNameOrPasswordAlert]];
|
||||
}
|
||||
|
||||
- (void)presentDownloaderAlertWithCountries:(storage::CountriesSet const &)countries
|
||||
code:(routing::RouterResultCode)code
|
||||
cancelBlock:(MWMVoidBlock)cancelBlock
|
||||
downloadBlock:(MWMDownloadBlock)downloadBlock
|
||||
downloadCompleteBlock:(MWMVoidBlock)downloadCompleteBlock {
|
||||
[self displayAlert:[MWMAlert downloaderAlertWithAbsentCountries:countries
|
||||
code:code
|
||||
cancelBlock:cancelBlock
|
||||
downloadBlock:downloadBlock
|
||||
downloadCompleteBlock:downloadCompleteBlock]];
|
||||
}
|
||||
|
||||
- (void)presentRoutingDisclaimerAlertWithOkBlock:(MWMVoidBlock)block {
|
||||
[self displayAlert:[MWMAlert routingDisclaimerAlertWithOkBlock:block]];
|
||||
}
|
||||
|
||||
- (void)presentDisabledLocationAlert {
|
||||
[self displayAlert:[MWMAlert disabledLocationAlert]];
|
||||
}
|
||||
|
||||
- (void)presentLocationServicesDisabledAlert; {
|
||||
[self displayAlert:[MWMAlert locationServicesDisabledAlert]];
|
||||
}
|
||||
|
||||
- (void)presentAlert:(routing::RouterResultCode)type {
|
||||
[self displayAlert:[MWMAlert alert:type]];
|
||||
}
|
||||
|
||||
- (void)presentDownloaderNoConnectionAlertWithOkBlock:(nonnull MWMVoidBlock)okBlock
|
||||
cancelBlock:(nonnull MWMVoidBlock)cancelBlock {
|
||||
[self displayAlert:[MWMAlert downloaderNoConnectionAlertWithOkBlock:okBlock cancelBlock:cancelBlock]];
|
||||
}
|
||||
|
||||
- (void)presentDownloaderNotEnoughSpaceAlert {
|
||||
[self displayAlert:[MWMAlert downloaderNotEnoughSpaceAlert]];
|
||||
}
|
||||
|
||||
- (void)presentDownloaderInternalErrorAlertWithOkBlock:(nonnull MWMVoidBlock)okBlock
|
||||
cancelBlock:(nonnull MWMVoidBlock)cancelBlock {
|
||||
[self displayAlert:[MWMAlert downloaderInternalErrorAlertWithOkBlock:okBlock cancelBlock:cancelBlock]];
|
||||
}
|
||||
|
||||
- (void)presentPlaceDoesntExistAlertWithBlock:(MWMStringBlock)block {
|
||||
[self displayAlert:[MWMAlert placeDoesntExistAlertWithBlock:block]];
|
||||
}
|
||||
|
||||
- (void)presentResetChangesAlertWithBlock:(MWMVoidBlock)block {
|
||||
[self displayAlert:[MWMAlert resetChangesAlertWithBlock:block]];
|
||||
}
|
||||
|
||||
- (void)presentDeleteFeatureAlertWithBlock:(MWMVoidBlock)block {
|
||||
[self displayAlert:[MWMAlert deleteFeatureAlertWithBlock:block]];
|
||||
}
|
||||
|
||||
- (void)presentPersonalInfoWarningAlertWithBlock:(nonnull MWMVoidBlock)block {
|
||||
[self displayAlert:[MWMAlert personalInfoWarningAlertWithBlock:block]];
|
||||
}
|
||||
|
||||
- (void)presentTrackWarningAlertWithCancelBlock:(nonnull MWMVoidBlock)block {
|
||||
[self displayAlert:[MWMAlert trackWarningAlertWithCancelBlock:block]];
|
||||
}
|
||||
|
||||
- (void)presentMobileInternetAlertWithBlock:(nonnull MWMMobileInternetAlertCompletionBlock)block {
|
||||
[self displayAlert:[MWMMobileInternetAlert alertWithBlock:block]];
|
||||
}
|
||||
|
||||
- (void)presentInfoAlert:(nonnull NSString *)title text:(nonnull NSString *)text {
|
||||
[self displayAlert:[MWMAlert infoAlert:title text:text]];
|
||||
}
|
||||
|
||||
- (void)presentInfoAlert:(nonnull NSString *)title {
|
||||
[self displayAlert:[MWMAlert infoAlert:title text:nil]];
|
||||
}
|
||||
|
||||
- (void)presentCreateBookmarkCategoryAlertWithMaxCharacterNum:(NSUInteger)max
|
||||
minCharacterNum:(NSUInteger)min
|
||||
callback:(nonnull MWMCheckStringBlock)callback {
|
||||
auto alert =
|
||||
static_cast<MWMBCCreateCategoryAlert *>([MWMAlert createBookmarkCategoryAlertWithMaxCharacterNum:max
|
||||
minCharacterNum:min
|
||||
callback:callback]);
|
||||
[self displayAlert:alert];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[alert.textField becomeFirstResponder];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)presentSpinnerAlertWithTitle:(nonnull NSString *)title cancel:(nullable MWMVoidBlock)cancel {
|
||||
[self displayAlert:[MWMAlert spinnerAlertWithTitle:title cancel:cancel]];
|
||||
}
|
||||
|
||||
- (void)presentBookmarkConversionErrorAlert {
|
||||
[self displayAlert:[MWMAlert bookmarkConversionErrorAlert]];
|
||||
}
|
||||
|
||||
- (void)presentTagsLoadingErrorAlertWithOkBlock:(nonnull MWMVoidBlock)okBlock
|
||||
cancelBlock:(nonnull MWMVoidBlock)cancelBlock {
|
||||
[self displayAlert:[MWMAlert tagsLoadingErrorAlertWithOkBlock:okBlock cancelBlock:cancelBlock]];
|
||||
}
|
||||
|
||||
- (void)presentBugReportAlertWithTitle:(nonnull NSString *)title {
|
||||
[self displayAlert:[MWMAlert bugReportAlertWithTitle:title]];
|
||||
}
|
||||
|
||||
- (void)presentDefaultAlertWithTitle:(nonnull NSString *)title
|
||||
message:(nullable NSString *)message
|
||||
rightButtonTitle:(nonnull NSString *)rightButtonTitle
|
||||
leftButtonTitle:(nullable NSString *)leftButtonTitle
|
||||
rightButtonAction:(nullable MWMVoidBlock)action {
|
||||
[self displayAlert:[MWMAlert defaultAlertWithTitle:title
|
||||
message:message
|
||||
rightButtonTitle:rightButtonTitle
|
||||
leftButtonTitle:leftButtonTitle
|
||||
rightButtonAction:action]];
|
||||
}
|
||||
|
||||
- (void)displayAlert:(MWMAlert *)alert {
|
||||
UIViewController *ownerVC = self.ownerViewController;
|
||||
if (ownerVC.navigationController != nil) {
|
||||
ownerVC = ownerVC.navigationController;
|
||||
}
|
||||
BOOL isOwnerLoaded = ownerVC.isViewLoaded;
|
||||
if (!isOwnerLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(igrechuhin): Remove this check on location manager refactoring.
|
||||
// Workaround for current location manager duplicate error alerts.
|
||||
if ([alert isKindOfClass:[MWMLocationAlert class]]) {
|
||||
for (MWMAlert *view in self.view.subviews) {
|
||||
if ([view isKindOfClass:[MWMLocationAlert class]])
|
||||
return;
|
||||
}
|
||||
}
|
||||
[UIView animateWithDuration:kDefaultAnimationDuration
|
||||
delay:0
|
||||
options:UIViewAnimationOptionBeginFromCurrentState
|
||||
animations:^{
|
||||
for (MWMAlert *view in self.view.subviews) {
|
||||
if (view != alert)
|
||||
view.alpha = 0.0;
|
||||
}
|
||||
}
|
||||
completion:nil];
|
||||
|
||||
[self willMoveToParentViewController:NULL];
|
||||
[self.view removeFromSuperview];
|
||||
[self removeFromParentViewController];
|
||||
|
||||
alert.alertController = self;
|
||||
|
||||
[ownerVC addChildViewController:self];
|
||||
self.view.frame = CGRectMake(0, 0, ownerVC.view.frame.size.width, ownerVC.view.frame.size.height);
|
||||
[ownerVC.view addSubview:self.view];
|
||||
[self didMoveToParentViewController:ownerVC];
|
||||
|
||||
alert.alpha = 0.;
|
||||
[self.view addSubview:alert];
|
||||
CGFloat const scale = 1.1;
|
||||
alert.transform = CGAffineTransformMakeScale(scale, scale);
|
||||
[UIView animateWithDuration:kDefaultAnimationDuration
|
||||
animations:^{
|
||||
self.view.alpha = 1.;
|
||||
alert.alpha = 1.;
|
||||
alert.transform = CGAffineTransformIdentity;
|
||||
}];
|
||||
[[MapsAppDelegate theApp].window endEditing:YES];
|
||||
}
|
||||
|
||||
- (void)closeAlert:(nullable MWMVoidBlock)completion {
|
||||
NSArray *subviews = self.view.subviews;
|
||||
MWMAlert *closeAlert = subviews.lastObject;
|
||||
MWMAlert *showAlert = (subviews.count >= 2 ? subviews[subviews.count - 2] : nil);
|
||||
[UIView animateWithDuration:kDefaultAnimationDuration
|
||||
delay:0
|
||||
options:UIViewAnimationOptionBeginFromCurrentState
|
||||
animations:^{
|
||||
closeAlert.alpha = 0.;
|
||||
if (showAlert)
|
||||
showAlert.alpha = 1.;
|
||||
else
|
||||
self.view.alpha = 0.;
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
[closeAlert removeFromSuperview];
|
||||
if (!showAlert) {
|
||||
[self.view removeFromSuperview];
|
||||
[self removeFromParentViewController];
|
||||
}
|
||||
if (completion)
|
||||
completion();
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MWMAlertViewController">
|
||||
<connections>
|
||||
<outlet property="view" destination="PTX-3C-07U" id="OUb-QU-9sV"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view multipleTouchEnabled="YES" contentMode="scaleToFill" id="PTX-3C-07U" customClass="SolidTouchView" propertyAccessControl="all">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.40000000000000002" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<gestureRecognizers/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<viewLayoutGuide key="safeArea" id="1tF-gt-bLj"/>
|
||||
<point key="canvasLocation" x="528" y="235"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
19
iphone/Maps/Classes/CustomAlert/BaseAlert/MWMAlert+CPP.h
Normal file
19
iphone/Maps/Classes/CustomAlert/BaseAlert/MWMAlert+CPP.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#import "MWMAlert.h"
|
||||
|
||||
#include "routing/router.hpp"
|
||||
#include "routing/routing_callbacks.hpp"
|
||||
#include "storage/storage.hpp"
|
||||
#include "storage/storage_defines.hpp"
|
||||
|
||||
using MWMDownloadBlock = void (^)(storage::CountriesVec const &, MWMVoidBlock);
|
||||
|
||||
@interface MWMAlert (CPP)
|
||||
|
||||
+ (MWMAlert *)alert:(routing::RouterResultCode)type;
|
||||
+ (MWMAlert *)downloaderAlertWithAbsentCountries:(storage::CountriesSet const &)countries
|
||||
code:(routing::RouterResultCode)code
|
||||
cancelBlock:(MWMVoidBlock)cancelBlock
|
||||
downloadBlock:(MWMDownloadBlock)downloadBlock
|
||||
downloadCompleteBlock:(MWMVoidBlock)downloadCompleteBlock;
|
||||
|
||||
@end
|
||||
51
iphone/Maps/Classes/CustomAlert/BaseAlert/MWMAlert.h
Normal file
51
iphone/Maps/Classes/CustomAlert/BaseAlert/MWMAlert.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
@class MWMAlertViewController;
|
||||
@interface MWMAlert : UIView
|
||||
|
||||
@property(weak, nonatomic) MWMAlertViewController *alertController;
|
||||
|
||||
+ (MWMAlert *)locationAlertWithCancelBlock:(MWMVoidBlock)cancelBlock;
|
||||
+ (MWMAlert *)routingDisclaimerAlertWithOkBlock:(MWMVoidBlock)block;
|
||||
+ (MWMAlert *)disabledLocationAlert;
|
||||
+ (MWMAlert *)locationServicesDisabledAlert;
|
||||
+ (MWMAlert *)noWiFiAlertWithOkBlock:(MWMVoidBlock)okBlock andCancelBlock:(MWMVoidBlock)cancelBlock;
|
||||
+ (MWMAlert *)noConnectionAlert;
|
||||
+ (MWMAlert *)deleteMapProhibitedAlert;
|
||||
+ (MWMAlert *)unsavedEditsAlertWithOkBlock:(MWMVoidBlock)okBlock;
|
||||
+ (MWMAlert *)locationServiceNotSupportedAlert;
|
||||
+ (MWMAlert *)incorrectFeaturePositionAlert;
|
||||
+ (MWMAlert *)notEnoughSpaceAlert;
|
||||
+ (MWMAlert *)invalidUserNameOrPasswordAlert;
|
||||
+ (MWMAlert *)point2PointAlertWithOkBlock:(MWMVoidBlock)okBlock needToRebuild:(BOOL)needToRebuild;
|
||||
+ (MWMAlert *)downloaderNoConnectionAlertWithOkBlock:(MWMVoidBlock)okBlock cancelBlock:(MWMVoidBlock)cancelBlock;
|
||||
+ (MWMAlert *)downloaderNotEnoughSpaceAlert;
|
||||
+ (MWMAlert *)downloaderInternalErrorAlertWithOkBlock:(MWMVoidBlock)okBlock cancelBlock:(MWMVoidBlock)cancelBlock;
|
||||
+ (MWMAlert *)placeDoesntExistAlertWithBlock:(MWMStringBlock)block;
|
||||
+ (MWMAlert *)resetChangesAlertWithBlock:(MWMVoidBlock)block;
|
||||
+ (MWMAlert *)deleteFeatureAlertWithBlock:(MWMVoidBlock)block;
|
||||
+ (MWMAlert *)personalInfoWarningAlertWithBlock:(MWMVoidBlock)block;
|
||||
+ (MWMAlert *)trackWarningAlertWithCancelBlock:(MWMVoidBlock)block;
|
||||
+ (MWMAlert *)infoAlert:(NSString *)title text:(NSString *)text;
|
||||
+ (MWMAlert *)createBookmarkCategoryAlertWithMaxCharacterNum:(NSUInteger)max
|
||||
minCharacterNum:(NSUInteger)min
|
||||
callback:(MWMCheckStringBlock)callback;
|
||||
+ (MWMAlert *)spinnerAlertWithTitle:(NSString *)title cancel:(MWMVoidBlock)cancel;
|
||||
+ (MWMAlert *)bookmarkConversionErrorAlert;
|
||||
+ (MWMAlert *)tagsLoadingErrorAlertWithOkBlock:okBlock cancelBlock:cancelBlock;
|
||||
+ (MWMAlert *)bugReportAlertWithTitle:(NSString *)title;
|
||||
|
||||
+ (MWMAlert *)defaultAlertWithTitle:(NSString *)title
|
||||
message:(NSString *)message
|
||||
rightButtonTitle:(NSString *)rightButtonTitle
|
||||
leftButtonTitle:(NSString *)leftButtonTitle
|
||||
rightButtonAction:(MWMVoidBlock)action;
|
||||
|
||||
- (void)close:(MWMVoidBlock)completion;
|
||||
|
||||
- (void)setNeedsCloseAlertAfterEnterBackground;
|
||||
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation;
|
||||
|
||||
- (void)rotate:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
|
||||
|
||||
- (void)addControllerViewToWindow;
|
||||
|
||||
@end
|
||||
234
iphone/Maps/Classes/CustomAlert/BaseAlert/MWMAlert.mm
Normal file
234
iphone/Maps/Classes/CustomAlert/BaseAlert/MWMAlert.mm
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
#import "MWMAlert+CPP.h"
|
||||
#import "MWMAlertViewController.h"
|
||||
#import "MWMDefaultAlert.h"
|
||||
#import "MWMDownloadTransitMapAlert.h"
|
||||
#import "MWMLocationAlert.h"
|
||||
#import "MWMPlaceDoesntExistAlert.h"
|
||||
#import "MWMRoutingDisclaimerAlert.h"
|
||||
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
@implementation MWMAlert
|
||||
|
||||
+ (MWMAlert *)locationAlertWithCancelBlock:(MWMVoidBlock)cancelBlock {
|
||||
return [MWMLocationAlert alertWithCancelBlock:cancelBlock];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)point2PointAlertWithOkBlock:(MWMVoidBlock)block needToRebuild:(BOOL)needToRebuild {
|
||||
return [MWMDefaultAlert point2PointAlertWithOkBlock:block needToRebuild:needToRebuild];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)routingDisclaimerAlertWithOkBlock:(MWMVoidBlock)block {
|
||||
return [MWMRoutingDisclaimerAlert alertWithOkBlock:block];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)disabledLocationAlert {
|
||||
return [MWMDefaultAlert disabledLocationAlert];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)locationServicesDisabledAlert {
|
||||
return [LocationServicesDisabledAlert alert];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)noWiFiAlertWithOkBlock:(MWMVoidBlock)okBlock andCancelBlock:(MWMVoidBlock)cancelBlock {
|
||||
return [MWMDefaultAlert noWiFiAlertWithOkBlock:okBlock andCancelBlock:cancelBlock];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)noConnectionAlert {
|
||||
return [MWMDefaultAlert noConnectionAlert];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)deleteMapProhibitedAlert {
|
||||
return [MWMDefaultAlert deleteMapProhibitedAlert];
|
||||
}
|
||||
+ (MWMAlert *)unsavedEditsAlertWithOkBlock:(MWMVoidBlock)okBlock {
|
||||
return [MWMDefaultAlert unsavedEditsAlertWithOkBlock:okBlock];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)locationServiceNotSupportedAlert {
|
||||
return [MWMDefaultAlert locationServiceNotSupportedAlert];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)downloaderAlertWithAbsentCountries:(storage::CountriesSet const &)countries
|
||||
code:(routing::RouterResultCode)code
|
||||
cancelBlock:(MWMVoidBlock)cancelBlock
|
||||
downloadBlock:(MWMDownloadBlock)downloadBlock
|
||||
downloadCompleteBlock:(MWMVoidBlock)downloadCompleteBlock {
|
||||
return [MWMDownloadTransitMapAlert downloaderAlertWithMaps:countries
|
||||
code:code
|
||||
cancelBlock:cancelBlock
|
||||
downloadBlock:downloadBlock
|
||||
downloadCompleteBlock:downloadCompleteBlock];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)alert:(routing::RouterResultCode)type {
|
||||
switch (type) {
|
||||
case routing::RouterResultCode::NoCurrentPosition:
|
||||
return [MWMDefaultAlert noCurrentPositionAlert];
|
||||
case routing::RouterResultCode::StartPointNotFound:
|
||||
return [MWMDefaultAlert startPointNotFoundAlert];
|
||||
case routing::RouterResultCode::EndPointNotFound:
|
||||
return [MWMDefaultAlert endPointNotFoundAlert];
|
||||
case routing::RouterResultCode::PointsInDifferentMWM:
|
||||
return [MWMDefaultAlert pointsInDifferentMWMAlert];
|
||||
case routing::RouterResultCode::TransitRouteNotFoundNoNetwork:
|
||||
return [MWMDefaultAlert routeNotFoundNoPublicTransportAlert];
|
||||
case routing::RouterResultCode::TransitRouteNotFoundTooLongPedestrian:
|
||||
return [MWMDefaultAlert routeNotFoundTooLongPedestrianAlert];
|
||||
case routing::RouterResultCode::RouteNotFoundRedressRouteError:
|
||||
case routing::RouterResultCode::RouteNotFound:
|
||||
case routing::RouterResultCode::InconsistentMWMandRoute:
|
||||
return [MWMDefaultAlert routeNotFoundAlert];
|
||||
case routing::RouterResultCode::RouteFileNotExist:
|
||||
case routing::RouterResultCode::FileTooOld:
|
||||
return [MWMDefaultAlert routeFileNotExistAlert];
|
||||
case routing::RouterResultCode::InternalError:
|
||||
return [MWMDefaultAlert internalRoutingErrorAlert];
|
||||
case routing::RouterResultCode::Cancelled:
|
||||
case routing::RouterResultCode::NoError:
|
||||
case routing::RouterResultCode::HasWarnings:
|
||||
case routing::RouterResultCode::NeedMoreMaps:
|
||||
return nil;
|
||||
case routing::RouterResultCode::IntermediatePointNotFound:
|
||||
return [MWMDefaultAlert intermediatePointNotFoundAlert];
|
||||
}
|
||||
}
|
||||
|
||||
+ (MWMAlert *)incorrectFeaturePositionAlert {
|
||||
return [MWMDefaultAlert incorrectFeaturePositionAlert];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)notEnoughSpaceAlert {
|
||||
return [MWMDefaultAlert notEnoughSpaceAlert];
|
||||
}
|
||||
+ (MWMAlert *)invalidUserNameOrPasswordAlert {
|
||||
return [MWMDefaultAlert invalidUserNameOrPasswordAlert];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)downloaderNoConnectionAlertWithOkBlock:(MWMVoidBlock)okBlock cancelBlock:(MWMVoidBlock)cancelBlock {
|
||||
return [MWMDefaultAlert downloaderNoConnectionAlertWithOkBlock:okBlock cancelBlock:cancelBlock];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)downloaderNotEnoughSpaceAlert {
|
||||
return [MWMDefaultAlert downloaderNotEnoughSpaceAlert];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)downloaderInternalErrorAlertWithOkBlock:(MWMVoidBlock)okBlock cancelBlock:(MWMVoidBlock)cancelBlock {
|
||||
return [MWMDefaultAlert downloaderInternalErrorAlertWithOkBlock:okBlock cancelBlock:cancelBlock];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)placeDoesntExistAlertWithBlock:(MWMStringBlock)block {
|
||||
return [MWMPlaceDoesntExistAlert alertWithBlock:block];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)resetChangesAlertWithBlock:(MWMVoidBlock)block {
|
||||
return [MWMDefaultAlert resetChangesAlertWithBlock:block];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)deleteFeatureAlertWithBlock:(MWMVoidBlock)block {
|
||||
return [MWMDefaultAlert deleteFeatureAlertWithBlock:block];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)personalInfoWarningAlertWithBlock:(MWMVoidBlock)block {
|
||||
return [MWMDefaultAlert personalInfoWarningAlertWithBlock:block];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)trackWarningAlertWithCancelBlock:(MWMVoidBlock)block {
|
||||
return [MWMDefaultAlert trackWarningAlertWithCancelBlock:block];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)infoAlert:(NSString *)title text:(NSString *)text {
|
||||
return [MWMDefaultAlert infoAlert:title text:text];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)createBookmarkCategoryAlertWithMaxCharacterNum:(NSUInteger)max
|
||||
minCharacterNum:(NSUInteger)min
|
||||
callback:(MWMCheckStringBlock)callback {
|
||||
return [MWMBCCreateCategoryAlert alertWithMaxCharachersNum:max minCharactersNum:min callback:callback];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)spinnerAlertWithTitle:(NSString *)title cancel:(MWMVoidBlock)cancel {
|
||||
return [MWMSpinnerAlert alertWithTitle:title cancel:cancel];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)bookmarkConversionErrorAlert {
|
||||
return [MWMDefaultAlert bookmarkConversionErrorAlert];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)tagsLoadingErrorAlertWithOkBlock:okBlock cancelBlock:cancelBlock {
|
||||
return [MWMDefaultAlert tagsLoadingErrorAlertWithOkBlock:okBlock cancelBlock:cancelBlock];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)bugReportAlertWithTitle:(NSString *)title {
|
||||
return [MWMDefaultAlert bugReportAlertWithTitle:title];
|
||||
}
|
||||
|
||||
+ (MWMAlert *)defaultAlertWithTitle:(NSString *)title
|
||||
message:(NSString *)message
|
||||
rightButtonTitle:(NSString *)rightButtonTitle
|
||||
leftButtonTitle:(NSString *)leftButtonTitle
|
||||
rightButtonAction:(MWMVoidBlock)action {
|
||||
return [MWMDefaultAlert defaultAlertWithTitle:title
|
||||
message:message
|
||||
rightButtonTitle:rightButtonTitle
|
||||
leftButtonTitle:leftButtonTitle
|
||||
rightButtonAction:action
|
||||
log:nil];
|
||||
}
|
||||
|
||||
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {
|
||||
// Should override this method if you want custom relayout after rotation.
|
||||
}
|
||||
|
||||
- (void)close:(MWMVoidBlock)completion {
|
||||
[self.alertController closeAlert:completion];
|
||||
}
|
||||
- (void)setNeedsCloseAlertAfterEnterBackground {
|
||||
[NSNotificationCenter.defaultCenter addObserver:self
|
||||
selector:@selector(applicationDidEnterBackground)
|
||||
name:UIApplicationDidEnterBackgroundNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[NSNotificationCenter.defaultCenter removeObserver:self];
|
||||
}
|
||||
- (void)applicationDidEnterBackground {
|
||||
// Should close alert when application entered background.
|
||||
[self close:nil];
|
||||
}
|
||||
|
||||
- (void)rotate:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
|
||||
if ([self respondsToSelector:@selector(willRotateToInterfaceOrientation:)])
|
||||
[self willRotateToInterfaceOrientation:toInterfaceOrientation];
|
||||
}
|
||||
|
||||
- (void)addControllerViewToWindow {
|
||||
UIWindow *window = UIApplication.sharedApplication.delegate.window;
|
||||
UIView *view = self.alertController.view;
|
||||
[window addSubview:view];
|
||||
view.frame = window.bounds;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
self.frame = self.superview.bounds;
|
||||
[super layoutSubviews];
|
||||
}
|
||||
|
||||
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
|
||||
[super traitCollectionDidChange:previousTraitCollection];
|
||||
if (self.traitCollection.userInterfaceStyle != previousTraitCollection.userInterfaceStyle) {
|
||||
[self updateViewStyle:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateViewStyle:(UIView *)view {
|
||||
if (!view)
|
||||
return;
|
||||
for (UIView *subview in view.subviews)
|
||||
[self updateViewStyle:subview];
|
||||
[view applyTheme];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
@objc(MWMBCCreateCategoryAlert)
|
||||
final class BCCreateCategoryAlert: MWMAlert {
|
||||
private enum State {
|
||||
case valid
|
||||
case tooFewSymbols
|
||||
case tooManySymbols
|
||||
case nameAlreadyExists
|
||||
}
|
||||
|
||||
@IBOutlet private(set) weak var textField: UITextField!
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
@IBOutlet private weak var textFieldContainer: UIView!
|
||||
@IBOutlet private weak var centerHorizontaly: NSLayoutConstraint!
|
||||
@IBOutlet private weak var errorLabel: UILabel!
|
||||
@IBOutlet private weak var charactersCountLabel: UILabel!
|
||||
@IBOutlet private weak var rightButton: UIButton!
|
||||
|
||||
private var maxCharactersNum: UInt?
|
||||
private var minCharactersNum: UInt?
|
||||
private var callback: MWMCheckStringBlock?
|
||||
|
||||
@objc static func alert(maxCharachersNum: UInt,
|
||||
minCharactersNum: UInt,
|
||||
callback: @escaping MWMCheckStringBlock) -> BCCreateCategoryAlert? {
|
||||
guard let alert = Bundle.main.loadNibNamed(className(), owner: nil, options: nil)?.first
|
||||
as? BCCreateCategoryAlert else {
|
||||
assertionFailure()
|
||||
return nil
|
||||
}
|
||||
|
||||
alert.titleLabel.text = L("bookmarks_create_new_group")
|
||||
let text = L("create").capitalized
|
||||
for s in [.normal, .highlighted, .disabled] as [UIControl.State] {
|
||||
alert.rightButton.setTitle(text, for: s)
|
||||
}
|
||||
|
||||
alert.maxCharactersNum = maxCharachersNum
|
||||
alert.minCharactersNum = minCharactersNum
|
||||
alert.callback = callback
|
||||
alert.process(state: .tooFewSymbols)
|
||||
alert.formatCharactersCountText()
|
||||
MWMKeyboard.add(alert)
|
||||
return alert
|
||||
}
|
||||
|
||||
@IBAction private func leftButtonTap() {
|
||||
MWMKeyboard.remove(self)
|
||||
close(nil)
|
||||
}
|
||||
|
||||
@IBAction private func rightButtonTap() {
|
||||
guard let callback = callback, let text = textField.text else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
if callback(text) {
|
||||
MWMKeyboard.remove(self)
|
||||
close(nil)
|
||||
} else {
|
||||
process(state: .nameAlreadyExists)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction private func editingChanged(sender: UITextField) {
|
||||
formatCharactersCountText()
|
||||
process(state: checkState())
|
||||
}
|
||||
|
||||
private func checkState() -> State {
|
||||
let count = textField.text!.count
|
||||
if count <= minCharactersNum! {
|
||||
return .tooFewSymbols
|
||||
}
|
||||
|
||||
if count > maxCharactersNum! {
|
||||
return .tooManySymbols
|
||||
}
|
||||
|
||||
return .valid
|
||||
}
|
||||
|
||||
private func process(state: State) {
|
||||
let style: TextColorStyleSheet
|
||||
switch state {
|
||||
case .valid:
|
||||
style = .blackHint
|
||||
rightButton.isEnabled = true
|
||||
errorLabel.isHidden = true
|
||||
case .tooFewSymbols:
|
||||
style = .blackHint
|
||||
errorLabel.isHidden = true
|
||||
rightButton.isEnabled = false
|
||||
case .tooManySymbols:
|
||||
style = .buttonRed
|
||||
errorLabel.isHidden = false
|
||||
errorLabel.text = L("bookmarks_error_title_list_name_too_long")
|
||||
rightButton.isEnabled = false
|
||||
case .nameAlreadyExists:
|
||||
style = .buttonRed
|
||||
errorLabel.isHidden = false
|
||||
errorLabel.text = L("bookmarks_error_title_list_name_already_taken")
|
||||
rightButton.isEnabled = false
|
||||
}
|
||||
|
||||
charactersCountLabel.setFontStyleAndApply(style)
|
||||
textFieldContainer.layer.borderColor = charactersCountLabel.textColor.cgColor
|
||||
}
|
||||
|
||||
private func formatCharactersCountText() {
|
||||
let count = textField.text!.count
|
||||
charactersCountLabel.text = String(count) + " / " + String(maxCharactersNum!)
|
||||
}
|
||||
}
|
||||
|
||||
extension BCCreateCategoryAlert: UITextFieldDelegate {
|
||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
if checkState() == .valid {
|
||||
rightButtonTap()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func textField(_ textField: UITextField,
|
||||
shouldChangeCharactersIn range: NSRange,
|
||||
replacementString string: String) -> Bool {
|
||||
let str = textField.text as NSString?
|
||||
let newStr = str?.replacingCharacters(in: range, with: string)
|
||||
guard let count = newStr?.count else {
|
||||
return true
|
||||
}
|
||||
|
||||
if count > maxCharactersNum! + 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension BCCreateCategoryAlert: MWMKeyboardObserver {
|
||||
func onKeyboardAnimation() {
|
||||
centerHorizontaly.constant = -MWMKeyboard.keyboardHeight() / 2
|
||||
layoutIfNeeded()
|
||||
}
|
||||
|
||||
func onKeyboardWillAnimate() {
|
||||
setNeedsLayout()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
<?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="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="QQB-Ml-wwB" customClass="MWMBCCreateCategoryAlert" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QG2-Qb-EKf" userLabel="ContainerView">
|
||||
<rect key="frame" x="47.5" y="248" width="280" height="171.5"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4T3-au-6h5" userLabel="Title">
|
||||
<rect key="frame" x="20" y="20" width="240" height="21.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="240" id="4gN-jJ-fkn"/>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="dpW-YS-s4x"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="18"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium18:blackPrimaryText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="bookmarks_create_new_group"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="otE-Ct-TPM">
|
||||
<rect key="frame" x="16" y="61.5" width="248" height="28"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="xlp-ke-FxI">
|
||||
<rect key="frame" x="4" y="0.0" width="240" height="28"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="240" id="T3H-0E-q2e"/>
|
||||
</constraints>
|
||||
<edgeInsets key="layoutMargins" top="4" left="4" bottom="4" right="4"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences" autocorrectionType="no" returnKeyType="done" textContentType="name"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Background:regular14:blackPrimaryText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedPlaceholder" value="bookmark_set_name"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="editingChangedWithSender:" destination="QQB-Ml-wwB" eventType="editingChanged" id="Rab-rj-UaD"/>
|
||||
<outlet property="delegate" destination="QQB-Ml-wwB" id="7Hk-7k-Biw"/>
|
||||
</connections>
|
||||
</textField>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="28" id="497-yd-ibe"/>
|
||||
<constraint firstItem="xlp-ke-FxI" firstAttribute="centerY" secondItem="otE-Ct-TPM" secondAttribute="centerY" id="LK1-93-zgY"/>
|
||||
<constraint firstAttribute="width" constant="248" id="hLe-SE-FJ2"/>
|
||||
<constraint firstItem="xlp-ke-FxI" firstAttribute="centerX" secondItem="otE-Ct-TPM" secondAttribute="centerX" id="sOC-II-DCo"/>
|
||||
<constraint firstItem="xlp-ke-FxI" firstAttribute="height" secondItem="otE-Ct-TPM" secondAttribute="height" id="tj3-TS-dmp"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="AlertViewTextFieldContainer"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uxx-Di-f1Q" userLabel="hDivider">
|
||||
<rect key="frame" x="0.0" y="127.5" width="280" height="1"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="9Ur-cw-cWk"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Wxi-5V-caq" userLabel="left">
|
||||
<rect key="frame" x="0.0" y="127.5" width="140" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="140" id="PcO-3k-jAQ"/>
|
||||
<constraint firstAttribute="height" constant="44" id="sIn-W3-LqB"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="left">
|
||||
<color key="titleColor" red="0.090196078430000007" green="0.61960784310000006" blue="0.30196078430000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="cancel"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="leftButtonTap" destination="QQB-Ml-wwB" eventType="touchUpInside" id="q1X-dI-dSs"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eg6-8K-8yh" userLabel="right">
|
||||
<rect key="frame" x="140" y="127.5" width="140" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="Pk8-S5-m8D"/>
|
||||
<constraint firstAttribute="width" constant="140" id="m9E-k8-sQh"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<state key="normal" title="right">
|
||||
<color key="titleColor" red="0.090196078430000007" green="0.61960784310000006" blue="0.30196078430000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="create"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="FlatNormalTransButton:medium17"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="rightButtonTap" destination="QQB-Ml-wwB" eventType="touchUpInside" id="HT2-dB-W2f"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QQD-2O-DHw" userLabel="vDivider">
|
||||
<rect key="frame" x="139.5" y="127.5" width="1" height="44"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="1" id="Lhw-ff-5Vq"/>
|
||||
<constraint firstAttribute="height" constant="44" id="eaZ-L1-nnD"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="This name is too long" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dX9-2y-ZEd" userLabel="Error label">
|
||||
<rect key="frame" x="16" y="95.5" width="112" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="W4K-74-Pug"/>
|
||||
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="198" id="uYL-oU-tYf"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="11"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular11:buttonRedText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="750" verticalHuggingPriority="251" text="69 / 60" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WKj-KX-xnU" userLabel="Count label">
|
||||
<rect key="frame" x="225.5" y="95.5" width="38.5" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="aJd-wn-gwl"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="11"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular11:buttonRedText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="4T3-au-6h5" firstAttribute="top" secondItem="QG2-Qb-EKf" secondAttribute="top" constant="20" id="2l5-Sp-9hA"/>
|
||||
<constraint firstItem="dX9-2y-ZEd" firstAttribute="top" secondItem="otE-Ct-TPM" secondAttribute="bottom" constant="6" id="3N9-5v-z4Y"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Wxi-5V-caq" secondAttribute="bottom" id="3Ze-v3-100"/>
|
||||
<constraint firstAttribute="trailing" secondItem="eg6-8K-8yh" secondAttribute="trailing" id="6oa-ch-DMu"/>
|
||||
<constraint firstItem="Wxi-5V-caq" firstAttribute="leading" secondItem="QG2-Qb-EKf" secondAttribute="leading" id="6v3-kv-oqt"/>
|
||||
<constraint firstItem="uxx-Di-f1Q" firstAttribute="top" secondItem="dX9-2y-ZEd" secondAttribute="bottom" constant="12" id="7Bf-UJ-7mU"/>
|
||||
<constraint firstItem="uxx-Di-f1Q" firstAttribute="centerX" secondItem="QG2-Qb-EKf" secondAttribute="centerX" id="CSk-xb-Fd3"/>
|
||||
<constraint firstAttribute="width" constant="280" id="CwP-uI-Ukp"/>
|
||||
<constraint firstItem="uxx-Di-f1Q" firstAttribute="width" secondItem="QG2-Qb-EKf" secondAttribute="width" id="Fcb-Hq-n6a"/>
|
||||
<constraint firstItem="otE-Ct-TPM" firstAttribute="top" secondItem="4T3-au-6h5" secondAttribute="bottom" constant="20" id="NAI-NR-TFE"/>
|
||||
<constraint firstItem="otE-Ct-TPM" firstAttribute="centerX" secondItem="QG2-Qb-EKf" secondAttribute="centerX" id="Y2k-u1-PgF"/>
|
||||
<constraint firstItem="Wxi-5V-caq" firstAttribute="top" secondItem="uxx-Di-f1Q" secondAttribute="bottom" constant="-1" id="Yo8-tt-8Q6"/>
|
||||
<constraint firstAttribute="trailing" secondItem="WKj-KX-xnU" secondAttribute="trailing" constant="16" id="aRt-r1-EwI"/>
|
||||
<constraint firstItem="4T3-au-6h5" firstAttribute="centerX" secondItem="QG2-Qb-EKf" secondAttribute="centerX" id="cTf-xg-SpW"/>
|
||||
<constraint firstItem="dX9-2y-ZEd" firstAttribute="leading" secondItem="QG2-Qb-EKf" secondAttribute="leading" constant="16" id="gyf-Hq-FLl"/>
|
||||
<constraint firstItem="QQD-2O-DHw" firstAttribute="centerX" secondItem="QG2-Qb-EKf" secondAttribute="centerX" id="sUU-uz-fhn"/>
|
||||
<constraint firstAttribute="bottom" secondItem="QQD-2O-DHw" secondAttribute="bottom" id="vgE-JK-Ong"/>
|
||||
<constraint firstAttribute="bottom" secondItem="eg6-8K-8yh" secondAttribute="bottom" id="vzg-UL-heQ"/>
|
||||
<constraint firstItem="WKj-KX-xnU" firstAttribute="top" secondItem="otE-Ct-TPM" secondAttribute="bottom" constant="6" id="zK8-7y-xuI"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="AlertView"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="QG2-Qb-EKf" firstAttribute="centerY" secondItem="QQB-Ml-wwB" secondAttribute="centerY" id="od7-2i-rqy"/>
|
||||
<constraint firstItem="QG2-Qb-EKf" firstAttribute="centerX" secondItem="QQB-Ml-wwB" secondAttribute="centerX" id="zjO-HS-7Up"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="oQA-N9-AkF"/>
|
||||
<connections>
|
||||
<outlet property="centerHorizontaly" destination="od7-2i-rqy" id="lVF-DA-PUg"/>
|
||||
<outlet property="charactersCountLabel" destination="WKj-KX-xnU" id="X0L-zL-ZcF"/>
|
||||
<outlet property="errorLabel" destination="dX9-2y-ZEd" id="XLx-WJ-h4h"/>
|
||||
<outlet property="rightButton" destination="eg6-8K-8yh" id="YGF-Zh-u1y"/>
|
||||
<outlet property="textField" destination="xlp-ke-FxI" id="pBV-Ne-ekt"/>
|
||||
<outlet property="textFieldContainer" destination="otE-Ct-TPM" id="Nf8-ly-qdn"/>
|
||||
<outlet property="titleLabel" destination="4T3-au-6h5" id="ye3-HE-xqN"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="304.5" y="171.5"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="dialog_btn_press" width="280" height="44"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
#import "MWMAlert.h"
|
||||
|
||||
@interface MWMDefaultAlert : MWMAlert
|
||||
|
||||
+ (instancetype)routeNotFoundAlert;
|
||||
+ (instancetype)routeNotFoundNoPublicTransportAlert;
|
||||
+ (instancetype)routeNotFoundTooLongPedestrianAlert;
|
||||
+ (instancetype)routeFileNotExistAlert;
|
||||
+ (instancetype)endPointNotFoundAlert;
|
||||
+ (instancetype)startPointNotFoundAlert;
|
||||
+ (instancetype)intermediatePointNotFoundAlert;
|
||||
+ (instancetype)internalRoutingErrorAlert;
|
||||
+ (instancetype)incorrectFeaturePositionAlert;
|
||||
+ (instancetype)notEnoughSpaceAlert;
|
||||
+ (instancetype)invalidUserNameOrPasswordAlert;
|
||||
+ (instancetype)noCurrentPositionAlert;
|
||||
+ (instancetype)pointsInDifferentMWMAlert;
|
||||
+ (instancetype)disabledLocationAlert;
|
||||
+ (instancetype)noWiFiAlertWithOkBlock:(MWMVoidBlock)okBlock andCancelBlock:(MWMVoidBlock)cancelBlock;
|
||||
+ (instancetype)noConnectionAlert;
|
||||
+ (instancetype)deleteMapProhibitedAlert;
|
||||
+ (instancetype)unsavedEditsAlertWithOkBlock:(MWMVoidBlock)okBlock;
|
||||
+ (instancetype)locationServiceNotSupportedAlert;
|
||||
+ (instancetype)point2PointAlertWithOkBlock:(MWMVoidBlock)okBlock needToRebuild:(BOOL)needToRebuild;
|
||||
+ (instancetype)downloaderNoConnectionAlertWithOkBlock:(MWMVoidBlock)okBlock cancelBlock:(MWMVoidBlock)cancelBlock;
|
||||
+ (instancetype)downloaderNotEnoughSpaceAlert;
|
||||
+ (instancetype)downloaderInternalErrorAlertWithOkBlock:(MWMVoidBlock)okBlock cancelBlock:(MWMVoidBlock)cancelBlock;
|
||||
+ (instancetype)resetChangesAlertWithBlock:(MWMVoidBlock)block;
|
||||
+ (instancetype)deleteFeatureAlertWithBlock:(MWMVoidBlock)block;
|
||||
+ (instancetype)personalInfoWarningAlertWithBlock:(MWMVoidBlock)block;
|
||||
+ (instancetype)trackWarningAlertWithCancelBlock:(MWMVoidBlock)block;
|
||||
+ (instancetype)infoAlert:(NSString *)title text:(NSString *)text;
|
||||
+ (instancetype)convertBookmarksWithCount:(NSUInteger)count okBlock:(MWMVoidBlock)okBlock;
|
||||
+ (instancetype)bookmarkConversionErrorAlert;
|
||||
+ (instancetype)tagsLoadingErrorAlertWithOkBlock:(MWMVoidBlock)okBlock cancelBlock:(MWMVoidBlock)cancelBlock;
|
||||
+ (instancetype)bugReportAlertWithTitle:(NSString *)title;
|
||||
|
||||
+ (instancetype)defaultAlertWithTitle:(NSString *)title
|
||||
message:(NSString *)message
|
||||
rightButtonTitle:(NSString *)rightButtonTitle
|
||||
leftButtonTitle:(NSString *)leftButtonTitle
|
||||
rightButtonAction:(MWMVoidBlock)action
|
||||
log:(NSString *)log;
|
||||
|
||||
@end
|
||||
404
iphone/Maps/Classes/CustomAlert/DefaultAlert/MWMDefaultAlert.mm
Normal file
404
iphone/Maps/Classes/CustomAlert/DefaultAlert/MWMDefaultAlert.mm
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
#import "MWMDefaultAlert.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
#include <CoreApi/Framework.h>
|
||||
|
||||
static CGFloat const kDividerTopConstant = -8.;
|
||||
|
||||
@interface MWMDefaultAlert ()
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UILabel *messageLabel;
|
||||
@property(weak, nonatomic) IBOutlet UIButton *rightButton;
|
||||
@property(weak, nonatomic) IBOutlet UIButton *leftButton;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *titleLabel;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *rightButtonWidth;
|
||||
@property(copy, nonatomic) MWMVoidBlock leftButtonAction;
|
||||
@property(copy, nonatomic, readwrite) MWMVoidBlock rightButtonAction;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *dividerTop;
|
||||
@property(weak, nonatomic) IBOutlet UIView *vDivider;
|
||||
|
||||
@end
|
||||
|
||||
static NSString *const kDefaultAlertNibName = @"MWMDefaultAlert";
|
||||
|
||||
@implementation MWMDefaultAlert
|
||||
|
||||
+ (instancetype)routeFileNotExistAlert {
|
||||
return [self defaultAlertWithTitle:L(@"dialog_routing_download_files")
|
||||
message:L(@"dialog_routing_download_and_update_all")
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"Route File Not Exist Alert"];
|
||||
}
|
||||
|
||||
+ (instancetype)routeNotFoundAlert {
|
||||
return [self defaultAlertWithTitle:L(@"dialog_routing_unable_locate_route")
|
||||
message:L(@"dialog_routing_change_start_or_end")
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"Route File Not Exist Alert"];
|
||||
}
|
||||
|
||||
+ (instancetype)routeNotFoundNoPublicTransportAlert {
|
||||
return [self defaultAlertWithTitle:L(@"transit_not_found")
|
||||
message:nil
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"transit_not_found"];
|
||||
}
|
||||
|
||||
+ (instancetype)routeNotFoundTooLongPedestrianAlert {
|
||||
return [self defaultAlertWithTitle:L(@"dialog_pedestrian_route_is_long_header")
|
||||
message:L(@"dialog_pedestrian_route_is_long_message")
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"Long Pedestrian Route Alert"];
|
||||
}
|
||||
|
||||
+ (instancetype)locationServiceNotSupportedAlert {
|
||||
return [self defaultAlertWithTitle:L(@"current_location_unknown_error_title")
|
||||
message:L(@"current_location_unknown_error_message")
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"Location Service Not Supported Alert"];
|
||||
}
|
||||
|
||||
+ (instancetype)noConnectionAlert {
|
||||
MWMDefaultAlert *alert = [self defaultAlertWithTitle:L(@"common_check_internet_connection_dialog")
|
||||
message:nil
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"No Connection Alert"];
|
||||
[alert setNeedsCloseAlertAfterEnterBackground];
|
||||
return alert;
|
||||
}
|
||||
|
||||
+ (instancetype)deleteMapProhibitedAlert {
|
||||
MWMDefaultAlert *alert = [self defaultAlertWithTitle:L(@"downloader_delete_map")
|
||||
message:L(@"downloader_delete_map_while_routing_dialog")
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"Delete Map Prohibited Alert"];
|
||||
[alert setNeedsCloseAlertAfterEnterBackground];
|
||||
return alert;
|
||||
}
|
||||
|
||||
+ (instancetype)unsavedEditsAlertWithOkBlock:(MWMVoidBlock)okBlock {
|
||||
return [self defaultAlertWithTitle:L(@"please_note")
|
||||
message:L(@"downloader_delete_map_dialog")
|
||||
rightButtonTitle:L(@"delete")
|
||||
leftButtonTitle:L(@"cancel")
|
||||
rightButtonAction:okBlock
|
||||
log:@"Editor unsaved changes on delete"];
|
||||
}
|
||||
|
||||
+ (instancetype)noWiFiAlertWithOkBlock:(MWMVoidBlock)okBlock andCancelBlock:(MWMVoidBlock)cancelBlock {
|
||||
MWMDefaultAlert *alert = [self defaultAlertWithTitle:L(@"download_over_mobile_header")
|
||||
message:L(@"download_over_mobile_message")
|
||||
rightButtonTitle:L(@"use_cellular_data")
|
||||
leftButtonTitle:L(@"cancel")
|
||||
rightButtonAction:okBlock
|
||||
log:@"No WiFi Alert"];
|
||||
alert.leftButtonAction = cancelBlock;
|
||||
[alert setNeedsCloseAlertAfterEnterBackground];
|
||||
return alert;
|
||||
}
|
||||
|
||||
+ (instancetype)endPointNotFoundAlert {
|
||||
NSString *message = [NSString
|
||||
stringWithFormat:@"%@\n\n%@", L(@"dialog_routing_end_not_determined"), L(@"dialog_routing_select_closer_end")];
|
||||
return [self defaultAlertWithTitle:L(@"dialog_routing_change_end")
|
||||
message:message
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"End Point Not Found Alert"];
|
||||
}
|
||||
|
||||
+ (instancetype)startPointNotFoundAlert {
|
||||
NSString *message = [NSString
|
||||
stringWithFormat:@"%@\n\n%@", L(@"dialog_routing_start_not_determined"), L(@"dialog_routing_select_closer_start")];
|
||||
return [self defaultAlertWithTitle:L(@"dialog_routing_change_start")
|
||||
message:message
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"Start Point Not Found Alert"];
|
||||
}
|
||||
|
||||
+ (instancetype)intermediatePointNotFoundAlert {
|
||||
return [self defaultAlertWithTitle:L(@"dialog_routing_change_intermediate")
|
||||
message:L(@"dialog_routing_intermediate_not_determined")
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"Intermediate Point Not Found Alert"];
|
||||
}
|
||||
|
||||
+ (instancetype)internalRoutingErrorAlert {
|
||||
NSString *message =
|
||||
[NSString stringWithFormat:@"%@\n\n%@", L(@"dialog_routing_application_error"), L(@"dialog_routing_try_again")];
|
||||
return [self defaultAlertWithTitle:L(@"dialog_routing_system_error")
|
||||
message:message
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"Internal Routing Error Alert"];
|
||||
}
|
||||
|
||||
+ (instancetype)incorrectFeaturePositionAlert {
|
||||
return [self defaultAlertWithTitle:L(@"dialog_incorrect_feature_position")
|
||||
message:L(@"message_invalid_feature_position")
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"Incorrect Feature Possition Alert"];
|
||||
}
|
||||
|
||||
+ (instancetype)notEnoughSpaceAlert {
|
||||
MWMDefaultAlert *alert = [self defaultAlertWithTitle:L(@"downloader_no_space_title")
|
||||
message:L(@"migration_no_space_message")
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"Not Enough Space Alert"];
|
||||
[alert setNeedsCloseAlertAfterEnterBackground];
|
||||
return alert;
|
||||
}
|
||||
|
||||
+ (instancetype)invalidUserNameOrPasswordAlert {
|
||||
return [self defaultAlertWithTitle:L(@"invalid_username_or_password")
|
||||
message:nil
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"Invalid User Name or Password Alert"];
|
||||
}
|
||||
|
||||
+ (instancetype)noCurrentPositionAlert {
|
||||
NSString *message = [NSString stringWithFormat:@"%@\n\n%@", L(@"dialog_routing_error_location_not_found"),
|
||||
L(@"dialog_routing_location_turn_wifi")];
|
||||
return [self defaultAlertWithTitle:L(@"dialog_routing_check_gps")
|
||||
message:message
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"No Current Position Alert"];
|
||||
}
|
||||
|
||||
+ (instancetype)disabledLocationAlert {
|
||||
MWMVoidBlock action = ^{
|
||||
GetFramework().SwitchMyPositionNextMode();
|
||||
};
|
||||
return [self defaultAlertWithTitle:L(@"dialog_routing_location_turn_on")
|
||||
message:L(@"dialog_routing_location_unknown_turn_on")
|
||||
rightButtonTitle:L(@"turn_on")
|
||||
leftButtonTitle:L(@"later")
|
||||
rightButtonAction:action
|
||||
log:@"Disabled Location Alert"];
|
||||
}
|
||||
|
||||
+ (instancetype)pointsInDifferentMWMAlert {
|
||||
return [self defaultAlertWithTitle:L(@"routing_failed_cross_mwm_building")
|
||||
message:nil
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"Points In Different MWM Alert"];
|
||||
}
|
||||
|
||||
+ (instancetype)point2PointAlertWithOkBlock:(MWMVoidBlock)okBlock needToRebuild:(BOOL)needToRebuild {
|
||||
if (needToRebuild) {
|
||||
return [self defaultAlertWithTitle:L(@"p2p_only_from_current")
|
||||
message:L(@"p2p_reroute_from_current")
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:L(@"cancel")
|
||||
rightButtonAction:okBlock
|
||||
log:@"Default Alert"];
|
||||
} else {
|
||||
return [self defaultAlertWithTitle:L(@"p2p_only_from_current")
|
||||
message:nil
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"Default Alert"];
|
||||
}
|
||||
}
|
||||
|
||||
+ (instancetype)downloaderNoConnectionAlertWithOkBlock:(MWMVoidBlock)okBlock cancelBlock:(MWMVoidBlock)cancelBlock {
|
||||
MWMDefaultAlert *alert = [self defaultAlertWithTitle:L(@"downloader_status_failed")
|
||||
message:L(@"common_check_internet_connection_dialog")
|
||||
rightButtonTitle:L(@"downloader_retry")
|
||||
leftButtonTitle:L(@"cancel")
|
||||
rightButtonAction:okBlock
|
||||
log:@"Downloader No Connection Alert"];
|
||||
alert.leftButtonAction = cancelBlock;
|
||||
[alert setNeedsCloseAlertAfterEnterBackground];
|
||||
return alert;
|
||||
}
|
||||
|
||||
+ (instancetype)downloaderNotEnoughSpaceAlert {
|
||||
MWMDefaultAlert *alert = [self defaultAlertWithTitle:L(@"downloader_no_space_title")
|
||||
message:L(@"downloader_no_space_message")
|
||||
rightButtonTitle:L(@"close")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"Downloader Not Enough Space Alert"];
|
||||
[alert setNeedsCloseAlertAfterEnterBackground];
|
||||
return alert;
|
||||
}
|
||||
|
||||
+ (instancetype)downloaderInternalErrorAlertWithOkBlock:(MWMVoidBlock)okBlock cancelBlock:(MWMVoidBlock)cancelBlock {
|
||||
MWMDefaultAlert *alert = [self defaultAlertWithTitle:L(@"migration_download_error_dialog")
|
||||
message:nil
|
||||
rightButtonTitle:L(@"downloader_retry")
|
||||
leftButtonTitle:L(@"cancel")
|
||||
rightButtonAction:okBlock
|
||||
log:@"Downloader Internal Error Alert"];
|
||||
alert.leftButtonAction = cancelBlock;
|
||||
[alert setNeedsCloseAlertAfterEnterBackground];
|
||||
return alert;
|
||||
}
|
||||
|
||||
+ (instancetype)resetChangesAlertWithBlock:(MWMVoidBlock)block {
|
||||
MWMDefaultAlert *alert = [self defaultAlertWithTitle:L(@"editor_reset_edits_message")
|
||||
message:nil
|
||||
rightButtonTitle:L(@"editor_reset_edits_button")
|
||||
leftButtonTitle:L(@"cancel")
|
||||
rightButtonAction:block
|
||||
log:@"Reset changes alert"];
|
||||
return alert;
|
||||
}
|
||||
|
||||
+ (instancetype)deleteFeatureAlertWithBlock:(MWMVoidBlock)block {
|
||||
MWMDefaultAlert *alert = [self defaultAlertWithTitle:L(@"editor_remove_place_message")
|
||||
message:nil
|
||||
rightButtonTitle:L(@"editor_remove_place_button")
|
||||
leftButtonTitle:L(@"cancel")
|
||||
rightButtonAction:block
|
||||
log:@"Delete feature alert"];
|
||||
return alert;
|
||||
}
|
||||
|
||||
+ (instancetype)personalInfoWarningAlertWithBlock:(MWMVoidBlock)block {
|
||||
NSString *message = [NSString
|
||||
stringWithFormat:@"%@\n%@", L(@"editor_share_to_all_dialog_message_1"), L(@"editor_share_to_all_dialog_message_2")];
|
||||
MWMDefaultAlert *alert = [self defaultAlertWithTitle:L(@"editor_share_to_all_dialog_title")
|
||||
message:message
|
||||
rightButtonTitle:L(@"editor_report_problem_send_button")
|
||||
leftButtonTitle:L(@"cancel")
|
||||
rightButtonAction:block
|
||||
log:@"Personal info warning alert"];
|
||||
return alert;
|
||||
}
|
||||
|
||||
+ (instancetype)trackWarningAlertWithCancelBlock:(MWMVoidBlock)block {
|
||||
MWMDefaultAlert *alert = [self defaultAlertWithTitle:L(@"recent_track_background_dialog_title")
|
||||
message:L(@"recent_track_background_dialog_message")
|
||||
rightButtonTitle:L(@"off_recent_track_background_button")
|
||||
leftButtonTitle:L(@"continue_button")
|
||||
rightButtonAction:block
|
||||
log:@"Track warning alert"];
|
||||
return alert;
|
||||
}
|
||||
|
||||
+ (instancetype)infoAlert:(NSString *)title text:(NSString *)text {
|
||||
return [self defaultAlertWithTitle:title
|
||||
message:text
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:@"Info Alert"];
|
||||
}
|
||||
|
||||
+ (instancetype)convertBookmarksWithCount:(NSUInteger)count okBlock:(MWMVoidBlock)okBlock {
|
||||
return [self defaultAlertWithTitle:L(@"bookmarks_detect_title")
|
||||
message:[NSString stringWithFormat:L(@"bookmarks_detect_message"), count]
|
||||
rightButtonTitle:L(@"button_convert")
|
||||
leftButtonTitle:L(@"cancel")
|
||||
rightButtonAction:okBlock
|
||||
log:nil];
|
||||
}
|
||||
|
||||
+ (instancetype)bookmarkConversionErrorAlert {
|
||||
return [self defaultAlertWithTitle:L(@"bookmarks_convert_error_title")
|
||||
message:L(@"bookmarks_convert_error_message")
|
||||
rightButtonTitle:L(@"ok")
|
||||
leftButtonTitle:nil
|
||||
rightButtonAction:nil
|
||||
log:nil];
|
||||
}
|
||||
|
||||
+ (instancetype)tagsLoadingErrorAlertWithOkBlock:(MWMVoidBlock)okBlock cancelBlock:(MWMVoidBlock)cancelBlock {
|
||||
MWMDefaultAlert *alert = [self defaultAlertWithTitle:L(@"title_error_downloading_bookmarks")
|
||||
message:L(@"tags_loading_error_subtitle")
|
||||
rightButtonTitle:L(@"downloader_retry")
|
||||
leftButtonTitle:L(@"cancel")
|
||||
rightButtonAction:okBlock
|
||||
log:nil];
|
||||
alert.leftButtonAction = cancelBlock;
|
||||
[alert setNeedsCloseAlertAfterEnterBackground];
|
||||
return alert;
|
||||
}
|
||||
|
||||
+ (instancetype)bugReportAlertWithTitle:(NSString *)title {
|
||||
MWMDefaultAlert *alert = [self defaultAlertWithTitle:title
|
||||
message:L(@"bugreport_alert_message")
|
||||
rightButtonTitle:L(@"report_a_bug")
|
||||
leftButtonTitle:L(@"cancel")
|
||||
rightButtonAction:^{ [MailComposer sendBugReportWithTitle:title]; }
|
||||
log:nil];
|
||||
[alert setNeedsCloseAlertAfterEnterBackground];
|
||||
return alert;
|
||||
}
|
||||
|
||||
+ (instancetype)defaultAlertWithTitle:(NSString *)title
|
||||
message:(NSString *)message
|
||||
rightButtonTitle:(NSString *)rightButtonTitle
|
||||
leftButtonTitle:(NSString *)leftButtonTitle
|
||||
rightButtonAction:(MWMVoidBlock)action
|
||||
log:(NSString *)log {
|
||||
if (log) {
|
||||
LOG(LINFO, ([log UTF8String]));
|
||||
}
|
||||
MWMDefaultAlert *alert = [NSBundle.mainBundle loadNibNamed:kDefaultAlertNibName owner:self options:nil].firstObject;
|
||||
alert.titleLabel.text = title;
|
||||
alert.messageLabel.text = message;
|
||||
if (!message) {
|
||||
alert.dividerTop.constant = kDividerTopConstant;
|
||||
[alert layoutIfNeeded];
|
||||
}
|
||||
|
||||
[alert.rightButton setTitle:rightButtonTitle forState:UIControlStateNormal];
|
||||
[alert.rightButton setTitle:rightButtonTitle forState:UIControlStateDisabled];
|
||||
|
||||
alert.rightButtonAction = action;
|
||||
if (leftButtonTitle) {
|
||||
[alert.leftButton setTitle:leftButtonTitle forState:UIControlStateNormal];
|
||||
[alert.leftButton setTitle:leftButtonTitle forState:UIControlStateDisabled];
|
||||
} else {
|
||||
alert.vDivider.hidden = YES;
|
||||
alert.leftButton.hidden = YES;
|
||||
alert.rightButtonWidth.constant = [alert.subviews.firstObject width];
|
||||
}
|
||||
return alert;
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (IBAction)rightButtonTap {
|
||||
[self close:self.rightButtonAction];
|
||||
}
|
||||
|
||||
- (IBAction)leftButtonTap {
|
||||
[self close:self.leftButtonAction];
|
||||
}
|
||||
|
||||
@end
|
||||
155
iphone/Maps/Classes/CustomAlert/DefaultAlert/MWMDefaultAlert.xib
Normal file
155
iphone/Maps/Classes/CustomAlert/DefaultAlert/MWMDefaultAlert.xib
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="MWMDefaultAlert" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="zso-DD-6SG" userLabel="ContainerView">
|
||||
<rect key="frame" x="47.5" y="264.5" width="280" height="138.5"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Grl-z5-2cE" userLabel="Title">
|
||||
<rect key="frame" x="20" y="20" width="240" height="21.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="0SG-Lk-wkv"/>
|
||||
<constraint firstAttribute="width" constant="240" id="9rm-Av-9eY"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="18"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium18:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Message" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0D6-oV-Lio" userLabel="Message">
|
||||
<rect key="frame" x="20" y="53.5" width="240" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="dG8-qs-ZWQ"/>
|
||||
<constraint firstAttribute="width" constant="240" id="wbf-hD-V6a"/>
|
||||
</constraints>
|
||||
<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:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="f8D-qe-39W" userLabel="hDivider">
|
||||
<rect key="frame" x="0.0" y="93.5" width="280" height="1"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="HTM-C2-dMq"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="jAg-cp-4Ms" userLabel="left">
|
||||
<rect key="frame" x="0.0" y="94.5" width="140" height="44"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="cancelButton"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="140" id="4bW-b0-naB"/>
|
||||
<constraint firstAttribute="height" constant="44" id="RRQ-jI-El3"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="left">
|
||||
<color key="titleColor" red="0.090196078430000007" green="0.61960784310000006" blue="0.30196078430000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="ok"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="leftButtonTap" destination="iN0-l3-epB" eventType="touchUpInside" id="xE7-aE-OzM"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5H4-oD-Dex" userLabel="vDivider">
|
||||
<rect key="frame" x="139" y="94" width="1" height="44"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="8r3-72-asP"/>
|
||||
<constraint firstAttribute="width" constant="1" id="ORs-aP-K10"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="RSB-i6-pEP" userLabel="right">
|
||||
<rect key="frame" x="140" y="94.5" width="140" height="44"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="OKButton"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="140" id="C3i-gd-pxv"/>
|
||||
<constraint firstAttribute="height" constant="44" id="dta-cI-rDi"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<state key="normal" title="right">
|
||||
<color key="titleColor" red="0.090196078430000007" green="0.61960784310000006" blue="0.30196078430000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="ok"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="rightButtonTap" destination="iN0-l3-epB" eventType="touchUpInside" id="UO8-7c-q1i"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.88" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="Grl-z5-2cE" firstAttribute="top" secondItem="zso-DD-6SG" secondAttribute="top" constant="20" id="5iy-IL-Non"/>
|
||||
<constraint firstAttribute="bottom" secondItem="RSB-i6-pEP" secondAttribute="bottom" id="7PX-qC-iuH"/>
|
||||
<constraint firstItem="jAg-cp-4Ms" firstAttribute="top" secondItem="f8D-qe-39W" secondAttribute="bottom" id="BZX-XF-Cwq"/>
|
||||
<constraint firstItem="RSB-i6-pEP" firstAttribute="top" secondItem="f8D-qe-39W" secondAttribute="bottom" id="FV6-rs-AaS"/>
|
||||
<constraint firstAttribute="width" constant="280" id="H9k-u4-6Xx"/>
|
||||
<constraint firstAttribute="centerX" secondItem="0D6-oV-Lio" secondAttribute="centerX" id="HOk-vG-jd5"/>
|
||||
<constraint firstItem="0D6-oV-Lio" firstAttribute="top" secondItem="Grl-z5-2cE" secondAttribute="bottom" constant="12" id="QBn-bf-ANB"/>
|
||||
<constraint firstAttribute="bottom" secondItem="jAg-cp-4Ms" secondAttribute="bottom" id="QhB-d8-4KJ"/>
|
||||
<constraint firstItem="RSB-i6-pEP" firstAttribute="leading" secondItem="5H4-oD-Dex" secondAttribute="trailing" id="SbQ-E0-P7N"/>
|
||||
<constraint firstAttribute="centerX" secondItem="f8D-qe-39W" secondAttribute="centerX" id="TuK-ia-Gl9"/>
|
||||
<constraint firstItem="f8D-qe-39W" firstAttribute="top" secondItem="0D6-oV-Lio" secondAttribute="bottom" constant="20" id="XWN-lh-LsI"/>
|
||||
<constraint firstAttribute="bottom" secondItem="5H4-oD-Dex" secondAttribute="bottom" constant="0.5" id="el1-Te-FrO"/>
|
||||
<constraint firstAttribute="width" secondItem="f8D-qe-39W" secondAttribute="width" id="rtN-ma-JgY"/>
|
||||
<constraint firstItem="jAg-cp-4Ms" firstAttribute="leading" secondItem="zso-DD-6SG" secondAttribute="leading" id="x4L-bc-J3N"/>
|
||||
<constraint firstAttribute="trailing" secondItem="RSB-i6-pEP" secondAttribute="trailing" id="zAm-vx-0g5"/>
|
||||
<constraint firstAttribute="centerX" secondItem="Grl-z5-2cE" secondAttribute="centerX" id="zap-Oc-W3i"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="AlertView"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="centerY" secondItem="zso-DD-6SG" secondAttribute="centerY" id="2Yk-mx-NtS"/>
|
||||
<constraint firstAttribute="centerX" secondItem="zso-DD-6SG" secondAttribute="centerX" id="rvU-lH-fCc"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="4wG-7A-b6h"/>
|
||||
<connections>
|
||||
<outlet property="dividerTop" destination="XWN-lh-LsI" id="Ken-WZ-bMm"/>
|
||||
<outlet property="leftButton" destination="jAg-cp-4Ms" id="UpD-3H-PmN"/>
|
||||
<outlet property="messageLabel" destination="0D6-oV-Lio" id="1pY-jt-AFL"/>
|
||||
<outlet property="rightButton" destination="RSB-i6-pEP" id="DC9-9z-U2K"/>
|
||||
<outlet property="rightButtonWidth" destination="C3i-gd-pxv" id="YMb-U5-4iJ"/>
|
||||
<outlet property="titleLabel" destination="Grl-z5-2cE" id="p6A-DJ-xjR"/>
|
||||
<outlet property="vDivider" destination="5H4-oD-Dex" id="7xY-ln-bYK"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="305" y="172"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="dialog_btn_press" width="280" height="44"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#import "MWMDefaultAlert.h"
|
||||
|
||||
@interface MWMDefaultAlert (Protected)
|
||||
|
||||
+ (nonnull instancetype)defaultAlertWithTitle:(nonnull NSString *)title
|
||||
message:(nullable NSString *)message
|
||||
rightButtonTitle:(nonnull NSString *)rightButtonTitle
|
||||
leftButtonTitle:(nullable NSString *)leftButtonTitle
|
||||
rightButtonAction:(nullable MWMVoidBlock)action
|
||||
log:(nullable NSString *)log;
|
||||
|
||||
@property(copy, nonatomic, readonly, nullable) MWMVoidBlock rightButtonAction;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#import "MWMAlert+CPP.h"
|
||||
|
||||
#include "routing/routing_callbacks.hpp"
|
||||
|
||||
#include "storage/storage_defines.hpp"
|
||||
|
||||
@interface MWMDownloadTransitMapAlert : MWMAlert
|
||||
|
||||
+ (instancetype)downloaderAlertWithMaps:(storage::CountriesSet const &)countries
|
||||
code:(routing::RouterResultCode)code
|
||||
cancelBlock:(MWMVoidBlock)cancelBlock
|
||||
downloadBlock:(MWMDownloadBlock)downloadBlock
|
||||
downloadCompleteBlock:(MWMVoidBlock)downloadCompleteBlock;
|
||||
- (void)showDownloadDetail:(UIButton *)sender;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,352 @@
|
|||
#import "MWMDownloadTransitMapAlert.h"
|
||||
#import "MWMCircularProgress.h"
|
||||
#import "MWMDownloaderDialogCell.h"
|
||||
#import "MWMDownloaderDialogHeader.h"
|
||||
#import "MWMStorage+UI.h"
|
||||
#import "SwiftBridge.h"
|
||||
|
||||
#include <CoreApi/Framework.h>
|
||||
|
||||
#include "platform/downloader_defines.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
NSString * const kDownloadTransitMapAlertNibName = @"MWMDownloadTransitMapAlert";
|
||||
|
||||
CGFloat const kCellHeight = 32.;
|
||||
CGFloat const kHeaderHeight = 44.;
|
||||
CGFloat const kMinimumOffset = 20.;
|
||||
CGFloat const kAnimationDuration = .05;
|
||||
} // namespace
|
||||
|
||||
@interface MWMDownloadTransitMapAlert () <UITableViewDataSource, UITableViewDelegate, MWMStorageObserver, MWMCircularProgressProtocol>
|
||||
|
||||
@property(copy, nonatomic) MWMVoidBlock cancelBlock;
|
||||
@property(copy, nonatomic) MWMDownloadBlock downloadBlock;
|
||||
@property(copy, nonatomic) MWMVoidBlock downloadCompleteBlock;
|
||||
|
||||
@property (nonatomic) MWMCircularProgress * progress;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView * containerView;
|
||||
@property (weak, nonatomic) IBOutlet UILabel * titleLabel;
|
||||
@property (weak, nonatomic) IBOutlet UILabel * messageLabel;
|
||||
@property (weak, nonatomic) IBOutlet UITableView * dialogsTableView;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint * tableViewHeight;
|
||||
@property (nonatomic) MWMDownloaderDialogHeader * listHeader;
|
||||
@property (nonatomic) BOOL listExpanded;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView * progressWrapper;
|
||||
@property (weak, nonatomic) IBOutlet UIView * hDivider;
|
||||
@property (weak, nonatomic) IBOutlet UIView * vDivider;
|
||||
@property (weak, nonatomic) IBOutlet UIButton * leftButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton * rightButton;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint * dialogsBottomOffset;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint * progressWrapperBottomOffset;
|
||||
|
||||
@property (copy, nonatomic) NSArray<NSString *> * countriesNames;
|
||||
@property (copy, nonatomic) NSString * countriesSize;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMDownloadTransitMapAlert
|
||||
{
|
||||
storage::CountriesVec m_countries;
|
||||
}
|
||||
|
||||
+ (instancetype)downloaderAlertWithMaps:(storage::CountriesSet const &)countries
|
||||
code:(routing::RouterResultCode)code
|
||||
cancelBlock:(MWMVoidBlock)cancelBlock
|
||||
downloadBlock:(MWMDownloadBlock)downloadBlock
|
||||
downloadCompleteBlock:(MWMVoidBlock)downloadCompleteBlock
|
||||
{
|
||||
MWMDownloadTransitMapAlert * alert = [self alertWithCountries:countries];
|
||||
switch (code)
|
||||
{
|
||||
case routing::RouterResultCode::InconsistentMWMandRoute:
|
||||
case routing::RouterResultCode::RouteNotFound:
|
||||
case routing::RouterResultCode::RouteFileNotExist:
|
||||
alert.titleLabel.text = L(@"dialog_routing_download_files");
|
||||
alert.messageLabel.text = L(@"dialog_routing_download_and_update_all");
|
||||
break;
|
||||
case routing::RouterResultCode::FileTooOld:
|
||||
alert.titleLabel.text = L(@"dialog_routing_download_files");
|
||||
alert.messageLabel.text = L(@"dialog_routing_download_and_update_maps");
|
||||
break;
|
||||
case routing::RouterResultCode::NeedMoreMaps:
|
||||
alert.titleLabel.text = L(@"dialog_routing_download_and_build_cross_route");
|
||||
alert.messageLabel.text = L(@"dialog_routing_download_cross_route");
|
||||
break;
|
||||
default:
|
||||
NSAssert(false, @"Incorrect code!");
|
||||
break;
|
||||
}
|
||||
alert.cancelBlock = cancelBlock;
|
||||
alert.downloadBlock = downloadBlock;
|
||||
alert.downloadCompleteBlock = downloadCompleteBlock;
|
||||
return alert;
|
||||
}
|
||||
|
||||
+ (instancetype)alertWithCountries:(storage::CountriesSet const &)countries
|
||||
{
|
||||
NSAssert(!countries.empty(), @"countries can not be empty.");
|
||||
MWMDownloadTransitMapAlert * alert =
|
||||
[NSBundle.mainBundle loadNibNamed:kDownloadTransitMapAlertNibName owner:nil options:nil]
|
||||
.firstObject;
|
||||
|
||||
alert->m_countries = storage::CountriesVec(countries.begin(), countries.end());
|
||||
[alert configure];
|
||||
[alert updateCountriesList];
|
||||
[[MWMStorage sharedStorage] addObserver:alert];
|
||||
return alert;
|
||||
}
|
||||
|
||||
- (void)configure
|
||||
{
|
||||
[self.dialogsTableView registerNibWithCellClass:[MWMDownloaderDialogCell class]];
|
||||
self.listExpanded = NO;
|
||||
CALayer * containerViewLayer = self.containerView.layer;
|
||||
containerViewLayer.shouldRasterize = YES;
|
||||
containerViewLayer.rasterizationScale = [[UIScreen mainScreen] scale];
|
||||
[self.dialogsTableView reloadData];
|
||||
}
|
||||
|
||||
- (void)updateCountriesList
|
||||
{
|
||||
auto const & s = GetFramework().GetStorage();
|
||||
m_countries.erase(
|
||||
remove_if(m_countries.begin(), m_countries.end(),
|
||||
[&s](storage::CountryId const & countryId) { return s.HasLatestVersion(countryId); }),
|
||||
m_countries.end());
|
||||
NSMutableArray<NSString *> * titles = [@[] mutableCopy];
|
||||
MwmSize totalSize = 0;
|
||||
for (auto const & countryId : m_countries)
|
||||
{
|
||||
storage::NodeAttrs attrs;
|
||||
s.GetNodeAttrs(countryId, attrs);
|
||||
[titles addObject:@(attrs.m_nodeLocalName.c_str())];
|
||||
totalSize += attrs.m_mwmSize;
|
||||
}
|
||||
self.countriesNames = titles;
|
||||
self.countriesSize = formattedSize(totalSize);
|
||||
}
|
||||
|
||||
#pragma mark - MWMCircularProgressProtocol
|
||||
|
||||
- (void)progressButtonPressed:(nonnull MWMCircularProgress *)progress
|
||||
{
|
||||
for (auto const & countryId : m_countries)
|
||||
[[MWMStorage sharedStorage] cancelDownloadNode:@(countryId.c_str())];
|
||||
[self cancelButtonTap];
|
||||
}
|
||||
|
||||
#pragma mark - MWMStorageObserver
|
||||
|
||||
- (void)processCountryEvent:(NSString *)countryId
|
||||
{
|
||||
if (find(m_countries.begin(), m_countries.end(), countryId.UTF8String) == m_countries.end())
|
||||
return;
|
||||
if (self.rightButton.hidden)
|
||||
{
|
||||
auto const & s = GetFramework().GetStorage();
|
||||
auto const & p = GetFramework().GetDownloadingPolicy();
|
||||
if (s.CheckFailedCountries(m_countries))
|
||||
{
|
||||
if (p.IsAutoRetryDownloadFailed())
|
||||
[self close:nil];
|
||||
return;
|
||||
}
|
||||
auto const overallProgress = s.GetOverallProgress(m_countries);
|
||||
// Test if downloading has finished by comparing downloaded and total sizes.
|
||||
if (overallProgress.m_bytesDownloaded == overallProgress.m_bytesTotal)
|
||||
[self close:self.downloadCompleteBlock];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self updateCountriesList];
|
||||
[self.dialogsTableView reloadSections:[NSIndexSet indexSetWithIndex:0]
|
||||
withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
if (m_countries.empty())
|
||||
[self close:self.downloadCompleteBlock];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)processCountry:(NSString *)countryId
|
||||
downloadedBytes:(uint64_t)downloadedBytes
|
||||
totalBytes:(uint64_t)totalBytes
|
||||
{
|
||||
if (!self.rightButton.hidden ||
|
||||
find(m_countries.begin(), m_countries.end(), countryId.UTF8String) == m_countries.end())
|
||||
return;
|
||||
auto const overallProgress = GetFramework().GetStorage().GetOverallProgress(m_countries);
|
||||
CGFloat const progressValue =
|
||||
static_cast<CGFloat>(overallProgress.m_bytesDownloaded) / overallProgress.m_bytesTotal;
|
||||
self.progress.progress = progressValue;
|
||||
self.titleLabel.text = [NSString stringWithFormat:@"%@%@%%", L(@"downloading"), @(floor(progressValue * 100))];
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (IBAction)cancelButtonTap
|
||||
{
|
||||
[self close:self.cancelBlock];
|
||||
}
|
||||
|
||||
- (IBAction)downloadButtonTap
|
||||
{
|
||||
[self updateCountriesList];
|
||||
if (m_countries.empty())
|
||||
{
|
||||
[self close:self.downloadCompleteBlock];
|
||||
return;
|
||||
}
|
||||
self.downloadBlock(m_countries, ^{
|
||||
self.titleLabel.text = L(@"downloading");
|
||||
self.messageLabel.hidden = YES;
|
||||
self.progressWrapper.hidden = NO;
|
||||
self.progress.state = MWMCircularProgressStateSpinner;
|
||||
self.hDivider.hidden = YES;
|
||||
self.vDivider.hidden = YES;
|
||||
self.leftButton.hidden = YES;
|
||||
self.rightButton.hidden = YES;
|
||||
self.dialogsBottomOffset.priority = UILayoutPriorityDefaultHigh;
|
||||
self.progressWrapperBottomOffset.priority = UILayoutPriorityDefaultHigh;
|
||||
[UIView animateWithDuration:kAnimationDuration
|
||||
animations:^{
|
||||
[self layoutSubviews];
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)showDownloadDetail:(UIButton *)sender
|
||||
{
|
||||
self.listExpanded = sender.selected;
|
||||
}
|
||||
|
||||
- (void)setListExpanded:(BOOL)listExpanded
|
||||
{
|
||||
_listExpanded = listExpanded;
|
||||
[self layoutIfNeeded];
|
||||
auto const updateCells = ^(BOOL show)
|
||||
{
|
||||
for (MWMDownloaderDialogCell * cell in self.dialogsTableView.visibleCells)
|
||||
{
|
||||
cell.titleLabel.alpha = show ? 1. : 0.;
|
||||
}
|
||||
[self.dialogsTableView refresh];
|
||||
};
|
||||
if (listExpanded)
|
||||
{
|
||||
CGFloat const actualHeight = kCellHeight * m_countries.size() + kHeaderHeight;
|
||||
CGFloat const height = [self bounded:actualHeight withHeight:self.superview.height];
|
||||
self.tableViewHeight.constant = height;
|
||||
self.dialogsTableView.scrollEnabled = actualHeight > self.tableViewHeight.constant;
|
||||
[UIView animateWithDuration:kAnimationDuration animations:^{ [self layoutSubviews]; }
|
||||
completion:^(BOOL finished)
|
||||
{
|
||||
[UIView animateWithDuration:kDefaultAnimationDuration animations:^{ updateCells(YES); }];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.tableViewHeight.constant = kHeaderHeight;
|
||||
self.dialogsTableView.scrollEnabled = NO;
|
||||
updateCells(NO);
|
||||
[UIView animateWithDuration:kAnimationDuration animations:^{ [self layoutSubviews]; }];
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)bounded:(CGFloat)f withHeight:(CGFloat)h
|
||||
{
|
||||
CGFloat const currentHeight = [self.subviews.firstObject height];
|
||||
CGFloat const maximumHeight = h - 2. * kMinimumOffset;
|
||||
CGFloat const availableHeight = maximumHeight - currentHeight;
|
||||
return MIN(f, availableHeight + self.tableViewHeight.constant);
|
||||
}
|
||||
|
||||
- (void)invalidateTableConstraintWithHeight:(CGFloat)height
|
||||
{
|
||||
if (self.listExpanded)
|
||||
{
|
||||
CGFloat const actualHeight = kCellHeight * m_countries.size() + kHeaderHeight;
|
||||
self.tableViewHeight.constant = [self bounded:actualHeight withHeight:height];
|
||||
self.dialogsTableView.scrollEnabled = actualHeight > self.tableViewHeight.constant;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.tableViewHeight.constant = kHeaderHeight;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation
|
||||
{
|
||||
CGFloat const height = UIInterfaceOrientationIsLandscape(orientation) ? MIN(self.superview.width, self.superview.height) : MAX(self.superview.width, self.superview.height);
|
||||
[self invalidateTableConstraintWithHeight:height];
|
||||
}
|
||||
|
||||
- (void)close:(MWMVoidBlock)completion
|
||||
{
|
||||
[[MWMStorage sharedStorage] removeObserver:self];
|
||||
[super close:completion];
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDelegate
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
return m_countries.size();
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
return kHeaderHeight;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return kCellHeight;
|
||||
}
|
||||
|
||||
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
return self.listHeader;
|
||||
}
|
||||
|
||||
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
|
||||
{
|
||||
UIView * view = [[UIView alloc] init];
|
||||
view.styleName = @"BlackOpaqueBackground";
|
||||
return view;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
Class cls = [MWMDownloaderDialogCell class];
|
||||
auto cell = static_cast<MWMDownloaderDialogCell *>(
|
||||
[tableView dequeueReusableCellWithCellClass:cls indexPath:indexPath]);
|
||||
cell.titleLabel.text = self.countriesNames[indexPath.row];
|
||||
return cell;
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (MWMDownloaderDialogHeader *)listHeader
|
||||
{
|
||||
if (!_listHeader)
|
||||
_listHeader = [MWMDownloaderDialogHeader headerForOwnerAlert:self];
|
||||
|
||||
[_listHeader setTitle:[NSString stringWithFormat:@"%@ (%@)", L(@"downloader_status_maps"), @(m_countries.size())]
|
||||
size:self.countriesSize];
|
||||
return _listHeader;
|
||||
}
|
||||
|
||||
- (MWMCircularProgress *)progress
|
||||
{
|
||||
if (!_progress)
|
||||
{
|
||||
_progress = [MWMCircularProgress downloaderProgressForParentView:self.progressWrapper];
|
||||
_progress.delegate = self;
|
||||
}
|
||||
return _progress;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="jqI-GQ-yDh" customClass="MWMDownloadTransitMapAlert" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Hay-Qx-kVw" userLabel="ContainerView">
|
||||
<rect key="frame" x="47.5" y="218.5" width="280" height="230"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MvX-7q-CIH" userLabel="Title">
|
||||
<rect key="frame" x="20" y="20" width="240" height="29"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="29" id="018-Nz-384"/>
|
||||
<constraint firstAttribute="width" constant="240" id="cHd-lJ-pXe"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="18"/>
|
||||
<color key="textColor" red="0.12941176470588234" green="0.12941176470588234" blue="0.12941176470588234" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<size key="shadowOffset" width="0.0" height="0.0"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="routing_download_maps_along"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium18:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bco-zh-ekL">
|
||||
<rect key="frame" x="122" y="65" 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="0Pg-8w-QZa"/>
|
||||
<constraint firstAttribute="width" constant="36" id="cjU-dH-SAG"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Message" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Q3X-9G-3PT">
|
||||
<rect key="frame" x="23" y="61" width="234" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="234" id="YLG-PG-WHS"/>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="17" id="iOI-5t-pEY"/>
|
||||
</constraints>
|
||||
<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="localizedText" value="routing_requires_all_map"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" verticalHuggingPriority="1000" alwaysBounceVertical="YES" scrollEnabled="NO" style="grouped" separatorStyle="none" rowHeight="32" sectionHeaderHeight="44" sectionFooterHeight="1" translatesAutoresizingMaskIntoConstraints="NO" id="1lL-cj-2oS">
|
||||
<rect key="frame" x="0.0" y="98" width="280" height="88"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="88" id="92H-Th-Xgt"/>
|
||||
</constraints>
|
||||
<color key="separatorColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="sectionIndexBackgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="sectionIndexTrackingBackgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="jqI-GQ-yDh" id="8mP-kY-rEz"/>
|
||||
<outlet property="delegate" destination="jqI-GQ-yDh" id="9Fy-MW-IYa"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="2nQ-k3-Rx3" userLabel="hDivider">
|
||||
<rect key="frame" x="0.0" y="186" width="280" height="1"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="280" id="YGc-0s-8yD"/>
|
||||
<constraint firstAttribute="height" constant="1" id="l1s-jg-dng"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="q87-qQ-0rn">
|
||||
<rect key="frame" x="140" y="186" width="140" height="44"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="downloadNowButton"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="140" id="1Io-1t-Odn"/>
|
||||
<constraint firstAttribute="height" constant="44" id="CeP-fJ-qZs"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<state key="normal" title="right">
|
||||
<color key="titleColor" red="0.090196078430000007" green="0.61960784310000006" blue="0.30196078430000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="download"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="downloadButtonTap" destination="jqI-GQ-yDh" eventType="touchUpInside" id="wBd-0C-U51"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dhS-fg-rNl">
|
||||
<rect key="frame" x="0.0" y="186" width="140" height="44"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="notNowButton"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="7gw-Uu-UEK"/>
|
||||
<constraint firstAttribute="width" constant="140" id="EMA-LM-Zje"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="left">
|
||||
<color key="titleColor" red="0.090196078430000007" green="0.61960784310000006" blue="0.30196078430000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="later"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="cancelButtonTap" destination="jqI-GQ-yDh" eventType="touchUpInside" id="xii-qA-4BV"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Jjh-VP-emP" userLabel="vDivider">
|
||||
<rect key="frame" x="139" y="186" width="1" height="44"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="1" id="8kQ-Yd-0D3"/>
|
||||
<constraint firstAttribute="height" constant="44" id="vYd-td-NOl"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.88" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="dhS-fg-rNl" firstAttribute="top" secondItem="1lL-cj-2oS" secondAttribute="bottom" priority="500" id="2aT-au-ilL"/>
|
||||
<constraint firstAttribute="bottom" secondItem="dhS-fg-rNl" secondAttribute="bottom" id="2bS-ac-iDr"/>
|
||||
<constraint firstItem="bco-zh-ekL" firstAttribute="centerX" secondItem="Hay-Qx-kVw" secondAttribute="centerX" id="3xy-PQ-9lP"/>
|
||||
<constraint firstAttribute="bottom" secondItem="q87-qQ-0rn" secondAttribute="bottom" id="3yg-r8-Lm3"/>
|
||||
<constraint firstAttribute="bottom" secondItem="1lL-cj-2oS" secondAttribute="bottom" priority="250" id="58e-JY-U6g"/>
|
||||
<constraint firstAttribute="centerX" secondItem="2nQ-k3-Rx3" secondAttribute="centerX" id="5S3-Lh-pMR"/>
|
||||
<constraint firstAttribute="trailing" secondItem="q87-qQ-0rn" secondAttribute="trailing" id="Bud-qr-UTy"/>
|
||||
<constraint firstItem="bco-zh-ekL" firstAttribute="top" secondItem="MvX-7q-CIH" secondAttribute="bottom" constant="16" id="D9b-za-yb8"/>
|
||||
<constraint firstItem="q87-qQ-0rn" firstAttribute="top" secondItem="1lL-cj-2oS" secondAttribute="bottom" priority="500" id="Lpk-cS-FHG"/>
|
||||
<constraint firstItem="Q3X-9G-3PT" firstAttribute="top" secondItem="MvX-7q-CIH" secondAttribute="bottom" constant="12" id="PN3-Vl-yKe"/>
|
||||
<constraint firstAttribute="width" constant="280" id="PoR-0E-Yd5"/>
|
||||
<constraint firstItem="1lL-cj-2oS" firstAttribute="top" secondItem="bco-zh-ekL" secondAttribute="bottom" priority="250" constant="20" id="QQh-zT-bWE"/>
|
||||
<constraint firstAttribute="centerX" secondItem="1lL-cj-2oS" secondAttribute="centerX" id="Td0-Pa-b2s"/>
|
||||
<constraint firstAttribute="centerX" secondItem="MvX-7q-CIH" secondAttribute="centerX" id="Uwa-a8-9bd"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Jjh-VP-emP" secondAttribute="bottom" id="YL8-fj-ENd"/>
|
||||
<constraint firstAttribute="bottom" secondItem="2nQ-k3-Rx3" secondAttribute="bottom" constant="43" id="YPn-NL-TPQ"/>
|
||||
<constraint firstItem="q87-qQ-0rn" firstAttribute="leading" secondItem="Jjh-VP-emP" secondAttribute="trailing" id="hwW-Pw-fPz"/>
|
||||
<constraint firstItem="dhS-fg-rNl" firstAttribute="leading" secondItem="Hay-Qx-kVw" secondAttribute="leading" id="hzI-MA-Jal"/>
|
||||
<constraint firstItem="1lL-cj-2oS" firstAttribute="width" secondItem="Hay-Qx-kVw" secondAttribute="width" id="i3L-zR-0ay"/>
|
||||
<constraint firstAttribute="centerX" secondItem="Q3X-9G-3PT" secondAttribute="centerX" id="l5p-jN-Vvq"/>
|
||||
<constraint firstItem="MvX-7q-CIH" firstAttribute="top" secondItem="Hay-Qx-kVw" secondAttribute="top" constant="20" id="xBi-k5-Mze"/>
|
||||
<constraint firstItem="1lL-cj-2oS" firstAttribute="top" secondItem="Q3X-9G-3PT" secondAttribute="bottom" priority="500" constant="20" id="xQk-cL-cLn"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="AlertView"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="centerY" secondItem="Hay-Qx-kVw" secondAttribute="centerY" id="5Z9-NF-BEW"/>
|
||||
<constraint firstAttribute="centerX" secondItem="Hay-Qx-kVw" secondAttribute="centerX" id="e0c-Aj-Anh"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="ZL2-Bv-eBx"/>
|
||||
<connections>
|
||||
<outlet property="containerView" destination="Hay-Qx-kVw" id="YBK-zz-Ejj"/>
|
||||
<outlet property="dialogsBottomOffset" destination="58e-JY-U6g" id="D2s-m8-XQl"/>
|
||||
<outlet property="dialogsTableView" destination="1lL-cj-2oS" id="QPh-bX-GfH"/>
|
||||
<outlet property="hDivider" destination="2nQ-k3-Rx3" id="5BD-2K-Qcc"/>
|
||||
<outlet property="leftButton" destination="dhS-fg-rNl" id="t6u-5i-zwZ"/>
|
||||
<outlet property="messageLabel" destination="Q3X-9G-3PT" id="AUA-bn-mAy"/>
|
||||
<outlet property="progressWrapper" destination="bco-zh-ekL" id="AB4-SS-xCc"/>
|
||||
<outlet property="progressWrapperBottomOffset" destination="QQh-zT-bWE" id="vBy-LT-WOC"/>
|
||||
<outlet property="rightButton" destination="q87-qQ-0rn" id="91J-mu-sXT"/>
|
||||
<outlet property="tableViewHeight" destination="92H-Th-Xgt" id="qnm-KF-Hb7"/>
|
||||
<outlet property="titleLabel" destination="MvX-7q-CIH" id="hej-5l-48z"/>
|
||||
<outlet property="vDivider" destination="Jjh-VP-emP" id="bNj-9y-l3s"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="295" y="401"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="dialog_btn_press" width="280" height="44"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#import "MWMTableViewCell.h"
|
||||
|
||||
@interface MWMDownloaderDialogCell : MWMTableViewCell
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UILabel * titleLabel;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#import "MWMDownloaderDialogCell.h"
|
||||
|
||||
@implementation MWMDownloaderDialogCell
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="MWMDownloaderDialogCell" rowHeight="33" id="LWd-Zy-XGd" customClass="MWMDownloaderDialogCell" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="280" height="33"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="LWd-Zy-XGd" id="gf8-bS-tvq">
|
||||
<rect key="frame" x="0.0" y="0.0" width="280" height="33"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1B4-T1-g4X">
|
||||
<rect key="frame" x="20" y="8" width="240" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="17" id="u5A-6l-INO"/>
|
||||
</constraints>
|
||||
<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:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.90196078431372551" green="0.90196078431372551" blue="0.90196078431372551" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="centerX" secondItem="1B4-T1-g4X" secondAttribute="centerX" id="2AW-qe-sgg"/>
|
||||
<constraint firstAttribute="centerY" secondItem="1B4-T1-g4X" secondAttribute="centerY" id="TD9-6y-WM8"/>
|
||||
<constraint firstAttribute="trailing" secondItem="1B4-T1-g4X" secondAttribute="trailing" constant="20" id="ruG-JF-m2y"/>
|
||||
<constraint firstItem="1B4-T1-g4X" firstAttribute="leading" secondItem="gf8-bS-tvq" secondAttribute="leading" constant="20" id="sZo-pt-LJL"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="PressBackground"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="0.90196078431372551" green="0.90196078431372551" blue="0.90196078431372551" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<viewLayoutGuide key="safeArea" id="hHf-it-4Q9"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="PressBackground"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="titleLabel" destination="1B4-T1-g4X" id="m4n-H9-1Ta"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="235" y="180"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
@class MWMDownloadTransitMapAlert;
|
||||
|
||||
@interface MWMDownloaderDialogHeader : UIView
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIButton * headerButton;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView * expandImage;
|
||||
|
||||
+ (instancetype)headerForOwnerAlert:(MWMDownloadTransitMapAlert *)alert;
|
||||
- (void)layoutSizeLabel;
|
||||
|
||||
- (void)setTitle:(NSString *)title size:(NSString *)size;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
#import "MWMDownloaderDialogHeader.h"
|
||||
#import "MWMDownloadTransitMapAlert.h"
|
||||
|
||||
static NSString * const kDownloaderDialogHeaderNibName = @"MWMDownloaderDialogHeader";
|
||||
|
||||
@interface MWMDownloaderDialogHeader ()
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UILabel * title;
|
||||
@property (weak, nonatomic) IBOutlet UILabel * size;
|
||||
@property (weak, nonatomic) IBOutlet UIView * dividerView;
|
||||
@property (weak, nonatomic) MWMDownloadTransitMapAlert * ownerAlert;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint * sizeTrailing;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint * titleLeading;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMDownloaderDialogHeader
|
||||
|
||||
+ (instancetype)headerForOwnerAlert:(MWMDownloadTransitMapAlert *)alert
|
||||
{
|
||||
MWMDownloaderDialogHeader * header =
|
||||
[NSBundle.mainBundle loadNibNamed:kDownloaderDialogHeaderNibName owner:nil options:nil]
|
||||
.firstObject;
|
||||
header.ownerAlert = alert;
|
||||
return header;
|
||||
}
|
||||
|
||||
- (IBAction)headerButtonTap:(UIButton *)sender
|
||||
{
|
||||
BOOL const currentState = sender.selected;
|
||||
sender.selected = !currentState;
|
||||
self.dividerView.hidden = currentState;
|
||||
[UIView animateWithDuration:kDefaultAnimationDuration animations:^
|
||||
{
|
||||
self.expandImage.transform = sender.selected ? CGAffineTransformMakeRotation(M_PI) : CGAffineTransformIdentity;
|
||||
}];
|
||||
[self.ownerAlert showDownloadDetail:sender];
|
||||
}
|
||||
|
||||
- (void)layoutSizeLabel
|
||||
{
|
||||
if (self.expandImage.hidden)
|
||||
self.sizeTrailing.constant = self.titleLeading.constant;
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (void)setTitle:(NSString *)title size:(NSString *)size
|
||||
{
|
||||
self.title.text = title;
|
||||
self.size.text = size;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="MWMDownloaderDialogHeader" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="280" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hh9-2l-gu2">
|
||||
<rect key="frame" x="20" y="14" width="160" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="160" id="4WE-p5-RdF"/>
|
||||
<constraint firstAttribute="height" constant="16" id="FQE-Zk-gXr"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.0977349653840065" green="0.0977320596575737" blue="0.09773370623588562" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_arrow_gray_down" translatesAutoresizingMaskIntoConstraints="NO" id="eqn-NQ-zOk">
|
||||
<rect key="frame" x="248" y="8" width="28" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="28" id="CkX-aX-oaf"/>
|
||||
<constraint firstAttribute="height" constant="28" id="mLX-ow-5er"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="MWMGray"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</imageView>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tqG-fM-g6J">
|
||||
<rect key="frame" x="0.0" y="43" width="280" height="1"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="Vc3-uM-vHv"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="size" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="m8f-ps-pFV">
|
||||
<rect key="frame" x="180" y="14" width="64" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="16" id="7Hg-8z-e56"/>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="64" id="hct-Jz-zGl"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.0977349653840065" green="0.0977320596575737" blue="0.09773370623588562" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eIt-Yb-1Yl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="280" height="44"/>
|
||||
<state key="normal">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<connections>
|
||||
<action selector="headerButtonTap:" destination="iN0-l3-epB" eventType="touchUpInside" id="SW0-Mx-TVi"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.059999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="eIt-Yb-1Yl" secondAttribute="bottom" id="61x-d3-FtO"/>
|
||||
<constraint firstAttribute="trailing" secondItem="eqn-NQ-zOk" secondAttribute="trailing" constant="4" id="8Xd-cJ-b0r"/>
|
||||
<constraint firstAttribute="trailing" secondItem="tqG-fM-g6J" secondAttribute="trailing" id="9Xc-Au-fuY"/>
|
||||
<constraint firstItem="eqn-NQ-zOk" firstAttribute="centerY" secondItem="m8f-ps-pFV" secondAttribute="centerY" id="DOo-JP-5fj"/>
|
||||
<constraint firstAttribute="centerY" secondItem="m8f-ps-pFV" secondAttribute="centerY" id="F82-yF-6kJ"/>
|
||||
<constraint firstAttribute="centerY" secondItem="hh9-2l-gu2" secondAttribute="centerY" id="GYb-1Q-mMG"/>
|
||||
<constraint firstItem="eIt-Yb-1Yl" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="Jdx-uy-XaA"/>
|
||||
<constraint firstAttribute="trailing" secondItem="eIt-Yb-1Yl" secondAttribute="trailing" id="MSS-uK-Dby"/>
|
||||
<constraint firstItem="eIt-Yb-1Yl" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="Xxs-g9-ygX"/>
|
||||
<constraint firstItem="hh9-2l-gu2" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" id="ZfI-xz-HEx"/>
|
||||
<constraint firstAttribute="trailing" secondItem="m8f-ps-pFV" secondAttribute="trailing" constant="36" id="kwN-VG-nEr"/>
|
||||
<constraint firstItem="tqG-fM-g6J" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="rDI-mo-gnf"/>
|
||||
<constraint firstItem="tqG-fM-g6J" firstAttribute="top" secondItem="m8f-ps-pFV" secondAttribute="bottom" constant="13" id="v0U-Qb-rW1"/>
|
||||
</constraints>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<viewLayoutGuide key="safeArea" id="gAS-Hf-5F8"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="BlackOpaqueBackground"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="dividerView" destination="tqG-fM-g6J" id="eOr-3y-kbn"/>
|
||||
<outlet property="expandImage" destination="eqn-NQ-zOk" id="fGc-MN-ss7"/>
|
||||
<outlet property="headerButton" destination="eIt-Yb-1Yl" id="f0E-0A-6UG"/>
|
||||
<outlet property="size" destination="m8f-ps-pFV" id="5ax-Hb-xEm"/>
|
||||
<outlet property="sizeTrailing" destination="kwN-VG-nEr" id="K52-ZK-LuH"/>
|
||||
<outlet property="title" destination="hh9-2l-gu2" id="JNQ-ls-kgI"/>
|
||||
<outlet property="titleLeading" destination="ZfI-xz-HEx" id="Vzg-Cd-ztu"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="202" y="259"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="dialog_btn_press" width="280" height="44"/>
|
||||
<image name="ic_arrow_gray_down" width="28" height="28"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import UIKit
|
||||
|
||||
final class LocationServicesDisabledAlert: MWMAlert {
|
||||
|
||||
@objc
|
||||
class func alert() -> LocationServicesDisabledAlert? {
|
||||
guard let alert = Bundle.main.loadNibNamed("LocationServicesDisabledAlert", owner: nil)?.first as? LocationServicesDisabledAlert else {
|
||||
assertionFailure("Error: LocationServicesDisabledAlert failed lo load from nib.")
|
||||
return nil
|
||||
}
|
||||
alert.setNeedsCloseAfterEnterBackground()
|
||||
return alert
|
||||
}
|
||||
|
||||
@IBAction func okButtonDidTap(_ sender: Any) {
|
||||
close(nil)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<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"/>
|
||||
<view contentMode="scaleToFill" id="WR7-Hs-CVJ" customClass="LocationServicesDisabledAlert" customModule="CoMaps" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="539" height="770"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="W9l-ze-azg" userLabel="ContainerView">
|
||||
<rect key="frame" x="30" y="255" width="479" height="285"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lMb-vR-q3Y" userLabel="Title">
|
||||
<rect key="frame" x="20" y="15" width="439" height="22"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="22" id="MGn-Ke-8G8"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="18"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium18:blackPrimaryText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="location_services_disabled_header"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Message" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fz4-9d-5tq" userLabel="Message">
|
||||
<rect key="frame" x="20" y="47" width="439" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="jCV-Ko-ByO"/>
|
||||
</constraints>
|
||||
<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:blackSecondaryText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="location_services_disabled_message"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="1000" verticalHuggingPriority="1000" axis="vertical" distribution="equalSpacing" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="PjR-yG-eRZ">
|
||||
<rect key="frame" x="20" y="77" width="439" height="127"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" alignment="center" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="vWN-wX-veH" userLabel="OpenSettings">
|
||||
<rect key="frame" x="0.0" y="0.0" width="439" height="28"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_settings" translatesAutoresizingMaskIntoConstraints="NO" id="Ya1-ek-kDX" userLabel="settings">
|
||||
<rect key="frame" x="0.0" y="0.0" width="28" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="28" id="LKZ-GV-grJ"/>
|
||||
<constraint firstAttribute="width" constant="28" id="t73-v6-puf"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="1. Open Settings" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="PBp-x6-g2s" userLabel="openSettings">
|
||||
<rect key="frame" x="38" y="5.6666666666666856" width="401" height="17"/>
|
||||
<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="localizedText" value="location_services_disabled_1"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" alignment="center" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="q6r-CN-tgz" userLabel="SelectPrivacy">
|
||||
<rect key="frame" x="0.0" y="33" width="439" height="28"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_privacy" translatesAutoresizingMaskIntoConstraints="NO" id="7fg-Gm-P0f" userLabel="settings">
|
||||
<rect key="frame" x="0.0" y="0.0" width="28" height="28"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<color key="tintColor" systemColor="systemBlueColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="28" id="guV-Tr-7TT"/>
|
||||
<constraint firstAttribute="height" constant="28" id="srW-6x-82R"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="2. Select Privacy" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="973-fi-RfE" userLabel="openSettings">
|
||||
<rect key="frame" x="38" y="5.6666666666666856" width="401" height="17"/>
|
||||
<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="localizedText" value="location_services_disabled_on_device_2"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" alignment="center" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="W0F-rf-Edh" userLabel="SelectLocationServices">
|
||||
<rect key="frame" x="0.0" y="66" width="439" height="28"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_geoposition" translatesAutoresizingMaskIntoConstraints="NO" id="jzE-sK-mEB" userLabel="geoposition">
|
||||
<rect key="frame" x="0.0" y="0.0" width="28" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="28" id="HQ9-9t-v50"/>
|
||||
<constraint firstAttribute="height" constant="28" id="dY9-Jf-hIB"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="3. Select Location Services" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hhw-jx-9Fe" userLabel="geoposition">
|
||||
<rect key="frame" x="38" y="5.6666666666666856" width="401" height="17"/>
|
||||
<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="localizedText" value="location_services_disabled_on_device_3"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" alignment="center" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="pf7-7u-B0E" userLabel="TurnOnLocationServices">
|
||||
<rect key="frame" x="0.0" y="99" width="439" height="28"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_swither" translatesAutoresizingMaskIntoConstraints="NO" id="UcF-g2-Faq" userLabel="turnon">
|
||||
<rect key="frame" x="0.0" y="0.0" width="28" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="28" id="GKu-yv-3JW"/>
|
||||
<constraint firstAttribute="height" constant="28" id="lNW-jN-VJf"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="4. Turn On Location Services" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Q1u-dB-Esr" userLabel="select "Always"">
|
||||
<rect key="frame" x="38" y="5.6666666666666856" width="401" height="17"/>
|
||||
<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="localizedText" value="location_services_disabled_on_device_4"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Additional message" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fc4-12-xx4" userLabel="Additional message">
|
||||
<rect key="frame" x="20" y="214" width="439" height="17"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.54000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="location_services_disabled_on_device_additional_message"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3eJ-wI-Y11" userLabel="hDivider">
|
||||
<rect key="frame" x="0.0" y="241" width="479" height="1"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="wiR-YE-iVH"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="WVU-qc-efT" userLabel="okButton">
|
||||
<rect key="frame" x="0.0" y="241" width="479" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="0vj-j6-oga"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="ok">
|
||||
<color key="titleColor" red="0.090196078430000007" green="0.61960784310000006" blue="0.30196078430000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="ok"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="okButtonDidTap:" destination="WR7-Hs-CVJ" eventType="touchUpInside" id="2Th-Md-oau"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.88" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="450" id="2jy-XF-Yc6"/>
|
||||
<constraint firstItem="PjR-yG-eRZ" firstAttribute="bottom" secondItem="fc4-12-xx4" secondAttribute="top" constant="-10" id="Bda-Vu-S3B"/>
|
||||
<constraint firstItem="3eJ-wI-Y11" firstAttribute="top" secondItem="WVU-qc-efT" secondAttribute="top" id="DWH-KY-Gih"/>
|
||||
<constraint firstAttribute="width" secondItem="3eJ-wI-Y11" secondAttribute="width" id="Dqh-aw-vv1"/>
|
||||
<constraint firstAttribute="bottom" secondItem="WVU-qc-efT" secondAttribute="bottom" id="Hbe-e3-xsI"/>
|
||||
<constraint firstItem="lMb-vR-q3Y" firstAttribute="leading" secondItem="W9l-ze-azg" secondAttribute="leading" constant="20" id="KLk-P2-6HX"/>
|
||||
<constraint firstItem="fc4-12-xx4" firstAttribute="bottom" secondItem="WVU-qc-efT" secondAttribute="top" constant="-10" id="MTa-xK-xfb"/>
|
||||
<constraint firstItem="lMb-vR-q3Y" firstAttribute="top" secondItem="W9l-ze-azg" secondAttribute="top" constant="15" id="NWh-CB-SSg"/>
|
||||
<constraint firstAttribute="trailing" secondItem="fc4-12-xx4" secondAttribute="trailing" constant="20" id="Noy-do-AD5"/>
|
||||
<constraint firstItem="WVU-qc-efT" firstAttribute="leading" secondItem="W9l-ze-azg" secondAttribute="leading" id="P4b-ex-cIo"/>
|
||||
<constraint firstItem="fc4-12-xx4" firstAttribute="leading" secondItem="W9l-ze-azg" secondAttribute="leading" constant="20" id="UWg-4r-gTh"/>
|
||||
<constraint firstItem="PjR-yG-eRZ" firstAttribute="leading" secondItem="W9l-ze-azg" secondAttribute="leading" constant="20" id="Xig-e5-dMF"/>
|
||||
<constraint firstAttribute="trailing" secondItem="lMb-vR-q3Y" secondAttribute="trailing" constant="20" id="ZjY-OS-5Ar"/>
|
||||
<constraint firstItem="fz4-9d-5tq" firstAttribute="top" secondItem="lMb-vR-q3Y" secondAttribute="bottom" constant="10" id="alo-B9-UKo"/>
|
||||
<constraint firstItem="PjR-yG-eRZ" firstAttribute="top" secondItem="fz4-9d-5tq" secondAttribute="bottom" constant="10" id="ceU-Nn-UvW"/>
|
||||
<constraint firstAttribute="trailing" secondItem="PjR-yG-eRZ" secondAttribute="trailing" constant="20" id="rb5-li-7z9"/>
|
||||
<constraint firstAttribute="centerX" secondItem="fz4-9d-5tq" secondAttribute="centerX" id="t2L-hx-Wvu"/>
|
||||
<constraint firstAttribute="trailing" secondItem="WVU-qc-efT" secondAttribute="trailing" id="uf8-cg-WgD"/>
|
||||
<constraint firstItem="fz4-9d-5tq" firstAttribute="leading" secondItem="W9l-ze-azg" secondAttribute="leading" constant="20" id="v2M-33-BDA"/>
|
||||
<constraint firstAttribute="centerX" secondItem="3eJ-wI-Y11" secondAttribute="centerX" id="wRK-vV-cs9"/>
|
||||
<constraint firstAttribute="trailing" secondItem="fz4-9d-5tq" secondAttribute="trailing" constant="20" id="wz1-Ok-gIi"/>
|
||||
<constraint firstAttribute="centerX" secondItem="lMb-vR-q3Y" secondAttribute="centerX" id="yjM-dk-hoP"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="AlertView"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<variation key="default">
|
||||
<mask key="constraints">
|
||||
<exclude reference="2jy-XF-Yc6"/>
|
||||
</mask>
|
||||
</variation>
|
||||
<variation key="heightClass=regular-widthClass=regular">
|
||||
<mask key="constraints">
|
||||
<include reference="2jy-XF-Yc6"/>
|
||||
</mask>
|
||||
</variation>
|
||||
</view>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="nuQ-1f-mMT"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="W9l-ze-azg" firstAttribute="centerY" secondItem="nuQ-1f-mMT" secondAttribute="centerY" id="1Le-Bj-m03"/>
|
||||
<constraint firstItem="nuQ-1f-mMT" firstAttribute="trailing" secondItem="W9l-ze-azg" secondAttribute="trailing" constant="30" id="1wd-E9-kcQ"/>
|
||||
<constraint firstItem="nuQ-1f-mMT" firstAttribute="bottom" relation="lessThanOrEqual" secondItem="W9l-ze-azg" secondAttribute="bottom" constant="30" id="9vB-UV-vTP"/>
|
||||
<constraint firstItem="W9l-ze-azg" firstAttribute="leading" secondItem="nuQ-1f-mMT" secondAttribute="leading" constant="30" id="Bvm-sK-Baz"/>
|
||||
<constraint firstItem="W9l-ze-azg" firstAttribute="top" relation="greaterThanOrEqual" secondItem="nuQ-1f-mMT" secondAttribute="top" constant="30" id="Eg7-7g-IAE"/>
|
||||
<constraint firstItem="nuQ-1f-mMT" firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="W9l-ze-azg" secondAttribute="bottom" constant="30" id="qIv-d3-sJM"/>
|
||||
<constraint firstItem="W9l-ze-azg" firstAttribute="top" relation="lessThanOrEqual" secondItem="nuQ-1f-mMT" secondAttribute="top" constant="30" id="zs3-3Q-gZU"/>
|
||||
</constraints>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<variation key="default">
|
||||
<mask key="constraints">
|
||||
<exclude reference="1wd-E9-kcQ"/>
|
||||
<exclude reference="9vB-UV-vTP"/>
|
||||
<exclude reference="Bvm-sK-Baz"/>
|
||||
<exclude reference="zs3-3Q-gZU"/>
|
||||
</mask>
|
||||
</variation>
|
||||
<variation key="widthClass=compact">
|
||||
<mask key="constraints">
|
||||
<include reference="1wd-E9-kcQ"/>
|
||||
<include reference="Bvm-sK-Baz"/>
|
||||
</mask>
|
||||
</variation>
|
||||
<variation key="widthClass=regular">
|
||||
<mask key="constraints">
|
||||
<include reference="9vB-UV-vTP"/>
|
||||
<include reference="zs3-3Q-gZU"/>
|
||||
</mask>
|
||||
</variation>
|
||||
<point key="canvasLocation" x="115.26717557251908" y="-30.281690140845072"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="dialog_btn_press" width="280" height="44"/>
|
||||
<image name="ic_geoposition" width="28" height="28"/>
|
||||
<image name="ic_privacy" width="17" height="17"/>
|
||||
<image name="ic_settings" width="28" height="28"/>
|
||||
<image name="ic_swither" width="30" height="29"/>
|
||||
<systemColor name="systemBlueColor">
|
||||
<color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#import "MWMAlert.h"
|
||||
|
||||
@interface MWMLocationAlert : MWMAlert
|
||||
|
||||
+ (instancetype)alertWithCancelBlock:(MWMVoidBlock)cancelBlock;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#import "MWMLocationAlert.h"
|
||||
#import "MWMAlertViewController.h"
|
||||
|
||||
static NSString * const kLocationAlertNibName = @"MWMLocationAlert";
|
||||
|
||||
@interface MWMLocationAlert ()
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIButton * rightButton;
|
||||
@property (nullable, nonatomic) MWMVoidBlock cancelBlock;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMLocationAlert
|
||||
|
||||
+ (instancetype)alertWithCancelBlock:(MWMVoidBlock)cancelBlock
|
||||
{
|
||||
MWMLocationAlert * alert =
|
||||
[NSBundle.mainBundle loadNibNamed:kLocationAlertNibName owner:nil options:nil].firstObject;
|
||||
[alert setNeedsCloseAlertAfterEnterBackground];
|
||||
alert.cancelBlock = cancelBlock;
|
||||
return alert;
|
||||
}
|
||||
|
||||
- (IBAction)settingsTap
|
||||
{
|
||||
[self close:^{
|
||||
NSURL * url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
|
||||
UIApplication * a = UIApplication.sharedApplication;
|
||||
if ([a canOpenURL:url])
|
||||
[a openURL:url options:@{} completionHandler:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)closeTap
|
||||
{
|
||||
[self close:self.cancelBlock];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="mnY-7K-ids" customClass="MWMLocationAlert" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8dg-2G-Gkm" userLabel="ContainerView">
|
||||
<rect key="frame" x="47.5" y="198.5" width="280" height="291.5"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="D5k-he-a1F" userLabel="Title">
|
||||
<rect key="frame" x="20" y="20" width="240" height="22"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="240" id="gGL-U8-TMu"/>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="22" id="h5g-o2-ynw"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="18"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="location_services_disabled_header"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium18:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Message" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0fV-Pc-ec6" userLabel="Message">
|
||||
<rect key="frame" x="20" y="53.5" width="240" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="240" id="bjX-A8-pbd"/>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="kX0-pg-KbR"/>
|
||||
</constraints>
|
||||
<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="localizedText" value="location_services_disabled_message"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oy8-sO-eim" userLabel="hDivider">
|
||||
<rect key="frame" x="0.0" y="247.5" width="280" height="1"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="v6N-0M-ODb"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="opl-lk-5kD" userLabel="left">
|
||||
<rect key="frame" x="0.0" y="247.5" width="140" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="140" id="Ant-We-sLi"/>
|
||||
<constraint firstAttribute="height" constant="44" id="eOg-pv-nif"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="left">
|
||||
<color key="titleColor" red="0.090196078430000007" green="0.61960784310000006" blue="0.30196078430000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="cancel"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="closeTap" destination="mnY-7K-ids" eventType="touchUpInside" id="X82-1U-rec"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1Ho-Pk-bDe" userLabel="right">
|
||||
<rect key="frame" x="140" y="247.5" width="140" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="140" id="5wg-5d-hPp"/>
|
||||
<constraint firstAttribute="height" constant="44" id="Mdl-Ix-DGD"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<state key="normal" title="right">
|
||||
<color key="titleColor" red="0.090196078430000007" green="0.61960784310000006" blue="0.30196078430000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="settings"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="settingsTap" destination="mnY-7K-ids" eventType="touchUpInside" id="y48-t3-n6f"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oO4-NM-mFk" userLabel="vDivider">
|
||||
<rect key="frame" x="140" y="247.5" width="1" height="44"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="1" id="mcR-0M-Ul0"/>
|
||||
<constraint firstAttribute="height" constant="44" id="zL6-eK-WfS"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_settings" translatesAutoresizingMaskIntoConstraints="NO" id="icc-OP-m6N" userLabel="settings">
|
||||
<rect key="frame" x="20" y="101.5" width="28" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="28" id="Ush-HH-2TA"/>
|
||||
<constraint firstAttribute="width" constant="28" id="r2M-fp-8z4"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_geoposition" translatesAutoresizingMaskIntoConstraints="NO" id="S4o-n9-iX5" userLabel="geoposition">
|
||||
<rect key="frame" x="20" y="145.5" width="28" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="28" id="Gdg-65-WkZ"/>
|
||||
<constraint firstAttribute="width" constant="28" id="Wf3-ar-r44"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_select" translatesAutoresizingMaskIntoConstraints="NO" id="4hl-PR-x4M" userLabel="turnon">
|
||||
<rect key="frame" x="20" y="189.5" width="28" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="28" id="A5h-wL-BHj"/>
|
||||
<constraint firstAttribute="width" constant="28" id="mOL-nA-Afc"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="1. Откройте Настройки" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EHh-Zz-Us5" userLabel="openSettings">
|
||||
<rect key="frame" x="56" y="105.5" width="204" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="204" id="4JO-6s-aaI"/>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="jD6-K3-YYh"/>
|
||||
</constraints>
|
||||
<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="localizedText" value="location_services_disabled_1"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="2. Нажмите Геопозиция" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="stQ-KI-WeP" userLabel="openSettings">
|
||||
<rect key="frame" x="56" y="149.5" width="204" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="AAw-Ri-mbT"/>
|
||||
<constraint firstAttribute="width" constant="204" id="cNm-sH-jni"/>
|
||||
</constraints>
|
||||
<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="localizedText" value="location_services_disabled_2"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="3. Выберите "Всегда"" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eiz-Ie-23H" userLabel="select "Always"">
|
||||
<rect key="frame" x="56" y="193.5" width="204" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="bIH-9x-S0s"/>
|
||||
<constraint firstAttribute="width" constant="204" id="qBs-0m-j1B"/>
|
||||
</constraints>
|
||||
<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="localizedText" value="location_services_disabled_3"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.88" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="opl-lk-5kD" secondAttribute="bottom" id="0Mz-4Z-230"/>
|
||||
<constraint firstAttribute="width" secondItem="oy8-sO-eim" secondAttribute="width" id="3MR-Nx-zJb"/>
|
||||
<constraint firstItem="EHh-Zz-Us5" firstAttribute="centerY" secondItem="icc-OP-m6N" secondAttribute="centerY" id="3SF-wh-w9N"/>
|
||||
<constraint firstItem="eiz-Ie-23H" firstAttribute="leading" secondItem="4hl-PR-x4M" secondAttribute="trailing" constant="8" id="4DN-XY-UYe"/>
|
||||
<constraint firstItem="EHh-Zz-Us5" firstAttribute="leading" secondItem="icc-OP-m6N" secondAttribute="trailing" constant="8" id="4EP-kd-cxV"/>
|
||||
<constraint firstItem="opl-lk-5kD" firstAttribute="top" secondItem="eiz-Ie-23H" secondAttribute="bottom" constant="34" id="6RO-Fl-s2K"/>
|
||||
<constraint firstItem="stQ-KI-WeP" firstAttribute="centerY" secondItem="S4o-n9-iX5" secondAttribute="centerY" id="9bR-F2-lU7"/>
|
||||
<constraint firstItem="icc-OP-m6N" firstAttribute="top" secondItem="0fV-Pc-ec6" secondAttribute="bottom" constant="28" id="C8m-mW-KEF"/>
|
||||
<constraint firstAttribute="trailing" secondItem="1Ho-Pk-bDe" secondAttribute="trailing" id="CTo-Fd-m0E"/>
|
||||
<constraint firstAttribute="width" constant="280" id="Fif-Pa-YZH"/>
|
||||
<constraint firstAttribute="centerX" secondItem="oy8-sO-eim" secondAttribute="centerX" id="Gbp-Y5-rsD"/>
|
||||
<constraint firstItem="oy8-sO-eim" firstAttribute="top" secondItem="eiz-Ie-23H" secondAttribute="bottom" constant="34" id="JJw-71-2in"/>
|
||||
<constraint firstItem="0fV-Pc-ec6" firstAttribute="top" secondItem="D5k-he-a1F" secondAttribute="bottom" constant="11.5" id="Jth-x6-IhP"/>
|
||||
<constraint firstItem="1Ho-Pk-bDe" firstAttribute="top" secondItem="eiz-Ie-23H" secondAttribute="bottom" constant="34" id="LUY-3a-Inq"/>
|
||||
<constraint firstAttribute="bottom" secondItem="1Ho-Pk-bDe" secondAttribute="bottom" id="NE1-oz-CLF"/>
|
||||
<constraint firstItem="4hl-PR-x4M" firstAttribute="leading" secondItem="8dg-2G-Gkm" secondAttribute="leading" constant="20" id="Q7v-Re-gje"/>
|
||||
<constraint firstAttribute="centerX" secondItem="D5k-he-a1F" secondAttribute="centerX" id="XX2-IK-R6K"/>
|
||||
<constraint firstItem="D5k-he-a1F" firstAttribute="top" secondItem="8dg-2G-Gkm" secondAttribute="top" constant="20" id="Z54-gN-f1U"/>
|
||||
<constraint firstAttribute="bottom" secondItem="oO4-NM-mFk" secondAttribute="bottom" id="aNg-ua-Xvg"/>
|
||||
<constraint firstItem="icc-OP-m6N" firstAttribute="leading" secondItem="8dg-2G-Gkm" secondAttribute="leading" constant="20" id="cIX-d5-lZJ"/>
|
||||
<constraint firstAttribute="centerX" secondItem="0fV-Pc-ec6" secondAttribute="centerX" id="lTc-yR-XqA"/>
|
||||
<constraint firstItem="eiz-Ie-23H" firstAttribute="centerY" secondItem="4hl-PR-x4M" secondAttribute="centerY" id="n0S-Sb-bFh"/>
|
||||
<constraint firstItem="4hl-PR-x4M" firstAttribute="top" secondItem="stQ-KI-WeP" secondAttribute="bottom" constant="20" id="nov-7z-XFm"/>
|
||||
<constraint firstItem="S4o-n9-iX5" firstAttribute="top" secondItem="EHh-Zz-Us5" secondAttribute="bottom" constant="20" id="qqy-xa-ojy"/>
|
||||
<constraint firstItem="S4o-n9-iX5" firstAttribute="leading" secondItem="8dg-2G-Gkm" secondAttribute="leading" constant="20" id="stf-om-BNZ"/>
|
||||
<constraint firstItem="stQ-KI-WeP" firstAttribute="leading" secondItem="S4o-n9-iX5" secondAttribute="trailing" constant="8" id="uyt-fW-FYM"/>
|
||||
<constraint firstItem="oO4-NM-mFk" firstAttribute="leading" secondItem="opl-lk-5kD" secondAttribute="trailing" id="z1h-Dd-0AD"/>
|
||||
<constraint firstItem="opl-lk-5kD" firstAttribute="leading" secondItem="8dg-2G-Gkm" secondAttribute="leading" id="z9J-7w-u9C"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="AlertView"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="centerY" secondItem="8dg-2G-Gkm" secondAttribute="centerY" constant="-10.5" id="W3z-HA-6ee"/>
|
||||
<constraint firstAttribute="centerX" secondItem="8dg-2G-Gkm" secondAttribute="centerX" id="t4o-pT-rEC"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="k9W-qh-TkJ"/>
|
||||
<connections>
|
||||
<outlet property="rightButton" destination="1Ho-Pk-bDe" id="xO5-oe-a3h"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="305" y="172"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="dialog_btn_press" width="280" height="44"/>
|
||||
<image name="ic_geoposition" width="28" height="28"/>
|
||||
<image name="ic_select" width="28" height="28"/>
|
||||
<image name="ic_settings" width="28" height="28"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#import "MWMAlert.h"
|
||||
|
||||
@interface MWMPlaceDoesntExistAlert : MWMAlert
|
||||
|
||||
+ (instancetype)alertWithBlock:(MWMStringBlock)block;
|
||||
|
||||
@end
|
||||
46
iphone/Maps/Classes/CustomAlert/MWMPlaceDoesntExistAlert.m
Normal file
46
iphone/Maps/Classes/CustomAlert/MWMPlaceDoesntExistAlert.m
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#import "MWMPlaceDoesntExistAlert.h"
|
||||
#import "MWMKeyboard.h"
|
||||
|
||||
@interface MWMPlaceDoesntExistAlert ()<MWMKeyboardObserver>
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UITextField * textField;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * centerHorizontaly;
|
||||
@property(copy, nonatomic) MWMStringBlock block;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMPlaceDoesntExistAlert
|
||||
|
||||
+ (instancetype)alertWithBlock:(MWMStringBlock)block
|
||||
{
|
||||
MWMPlaceDoesntExistAlert * alert =
|
||||
[NSBundle.mainBundle loadNibNamed:[self className] owner:nil options:nil].firstObject;
|
||||
alert.block = block;
|
||||
[MWMKeyboard addObserver:alert];
|
||||
return alert;
|
||||
}
|
||||
|
||||
- (IBAction)rightButtonTap
|
||||
{
|
||||
[self.textField resignFirstResponder];
|
||||
[self close:^{
|
||||
self.block(self.textField.text);
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)leftButtonTap
|
||||
{
|
||||
[self.textField resignFirstResponder];
|
||||
[self close:nil];
|
||||
}
|
||||
|
||||
#pragma mark - MWMKeyboard
|
||||
|
||||
- (void)onKeyboardAnimation
|
||||
{
|
||||
self.centerHorizontaly.constant = -[MWMKeyboard keyboardHeight] / 2;
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (void)onKeyboardWillAnimate { [self setNeedsLayout]; }
|
||||
@end
|
||||
181
iphone/Maps/Classes/CustomAlert/MWMPlaceDoesntExistAlert.xib
Normal file
181
iphone/Maps/Classes/CustomAlert/MWMPlaceDoesntExistAlert.xib
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
<?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="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="bh1-8l-voy" customClass="MWMPlaceDoesntExistAlert">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XjH-qf-m6h" userLabel="ContainerView">
|
||||
<rect key="frame" x="47.5" y="250.5" width="280" height="166"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="NwN-e5-YQR" userLabel="Title">
|
||||
<rect key="frame" x="20" y="20" width="240" height="22"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="240" id="Bz8-HZ-yfK"/>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="22" id="JWn-Ze-jrX"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="18"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium18:blackPrimaryText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="editor_place_doesnt_exist"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Message" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VvH-Bb-fV9" userLabel="Message">
|
||||
<rect key="frame" x="20" y="54" width="240" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="240" id="Gau-b5-VxP"/>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="VWx-20-JCg"/>
|
||||
</constraints>
|
||||
<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:blackSecondaryText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="editor_place_doesnt_exist_description"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="npf-dH-f50">
|
||||
<rect key="frame" x="16" y="86" width="248" height="24"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="0f8-vD-nJT">
|
||||
<rect key="frame" x="4" y="0.0" width="240" height="24"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="240" id="uql-Jo-4VK"/>
|
||||
</constraints>
|
||||
<edgeInsets key="layoutMargins" top="4" left="4" bottom="4" right="4"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="AlertViewTextField"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedPlaceholder" value="editor_comment_hint"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</textField>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="0f8-vD-nJT" firstAttribute="centerX" secondItem="npf-dH-f50" secondAttribute="centerX" id="Pm7-RK-HAq"/>
|
||||
<constraint firstItem="0f8-vD-nJT" firstAttribute="centerY" secondItem="npf-dH-f50" secondAttribute="centerY" id="T6e-qA-hJ1"/>
|
||||
<constraint firstAttribute="height" constant="24" id="ZAd-qX-QyG"/>
|
||||
<constraint firstAttribute="width" constant="248" id="x63-Bq-F0w"/>
|
||||
<constraint firstItem="0f8-vD-nJT" firstAttribute="height" secondItem="npf-dH-f50" secondAttribute="height" id="zHi-xB-aCQ"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="AlertViewTextFieldContainer"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kEx-DM-ynC" userLabel="hDivider">
|
||||
<rect key="frame" x="0.0" y="122" width="280" height="1"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="JKX-3f-YE4"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="zaM-0A-71d" userLabel="left">
|
||||
<rect key="frame" x="0.0" y="122" width="140" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="JRt-9h-Ljb"/>
|
||||
<constraint firstAttribute="width" constant="140" id="y86-N4-bGg"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="left">
|
||||
<color key="titleColor" red="0.090196078430000007" green="0.61960784310000006" blue="0.30196078430000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="cancel"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="leftButtonTap" destination="bh1-8l-voy" eventType="touchUpInside" id="Ge7-Dx-Pf3"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="MkK-sG-n6m" userLabel="right">
|
||||
<rect key="frame" x="140" y="122" width="140" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="2ra-bP-J3a"/>
|
||||
<constraint firstAttribute="width" constant="140" id="pNb-5U-J8Z"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<state key="normal" title="right">
|
||||
<color key="titleColor" red="0.090196078430000007" green="0.61960784310000006" blue="0.30196078430000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="editor_report_problem_send_button"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="rightButtonTap" destination="bh1-8l-voy" eventType="touchUpInside" id="4FZ-zi-TAB"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Tyn-yU-dk3" userLabel="vDivider">
|
||||
<rect key="frame" x="139.5" y="122" width="1" height="44"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="1" id="Bva-2f-zNE"/>
|
||||
<constraint firstAttribute="height" constant="44" id="jEO-cN-3PZ"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="MkK-sG-n6m" secondAttribute="trailing" id="21Y-zv-8JR"/>
|
||||
<constraint firstItem="NwN-e5-YQR" firstAttribute="top" secondItem="XjH-qf-m6h" secondAttribute="top" constant="20" id="6pQ-wv-c4Z"/>
|
||||
<constraint firstItem="VvH-Bb-fV9" firstAttribute="top" secondItem="NwN-e5-YQR" secondAttribute="bottom" constant="12" id="EAW-Ft-SfJ"/>
|
||||
<constraint firstAttribute="bottom" secondItem="MkK-sG-n6m" secondAttribute="bottom" id="K0E-y1-z0P"/>
|
||||
<constraint firstAttribute="bottom" secondItem="zaM-0A-71d" secondAttribute="bottom" id="KzG-74-cDe"/>
|
||||
<constraint firstItem="kEx-DM-ynC" firstAttribute="width" secondItem="XjH-qf-m6h" secondAttribute="width" id="Ldw-GJ-Ulm"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Tyn-yU-dk3" secondAttribute="bottom" id="Mb3-VE-bBR"/>
|
||||
<constraint firstItem="Tyn-yU-dk3" firstAttribute="centerX" secondItem="XjH-qf-m6h" secondAttribute="centerX" id="PkD-5c-ltY"/>
|
||||
<constraint firstItem="zaM-0A-71d" firstAttribute="leading" secondItem="XjH-qf-m6h" secondAttribute="leading" id="RCZ-fs-HwM"/>
|
||||
<constraint firstItem="kEx-DM-ynC" firstAttribute="top" secondItem="npf-dH-f50" secondAttribute="bottom" constant="12" id="TD5-DZ-mt2"/>
|
||||
<constraint firstItem="NwN-e5-YQR" firstAttribute="centerX" secondItem="XjH-qf-m6h" secondAttribute="centerX" id="ZbO-Kn-lsV"/>
|
||||
<constraint firstItem="npf-dH-f50" firstAttribute="top" secondItem="VvH-Bb-fV9" secondAttribute="bottom" constant="12" id="apH-SR-1Ir"/>
|
||||
<constraint firstItem="zaM-0A-71d" firstAttribute="top" secondItem="kEx-DM-ynC" secondAttribute="bottom" constant="-1" id="cPx-SM-J1B"/>
|
||||
<constraint firstItem="VvH-Bb-fV9" firstAttribute="centerX" secondItem="XjH-qf-m6h" secondAttribute="centerX" id="mPb-7n-Ns1"/>
|
||||
<constraint firstItem="npf-dH-f50" firstAttribute="centerX" secondItem="XjH-qf-m6h" secondAttribute="centerX" id="nfD-rk-FZo"/>
|
||||
<constraint firstAttribute="width" constant="280" id="uve-xM-xfj"/>
|
||||
<constraint firstItem="kEx-DM-ynC" firstAttribute="centerX" secondItem="XjH-qf-m6h" secondAttribute="centerX" id="vop-cl-2u7"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="AlertView"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="XjH-qf-m6h" firstAttribute="centerX" secondItem="bh1-8l-voy" secondAttribute="centerX" id="Ez4-IC-X2C"/>
|
||||
<constraint firstItem="XjH-qf-m6h" firstAttribute="centerY" secondItem="bh1-8l-voy" secondAttribute="centerY" id="ihl-fu-ac9"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="4Cb-Xf-v8p"/>
|
||||
<connections>
|
||||
<outlet property="centerHorizontaly" destination="ihl-fu-ac9" id="f11-9c-PMh"/>
|
||||
<outlet property="textField" destination="0f8-vD-nJT" id="tm8-Pb-b1x"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="305" y="172"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="dialog_btn_press" width="280" height="44"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#import "MWMAlert.h"
|
||||
|
||||
typedef NS_ENUM(NSInteger, MWMMobileInternetAlertResult) {
|
||||
MWMMobileInternetAlertResultAlways,
|
||||
MWMMobileInternetAlertResultToday,
|
||||
MWMMobileInternetAlertResultNotToday
|
||||
};
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void(^MWMMobileInternetAlertCompletionBlock)(MWMMobileInternetAlertResult result);
|
||||
|
||||
@interface MWMMobileInternetAlert : MWMAlert
|
||||
|
||||
+ (instancetype)alertWithBlock:(MWMMobileInternetAlertCompletionBlock)block;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#import "MWMMobileInternetAlert.h"
|
||||
|
||||
@interface MWMMobileInternetAlert ()
|
||||
|
||||
@property(copy, nonatomic) MWMMobileInternetAlertCompletionBlock completionBlock;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMMobileInternetAlert
|
||||
|
||||
+ (instancetype)alertWithBlock:(MWMMobileInternetAlertCompletionBlock)block;
|
||||
{
|
||||
MWMMobileInternetAlert * alert =
|
||||
[NSBundle.mainBundle loadNibNamed:[self className] owner:nil options:nil].firstObject;
|
||||
alert.completionBlock = block;
|
||||
return alert;
|
||||
}
|
||||
|
||||
- (IBAction)alwaysTap
|
||||
{
|
||||
[self close:^{
|
||||
self.completionBlock(MWMMobileInternetAlertResultAlways);
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)askTap
|
||||
{
|
||||
[self close:^{
|
||||
self.completionBlock(MWMMobileInternetAlertResultToday);
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)neverTap
|
||||
{
|
||||
[self close:^{
|
||||
self.completionBlock(MWMMobileInternetAlertResultNotToday);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="Tlh-TS-tkk" customClass="MWMMobileInternetAlert" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Tx8-cY-Dyg" userLabel="ContainerView">
|
||||
<rect key="frame" x="47.5" y="219.5" width="280" height="228.5"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="REz-A0-Vg9" userLabel="Title">
|
||||
<rect key="frame" x="20" y="20" width="240" height="21.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="240" id="QEO-gJ-c3y"/>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="RsI-at-W6p"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="18"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium18:blackPrimaryText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="mobile_data"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Message" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fGq-0K-ugB" userLabel="Message">
|
||||
<rect key="frame" x="20" y="53.5" width="240" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="F3h-lQ-gSS"/>
|
||||
<constraint firstAttribute="width" constant="240" id="XgA-ox-stk"/>
|
||||
</constraints>
|
||||
<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:blackSecondaryText"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="mobile_data_dialog"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0vG-Ty-4hj" userLabel="button1">
|
||||
<rect key="frame" x="0.0" y="94.5" width="280" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="RyA-xn-gAo"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="Использовать всегда">
|
||||
<color key="titleColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="mobile_data_option_always"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="bold17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="alwaysTap" destination="Tlh-TS-tkk" eventType="touchUpInside" id="fuJ-lY-I2V"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="d5L-hE-z56" userLabel="button2">
|
||||
<rect key="frame" x="0.0" y="139.5" width="280" height="44"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="Спрашивать">
|
||||
<color key="titleColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="mobile_data_option_today"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="askTap" destination="Tlh-TS-tkk" eventType="touchUpInside" id="brA-aR-BSr"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Xff-AT-bjO" userLabel="button3">
|
||||
<rect key="frame" x="0.0" y="184.5" width="280" height="44"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="Никогда не использовать">
|
||||
<color key="titleColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="mobile_data_option_not_today"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="neverTap" destination="Tlh-TS-tkk" eventType="touchUpInside" id="wMh-1n-svo"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="VZe-Gu-rIx" userLabel="hDivider1">
|
||||
<rect key="frame" x="0.0" y="93.5" width="280" height="1"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="J0l-nh-ng5"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="pNk-h2-LSK" userLabel="hDivider2">
|
||||
<rect key="frame" x="0.0" y="138.5" width="280" height="1"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ZZ7-CE-a8J" userLabel="hDivider3">
|
||||
<rect key="frame" x="0.0" y="183.5" width="280" height="1"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.88" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="d5L-hE-z56" firstAttribute="leading" secondItem="0vG-Ty-4hj" secondAttribute="leading" id="0nb-Mk-riG"/>
|
||||
<constraint firstAttribute="centerX" secondItem="REz-A0-Vg9" secondAttribute="centerX" id="1H6-10-IWz"/>
|
||||
<constraint firstItem="VZe-Gu-rIx" firstAttribute="top" secondItem="fGq-0K-ugB" secondAttribute="bottom" constant="20" id="7Sc-TP-Opl"/>
|
||||
<constraint firstItem="0vG-Ty-4hj" firstAttribute="top" secondItem="VZe-Gu-rIx" secondAttribute="bottom" id="8lH-Op-m0b"/>
|
||||
<constraint firstItem="d5L-hE-z56" firstAttribute="trailing" secondItem="0vG-Ty-4hj" secondAttribute="trailing" id="9RH-ob-nxK"/>
|
||||
<constraint firstAttribute="width" secondItem="VZe-Gu-rIx" secondAttribute="width" id="CF8-wO-Qmv"/>
|
||||
<constraint firstItem="ZZ7-CE-a8J" firstAttribute="top" secondItem="d5L-hE-z56" secondAttribute="bottom" priority="500" id="Ccg-Zb-Jdz"/>
|
||||
<constraint firstItem="Xff-AT-bjO" firstAttribute="leading" secondItem="0vG-Ty-4hj" secondAttribute="leading" id="FF7-dF-mRZ"/>
|
||||
<constraint firstItem="REz-A0-Vg9" firstAttribute="top" secondItem="Tx8-cY-Dyg" secondAttribute="top" constant="20" id="FgB-En-Kgp"/>
|
||||
<constraint firstItem="0vG-Ty-4hj" firstAttribute="trailing" secondItem="VZe-Gu-rIx" secondAttribute="trailing" id="INy-WF-7W6"/>
|
||||
<constraint firstAttribute="centerX" secondItem="VZe-Gu-rIx" secondAttribute="centerX" id="KrD-nj-Oh6"/>
|
||||
<constraint firstItem="Xff-AT-bjO" firstAttribute="top" secondItem="ZZ7-CE-a8J" secondAttribute="bottom" id="NXi-bS-mbk"/>
|
||||
<constraint firstItem="0vG-Ty-4hj" firstAttribute="leading" secondItem="VZe-Gu-rIx" secondAttribute="leading" id="O7A-Ma-3gA"/>
|
||||
<constraint firstItem="pNk-h2-LSK" firstAttribute="top" secondItem="0vG-Ty-4hj" secondAttribute="bottom" id="OS9-H6-KDF"/>
|
||||
<constraint firstItem="pNk-h2-LSK" firstAttribute="height" secondItem="VZe-Gu-rIx" secondAttribute="height" id="Z3c-z0-QUD"/>
|
||||
<constraint firstItem="ZZ7-CE-a8J" firstAttribute="leading" secondItem="VZe-Gu-rIx" secondAttribute="leading" id="apm-L8-gZW"/>
|
||||
<constraint firstItem="ZZ7-CE-a8J" firstAttribute="trailing" secondItem="VZe-Gu-rIx" secondAttribute="trailing" id="bXV-jy-LQ5"/>
|
||||
<constraint firstItem="d5L-hE-z56" firstAttribute="height" secondItem="0vG-Ty-4hj" secondAttribute="height" id="d0s-RF-Hth"/>
|
||||
<constraint firstItem="pNk-h2-LSK" firstAttribute="trailing" secondItem="VZe-Gu-rIx" secondAttribute="trailing" id="dKB-th-WyI"/>
|
||||
<constraint firstAttribute="centerX" secondItem="fGq-0K-ugB" secondAttribute="centerX" id="hum-18-a8G"/>
|
||||
<constraint firstItem="Xff-AT-bjO" firstAttribute="trailing" secondItem="0vG-Ty-4hj" secondAttribute="trailing" id="ibO-5L-PH1"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Xff-AT-bjO" secondAttribute="bottom" id="kZ3-1x-e5J"/>
|
||||
<constraint firstItem="pNk-h2-LSK" firstAttribute="leading" secondItem="VZe-Gu-rIx" secondAttribute="leading" id="kv8-5K-4MW"/>
|
||||
<constraint firstItem="Xff-AT-bjO" firstAttribute="height" secondItem="0vG-Ty-4hj" secondAttribute="height" id="net-N9-aSB"/>
|
||||
<constraint firstAttribute="width" constant="280" id="oBl-6m-Exe"/>
|
||||
<constraint firstItem="d5L-hE-z56" firstAttribute="top" secondItem="pNk-h2-LSK" secondAttribute="bottom" id="psC-4J-kSw"/>
|
||||
<constraint firstItem="ZZ7-CE-a8J" firstAttribute="height" secondItem="VZe-Gu-rIx" secondAttribute="height" id="wqS-Qh-ept"/>
|
||||
<constraint firstItem="fGq-0K-ugB" firstAttribute="top" secondItem="REz-A0-Vg9" secondAttribute="bottom" constant="12" id="xjY-UE-eIh"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="AlertView"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="centerY" secondItem="Tx8-cY-Dyg" secondAttribute="centerY" id="Zpv-vh-Jxs"/>
|
||||
<constraint firstAttribute="centerX" secondItem="Tx8-cY-Dyg" secondAttribute="centerX" id="oWX-V9-KBj"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="y7G-G6-vSl"/>
|
||||
<point key="canvasLocation" x="304.5" y="171.5"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="dialog_btn_press" width="280" height="44"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#import "MWMAlert.h"
|
||||
|
||||
@interface MWMRoutingDisclaimerAlert : MWMAlert
|
||||
|
||||
+ (instancetype)alertWithOkBlock:(MWMVoidBlock)block;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
#import "MWMRoutingDisclaimerAlert.h"
|
||||
#import "MWMAlertViewController.h"
|
||||
|
||||
static CGFloat const kMinimumOffset = 20.;
|
||||
|
||||
@interface MWMRoutingDisclaimerAlert ()
|
||||
|
||||
@property(weak, nonatomic) IBOutlet UITextView * textView;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint * textViewHeight;
|
||||
@property(copy, nonatomic) MWMVoidBlock okBlock;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMRoutingDisclaimerAlert
|
||||
|
||||
+ (instancetype)alertWithOkBlock:(MWMVoidBlock)block
|
||||
{
|
||||
MWMRoutingDisclaimerAlert * alert =
|
||||
[NSBundle.mainBundle loadNibNamed:[self className] owner:nil options:nil].firstObject;
|
||||
NSString * message = [NSString stringWithFormat:@"%@\n\n%@\n\n%@\n\n%@\n\n%@",
|
||||
L(@"dialog_routing_disclaimer_priority"),
|
||||
L(@"dialog_routing_disclaimer_precision"),
|
||||
L(@"dialog_routing_disclaimer_recommendations"),
|
||||
L(@"dialog_routing_disclaimer_borders"),
|
||||
L(@"dialog_routing_disclaimer_beware")];
|
||||
|
||||
alert.textView.attributedText =
|
||||
[[NSAttributedString alloc] initWithString:message
|
||||
attributes:@{
|
||||
NSFontAttributeName : UIFont.regular14,
|
||||
NSForegroundColorAttributeName : UIColor.blackSecondaryText
|
||||
}];
|
||||
[alert.textView sizeToFit];
|
||||
UIWindow * window = UIApplication.sharedApplication.keyWindow;
|
||||
[alert invalidateTextViewHeight:alert.textView.height withHeight:window.height];
|
||||
alert.okBlock = block;
|
||||
return alert;
|
||||
}
|
||||
|
||||
- (IBAction)okTap
|
||||
{
|
||||
[self close:self.okBlock];
|
||||
}
|
||||
|
||||
- (IBAction)cancelTap
|
||||
{
|
||||
[self close:nil];
|
||||
}
|
||||
|
||||
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation
|
||||
{
|
||||
UIView * superview = self.superview ?: UIApplication.sharedApplication.keyWindow;
|
||||
CGFloat const height = UIInterfaceOrientationIsLandscape(orientation)
|
||||
? MIN(superview.width, superview.height)
|
||||
: MAX(superview.width, superview.height);
|
||||
[self invalidateTextViewHeight:self.textView.contentSize.height withHeight:height];
|
||||
}
|
||||
|
||||
- (void)invalidateTextViewHeight:(CGFloat)textViewHeight withHeight:(CGFloat)height
|
||||
{
|
||||
self.textViewHeight.constant = [self bounded:textViewHeight withHeight:height];
|
||||
self.textView.scrollEnabled = textViewHeight > self.textViewHeight.constant;
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (CGFloat)bounded:(CGFloat)f withHeight:(CGFloat)h
|
||||
{
|
||||
CGFloat const currentHeight = [self.subviews.firstObject height];
|
||||
CGFloat const maximumHeight = h - 2. * kMinimumOffset;
|
||||
CGFloat const availableHeight = maximumHeight - currentHeight;
|
||||
return MIN(f, availableHeight + self.textViewHeight.constant);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="Ie9-JJ-hnZ" customClass="MWMRoutingDisclaimerAlert" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ogu-AE-Ohh" userLabel="ContainerView">
|
||||
<rect key="frame" x="47.5" y="265" width="280" height="137.5"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="S3s-j7-v6E" userLabel="Title">
|
||||
<rect key="frame" x="20" y="20" width="240" height="21.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="uht-Xi-vWk"/>
|
||||
<constraint firstAttribute="width" constant="240" id="wsn-Cq-kJT"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="18"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="dialog_routing_disclaimer_title"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium18:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" usesAttributedText="YES" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lbr-Xd-hLF">
|
||||
<rect key="frame" x="20" y="52.5" width="240" height="20"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="240" id="GeJ-mk-jko"/>
|
||||
<constraint firstAttribute="height" constant="20" id="XLp-um-S4A"/>
|
||||
</constraints>
|
||||
<attributedString key="attributedText">
|
||||
<fragment content="Message">
|
||||
<attributes>
|
||||
<color key="NSColor" red="0.0" green="0.0" blue="0.0" alpha="0.54000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<font key="NSFont" size="14" name="HelveticaNeue"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
</attributedString>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences" autocorrectionType="no" spellCheckingType="no"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="regular14:blackSecondaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</textView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XIL-q9-MMp" userLabel="hDivider">
|
||||
<rect key="frame" x="0.0" y="92.5" width="280" height="1"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="g4E-tB-Y4R"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aCY-1G-NL5" userLabel="right">
|
||||
<rect key="frame" x="140.5" y="93.5" width="139.5" height="44"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="acceptButton"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="AAv-pX-uuc"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<state key="normal" title="right">
|
||||
<color key="titleColor" red="0.090196078430000007" green="0.61960784310000006" blue="0.30196078430000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="accept"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="okTap" destination="Ie9-JJ-hnZ" eventType="touchUpInside" id="vfz-Rd-Kh6"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="JfV-qu-qmI" userLabel="left">
|
||||
<rect key="frame" x="0.0" y="93.5" width="139.5" height="44"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="cancelButton"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<state key="normal" title="left">
|
||||
<color key="titleColor" red="0.090196078430000007" green="0.61960784310000006" blue="0.30196078430000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="decline"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="cancelTap" destination="Ie9-JJ-hnZ" eventType="touchUpInside" id="KWl-Dn-BpU"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="930-J1-9o0" userLabel="vDivider">
|
||||
<rect key="frame" x="139.5" y="93.5" width="1" height="44"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="1" id="mf2-e7-Qg6"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.88" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="lbr-Xd-hLF" firstAttribute="top" secondItem="S3s-j7-v6E" secondAttribute="bottom" constant="11" id="20T-2E-mmf"/>
|
||||
<constraint firstAttribute="width" secondItem="XIL-q9-MMp" secondAttribute="width" id="3m7-Ha-HlN"/>
|
||||
<constraint firstItem="XIL-q9-MMp" firstAttribute="top" secondItem="lbr-Xd-hLF" secondAttribute="bottom" constant="20" id="4k1-g8-WZV"/>
|
||||
<constraint firstAttribute="trailing" secondItem="aCY-1G-NL5" secondAttribute="trailing" id="BcN-zR-SdX"/>
|
||||
<constraint firstItem="930-J1-9o0" firstAttribute="bottom" secondItem="aCY-1G-NL5" secondAttribute="bottom" id="F4d-Td-XJg"/>
|
||||
<constraint firstItem="JfV-qu-qmI" firstAttribute="width" secondItem="aCY-1G-NL5" secondAttribute="width" id="KAH-qc-1f1"/>
|
||||
<constraint firstAttribute="width" constant="280" id="KpN-ir-OWh"/>
|
||||
<constraint firstAttribute="centerX" secondItem="lbr-Xd-hLF" secondAttribute="centerX" id="KuP-DA-FWV"/>
|
||||
<constraint firstItem="JfV-qu-qmI" firstAttribute="bottom" secondItem="aCY-1G-NL5" secondAttribute="bottom" id="MPL-wr-BbK"/>
|
||||
<constraint firstItem="aCY-1G-NL5" firstAttribute="leading" secondItem="930-J1-9o0" secondAttribute="trailing" id="N4p-JX-gzl"/>
|
||||
<constraint firstAttribute="centerX" secondItem="S3s-j7-v6E" secondAttribute="centerX" id="Oag-6s-ais"/>
|
||||
<constraint firstItem="S3s-j7-v6E" firstAttribute="top" secondItem="Ogu-AE-Ohh" secondAttribute="top" constant="20" id="OoP-Qp-YIb"/>
|
||||
<constraint firstItem="JfV-qu-qmI" firstAttribute="leading" secondItem="Ogu-AE-Ohh" secondAttribute="leading" id="VYG-iz-HL8"/>
|
||||
<constraint firstAttribute="bottom" secondItem="aCY-1G-NL5" secondAttribute="bottom" id="WB8-od-eAG"/>
|
||||
<constraint firstItem="930-J1-9o0" firstAttribute="top" secondItem="aCY-1G-NL5" secondAttribute="top" id="aP7-3d-zW1"/>
|
||||
<constraint firstItem="JfV-qu-qmI" firstAttribute="top" secondItem="aCY-1G-NL5" secondAttribute="top" id="b8D-tm-ZFK"/>
|
||||
<constraint firstAttribute="centerX" secondItem="XIL-q9-MMp" secondAttribute="centerX" id="bas-rx-OTg"/>
|
||||
<constraint firstItem="930-J1-9o0" firstAttribute="leading" secondItem="JfV-qu-qmI" secondAttribute="trailing" id="gTM-qA-I1T"/>
|
||||
<constraint firstItem="aCY-1G-NL5" firstAttribute="top" secondItem="XIL-q9-MMp" secondAttribute="bottom" id="swb-Yx-lVo"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="AlertView"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="centerY" secondItem="Ogu-AE-Ohh" secondAttribute="centerY" id="Kg9-hX-fvu"/>
|
||||
<constraint firstAttribute="centerX" secondItem="Ogu-AE-Ohh" secondAttribute="centerX" id="dkW-i0-CZ2"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="n9h-Fb-ugd"/>
|
||||
<connections>
|
||||
<outlet property="textView" destination="lbr-Xd-hLF" id="ru3-Uu-UKw"/>
|
||||
<outlet property="textViewHeight" destination="XLp-um-S4A" id="GmU-gr-jlY"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="305" y="172"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="dialog_btn_press" width="280" height="44"/>
|
||||
</resources>
|
||||
</document>
|
||||
110
iphone/Maps/Classes/CustomAlert/SpinnerAlert/MWMSpinnerAlert.xib
Normal file
110
iphone/Maps/Classes/CustomAlert/SpinnerAlert/MWMSpinnerAlert.xib
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="zbk-bu-cTT" customClass="MWMSpinnerAlert" propertyAccessControl="none">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fZW-Y8-F8A" userLabel="ContainerView">
|
||||
<rect key="frame" x="47.5" y="264.5" width="280" height="138.5"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6Ik-Gs-Yc7" userLabel="button1">
|
||||
<rect key="frame" x="0.0" y="94.5" width="280" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="ejP-Qr-J5O"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<state key="normal" title="cancel">
|
||||
<color key="titleColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" backgroundImage="dialog_btn_press"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="cancel"/>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="bold17:linkBlueText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<action selector="tap" destination="zbk-bu-cTT" eventType="touchUpInside" id="a2c-ED-0jy"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lX1-uE-UHN" userLabel="hDivider1">
|
||||
<rect key="frame" x="0.0" y="93.5" width="280" height="1"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="f9v-KZ-u2L"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="Divider"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kq1-MC-Bth">
|
||||
<rect key="frame" x="124" y="16" width="32" height="32"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="32" id="BsB-R0-OVr"/>
|
||||
<constraint firstAttribute="width" constant="32" id="ElH-YQ-cGZ"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JHp-iN-vQc" userLabel="Title">
|
||||
<rect key="frame" x="20" y="56" width="240" height="21.5"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="57O-O2-CsM"/>
|
||||
<constraint firstAttribute="width" constant="240" id="M8o-cf-xqy"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="18"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium18:blackPrimaryText"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.88" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="6Ik-Gs-Yc7" firstAttribute="top" secondItem="lX1-uE-UHN" secondAttribute="bottom" id="04f-3J-sOv"/>
|
||||
<constraint firstItem="kq1-MC-Bth" firstAttribute="centerX" secondItem="fZW-Y8-F8A" secondAttribute="centerX" id="1bI-tg-lK9"/>
|
||||
<constraint firstAttribute="width" constant="280" id="7A5-aI-a0x"/>
|
||||
<constraint firstAttribute="centerX" secondItem="lX1-uE-UHN" secondAttribute="centerX" id="DKt-je-czo"/>
|
||||
<constraint firstItem="kq1-MC-Bth" firstAttribute="top" secondItem="fZW-Y8-F8A" secondAttribute="top" constant="16" id="Ft7-Dd-lYK"/>
|
||||
<constraint firstAttribute="width" secondItem="lX1-uE-UHN" secondAttribute="width" id="Qpr-nH-Na5"/>
|
||||
<constraint firstAttribute="bottom" secondItem="6Ik-Gs-Yc7" secondAttribute="bottom" id="Tqv-1O-YVs"/>
|
||||
<constraint firstItem="lX1-uE-UHN" firstAttribute="top" secondItem="JHp-iN-vQc" secondAttribute="bottom" constant="16" id="b0T-2G-xsF"/>
|
||||
<constraint firstItem="6Ik-Gs-Yc7" firstAttribute="trailing" secondItem="lX1-uE-UHN" secondAttribute="trailing" id="tYJ-fJ-ov9"/>
|
||||
<constraint firstItem="6Ik-Gs-Yc7" firstAttribute="leading" secondItem="lX1-uE-UHN" secondAttribute="leading" id="vDN-x9-Sw8"/>
|
||||
<constraint firstAttribute="centerX" secondItem="JHp-iN-vQc" secondAttribute="centerX" id="wwC-8B-KBV"/>
|
||||
<constraint firstItem="JHp-iN-vQc" firstAttribute="top" secondItem="kq1-MC-Bth" secondAttribute="bottom" constant="8" id="yW9-DY-uxC"/>
|
||||
</constraints>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="AlertView"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="centerX" secondItem="fZW-Y8-F8A" secondAttribute="centerX" id="Ll6-go-lGy"/>
|
||||
<constraint firstAttribute="centerY" secondItem="fZW-Y8-F8A" secondAttribute="centerY" id="Sz5-hL-nLu"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="fPw-k5-Wbc"/>
|
||||
<connections>
|
||||
<outlet property="cancelButton" destination="6Ik-Gs-Yc7" id="PcZ-Qo-ycv"/>
|
||||
<outlet property="cancelHeight" destination="ejP-Qr-J5O" id="sxx-S8-gDN"/>
|
||||
<outlet property="divider" destination="lX1-uE-UHN" id="BGf-W6-2UF"/>
|
||||
<outlet property="progressView" destination="kq1-MC-Bth" id="FK3-oI-lWX"/>
|
||||
<outlet property="title" destination="JHp-iN-vQc" id="6lQ-ps-HJD"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="304.5" y="171.5"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="dialog_btn_press" width="280" height="44"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
@objc(MWMSpinnerAlert)
|
||||
final class SpinnerAlert: MWMAlert {
|
||||
@IBOutlet private weak var progressView: UIView!
|
||||
@IBOutlet private weak var title: UILabel!
|
||||
@IBOutlet private weak var cancelHeight: NSLayoutConstraint!
|
||||
@IBOutlet private weak var cancelButton: UIButton!
|
||||
@IBOutlet private weak var divider: UIView!
|
||||
|
||||
private var cancel: MWMVoidBlock?
|
||||
|
||||
private lazy var progress: MWMCircularProgress = {
|
||||
var p = MWMCircularProgress.downloaderProgress(forParentView: progressView)
|
||||
return p
|
||||
}()
|
||||
|
||||
|
||||
@objc static func alert(title: String, cancel: MWMVoidBlock?) -> SpinnerAlert? {
|
||||
guard let alert = Bundle.main.loadNibNamed(className(), owner: nil, options: nil)?.first
|
||||
as? SpinnerAlert else {
|
||||
assertionFailure()
|
||||
return nil
|
||||
}
|
||||
|
||||
alert.title.text = title
|
||||
alert.progress.state = .spinner
|
||||
alert.progress.setCancelButtonHidden()
|
||||
|
||||
if let cancel = cancel {
|
||||
alert.cancel = cancel
|
||||
} else {
|
||||
alert.cancelHeight.constant = 0
|
||||
alert.cancelButton.isHidden = true
|
||||
alert.divider.isHidden = true
|
||||
alert.setNeedsLayout()
|
||||
}
|
||||
|
||||
return alert
|
||||
}
|
||||
|
||||
@IBAction private func tap() {
|
||||
close(cancel)
|
||||
}
|
||||
}
|
||||
122
iphone/Maps/Classes/CustomAlert/Toast/Toast.swift
Normal file
122
iphone/Maps/Classes/CustomAlert/Toast/Toast.swift
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
@objc
|
||||
final class Toast: NSObject {
|
||||
@objc
|
||||
enum Alignment: Int {
|
||||
case bottom
|
||||
case top
|
||||
}
|
||||
|
||||
private enum Constants {
|
||||
static let presentationDuration: TimeInterval = 3
|
||||
static let animationDuration: TimeInterval = kDefaultAnimationDuration
|
||||
static let bottomOffset: CGFloat = 63
|
||||
static let topOffset: CGFloat = 50
|
||||
static let horizontalOffset: CGFloat = 16
|
||||
static let labelOffsets = UIEdgeInsets(top: 10, left: 14, bottom: -10, right: -14)
|
||||
static let maxWidth: CGFloat = 400
|
||||
}
|
||||
|
||||
private static var toasts: [Toast] = []
|
||||
private var blurView: UIVisualEffectView
|
||||
|
||||
private init(_ text: String) {
|
||||
blurView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
|
||||
blurView.setStyle(.toastBackground)
|
||||
blurView.isUserInteractionEnabled = false
|
||||
blurView.alpha = 0
|
||||
blurView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
let label = UILabel()
|
||||
label.text = text
|
||||
label.setStyle(.toastLabel)
|
||||
label.numberOfLines = 0
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
||||
blurView.contentView.addSubview(label)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
label.leadingAnchor.constraint(equalTo: blurView.contentView.leadingAnchor, constant: Constants.labelOffsets.left),
|
||||
label.trailingAnchor.constraint(equalTo: blurView.contentView.trailingAnchor, constant: Constants.labelOffsets.right),
|
||||
label.topAnchor.constraint(equalTo: blurView.contentView.topAnchor, constant: Constants.labelOffsets.top),
|
||||
label.bottomAnchor.constraint(equalTo: blurView.contentView.bottomAnchor, constant: Constants.labelOffsets.bottom)
|
||||
])
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
@objc
|
||||
static func show(withText text: String) {
|
||||
show(withText: text, alignment: .bottom)
|
||||
}
|
||||
|
||||
@objc
|
||||
static func show(withText text: String, alignment: Alignment) {
|
||||
show(withText: text, alignment: alignment, pinToSafeArea: true)
|
||||
}
|
||||
|
||||
@objc
|
||||
static func show(withText text: String, alignment: Alignment, pinToSafeArea: Bool) {
|
||||
let toast = Toast(text)
|
||||
toasts.append(toast)
|
||||
toast.show(withAlignment: alignment, pinToSafeArea: pinToSafeArea)
|
||||
}
|
||||
|
||||
@objc
|
||||
static func hideAll() {
|
||||
toasts.forEach { $0.hide() }
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
|
||||
private func show(withAlignment alignment: Alignment, pinToSafeArea: Bool) {
|
||||
Self.hideAll()
|
||||
guard let view = (UIApplication.shared.connectedScenes.filter { $0.activationState == .foregroundActive }.first(where: { $0 is UIWindowScene }) as? UIWindowScene)?.keyWindow else { return }
|
||||
view.addSubview(blurView)
|
||||
|
||||
let leadingConstraint = blurView.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor, constant: Constants.horizontalOffset)
|
||||
let trailingConstraint = blurView.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor, constant: -Constants.horizontalOffset)
|
||||
let maxWidthConstraint = blurView.widthAnchor.constraint(equalToConstant: Constants.maxWidth).withPriority(.defaultLow)
|
||||
|
||||
let verticalConstraint: NSLayoutConstraint
|
||||
switch alignment {
|
||||
case .bottom:
|
||||
verticalConstraint = blurView.bottomAnchor.constraint(equalTo: pinToSafeArea ? view.safeAreaLayoutGuide.bottomAnchor : view.bottomAnchor,
|
||||
constant: -Constants.bottomOffset)
|
||||
case .top:
|
||||
verticalConstraint = blurView.topAnchor.constraint(equalTo: pinToSafeArea ? view.safeAreaLayoutGuide.topAnchor : view.topAnchor,
|
||||
constant: Constants.topOffset)
|
||||
}
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
leadingConstraint,
|
||||
trailingConstraint,
|
||||
maxWidthConstraint,
|
||||
verticalConstraint,
|
||||
blurView.centerXAnchor.constraint(equalTo: pinToSafeArea ? view.safeAreaLayoutGuide.centerXAnchor : view.centerXAnchor)
|
||||
])
|
||||
|
||||
UIView.animate(withDuration: Constants.animationDuration, animations: {
|
||||
self.blurView.alpha = 1
|
||||
} , completion: { _ in
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + Constants.presentationDuration) {
|
||||
self.hide()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private func hide() {
|
||||
if self.blurView.superview != nil {
|
||||
UIView.animate(withDuration: Constants.animationDuration,
|
||||
animations: { self.blurView.alpha = 0 }) { [self] _ in
|
||||
self.blurView.removeFromSuperview()
|
||||
Self.toasts.removeAll(where: { $0 === self }) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension NSLayoutConstraint {
|
||||
func withPriority(_ priority: UILayoutPriority) -> NSLayoutConstraint {
|
||||
self.priority = priority
|
||||
return self
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue