From 8ab69324360a8f4dc46e0da924bf9618110bd1fa Mon Sep 17 00:00:00 2001 From: Quentin Nolan Date: Sat, 24 Mar 2018 19:48:18 -0400 Subject: [PATCH 1/7] +Get les Parts --- ios/MapPing.xcodeproj/project.pbxproj | 12 ++++++++-- .../Domain/Model/FailableDecodable.swift | 17 +++++++++++++ ios/MapPing/Classes/Domain/Model/Part.swift | 8 +++++++ ios/MapPing/Classes/Domain/Model/Parts.swift | 12 ++++++++++ .../Classes/Domain/Service/HttpService.swift | 23 ++++++++++++++++++ .../Classes/Domain/Service/PartService.swift | 24 +++++++++++++++---- 6 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 ios/MapPing/Classes/Domain/Model/FailableDecodable.swift create mode 100644 ios/MapPing/Classes/Domain/Model/Parts.swift create mode 100644 ios/MapPing/Classes/Domain/Service/HttpService.swift diff --git a/ios/MapPing.xcodeproj/project.pbxproj b/ios/MapPing.xcodeproj/project.pbxproj index b848def..0ed5034 100644 --- a/ios/MapPing.xcodeproj/project.pbxproj +++ b/ios/MapPing.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 665624242067128700F37197 /* HttpService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 665624232067128700F37197 /* HttpService.swift */; }; + 6656242820671A7900F37197 /* FailableDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6656242720671A7900F37197 /* FailableDecodable.swift */; }; 940F0B61F668AD88441334C2 /* Pods_MapPing.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 16CD00BB887C10CE3E91B52C /* Pods_MapPing.framework */; }; F1196A76206481B60084672D /* PartAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1196A75206481B60084672D /* PartAnnotationView.swift */; }; F1196A782064857D0084672D /* PartAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1196A772064857D0084672D /* PartAnnotation.swift */; }; @@ -38,6 +40,8 @@ /* Begin PBXFileReference section */ 16CD00BB887C10CE3E91B52C /* Pods_MapPing.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MapPing.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4BFA57CDB381CDA05A7C2162 /* Pods-MapPing.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MapPing.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MapPing/Pods-MapPing.debug.xcconfig"; sourceTree = ""; }; + 665624232067128700F37197 /* HttpService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpService.swift; sourceTree = ""; }; + 6656242720671A7900F37197 /* FailableDecodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailableDecodable.swift; sourceTree = ""; }; E901EFF955E9ACF4A910DAE7 /* Pods-MapPing.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MapPing.release.xcconfig"; path = "Pods/Target Support Files/Pods-MapPing/Pods-MapPing.release.xcconfig"; sourceTree = ""; }; F1196A75206481B60084672D /* PartAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartAnnotationView.swift; sourceTree = ""; }; F1196A772064857D0084672D /* PartAnnotation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartAnnotation.swift; sourceTree = ""; }; @@ -143,6 +147,7 @@ isa = PBXGroup; children = ( F1196A8D20653B660084672D /* Part.swift */, + 6656242720671A7900F37197 /* FailableDecodable.swift */, ); path = Model; sourceTree = ""; @@ -151,6 +156,7 @@ isa = PBXGroup; children = ( F1196A8F20653D4A0084672D /* PartService.swift */, + 665624232067128700F37197 /* HttpService.swift */, ); path = Service; sourceTree = ""; @@ -419,6 +425,7 @@ F19EA0AC2061C5BE00FE9A5E /* BaseViewController.swift in Sources */, F19EA0AE2061C5E800FE9A5E /* RootView.swift in Sources */, F19EA0C22061DA7E00FE9A5E /* Stylesheet.swift in Sources */, + 6656242820671A7900F37197 /* FailableDecodable.swift in Sources */, F1196A92206541700084672D /* ServiceFactory.swift in Sources */, F1196A8420649D580084672D /* PartCellView.swift in Sources */, F19EA0A92061C59B00FE9A5E /* ViewControllerFactory.swift in Sources */, @@ -432,6 +439,7 @@ F19EA0A72061C58400FE9A5E /* RootViewController.swift in Sources */, F1196A76206481B60084672D /* PartAnnotationView.swift in Sources */, F19EA0B72061C6C100FE9A5E /* ListViewController.swift in Sources */, + 665624242067128700F37197 /* HttpService.swift in Sources */, F19EA0BB2061C6DC00FE9A5E /* MapView.swift in Sources */, F19EA0BD2061C6E300FE9A5E /* ListView.swift in Sources */, F19EA0B52061C6B300FE9A5E /* MapViewController.swift in Sources */, @@ -567,7 +575,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 4Q596JWQC5; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/MapPing/SupportingFiles/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mirego.MapPing; @@ -583,7 +591,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 4Q596JWQC5; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/MapPing/SupportingFiles/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mirego.MapPing; diff --git a/ios/MapPing/Classes/Domain/Model/FailableDecodable.swift b/ios/MapPing/Classes/Domain/Model/FailableDecodable.swift new file mode 100644 index 0000000..5f9ddf8 --- /dev/null +++ b/ios/MapPing/Classes/Domain/Model/FailableDecodable.swift @@ -0,0 +1,17 @@ +// +// FailableDecodable.swift +// MapPing +// +// Created by Quentin Nolan on 18-03-24. +// Copyright © 2018 Mirego. All rights reserved. +// + +struct FailableDecodable : Decodable { + + let base: Base? + + init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + self.base = try? container.decode(Base.self) + } +} diff --git a/ios/MapPing/Classes/Domain/Model/Part.swift b/ios/MapPing/Classes/Domain/Model/Part.swift index 9180818..7a51225 100644 --- a/ios/MapPing/Classes/Domain/Model/Part.swift +++ b/ios/MapPing/Classes/Domain/Model/Part.swift @@ -8,11 +8,19 @@ struct Part: Codable { enum CodingKeys: String, CodingKey { case name + case component + case notes + case type case latitude = "lat" case longitude = "lon" + case address } let name: String + let component: String + let notes: String + let type: String let latitude: Float let longitude: Float + let address: String } diff --git a/ios/MapPing/Classes/Domain/Model/Parts.swift b/ios/MapPing/Classes/Domain/Model/Parts.swift new file mode 100644 index 0000000..f0dc05c --- /dev/null +++ b/ios/MapPing/Classes/Domain/Model/Parts.swift @@ -0,0 +1,12 @@ +// +// Parts.swift +// MapPing +// +// Created by Quentin Nolan on 18-03-24. +// Copyright © 2018 Mirego. All rights reserved. +// + +struct Parts: Codable { + let data: [Part] +} + diff --git a/ios/MapPing/Classes/Domain/Service/HttpService.swift b/ios/MapPing/Classes/Domain/Service/HttpService.swift new file mode 100644 index 0000000..e9dbe63 --- /dev/null +++ b/ios/MapPing/Classes/Domain/Service/HttpService.swift @@ -0,0 +1,23 @@ +// +// HttpService.swift +// MapPing +// +// Created by Quentin Nolan on 18-03-24. +// Copyright © 2018 Mirego. All rights reserved. +// + +import Foundation + +class HttpService { + + static func get(url: String, completion: @escaping (_ data: Data?, _ error: Error?) -> ()) { + guard let queryUrl = URL(string: url) else { + completion(nil, NSError(domain: "HttpService", code: -1, userInfo: ["description" : "Invalid url"])) + return + } + + URLSession.shared.dataTask(with: queryUrl) { (data, urlResponse, error) in + DispatchQueue.main.async { completion(data, error) } + }.resume() + } +} diff --git a/ios/MapPing/Classes/Domain/Service/PartService.swift b/ios/MapPing/Classes/Domain/Service/PartService.swift index a340aa3..e3deee1 100644 --- a/ios/MapPing/Classes/Domain/Service/PartService.swift +++ b/ios/MapPing/Classes/Domain/Service/PartService.swift @@ -5,15 +5,31 @@ // Copyright © 2018 Mirego. All rights reserved. // -import Foundation +import UIKit class PartService { - private let partsUrl = URL(string: "https://s3.amazonaws.com/shared.ws.mirego.com/competition/mapping.json")! + let partsUrl = "https://s3.amazonaws.com/shared.ws.mirego.com/competition/mapping.json" var partsObservable = Observable<[Part]>() func refreshParts() { - partsObservable.notify(data: []) - // TODO 🙄 + HttpService.get(url: partsUrl) { (data, error) in + guard let data = data else { + return + } + do { + let partsData = try JSONDecoder() + .decode([FailableDecodable].self, from: data) + .flatMap { $0.base } + + self.partsObservable.notify(data:partsData) + } catch { + print("Error trying to convert data to JSON") + } + } + } + + static func getParts(completion: @escaping (_ posts: [Part]?, _ error: Error?) -> ()) { + } } From 3a188ebd5b210e431ac14e623631b09657d70425 Mon Sep 17 00:00:00 2001 From: Quentin Nolan Date: Sat, 24 Mar 2018 20:01:32 -0400 Subject: [PATCH 2/7] +Ajouter PartType --- ios/MapPing.xcodeproj/project.pbxproj | 4 ++ ios/MapPing/Classes/Domain/Model/Part.swift | 2 +- .../Classes/Domain/Model/PartType.swift | 40 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 ios/MapPing/Classes/Domain/Model/PartType.swift diff --git a/ios/MapPing.xcodeproj/project.pbxproj b/ios/MapPing.xcodeproj/project.pbxproj index 0ed5034..241e19e 100644 --- a/ios/MapPing.xcodeproj/project.pbxproj +++ b/ios/MapPing.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 665624242067128700F37197 /* HttpService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 665624232067128700F37197 /* HttpService.swift */; }; 6656242820671A7900F37197 /* FailableDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6656242720671A7900F37197 /* FailableDecodable.swift */; }; + 6656242A20671BE300F37197 /* PartType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6656242920671BE300F37197 /* PartType.swift */; }; 940F0B61F668AD88441334C2 /* Pods_MapPing.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 16CD00BB887C10CE3E91B52C /* Pods_MapPing.framework */; }; F1196A76206481B60084672D /* PartAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1196A75206481B60084672D /* PartAnnotationView.swift */; }; F1196A782064857D0084672D /* PartAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1196A772064857D0084672D /* PartAnnotation.swift */; }; @@ -42,6 +43,7 @@ 4BFA57CDB381CDA05A7C2162 /* Pods-MapPing.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MapPing.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MapPing/Pods-MapPing.debug.xcconfig"; sourceTree = ""; }; 665624232067128700F37197 /* HttpService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpService.swift; sourceTree = ""; }; 6656242720671A7900F37197 /* FailableDecodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailableDecodable.swift; sourceTree = ""; }; + 6656242920671BE300F37197 /* PartType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartType.swift; sourceTree = ""; }; E901EFF955E9ACF4A910DAE7 /* Pods-MapPing.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MapPing.release.xcconfig"; path = "Pods/Target Support Files/Pods-MapPing/Pods-MapPing.release.xcconfig"; sourceTree = ""; }; F1196A75206481B60084672D /* PartAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartAnnotationView.swift; sourceTree = ""; }; F1196A772064857D0084672D /* PartAnnotation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartAnnotation.swift; sourceTree = ""; }; @@ -148,6 +150,7 @@ children = ( F1196A8D20653B660084672D /* Part.swift */, 6656242720671A7900F37197 /* FailableDecodable.swift */, + 6656242920671BE300F37197 /* PartType.swift */, ); path = Model; sourceTree = ""; @@ -426,6 +429,7 @@ F19EA0AE2061C5E800FE9A5E /* RootView.swift in Sources */, F19EA0C22061DA7E00FE9A5E /* Stylesheet.swift in Sources */, 6656242820671A7900F37197 /* FailableDecodable.swift in Sources */, + 6656242A20671BE300F37197 /* PartType.swift in Sources */, F1196A92206541700084672D /* ServiceFactory.swift in Sources */, F1196A8420649D580084672D /* PartCellView.swift in Sources */, F19EA0A92061C59B00FE9A5E /* ViewControllerFactory.swift in Sources */, diff --git a/ios/MapPing/Classes/Domain/Model/Part.swift b/ios/MapPing/Classes/Domain/Model/Part.swift index 7a51225..bb3dff9 100644 --- a/ios/MapPing/Classes/Domain/Model/Part.swift +++ b/ios/MapPing/Classes/Domain/Model/Part.swift @@ -19,7 +19,7 @@ struct Part: Codable { let name: String let component: String let notes: String - let type: String + let type: PartType let latitude: Float let longitude: Float let address: String diff --git a/ios/MapPing/Classes/Domain/Model/PartType.swift b/ios/MapPing/Classes/Domain/Model/PartType.swift new file mode 100644 index 0000000..70b4ec9 --- /dev/null +++ b/ios/MapPing/Classes/Domain/Model/PartType.swift @@ -0,0 +1,40 @@ +// +// PartType.swift +// MapPing +// +// Created by Quentin Nolan on 18-03-24. +// Copyright © 2018 Mirego. All rights reserved. +// + +enum PartType: String, Codable { + case battery + case bulb + case cable + case chain + case clutch + case coil + case dial + case disk + case fan + case filter + case fuel + case gear + case generator + case heads + case hose + case mag + case mount + case piston + case plugs + case radiator + case regulator + case reservoir + case scanner + case sensor + case shaft + case sink + case sparkplug + case turbo + case valve + case wheel +} From d7756880d2a4e9292d426697c62fec126265adc0 Mon Sep 17 00:00:00 2001 From: Quentin Nolan Date: Sat, 24 Mar 2018 20:04:01 -0400 Subject: [PATCH 3/7] +Retirer function --- ios/MapPing/Classes/Domain/Service/PartService.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ios/MapPing/Classes/Domain/Service/PartService.swift b/ios/MapPing/Classes/Domain/Service/PartService.swift index e3deee1..62af7ff 100644 --- a/ios/MapPing/Classes/Domain/Service/PartService.swift +++ b/ios/MapPing/Classes/Domain/Service/PartService.swift @@ -28,8 +28,4 @@ class PartService { } } } - - static func getParts(completion: @escaping (_ posts: [Part]?, _ error: Error?) -> ()) { - - } } From 7c05952245a298a7dbbcb425a3f4b576936f0432 Mon Sep 17 00:00:00 2001 From: Quentin Nolan Date: Sat, 24 Mar 2018 20:41:37 -0400 Subject: [PATCH 4/7] +Afficher la liste des parts --- ios/MapPing/Classes/UI/List/ListView.swift | 72 +++++++++++++++++-- .../Classes/UI/List/ListViewController.swift | 2 + .../UI/List/Subviews/PartCellView.swift | 19 ++--- 3 files changed, 80 insertions(+), 13 deletions(-) diff --git a/ios/MapPing/Classes/UI/List/ListView.swift b/ios/MapPing/Classes/UI/List/ListView.swift index a421c66..8decd0d 100644 --- a/ios/MapPing/Classes/UI/List/ListView.swift +++ b/ios/MapPing/Classes/UI/List/ListView.swift @@ -8,15 +8,37 @@ import UIKit class ListView: UIView { - + + private var parts: [Part] = [] private let partCellView = PartCellView() + + fileprivate lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.sectionInset = UIEdgeInsets.zero + layout.minimumInteritemSpacing = 0 + layout.minimumLineSpacing = 0 + return layout + }() + + fileprivate lazy var collectionView: UICollectionView = { + let collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: 0, height: 0), collectionViewLayout: self.collectionViewLayout) + collectionView.backgroundColor = UIColor.clear + collectionView.delegate = self + collectionView.dataSource = self + return collectionView + }() + + let cellMargin: CGFloat = 1 + var cellDimensions: CGSize { + return CGSize(width: UIScreen.main.bounds.width, height: 100) + } init() { super.init(frame: .zero) backgroundColor = .white - - partCellView.configure(partImageName: "part-sensor", title: "Bougie 4W", subTitle: "Moteur principal", coordinates: "46.7552° N, 71.2265° W", distance: "(0.62 km)") - addSubview(partCellView) + + collectionView.register(PartCellView.self, forCellWithReuseIdentifier: PartCellView.reuseIdentifier) + addSubview(collectionView) } required init(coder aDecoder: NSCoder) { @@ -25,6 +47,46 @@ class ListView: UIView { override func layoutSubviews() { super.layoutSubviews() - partCellView.pin.top().horizontally() + + collectionView.frame.origin.x = 0 - cellMargin + collectionView.frame.size.width = self.frame.size.width + cellMargin*2 + collectionView.frame.size.height = self.frame.size.height + collectionView.frame.origin.y = 0 + } + + func configure(parts: [Part]) { + self.parts = parts + collectionView.reloadData() + } +} + +extension ListView: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return parts.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PartCellView.reuseIdentifier, for: indexPath) as! PartCellView + cell.configure(part: parts[indexPath.row]) + return cell + } +} + +extension ListView: UICollectionViewDelegateFlowLayout, UICollectionViewDelegate +{ + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return cellDimensions + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return cellMargin*2 + 1.5 + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return cellMargin*2 + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { + return UIEdgeInsetsMake(cellMargin, cellMargin, cellMargin, cellMargin) // margin between cells } } diff --git a/ios/MapPing/Classes/UI/List/ListViewController.swift b/ios/MapPing/Classes/UI/List/ListViewController.swift index e1daee2..f88272e 100644 --- a/ios/MapPing/Classes/UI/List/ListViewController.swift +++ b/ios/MapPing/Classes/UI/List/ListViewController.swift @@ -39,6 +39,8 @@ class ListViewController: BaseViewController { _ = partService.partsObservable.register { (_, parts) in print("Nb of parts received: \(parts.count)") + + self.mainView.configure(parts: parts) } } } diff --git a/ios/MapPing/Classes/UI/List/Subviews/PartCellView.swift b/ios/MapPing/Classes/UI/List/Subviews/PartCellView.swift index eafa994..90029a4 100644 --- a/ios/MapPing/Classes/UI/List/Subviews/PartCellView.swift +++ b/ios/MapPing/Classes/UI/List/Subviews/PartCellView.swift @@ -7,7 +7,8 @@ import UIKit -class PartCellView: UIView { +class PartCellView: UICollectionViewCell { + static let reuseIdentifier = "PartCellView" private let partImage = UIImageView() private let title = UILabel() @@ -15,8 +16,8 @@ class PartCellView: UIView { private let coordinates = UILabel() private let distance = UILabel() - init() { - super.init(frame: .zero) + override init(frame: CGRect) { + super.init(frame: frame) partImage.backgroundColor = .white partImage.contentMode = .center @@ -55,12 +56,14 @@ class PartCellView: UIView { distance.pin.right(of: coordinates, aligned: .center).marginLeft(6) } - func configure(partImageName: String, title: String, subTitle: String, coordinates: String, distance: String) { - partImage.image = UIImage(named: partImageName) - self.title.setProperties(text: title, fit: true) - self.subTitle.setProperties(text: subTitle, fit: true) + func configure(part: Part) { + let coordinates = "\(part.latitude)° N, \(part.longitude)° W"; + + partImage.image = UIImage(named: "part-\(part.type)") + self.title.setProperties(text: part.name, fit: true) + self.subTitle.setProperties(text: part.component, fit: true) self.coordinates.setProperties(text: coordinates, fit: true) - self.distance.setProperties(text: distance, fit: true) + self.distance.setProperties(text: "(0.62 km)", fit: true) setNeedsLayout() } } From 1d6eafc0124798012844623095d46cb12483c4bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Antoine=20Sauv=C3=A9?= Date: Sat, 24 Mar 2018 20:47:57 -0400 Subject: [PATCH 5/7] Add the callout --- ios/MapPing.xcodeproj/project.pbxproj | 4 ++ ios/MapPing/Classes/Domain/Model/Part.swift | 4 +- ios/MapPing/Classes/UI/Map/MapView.swift | 16 ++++- .../Classes/UI/Map/MapViewController.swift | 2 +- .../Classes/UI/Map/PartAnnotation.swift | 12 +++- .../UI/Map/Subviews/PartAnnotationView.swift | 59 ++++++++++++++++++- .../Subviews/PartClusterAnnotationView.swift | 39 ++++++++++++ 7 files changed, 127 insertions(+), 9 deletions(-) create mode 100644 ios/MapPing/Classes/UI/Map/Subviews/PartClusterAnnotationView.swift diff --git a/ios/MapPing.xcodeproj/project.pbxproj b/ios/MapPing.xcodeproj/project.pbxproj index 0ed5034..08f751e 100644 --- a/ios/MapPing.xcodeproj/project.pbxproj +++ b/ios/MapPing.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 665624242067128700F37197 /* HttpService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 665624232067128700F37197 /* HttpService.swift */; }; 6656242820671A7900F37197 /* FailableDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6656242720671A7900F37197 /* FailableDecodable.swift */; }; + 93695A4C20671488007D1410 /* PartClusterAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93695A4B20671488007D1410 /* PartClusterAnnotationView.swift */; }; 940F0B61F668AD88441334C2 /* Pods_MapPing.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 16CD00BB887C10CE3E91B52C /* Pods_MapPing.framework */; }; F1196A76206481B60084672D /* PartAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1196A75206481B60084672D /* PartAnnotationView.swift */; }; F1196A782064857D0084672D /* PartAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1196A772064857D0084672D /* PartAnnotation.swift */; }; @@ -42,6 +43,7 @@ 4BFA57CDB381CDA05A7C2162 /* Pods-MapPing.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MapPing.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MapPing/Pods-MapPing.debug.xcconfig"; sourceTree = ""; }; 665624232067128700F37197 /* HttpService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpService.swift; sourceTree = ""; }; 6656242720671A7900F37197 /* FailableDecodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailableDecodable.swift; sourceTree = ""; }; + 93695A4B20671488007D1410 /* PartClusterAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartClusterAnnotationView.swift; sourceTree = ""; }; E901EFF955E9ACF4A910DAE7 /* Pods-MapPing.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MapPing.release.xcconfig"; path = "Pods/Target Support Files/Pods-MapPing/Pods-MapPing.release.xcconfig"; sourceTree = ""; }; F1196A75206481B60084672D /* PartAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartAnnotationView.swift; sourceTree = ""; }; F1196A772064857D0084672D /* PartAnnotation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartAnnotation.swift; sourceTree = ""; }; @@ -105,6 +107,7 @@ isa = PBXGroup; children = ( F1196A75206481B60084672D /* PartAnnotationView.swift */, + 93695A4B20671488007D1410 /* PartClusterAnnotationView.swift */, ); path = Subviews; sourceTree = ""; @@ -431,6 +434,7 @@ F19EA0A92061C59B00FE9A5E /* ViewControllerFactory.swift in Sources */, F1196A9020653D4A0084672D /* PartService.swift in Sources */, F19EA0902061C36000FE9A5E /* AppDelegate.swift in Sources */, + 93695A4C20671488007D1410 /* PartClusterAnnotationView.swift in Sources */, F19EA0B32061C6A800FE9A5E /* AugmentedRealityViewController.swift in Sources */, F1196A8E20653B660084672D /* Part.swift in Sources */, F19EA0C02061CB7100FE9A5E /* NavigationButtons.swift in Sources */, diff --git a/ios/MapPing/Classes/Domain/Model/Part.swift b/ios/MapPing/Classes/Domain/Model/Part.swift index 7a51225..e369c48 100644 --- a/ios/MapPing/Classes/Domain/Model/Part.swift +++ b/ios/MapPing/Classes/Domain/Model/Part.swift @@ -20,7 +20,7 @@ struct Part: Codable { let component: String let notes: String let type: String - let latitude: Float - let longitude: Float + let latitude: Double + let longitude: Double let address: String } diff --git a/ios/MapPing/Classes/UI/Map/MapView.swift b/ios/MapPing/Classes/UI/Map/MapView.swift index 9496145..1026af5 100644 --- a/ios/MapPing/Classes/UI/Map/MapView.swift +++ b/ios/MapPing/Classes/UI/Map/MapView.swift @@ -34,7 +34,21 @@ extension MapView: MKMapViewDelegate { guard !(annotation is MKUserLocation), let annotation = annotation as? PartAnnotation else { return nil } let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: PartAnnotationView.reuseIdentifier, for: annotation) as! PartAnnotationView - annotationView.configure(partImageName: annotation.iconName) + annotationView.configure(partAnnotation: annotation) { + print("TODO") + } return annotationView } + + func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) { + guard let annotationView = view as? PartAnnotationView else { return } + + annotationView.didSelect() + } + + func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) { + guard let annotationView = view as? PartAnnotationView else { return } + + annotationView.didDeselect() + } } diff --git a/ios/MapPing/Classes/UI/Map/MapViewController.swift b/ios/MapPing/Classes/UI/Map/MapViewController.swift index ad7d72e..4c5cbd5 100644 --- a/ios/MapPing/Classes/UI/Map/MapViewController.swift +++ b/ios/MapPing/Classes/UI/Map/MapViewController.swift @@ -32,6 +32,6 @@ class MapViewController: BaseViewController { override func viewDidLoad() { super.viewDidLoad() mainView.mapView.setRegion(MKCoordinateRegion(center: quebecCityCoordinate, span: startSpan), animated: false) - mainView.mapView.addAnnotation(PartAnnotation(coordinate: quebecCityCoordinate, iconName: "part-clutch")) + mainView.mapView.addAnnotation(PartAnnotation(part: Part(name: "Clutch 1", component: "Clutch thing", notes: "Note", type: "clutch", latitude: quebecCityCoordinate.latitude, longitude: quebecCityCoordinate.longitude, address: "123 blablabla"))) } } diff --git a/ios/MapPing/Classes/UI/Map/PartAnnotation.swift b/ios/MapPing/Classes/UI/Map/PartAnnotation.swift index 6dbf4c5..e003815 100644 --- a/ios/MapPing/Classes/UI/Map/PartAnnotation.swift +++ b/ios/MapPing/Classes/UI/Map/PartAnnotation.swift @@ -10,9 +10,15 @@ import MapKit class PartAnnotation: NSObject, MKAnnotation { let coordinate: CLLocationCoordinate2D let iconName: String + let name: String + + var title: String? { + return name + } - init(coordinate: CLLocationCoordinate2D, iconName: String) { - self.coordinate = coordinate - self.iconName = iconName + init(part: Part) { + self.coordinate = CLLocationCoordinate2DMake(part.latitude, part.longitude) + iconName = "part-\(part.type)" + self.name = part.name } } diff --git a/ios/MapPing/Classes/UI/Map/Subviews/PartAnnotationView.swift b/ios/MapPing/Classes/UI/Map/Subviews/PartAnnotationView.swift index d881f7a..6d9fc0f 100644 --- a/ios/MapPing/Classes/UI/Map/Subviews/PartAnnotationView.swift +++ b/ios/MapPing/Classes/UI/Map/Subviews/PartAnnotationView.swift @@ -11,14 +11,29 @@ class PartAnnotationView: MKAnnotationView { static let reuseIdentifier = "PartAnnotationView" private let pinImage = UIImageView(image: #imageLiteral(resourceName: "icn-pin")) + private let partCallout = UIView() + private let partName = UILabel() private let partImage = UIImageView() + private let calloutButton = UIButton(type: .detailDisclosure) + + private let calloutDetailsPressed: (() -> ())? = nil override init(annotation: MKAnnotation?, reuseIdentifier: String?) { super.init(annotation: annotation, reuseIdentifier: reuseIdentifier) addSubview(pinImage) + addSubview(partCallout) addSubview(partImage) + partCallout.addSubview(partName) + partCallout.addSubview(calloutButton) + calloutButton.addTarget(self, action: #selector(calloutDetailPressed), for: .touchUpInside) size = pinImage.size + partCallout.isHidden = true + } + + @objc + func calloutDetailPressed() { + calloutDetailsPressed?() } required init?(coder aDecoder: NSCoder) { @@ -28,10 +43,50 @@ class PartAnnotationView: MKAnnotationView { override func layoutSubviews() { super.layoutSubviews() partImage.pin.hCenter().top(11).size(CGSize(width: 22, height: 22)) + partCallout.pin.hCenter().top(-64).size(CGSize(width: 128, height: 48)) + partCallout.setRoundCornersMask() + partCallout.backgroundColor = UIColor(hex: 0x000000, alpha: 0.7) + partName.pin.vCenter().width(100).sizeToFit(.widthFlexible).maxHeight(32).before(of: calloutButton) + partName.textColor = UIColor.white + calloutButton.pin.right(8).vCenter() } - func configure(partImageName: String) { - partImage.image = UIImage(named: partImageName) + func configure(partAnnotation: PartAnnotation, onDetailPressed: (() -> ())? = nil) { + partImage.image = UIImage(named: partAnnotation.iconName) + partName.text = partAnnotation.name setNeedsLayout() } + + func didSelect() { + partCallout.isHidden = false + } + + func didDeselect() { + partCallout.isHidden = true + } + + // Click + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + let hitView = super.hitTest(point, with: event) + if (hitView != nil) { + self.superview?.bringSubview(toFront: self) + } + return hitView; + } + + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + let rect = self.bounds + var isInside = rect.contains(point) + + if(!isInside) { + for view in self.subviews { + isInside = view.frame.contains(point) + break; + } + } + + return isInside + } + } diff --git a/ios/MapPing/Classes/UI/Map/Subviews/PartClusterAnnotationView.swift b/ios/MapPing/Classes/UI/Map/Subviews/PartClusterAnnotationView.swift new file mode 100644 index 0000000..97f2923 --- /dev/null +++ b/ios/MapPing/Classes/UI/Map/Subviews/PartClusterAnnotationView.swift @@ -0,0 +1,39 @@ +// +// PartClusterAnnotationView.swift +// MapPing +// +// Created by Marc-Antoine on 18-03-24. +// Copyright © 2018 Mirego. All rights reserved. +// + +import MapKit + +class PartClusterAnnotationView: MKAnnotationView { + static let reuseIdentifier = "PartAnnotationView" + + private let pinImage = UIImageView(image: #imageLiteral(resourceName: "icn-pin")) + private let partImage = UIImageView() + + override init(annotation: MKAnnotation?, reuseIdentifier: String?) { + super.init(annotation: annotation, reuseIdentifier: reuseIdentifier) + + addSubview(pinImage) + addSubview(partImage) + size = pinImage.size + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + partImage.pin.hCenter().top(11).size(CGSize(width: 22, height: 22)) + } + + func configure(partImageName: String) { + partImage.image = UIImage(named: partImageName) + setNeedsLayout() + } +} + From 9a03ba8d64c3962c44a9d834231af5cc11203929 Mon Sep 17 00:00:00 2001 From: Quentin Nolan Date: Sat, 24 Mar 2018 21:34:10 -0400 Subject: [PATCH 6/7] +Afficher les details --- ios/MapPing.xcodeproj/project.pbxproj | 20 ++++- .../Classes/UI/Details/DetailsView.swift | 80 +++++++++++++++++++ .../UI/Details/DetailsViewController.swift | 42 ++++++++++ ios/MapPing/Classes/UI/List/ListView.swift | 13 ++- .../Classes/UI/List/ListViewController.swift | 5 +- .../Classes/UI/Map/MapViewController.swift | 2 +- 6 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 ios/MapPing/Classes/UI/Details/DetailsView.swift create mode 100644 ios/MapPing/Classes/UI/Details/DetailsViewController.swift diff --git a/ios/MapPing.xcodeproj/project.pbxproj b/ios/MapPing.xcodeproj/project.pbxproj index 0c9ba32..6796d30 100644 --- a/ios/MapPing.xcodeproj/project.pbxproj +++ b/ios/MapPing.xcodeproj/project.pbxproj @@ -9,8 +9,10 @@ /* Begin PBXBuildFile section */ 665624242067128700F37197 /* HttpService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 665624232067128700F37197 /* HttpService.swift */; }; 6656242820671A7900F37197 /* FailableDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6656242720671A7900F37197 /* FailableDecodable.swift */; }; - 93695A4C20671488007D1410 /* PartClusterAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93695A4B20671488007D1410 /* PartClusterAnnotationView.swift */; }; 6656242A20671BE300F37197 /* PartType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6656242920671BE300F37197 /* PartType.swift */; }; + 6656242D20672F4900F37197 /* DetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6656242C20672F4900F37197 /* DetailsView.swift */; }; + 6656242F20672F5600F37197 /* DetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6656242E20672F5600F37197 /* DetailsViewController.swift */; }; + 93695A4C20671488007D1410 /* PartClusterAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93695A4B20671488007D1410 /* PartClusterAnnotationView.swift */; }; 940F0B61F668AD88441334C2 /* Pods_MapPing.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 16CD00BB887C10CE3E91B52C /* Pods_MapPing.framework */; }; F1196A76206481B60084672D /* PartAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1196A75206481B60084672D /* PartAnnotationView.swift */; }; F1196A782064857D0084672D /* PartAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1196A772064857D0084672D /* PartAnnotation.swift */; }; @@ -44,8 +46,10 @@ 4BFA57CDB381CDA05A7C2162 /* Pods-MapPing.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MapPing.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MapPing/Pods-MapPing.debug.xcconfig"; sourceTree = ""; }; 665624232067128700F37197 /* HttpService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpService.swift; sourceTree = ""; }; 6656242720671A7900F37197 /* FailableDecodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailableDecodable.swift; sourceTree = ""; }; - 93695A4B20671488007D1410 /* PartClusterAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartClusterAnnotationView.swift; sourceTree = ""; }; 6656242920671BE300F37197 /* PartType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartType.swift; sourceTree = ""; }; + 6656242C20672F4900F37197 /* DetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsView.swift; sourceTree = ""; }; + 6656242E20672F5600F37197 /* DetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsViewController.swift; sourceTree = ""; }; + 93695A4B20671488007D1410 /* PartClusterAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartClusterAnnotationView.swift; sourceTree = ""; }; E901EFF955E9ACF4A910DAE7 /* Pods-MapPing.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MapPing.release.xcconfig"; path = "Pods/Target Support Files/Pods-MapPing/Pods-MapPing.release.xcconfig"; sourceTree = ""; }; F1196A75206481B60084672D /* PartAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartAnnotationView.swift; sourceTree = ""; }; F1196A772064857D0084672D /* PartAnnotation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartAnnotation.swift; sourceTree = ""; }; @@ -97,6 +101,15 @@ name = Pods; sourceTree = ""; }; + 6656242B20672F0000F37197 /* Details */ = { + isa = PBXGroup; + children = ( + 6656242E20672F5600F37197 /* DetailsViewController.swift */, + 6656242C20672F4900F37197 /* DetailsView.swift */, + ); + path = Details; + sourceTree = ""; + }; 6DAB11BEC4EE23ADF3FF7B54 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -227,6 +240,7 @@ F19EA0A42061C56600FE9A5E /* UI */ = { isa = PBXGroup; children = ( + 6656242B20672F0000F37197 /* Details */, F19EA0B12061C68C00FE9A5E /* AugmentedReality */, F19EA0AA2061C5B400FE9A5E /* Common */, F19EA0AF2061C66800FE9A5E /* Map */, @@ -438,6 +452,8 @@ F19EA0A92061C59B00FE9A5E /* ViewControllerFactory.swift in Sources */, F1196A9020653D4A0084672D /* PartService.swift in Sources */, F19EA0902061C36000FE9A5E /* AppDelegate.swift in Sources */, + 6656242F20672F5600F37197 /* DetailsViewController.swift in Sources */, + 6656242D20672F4900F37197 /* DetailsView.swift in Sources */, 93695A4C20671488007D1410 /* PartClusterAnnotationView.swift in Sources */, F19EA0B32061C6A800FE9A5E /* AugmentedRealityViewController.swift in Sources */, F1196A8E20653B660084672D /* Part.swift in Sources */, diff --git a/ios/MapPing/Classes/UI/Details/DetailsView.swift b/ios/MapPing/Classes/UI/Details/DetailsView.swift new file mode 100644 index 0000000..1d16711 --- /dev/null +++ b/ios/MapPing/Classes/UI/Details/DetailsView.swift @@ -0,0 +1,80 @@ +// +// DetailsView.swift +// MapPing +// +// Created by Quentin Nolan on 18-03-24. +// Copyright © 2018 Mirego. All rights reserved. +// + +import UIKit + +class DetailsView: UIView { + + private let partImage = UIImageView() + private let title = UILabel() + private let subTitle = UILabel() + private let coordinates = UILabel() + private let distance = UILabel() + private let address = UILabel() + private let notes = UILabel() + + init() { + super.init(frame: .zero) + backgroundColor = .white + + partImage.backgroundColor = .white + partImage.contentMode = .center + partImage.size = CGSize(width: 66, height: 66) + partImage.layer.cornerRadius = partImage.height / 2 + partImage.layer.borderColor = UIColor.copper.cgColor + partImage.layer.borderWidth = 4 + addSubview(partImage) + + title.setProperties(font: .leagueSpartanBold(14), textColor: .purpleBrown) + addSubview(title) + + subTitle.setProperties(font: .systemFont(ofSize: 12), textColor: .purpleBrown) + addSubview(subTitle) + + coordinates.setProperties(font: .systemFont(ofSize: 12), textColor: .brownishGrey) + addSubview(coordinates) + + distance.setProperties(font: .italicSystemFont(ofSize: 12), textColor: .brownishGrey) + addSubview(distance) + + address.setProperties(font: .italicSystemFont(ofSize: 12), textColor: .brownishGrey) + addSubview(address) + + notes.setProperties(font: .italicSystemFont(ofSize: 12), textColor: .brownishGrey) + addSubview(notes) + } + + required init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + + partImage.pin.left(15).vCenter() + title.pin.right(of: partImage, aligned: .top).marginLeft(15).marginTop(8) + subTitle.pin.below(of: title, aligned: .left).marginTop(1) + coordinates.pin.below(of: subTitle, aligned: .left).marginTop(5) + distance.pin.right(of: coordinates, aligned: .center).marginLeft(6) + address.pin.below(of: partImage, aligned: .left).marginTop(8) + notes.pin.below(of: address, aligned: .left).marginTop(8) + } + + func configure(part: Part) { + let coordinates = "\(part.latitude)° N, \(part.longitude)° W"; + + partImage.image = UIImage(named: "part-\(part.type)") + self.title.setProperties(text: part.name, fit: true) + self.subTitle.setProperties(text: part.component, fit: true) + self.coordinates.setProperties(text: coordinates, fit: true) + self.distance.setProperties(text: "(0.62 km)", fit: true) + self.coordinates.setProperties(text: coordinates, fit: true) + self.address.setProperties(text: part.address, fit: true) + self.notes.setProperties(text: part.notes, fit: true) + } +} diff --git a/ios/MapPing/Classes/UI/Details/DetailsViewController.swift b/ios/MapPing/Classes/UI/Details/DetailsViewController.swift new file mode 100644 index 0000000..86420ce --- /dev/null +++ b/ios/MapPing/Classes/UI/Details/DetailsViewController.swift @@ -0,0 +1,42 @@ +// +// DetailsViewController.swift +// MapPing +// +// Created by Quentin Nolan on 18-03-24. +// Copyright © 2018 Mirego. All rights reserved. +// + +import UIKit + +class DetailsViewController: BaseViewController { + + private var mainView: DetailsView { + return self.view as! DetailsView + } + + private let part: Part + + init(part: Part) { + self.part = part + super.init(nibName: nil, bundle: nil) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return .lightContent + } + + override func loadView() { + view = DetailsView() + } + + override func viewDidLoad() { + super.viewDidLoad() + + self.mainView.configure(part: part) + } + +} diff --git a/ios/MapPing/Classes/UI/List/ListView.swift b/ios/MapPing/Classes/UI/List/ListView.swift index 8decd0d..6f6a0f3 100644 --- a/ios/MapPing/Classes/UI/List/ListView.swift +++ b/ios/MapPing/Classes/UI/List/ListView.swift @@ -12,6 +12,8 @@ class ListView: UIView { private var parts: [Part] = [] private let partCellView = PartCellView() + fileprivate var calloutDetailsPressed: ((Part) -> ())? = nil + fileprivate lazy var collectionViewLayout: UICollectionViewFlowLayout = { let layout = UICollectionViewFlowLayout() layout.sectionInset = UIEdgeInsets.zero @@ -54,9 +56,14 @@ class ListView: UIView { collectionView.frame.origin.y = 0 } - func configure(parts: [Part]) { + func configure(parts: [Part], onDetailPressed: ((Part) -> ())? = nil) { self.parts = parts collectionView.reloadData() + self.calloutDetailsPressed = onDetailPressed + } + + func calloutDetailPressed(part: Part) { + calloutDetailsPressed?(part) } } @@ -89,4 +96,8 @@ extension ListView: UICollectionViewDelegateFlowLayout, UICollectionViewDelegate func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { return UIEdgeInsetsMake(cellMargin, cellMargin, cellMargin, cellMargin) // margin between cells } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + calloutDetailPressed(part: parts[(indexPath as NSIndexPath).row]); + } } diff --git a/ios/MapPing/Classes/UI/List/ListViewController.swift b/ios/MapPing/Classes/UI/List/ListViewController.swift index f88272e..c5bdd36 100644 --- a/ios/MapPing/Classes/UI/List/ListViewController.swift +++ b/ios/MapPing/Classes/UI/List/ListViewController.swift @@ -40,7 +40,10 @@ class ListViewController: BaseViewController { _ = partService.partsObservable.register { (_, parts) in print("Nb of parts received: \(parts.count)") - self.mainView.configure(parts: parts) + self.mainView.configure(parts: parts) { (part) in + let detailsViewControlller = DetailsViewController(part: part) + self.navigationController?.pushViewController(detailsViewControlller, animated: true) + } } } } diff --git a/ios/MapPing/Classes/UI/Map/MapViewController.swift b/ios/MapPing/Classes/UI/Map/MapViewController.swift index 4c5cbd5..5063106 100644 --- a/ios/MapPing/Classes/UI/Map/MapViewController.swift +++ b/ios/MapPing/Classes/UI/Map/MapViewController.swift @@ -32,6 +32,6 @@ class MapViewController: BaseViewController { override func viewDidLoad() { super.viewDidLoad() mainView.mapView.setRegion(MKCoordinateRegion(center: quebecCityCoordinate, span: startSpan), animated: false) - mainView.mapView.addAnnotation(PartAnnotation(part: Part(name: "Clutch 1", component: "Clutch thing", notes: "Note", type: "clutch", latitude: quebecCityCoordinate.latitude, longitude: quebecCityCoordinate.longitude, address: "123 blablabla"))) + //mainView.mapView.addAnnotation(PartAnnotation(part: Part(name: "Clutch 1", component: "Clutch thing", notes: "Note", type: "clutch", latitude: quebecCityCoordinate.latitude, longitude: quebecCityCoordinate.longitude, address: "123 blablabla"))) } } From bd08a80ed26a7d219968bc8cc8049f92b66ae2a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Antoine=20Sauv=C3=A9?= Date: Sat, 24 Mar 2018 21:35:40 -0400 Subject: [PATCH 7/7] Fix callout text --- ios/MapPing.xcodeproj/project.pbxproj | 4 +-- .../Classes/UI/List/ListViewController.swift | 4 +-- ios/MapPing/Classes/UI/Map/MapView.swift | 8 +++-- .../Classes/UI/Map/MapViewController.swift | 15 ++++++++-- .../UI/Map/Subviews/PartAnnotationView.swift | 29 ++----------------- .../Classes/UI/ViewControllerFactory.swift | 2 +- 6 files changed, 27 insertions(+), 35 deletions(-) diff --git a/ios/MapPing.xcodeproj/project.pbxproj b/ios/MapPing.xcodeproj/project.pbxproj index 0c9ba32..67efff6 100644 --- a/ios/MapPing.xcodeproj/project.pbxproj +++ b/ios/MapPing.xcodeproj/project.pbxproj @@ -9,8 +9,8 @@ /* Begin PBXBuildFile section */ 665624242067128700F37197 /* HttpService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 665624232067128700F37197 /* HttpService.swift */; }; 6656242820671A7900F37197 /* FailableDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6656242720671A7900F37197 /* FailableDecodable.swift */; }; - 93695A4C20671488007D1410 /* PartClusterAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93695A4B20671488007D1410 /* PartClusterAnnotationView.swift */; }; 6656242A20671BE300F37197 /* PartType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6656242920671BE300F37197 /* PartType.swift */; }; + 93695A4C20671488007D1410 /* PartClusterAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93695A4B20671488007D1410 /* PartClusterAnnotationView.swift */; }; 940F0B61F668AD88441334C2 /* Pods_MapPing.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 16CD00BB887C10CE3E91B52C /* Pods_MapPing.framework */; }; F1196A76206481B60084672D /* PartAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1196A75206481B60084672D /* PartAnnotationView.swift */; }; F1196A782064857D0084672D /* PartAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1196A772064857D0084672D /* PartAnnotation.swift */; }; @@ -44,8 +44,8 @@ 4BFA57CDB381CDA05A7C2162 /* Pods-MapPing.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MapPing.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MapPing/Pods-MapPing.debug.xcconfig"; sourceTree = ""; }; 665624232067128700F37197 /* HttpService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpService.swift; sourceTree = ""; }; 6656242720671A7900F37197 /* FailableDecodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailableDecodable.swift; sourceTree = ""; }; - 93695A4B20671488007D1410 /* PartClusterAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartClusterAnnotationView.swift; sourceTree = ""; }; 6656242920671BE300F37197 /* PartType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartType.swift; sourceTree = ""; }; + 93695A4B20671488007D1410 /* PartClusterAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartClusterAnnotationView.swift; sourceTree = ""; }; E901EFF955E9ACF4A910DAE7 /* Pods-MapPing.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MapPing.release.xcconfig"; path = "Pods/Target Support Files/Pods-MapPing/Pods-MapPing.release.xcconfig"; sourceTree = ""; }; F1196A75206481B60084672D /* PartAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartAnnotationView.swift; sourceTree = ""; }; F1196A772064857D0084672D /* PartAnnotation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PartAnnotation.swift; sourceTree = ""; }; diff --git a/ios/MapPing/Classes/UI/List/ListViewController.swift b/ios/MapPing/Classes/UI/List/ListViewController.swift index f88272e..67ab004 100644 --- a/ios/MapPing/Classes/UI/List/ListViewController.swift +++ b/ios/MapPing/Classes/UI/List/ListViewController.swift @@ -37,10 +37,10 @@ class ListViewController: BaseViewController { override func viewDidLoad() { super.viewDidLoad() - _ = partService.partsObservable.register { (_, parts) in + _ = partService.partsObservable.register { [weak self] (_, parts) in print("Nb of parts received: \(parts.count)") - self.mainView.configure(parts: parts) + self?.mainView.configure(parts: parts) } } } diff --git a/ios/MapPing/Classes/UI/Map/MapView.swift b/ios/MapPing/Classes/UI/Map/MapView.swift index 1026af5..d3c7116 100644 --- a/ios/MapPing/Classes/UI/Map/MapView.swift +++ b/ios/MapPing/Classes/UI/Map/MapView.swift @@ -32,18 +32,22 @@ class MapView: UIView { extension MapView: MKMapViewDelegate { func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { guard !(annotation is MKUserLocation), let annotation = annotation as? PartAnnotation else { return nil } - + let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: PartAnnotationView.reuseIdentifier, for: annotation) as! PartAnnotationView annotationView.configure(partAnnotation: annotation) { - print("TODO") + // TODO } return annotationView + } func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) { guard let annotationView = view as? PartAnnotationView else { return } annotationView.didSelect() + if let location = annotationView.annotation?.coordinate { + mapView.setCenter(location, animated: true) + } } func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) { diff --git a/ios/MapPing/Classes/UI/Map/MapViewController.swift b/ios/MapPing/Classes/UI/Map/MapViewController.swift index 4c5cbd5..2f0a467 100644 --- a/ios/MapPing/Classes/UI/Map/MapViewController.swift +++ b/ios/MapPing/Classes/UI/Map/MapViewController.swift @@ -16,7 +16,10 @@ class MapViewController: BaseViewController { return self.view as! MapView } - init() { + private let partService: PartService + + init(partService: PartService) { + self.partService = partService super.init(nibName: nil, bundle: nil) navigationIcon = #imageLiteral(resourceName: "icn-map") } @@ -32,6 +35,14 @@ class MapViewController: BaseViewController { override func viewDidLoad() { super.viewDidLoad() mainView.mapView.setRegion(MKCoordinateRegion(center: quebecCityCoordinate, span: startSpan), animated: false) - mainView.mapView.addAnnotation(PartAnnotation(part: Part(name: "Clutch 1", component: "Clutch thing", notes: "Note", type: "clutch", latitude: quebecCityCoordinate.latitude, longitude: quebecCityCoordinate.longitude, address: "123 blablabla"))) + + _ = partService.partsObservable.register { [weak self] (_, parts) in + let partAnnotations = parts.map { + PartAnnotation(part: $0) + } + for part in partAnnotations { + self?.mainView.mapView.addAnnotation(part) + } + } } } diff --git a/ios/MapPing/Classes/UI/Map/Subviews/PartAnnotationView.swift b/ios/MapPing/Classes/UI/Map/Subviews/PartAnnotationView.swift index 6d9fc0f..713e18e 100644 --- a/ios/MapPing/Classes/UI/Map/Subviews/PartAnnotationView.swift +++ b/ios/MapPing/Classes/UI/Map/Subviews/PartAnnotationView.swift @@ -24,6 +24,7 @@ class PartAnnotationView: MKAnnotationView { addSubview(pinImage) addSubview(partCallout) addSubview(partImage) + partName.adjustsFontSizeToFitWidth = true partCallout.addSubview(partName) partCallout.addSubview(calloutButton) calloutButton.addTarget(self, action: #selector(calloutDetailPressed), for: .touchUpInside) @@ -43,10 +44,10 @@ class PartAnnotationView: MKAnnotationView { override func layoutSubviews() { super.layoutSubviews() partImage.pin.hCenter().top(11).size(CGSize(width: 22, height: 22)) - partCallout.pin.hCenter().top(-64).size(CGSize(width: 128, height: 48)) + partCallout.pin.hCenter().top(-64).size(CGSize(width: 258, height: 48)) partCallout.setRoundCornersMask() partCallout.backgroundColor = UIColor(hex: 0x000000, alpha: 0.7) - partName.pin.vCenter().width(100).sizeToFit(.widthFlexible).maxHeight(32).before(of: calloutButton) + partName.pin.vCenter().start(8).height(32).before(of: calloutButton).marginRight(8) partName.textColor = UIColor.white calloutButton.pin.right(8).vCenter() } @@ -64,29 +65,5 @@ class PartAnnotationView: MKAnnotationView { func didDeselect() { partCallout.isHidden = true } - - // Click - - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - let hitView = super.hitTest(point, with: event) - if (hitView != nil) { - self.superview?.bringSubview(toFront: self) - } - return hitView; - } - - override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { - let rect = self.bounds - var isInside = rect.contains(point) - - if(!isInside) { - for view in self.subviews { - isInside = view.frame.contains(point) - break; - } - } - - return isInside - } } diff --git a/ios/MapPing/Classes/UI/ViewControllerFactory.swift b/ios/MapPing/Classes/UI/ViewControllerFactory.swift index 21788a4..168d4e5 100644 --- a/ios/MapPing/Classes/UI/ViewControllerFactory.swift +++ b/ios/MapPing/Classes/UI/ViewControllerFactory.swift @@ -19,7 +19,7 @@ class ViewControllerFactory { } private func mapViewController() -> MapViewController { - return assign(MapViewController()) + return assign(MapViewController(partService: serviceFactory.partService())) } private func listViewController() -> NavigationViewController {