Complete Day 77 Challenge
> Your goal is to build an app that asks users to import a picture from their photo library, then attach a name to whatever they imported.
This commit is contained in:
parent
ddb4f20990
commit
b74bdfc9cd
|
@ -22,6 +22,8 @@
|
|||
F36D295F239A071F00095B66 /* CoreDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F36D295E239A071F00095B66 /* CoreDataManager.swift */; };
|
||||
F36D2965239A0ACA00095B66 /* LocationCollectionMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F36D2963239A0ACA00095B66 /* LocationCollectionMapView.swift */; };
|
||||
F36D2966239A0ACA00095B66 /* LocationCollectionMapView+Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F36D2964239A0ACA00095B66 /* LocationCollectionMapView+Coordinator.swift */; };
|
||||
F37071DA23C3E9B900FAC6AB /* UIImagePickerWrapper+Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F37071D823C3E9B900FAC6AB /* UIImagePickerWrapper+Coordinator.swift */; };
|
||||
F37071DB23C3E9B900FAC6AB /* UIImagePickerWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = F37071D923C3E9B900FAC6AB /* UIImagePickerWrapper.swift */; };
|
||||
F374DC0A239A431E0016CC65 /* LocationCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F374DC09239A431E0016CC65 /* LocationCollectionView.swift */; };
|
||||
F374DC14239A4B2C0016CC65 /* Location+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = F374DC12239A4B2B0016CC65 /* Location+CoreDataClass.swift */; };
|
||||
F374DC15239A4B2C0016CC65 /* Location+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = F374DC13239A4B2B0016CC65 /* Location+CoreDataProperties.swift */; };
|
||||
|
@ -76,6 +78,8 @@
|
|||
F36D295E239A071F00095B66 /* CoreDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataManager.swift; sourceTree = "<group>"; };
|
||||
F36D2963239A0ACA00095B66 /* LocationCollectionMapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationCollectionMapView.swift; sourceTree = "<group>"; };
|
||||
F36D2964239A0ACA00095B66 /* LocationCollectionMapView+Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LocationCollectionMapView+Coordinator.swift"; sourceTree = "<group>"; };
|
||||
F37071D823C3E9B900FAC6AB /* UIImagePickerWrapper+Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImagePickerWrapper+Coordinator.swift"; sourceTree = "<group>"; };
|
||||
F37071D923C3E9B900FAC6AB /* UIImagePickerWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImagePickerWrapper.swift; sourceTree = "<group>"; };
|
||||
F374DC09239A431E0016CC65 /* LocationCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationCollectionView.swift; sourceTree = "<group>"; };
|
||||
F374DC12239A4B2B0016CC65 /* Location+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Location+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
F374DC13239A4B2B0016CC65 /* Location+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Location+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
|
@ -220,6 +224,7 @@
|
|||
F36D2961239A093D00095B66 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F37071D623C3E96400FAC6AB /* UIView Wrappers */,
|
||||
F36D2962239A0A6900095B66 /* LocationCollectionMapView */,
|
||||
);
|
||||
path = Views;
|
||||
|
@ -234,6 +239,23 @@
|
|||
path = LocationCollectionMapView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F37071D623C3E96400FAC6AB /* UIView Wrappers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F37071D723C3E96D00FAC6AB /* UIImagePickerController */,
|
||||
);
|
||||
path = "UIView Wrappers";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F37071D723C3E96D00FAC6AB /* UIImagePickerController */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F37071D823C3E9B900FAC6AB /* UIImagePickerWrapper+Coordinator.swift */,
|
||||
F37071D923C3E9B900FAC6AB /* UIImagePickerWrapper.swift */,
|
||||
);
|
||||
path = UIImagePickerController;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F374DC08239A42C90016CC65 /* Location Collection */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -420,6 +442,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F394D0A8239FC6E2002E8FFC /* LocationCollectionsListView.swift in Sources */,
|
||||
F37071DA23C3E9B900FAC6AB /* UIImagePickerWrapper+Coordinator.swift in Sources */,
|
||||
F394D09C239E77B0002E8FFC /* LocationCollectionsState.swift in Sources */,
|
||||
F3BF5C1F239D53B700B9F8D8 /* SampleData.swift in Sources */,
|
||||
F34643C123A5BDBE00033065 /* CurrentApplication.swift in Sources */,
|
||||
|
@ -443,6 +466,7 @@
|
|||
F36D295F239A071F00095B66 /* CoreDataManager.swift in Sources */,
|
||||
F3C8F65623A837A7008B66A7 /* EditLocationViewModel.swift in Sources */,
|
||||
F394D0A1239EC7C2002E8FFC /* AppStore.swift in Sources */,
|
||||
F37071DB23C3E9B900FAC6AB /* UIImagePickerWrapper.swift in Sources */,
|
||||
F39A41BA23A3E2BB00F3B91D /* Binding+OptionalWrappers.swift in Sources */,
|
||||
F39A41C023A40C3800F3B91D /* WikipediaAPIService.swift in Sources */,
|
||||
F34643D923A6D52700033065 /* WikiPage+Comparable.swift in Sources */,
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
// ✌️
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension Location {
|
||||
|
||||
|
@ -17,5 +16,14 @@ extension Location {
|
|||
return wikiPages.sorted()
|
||||
}
|
||||
|
||||
|
||||
var userPhoto: Image? {
|
||||
guard
|
||||
let data = userPhotoData,
|
||||
let uiImage = UIImage(data: data)
|
||||
else { return nil }
|
||||
|
||||
return Image(uiImage: uiImage)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ extension Location {
|
|||
@NSManaged public var title: String?
|
||||
@NSManaged public var subtitle: String?
|
||||
@NSManaged public var longDescription: String?
|
||||
@NSManaged public var userPhotoData: Data?
|
||||
|
||||
|
||||
@NSManaged public var collection: LocationCollection?
|
||||
@NSManaged public var wikiPages: NSSet?
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<attribute name="longitude" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="subtitle" optional="YES" attributeType="String"/>
|
||||
<attribute name="title" optional="YES" attributeType="String"/>
|
||||
<attribute name="userPhotoData" optional="YES" attributeType="Binary" allowsExternalBinaryDataStorage="YES"/>
|
||||
<relationship name="collection" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="LocationCollection" inverseName="locations" inverseEntity="LocationCollection"/>
|
||||
<relationship name="wikiPages" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="WikiPage" inverseName="location" inverseEntity="WikiPage"/>
|
||||
<uniquenessConstraints>
|
||||
|
@ -37,7 +38,7 @@
|
|||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="Location" positionX="-144.60546875" positionY="60.58203125" width="128" height="28"/>
|
||||
<element name="Location" positionX="-144.60546875" positionY="60.58203125" width="128" height="163"/>
|
||||
<element name="LocationCollection" positionX="-386.72265625" positionY="-110.62109375" width="128" height="73"/>
|
||||
<element name="WikiPage" positionX="54.08984375" positionY="264.7734375" width="155.65234375" height="28"/>
|
||||
</elements>
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>This application would like permission to read and load images from your Photos Album. These can be used to as custom images for a location. </string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
|
|
21
day-068/Projects/PlaceCase/PlaceCase/Resources/Assets.xcassets/santorini.imageset/Contents.json
vendored
Normal file
21
day-068/Projects/PlaceCase/PlaceCase/Resources/Assets.xcassets/santorini.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "santorini.jpg",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
BIN
day-068/Projects/PlaceCase/PlaceCase/Resources/Assets.xcassets/santorini.imageset/santorini.jpg
vendored
Normal file
BIN
day-068/Projects/PlaceCase/PlaceCase/Resources/Assets.xcassets/santorini.imageset/santorini.jpg
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 305 KiB |
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// UIImagePickerWrapper+Coordinator.swift
|
||||
// PlaceCase
|
||||
//
|
||||
// Created by CypherPoet on 1/6/20.
|
||||
// ✌️
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
|
||||
extension UIImagePickerWrapper {
|
||||
|
||||
class Coordinator: NSObject {
|
||||
let onSelect: ((UIImage) -> Void)
|
||||
|
||||
init(onSelect: @escaping ((UIImage) -> Void)) {
|
||||
self.onSelect = onSelect
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UIImagePickerControllerDelegate
|
||||
extension UIImagePickerWrapper.Coordinator: UIImagePickerControllerDelegate {
|
||||
func imagePickerController(
|
||||
_ picker: UIImagePickerController,
|
||||
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]
|
||||
) {
|
||||
guard let selectedImage = (info[.editedImage] ?? info[.originalImage]) as? UIImage else {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
onSelect(selectedImage)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UINavigationControllerDelegate
|
||||
extension UIImagePickerWrapper.Coordinator: UINavigationControllerDelegate {}
|
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// UIImagePickerWrapper.swift
|
||||
// PlaceCase
|
||||
//
|
||||
// Created by CypherPoet on 1/6/20.
|
||||
// ✌️
|
||||
//
|
||||
|
||||
|
||||
import SwiftUI
|
||||
|
||||
|
||||
struct UIImagePickerWrapper {
|
||||
typealias UIViewControllerType = UIImagePickerController
|
||||
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
|
||||
let onSelect: ((UIImage) -> Void)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UIViewControllerRepresentable
|
||||
extension UIImagePickerWrapper: UIViewControllerRepresentable {
|
||||
|
||||
func makeCoordinator() -> UIImagePickerWrapper.Coordinator {
|
||||
Self.Coordinator(onSelect: self.imageSelected(_:))
|
||||
}
|
||||
|
||||
|
||||
func makeUIViewController(
|
||||
context: UIViewControllerRepresentableContext<UIImagePickerWrapper>
|
||||
) -> UIImagePickerController {
|
||||
let picker = UIImagePickerController()
|
||||
|
||||
picker.delegate = context.coordinator
|
||||
|
||||
return picker
|
||||
}
|
||||
|
||||
|
||||
func updateUIViewController(
|
||||
_ imagePickerController: UIImagePickerController,
|
||||
context: UIViewControllerRepresentableContext<UIImagePickerWrapper>
|
||||
) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private extension UIImagePickerWrapper {
|
||||
|
||||
func imageSelected(_ image: UIImage) {
|
||||
onSelect(image)
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
}
|
||||
}
|
|
@ -14,6 +14,8 @@ struct EditLocationView: View {
|
|||
@EnvironmentObject var store: AppStore
|
||||
|
||||
@ObservedObject var viewModel: EditLocationViewModel
|
||||
|
||||
@State private var isShowingImagePicker = false
|
||||
}
|
||||
|
||||
|
||||
|
@ -35,33 +37,8 @@ extension EditLocationView {
|
|||
}
|
||||
|
||||
|
||||
Section(header: Text("Add A Photo").font(.headline)) {
|
||||
|
||||
Button(action: {
|
||||
|
||||
}) {
|
||||
Group {
|
||||
// if viewModel.location.userPhoto != nil {
|
||||
if false {
|
||||
} else {
|
||||
HStack {
|
||||
Spacer()
|
||||
|
||||
VStack {
|
||||
Image(systemName: "camera")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 120)
|
||||
|
||||
Text("No Photo Selected")
|
||||
.font(.title)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(height: 200)
|
||||
}
|
||||
Section(header: Text("Custom Photo").font(.headline)) {
|
||||
customPhotoField
|
||||
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
|
||||
}
|
||||
|
||||
|
@ -85,20 +62,54 @@ extension EditLocationView {
|
|||
self.store.send(.wikiPages(.fetchStateSet(.fetching)))
|
||||
self.store.send(WikiPagesSideEffect.fetchPages(near: self.viewModel.location))
|
||||
}
|
||||
.sheet(isPresented: $isShowingImagePicker) {
|
||||
UIImagePickerWrapper(onSelect: self.userPhotoSelected(_:))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Computeds
|
||||
extension EditLocationView {
|
||||
extension EditLocationView {}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MARK: - View Variables
|
||||
extension EditLocationView {
|
||||
|
||||
private var customPhotoField: some View {
|
||||
Button(action: {
|
||||
self.isShowingImagePicker = true
|
||||
}) {
|
||||
HStack {
|
||||
Group {
|
||||
if viewModel.location.userPhoto != nil {
|
||||
viewModel.location.userPhoto!
|
||||
.renderingMode(.original)
|
||||
.resizable()
|
||||
.scaledToFill()
|
||||
.frame(height: 200)
|
||||
.clipped()
|
||||
} else {
|
||||
Spacer()
|
||||
|
||||
VStack {
|
||||
Image(systemName: "camera")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 120)
|
||||
|
||||
Text("Add a Photo")
|
||||
.font(.title)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(height: 200)
|
||||
}
|
||||
}
|
||||
|
||||
private var saveButton: some View {
|
||||
Button(action: {
|
||||
self.presentationMode.wrappedValue.dismiss()
|
||||
|
@ -109,6 +120,16 @@ extension EditLocationView {
|
|||
}
|
||||
|
||||
|
||||
// MARK: - Private Helpers
|
||||
private extension EditLocationView {
|
||||
|
||||
func userPhotoSelected(_ uiImage: UIImage) {
|
||||
viewModel.location.userPhotoData = uiImage.pngData()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// MARK: - Preview
|
||||
struct EditLocationView_Previews: PreviewProvider {
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 305 KiB |
Loading…
Reference in New Issue