Begin implementing Pad details view.

This commit is contained in:
CypherPoet 2020-01-28 20:10:15 -06:00
parent 8faf051801
commit 922337f7e6
17 changed files with 628 additions and 16 deletions

View File

@ -9,6 +9,13 @@
/* Begin PBXBuildFile section */
F32ED61823DF69E6006A5195 /* PadsListContainerView+WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32ED61723DF69E6006A5195 /* PadsListContainerView+WelcomeView.swift */; };
F32ED61A23DF91C1006A5195 /* Pad+PadType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32ED61923DF91C1006A5195 /* Pad+PadType.swift */; };
F32ED61D23DF9B46006A5195 /* PadDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32ED61C23DF9B46006A5195 /* PadDetailsView.swift */; };
F32ED61F23DF9B7C006A5195 /* PadDetailsView+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32ED61E23DF9B7C006A5195 /* PadDetailsView+ViewModel.swift */; };
F32ED62223DFB836006A5195 /* NumberFormatters.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32ED62123DFB836006A5195 /* NumberFormatters.swift */; };
F32ED62423DFC7D5006A5195 /* Pad+SnapshotUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32ED62323DFC7D5006A5195 /* Pad+SnapshotUtils.swift */; };
F32ED62623E0D525006A5195 /* Pad+Computeds.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32ED62523E0D525006A5195 /* Pad+Computeds.swift */; };
F32ED62923E0F0C6006A5195 /* MapSnapshottingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32ED62823E0F0C6006A5195 /* MapSnapshottingService.swift */; };
F32ED62B23E0F0F1006A5195 /* MapSnapshotServicing.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32ED62A23E0F0F1006A5195 /* MapSnapshotServicing.swift */; };
F331C45C23DDB0AE0061925E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331C45B23DDB0AE0061925E /* AppDelegate.swift */; };
F331C45E23DDB0AE0061925E /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F331C45D23DDB0AE0061925E /* SceneDelegate.swift */; };
F331C46223DDB0B00061925E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F331C46123DDB0B00061925E /* Assets.xcassets */; };
@ -33,6 +40,13 @@
/* Begin PBXFileReference section */
F32ED61723DF69E6006A5195 /* PadsListContainerView+WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PadsListContainerView+WelcomeView.swift"; sourceTree = "<group>"; };
F32ED61923DF91C1006A5195 /* Pad+PadType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Pad+PadType.swift"; sourceTree = "<group>"; };
F32ED61C23DF9B46006A5195 /* PadDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PadDetailsView.swift; sourceTree = "<group>"; };
F32ED61E23DF9B7C006A5195 /* PadDetailsView+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PadDetailsView+ViewModel.swift"; sourceTree = "<group>"; };
F32ED62123DFB836006A5195 /* NumberFormatters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberFormatters.swift; sourceTree = "<group>"; };
F32ED62323DFC7D5006A5195 /* Pad+SnapshotUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Pad+SnapshotUtils.swift"; sourceTree = "<group>"; };
F32ED62523E0D525006A5195 /* Pad+Computeds.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Pad+Computeds.swift"; sourceTree = "<group>"; };
F32ED62823E0F0C6006A5195 /* MapSnapshottingService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapSnapshottingService.swift; sourceTree = "<group>"; };
F32ED62A23E0F0F1006A5195 /* MapSnapshotServicing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapSnapshotServicing.swift; sourceTree = "<group>"; };
F331C45823DDB0AE0061925E /* PadFinder.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PadFinder.app; sourceTree = BUILT_PRODUCTS_DIR; };
F331C45B23DDB0AE0061925E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
F331C45D23DDB0AE0061925E /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
@ -67,6 +81,32 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
F32ED61B23DF9B36006A5195 /* Pad Details */ = {
isa = PBXGroup;
children = (
F32ED61C23DF9B46006A5195 /* PadDetailsView.swift */,
F32ED61E23DF9B7C006A5195 /* PadDetailsView+ViewModel.swift */,
);
path = "Pad Details";
sourceTree = "<group>";
};
F32ED62023DFB7E9006A5195 /* Formatters */ = {
isa = PBXGroup;
children = (
F32ED62123DFB836006A5195 /* NumberFormatters.swift */,
);
path = Formatters;
sourceTree = "<group>";
};
F32ED62723E0F0B8006A5195 /* Services */ = {
isa = PBXGroup;
children = (
F32ED62823E0F0C6006A5195 /* MapSnapshottingService.swift */,
F32ED62A23E0F0F1006A5195 /* MapSnapshotServicing.swift */,
);
path = Services;
sourceTree = "<group>";
};
F331C44F23DDB0AE0061925E = {
isa = PBXGroup;
children = (
@ -126,6 +166,8 @@
F331C47123DDB0FF0061925E /* Reusables */ = {
isa = PBXGroup;
children = (
F32ED62723E0F0B8006A5195 /* Services */,
F32ED62023DFB7E9006A5195 /* Formatters */,
);
path = Reusables;
sourceTree = "<group>";
@ -164,6 +206,7 @@
isa = PBXGroup;
children = (
F331C47B23DDB1710061925E /* PadsListContainerView.swift */,
F32ED61B23DF9B36006A5195 /* Pad Details */,
F32ED61723DF69E6006A5195 /* PadsListContainerView+WelcomeView.swift */,
F331C49B23DE18650061925E /* PadsListView.swift */,
F331C49D23DE187A0061925E /* PadsListView+ViewModel.swift */,
@ -192,6 +235,8 @@
isa = PBXGroup;
children = (
F331C48823DDDF0E0061925E /* Pad.swift */,
F32ED62323DFC7D5006A5195 /* Pad+SnapshotUtils.swift */,
F32ED62523E0D525006A5195 /* Pad+Computeds.swift */,
F32ED61923DF91C1006A5195 /* Pad+PadType.swift */,
);
path = Pad;
@ -287,11 +332,17 @@
files = (
F331C47C23DDB1710061925E /* PadsListContainerView.swift in Sources */,
F331C48523DDDE710061925E /* AppState.swift in Sources */,
F32ED62923E0F0C6006A5195 /* MapSnapshottingService.swift in Sources */,
F32ED61F23DF9B7C006A5195 /* PadDetailsView+ViewModel.swift in Sources */,
F331C49C23DE18650061925E /* PadsListView.swift in Sources */,
F32ED61823DF69E6006A5195 /* PadsListContainerView+WelcomeView.swift in Sources */,
F331C48923DDDF0E0061925E /* Pad.swift in Sources */,
F331C49623DE0FED0061925E /* PreviewData.swift in Sources */,
F32ED62423DFC7D5006A5195 /* Pad+SnapshotUtils.swift in Sources */,
F331C48C23DDE6A90061925E /* LaunchLibraryAPIService.swift in Sources */,
F32ED62B23E0F0F1006A5195 /* MapSnapshotServicing.swift in Sources */,
F32ED62623E0D525006A5195 /* Pad+Computeds.swift in Sources */,
F32ED62223DFB836006A5195 /* NumberFormatters.swift in Sources */,
F331C49E23DE187A0061925E /* PadsListView+ViewModel.swift in Sources */,
F331C45C23DDB0AE0061925E /* AppDelegate.swift in Sources */,
F331C48323DDDE570061925E /* CurrentApplication.swift in Sources */,
@ -301,6 +352,7 @@
F32ED61A23DF91C1006A5195 /* Pad+PadType.swift in Sources */,
F331C49823DE10010061925E /* PreviewData+AppStore.swift in Sources */,
F331C48723DDDEC30061925E /* PadsState.swift in Sources */,
F32ED61D23DF9B46006A5195 /* PadDetailsView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -15,8 +15,8 @@
"repositoryURL": "https://github.com/CypherPoet/CypherPoetSwiftUIKit.git",
"state": {
"branch": null,
"revision": "65d4268cddbedfeabe44cd625b35bd1812a37587",
"version": "0.0.33"
"revision": "dca4353f34bdf5a622fd5fc7c31dc72ad377194a",
"version": "0.0.35"
}
}
]

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1130"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F331C45723DDB0AE0061925E"
BuildableName = "PadFinder.app"
BlueprintName = "PadFinder"
ReferencedContainer = "container:PadFinder.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F331C45723DDB0AE0061925E"
BuildableName = "PadFinder.app"
BlueprintName = "PadFinder"
ReferencedContainer = "container:PadFinder.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F331C45723DDB0AE0061925E"
BuildableName = "PadFinder.app"
BlueprintName = "PadFinder"
ReferencedContainer = "container:PadFinder.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,19 @@
//
// Pad+Computeds.swift
// PadFinder
//
// Created by CypherPoet on 1/28/20.
//
//
import Foundation
import CoreLocation
extension Pad {
var coordinate: CLLocationCoordinate2D {
.init(latitude: latitude, longitude: longitude)
}
}

View File

@ -0,0 +1,27 @@
//
// Pad+SnapshotUtils.swift
// PadFinder
//
// Created by CypherPoet on 1/27/20.
//
//
import Foundation
import MapKit
extension Pad {
var baseSnapshotOptions: MKMapSnapshotter.Options {
let options = MKMapSnapshotter.Options()
options.mapType = .standard
options.scale = UIScreen.main.scale
options.showsBuildings = true
// options.pointOfInterestFilter = .init(including: [.airport, .cafe])
options.pointOfInterestFilter = .includingAll
return options
}
}

View File

@ -15,10 +15,11 @@ struct Pad {
var name: String
var padType: PadType
var mapURL: URL
var wikiURL: URL?
var latitude: CLLocationDegrees
var longitude: CLLocationDegrees
var isRetired: Bool
var infoURLs: [URL]
var infoURLs: [URL]?
}
@ -42,6 +43,7 @@ extension Pad: Decodable {
case name = "name"
case padType = "padType"
case mapURL = "mapURL"
case wikiURL = "wikiURL"
case latitude = "latitude"
case longitude = "longitude"
case isRetired = "retired"
@ -64,6 +66,14 @@ extension Pad: Decodable {
// print("failed to make URL from string \(mapURLString)")
// }
if let wikiURLString = try? container
.decode(String.self, forKey: .wikiURL)
.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
{
wikiURL = URL(string: wikiURLString)
}
let latitudeString = try container.decode(String.self, forKey: .latitude)
latitude = CLLocationDegrees(latitudeString) ?? 0.0
@ -73,7 +83,7 @@ extension Pad: Decodable {
let isRetiredInt = try container.decode(Int.self, forKey: .isRetired)
isRetired = Pad.isRetired(int: isRetiredInt)
infoURLs = try container.decodeIfPresent([URL].self, forKey: .infoURLs) ?? []
infoURLs = try container.decodeIfPresent([URL].self, forKey: .infoURLs)
}
}

View File

@ -16,7 +16,6 @@ extension PreviewData {
id: 166,
name: "Rocket Lab Launch Complex 1",
padType: .landing,
// mapURL: URL(string: #"https:\/\/twitter.com\/rocketlab"#.replacingOccurrences(of: #"\"#, with: ""))!,
mapURL: URL(
string: #"https:\/\/www.google.ee\/maps\/place\/39°15'46.2\"S+177°51'52.1\"E\/"#
.replacingOccurrences(of: #"\"#, with: "")

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "CanaveralMapSample.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,22 @@
//
// Double+CoordinateFormat.swift
// PadFinder
//
// Created by CypherPoet on 1/27/20.
//
//
import Foundation
enum NumberFormatters {
static var padCoordinateDisplay: NumberFormatter {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 4
return formatter
}
}

View File

@ -0,0 +1,62 @@
//
// MapSnapshotServicing.swift
// PadFinder
//
// Created by CypherPoet on 1/28/20.
//
//
import SwiftUI
import MapKit
import Combine
protocol MapSnapshotServicing: class {
var snapshotOptions: MKMapSnapshotter.Options { get }
var queue: DispatchQueue { get }
func takeSnapshot(
with size: CGSize,
at coordinate: CLLocationCoordinate2D,
latitudeSpan: CLLocationDegrees,
longitudeSpan: CLLocationDegrees
) -> Future<MKMapSnapshotter.Snapshot, Error>
}
extension MapSnapshotServicing {
func takeSnapshot(
with size: CGSize,
at coordinate: CLLocationCoordinate2D,
latitudeSpan: CLLocationDegrees = 0.15,
longitudeSpan: CLLocationDegrees = 0.15
) -> Future<MKMapSnapshotter.Snapshot, Error> {
let span = MKCoordinateSpan(latitudeDelta: latitudeSpan, longitudeDelta: longitudeSpan)
snapshotOptions.region = MKCoordinateRegion(
center: coordinate,
span: span
)
snapshotOptions.size = size
let snapshotter = MKMapSnapshotter(options: snapshotOptions)
return Future { promise in
snapshotter.start(with: self.queue) { (snapshot, error) in
guard error == nil else {
return promise(.failure(error!))
}
guard let snapshot = snapshot else {
preconditionFailure("No snapshot returned despite snapshotter completing without error.")
}
return promise(.success(snapshot))
}
}
}
}

View File

@ -0,0 +1,25 @@
//
// MapSnapshottingService.swift
// PadFinder
//
// Created by CypherPoet on 1/28/20.
//
//
import Foundation
import MapKit
final class MapSnapshottingService: MapSnapshotServicing {
var snapshotOptions: MKMapSnapshotter.Options
var queue: DispatchQueue
init(
snapshotOptions: MKMapSnapshotter.Options = .init(),
queue: DispatchQueue = DispatchQueue(label: "Map Snapshotting Service", qos: .default)
) {
self.snapshotOptions = snapshotOptions
self.queue = queue
}
}

View File

@ -0,0 +1,125 @@
//
// PadDetailsView+ViewModel.swift
// PadFinder
//
// Created by CypherPoet on 1/27/20.
//
//
import SwiftUI
import MapKit
import Combine
extension PadDetailsView {
final class ViewModel: ObservableObject {
private var subscriptions = Set<AnyCancellable>()
private let pad: Pad
private let snapshotService: MapSnapshotServicing
// MARK: - Published Outputs
@Published var mapSnapshotImage: UIImage?
// MARK: - Init
init(
pad: Pad,
snapshotService: MapSnapshotServicing
) {
self.pad = pad
self.snapshotService = snapshotService
setupSubscribers()
}
}
}
// MARK: - Publishers
extension PadDetailsView.ViewModel {
}
// MARK: - Computeds
extension PadDetailsView.ViewModel {
// TODO: Replace with MapKit Snapshot
var mapImageName: String {
"CanaveralMapSample"
}
var padNameText: String {
pad.name
}
var latitudeText: String {
NumberFormatters.padCoordinateDisplay.string(for: pad.latitude) ?? ""
}
var longitudeText: String {
NumberFormatters.padCoordinateDisplay.string(for: pad.longitude) ?? ""
}
var wikipediaURL: URL? {
pad.wikiURL
}
var webLinkData: [(hostName: String, url: URL)] {
([wikipediaURL] + (pad.infoURLs ?? [URL?]()))
.compactMap { url in
guard
let url = url,
let hostName = url.host
else { return nil }
return (hostName: strippedHostName(from: hostName), url: url)
}
}
}
// MARK: - Public Methods
extension PadDetailsView.ViewModel {
func takeMapSnapshot(with size: CGSize) {
snapshotService
.takeSnapshot(with: size, at: pad.coordinate)
.assertNoFailure()
.print("takeMapSnapshot")
.map(\.image)
.receive(on: DispatchQueue.main)
.sink(
receiveValue: { self.mapSnapshotImage = $0 }
)
.store(in: &subscriptions)
}
}
// MARK: - Private Helpers
private extension PadDetailsView.ViewModel {
func strippedHostName(from hostNameString: String) -> String {
hostNameString
.replacingOccurrences(of: "www.", with: "")
.replacingOccurrences(of: "\\.(.*)", with: "", options: .regularExpression)
.capitalized
}
func setupSubscribers() {
// snapshotImagePublisher
// .receive(on: DispatchQueue.main)
// .sink(
// receiveValue: { self.mapSnapshotImage = $0 }
// )
// .store(in: &subscriptions)
}
}

View File

@ -0,0 +1,127 @@
//
// PadDetailsView.swift
// PadFinder
//
// Created by CypherPoet on 1/27/20.
//
//
import SwiftUI
import MapKit
import CypherPoetSwiftUIKit
struct PadDetailsView {
@ObservedObject var viewModel: ViewModel
}
// MARK: - View
extension PadDetailsView: View {
var body: some View {
GeometryReader { geometry in
VStack {
if self.viewModel.mapSnapshotImage != nil {
Image(uiImage: self.viewModel.mapSnapshotImage!)
.resizable()
.scaledToFit()
}
Group {
self.coordinateHeader
VStack(alignment: .leading) {
if self.viewModel.webLinkData.isEmpty == false {
self.linksSection
}
}
}
.padding(.horizontal)
}
.onAppear {
self.viewModel.takeMapSnapshot(
with: CGSize(
width: UIScreen.main.bounds.width,
height: UIScreen.main.bounds.width * 0.75
)
)
}
.embedInScrollView(axes: .vertical)
}
.navigationBarTitle(Text(viewModel.padNameText), displayMode: .inline)
}
}
// MARK: - Computeds
extension PadDetailsView {
}
// MARK: - View Variables
extension PadDetailsView {
private var coordinateHeader: some View {
HStack(spacing: 12) {
Image(systemName: "mappin")
.padding()
.foregroundColor(.white)
.background(Color.red)
.clipShape(Circle())
Group {
Text("Lat: ").fontWeight(.bold)
+ Text(self.viewModel.latitudeText)
Text("Lon: ").fontWeight(.bold)
+ Text(self.viewModel.longitudeText)
}
.embedInCompactableStack()
}
.padding(.top)
}
private var linksSection: some View {
Section(
header: Text("Links").font(.headline)
) {
List(viewModel.webLinkData, id: \.0) { linkItem in
Button(action: {
UIApplication.shared.open(linkItem.url)
}) {
Text(linkItem.hostName)
.foregroundColor(.accentColor)
}
}
}
}
}
// MARK: - Private Helpers
private extension PadDetailsView {
}
// MARK: - Preview
struct PadDetailsView_Previews: PreviewProvider {
static var previews: some View {
let snapshotOptions = PreviewData.Pads.pad1.baseSnapshotOptions
return NavigationView {
PadDetailsView(
viewModel: .init(
pad: PreviewData.Pads.pad1,
snapshotService: MapSnapshottingService(
snapshotOptions: snapshotOptions
)
)
)
}
}
}

View File

@ -7,6 +7,7 @@
//
import SwiftUI
import MapKit
struct PadsListContainerView {
@ -19,7 +20,10 @@ extension PadsListContainerView: View {
var body: some View {
NavigationView {
PadsListView(viewModel: .init(padsState: padsState))
PadsListView(
viewModel: .init(padsState: padsState),
buildDestination: buildDestination(forPad:)
)
.navigationBarTitle("Launch Pads")
WelcomeView()
@ -47,9 +51,52 @@ private extension PadsListContainerView {
func fetchPads() {
store.send(PadsSideEffect.fetchPads)
}
// func makeSnapshotter(for pad: Pad) -> MKMapSnapshotter {
// let snapshotOptions = pad.baseSnapshotOptions
//
// snapshotOptions.size = CGSize(width: 200, height: 200)
//
//
// return MKMapSnapshotter(options: snapshotOptions)
// }
func makeSnapshotOptions(for pad: Pad) -> MKMapSnapshotter.Options {
let snapshotOptions = pad.baseSnapshotOptions
return snapshotOptions
}
// func makeSnapshotService(for pad: Pad) -> MKMapSnapshotter {
// let service = MapSnapshottingService.shared
// let snapshotOptions = pad.baseSnapshotOptions
//
// snapshotOptions.size = CGSize(width: 300, height: 300)
//
//
// return MKMapSnapshotter(options: snapshotOptions)
// }
//
func buildDestination(forPad pad: Pad) -> some View {
PadDetailsView(
viewModel: .init(
pad: pad,
snapshotService: MapSnapshottingService(
// snapshotter: makeSnapshotter(for: pad)
snapshotOptions: makeSnapshotOptions(for: pad)
)
)
)
}
}
// MARK: - Preview
struct PadsListContainerView_Previews: PreviewProvider {
@ -57,11 +104,7 @@ struct PadsListContainerView_Previews: PreviewProvider {
static var previews: some View {
let store = PreviewData.AppStores.withPads
return PadsListContainerView(
// viewModel: .init(
// padsState: store.state.padsState
// )
)
return PadsListContainerView()
.environmentObject(store)
}
}

View File

@ -9,10 +9,11 @@
import SwiftUI
struct PadsListView {
struct PadsListView<Destination: View> {
@EnvironmentObject private var store: AppStore
@ObservedObject var viewModel: ViewModel
let buildDestination: ((Pad) -> Destination)
}
@ -21,7 +22,7 @@ extension PadsListView: View {
var body: some View {
List(viewModel.pads) { pad in
NavigationLink(destination: Text(pad.name)) {
NavigationLink(destination: self.buildDestination(pad)) {
HStack {
pad.padType.listItemImage
@ -64,7 +65,8 @@ struct PadsListView_Previews: PreviewProvider {
let store = PreviewData.AppStores.withPads
return PadsListView(
viewModel: .init(padsState: store.state.padsState)
viewModel: .init(padsState: store.state.padsState),
buildDestination: { _ in EmptyView() }
)
.environmentObject(store)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB