diff --git a/ios-sdk.xcodeproj/project.pbxproj b/ios-sdk.xcodeproj/project.pbxproj index d44aa32..cf77066 100644 --- a/ios-sdk.xcodeproj/project.pbxproj +++ b/ios-sdk.xcodeproj/project.pbxproj @@ -9,6 +9,10 @@ /* Begin PBXBuildFile section */ 7D2FB7671E6103D500CB82CF /* DictionaryExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2FB7661E6103D500CB82CF /* DictionaryExtensions.swift */; }; 7D2FB7691E64E66B00CB82CF /* DictionaryExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2FB7681E64E66B00CB82CF /* DictionaryExtensionsTests.swift */; }; + 7D7DAE161E60AF9400FFCA6F /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D7DAE151E60AF9400FFCA6F /* Localizable.strings */; }; + 7D7847251E6775EA000D56CA /* Dimensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D7847241E6775EA000D56CA /* Dimensions.swift */; }; + 7D7DAE1D1E60C5C900FFCA6F /* TestApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D7DAE1C1E60C5C900FFCA6F /* TestApplication.swift */; }; + 7D7DAE211E60EEA300FFCA6F /* ApplicationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D7DAE201E60EEA300FFCA6F /* ApplicationExtensions.swift */; }; 7DF0AA2E1E5520ED00B0406E /* TestTGMapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DF0AA2D1E5520ED00B0406E /* TestTGMapViewController.swift */; }; AC586709D7E7D4C22AA9474D /* Pods_ios_sdk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 99B3EC60450343448FA4E699 /* Pods_ios_sdk.framework */; }; C8F52AC1BCE46BB17E1CE0A5 /* Pods_ios_sdkTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A3EA87256F2EA6D0C43260A /* Pods_ios_sdkTests.framework */; }; @@ -75,6 +79,10 @@ 5A3EA87256F2EA6D0C43260A /* Pods_ios_sdkTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ios_sdkTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7D2FB7661E6103D500CB82CF /* DictionaryExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DictionaryExtensions.swift; sourceTree = ""; }; 7D2FB7681E64E66B00CB82CF /* DictionaryExtensionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DictionaryExtensionsTests.swift; sourceTree = ""; }; + 7D7DAE151E60AF9400FFCA6F /* Localizable.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; }; + 7D7847241E6775EA000D56CA /* Dimensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dimensions.swift; sourceTree = ""; }; + 7D7DAE1C1E60C5C900FFCA6F /* TestApplication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestApplication.swift; sourceTree = ""; }; + 7D7DAE201E60EEA300FFCA6F /* ApplicationExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApplicationExtensions.swift; sourceTree = ""; }; 7DF0AA2D1E5520ED00B0406E /* TestTGMapViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestTGMapViewController.swift; sourceTree = ""; }; 92C35E8790FB3255237B1E77 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 99B3EC60450343448FA4E699 /* Pods_ios_sdk.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ios_sdk.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -138,6 +146,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 7D7847231E6775EA000D56CA /* dimensions */ = { + isa = PBXGroup; + children = ( + 7D7847241E6775EA000D56CA /* Dimensions.swift */, + ); + path = dimensions; + sourceTree = ""; + }; 8E4E4B43C1B764470EBBDBBA /* Pods */ = { isa = PBXGroup; children = ( @@ -232,6 +248,7 @@ DB188EEB1E28492A0054DEFD /* MapzenManagerTests.swift */, 7DF0AA2D1E5520ED00B0406E /* TestTGMapViewController.swift */, 7D2FB7681E64E66B00CB82CF /* DictionaryExtensionsTests.swift */, + 7D7DAE1C1E60C5C900FFCA6F /* TestApplication.swift */, ); path = "ios-sdkTests"; sourceTree = ""; @@ -239,6 +256,7 @@ DBC84EA51C985B6D00EE80D8 /* src */ = { isa = PBXGroup; children = ( + 7D7847231E6775EA000D56CA /* dimensions */, DBC869911D3663C700DDC4FE /* PeliasMapkitExtensions.swift */, DB70FEA81DE3425800249509 /* MapViewController.swift */, DB5B31501DE4A173005EB816 /* LocationManager.swift */, @@ -246,6 +264,7 @@ DB188EE91E2846600054DEFD /* MapzenManager.swift */, DB188EED1E290D310054DEFD /* MapzenRoutingController.swift */, 7D2FB7661E6103D500CB82CF /* DictionaryExtensions.swift */, + 7D7DAE201E60EEA300FFCA6F /* ApplicationExtensions.swift */, ); path = src; sourceTree = ""; @@ -485,6 +504,8 @@ files = ( DBFB759F1E2046D100CF6173 /* TangramExtensions.swift in Sources */, DBFB759D1E20345500CF6173 /* SearchPinsViewController.swift in Sources */, + 7D7DAE211E60EEA300FFCA6F /* ApplicationExtensions.swift in Sources */, + 7D7847251E6775EA000D56CA /* Dimensions.swift in Sources */, DB188EEA1E2846600054DEFD /* MapzenManager.swift in Sources */, DBF441AE1D351B0A007DEE95 /* TangramVC.swift in Sources */, DB22E59E1DA473CA004264E0 /* RouteDirectionCell.swift in Sources */, @@ -506,6 +527,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7D7DAE1D1E60C5C900FFCA6F /* TestApplication.swift in Sources */, 7DF0AA2E1E5520ED00B0406E /* TestTGMapViewController.swift in Sources */, DB188EEC1E28492A0054DEFD /* MapzenManagerTests.swift in Sources */, DBCBC5BF1E0AE1CF0045B345 /* MapViewControllerTests.swift in Sources */, diff --git a/ios-sdkTests/MapViewControllerTests.swift b/ios-sdkTests/MapViewControllerTests.swift index 7b63e1c..abb3350 100644 --- a/ios-sdkTests/MapViewControllerTests.swift +++ b/ios-sdkTests/MapViewControllerTests.swift @@ -12,7 +12,6 @@ import TangramMap import CoreLocation class TestMapViewController: MapViewController { - func lastSetPointValue() -> TGGeoPoint? { return lastSetPoint } @@ -32,11 +31,13 @@ class MockHTTPHandler: TGHttpHandler { class MapViewControllerTests: XCTestCase { + let testApplication = TestApplication() var controller = TestMapViewController() var tgViewController = TestTGMapViewController() let mockLocation = CLLocation(latitude: 0.0, longitude: 0.0) // Null Island! override func setUp() { + controller = TestMapViewController(applicationProtocol: testApplication) controller.tgViewController = tgViewController let mockHTTP = MockHTTPHandler() controller.tgViewController.httpHandler = mockHTTP @@ -233,6 +234,7 @@ class MapViewControllerTests: XCTestCase { } func testFindMeButtonInitialState() { + controller.setupFindMeButton() //Test Initial State XCTAssertTrue(controller.findMeButton.isHidden) XCTAssertFalse(controller.findMeButton.isEnabled) @@ -559,6 +561,15 @@ class MapViewControllerTests: XCTestCase { controller.mapView(tgViewController, didSelectMarker: nil, atScreenPosition: CGPoint()) XCTAssertFalse(delegate.markerPicked) } + + func testAttributionOpensRights() { + controller.viewDidLoad() + let actions = controller.attributionBtn.actions(forTarget: controller, forControlEvent: .touchUpInside) + let selectorStr = actions?.first + controller.perform(NSSelectorFromString(selectorStr!)) + XCTAssertEqual(testApplication.urlToOpen?.absoluteString, "https://mapzen.com/rights/") + } + } class TestPanDelegate : MapPanGestureDelegate { diff --git a/ios-sdkTests/TestApplication.swift b/ios-sdkTests/TestApplication.swift new file mode 100644 index 0000000..2fb796c --- /dev/null +++ b/ios-sdkTests/TestApplication.swift @@ -0,0 +1,20 @@ +// +// TestApplication.swift +// ios-sdk +// +// Created by Sarah Lensing on 2/24/17. +// Copyright © 2017 Mapzen. All rights reserved. +// + +import UIKit +@testable import ios_sdk + +class TestApplication: ApplicationProtocol { + + open var urlToOpen : URL? + + func openURL(_ url: URL) -> Bool { + urlToOpen = url + return false + } +} diff --git a/src/ApplicationExtensions.swift b/src/ApplicationExtensions.swift new file mode 100644 index 0000000..8e964bd --- /dev/null +++ b/src/ApplicationExtensions.swift @@ -0,0 +1,15 @@ +// +// ApplicationExtensions.swift +// ios-sdk +// +// Created by Sarah Lensing on 2/24/17. +// Copyright © 2017 Mapzen. All rights reserved. +// + +import UIKit + +protocol ApplicationProtocol { + func openURL(_ url: URL) -> Bool +} + +extension UIApplication: ApplicationProtocol {} diff --git a/src/MapViewController.swift b/src/MapViewController.swift index 4b3fe8b..3a7d23b 100644 --- a/src/MapViewController.swift +++ b/src/MapViewController.swift @@ -68,7 +68,9 @@ open class MapViewController: UIViewController, LocationManagerDelegate { //Error Domains for NSError Appeasement open static let MapzenGeneralErrorDomain = "MapzenGeneralErrorDomain" + private static let mapzenRights = "https://mapzen.com/rights/" + let application : ApplicationProtocol open var tgViewController: TGMapViewController = TGMapViewController() var currentLocationGem: TGMapMarkerId? var lastSetPoint: TGGeoPoint? @@ -77,6 +79,7 @@ open class MapViewController: UIViewController, LocationManagerDelegate { open var shouldFollowCurrentLocation = false open var findMeButton = UIButton(type: .custom) open var currentAnnotations: [PeliasMapkitAnnotation : TGMapMarkerId] = Dictionary() + open var attributionBtn = UIButton() open var cameraType: TGCameraType { set { @@ -143,18 +146,32 @@ open class MapViewController: UIViewController, LocationManagerDelegate { public typealias OnSceneLoaded = (String) -> () fileprivate var onSceneLoadedClosure : OnSceneLoaded? = nil - init(){ + init() { + application = UIApplication.shared super.init(nibName: nil, bundle: nil) - defer { - findMeButton = createFindMeButton() - } } required public init?(coder aDecoder: NSCoder) { + application = UIApplication.shared super.init(coder: aDecoder) - defer { - findMeButton = createFindMeButton() - } + } + + public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + application = UIApplication.shared + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + } + + init(applicationProtocol: ApplicationProtocol) { + application = applicationProtocol + super.init(nibName: nil, bundle: nil) + } + + open var mapView : GLKView { + return tgViewController.view as! GLKView + } + + open var tabBarHeight : CGFloat { + return self.tabBarController?.tabBar.frame.height ?? 0 } open func animate(toPosition position: TGGeoPoint, withDuration seconds: Float) { @@ -396,38 +413,19 @@ open class MapViewController: UIViewController, LocationManagerDelegate { override open func viewDidLoad() { super.viewDidLoad() LocationManager.sharedManager.delegate = self - - self.view.addSubview(tgViewController.view) - + + setupTgControllerView() + setupAttribution() + setupFindMeButton() + tgViewController.gestureDelegate = self tgViewController.mapViewDelegate = self } override open func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) - tgViewController.viewWillTransition(to: size, with:coordinator) - } - - override open func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - let viewRect = view.bounds - findMeButton.frame = CGRect(x: viewRect.width - 60.0, y: viewRect.height - 100.0, width: CGFloat(48), height: CGFloat(48)) - view.addSubview(findMeButton) - findMeButton.sizeToFit() - } - - func createFindMeButton() -> UIButton { - let findMeButton = UIButton(type: UIButtonType.custom) - findMeButton.addTarget(self, action: #selector(MapViewController.defaultFindMeAction(_:touchEvent:)), for: .touchUpInside) - findMeButton.isEnabled = false - findMeButton.isHidden = true - findMeButton.adjustsImageWhenHighlighted = false - findMeButton.setBackgroundImage(UIImage(named: "ic_find_me_normal"), for: UIControlState()) - //TODO: This should also have .Highlighted as well .Selected , but something about the @3x assets and UIButton is misbehaving; might need bug opened with Apple. - findMeButton.setBackgroundImage(UIImage(named: "ic_find_me_pressed"), for: [.selected]) - findMeButton.backgroundColor = UIColor.white - findMeButton.autoresizingMask = [.flexibleTopMargin, .flexibleLeftMargin] - return findMeButton + let adjustedSize = CGSize(width: size.width, height: size.height-tabBarHeight) + tgViewController.viewWillTransition(to: adjustedSize, with:coordinator) } //MARK: - LocationManagerDelegate @@ -469,6 +467,60 @@ open class MapViewController: UIViewController, LocationManagerDelegate { return } + //MARK: - private + + private func setupTgControllerView() { + tgViewController.view.translatesAutoresizingMaskIntoConstraints = false + self.view.addSubview(tgViewController.view) + + let leftConstraint = tgViewController.view.leftAnchor.constraint(equalTo: view.leftAnchor) + let rightConstraint = tgViewController.view.rightAnchor.constraint(equalTo: view.rightAnchor) + let topConstraint = tgViewController.view.topAnchor.constraint(equalTo: view.topAnchor) + let bottomConstraint = tgViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -tabBarHeight) + NSLayoutConstraint.activate([leftConstraint, rightConstraint, topConstraint, bottomConstraint]) + } + + private func setupAttribution() { + attributionBtn = UIButton() + attributionBtn.setTitle("Powered by Mapzen", for: .normal) + attributionBtn.setTitleColor(.darkGray, for: .normal) + attributionBtn.titleLabel?.font = UIFont.systemFont(ofSize: 14) + attributionBtn.addTarget(self, action: #selector(openMapzenTerms), for: .touchUpInside) + attributionBtn.sizeToFit() + attributionBtn.translatesAutoresizingMaskIntoConstraints = false + mapView.addSubview(attributionBtn) + + let horizontalConstraint = attributionBtn.leftAnchor.constraint(equalTo: mapView.leftAnchor, constant: Dimensions.defaultPadding) + let verticalConstraint = attributionBtn.bottomAnchor.constraint(equalTo: mapView.bottomAnchor, constant: -Dimensions.defaultPadding) + NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint]) + } + + func setupFindMeButton() { + findMeButton = UIButton(type: UIButtonType.custom) + findMeButton.addTarget(self, action: #selector(MapViewController.defaultFindMeAction(_:touchEvent:)), for: .touchUpInside) + findMeButton.isEnabled = false + findMeButton.isHidden = true + findMeButton.adjustsImageWhenHighlighted = false + findMeButton.setBackgroundImage(UIImage(named: "ic_find_me_normal"), for: UIControlState()) + //TODO: This should also have .Highlighted as well .Selected , but something about the @3x assets and UIButton is misbehaving; might need bug opened with Apple. + findMeButton.setBackgroundImage(UIImage(named: "ic_find_me_pressed"), for: [.selected]) + findMeButton.backgroundColor = UIColor.white + //findMeButton.autoresizingMask = [.flexibleTopMargin, .flexibleLeftMargin] + findMeButton.translatesAutoresizingMaskIntoConstraints = false + mapView.addSubview(findMeButton) + + let horizontalConstraint = findMeButton.rightAnchor.constraint(equalTo: mapView.rightAnchor, constant: -Dimensions.defaultPadding) + let verticalConstraint = findMeButton.bottomAnchor.constraint(equalTo: mapView.bottomAnchor, constant: -Dimensions.defaultPadding) + let widthConstraint = findMeButton.widthAnchor.constraint(equalToConstant: Dimensions.squareMapBtnSize) + let heightConstraint = findMeButton.widthAnchor.constraint(equalToConstant: Dimensions.squareMapBtnSize) + NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint, widthConstraint, heightConstraint]) + } + + @objc private func openMapzenTerms() { + guard let url = URL(string: MapViewController.mapzenRights) else { return } + let _ = application.openURL(url) + } + private func updatesWithApiKeyUpdate(_ sceneUpdates: [TGSceneUpdate]) throws -> [TGSceneUpdate] { guard let apiKey = MapzenManager.sharedManager.apiKey else { throw NSError(domain: MapViewController.MapzenGeneralErrorDomain, @@ -579,5 +631,6 @@ extension MapViewController : TGMapViewDelegate, TGRecognizerDelegate { open func mapView(_ view: TGMapViewController, recognizer: UIGestureRecognizer, didRecognizeShoveGesture displacement: CGPoint) { shoveDelegate?.mapController(self, didShoveMap: displacement) } + } diff --git a/src/dimensions/Dimensions.swift b/src/dimensions/Dimensions.swift new file mode 100644 index 0000000..11d88f1 --- /dev/null +++ b/src/dimensions/Dimensions.swift @@ -0,0 +1,17 @@ +// +// Dimensions.swift +// ios-sdk +// +// Created by Sarah Lensing on 2/24/17. +// Copyright © 2017 Mapzen. All rights reserved. +// + +import CoreGraphics + +// Holds constant values for UI element properties such as padding and width/height +public class Dimensions { + // Default padding used for map UI elements + public static let defaultPadding : CGFloat = 10.0 + // Size of all square map buttons such as the "find me" button + public static let squareMapBtnSize : CGFloat = 48.0 +}