Skip to content

Commit 82c28e9

Browse files
committed
finished revising photo library and camera examples
1 parent e9e2b50 commit 82c28e9

File tree

8 files changed

+152
-56
lines changed

8 files changed

+152
-56
lines changed

bk2ch17p696PhotoPermissions/PhotoPermissions/Info.plist

+2
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,7 @@
6666
<string>The app wants to stuff your photo library full of ugly pictures.</string>
6767
<key>NSPhotoLibraryUsageDescription</key>
6868
<string>The app wants to blur your photos.</string>
69+
<key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key>
70+
<true/>
6971
</dict>
7072
</plist>

bk2ch17p696PhotoPermissions/PhotoPermissions/ViewController.swift

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class ViewController: UIViewController {
4040
}
4141
}
4242
@IBAction func doButton2(_ sender: Any) {
43+
// Select Photos, Allow Access to All Photos, Don't Allow
4344
PHPhotoLibrary.requestAuthorization(for: .readWrite) { status in
4445
switch status {
4546
case .notDetermined:

bk2ch17p697pickaMovieOrPhoto/ch30p960pickaMovie/ViewController.swift

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class ViewController: UIViewController {
4747
extension ViewController : PHPickerViewControllerDelegate {
4848
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
4949
picker.dismiss(animated: true) { // NB if you don't say this, it won't dismiss!
50+
print(Thread.isMainThread) // yep
5051
guard let result = results.first else { return }
5152
// proving you don't get asset id unless you specified library
5253
let assetid = result.assetIdentifier

bk2ch17p700PhotoKitImages/PhotoKitImages/DataViewController.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ class DataViewController: UIViewController, EditingViewControllerDelegate {
5959
opts.version = .current
6060
// NB I do _not_ use PHImageManager.default! That's because it caches...
6161
// so when I reload the image after editing it, I'm having trouble getting the new one to load
62-
PHImageManager().requestImage(for: asset, targetSize: CGSize(600,600), contentMode: .aspectFit, options: opts) { im, info in
62+
// ooooh, hold it, looks like they fixed this! using `.default` after all
63+
PHImageManager.default().requestImage(for: asset, targetSize: CGSize(600,600), contentMode: .aspectFit, options: opts) { im, info in
6364
// this block can be called multiple times
6465
// and you can see why: initially we might get a degraded version of the image
6566
// and in fact we do, as I show with logging

bk2ch17p702takeAPicture/ch30p962takeAPicture/ViewController.swift

+12-12
Original file line numberDiff line numberDiff line change
@@ -123,17 +123,17 @@ UINavigationControllerDelegate, UIImagePickerControllerDelegate {
123123
let src = UIImagePickerController.SourceType.camera
124124
guard UIImagePickerController.isSourceTypeAvailable(src) else {return}
125125

126-
var which : Int {return 3} // 1, 2, 3, 4
126+
var which : Int {return 3} // 1, 2, 3
127127
let desiredTypes : [String] = {
128128
switch which {
129-
case 1: return [kUTTypeImage as String]
130-
case 2: return [kUTTypeMovie as String]
131-
case 3: return [kUTTypeImage as String, kUTTypeMovie as String]
132-
case 4: return [kUTTypeImage as String, kUTTypeLivePhoto as String] // nope
133-
default: return [kUTTypeImage as String, kUTTypeMovie as String, kUTTypeLivePhoto as String] // nope
129+
case 1: return [UTType.image.identifier]
130+
case 2: return [UTType.movie.identifier]
131+
case 3: return [UTType.image.identifier, UTType.movie.identifier]
132+
default: fatalError("hey")
134133
}
135134
}()
136-
// so I think what this proves is that they are not going to let me take a live photo this way
135+
// they are not going to let me take a live photo this way
136+
137137
let picker = UIImagePickerController()
138138
picker.sourceType = src
139139
picker.mediaTypes = desiredTypes
@@ -166,13 +166,13 @@ UINavigationControllerDelegate, UIImagePickerControllerDelegate {
166166
print(m as Any)
167167
self.dismiss(animated:true) {
168168
let mediatype = info[.mediaType]
169-
guard let type = mediatype as? NSString else {return}
170-
switch type as CFString {
171-
case kUTTypeImage:
169+
guard let type = mediatype as? String else {return}
170+
switch type {
171+
case UTType.image.identifier:
172172
if im != nil {
173173
self.showImage(im!)
174174
// showing how simple it is to save into the Camera Roll
175-
// return;
175+
return;
176176
checkForPhotoLibraryAccess {
177177
var which : Int { return 1 }
178178
switch which {
@@ -201,7 +201,7 @@ UINavigationControllerDelegate, UIImagePickerControllerDelegate {
201201

202202
}
203203
}
204-
case kUTTypeMovie:
204+
case UTType.movie.identifier:
205205
if url != nil {
206206
self.showMovie(url!)
207207
}

bk2ch17p704takeAPicture2/ch30p962takeAPicture2/ViewController.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ UINavigationControllerDelegate, UIImagePickerControllerDelegate {
8686

8787
let picker = UIImagePickerController()
8888
picker.sourceType = src
89-
picker.mediaTypes = [kUTTypeImage as String]
89+
picker.mediaTypes = [UTType.image.identifier]
9090
picker.allowsEditing = true
9191
picker.delegate = self
9292

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Scheme
3+
LastUpgradeVersion = "1200"
4+
version = "1.3">
5+
<BuildAction
6+
parallelizeBuildables = "YES"
7+
buildImplicitDependencies = "YES">
8+
<BuildActionEntries>
9+
<BuildActionEntry
10+
buildForTesting = "YES"
11+
buildForRunning = "YES"
12+
buildForProfiling = "YES"
13+
buildForArchiving = "YES"
14+
buildForAnalyzing = "YES">
15+
<BuildableReference
16+
BuildableIdentifier = "primary"
17+
BlueprintIdentifier = "C9E2177D18287DD400A58EAD"
18+
BuildableName = "ch30p966takeAPicAVFoundation.app"
19+
BlueprintName = "ch30p966takeAPicAVFoundation"
20+
ReferencedContainer = "container:ch30p966takeAPicAVFoundation.xcodeproj">
21+
</BuildableReference>
22+
</BuildActionEntry>
23+
</BuildActionEntries>
24+
</BuildAction>
25+
<TestAction
26+
buildConfiguration = "Debug"
27+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29+
shouldUseLaunchSchemeArgsEnv = "YES">
30+
<Testables>
31+
</Testables>
32+
</TestAction>
33+
<LaunchAction
34+
buildConfiguration = "Debug"
35+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
36+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
37+
launchStyle = "0"
38+
useCustomWorkingDirectory = "NO"
39+
ignoresPersistentStateOnLaunch = "NO"
40+
debugDocumentVersioning = "YES"
41+
debugServiceExtension = "internal"
42+
allowLocationSimulation = "YES">
43+
<BuildableProductRunnable
44+
runnableDebuggingMode = "0">
45+
<BuildableReference
46+
BuildableIdentifier = "primary"
47+
BlueprintIdentifier = "C9E2177D18287DD400A58EAD"
48+
BuildableName = "ch30p966takeAPicAVFoundation.app"
49+
BlueprintName = "ch30p966takeAPicAVFoundation"
50+
ReferencedContainer = "container:ch30p966takeAPicAVFoundation.xcodeproj">
51+
</BuildableReference>
52+
</BuildableProductRunnable>
53+
</LaunchAction>
54+
<ProfileAction
55+
buildConfiguration = "Release"
56+
shouldUseLaunchSchemeArgsEnv = "YES"
57+
savedToolIdentifier = ""
58+
useCustomWorkingDirectory = "NO"
59+
debugDocumentVersioning = "YES">
60+
<BuildableProductRunnable
61+
runnableDebuggingMode = "0">
62+
<BuildableReference
63+
BuildableIdentifier = "primary"
64+
BlueprintIdentifier = "C9E2177D18287DD400A58EAD"
65+
BuildableName = "ch30p966takeAPicAVFoundation.app"
66+
BlueprintName = "ch30p966takeAPicAVFoundation"
67+
ReferencedContainer = "container:ch30p966takeAPicAVFoundation.xcodeproj">
68+
</BuildableReference>
69+
</BuildableProductRunnable>
70+
</ProfileAction>
71+
<AnalyzeAction
72+
buildConfiguration = "Debug">
73+
</AnalyzeAction>
74+
<ArchiveAction
75+
buildConfiguration = "Release"
76+
revealArchiveInOrganizer = "YES">
77+
</ArchiveAction>
78+
</Scheme>

bk2ch17p707takeAPicAVFoundation/ch30p966takeAPicAVFoundation/ViewController.swift

+55-42
Original file line numberDiff line numberDiff line change
@@ -153,69 +153,82 @@ class ViewController: UIViewController {
153153
checkForMicrophoneCaptureAccess(andThen:self.reallyStart)
154154
}
155155

156+
let sessionQueue = DispatchQueue(label: "sessionQueue")
156157
func reallyStart() {
157158
if self.sess != nil && self.sess.isRunning {
158159
self.sess.stopRunning()
159160
self.previewLayer.removeFromSuperlayer()
160161
self.sess = nil
161162
return
162163
}
163-
self.iv?.removeFromSuperview()
164164

165165
self.sess = AVCaptureSession()
166-
167-
do {
168-
self.sess.beginConfiguration()
169166

170-
guard self.sess.canSetSessionPreset(self.sess.sessionPreset) else {return}
171-
self.sess.sessionPreset = .photo
167+
self.sessionQueue.async {
168+
do {
169+
self.sess.beginConfiguration()
170+
171+
guard self.sess.canSetSessionPreset(self.sess.sessionPreset) else {return}
172+
self.sess.sessionPreset = .photo
173+
174+
let output = AVCapturePhotoOutput()
175+
guard self.sess.canAddOutput(output) else {return}
176+
self.sess.addOutput(output)
177+
178+
self.sess.commitConfiguration()
179+
}
180+
181+
guard let cam = AVCaptureDevice.default(for: .video),
182+
let input = try? AVCaptureDeviceInput(device:cam)
183+
else {return}
184+
self.sess.addInput(input)
172185

173-
let output = AVCapturePhotoOutput()
174-
guard self.sess.canAddOutput(output) else {return}
175-
self.sess.addOutput(output)
186+
self.sess.startRunning()
176187

177-
self.sess.commitConfiguration()
188+
DispatchQueue.main.async {
189+
self.iv?.removeFromSuperview()
190+
let lay = AVCaptureVideoPreviewLayer(session:self.sess)
191+
lay.frame = self.previewRect
192+
self.view.layer.addSublayer(lay)
193+
self.previewLayer = lay // keep a ref so we can remove it later
194+
}
178195
}
179-
180-
guard let cam = AVCaptureDevice.default(for: .video),
181-
let input = try? AVCaptureDeviceInput(device:cam)
182-
else {return}
183-
self.sess.addInput(input)
184-
185-
let lay = AVCaptureVideoPreviewLayer(session:self.sess)
186-
lay.frame = self.previewRect
187-
self.view.layer.addSublayer(lay)
188-
self.previewLayer = lay // keep a ref so we can remove it later
189-
190-
self.sess.startRunning()
191196
}
192197

193198
@IBAction func doSnap (_ sender: Any) {
194199
guard self.sess != nil && self.sess.isRunning else {
195200
return
196201
}
197-
let settings = AVCapturePhotoSettings()
198-
settings.flashMode = .auto
199-
// let's also ask for a preview image
200-
let pbpf = settings.availablePreviewPhotoPixelFormatTypes[0]
201-
let len = max(self.previewLayer.bounds.width, self.previewLayer.bounds.height)
202-
settings.previewPhotoFormat = [
203-
kCVPixelBufferPixelFormatTypeKey as String : pbpf,
204-
kCVPixelBufferWidthKey as String : len,
205-
kCVPixelBufferHeightKey as String : len
206-
]
207-
// let's also ask for a thumnail image
208-
settings.embeddedThumbnailPhotoFormat = [
209-
AVVideoCodecKey : AVVideoCodecType.jpeg
210-
]
202+
203+
self.sessionQueue.async {
204+
guard let output = self.sess.outputs[0] as? AVCapturePhotoOutput else {return}
205+
let settings = AVCapturePhotoSettings()
206+
// let's also ask for a preview image
207+
let pbpf = settings.availablePreviewPhotoPixelFormatTypes[0]
208+
let len = max(self.previewLayer.bounds.width, self.previewLayer.bounds.height)
209+
settings.previewPhotoFormat = [
210+
kCVPixelBufferPixelFormatTypeKey as String : pbpf,
211+
kCVPixelBufferWidthKey as String : len,
212+
kCVPixelBufferHeightKey as String : len
213+
]
214+
// let's also ask for a thumnail image
215+
settings.embeddedThumbnailPhotoFormat = [
216+
AVVideoCodecKey : AVVideoCodecType.jpeg
217+
]
211218

212-
guard let output = self.sess.outputs[0] as? AVCapturePhotoOutput else {return}
213-
// how to deal with orientation; stolen from Apple's AVCam example!
214-
if let conn = output.connection(with: .video) {
215-
let orientation = UIDevice.current.orientation.videoOrientation!
216-
conn.videoOrientation = orientation
219+
220+
let supported = output.supportedFlashModes
221+
if supported.contains(.auto) {
222+
settings.flashMode = .auto
223+
}
224+
225+
// how to deal with orientation; stolen from Apple's AVCam example!
226+
if let conn = output.connection(with: .video) {
227+
let orientation = UIDevice.current.orientation.videoOrientation!
228+
conn.videoOrientation = orientation
229+
}
230+
output.capturePhoto(with: settings, delegate: self)
217231
}
218-
output.capturePhoto(with: settings, delegate: self)
219232
}
220233

221234
}

0 commit comments

Comments
 (0)