Merge pull request #10 from Lax/update

This commit is contained in:
Liu Lantao 2017-10-16 23:15:42 +08:00 committed by GitHub
commit 3f69e9478b
419 changed files with 31510 additions and 1575 deletions

View File

@ -0,0 +1,11 @@
//
// SampleCode.xcconfig
//
// The `SAMPLE_CODE_DISAMBIGUATOR` configuration is to make it easier to build
// and run a sample code project. Once you set your project's development team,
// you'll have a unique bundle identifier. This is because the bundle identifier
// is derived based on the 'SAMPLE_CODE_DISAMBIGUATOR' value. Do not use this
// approach in your own projects—it's only useful for sample code projects because
// they are frequently downloaded and don't have a development team set.
SAMPLE_CODE_DISAMBIGUATOR=${DEVELOPMENT_TEAM}

View File

@ -1,5 +1,5 @@
Sample code project: AVCam-iOS: Using AVFoundation to Capture Images and Movies Sample code project: AVCam-iOS: Using AVFoundation to Capture Images and Movies
Version: 6.1 Version: 7.1
IMPORTANT: This Apple software is supplied to you by Apple IMPORTANT: This Apple software is supplied to you by Apple
Inc. ("Apple") in consideration of your agreement to the following Inc. ("Apple") in consideration of your agreement to the following
@ -39,4 +39,4 @@ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. POSSIBILITY OF SUCH DAMAGE.
Copyright (C) 2016 Apple Inc. All Rights Reserved. Copyright (C) 2017 Apple Inc. All Rights Reserved.

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array/>
</plist>

View File

@ -29,10 +29,12 @@
220626871A1E345E00A45150 /* AVCamCameraViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AVCamCameraViewController.m; sourceTree = "<group>"; }; 220626871A1E345E00A45150 /* AVCamCameraViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AVCamCameraViewController.m; sourceTree = "<group>"; };
22CA31B71B022D1300D2DE70 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = AVCam/Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 22CA31B71B022D1300D2DE70 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = AVCam/Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
22CA31B91B0250C300D2DE70 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; }; 22CA31B91B0250C300D2DE70 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
3E25DCAD1F7EA483007EF37E /* LICENSE.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = LICENSE.txt; path = ../../LICENSE.txt; sourceTree = "<group>"; };
7A7444781CEE6B0F00C70C83 /* AVCamPhotoCaptureDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVCamPhotoCaptureDelegate.h; sourceTree = "<group>"; }; 7A7444781CEE6B0F00C70C83 /* AVCamPhotoCaptureDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVCamPhotoCaptureDelegate.h; sourceTree = "<group>"; };
7A7444791CEE6B0F00C70C83 /* AVCamPhotoCaptureDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AVCamPhotoCaptureDelegate.m; sourceTree = "<group>"; }; 7A7444791CEE6B0F00C70C83 /* AVCamPhotoCaptureDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AVCamPhotoCaptureDelegate.m; sourceTree = "<group>"; };
7A74447B1CEE6B4B00C70C83 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 7A74447B1CEE6B4B00C70C83 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
7A74447D1CEE6B5900C70C83 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; 7A74447D1CEE6B5900C70C83 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
95F1FD6060E3CAB64438CE51 /* SampleCode.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = SampleCode.xcconfig; path = ../Configuration/SampleCode.xcconfig; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -52,6 +54,8 @@
22CA31B91B0250C300D2DE70 /* README.md */, 22CA31B91B0250C300D2DE70 /* README.md */,
2206265B1A1E330400A45150 /* AVCam */, 2206265B1A1E330400A45150 /* AVCam */,
2206265A1A1E330400A45150 /* Products */, 2206265A1A1E330400A45150 /* Products */,
DDF88DBAE19DC8C1451D62E5 /* Configuration */,
3E25DCAC1F7EA45D007EF37E /* LICENSE */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@ -83,6 +87,23 @@
path = AVCam; path = AVCam;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
3E25DCAC1F7EA45D007EF37E /* LICENSE */ = {
isa = PBXGroup;
children = (
3E25DCAD1F7EA483007EF37E /* LICENSE.txt */,
);
name = LICENSE;
path = AVCam;
sourceTree = "<group>";
};
DDF88DBAE19DC8C1451D62E5 /* Configuration */ = {
isa = PBXGroup;
children = (
95F1FD6060E3CAB64438CE51 /* SampleCode.xcconfig */,
);
name = Configuration;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@ -109,7 +130,8 @@
220626511A1E330400A45150 /* Project object */ = { 220626511A1E330400A45150 /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 0800; LastUpgradeCheck = 0900;
ORGANIZATIONNAME = Apple;
TargetAttributes = { TargetAttributes = {
220626581A1E330400A45150 = { 220626581A1E330400A45150 = {
CreatedOnToolsVersion = 6.1; CreatedOnToolsVersion = 6.1;
@ -178,21 +200,31 @@
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
2206267A1A1E330400A45150 /* Debug */ = { 2206267A1A1E330400A45150 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 95F1FD6060E3CAB64438CE51 /* SampleCode.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
@ -212,7 +244,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -222,21 +254,31 @@
}; };
2206267B1A1E330400A45150 /* Release */ = { 2206267B1A1E330400A45150 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 95F1FD6060E3CAB64438CE51 /* SampleCode.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES; COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
@ -249,7 +291,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
@ -259,26 +301,32 @@
}; };
2206267D1A1E330400A45150 /* Debug */ = { 2206267D1A1E330400A45150 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 95F1FD6060E3CAB64438CE51 /* SampleCode.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = AVCam/Info.plist; INFOPLIST_FILE = AVCam/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.AVCam"; PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.AVCam${SAMPLE_CODE_DISAMBIGUATOR}";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = iphoneos; SDKROOT = iphoneos;
}; };
name = Debug; name = Debug;
}; };
2206267E1A1E330400A45150 /* Release */ = { 2206267E1A1E330400A45150 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 95F1FD6060E3CAB64438CE51 /* SampleCode.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = AVCam/Info.plist; INFOPLIST_FILE = AVCam/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.AVCam"; PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.AVCam${SAMPLE_CODE_DISAMBIGUATOR}";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = iphoneos; SDKROOT = iphoneos;
}; };
name = Release; name = Release;

View File

@ -1,91 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "220626581A1E330400A45150"
BuildableName = "AVCam.app"
BlueprintName = "AVCam"
ReferencedContainer = "container:AVCam Objective-C.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "220626581A1E330400A45150"
BuildableName = "AVCam.app"
BlueprintName = "AVCam"
ReferencedContainer = "container:AVCam Objective-C.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</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 = "220626581A1E330400A45150"
BuildableName = "AVCam.app"
BlueprintName = "AVCam"
ReferencedContainer = "container:AVCam Objective-C.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "220626581A1E330400A45150"
BuildableName = "AVCam.app"
BlueprintName = "AVCam"
ReferencedContainer = "container:AVCam Objective-C.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -1,9 +1,8 @@
/* /*
Copyright (C) 2016 Apple Inc. All Rights Reserved. See LICENSE.txt for this samples licensing information.
See LICENSE.txt for this samples licensing information
Abstract: Abstract:
Application delegate. Application delegate.
*/ */
@import UIKit; @import UIKit;

View File

@ -1,9 +1,8 @@
/* /*
Copyright (C) 2016 Apple Inc. All Rights Reserved. See LICENSE.txt for this samples licensing information.
See LICENSE.txt for this samples licensing information
Abstract: Abstract:
Application delegate. Application delegate.
*/ */
#import "AVCamAppDelegate.h" #import "AVCamAppDelegate.h"

View File

@ -1,9 +1,8 @@
/* /*
Copyright (C) 2016 Apple Inc. All Rights Reserved. See LICENSE.txt for this samples licensing information.
See LICENSE.txt for this samples licensing information
Abstract: Abstract:
View controller for camera interface. View controller for camera interface.
*/ */
@import UIKit; @import UIKit;

View File

@ -1,11 +1,9 @@
/* /*
Copyright (C) 2016 Apple Inc. All Rights Reserved. See LICENSE.txt for this samples licensing information.
See LICENSE.txt for this samples licensing information
Abstract: Abstract:
View controller for camera interface. View controller for camera interface.
*/ */
@import AVFoundation; @import AVFoundation;
@import Photos; @import Photos;
@ -31,6 +29,11 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
AVCamLivePhotoModeOff AVCamLivePhotoModeOff
}; };
typedef NS_ENUM( NSInteger, AVCamDepthDataDeliveryMode ) {
AVCamDepthDataDeliveryModeOn,
AVCamDepthDataDeliveryModeOff
};
@interface AVCaptureDeviceDiscoverySession (Utilities) @interface AVCaptureDeviceDiscoverySession (Utilities)
- (NSInteger)uniqueDevicePositionsCount; - (NSInteger)uniqueDevicePositionsCount;
@ -76,6 +79,8 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
@property (nonatomic, weak) IBOutlet UIButton *livePhotoModeButton; @property (nonatomic, weak) IBOutlet UIButton *livePhotoModeButton;
@property (nonatomic) AVCamLivePhotoMode livePhotoMode; @property (nonatomic) AVCamLivePhotoMode livePhotoMode;
@property (nonatomic, weak) IBOutlet UILabel *capturingLivePhotoLabel; @property (nonatomic, weak) IBOutlet UILabel *capturingLivePhotoLabel;
@property (nonatomic, weak) IBOutlet UIButton *depthDataDeliveryButton;
@property (nonatomic) AVCamDepthDataDeliveryMode depthDataDeliveryMode;
@property (nonatomic) AVCapturePhotoOutput *photoOutput; @property (nonatomic) AVCapturePhotoOutput *photoOutput;
@property (nonatomic) NSMutableDictionary<NSNumber *, AVCamPhotoCaptureDelegate *> *inProgressPhotoCaptureDelegates; @property (nonatomic) NSMutableDictionary<NSNumber *, AVCamPhotoCaptureDelegate *> *inProgressPhotoCaptureDelegates;
@ -104,12 +109,13 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
self.photoButton.enabled = NO; self.photoButton.enabled = NO;
self.livePhotoModeButton.enabled = NO; self.livePhotoModeButton.enabled = NO;
self.captureModeControl.enabled = NO; self.captureModeControl.enabled = NO;
self.depthDataDeliveryButton.enabled = NO;
// Create the AVCaptureSession. // Create the AVCaptureSession.
self.session = [[AVCaptureSession alloc] init]; self.session = [[AVCaptureSession alloc] init];
// Create a device discovery session. // Create a device discovery session.
NSArray<AVCaptureDeviceType> *deviceTypes = @[AVCaptureDeviceTypeBuiltInWideAngleCamera, AVCaptureDeviceTypeBuiltInDuoCamera]; NSArray<AVCaptureDeviceType> *deviceTypes = @[AVCaptureDeviceTypeBuiltInWideAngleCamera, AVCaptureDeviceTypeBuiltInDualCamera];
self.videoDeviceDiscoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:deviceTypes mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified]; self.videoDeviceDiscoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:deviceTypes mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified];
// Set up the preview view. // Set up the preview view.
@ -276,7 +282,7 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
// Add video input. // Add video input.
// Choose the back dual camera if available, otherwise default to a wide angle camera. // Choose the back dual camera if available, otherwise default to a wide angle camera.
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInDuoCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack]; AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInDualCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
if ( ! videoDevice ) { if ( ! videoDevice ) {
// If the back dual camera is not available, default to the back wide angle camera. // If the back dual camera is not available, default to the back wide angle camera.
videoDevice = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack]; videoDevice = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
@ -345,7 +351,11 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
self.photoOutput.highResolutionCaptureEnabled = YES; self.photoOutput.highResolutionCaptureEnabled = YES;
self.photoOutput.livePhotoCaptureEnabled = self.photoOutput.livePhotoCaptureSupported; self.photoOutput.livePhotoCaptureEnabled = self.photoOutput.livePhotoCaptureSupported;
self.photoOutput.depthDataDeliveryEnabled = self.photoOutput.depthDataDeliverySupported;
self.livePhotoMode = self.photoOutput.livePhotoCaptureSupported ? AVCamLivePhotoModeOn : AVCamLivePhotoModeOff; self.livePhotoMode = self.photoOutput.livePhotoCaptureSupported ? AVCamLivePhotoModeOn : AVCamLivePhotoModeOff;
self.depthDataDeliveryMode = self.photoOutput.depthDataDeliverySupported ? AVCamDepthDataDeliveryModeOn : AVCamDepthDataDeliveryModeOff;
self.inProgressPhotoCaptureDelegates = [NSMutableDictionary dictionary]; self.inProgressPhotoCaptureDelegates = [NSMutableDictionary dictionary];
self.inProgressLivePhotoCapturesCount = 0; self.inProgressLivePhotoCapturesCount = 0;
@ -405,7 +415,6 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
[self.session beginConfiguration]; [self.session beginConfiguration];
[self.session removeOutput:self.movieFileOutput]; [self.session removeOutput:self.movieFileOutput];
self.session.sessionPreset = AVCaptureSessionPresetPhoto; self.session.sessionPreset = AVCaptureSessionPresetPhoto;
[self.session commitConfiguration];
self.movieFileOutput = nil; self.movieFileOutput = nil;
@ -417,10 +426,22 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
self.livePhotoModeButton.hidden = NO; self.livePhotoModeButton.hidden = NO;
} ); } );
} }
if ( self.photoOutput.depthDataDeliverySupported ) {
self.photoOutput.depthDataDeliveryEnabled = YES;
dispatch_async( dispatch_get_main_queue(), ^{
self.depthDataDeliveryButton.hidden = NO;
self.depthDataDeliveryButton.enabled = YES;
} );
}
[self.session commitConfiguration];
} ); } );
} }
else if ( captureModeControl.selectedSegmentIndex == AVCamCaptureModeMovie ) { else if ( captureModeControl.selectedSegmentIndex == AVCamCaptureModeMovie ) {
self.livePhotoModeButton.hidden = YES; self.livePhotoModeButton.hidden = YES;
self.depthDataDeliveryButton.hidden = YES;
dispatch_async( self.sessionQueue, ^{ dispatch_async( self.sessionQueue, ^{
AVCaptureMovieFileOutput *movieFileOutput = [[AVCaptureMovieFileOutput alloc] init]; AVCaptureMovieFileOutput *movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
@ -468,7 +489,7 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
case AVCaptureDevicePositionUnspecified: case AVCaptureDevicePositionUnspecified:
case AVCaptureDevicePositionFront: case AVCaptureDevicePositionFront:
preferredPosition = AVCaptureDevicePositionBack; preferredPosition = AVCaptureDevicePositionBack;
preferredDeviceType = AVCaptureDeviceTypeBuiltInDuoCamera; preferredDeviceType = AVCaptureDeviceTypeBuiltInDualCamera;
break; break;
case AVCaptureDevicePositionBack: case AVCaptureDevicePositionBack:
preferredPosition = AVCaptureDevicePositionFront; preferredPosition = AVCaptureDevicePositionFront;
@ -523,12 +544,13 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
} }
/* /*
Set Live Photo capture enabled if it is supported. When changing cameras, the Set Live Photo capture and depth data delivery if it is supported. When changing cameras, the
`livePhotoCaptureEnabled` property of the AVCapturePhotoOutput gets set to NO when `livePhotoCaptureEnabled` and `depthDataDeliveryEnabled` properties of the AVCapturePhotoOutput gets set to NO when
a video device is disconnected from the session. After the new video device is a video device is disconnected from the session. After the new video device is
added to the session, re-enable Live Photo capture on the AVCapturePhotoOutput if it is supported. added to the session, re-enable Live Photo capture and depth data delivery if they are supported.
*/ */
self.photoOutput.livePhotoCaptureEnabled = self.photoOutput.livePhotoCaptureSupported; self.photoOutput.livePhotoCaptureEnabled = self.photoOutput.livePhotoCaptureSupported;
self.photoOutput.depthDataDeliveryEnabled = self.photoOutput.depthDataDeliverySupported;
[self.session commitConfiguration]; [self.session commitConfiguration];
} }
@ -539,6 +561,8 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
self.photoButton.enabled = YES; self.photoButton.enabled = YES;
self.livePhotoModeButton.enabled = YES; self.livePhotoModeButton.enabled = YES;
self.captureModeControl.enabled = YES; self.captureModeControl.enabled = YES;
self.depthDataDeliveryButton.enabled = self.photoOutput.isDepthDataDeliveryEnabled;
self.depthDataDeliveryButton.hidden = !self.photoOutput.depthDataDeliverySupported;
} ); } );
} ); } );
} }
@ -595,9 +619,18 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
AVCaptureConnection *photoOutputConnection = [self.photoOutput connectionWithMediaType:AVMediaTypeVideo]; AVCaptureConnection *photoOutputConnection = [self.photoOutput connectionWithMediaType:AVMediaTypeVideo];
photoOutputConnection.videoOrientation = videoPreviewLayerVideoOrientation; photoOutputConnection.videoOrientation = videoPreviewLayerVideoOrientation;
// Capture a JPEG photo with flash set to auto and high resolution photo enabled. AVCapturePhotoSettings *photoSettings;
AVCapturePhotoSettings *photoSettings = [AVCapturePhotoSettings photoSettings]; // Capture HEIF photo when supported, with flash set to auto and high resolution photo enabled.
if ( [self.photoOutput.availablePhotoCodecTypes containsObject:AVVideoCodecTypeHEVC] ) {
photoSettings = [AVCapturePhotoSettings photoSettingsWithFormat:@{ AVVideoCodecKey : AVVideoCodecTypeHEVC }];
}
else {
photoSettings = [AVCapturePhotoSettings photoSettings];
}
if ( self.videoDeviceInput.device.isFlashAvailable ) {
photoSettings.flashMode = AVCaptureFlashModeAuto; photoSettings.flashMode = AVCaptureFlashModeAuto;
}
photoSettings.highResolutionPhotoEnabled = YES; photoSettings.highResolutionPhotoEnabled = YES;
if ( photoSettings.availablePreviewPhotoPixelFormatTypes.count > 0 ) { if ( photoSettings.availablePreviewPhotoPixelFormatTypes.count > 0 ) {
photoSettings.previewPhotoFormat = @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : photoSettings.availablePreviewPhotoPixelFormatTypes.firstObject }; photoSettings.previewPhotoFormat = @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : photoSettings.availablePreviewPhotoPixelFormatTypes.firstObject };
@ -608,6 +641,12 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
photoSettings.livePhotoMovieFileURL = [NSURL fileURLWithPath:livePhotoMovieFilePath]; photoSettings.livePhotoMovieFileURL = [NSURL fileURLWithPath:livePhotoMovieFilePath];
} }
if ( self.depthDataDeliveryMode == AVCamDepthDataDeliveryModeOn && self.photoOutput.isDepthDataDeliverySupported ) {
photoSettings.depthDataDeliveryEnabled = YES;
} else {
photoSettings.depthDataDeliveryEnabled = NO;
}
// Use a separate object for the photo capture delegate to isolate each capture life cycle. // Use a separate object for the photo capture delegate to isolate each capture life cycle.
AVCamPhotoCaptureDelegate *photoCaptureDelegate = [[AVCamPhotoCaptureDelegate alloc] initWithRequestedPhotoSettings:photoSettings willCapturePhotoAnimation:^{ AVCamPhotoCaptureDelegate *photoCaptureDelegate = [[AVCamPhotoCaptureDelegate alloc] initWithRequestedPhotoSettings:photoSettings willCapturePhotoAnimation:^{
dispatch_async( dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
@ -616,7 +655,7 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
self.previewView.videoPreviewLayer.opacity = 1.0; self.previewView.videoPreviewLayer.opacity = 1.0;
}]; }];
} ); } );
} capturingLivePhoto:^( BOOL capturing ) { } livePhotoCaptureHandler:^( BOOL capturing ) {
/* /*
Because Live Photo captures can overlap, we need to keep track of the Because Live Photo captures can overlap, we need to keep track of the
number of in progress Live Photo captures to ensure that the number of in progress Live Photo captures to ensure that the
@ -643,7 +682,7 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
} }
} ); } );
} ); } );
} completed:^( AVCamPhotoCaptureDelegate *photoCaptureDelegate ) { } completionHandler:^( AVCamPhotoCaptureDelegate *photoCaptureDelegate ) {
// When the capture is complete, remove a reference to the photo capture delegate so it can be deallocated. // When the capture is complete, remove a reference to the photo capture delegate so it can be deallocated.
dispatch_async( self.sessionQueue, ^{ dispatch_async( self.sessionQueue, ^{
self.inProgressPhotoCaptureDelegates[@(photoCaptureDelegate.requestedPhotoSettings.uniqueID)] = nil; self.inProgressPhotoCaptureDelegates[@(photoCaptureDelegate.requestedPhotoSettings.uniqueID)] = nil;
@ -677,6 +716,23 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
} ); } );
} }
- (IBAction)toggleDepthDataDeliveryMode:(UIButton *)depthDataDeliveryButton
{
dispatch_async( self.sessionQueue, ^{
self.depthDataDeliveryMode = ( self.depthDataDeliveryMode == AVCamDepthDataDeliveryModeOn ) ? AVCamDepthDataDeliveryModeOff : AVCamDepthDataDeliveryModeOn;
AVCamDepthDataDeliveryMode depthDataDeliveryMode = self.depthDataDeliveryMode;
dispatch_async( dispatch_get_main_queue(), ^{
if ( depthDataDeliveryMode == AVCamDepthDataDeliveryModeOn ) {
[self.depthDataDeliveryButton setTitle:NSLocalizedString( @"Depth Data Delivery: On", @"Depth Data mode button on title" ) forState:UIControlStateNormal];
}
else {
[self.depthDataDeliveryButton setTitle:NSLocalizedString( @"Depth Data Delivery: Off", @"Depth Data mode button off title" ) forState:UIControlStateNormal];
}
} );
} );
}
#pragma mark Recording Movies #pragma mark Recording Movies
- (IBAction)toggleMovieRecording:(id)sender - (IBAction)toggleMovieRecording:(id)sender
@ -716,6 +772,11 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
AVCaptureConnection *movieFileOutputConnection = [self.movieFileOutput connectionWithMediaType:AVMediaTypeVideo]; AVCaptureConnection *movieFileOutputConnection = [self.movieFileOutput connectionWithMediaType:AVMediaTypeVideo];
movieFileOutputConnection.videoOrientation = videoPreviewLayerVideoOrientation; movieFileOutputConnection.videoOrientation = videoPreviewLayerVideoOrientation;
// Use HEVC codec if supported
if ( [self.movieFileOutput.availableVideoCodecTypes containsObject:AVVideoCodecTypeHEVC] ) {
[self.movieFileOutput setOutputSettings:@{ AVVideoCodecKey : AVVideoCodecTypeHEVC } forConnection:movieFileOutputConnection];
}
// Start recording to a temporary file. // Start recording to a temporary file.
NSString *outputFileName = [NSUUID UUID].UUIDString; NSString *outputFileName = [NSUUID UUID].UUIDString;
NSString *outputFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[outputFileName stringByAppendingPathExtension:@"mov"]]; NSString *outputFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[outputFileName stringByAppendingPathExtension:@"mov"]];
@ -751,7 +812,7 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
UIBackgroundTaskIdentifier currentBackgroundRecordingID = self.backgroundRecordingID; UIBackgroundTaskIdentifier currentBackgroundRecordingID = self.backgroundRecordingID;
self.backgroundRecordingID = UIBackgroundTaskInvalid; self.backgroundRecordingID = UIBackgroundTaskInvalid;
dispatch_block_t cleanup = ^{ dispatch_block_t cleanUp = ^{
if ( [[NSFileManager defaultManager] fileExistsAtPath:outputFileURL.path] ) { if ( [[NSFileManager defaultManager] fileExistsAtPath:outputFileURL.path] ) {
[[NSFileManager defaultManager] removeItemAtPath:outputFileURL.path error:NULL]; [[NSFileManager defaultManager] removeItemAtPath:outputFileURL.path error:NULL];
} }
@ -781,16 +842,16 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
if ( ! success ) { if ( ! success ) {
NSLog( @"Could not save movie to photo library: %@", error ); NSLog( @"Could not save movie to photo library: %@", error );
} }
cleanup(); cleanUp();
}]; }];
} }
else { else {
cleanup(); cleanUp();
} }
}]; }];
} }
else { else {
cleanup(); cleanUp();
} }
// Enable the Camera and Record buttons to let the user switch camera and start another recording. // Enable the Camera and Record buttons to let the user switch camera and start another recording.
@ -836,6 +897,8 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
BOOL isSessionRunning = [change[NSKeyValueChangeNewKey] boolValue]; BOOL isSessionRunning = [change[NSKeyValueChangeNewKey] boolValue];
BOOL livePhotoCaptureSupported = self.photoOutput.livePhotoCaptureSupported; BOOL livePhotoCaptureSupported = self.photoOutput.livePhotoCaptureSupported;
BOOL livePhotoCaptureEnabled = self.photoOutput.livePhotoCaptureEnabled; BOOL livePhotoCaptureEnabled = self.photoOutput.livePhotoCaptureEnabled;
BOOL depthDataDeliverySupported = self.photoOutput.depthDataDeliverySupported;
BOOL depthDataDeliveryEnabled = self.photoOutput.depthDataDeliveryEnabled;
dispatch_async( dispatch_get_main_queue(), ^{ dispatch_async( dispatch_get_main_queue(), ^{
// Only enable the ability to change camera if the device has more than one camera. // Only enable the ability to change camera if the device has more than one camera.
@ -845,6 +908,8 @@ typedef NS_ENUM( NSInteger, AVCamLivePhotoMode ) {
self.captureModeControl.enabled = isSessionRunning; self.captureModeControl.enabled = isSessionRunning;
self.livePhotoModeButton.enabled = isSessionRunning && livePhotoCaptureEnabled; self.livePhotoModeButton.enabled = isSessionRunning && livePhotoCaptureEnabled;
self.livePhotoModeButton.hidden = ! ( isSessionRunning && livePhotoCaptureSupported ); self.livePhotoModeButton.hidden = ! ( isSessionRunning && livePhotoCaptureSupported );
self.depthDataDeliveryButton.enabled = isSessionRunning && depthDataDeliveryEnabled ;
self.depthDataDeliveryButton.hidden = ! ( isSessionRunning && depthDataDeliverySupported );
} ); } );
} }
else { else {

View File

@ -1,16 +1,15 @@
/* /*
Copyright (C) 2016 Apple Inc. All Rights Reserved. See LICENSE.txt for this samples licensing information.
See LICENSE.txt for this samples licensing information
Abstract: Abstract:
Photo capture delegate. Photo capture delegate.
*/ */
@import AVFoundation; @import AVFoundation;
@interface AVCamPhotoCaptureDelegate : NSObject<AVCapturePhotoCaptureDelegate> @interface AVCamPhotoCaptureDelegate : NSObject<AVCapturePhotoCaptureDelegate>
- (instancetype)initWithRequestedPhotoSettings:(AVCapturePhotoSettings *)requestedPhotoSettings willCapturePhotoAnimation:(void (^)())willCapturePhotoAnimation capturingLivePhoto:(void (^)( BOOL capturing ))capturingLivePhoto completed:(void (^)( AVCamPhotoCaptureDelegate *photoCaptureDelegate ))completed; - (instancetype)initWithRequestedPhotoSettings:(AVCapturePhotoSettings *)requestedPhotoSettings willCapturePhotoAnimation:(void (^)(void))willCapturePhotoAnimation livePhotoCaptureHandler:(void (^)( BOOL capturing ))livePhotoCaptureHandler completionHandler:(void (^)( AVCamPhotoCaptureDelegate *photoCaptureDelegate ))completionHandler;
@property (nonatomic, readonly) AVCapturePhotoSettings *requestedPhotoSettings; @property (nonatomic, readonly) AVCapturePhotoSettings *requestedPhotoSettings;

View File

@ -1,11 +1,11 @@
/* /*
Copyright (C) 2016 Apple Inc. All Rights Reserved. See LICENSE.txt for this samples licensing information.
See LICENSE.txt for this samples licensing information
Abstract: Abstract:
Photo capture delegate. Photo capture delegate.
*/ */
#import "AVCamPhotoCaptureDelegate.h" #import "AVCamPhotoCaptureDelegate.h"
@import Photos; @import Photos;
@ -13,9 +13,9 @@
@interface AVCamPhotoCaptureDelegate () @interface AVCamPhotoCaptureDelegate ()
@property (nonatomic, readwrite) AVCapturePhotoSettings *requestedPhotoSettings; @property (nonatomic, readwrite) AVCapturePhotoSettings *requestedPhotoSettings;
@property (nonatomic) void (^willCapturePhotoAnimation)(); @property (nonatomic) void (^willCapturePhotoAnimation)(void);
@property (nonatomic) void (^capturingLivePhoto)(BOOL capturing); @property (nonatomic) void (^livePhotoCaptureHandler)(BOOL capturing);
@property (nonatomic) void (^completed)(AVCamPhotoCaptureDelegate *photoCaptureDelegate); @property (nonatomic) void (^completionHandler)(AVCamPhotoCaptureDelegate *photoCaptureDelegate);
@property (nonatomic) NSData *photoData; @property (nonatomic) NSData *photoData;
@property (nonatomic) NSURL *livePhotoCompanionMovieURL; @property (nonatomic) NSURL *livePhotoCompanionMovieURL;
@ -24,14 +24,14 @@
@implementation AVCamPhotoCaptureDelegate @implementation AVCamPhotoCaptureDelegate
- (instancetype)initWithRequestedPhotoSettings:(AVCapturePhotoSettings *)requestedPhotoSettings willCapturePhotoAnimation:(void (^)())willCapturePhotoAnimation capturingLivePhoto:(void (^)(BOOL))capturingLivePhoto completed:(void (^)(AVCamPhotoCaptureDelegate *))completed - (instancetype)initWithRequestedPhotoSettings:(AVCapturePhotoSettings *)requestedPhotoSettings willCapturePhotoAnimation:(void (^)(void))willCapturePhotoAnimation livePhotoCaptureHandler:(void (^)(BOOL))livePhotoCaptureHandler completionHandler:(void (^)(AVCamPhotoCaptureDelegate *))completionHandler
{ {
self = [super init]; self = [super init];
if ( self ) { if ( self ) {
self.requestedPhotoSettings = requestedPhotoSettings; self.requestedPhotoSettings = requestedPhotoSettings;
self.willCapturePhotoAnimation = willCapturePhotoAnimation; self.willCapturePhotoAnimation = willCapturePhotoAnimation;
self.capturingLivePhoto = capturingLivePhoto; self.livePhotoCaptureHandler = livePhotoCaptureHandler;
self.completed = completed; self.completionHandler = completionHandler;
} }
return self; return self;
} }
@ -47,13 +47,13 @@
} }
} }
self.completed( self ); self.completionHandler( self );
} }
- (void)captureOutput:(AVCapturePhotoOutput *)captureOutput willBeginCaptureForResolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings - (void)captureOutput:(AVCapturePhotoOutput *)captureOutput willBeginCaptureForResolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings
{ {
if ( ( resolvedSettings.livePhotoMovieDimensions.width > 0 ) && ( resolvedSettings.livePhotoMovieDimensions.height > 0 ) ) { if ( ( resolvedSettings.livePhotoMovieDimensions.width > 0 ) && ( resolvedSettings.livePhotoMovieDimensions.height > 0 ) ) {
self.capturingLivePhoto( YES ); self.livePhotoCaptureHandler( YES );
} }
} }
@ -62,19 +62,18 @@
self.willCapturePhotoAnimation(); self.willCapturePhotoAnimation();
} }
- (void)captureOutput:(AVCapturePhotoOutput *)captureOutput didFinishProcessingPhotoSampleBuffer:(CMSampleBufferRef)photoSampleBuffer previewPhotoSampleBuffer:(CMSampleBufferRef)previewPhotoSampleBuffer resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings bracketSettings:(AVCaptureBracketedStillImageSettings *)bracketSettings error:(NSError *)error - (void)captureOutput:(AVCapturePhotoOutput *)captureOutput didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(nullable NSError *)error
{ {
if ( error != nil ) { if ( error != nil ) {
NSLog( @"Error capturing photo: %@", error ); NSLog( @"Error capturing photo: %@", error );
return; return;
} }
self.photoData = [photo fileDataRepresentation];
self.photoData = [AVCapturePhotoOutput JPEGPhotoDataRepresentationForJPEGSampleBuffer:photoSampleBuffer previewPhotoSampleBuffer:previewPhotoSampleBuffer];
} }
- (void)captureOutput:(AVCapturePhotoOutput *)captureOutput didFinishRecordingLivePhotoMovieForEventualFileAtURL:(NSURL *)outputFileURL resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings - (void)captureOutput:(AVCapturePhotoOutput *)captureOutput didFinishRecordingLivePhotoMovieForEventualFileAtURL:(NSURL *)outputFileURL resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings
{ {
self.capturingLivePhoto(NO); self.livePhotoCaptureHandler(NO);
} }
- (void)captureOutput:(AVCapturePhotoOutput *)captureOutput didFinishProcessingLivePhotoToMovieFileAtURL:(NSURL *)outputFileURL duration:(CMTime)duration photoDisplayTime:(CMTime)photoDisplayTime resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings error:(NSError *)error - (void)captureOutput:(AVCapturePhotoOutput *)captureOutput didFinishProcessingLivePhotoToMovieFileAtURL:(NSURL *)outputFileURL duration:(CMTime)duration photoDisplayTime:(CMTime)photoDisplayTime resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings error:(NSError *)error
@ -104,8 +103,10 @@
[PHPhotoLibrary requestAuthorization:^( PHAuthorizationStatus status ) { [PHPhotoLibrary requestAuthorization:^( PHAuthorizationStatus status ) {
if ( status == PHAuthorizationStatusAuthorized ) { if ( status == PHAuthorizationStatusAuthorized ) {
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetResourceCreationOptions *options = [[PHAssetResourceCreationOptions alloc] init];
options.uniformTypeIdentifier = self.requestedPhotoSettings.processedFileType;
PHAssetCreationRequest *creationRequest = [PHAssetCreationRequest creationRequestForAsset]; PHAssetCreationRequest *creationRequest = [PHAssetCreationRequest creationRequestForAsset];
[creationRequest addResourceWithType:PHAssetResourceTypePhoto data:self.photoData options:nil]; [creationRequest addResourceWithType:PHAssetResourceTypePhoto data:self.photoData options:options];
if ( self.livePhotoCompanionMovieURL ) { if ( self.livePhotoCompanionMovieURL ) {
PHAssetResourceCreationOptions *livePhotoCompanionMovieResourceOptions = [[PHAssetResourceCreationOptions alloc] init]; PHAssetResourceCreationOptions *livePhotoCompanionMovieResourceOptions = [[PHAssetResourceCreationOptions alloc] init];

View File

@ -1,9 +1,8 @@
/* /*
Copyright (C) 2016 Apple Inc. All Rights Reserved. See LICENSE.txt for this samples licensing information.
See LICENSE.txt for this samples licensing information
Abstract: Abstract:
Application preview view. Application preview view.
*/ */
@import UIKit; @import UIKit;

View File

@ -1,11 +1,9 @@
/* /*
Copyright (C) 2016 Apple Inc. All Rights Reserved. See LICENSE.txt for this samples licensing information.
See LICENSE.txt for this samples licensing information
Abstract: Abstract:
Application preview view. Application preview view.
*/ */
@import AVFoundation; @import AVFoundation;
#import "AVCamPreviewView.h" #import "AVCamPreviewView.h"

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,124 +1,149 @@
{ {
"images" : [ "images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{ {
"size" : "29x29", "size" : "29x29",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-Small.png", "filename" : "AVCam_Icon_29x29-1.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "29x29", "size" : "29x29",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-Small@2x.png", "filename" : "AVCam_Icon_29x29@2x-1.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "29x29", "size" : "29x29",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-Small@3x.png", "filename" : "AVCam_Icon_29x29@3x.png",
"scale" : "3x" "scale" : "3x"
}, },
{ {
"size" : "40x40", "size" : "40x40",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-40@2x.png", "filename" : "AVCam_Icon_40x40@2x-1.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "40x40", "size" : "40x40",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-40@3x.png", "filename" : "AVCam_Icon_40x40@3x.png",
"scale" : "3x" "scale" : "3x"
}, },
{ {
"size" : "57x57", "size" : "57x57",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon.png", "filename" : "AVCam_Icon_57x57.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "57x57", "size" : "57x57",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon@2x.png", "filename" : "AVCam_Icon_57x57@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "60x60", "size" : "60x60",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-60@2x.png", "filename" : "AVCam_Icon_60x60@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "60x60", "size" : "60x60",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-60@3x.png", "filename" : "AVCam_Icon_60x60@3x.png",
"scale" : "3x" "scale" : "3x"
}, },
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{ {
"size" : "29x29", "size" : "29x29",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-Small.png", "filename" : "AVCam_Icon_29x29.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "29x29", "size" : "29x29",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-Small@2x.png", "filename" : "AVCam_Icon_29x29@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "40x40", "size" : "40x40",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-40.png", "filename" : "AVCam_Icon_40x40.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "40x40", "size" : "40x40",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-40@2x.png", "filename" : "AVCam_Icon_40x40@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "50x50", "size" : "50x50",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-Small-50.png", "filename" : "AVCam_Icon_50x50.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "50x50", "size" : "50x50",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-Small-50@2x.png", "filename" : "AVCam_Icon_50x50@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "72x72", "size" : "72x72",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-72.png", "filename" : "AVCam_Icon_72x72.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "72x72", "size" : "72x72",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-72@2x.png", "filename" : "AVCam_Icon_72x72@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "76x76", "size" : "76x76",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-76.png", "filename" : "AVCam_Icon_76x76.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "76x76", "size" : "76x76",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-76@2x.png", "filename" : "AVCam_Icon_76x76@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "83.5x83.5", "size" : "83.5x83.5",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-83.5@2x.png", "filename" : "AVCam_Icon_83.5x83.5.png",
"scale" : "2x" "scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
} }
], ],
"info" : { "info" : {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,24 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11129.12" systemVersion="15F24" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13189.4" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina5_5" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13165.3"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11103.9"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<scenes> <scenes>
<!--Cam Camera View Controller--> <!--Camera View Controller-->
<scene sceneID="tne-QT-ifu"> <scene sceneID="tne-QT-ifu">
<objects> <objects>
<viewController id="BYZ-38-t0r" customClass="AVCamCameraViewController" sceneMemberID="viewController"> <viewController id="BYZ-38-t0r" userLabel="Camera View Controller" customClass="AVCamCameraViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> <rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3eR-Rn-XpZ" userLabel="Preview" customClass="AVCamPreviewView"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3eR-Rn-XpZ" userLabel="Preview" customClass="AVCamPreviewView">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<gestureRecognizers/> <gestureRecognizers/>
<connections> <connections>
@ -26,12 +26,14 @@
</connections> </connections>
</view> </view>
<label hidden="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Camera Unavailable" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zf0-db-esM" userLabel="Camera Unavailable"> <label hidden="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Camera Unavailable" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zf0-db-esM" userLabel="Camera Unavailable">
<rect key="frame" x="103" y="353.66666666666669" width="208" height="29"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="24"/> <fontDescription key="fontDescription" type="system" pointSize="24"/>
<color key="textColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FZr-Ip-7WL" userLabel="Resume"> <button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FZr-Ip-7WL" userLabel="Resume">
<rect key="frame" x="124.66666666666669" y="348.66666666666669" width="165" height="39"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="24"/> <fontDescription key="fontDescription" type="system" pointSize="24"/>
<inset key="contentEdgeInsets" minX="10" minY="5" maxX="10" maxY="5"/> <inset key="contentEdgeInsets" minX="10" minY="5" maxX="10" maxY="5"/>
@ -48,6 +50,7 @@
</connections> </connections>
</button> </button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eRT-dK-6dM" userLabel="Record"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eRT-dK-6dM" userLabel="Record">
<rect key="frame" x="67" y="686" width="80" height="30"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="20"/> <fontDescription key="fontDescription" type="system" pointSize="20"/>
<state key="normal" title="Record"> <state key="normal" title="Record">
@ -62,11 +65,12 @@
<action selector="toggleMovieRecording:" destination="BYZ-38-t0r" eventType="touchUpInside" id="9R7-Ok-FpB"/> <action selector="toggleMovieRecording:" destination="BYZ-38-t0r" eventType="touchUpInside" id="9R7-Ok-FpB"/>
</connections> </connections>
</button> </button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uCj-6P-mHF" userLabel="Still"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uCj-6P-mHF" userLabel="Photo">
<rect key="frame" x="167" y="686" width="80" height="30"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="30" id="NtC-UN-gTs"/> <constraint firstAttribute="height" constant="30" id="NtC-UN-gTs"/>
<constraint firstAttribute="width" constant="80" id="dxU-UP-4Ae"/> <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="80" id="dxU-UP-4Ae"/>
</constraints> </constraints>
<fontDescription key="fontDescription" type="system" pointSize="20"/> <fontDescription key="fontDescription" type="system" pointSize="20"/>
<state key="normal" title="Photo"> <state key="normal" title="Photo">
@ -82,6 +86,7 @@
</connections> </connections>
</button> </button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rUJ-G6-RPv" userLabel="Camera"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rUJ-G6-RPv" userLabel="Camera">
<rect key="frame" x="267" y="686" width="80" height="30"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="20"/> <fontDescription key="fontDescription" type="system" pointSize="20"/>
<state key="normal" title="Camera"> <state key="normal" title="Camera">
@ -96,7 +101,8 @@
<action selector="changeCamera:" destination="BYZ-38-t0r" eventType="touchUpInside" id="3W0-h3-6fc"/> <action selector="changeCamera:" destination="BYZ-38-t0r" eventType="touchUpInside" id="3W0-h3-6fc"/>
</connections> </connections>
</button> </button>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="FAC-co-10c"> <segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="FAC-co-10c" userLabel="Capture Mode">
<rect key="frame" x="155.66666666666669" y="638" width="103.00000000000006" height="29"/>
<segments> <segments>
<segment title="Photo"/> <segment title="Photo"/>
<segment title="Movie"/> <segment title="Movie"/>
@ -105,10 +111,28 @@
<action selector="toggleCaptureMode:" destination="BYZ-38-t0r" eventType="valueChanged" id="SKd-67-ZHh"/> <action selector="toggleCaptureMode:" destination="BYZ-38-t0r" eventType="valueChanged" id="SKd-67-ZHh"/>
</connections> </connections>
</segmentedControl> </segmentedControl>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eI6-gV-W7d"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9i1-NX-Qxg" userLabel="Depth Data Delivery">
<rect key="frame" x="102" y="28" width="210" height="25"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="DBZ-an-hCH"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="20"/>
<state key="normal" title="Depth Data Delivery: On"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="4"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="toggleDepthDataDeliveryMode:" destination="BYZ-38-t0r" eventType="touchUpInside" id="iSb-YO-MyR"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eI6-gV-W7d" userLabel="Live Photo Mode">
<rect key="frame" x="116" y="61" width="182" height="25"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="200" id="heR-zX-F6K"/> <constraint firstAttribute="height" constant="25" id="om7-Gh-HVl"/>
</constraints> </constraints>
<fontDescription key="fontDescription" type="system" pointSize="20"/> <fontDescription key="fontDescription" type="system" pointSize="20"/>
<state key="normal" title="Live Photo Mode: On"/> <state key="normal" title="Live Photo Mode: On"/>
@ -121,12 +145,9 @@
<action selector="toggleLivePhotoMode:" destination="BYZ-38-t0r" eventType="touchUpInside" id="JqX-wJ-Xf1"/> <action selector="toggleLivePhotoMode:" destination="BYZ-38-t0r" eventType="touchUpInside" id="JqX-wJ-Xf1"/>
</connections> </connections>
</button> </button>
<label hidden="YES" opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Live" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Pii-2r-R2l"> <label hidden="YES" opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Live" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Pii-2r-R2l" userLabel="Capturing Live Photo">
<rect key="frame" x="191.66666666666666" y="94" width="31" height="20.666666666666671"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="Kxo-zf-Fe1"/>
<constraint firstAttribute="width" constant="40" id="eRd-mj-8Du"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@ -139,31 +160,36 @@
</subviews> </subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="3eR-Rn-XpZ" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="125-kC-WZF"/> <constraint firstItem="3eR-Rn-XpZ" firstAttribute="trailing" secondItem="nyU-fN-aJh" secondAttribute="trailing" id="2SX-Lc-5HP"/>
<constraint firstItem="eI6-gV-W7d" firstAttribute="top" secondItem="9i1-NX-Qxg" secondAttribute="bottom" constant="8" id="6iA-0j-auu"/>
<constraint firstItem="eI6-gV-W7d" firstAttribute="centerX" secondItem="nyU-fN-aJh" secondAttribute="centerX" id="ACB-oH-2jU"/>
<constraint firstItem="uCj-6P-mHF" firstAttribute="height" secondItem="eRT-dK-6dM" secondAttribute="height" id="AEV-ew-H4g"/> <constraint firstItem="uCj-6P-mHF" firstAttribute="height" secondItem="eRT-dK-6dM" secondAttribute="height" id="AEV-ew-H4g"/>
<constraint firstItem="eI6-gV-W7d" firstAttribute="top" secondItem="y3c-jy-aDJ" secondAttribute="bottom" constant="8" id="Aao-6b-vLN"/> <constraint firstItem="Pii-2r-R2l" firstAttribute="top" secondItem="eI6-gV-W7d" secondAttribute="bottom" constant="8" id="B43-ME-uK5"/>
<constraint firstItem="3eR-Rn-XpZ" firstAttribute="height" secondItem="8bC-Xf-vdC" secondAttribute="height" id="Ice-47-M9N"/> <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="rUJ-G6-RPv" secondAttribute="trailing" constant="20" symbolic="YES" id="J70-ky-10v"/>
<constraint firstItem="uCj-6P-mHF" firstAttribute="top" secondItem="rUJ-G6-RPv" secondAttribute="top" id="NFm-e8-abT"/> <constraint firstItem="uCj-6P-mHF" firstAttribute="top" secondItem="rUJ-G6-RPv" secondAttribute="top" id="NFm-e8-abT"/>
<constraint firstItem="FZr-Ip-7WL" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="OaZ-uO-vXY"/> <constraint firstItem="FZr-Ip-7WL" firstAttribute="centerX" secondItem="nyU-fN-aJh" secondAttribute="centerX" id="OaZ-uO-vXY"/>
<constraint firstItem="FAC-co-10c" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="Oow-A6-mDp"/> <constraint firstItem="FAC-co-10c" firstAttribute="centerX" secondItem="nyU-fN-aJh" secondAttribute="centerX" id="Oow-A6-mDp"/>
<constraint firstItem="9i1-NX-Qxg" firstAttribute="top" secondItem="nyU-fN-aJh" secondAttribute="top" constant="8" id="PNv-qh-VmU"/>
<constraint firstItem="3eR-Rn-XpZ" firstAttribute="top" secondItem="8bC-Xf-vdC" secondAttribute="top" id="Pik-0A-HnE"/>
<constraint firstItem="3eR-Rn-XpZ" firstAttribute="bottom" secondItem="8bC-Xf-vdC" secondAttribute="bottom" id="RNu-d4-nQ2"/>
<constraint firstItem="zf0-db-esM" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="Ris-mI-8lA"/> <constraint firstItem="zf0-db-esM" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="Ris-mI-8lA"/>
<constraint firstItem="Pii-2r-R2l" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="Upd-h8-1dL"/> <constraint firstItem="Pii-2r-R2l" firstAttribute="centerX" secondItem="nyU-fN-aJh" secondAttribute="centerX" id="SXi-MU-H9D"/>
<constraint firstItem="zf0-db-esM" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="W6q-xJ-jfF"/> <constraint firstItem="zf0-db-esM" firstAttribute="centerX" secondItem="nyU-fN-aJh" secondAttribute="centerX" id="W6q-xJ-jfF"/>
<constraint firstItem="uCj-6P-mHF" firstAttribute="height" secondItem="rUJ-G6-RPv" secondAttribute="height" id="aQi-F7-E2b"/> <constraint firstItem="uCj-6P-mHF" firstAttribute="height" secondItem="rUJ-G6-RPv" secondAttribute="height" id="aQi-F7-E2b"/>
<constraint firstItem="uCj-6P-mHF" firstAttribute="top" secondItem="FAC-co-10c" secondAttribute="bottom" constant="20" id="aSR-Je-0lW"/> <constraint firstItem="uCj-6P-mHF" firstAttribute="top" secondItem="FAC-co-10c" secondAttribute="bottom" constant="20" id="aSR-Je-0lW"/>
<constraint firstItem="uCj-6P-mHF" firstAttribute="top" secondItem="eRT-dK-6dM" secondAttribute="top" id="bQd-ro-0Hw"/> <constraint firstItem="uCj-6P-mHF" firstAttribute="top" secondItem="eRT-dK-6dM" secondAttribute="top" id="bQd-ro-0Hw"/>
<constraint firstItem="wfy-db-euE" firstAttribute="top" secondItem="uCj-6P-mHF" secondAttribute="bottom" constant="20" id="eWs-co-Aaz"/> <constraint firstItem="eRT-dK-6dM" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="8bC-Xf-vdC" secondAttribute="leading" constant="20" symbolic="YES" id="cCX-ki-9gT"/>
<constraint firstItem="3eR-Rn-XpZ" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="igk-MQ-CGt"/> <constraint firstItem="nyU-fN-aJh" firstAttribute="bottom" secondItem="uCj-6P-mHF" secondAttribute="bottom" constant="20" id="eWs-co-Aaz"/>
<constraint firstItem="rUJ-G6-RPv" firstAttribute="leading" secondItem="uCj-6P-mHF" secondAttribute="trailing" constant="20" id="lsk-Hm-rTd"/> <constraint firstItem="rUJ-G6-RPv" firstAttribute="leading" secondItem="eRT-dK-6dM" secondAttribute="trailing" constant="120" id="lsk-Hm-rTd"/>
<constraint firstAttribute="centerX" secondItem="uCj-6P-mHF" secondAttribute="centerX" id="m8a-cF-Rf0"/> <constraint firstItem="nyU-fN-aJh" firstAttribute="centerX" secondItem="uCj-6P-mHF" secondAttribute="centerX" id="m8a-cF-Rf0"/>
<constraint firstItem="uCj-6P-mHF" firstAttribute="width" secondItem="rUJ-G6-RPv" secondAttribute="width" id="o8j-gw-35B"/> <constraint firstItem="uCj-6P-mHF" firstAttribute="width" secondItem="rUJ-G6-RPv" secondAttribute="width" id="o8j-gw-35B"/>
<constraint firstItem="Pii-2r-R2l" firstAttribute="top" secondItem="eI6-gV-W7d" secondAttribute="bottom" constant="8" id="oDE-jY-ryC"/>
<constraint firstItem="3eR-Rn-XpZ" firstAttribute="width" secondItem="8bC-Xf-vdC" secondAttribute="width" id="pSC-xP-dl0"/>
<constraint firstItem="eI6-gV-W7d" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="rqt-bn-mSt"/>
<constraint firstItem="uCj-6P-mHF" firstAttribute="width" secondItem="eRT-dK-6dM" secondAttribute="width" id="s8u-Y8-n27"/> <constraint firstItem="uCj-6P-mHF" firstAttribute="width" secondItem="eRT-dK-6dM" secondAttribute="width" id="s8u-Y8-n27"/>
<constraint firstItem="FZr-Ip-7WL" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="sTY-i6-czN"/> <constraint firstItem="FZr-Ip-7WL" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="sTY-i6-czN"/>
<constraint firstItem="9i1-NX-Qxg" firstAttribute="centerX" secondItem="nyU-fN-aJh" secondAttribute="centerX" id="wWj-VD-34F"/>
<constraint firstItem="3eR-Rn-XpZ" firstAttribute="leading" secondItem="nyU-fN-aJh" secondAttribute="leading" id="x70-kJ-WPk"/>
<constraint firstItem="uCj-6P-mHF" firstAttribute="leading" secondItem="eRT-dK-6dM" secondAttribute="trailing" constant="20" id="zwj-TX-t6O"/> <constraint firstItem="uCj-6P-mHF" firstAttribute="leading" secondItem="eRT-dK-6dM" secondAttribute="trailing" constant="20" id="zwj-TX-t6O"/>
</constraints> </constraints>
<viewLayoutGuide key="safeArea" id="nyU-fN-aJh"/>
</view> </view>
<extendedEdge key="edgesForExtendedLayout"/> <extendedEdge key="edgesForExtendedLayout"/>
<nil key="simulatedStatusBarMetrics"/> <nil key="simulatedStatusBarMetrics"/>
@ -172,6 +198,7 @@
<outlet property="cameraUnavailableLabel" destination="zf0-db-esM" id="P9W-lb-Pb8"/> <outlet property="cameraUnavailableLabel" destination="zf0-db-esM" id="P9W-lb-Pb8"/>
<outlet property="captureModeControl" destination="FAC-co-10c" id="KXj-wg-BvS"/> <outlet property="captureModeControl" destination="FAC-co-10c" id="KXj-wg-BvS"/>
<outlet property="capturingLivePhotoLabel" destination="Pii-2r-R2l" id="JAa-4l-5SD"/> <outlet property="capturingLivePhotoLabel" destination="Pii-2r-R2l" id="JAa-4l-5SD"/>
<outlet property="depthDataDeliveryButton" destination="9i1-NX-Qxg" id="The-P3-y3W"/>
<outlet property="livePhotoModeButton" destination="eI6-gV-W7d" id="r9f-cN-YSH"/> <outlet property="livePhotoModeButton" destination="eI6-gV-W7d" id="r9f-cN-YSH"/>
<outlet property="photoButton" destination="uCj-6P-mHF" id="Ha8-ua-hxy"/> <outlet property="photoButton" destination="uCj-6P-mHF" id="Ha8-ua-hxy"/>
<outlet property="previewView" destination="3eR-Rn-XpZ" id="e7I-nu-L6j"/> <outlet property="previewView" destination="3eR-Rn-XpZ" id="e7I-nu-L6j"/>

View File

@ -1,9 +1,8 @@
/* /*
Copyright (C) 2016 Apple Inc. All Rights Reserved. See LICENSE.txt for this samples licensing information.
See LICENSE.txt for this samples licensing information
Abstract: Abstract:
Main application entry point. Main application entry point.
*/ */
@import UIKit; @import UIKit;

View File

@ -1,24 +1,25 @@
# AVCam-iOS: Using AVFoundation to Capture Photos and Movies # AVCam-iOS
AVCam demonstrates how to use the AVFoundation capture API to record movies and capture photos. The sample has a record button for recording movies, a photo button for capturing photos, a Live Photo mode button for enabling Live Photo capture, a capture mode control for toggling between photo and movie capture modes, and a camera button for switching between front and back cameras (on supported devices). AVCam runs only on an actual device, either an iPad or iPhone, and cannot be run in Simulator. AVCam demonstrates how to use the AVFoundation capture API to record movies and capture photos.
## Overview
The sample has a record button for recording movies, a photo button for capturing photos, a Live Photo mode button for enabling Live Photo capture, a Depth Data delivery button for enabling depth data delivery in capture, a capture mode control for toggling between photo and movie capture modes, and a camera button for switching between front and back cameras (on supported devices). AVCam runs only on an actual device, either an iPad or iPhone, and cannot be run in Simulator.
## Requirements ## Requirements
### Build ### Build
Xcode 8.0, iOS 10.0 SDK Xcode 9.0, iOS 11.0 SDK
### Runtime ### Runtime
iOS 10.0 or later iOS 11.0 or later
## Changes from Previous Version ## Changes from Previous Version
- Adopt AVCapturePhotoOutput - Adopt AVCapturePhoto
- Capture Live Photos - Add HEIF and HEVC support
- Add privacy keys to Info.plist - Include Depth Data Delivery in photo capture
- Add a version of AVCam in Swift 3 - Upgrade from Swift 3 to Swift 4
- Remove support for AVCaptureStillImageOutput
- Bug fixes - Bug fixes
Copyright (C) 2016 Apple Inc. All rights reserved.

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array/>
</plist>

View File

@ -17,6 +17,7 @@
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
3E25DCAF1F7EA506007EF37E /* LICENSE.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = LICENSE.txt; path = ../../LICENSE.txt; sourceTree = "<group>"; };
7AA677111CFF765600B353FB /* AVCam.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AVCam.app; sourceTree = BUILT_PRODUCTS_DIR; }; 7AA677111CFF765600B353FB /* AVCam.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AVCam.app; sourceTree = BUILT_PRODUCTS_DIR; };
7AA677141CFF765600B353FB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 7AA677141CFF765600B353FB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AA677161CFF765600B353FB /* CameraViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraViewController.swift; sourceTree = "<group>"; }; 7AA677161CFF765600B353FB /* CameraViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraViewController.swift; sourceTree = "<group>"; };
@ -27,6 +28,7 @@
7AA677261CFF774800B353FB /* PhotoCaptureDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoCaptureDelegate.swift; sourceTree = "<group>"; }; 7AA677261CFF774800B353FB /* PhotoCaptureDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoCaptureDelegate.swift; sourceTree = "<group>"; };
7AA677281CFF7B5C00B353FB /* PreviewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewView.swift; sourceTree = "<group>"; }; 7AA677281CFF7B5C00B353FB /* PreviewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewView.swift; sourceTree = "<group>"; };
7AE4754E1D00FFA900C2CB9E /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; }; 7AE4754E1D00FFA900C2CB9E /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
C0E23044F2276005EA5958F2 /* SampleCode.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = SampleCode.xcconfig; path = ../Configuration/SampleCode.xcconfig; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -40,12 +42,23 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
3E25DCAE1F7EA4F7007EF37E /* LICENSE */ = {
isa = PBXGroup;
children = (
3E25DCAF1F7EA506007EF37E /* LICENSE.txt */,
);
name = LICENSE;
path = AVCam;
sourceTree = "<group>";
};
7AA677081CFF765500B353FB = { 7AA677081CFF765500B353FB = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7AE4754E1D00FFA900C2CB9E /* README.md */, 7AE4754E1D00FFA900C2CB9E /* README.md */,
7AA677131CFF765600B353FB /* AVCam */, 7AA677131CFF765600B353FB /* AVCam */,
7AA677121CFF765600B353FB /* Products */, 7AA677121CFF765600B353FB /* Products */,
DFC195FC1DBE566F0F3F6735 /* Configuration */,
3E25DCAE1F7EA4F7007EF37E /* LICENSE */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@ -72,6 +85,14 @@
path = AVCam; path = AVCam;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
DFC195FC1DBE566F0F3F6735 /* Configuration */ = {
isa = PBXGroup;
children = (
C0E23044F2276005EA5958F2 /* SampleCode.xcconfig */,
);
name = Configuration;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@ -99,8 +120,8 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 0800; LastSwiftUpdateCheck = 0800;
LastUpgradeCheck = 0800; LastUpgradeCheck = 0900;
ORGANIZATIONNAME = "Apple, Inc."; ORGANIZATIONNAME = Apple;
TargetAttributes = { TargetAttributes = {
7AA677101CFF765500B353FB = { 7AA677101CFF765500B353FB = {
CreatedOnToolsVersion = 8.0; CreatedOnToolsVersion = 8.0;
@ -175,6 +196,7 @@
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
7AA677211CFF765600B353FB /* Debug */ = { 7AA677211CFF765600B353FB /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = C0E23044F2276005EA5958F2 /* SampleCode.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
@ -188,8 +210,11 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -211,7 +236,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -223,6 +248,7 @@
}; };
7AA677221CFF765600B353FB /* Release */ = { 7AA677221CFF765600B353FB /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = C0E23044F2276005EA5958F2 /* SampleCode.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
@ -236,8 +262,11 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -253,7 +282,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
@ -264,29 +293,35 @@
}; };
7AA677241CFF765600B353FB /* Debug */ = { 7AA677241CFF765600B353FB /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = C0E23044F2276005EA5958F2 /* SampleCode.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = AVCam/Info.plist; INFOPLIST_FILE = AVCam/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.AVCam"; PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.AVCam${SAMPLE_CODE_DISAMBIGUATOR}";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_VERSION = 3.0; SWIFT_VERSION = 4.0;
}; };
name = Debug; name = Debug;
}; };
7AA677251CFF765600B353FB /* Release */ = { 7AA677251CFF765600B353FB /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = C0E23044F2276005EA5958F2 /* SampleCode.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = AVCam/Info.plist; INFOPLIST_FILE = AVCam/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.AVCam"; PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.AVCam${SAMPLE_CODE_DISAMBIGUATOR}";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_VERSION = 3.0; SWIFT_VERSION = 4.0;
}; };
name = Release; name = Release;
}; };

View File

@ -1,91 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7AA677101CFF765500B353FB"
BuildableName = "AVCam.app"
BlueprintName = "AVCam"
ReferencedContainer = "container:AVCam Swift.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7AA677101CFF765500B353FB"
BuildableName = "AVCam.app"
BlueprintName = "AVCam"
ReferencedContainer = "container:AVCam Swift.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</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 = "7AA677101CFF765500B353FB"
BuildableName = "AVCam.app"
BlueprintName = "AVCam"
ReferencedContainer = "container:AVCam Swift.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7AA677101CFF765500B353FB"
BuildableName = "AVCam.app"
BlueprintName = "AVCam"
ReferencedContainer = "container:AVCam Swift.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -1,9 +1,8 @@
/* /*
Copyright (C) 2016 Apple Inc. All Rights Reserved. See LICENSE.txt for this samples licensing information.
See LICENSE.txt for this samples licensing information
Abstract: Abstract:
Application delegate. Application delegate.
*/ */
import UIKit import UIKit

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,124 +1,149 @@
{ {
"images" : [ "images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{ {
"size" : "29x29", "size" : "29x29",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-Small.png", "filename" : "AVCam_Icon_29x29.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "29x29", "size" : "29x29",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-Small@2x.png", "filename" : "AVCam_Icon_29x29@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "29x29", "size" : "29x29",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-Small@3x.png", "filename" : "AVCam_Icon_29x29@3x.png",
"scale" : "3x" "scale" : "3x"
}, },
{ {
"size" : "40x40", "size" : "40x40",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-40@2x.png", "filename" : "AVCam_Icon_40x40@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "40x40", "size" : "40x40",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-40@3x.png", "filename" : "AVCam_Icon_40x40@3x.png",
"scale" : "3x" "scale" : "3x"
}, },
{ {
"size" : "57x57", "size" : "57x57",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon.png", "filename" : "AVCam_Icon_57x57.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "57x57", "size" : "57x57",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon@2x.png", "filename" : "AVCam_Icon_57x57@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "60x60", "size" : "60x60",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-60@2x.png", "filename" : "AVCam_Icon_60x60@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "60x60", "size" : "60x60",
"idiom" : "iphone", "idiom" : "iphone",
"filename" : "Icon-60@3x.png", "filename" : "AVCam_Icon_60x60@3x.png",
"scale" : "3x" "scale" : "3x"
}, },
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{ {
"size" : "29x29", "size" : "29x29",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-Small.png", "filename" : "AVCam_Icon_29x29-1.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "29x29", "size" : "29x29",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-Small@2x.png", "filename" : "AVCam_Icon_29x29@2x-1.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "40x40", "size" : "40x40",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-40.png", "filename" : "AVCam_Icon_40x40.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "40x40", "size" : "40x40",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-40@2x.png", "filename" : "AVCam_Icon_40x40@2x-1.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "50x50", "size" : "50x50",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-Small-50.png", "filename" : "AVCam_Icon_50x50.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "50x50", "size" : "50x50",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-Small-50@2x.png", "filename" : "AVCam_Icon_50x50@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "72x72", "size" : "72x72",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-72.png", "filename" : "AVCam_Icon_72x72.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "72x72", "size" : "72x72",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-72@2x.png", "filename" : "AVCam_Icon_72x72@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "76x76", "size" : "76x76",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-76.png", "filename" : "AVCam_Icon_76x76.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "76x76", "size" : "76x76",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-76@2x.png", "filename" : "AVCam_Icon_76x76@2x.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "83.5x83.5", "size" : "83.5x83.5",
"idiom" : "ipad", "idiom" : "ipad",
"filename" : "Icon-83.5@2x.png", "filename" : "AVCam_Icon_83.5x83.5.png",
"scale" : "2x" "scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
} }
], ],
"info" : { "info" : {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,24 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11129.12" systemVersion="15F24" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13189.4" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina5_5" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13165.3"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11103.9"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<scenes> <scenes>
<!--Camera View Controller--> <!--Camera View Controller-->
<scene sceneID="tne-QT-ifu"> <scene sceneID="tne-QT-ifu">
<objects> <objects>
<viewController id="BYZ-38-t0r" customClass="CameraViewController" customModule="AVCam" customModuleProvider="target" sceneMemberID="viewController"> <viewController id="BYZ-38-t0r" userLabel="Camera View Controller" customClass="CameraViewController" customModule="AVCam" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> <rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3eR-Rn-XpZ" userLabel="Preview" customClass="PreviewView" customModule="AVCam" customModuleProvider="target"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3eR-Rn-XpZ" userLabel="Preview" customClass="PreviewView" customModule="AVCam" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<gestureRecognizers/> <gestureRecognizers/>
<connections> <connections>
@ -26,12 +26,14 @@
</connections> </connections>
</view> </view>
<label hidden="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Camera Unavailable" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zf0-db-esM" userLabel="Camera Unavailable"> <label hidden="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Camera Unavailable" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zf0-db-esM" userLabel="Camera Unavailable">
<rect key="frame" x="103" y="353.66666666666669" width="208" height="29"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="24"/> <fontDescription key="fontDescription" type="system" pointSize="24"/>
<color key="textColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FZr-Ip-7WL" userLabel="Resume"> <button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FZr-Ip-7WL" userLabel="Resume">
<rect key="frame" x="124.66666666666669" y="348.66666666666669" width="165" height="39"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="24"/> <fontDescription key="fontDescription" type="system" pointSize="24"/>
<inset key="contentEdgeInsets" minX="10" minY="5" maxX="10" maxY="5"/> <inset key="contentEdgeInsets" minX="10" minY="5" maxX="10" maxY="5"/>
@ -44,10 +46,11 @@
</userDefinedRuntimeAttribute> </userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
<connections> <connections>
<action selector="resumeInterruptedSession:" destination="BYZ-38-t0r" eventType="touchUpInside" id="o7T-5Z-tfn"/> <action selector="resumeInterruptedSession:" destination="BYZ-38-t0r" eventType="touchUpInside" id="42K-1B-qJd"/>
</connections> </connections>
</button> </button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eRT-dK-6dM" userLabel="Record"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eRT-dK-6dM" userLabel="Record">
<rect key="frame" x="67" y="686" width="80" height="30"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="20"/> <fontDescription key="fontDescription" type="system" pointSize="20"/>
<state key="normal" title="Record"> <state key="normal" title="Record">
@ -62,11 +65,12 @@
<action selector="toggleMovieRecording:" destination="BYZ-38-t0r" eventType="touchUpInside" id="9R7-Ok-FpB"/> <action selector="toggleMovieRecording:" destination="BYZ-38-t0r" eventType="touchUpInside" id="9R7-Ok-FpB"/>
</connections> </connections>
</button> </button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uCj-6P-mHF" userLabel="Still"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uCj-6P-mHF" userLabel="Photo">
<rect key="frame" x="167" y="686" width="80" height="30"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="30" id="NtC-UN-gTs"/> <constraint firstAttribute="height" constant="30" id="NtC-UN-gTs"/>
<constraint firstAttribute="width" constant="80" id="dxU-UP-4Ae"/> <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="80" id="dxU-UP-4Ae"/>
</constraints> </constraints>
<fontDescription key="fontDescription" type="system" pointSize="20"/> <fontDescription key="fontDescription" type="system" pointSize="20"/>
<state key="normal" title="Photo"> <state key="normal" title="Photo">
@ -82,6 +86,7 @@
</connections> </connections>
</button> </button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rUJ-G6-RPv" userLabel="Camera"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rUJ-G6-RPv" userLabel="Camera">
<rect key="frame" x="267" y="686" width="80" height="30"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="20"/> <fontDescription key="fontDescription" type="system" pointSize="20"/>
<state key="normal" title="Camera"> <state key="normal" title="Camera">
@ -96,7 +101,8 @@
<action selector="changeCamera:" destination="BYZ-38-t0r" eventType="touchUpInside" id="3W0-h3-6fc"/> <action selector="changeCamera:" destination="BYZ-38-t0r" eventType="touchUpInside" id="3W0-h3-6fc"/>
</connections> </connections>
</button> </button>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="FAC-co-10c"> <segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="FAC-co-10c" userLabel="Capture Mode">
<rect key="frame" x="155.66666666666669" y="638" width="103.00000000000006" height="29"/>
<segments> <segments>
<segment title="Photo"/> <segment title="Photo"/>
<segment title="Movie"/> <segment title="Movie"/>
@ -105,10 +111,28 @@
<action selector="toggleCaptureMode:" destination="BYZ-38-t0r" eventType="valueChanged" id="SKd-67-ZHh"/> <action selector="toggleCaptureMode:" destination="BYZ-38-t0r" eventType="valueChanged" id="SKd-67-ZHh"/>
</connections> </connections>
</segmentedControl> </segmentedControl>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eI6-gV-W7d"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9i1-NX-Qxg" userLabel="Depth Data Delivery">
<rect key="frame" x="102" y="28" width="210" height="25"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="DBZ-an-hCH"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="20"/>
<state key="normal" title="Depth Data Delivery: On"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="4"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="toggleDepthDataDeliveryMode:" destination="BYZ-38-t0r" eventType="touchUpInside" id="iSb-YO-MyR"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eI6-gV-W7d" userLabel="Live Photo Mode">
<rect key="frame" x="116" y="61" width="182" height="25"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="200" id="heR-zX-F6K"/> <constraint firstAttribute="height" constant="25" id="om7-Gh-HVl"/>
</constraints> </constraints>
<fontDescription key="fontDescription" type="system" pointSize="20"/> <fontDescription key="fontDescription" type="system" pointSize="20"/>
<state key="normal" title="Live Photo Mode: On"/> <state key="normal" title="Live Photo Mode: On"/>
@ -121,12 +145,9 @@
<action selector="toggleLivePhotoMode:" destination="BYZ-38-t0r" eventType="touchUpInside" id="JqX-wJ-Xf1"/> <action selector="toggleLivePhotoMode:" destination="BYZ-38-t0r" eventType="touchUpInside" id="JqX-wJ-Xf1"/>
</connections> </connections>
</button> </button>
<label hidden="YES" opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Live" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Pii-2r-R2l"> <label hidden="YES" opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Live" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Pii-2r-R2l" userLabel="Capturing Live Photo">
<rect key="frame" x="191.66666666666666" y="94" width="31" height="20.666666666666671"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="Kxo-zf-Fe1"/>
<constraint firstAttribute="width" constant="40" id="eRd-mj-8Du"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@ -139,31 +160,36 @@
</subviews> </subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="3eR-Rn-XpZ" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="125-kC-WZF"/> <constraint firstItem="eI6-gV-W7d" firstAttribute="top" secondItem="9i1-NX-Qxg" secondAttribute="bottom" constant="8" id="6iA-0j-auu"/>
<constraint firstItem="3eR-Rn-XpZ" firstAttribute="trailing" secondItem="nyU-fN-aJh" secondAttribute="trailing" id="8ud-UI-Sfr"/>
<constraint firstItem="eI6-gV-W7d" firstAttribute="centerX" secondItem="nyU-fN-aJh" secondAttribute="centerX" id="ACB-oH-2jU"/>
<constraint firstItem="uCj-6P-mHF" firstAttribute="height" secondItem="eRT-dK-6dM" secondAttribute="height" id="AEV-ew-H4g"/> <constraint firstItem="uCj-6P-mHF" firstAttribute="height" secondItem="eRT-dK-6dM" secondAttribute="height" id="AEV-ew-H4g"/>
<constraint firstItem="eI6-gV-W7d" firstAttribute="top" secondItem="y3c-jy-aDJ" secondAttribute="bottom" constant="8" id="Aao-6b-vLN"/> <constraint firstItem="Pii-2r-R2l" firstAttribute="top" secondItem="eI6-gV-W7d" secondAttribute="bottom" constant="8" id="B43-ME-uK5"/>
<constraint firstItem="3eR-Rn-XpZ" firstAttribute="height" secondItem="8bC-Xf-vdC" secondAttribute="height" id="Ice-47-M9N"/> <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="rUJ-G6-RPv" secondAttribute="trailing" constant="20" symbolic="YES" id="J70-ky-10v"/>
<constraint firstItem="uCj-6P-mHF" firstAttribute="top" secondItem="rUJ-G6-RPv" secondAttribute="top" id="NFm-e8-abT"/> <constraint firstItem="uCj-6P-mHF" firstAttribute="top" secondItem="rUJ-G6-RPv" secondAttribute="top" id="NFm-e8-abT"/>
<constraint firstItem="FZr-Ip-7WL" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="OaZ-uO-vXY"/> <constraint firstItem="FZr-Ip-7WL" firstAttribute="centerX" secondItem="nyU-fN-aJh" secondAttribute="centerX" id="OaZ-uO-vXY"/>
<constraint firstItem="FAC-co-10c" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="Oow-A6-mDp"/> <constraint firstItem="FAC-co-10c" firstAttribute="centerX" secondItem="nyU-fN-aJh" secondAttribute="centerX" id="Oow-A6-mDp"/>
<constraint firstItem="9i1-NX-Qxg" firstAttribute="top" secondItem="nyU-fN-aJh" secondAttribute="top" constant="8" id="PNv-qh-VmU"/>
<constraint firstItem="zf0-db-esM" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="Ris-mI-8lA"/> <constraint firstItem="zf0-db-esM" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="Ris-mI-8lA"/>
<constraint firstItem="Pii-2r-R2l" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="Upd-h8-1dL"/> <constraint firstItem="Pii-2r-R2l" firstAttribute="centerX" secondItem="nyU-fN-aJh" secondAttribute="centerX" id="SXi-MU-H9D"/>
<constraint firstItem="zf0-db-esM" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="W6q-xJ-jfF"/> <constraint firstItem="3eR-Rn-XpZ" firstAttribute="leading" secondItem="nyU-fN-aJh" secondAttribute="leading" id="Sdw-MH-42e"/>
<constraint firstItem="zf0-db-esM" firstAttribute="centerX" secondItem="nyU-fN-aJh" secondAttribute="centerX" id="W6q-xJ-jfF"/>
<constraint firstItem="uCj-6P-mHF" firstAttribute="height" secondItem="rUJ-G6-RPv" secondAttribute="height" id="aQi-F7-E2b"/> <constraint firstItem="uCj-6P-mHF" firstAttribute="height" secondItem="rUJ-G6-RPv" secondAttribute="height" id="aQi-F7-E2b"/>
<constraint firstItem="uCj-6P-mHF" firstAttribute="top" secondItem="FAC-co-10c" secondAttribute="bottom" constant="20" id="aSR-Je-0lW"/> <constraint firstItem="uCj-6P-mHF" firstAttribute="top" secondItem="FAC-co-10c" secondAttribute="bottom" constant="20" id="aSR-Je-0lW"/>
<constraint firstItem="uCj-6P-mHF" firstAttribute="top" secondItem="eRT-dK-6dM" secondAttribute="top" id="bQd-ro-0Hw"/> <constraint firstItem="uCj-6P-mHF" firstAttribute="top" secondItem="eRT-dK-6dM" secondAttribute="top" id="bQd-ro-0Hw"/>
<constraint firstItem="wfy-db-euE" firstAttribute="top" secondItem="uCj-6P-mHF" secondAttribute="bottom" constant="20" id="eWs-co-Aaz"/> <constraint firstItem="eRT-dK-6dM" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="8bC-Xf-vdC" secondAttribute="leading" constant="20" symbolic="YES" id="cCX-ki-9gT"/>
<constraint firstItem="nyU-fN-aJh" firstAttribute="bottom" secondItem="uCj-6P-mHF" secondAttribute="bottom" constant="20" id="eWs-co-Aaz"/>
<constraint firstItem="3eR-Rn-XpZ" firstAttribute="bottom" secondItem="8bC-Xf-vdC" secondAttribute="bottom" id="ea0-4F-Kd7"/>
<constraint firstItem="3eR-Rn-XpZ" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="igk-MQ-CGt"/> <constraint firstItem="3eR-Rn-XpZ" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="igk-MQ-CGt"/>
<constraint firstItem="rUJ-G6-RPv" firstAttribute="leading" secondItem="uCj-6P-mHF" secondAttribute="trailing" constant="20" id="lsk-Hm-rTd"/> <constraint firstItem="rUJ-G6-RPv" firstAttribute="leading" secondItem="eRT-dK-6dM" secondAttribute="trailing" constant="120" id="lsk-Hm-rTd"/>
<constraint firstAttribute="centerX" secondItem="uCj-6P-mHF" secondAttribute="centerX" id="m8a-cF-Rf0"/> <constraint firstItem="nyU-fN-aJh" firstAttribute="centerX" secondItem="uCj-6P-mHF" secondAttribute="centerX" id="m8a-cF-Rf0"/>
<constraint firstItem="uCj-6P-mHF" firstAttribute="width" secondItem="rUJ-G6-RPv" secondAttribute="width" id="o8j-gw-35B"/> <constraint firstItem="uCj-6P-mHF" firstAttribute="width" secondItem="rUJ-G6-RPv" secondAttribute="width" id="o8j-gw-35B"/>
<constraint firstItem="Pii-2r-R2l" firstAttribute="top" secondItem="eI6-gV-W7d" secondAttribute="bottom" constant="8" id="oDE-jY-ryC"/>
<constraint firstItem="3eR-Rn-XpZ" firstAttribute="width" secondItem="8bC-Xf-vdC" secondAttribute="width" id="pSC-xP-dl0"/>
<constraint firstItem="eI6-gV-W7d" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="rqt-bn-mSt"/>
<constraint firstItem="uCj-6P-mHF" firstAttribute="width" secondItem="eRT-dK-6dM" secondAttribute="width" id="s8u-Y8-n27"/> <constraint firstItem="uCj-6P-mHF" firstAttribute="width" secondItem="eRT-dK-6dM" secondAttribute="width" id="s8u-Y8-n27"/>
<constraint firstItem="FZr-Ip-7WL" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="sTY-i6-czN"/> <constraint firstItem="FZr-Ip-7WL" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="sTY-i6-czN"/>
<constraint firstItem="9i1-NX-Qxg" firstAttribute="centerX" secondItem="nyU-fN-aJh" secondAttribute="centerX" id="wWj-VD-34F"/>
<constraint firstItem="uCj-6P-mHF" firstAttribute="leading" secondItem="eRT-dK-6dM" secondAttribute="trailing" constant="20" id="zwj-TX-t6O"/> <constraint firstItem="uCj-6P-mHF" firstAttribute="leading" secondItem="eRT-dK-6dM" secondAttribute="trailing" constant="20" id="zwj-TX-t6O"/>
</constraints> </constraints>
<viewLayoutGuide key="safeArea" id="nyU-fN-aJh"/>
</view> </view>
<extendedEdge key="edgesForExtendedLayout"/> <extendedEdge key="edgesForExtendedLayout"/>
<nil key="simulatedStatusBarMetrics"/> <nil key="simulatedStatusBarMetrics"/>
@ -172,6 +198,7 @@
<outlet property="cameraUnavailableLabel" destination="zf0-db-esM" id="P9W-lb-Pb8"/> <outlet property="cameraUnavailableLabel" destination="zf0-db-esM" id="P9W-lb-Pb8"/>
<outlet property="captureModeControl" destination="FAC-co-10c" id="KXj-wg-BvS"/> <outlet property="captureModeControl" destination="FAC-co-10c" id="KXj-wg-BvS"/>
<outlet property="capturingLivePhotoLabel" destination="Pii-2r-R2l" id="JAa-4l-5SD"/> <outlet property="capturingLivePhotoLabel" destination="Pii-2r-R2l" id="JAa-4l-5SD"/>
<outlet property="depthDataDeliveryButton" destination="9i1-NX-Qxg" id="The-P3-y3W"/>
<outlet property="livePhotoModeButton" destination="eI6-gV-W7d" id="r9f-cN-YSH"/> <outlet property="livePhotoModeButton" destination="eI6-gV-W7d" id="r9f-cN-YSH"/>
<outlet property="photoButton" destination="uCj-6P-mHF" id="Ha8-ua-hxy"/> <outlet property="photoButton" destination="uCj-6P-mHF" id="Ha8-ua-hxy"/>
<outlet property="previewView" destination="3eR-Rn-XpZ" id="e7I-nu-L6j"/> <outlet property="previewView" destination="3eR-Rn-XpZ" id="e7I-nu-L6j"/>

View File

@ -1,9 +1,8 @@
/* /*
Copyright (C) 2016 Apple Inc. All Rights Reserved. See LICENSE.txt for this samples licensing information.
See LICENSE.txt for this samples licensing information
Abstract: Abstract:
View controller for camera interface. View controller for camera interface.
*/ */
import UIKit import UIKit
@ -21,6 +20,7 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
recordButton.isEnabled = false recordButton.isEnabled = false
photoButton.isEnabled = false photoButton.isEnabled = false
livePhotoModeButton.isEnabled = false livePhotoModeButton.isEnabled = false
depthDataDeliveryButton.isEnabled = false
captureModeControl.isEnabled = false captureModeControl.isEnabled = false
// Set up the video preview view. // Set up the video preview view.
@ -31,7 +31,7 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
access is optional. If audio access is denied, audio is not recorded access is optional. If audio access is denied, audio is not recorded
during movie recording. during movie recording.
*/ */
switch AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo) { switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized: case .authorized:
// The user has previously granted access to the camera. // The user has previously granted access to the camera.
break break
@ -46,7 +46,7 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
create an AVCaptureDeviceInput for audio during session setup. create an AVCaptureDeviceInput for audio during session setup.
*/ */
sessionQueue.suspend() sessionQueue.suspend()
AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { [unowned self] granted in AVCaptureDevice.requestAccess(for: .video, completionHandler: { granted in
if !granted { if !granted {
self.setupResult = .notAuthorized self.setupResult = .notAuthorized
} }
@ -68,7 +68,7 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
take a long time. We dispatch session setup to the sessionQueue so take a long time. We dispatch session setup to the sessionQueue so
that the main queue isn't blocked, which keeps the UI responsive. that the main queue isn't blocked, which keeps the UI responsive.
*/ */
sessionQueue.async { [unowned self] in sessionQueue.async {
self.configureSession() self.configureSession()
} }
} }
@ -85,11 +85,18 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
self.isSessionRunning = self.session.isRunning self.isSessionRunning = self.session.isRunning
case .notAuthorized: case .notAuthorized:
DispatchQueue.main.async { [unowned self] in DispatchQueue.main.async {
let message = NSLocalizedString("AVCam doesn't have permission to use the camera, please change privacy settings", comment: "Alert message when the user has denied access to the camera") let changePrivacySetting = "AVCam doesn't have permission to use the camera, please change privacy settings"
let message = NSLocalizedString(changePrivacySetting, comment: "Alert message when the user has denied access to the camera")
let alertController = UIAlertController(title: "AVCam", message: message, preferredStyle: .alert) let alertController = UIAlertController(title: "AVCam", message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"), style: .cancel, handler: nil))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Settings", comment: "Alert button to open Settings"), style: .`default`, handler: { action in alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"),
style: .cancel,
handler: nil))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Settings", comment: "Alert button to open Settings"),
style: .`default`,
handler: { _ in
UIApplication.shared.open(URL(string: UIApplicationOpenSettingsURLString)!, options: [:], completionHandler: nil) UIApplication.shared.open(URL(string: UIApplicationOpenSettingsURLString)!, options: [:], completionHandler: nil)
})) }))
@ -97,10 +104,14 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
} }
case .configurationFailed: case .configurationFailed:
DispatchQueue.main.async { [unowned self] in DispatchQueue.main.async {
let message = NSLocalizedString("Unable to capture media", comment: "Alert message when something goes wrong during capture session configuration") let alertMsg = "Alert message when something goes wrong during capture session configuration"
let message = NSLocalizedString("Unable to capture media", comment: alertMsg)
let alertController = UIAlertController(title: "AVCam", message: message, preferredStyle: .alert) let alertController = UIAlertController(title: "AVCam", message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"), style: .cancel, handler: nil))
alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"),
style: .cancel,
handler: nil))
self.present(alertController, animated: true, completion: nil) self.present(alertController, animated: true, completion: nil)
} }
@ -109,7 +120,7 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
} }
override func viewWillDisappear(_ animated: Bool) { override func viewWillDisappear(_ animated: Bool) {
sessionQueue.async { [unowned self] in sessionQueue.async {
if self.setupResult == .success { if self.setupResult == .success {
self.session.stopRunning() self.session.stopRunning()
self.isSessionRunning = self.session.isRunning self.isSessionRunning = self.session.isRunning
@ -137,7 +148,8 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
if let videoPreviewLayerConnection = previewView.videoPreviewLayer.connection { if let videoPreviewLayerConnection = previewView.videoPreviewLayer.connection {
let deviceOrientation = UIDevice.current.orientation let deviceOrientation = UIDevice.current.orientation
guard let newVideoOrientation = deviceOrientation.videoOrientation, deviceOrientation.isPortrait || deviceOrientation.isLandscape else { guard let newVideoOrientation = AVCaptureVideoOrientation(deviceOrientation: deviceOrientation),
deviceOrientation.isPortrait || deviceOrientation.isLandscape else {
return return
} }
@ -157,7 +169,7 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
private var isSessionRunning = false private var isSessionRunning = false
private let sessionQueue = DispatchQueue(label: "session queue", attributes: [], target: nil) // Communicate with the session and other session objects on this queue. private let sessionQueue = DispatchQueue(label: "session queue") // Communicate with the session and other session objects on this queue.
private var setupResult: SessionSetupResult = .success private var setupResult: SessionSetupResult = .success
@ -175,28 +187,29 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
/* /*
We do not create an AVCaptureMovieFileOutput when setting up the session because the We do not create an AVCaptureMovieFileOutput when setting up the session because the
AVCaptureMovieFileOutput does not support movie recording with AVCaptureSessionPresetPhoto. AVCaptureMovieFileOutput does not support movie recording with AVCaptureSession.Preset.Photo.
*/ */
session.sessionPreset = AVCaptureSessionPresetPhoto session.sessionPreset = .photo
// Add video input. // Add video input.
do { do {
var defaultVideoDevice: AVCaptureDevice? var defaultVideoDevice: AVCaptureDevice?
// Choose the back dual camera if available, otherwise default to a wide angle camera. // Choose the back dual camera if available, otherwise default to a wide angle camera.
if let dualCameraDevice = AVCaptureDevice.defaultDevice(withDeviceType: .builtInDuoCamera, mediaType: AVMediaTypeVideo, position: .back) { if let dualCameraDevice = AVCaptureDevice.default(.builtInDualCamera, for: .video, position: .back) {
defaultVideoDevice = dualCameraDevice defaultVideoDevice = dualCameraDevice
} } else if let backCameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) {
else if let backCameraDevice = AVCaptureDevice.defaultDevice(withDeviceType: .builtInWideAngleCamera, mediaType: AVMediaTypeVideo, position: .back) {
// If the back dual camera is not available, default to the back wide angle camera. // If the back dual camera is not available, default to the back wide angle camera.
defaultVideoDevice = backCameraDevice defaultVideoDevice = backCameraDevice
} } else if let frontCameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front) {
else if let frontCameraDevice = AVCaptureDevice.defaultDevice(withDeviceType: .builtInWideAngleCamera, mediaType: AVMediaTypeVideo, position: .front) { /*
// In some cases where users break their phones, the back wide angle camera is not available. In this case, we should default to the front wide angle camera. In some cases where users break their phones, the back wide angle camera is not available.
In this case, we should default to the front wide angle camera.
*/
defaultVideoDevice = frontCameraDevice defaultVideoDevice = frontCameraDevice
} }
let videoDeviceInput = try AVCaptureDeviceInput(device: defaultVideoDevice) let videoDeviceInput = try AVCaptureDeviceInput(device: defaultVideoDevice!)
if session.canAddInput(videoDeviceInput) { if session.canAddInput(videoDeviceInput) {
session.addInput(videoDeviceInput) session.addInput(videoDeviceInput)
@ -216,22 +229,20 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
let statusBarOrientation = UIApplication.shared.statusBarOrientation let statusBarOrientation = UIApplication.shared.statusBarOrientation
var initialVideoOrientation: AVCaptureVideoOrientation = .portrait var initialVideoOrientation: AVCaptureVideoOrientation = .portrait
if statusBarOrientation != .unknown { if statusBarOrientation != .unknown {
if let videoOrientation = statusBarOrientation.videoOrientation { if let videoOrientation = AVCaptureVideoOrientation(interfaceOrientation: statusBarOrientation) {
initialVideoOrientation = videoOrientation initialVideoOrientation = videoOrientation
} }
} }
self.previewView.videoPreviewLayer.connection.videoOrientation = initialVideoOrientation self.previewView.videoPreviewLayer.connection?.videoOrientation = initialVideoOrientation
} }
} } else {
else {
print("Could not add video device input to the session") print("Could not add video device input to the session")
setupResult = .configurationFailed setupResult = .configurationFailed
session.commitConfiguration() session.commitConfiguration()
return return
} }
} } catch {
catch {
print("Could not create video device input: \(error)") print("Could not create video device input: \(error)")
setupResult = .configurationFailed setupResult = .configurationFailed
session.commitConfiguration() session.commitConfiguration()
@ -240,30 +251,29 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
// Add audio input. // Add audio input.
do { do {
let audioDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeAudio) let audioDevice = AVCaptureDevice.default(for: .audio)
let audioDeviceInput = try AVCaptureDeviceInput(device: audioDevice) let audioDeviceInput = try AVCaptureDeviceInput(device: audioDevice!)
if session.canAddInput(audioDeviceInput) { if session.canAddInput(audioDeviceInput) {
session.addInput(audioDeviceInput) session.addInput(audioDeviceInput)
} } else {
else {
print("Could not add audio device input to the session") print("Could not add audio device input to the session")
} }
} } catch {
catch {
print("Could not create audio device input: \(error)") print("Could not create audio device input: \(error)")
} }
// Add photo output. // Add photo output.
if session.canAddOutput(photoOutput) if session.canAddOutput(photoOutput) {
{
session.addOutput(photoOutput) session.addOutput(photoOutput)
photoOutput.isHighResolutionCaptureEnabled = true photoOutput.isHighResolutionCaptureEnabled = true
photoOutput.isLivePhotoCaptureEnabled = photoOutput.isLivePhotoCaptureSupported photoOutput.isLivePhotoCaptureEnabled = photoOutput.isLivePhotoCaptureSupported
photoOutput.isDepthDataDeliveryEnabled = photoOutput.isDepthDataDeliverySupported
livePhotoMode = photoOutput.isLivePhotoCaptureSupported ? .on : .off livePhotoMode = photoOutput.isLivePhotoCaptureSupported ? .on : .off
} depthDataDeliveryMode = photoOutput.isDepthDataDeliverySupported ? .on : .off
else {
} else {
print("Could not add photo output to the session") print("Could not add photo output to the session")
setupResult = .configurationFailed setupResult = .configurationFailed
session.commitConfiguration() session.commitConfiguration()
@ -273,9 +283,8 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
session.commitConfiguration() session.commitConfiguration()
} }
@IBAction private func resumeInterruptedSession(_ resumeButton: UIButton) @IBAction private func resumeInterruptedSession(_ resumeButton: UIButton) {
{ sessionQueue.async {
sessionQueue.async { [unowned self] in
/* /*
The session might fail to start running, e.g., if a phone or FaceTime call is still The session might fail to start running, e.g., if a phone or FaceTime call is still
using audio or video. A failure to start the session running will be communicated via using audio or video. A failure to start the session running will be communicated via
@ -286,16 +295,15 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
self.session.startRunning() self.session.startRunning()
self.isSessionRunning = self.session.isRunning self.isSessionRunning = self.session.isRunning
if !self.session.isRunning { if !self.session.isRunning {
DispatchQueue.main.async { [unowned self] in DispatchQueue.main.async {
let message = NSLocalizedString("Unable to resume", comment: "Alert message when unable to resume the session running") let message = NSLocalizedString("Unable to resume", comment: "Alert message when unable to resume the session running")
let alertController = UIAlertController(title: "AVCam", message: message, preferredStyle: .alert) let alertController = UIAlertController(title: "AVCam", message: message, preferredStyle: .alert)
let cancelAction = UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"), style: .cancel, handler: nil) let cancelAction = UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"), style: .cancel, handler: nil)
alertController.addAction(cancelAction) alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil) self.present(alertController, animated: true, completion: nil)
} }
} } else {
else { DispatchQueue.main.async {
DispatchQueue.main.async { [unowned self] in
self.resumeButton.isHidden = true self.resumeButton.isHidden = true
} }
} }
@ -310,19 +318,24 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
@IBOutlet private weak var captureModeControl: UISegmentedControl! @IBOutlet private weak var captureModeControl: UISegmentedControl!
@IBAction private func toggleCaptureMode(_ captureModeControl: UISegmentedControl) { @IBAction private func toggleCaptureMode(_ captureModeControl: UISegmentedControl) {
captureModeControl.isEnabled = false
if captureModeControl.selectedSegmentIndex == CaptureMode.photo.rawValue { if captureModeControl.selectedSegmentIndex == CaptureMode.photo.rawValue {
recordButton.isEnabled = false recordButton.isEnabled = false
sessionQueue.async { [unowned self] in sessionQueue.async {
/* /*
Remove the AVCaptureMovieFileOutput from the session because movie recording is Remove the AVCaptureMovieFileOutput from the session because movie recording is
not supported with AVCaptureSessionPresetPhoto. Additionally, Live Photo not supported with AVCaptureSession.Preset.Photo. Additionally, Live Photo
capture is not supported when an AVCaptureMovieFileOutput is connected to the session. capture is not supported when an AVCaptureMovieFileOutput is connected to the session.
*/ */
self.session.beginConfiguration() self.session.beginConfiguration()
self.session.removeOutput(self.movieFileOutput) self.session.removeOutput(self.movieFileOutput!)
self.session.sessionPreset = AVCaptureSessionPresetPhoto self.session.sessionPreset = .photo
self.session.commitConfiguration()
DispatchQueue.main.async {
captureModeControl.isEnabled = true
}
self.movieFileOutput = nil self.movieFileOutput = nil
@ -334,29 +347,43 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
self.livePhotoModeButton.isHidden = false self.livePhotoModeButton.isHidden = false
} }
} }
}
}
else if captureModeControl.selectedSegmentIndex == CaptureMode.movie.rawValue
{
livePhotoModeButton.isHidden = true
sessionQueue.async { [unowned self] in if self.photoOutput.isDepthDataDeliverySupported {
self.photoOutput.isDepthDataDeliveryEnabled = true
DispatchQueue.main.async {
self.depthDataDeliveryButton.isHidden = false
self.depthDataDeliveryButton.isEnabled = true
}
}
self.session.commitConfiguration()
}
} else if captureModeControl.selectedSegmentIndex == CaptureMode.movie.rawValue {
livePhotoModeButton.isHidden = true
depthDataDeliveryButton.isHidden = true
sessionQueue.async {
let movieFileOutput = AVCaptureMovieFileOutput() let movieFileOutput = AVCaptureMovieFileOutput()
if self.session.canAddOutput(movieFileOutput) { if self.session.canAddOutput(movieFileOutput) {
self.session.beginConfiguration() self.session.beginConfiguration()
self.session.addOutput(movieFileOutput) self.session.addOutput(movieFileOutput)
self.session.sessionPreset = AVCaptureSessionPresetHigh self.session.sessionPreset = .high
if let connection = movieFileOutput.connection(withMediaType: AVMediaTypeVideo) { if let connection = movieFileOutput.connection(with: .video) {
if connection.isVideoStabilizationSupported { if connection.isVideoStabilizationSupported {
connection.preferredVideoStabilizationMode = .auto connection.preferredVideoStabilizationMode = .auto
} }
} }
self.session.commitConfiguration() self.session.commitConfiguration()
DispatchQueue.main.async {
captureModeControl.isEnabled = true
}
self.movieFileOutput = movieFileOutput self.movieFileOutput = movieFileOutput
DispatchQueue.main.async { [unowned self] in DispatchQueue.main.async {
self.recordButton.isEnabled = true self.recordButton.isEnabled = true
} }
} }
@ -369,8 +396,8 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
@IBOutlet private weak var cameraButton: UIButton! @IBOutlet private weak var cameraButton: UIButton!
@IBOutlet private weak var cameraUnavailableLabel: UILabel! @IBOutlet private weak var cameraUnavailableLabel: UILabel!
private let videoDeviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera, .builtInDualCamera],
private let videoDeviceDiscoverySession = AVCaptureDeviceDiscoverySession(deviceTypes: [.builtInWideAngleCamera, .builtInDuoCamera], mediaType: AVMediaTypeVideo, position: .unspecified)! mediaType: .video, position: .unspecified)
@IBAction private func changeCamera(_ cameraButton: UIButton) { @IBAction private func changeCamera(_ cameraButton: UIButton) {
cameraButton.isEnabled = false cameraButton.isEnabled = false
@ -379,31 +406,30 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
livePhotoModeButton.isEnabled = false livePhotoModeButton.isEnabled = false
captureModeControl.isEnabled = false captureModeControl.isEnabled = false
sessionQueue.async { [unowned self] in sessionQueue.async {
let currentVideoDevice = self.videoDeviceInput.device let currentVideoDevice = self.videoDeviceInput.device
let currentPosition = currentVideoDevice!.position let currentPosition = currentVideoDevice.position
let preferredPosition: AVCaptureDevicePosition let preferredPosition: AVCaptureDevice.Position
let preferredDeviceType: AVCaptureDeviceType let preferredDeviceType: AVCaptureDevice.DeviceType
switch currentPosition { switch currentPosition {
case .unspecified, .front: case .unspecified, .front:
preferredPosition = .back preferredPosition = .back
preferredDeviceType = .builtInDuoCamera preferredDeviceType = .builtInDualCamera
case .back: case .back:
preferredPosition = .front preferredPosition = .front
preferredDeviceType = .builtInWideAngleCamera preferredDeviceType = .builtInWideAngleCamera
} }
let devices = self.videoDeviceDiscoverySession.devices! let devices = self.videoDeviceDiscoverySession.devices
var newVideoDevice: AVCaptureDevice? = nil var newVideoDevice: AVCaptureDevice? = nil
// First, look for a device with both the preferred position and device type. Otherwise, look for a device with only the preferred position. // First, look for a device with both the preferred position and device type. Otherwise, look for a device with only the preferred position.
if let device = devices.filter({ $0.position == preferredPosition && $0.deviceType == preferredDeviceType }).first { if let device = devices.first(where: { $0.position == preferredPosition && $0.deviceType == preferredDeviceType }) {
newVideoDevice = device newVideoDevice = device
} } else if let device = devices.first(where: { $0.position == preferredPosition }) {
else if let device = devices.filter({ $0.position == preferredPosition }).first {
newVideoDevice = device newVideoDevice = device
} }
@ -417,56 +443,57 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
self.session.removeInput(self.videoDeviceInput) self.session.removeInput(self.videoDeviceInput)
if self.session.canAddInput(videoDeviceInput) { if self.session.canAddInput(videoDeviceInput) {
NotificationCenter.default.removeObserver(self, name: Notification.Name("AVCaptureDeviceSubjectAreaDidChangeNotification"), object: currentVideoDevice!) NotificationCenter.default.removeObserver(self, name: .AVCaptureDeviceSubjectAreaDidChange, object: currentVideoDevice)
NotificationCenter.default.addObserver(self, selector: #selector(self.subjectAreaDidChange), name: Notification.Name("AVCaptureDeviceSubjectAreaDidChangeNotification"), object: videoDeviceInput.device) NotificationCenter.default.addObserver(self, selector: #selector(self.subjectAreaDidChange), name: .AVCaptureDeviceSubjectAreaDidChange, object: videoDeviceInput.device)
self.session.addInput(videoDeviceInput) self.session.addInput(videoDeviceInput)
self.videoDeviceInput = videoDeviceInput self.videoDeviceInput = videoDeviceInput
} } else {
else { self.session.addInput(self.videoDeviceInput)
self.session.addInput(self.videoDeviceInput);
} }
if let connection = self.movieFileOutput?.connection(withMediaType: AVMediaTypeVideo) { if let connection = self.movieFileOutput?.connection(with: .video) {
if connection.isVideoStabilizationSupported { if connection.isVideoStabilizationSupported {
connection.preferredVideoStabilizationMode = .auto connection.preferredVideoStabilizationMode = .auto
} }
} }
/* /*
Set Live Photo capture enabled if it is supported. When changing cameras, the Set Live Photo capture and depth data delivery if it is supported. When changing cameras, the
`isLivePhotoCaptureEnabled` property of the AVCapturePhotoOutput gets set to NO when `livePhotoCaptureEnabled and depthDataDeliveryEnabled` properties of the AVCapturePhotoOutput gets set to NO when
a video device is disconnected from the session. After the new video device is a video device is disconnected from the session. After the new video device is
added to the session, re-enable Live Photo capture on the AVCapturePhotoOutput if it is supported. added to the session, re-enable them on the AVCapturePhotoOutput if it is supported.
*/ */
self.photoOutput.isLivePhotoCaptureEnabled = self.photoOutput.isLivePhotoCaptureSupported; self.photoOutput.isLivePhotoCaptureEnabled = self.photoOutput.isLivePhotoCaptureSupported
self.photoOutput.isDepthDataDeliveryEnabled = self.photoOutput.isDepthDataDeliverySupported
self.session.commitConfiguration() self.session.commitConfiguration()
} } catch {
catch {
print("Error occured while creating video device input: \(error)") print("Error occured while creating video device input: \(error)")
} }
} }
DispatchQueue.main.async { [unowned self] in DispatchQueue.main.async {
self.cameraButton.isEnabled = true self.cameraButton.isEnabled = true
self.recordButton.isEnabled = self.movieFileOutput != nil self.recordButton.isEnabled = self.movieFileOutput != nil
self.photoButton.isEnabled = true self.photoButton.isEnabled = true
self.livePhotoModeButton.isEnabled = true self.livePhotoModeButton.isEnabled = true
self.captureModeControl.isEnabled = true self.captureModeControl.isEnabled = true
self.depthDataDeliveryButton.isEnabled = self.photoOutput.isDepthDataDeliveryEnabled
self.depthDataDeliveryButton.isHidden = !self.photoOutput.isDepthDataDeliverySupported
} }
} }
} }
@IBAction private func focusAndExposeTap(_ gestureRecognizer: UITapGestureRecognizer) { @IBAction private func focusAndExposeTap(_ gestureRecognizer: UITapGestureRecognizer) {
let devicePoint = self.previewView.videoPreviewLayer.captureDevicePointOfInterest(for: gestureRecognizer.location(in: gestureRecognizer.view)) let devicePoint = previewView.videoPreviewLayer.captureDevicePointConverted(fromLayerPoint: gestureRecognizer.location(in: gestureRecognizer.view))
focus(with: .autoFocus, exposureMode: .autoExpose, at: devicePoint, monitorSubjectAreaChange: true) focus(with: .autoFocus, exposureMode: .autoExpose, at: devicePoint, monitorSubjectAreaChange: true)
} }
private func focus(with focusMode: AVCaptureFocusMode, exposureMode: AVCaptureExposureMode, at devicePoint: CGPoint, monitorSubjectAreaChange: Bool) { private func focus(with focusMode: AVCaptureDevice.FocusMode, exposureMode: AVCaptureDevice.ExposureMode, at devicePoint: CGPoint, monitorSubjectAreaChange: Bool) {
sessionQueue.async { [unowned self] in sessionQueue.async {
if let device = self.videoDeviceInput.device { let device = self.videoDeviceInput.device
do { do {
try device.lockForConfiguration() try device.lockForConfiguration()
@ -486,42 +513,48 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
device.isSubjectAreaChangeMonitoringEnabled = monitorSubjectAreaChange device.isSubjectAreaChangeMonitoringEnabled = monitorSubjectAreaChange
device.unlockForConfiguration() device.unlockForConfiguration()
} } catch {
catch {
print("Could not lock device for configuration: \(error)") print("Could not lock device for configuration: \(error)")
} }
} }
} }
}
// MARK: Capturing Photos // MARK: Capturing Photos
private let photoOutput = AVCapturePhotoOutput() private let photoOutput = AVCapturePhotoOutput()
private var inProgressPhotoCaptureDelegates = [Int64 : PhotoCaptureDelegate]() private var inProgressPhotoCaptureDelegates = [Int64: PhotoCaptureProcessor]()
@IBOutlet private weak var photoButton: UIButton! @IBOutlet private weak var photoButton: UIButton!
@IBAction private func capturePhoto(_ photoButton: UIButton) { @IBAction private func capturePhoto(_ photoButton: UIButton) {
/* /*
Retrieve the video preview layer's video orientation on the main queue before Retrieve the video preview layer's video orientation on the main queue before
entering the session queue. We do this to ensure UI elements are accessed on entering the session queue. We do this to ensure UI elements are accessed on
the main thread and session configuration is done on the session queue. the main thread and session configuration is done on the session queue.
*/ */
let videoPreviewLayerOrientation = previewView.videoPreviewLayer.connection.videoOrientation let videoPreviewLayerOrientation = previewView.videoPreviewLayer.connection?.videoOrientation
sessionQueue.async { sessionQueue.async {
// Update the photo output's connection to match the video orientation of the video preview layer. // Update the photo output's connection to match the video orientation of the video preview layer.
if let photoOutputConnection = self.photoOutput.connection(withMediaType: AVMediaTypeVideo) { if let photoOutputConnection = self.photoOutput.connection(with: .video) {
photoOutputConnection.videoOrientation = videoPreviewLayerOrientation photoOutputConnection.videoOrientation = videoPreviewLayerOrientation!
} }
// Capture a JPEG photo with flash set to auto and high resolution photo enabled. var photoSettings = AVCapturePhotoSettings()
let photoSettings = AVCapturePhotoSettings() // Capture HEIF photo when supported, with flash set to auto and high resolution photo enabled.
if self.photoOutput.availablePhotoCodecTypes.contains(.hevc) {
photoSettings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.hevc])
}
if self.videoDeviceInput.device.isFlashAvailable {
photoSettings.flashMode = .auto photoSettings.flashMode = .auto
}
photoSettings.isHighResolutionPhotoEnabled = true photoSettings.isHighResolutionPhotoEnabled = true
if photoSettings.availablePreviewPhotoPixelFormatTypes.count > 0 { if !photoSettings.__availablePreviewPhotoPixelFormatTypes.isEmpty {
photoSettings.previewPhotoFormat = [kCVPixelBufferPixelFormatTypeKey as String : photoSettings.availablePreviewPhotoPixelFormatTypes.first!] photoSettings.previewPhotoFormat = [kCVPixelBufferPixelFormatTypeKey as String: photoSettings.__availablePreviewPhotoPixelFormatTypes.first!]
} }
if self.livePhotoMode == .on && self.photoOutput.isLivePhotoCaptureSupported { // Live Photo capture is not supported in movie mode. if self.livePhotoMode == .on && self.photoOutput.isLivePhotoCaptureSupported { // Live Photo capture is not supported in movie mode.
let livePhotoMovieFileName = NSUUID().uuidString let livePhotoMovieFileName = NSUUID().uuidString
@ -529,45 +562,48 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
photoSettings.livePhotoMovieFileURL = URL(fileURLWithPath: livePhotoMovieFilePath) photoSettings.livePhotoMovieFileURL = URL(fileURLWithPath: livePhotoMovieFilePath)
} }
if self.depthDataDeliveryMode == .on && self.photoOutput.isDepthDataDeliverySupported {
photoSettings.isDepthDataDeliveryEnabled = true
} else {
photoSettings.isDepthDataDeliveryEnabled = false
}
// Use a separate object for the photo capture delegate to isolate each capture life cycle. // Use a separate object for the photo capture delegate to isolate each capture life cycle.
let photoCaptureDelegate = PhotoCaptureDelegate(with: photoSettings, willCapturePhotoAnimation: { let photoCaptureProcessor = PhotoCaptureProcessor(with: photoSettings, willCapturePhotoAnimation: {
DispatchQueue.main.async { [unowned self] in DispatchQueue.main.async {
self.previewView.videoPreviewLayer.opacity = 0 self.previewView.videoPreviewLayer.opacity = 0
UIView.animate(withDuration: 0.25) { [unowned self] in UIView.animate(withDuration: 0.25) {
self.previewView.videoPreviewLayer.opacity = 1 self.previewView.videoPreviewLayer.opacity = 1
} }
} }
}, capturingLivePhoto: { capturing in }, livePhotoCaptureHandler: { capturing in
/* /*
Because Live Photo captures can overlap, we need to keep track of the Because Live Photo captures can overlap, we need to keep track of the
number of in progress Live Photo captures to ensure that the number of in progress Live Photo captures to ensure that the
Live Photo label stays visible during these captures. Live Photo label stays visible during these captures.
*/ */
self.sessionQueue.async { [unowned self] in self.sessionQueue.async {
if capturing { if capturing {
self.inProgressLivePhotoCapturesCount += 1 self.inProgressLivePhotoCapturesCount += 1
} } else {
else {
self.inProgressLivePhotoCapturesCount -= 1 self.inProgressLivePhotoCapturesCount -= 1
} }
let inProgressLivePhotoCapturesCount = self.inProgressLivePhotoCapturesCount let inProgressLivePhotoCapturesCount = self.inProgressLivePhotoCapturesCount
DispatchQueue.main.async { [unowned self] in DispatchQueue.main.async {
if inProgressLivePhotoCapturesCount > 0 { if inProgressLivePhotoCapturesCount > 0 {
self.capturingLivePhotoLabel.isHidden = false self.capturingLivePhotoLabel.isHidden = false
} } else if inProgressLivePhotoCapturesCount == 0 {
else if inProgressLivePhotoCapturesCount == 0 {
self.capturingLivePhotoLabel.isHidden = true self.capturingLivePhotoLabel.isHidden = true
} } else {
else { print("Error: In progress live photo capture count is less than 0")
print("Error: In progress live photo capture count is less than 0");
} }
} }
} }
}, completed: { [unowned self] photoCaptureDelegate in }, completionHandler: { photoCaptureProcessor in
// When the capture is complete, remove a reference to the photo capture delegate so it can be deallocated. // When the capture is complete, remove a reference to the photo capture delegate so it can be deallocated.
self.sessionQueue.async { [unowned self] in self.sessionQueue.async {
self.inProgressPhotoCaptureDelegates[photoCaptureDelegate.requestedPhotoSettings.uniqueID] = nil self.inProgressPhotoCaptureDelegates[photoCaptureProcessor.requestedPhotoSettings.uniqueID] = nil
} }
} }
) )
@ -577,8 +613,8 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
we store it in an array to maintain a strong reference to this object we store it in an array to maintain a strong reference to this object
until the capture is completed. until the capture is completed.
*/ */
self.inProgressPhotoCaptureDelegates[photoCaptureDelegate.requestedPhotoSettings.uniqueID] = photoCaptureDelegate self.inProgressPhotoCaptureDelegates[photoCaptureProcessor.requestedPhotoSettings.uniqueID] = photoCaptureProcessor
self.photoOutput.capturePhoto(with: photoSettings, delegate: photoCaptureDelegate) self.photoOutput.capturePhoto(with: photoSettings, delegate: photoCaptureProcessor)
} }
} }
@ -587,35 +623,58 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
case off case off
} }
private enum DepthDataDeliveryMode {
case on
case off
}
private var livePhotoMode: LivePhotoMode = .off private var livePhotoMode: LivePhotoMode = .off
@IBOutlet private weak var livePhotoModeButton: UIButton! @IBOutlet private weak var livePhotoModeButton: UIButton!
@IBAction private func toggleLivePhotoMode(_ livePhotoModeButton: UIButton) { @IBAction private func toggleLivePhotoMode(_ livePhotoModeButton: UIButton) {
sessionQueue.async { [unowned self] in sessionQueue.async {
self.livePhotoMode = (self.livePhotoMode == .on) ? .off : .on self.livePhotoMode = (self.livePhotoMode == .on) ? .off : .on
let livePhotoMode = self.livePhotoMode let livePhotoMode = self.livePhotoMode
DispatchQueue.main.async { [unowned self] in DispatchQueue.main.async {
if livePhotoMode == .on { if livePhotoMode == .on {
self.livePhotoModeButton.setTitle(NSLocalizedString("Live Photo Mode: On", comment: "Live photo mode button on title"), for: []) self.livePhotoModeButton.setTitle(NSLocalizedString("Live Photo Mode: On", comment: "Live photo mode button on title"), for: [])
} } else {
else {
self.livePhotoModeButton.setTitle(NSLocalizedString("Live Photo Mode: Off", comment: "Live photo mode button off title"), for: []) self.livePhotoModeButton.setTitle(NSLocalizedString("Live Photo Mode: Off", comment: "Live photo mode button off title"), for: [])
} }
} }
} }
} }
private var depthDataDeliveryMode: DepthDataDeliveryMode = .off
@IBOutlet private weak var depthDataDeliveryButton: UIButton!
@IBAction func toggleDepthDataDeliveryMode(_ depthDataDeliveryButton: UIButton) {
sessionQueue.async {
self.depthDataDeliveryMode = (self.depthDataDeliveryMode == .on) ? .off : .on
let depthDataDeliveryMode = self.depthDataDeliveryMode
DispatchQueue.main.async {
if depthDataDeliveryMode == .on {
self.depthDataDeliveryButton.setTitle(NSLocalizedString("Depth Data Delivery: On", comment: "Depth Data Delivery button on title"), for: [])
} else {
self.depthDataDeliveryButton.setTitle(NSLocalizedString("Depth Data Delivery: Off", comment: "Depth Data Delivery button off title"), for: [])
}
}
}
}
private var inProgressLivePhotoCapturesCount = 0 private var inProgressLivePhotoCapturesCount = 0
@IBOutlet var capturingLivePhotoLabel: UILabel! @IBOutlet var capturingLivePhotoLabel: UILabel!
// MARK: Recording Movies // MARK: Recording Movies
private var movieFileOutput: AVCaptureMovieFileOutput? = nil private var movieFileOutput: AVCaptureMovieFileOutput?
private var backgroundRecordingID: UIBackgroundTaskIdentifier? = nil private var backgroundRecordingID: UIBackgroundTaskIdentifier?
@IBOutlet private weak var recordButton: UIButton! @IBOutlet private weak var recordButton: UIButton!
@ -641,9 +700,9 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
before entering the session queue. We do this to ensure UI elements are before entering the session queue. We do this to ensure UI elements are
accessed on the main thread and session configuration is done on the session queue. accessed on the main thread and session configuration is done on the session queue.
*/ */
let videoPreviewLayerOrientation = previewView.videoPreviewLayer.connection.videoOrientation let videoPreviewLayerOrientation = previewView.videoPreviewLayer.connection?.videoOrientation
sessionQueue.async { [unowned self] in sessionQueue.async {
if !movieFileOutput.isRecording { if !movieFileOutput.isRecording {
if UIDevice.current.isMultitaskingSupported { if UIDevice.current.isMultitaskingSupported {
/* /*
@ -658,29 +717,34 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
} }
// Update the orientation on the movie file output video connection before starting recording. // Update the orientation on the movie file output video connection before starting recording.
let movieFileOutputConnection = self.movieFileOutput?.connection(withMediaType: AVMediaTypeVideo) let movieFileOutputConnection = movieFileOutput.connection(with: .video)
movieFileOutputConnection?.videoOrientation = videoPreviewLayerOrientation movieFileOutputConnection?.videoOrientation = videoPreviewLayerOrientation!
let availableVideoCodecTypes = movieFileOutput.availableVideoCodecTypes
if availableVideoCodecTypes.contains(.hevc) {
movieFileOutput.setOutputSettings([AVVideoCodecKey: AVVideoCodecType.hevc], for: movieFileOutputConnection!)
}
// Start recording to a temporary file. // Start recording to a temporary file.
let outputFileName = NSUUID().uuidString let outputFileName = NSUUID().uuidString
let outputFilePath = (NSTemporaryDirectory() as NSString).appendingPathComponent((outputFileName as NSString).appendingPathExtension("mov")!) let outputFilePath = (NSTemporaryDirectory() as NSString).appendingPathComponent((outputFileName as NSString).appendingPathExtension("mov")!)
movieFileOutput.startRecording(toOutputFileURL: URL(fileURLWithPath: outputFilePath), recordingDelegate: self) movieFileOutput.startRecording(to: URL(fileURLWithPath: outputFilePath), recordingDelegate: self)
} } else {
else {
movieFileOutput.stopRecording() movieFileOutput.stopRecording()
} }
} }
} }
func capture(_ captureOutput: AVCaptureFileOutput!, didStartRecordingToOutputFileAt fileURL: URL!, fromConnections connections: [Any]!) { func fileOutput(_ output: AVCaptureFileOutput, didStartRecordingTo fileURL: URL, from connections: [AVCaptureConnection]) {
// Enable the Record button to let the user stop the recording. // Enable the Record button to let the user stop the recording.
DispatchQueue.main.async { [unowned self] in DispatchQueue.main.async {
self.recordButton.isEnabled = true self.recordButton.isEnabled = true
self.recordButton.setTitle(NSLocalizedString("Stop", comment: "Recording button stop title"), for: []) self.recordButton.setTitle(NSLocalizedString("Stop", comment: "Recording button stop title"), for: [])
} }
} }
func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) { func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
/* /*
Note that currentBackgroundRecordingID is used to end the background task Note that currentBackgroundRecordingID is used to end the background task
associated with this recording. This allows a new recording to be started, associated with this recording. This allows a new recording to be started,
@ -691,13 +755,12 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
Note: Since we use a unique file path for each recording, a new recording will Note: Since we use a unique file path for each recording, a new recording will
not overwrite a recording currently being saved. not overwrite a recording currently being saved.
*/ */
func cleanup() { func cleanUp() {
let path = outputFileURL.path let path = outputFileURL.path
if FileManager.default.fileExists(atPath: path) { if FileManager.default.fileExists(atPath: path) {
do { do {
try FileManager.default.removeItem(atPath: path) try FileManager.default.removeItem(atPath: path)
} } catch {
catch {
print("Could not remove file at url: \(outputFileURL)") print("Could not remove file at url: \(outputFileURL)")
} }
} }
@ -714,8 +777,8 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
var success = true var success = true
if error != nil { if error != nil {
print("Movie file finishing error: \(error)") print("Movie file finishing error: \(String(describing: error))")
success = (((error as NSError).userInfo[AVErrorRecordingSuccessfullyFinishedKey] as AnyObject).boolValue)! success = (((error! as NSError).userInfo[AVErrorRecordingSuccessfullyFinishedKey] as AnyObject).boolValue)!
} }
if success { if success {
@ -730,25 +793,23 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
creationRequest.addResource(with: .video, fileURL: outputFileURL, options: options) creationRequest.addResource(with: .video, fileURL: outputFileURL, options: options)
}, completionHandler: { success, error in }, completionHandler: { success, error in
if !success { if !success {
print("Could not save movie to photo library: \(error)") print("Could not save movie to photo library: \(String(describing: error))")
} }
cleanup() cleanUp()
} }
) )
} } else {
else { cleanUp()
cleanup()
} }
} }
} } else {
else { cleanUp()
cleanup()
} }
// Enable the Camera and Record buttons to let the user switch camera and start another recording. // Enable the Camera and Record buttons to let the user switch camera and start another recording.
DispatchQueue.main.async { [unowned self] in DispatchQueue.main.async {
// Only enable the ability to change camera if the device has more than one camera. // Only enable the ability to change camera if the device has more than one camera.
self.cameraButton.isEnabled = self.videoDeviceDiscoverySession.uniqueDevicePositionsCount() > 1 self.cameraButton.isEnabled = self.videoDeviceDiscoverySession.uniqueDevicePositionsCount > 1
self.recordButton.isEnabled = true self.recordButton.isEnabled = true
self.captureModeControl.isEnabled = true self.captureModeControl.isEnabled = true
self.recordButton.setTitle(NSLocalizedString("Record", comment: "Recording button record title"), for: []) self.recordButton.setTitle(NSLocalizedString("Record", comment: "Recording button record title"), for: [])
@ -757,13 +818,32 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
// MARK: KVO and Notifications // MARK: KVO and Notifications
private var sessionRunningObserveContext = 0 private var keyValueObservations = [NSKeyValueObservation]()
private func addObservers() { private func addObservers() {
session.addObserver(self, forKeyPath: "running", options: .new, context: &sessionRunningObserveContext) let keyValueObservation = session.observe(\.isRunning, options: .new) { _, change in
guard let isSessionRunning = change.newValue else { return }
let isLivePhotoCaptureSupported = self.photoOutput.isLivePhotoCaptureSupported
let isLivePhotoCaptureEnabled = self.photoOutput.isLivePhotoCaptureEnabled
let isDepthDeliveryDataSupported = self.photoOutput.isDepthDataDeliverySupported
let isDepthDeliveryDataEnabled = self.photoOutput.isDepthDataDeliveryEnabled
NotificationCenter.default.addObserver(self, selector: #selector(subjectAreaDidChange), name: Notification.Name("AVCaptureDeviceSubjectAreaDidChangeNotification"), object: videoDeviceInput.device) DispatchQueue.main.async {
NotificationCenter.default.addObserver(self, selector: #selector(sessionRuntimeError), name: Notification.Name("AVCaptureSessionRuntimeErrorNotification"), object: session) // Only enable the ability to change camera if the device has more than one camera.
self.cameraButton.isEnabled = isSessionRunning && self.videoDeviceDiscoverySession.uniqueDevicePositionsCount > 1
self.recordButton.isEnabled = isSessionRunning && self.movieFileOutput != nil
self.photoButton.isEnabled = isSessionRunning
self.captureModeControl.isEnabled = isSessionRunning
self.livePhotoModeButton.isEnabled = isSessionRunning && isLivePhotoCaptureEnabled
self.livePhotoModeButton.isHidden = !(isSessionRunning && isLivePhotoCaptureSupported)
self.depthDataDeliveryButton.isEnabled = isSessionRunning && isDepthDeliveryDataEnabled
self.depthDataDeliveryButton.isHidden = !(isSessionRunning && isDepthDeliveryDataSupported)
}
}
keyValueObservations.append(keyValueObservation)
NotificationCenter.default.addObserver(self, selector: #selector(subjectAreaDidChange), name: .AVCaptureDeviceSubjectAreaDidChange, object: videoDeviceInput.device)
NotificationCenter.default.addObserver(self, selector: #selector(sessionRuntimeError), name: .AVCaptureSessionRuntimeError, object: session)
/* /*
A session can only run when the app is full screen. It will be interrupted A session can only run when the app is full screen. It will be interrupted
@ -772,49 +852,29 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
interruptions and show a preview is paused message. See the documentation interruptions and show a preview is paused message. See the documentation
of AVCaptureSessionWasInterruptedNotification for other interruption reasons. of AVCaptureSessionWasInterruptedNotification for other interruption reasons.
*/ */
NotificationCenter.default.addObserver(self, selector: #selector(sessionWasInterrupted), name: Notification.Name("AVCaptureSessionWasInterruptedNotification"), object: session) NotificationCenter.default.addObserver(self, selector: #selector(sessionWasInterrupted), name: .AVCaptureSessionWasInterrupted, object: session)
NotificationCenter.default.addObserver(self, selector: #selector(sessionInterruptionEnded), name: Notification.Name("AVCaptureSessionInterruptionEndedNotification"), object: session) NotificationCenter.default.addObserver(self, selector: #selector(sessionInterruptionEnded), name: .AVCaptureSessionInterruptionEnded, object: session)
} }
private func removeObservers() { private func removeObservers() {
NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(self)
session.removeObserver(self, forKeyPath: "running", context: &sessionRunningObserveContext) for keyValueObservation in keyValueObservations {
} keyValueObservation.invalidate()
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { keyValueObservations.removeAll()
if context == &sessionRunningObserveContext {
let newValue = change?[.newKey] as AnyObject?
guard let isSessionRunning = newValue?.boolValue else { return }
let isLivePhotoCaptureSupported = photoOutput.isLivePhotoCaptureSupported
let isLivePhotoCaptureEnabled = photoOutput.isLivePhotoCaptureEnabled
DispatchQueue.main.async { [unowned self] in
// Only enable the ability to change camera if the device has more than one camera.
self.cameraButton.isEnabled = isSessionRunning && self.videoDeviceDiscoverySession.uniqueDevicePositionsCount() > 1
self.recordButton.isEnabled = isSessionRunning && self.movieFileOutput != nil
self.photoButton.isEnabled = isSessionRunning
self.captureModeControl.isEnabled = isSessionRunning
self.livePhotoModeButton.isEnabled = isSessionRunning && isLivePhotoCaptureEnabled
self.livePhotoModeButton.isHidden = !(isSessionRunning && isLivePhotoCaptureSupported)
}
}
else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
} }
@objc
func subjectAreaDidChange(notification: NSNotification) { func subjectAreaDidChange(notification: NSNotification) {
let devicePoint = CGPoint(x: 0.5, y: 0.5) let devicePoint = CGPoint(x: 0.5, y: 0.5)
focus(with: .autoFocus, exposureMode: .continuousAutoExposure, at: devicePoint, monitorSubjectAreaChange: false) focus(with: .continuousAutoFocus, exposureMode: .continuousAutoExposure, at: devicePoint, monitorSubjectAreaChange: false)
} }
@objc
func sessionRuntimeError(notification: NSNotification) { func sessionRuntimeError(notification: NSNotification) {
guard let errorValue = notification.userInfo?[AVCaptureSessionErrorKey] as? NSError else { guard let error = notification.userInfo?[AVCaptureSessionErrorKey] as? AVError else { return }
return
}
let error = AVError(_nsError: errorValue)
print("Capture session runtime error: \(error)") print("Capture session runtime error: \(error)")
/* /*
@ -823,23 +883,22 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
to try to resume the session running. to try to resume the session running.
*/ */
if error.code == .mediaServicesWereReset { if error.code == .mediaServicesWereReset {
sessionQueue.async { [unowned self] in sessionQueue.async {
if self.isSessionRunning { if self.isSessionRunning {
self.session.startRunning() self.session.startRunning()
self.isSessionRunning = self.session.isRunning self.isSessionRunning = self.session.isRunning
} } else {
else { DispatchQueue.main.async {
DispatchQueue.main.async { [unowned self] in
self.resumeButton.isHidden = false self.resumeButton.isHidden = false
} }
} }
} }
} } else {
else {
resumeButton.isHidden = false resumeButton.isHidden = false
} }
} }
@objc
func sessionWasInterrupted(notification: NSNotification) { func sessionWasInterrupted(notification: NSNotification) {
/* /*
In some scenarios we want to enable the user to resume the session running. In some scenarios we want to enable the user to resume the session running.
@ -849,19 +908,20 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
music playback in control center will not automatically resume the session music playback in control center will not automatically resume the session
running. Also note that it is not always possible to resume, see `resumeInterruptedSession(_:)`. running. Also note that it is not always possible to resume, see `resumeInterruptedSession(_:)`.
*/ */
if let userInfoValue = notification.userInfo?[AVCaptureSessionInterruptionReasonKey] as AnyObject?, let reasonIntegerValue = userInfoValue.integerValue, let reason = AVCaptureSessionInterruptionReason(rawValue: reasonIntegerValue) { if let userInfoValue = notification.userInfo?[AVCaptureSessionInterruptionReasonKey] as AnyObject?,
let reasonIntegerValue = userInfoValue.integerValue,
let reason = AVCaptureSession.InterruptionReason(rawValue: reasonIntegerValue) {
print("Capture session was interrupted with reason \(reason)") print("Capture session was interrupted with reason \(reason)")
var showResumeButton = false var showResumeButton = false
if reason == AVCaptureSessionInterruptionReason.audioDeviceInUseByAnotherClient || reason == AVCaptureSessionInterruptionReason.videoDeviceInUseByAnotherClient { if reason == .audioDeviceInUseByAnotherClient || reason == .videoDeviceInUseByAnotherClient {
showResumeButton = true showResumeButton = true
} } else if reason == .videoDeviceNotAvailableWithMultipleForegroundApps {
else if reason == AVCaptureSessionInterruptionReason.videoDeviceNotAvailableWithMultipleForegroundApps {
// Simply fade-in a label to inform the user that the camera is unavailable. // Simply fade-in a label to inform the user that the camera is unavailable.
cameraUnavailableLabel.alpha = 0 cameraUnavailableLabel.alpha = 0
cameraUnavailableLabel.isHidden = false cameraUnavailableLabel.isHidden = false
UIView.animate(withDuration: 0.25) { [unowned self] in UIView.animate(withDuration: 0.25) {
self.cameraUnavailableLabel.alpha = 1 self.cameraUnavailableLabel.alpha = 1
} }
} }
@ -870,30 +930,31 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
// Simply fade-in a button to enable the user to try to resume the session running. // Simply fade-in a button to enable the user to try to resume the session running.
resumeButton.alpha = 0 resumeButton.alpha = 0
resumeButton.isHidden = false resumeButton.isHidden = false
UIView.animate(withDuration: 0.25) { [unowned self] in UIView.animate(withDuration: 0.25) {
self.resumeButton.alpha = 1 self.resumeButton.alpha = 1
} }
} }
} }
} }
@objc
func sessionInterruptionEnded(notification: NSNotification) { func sessionInterruptionEnded(notification: NSNotification) {
print("Capture session interruption ended") print("Capture session interruption ended")
if !resumeButton.isHidden { if !resumeButton.isHidden {
UIView.animate(withDuration: 0.25, UIView.animate(withDuration: 0.25,
animations: { [unowned self] in animations: {
self.resumeButton.alpha = 0 self.resumeButton.alpha = 0
}, completion: { [unowned self] finished in }, completion: { _ in
self.resumeButton.isHidden = true self.resumeButton.isHidden = true
} }
) )
} }
if !cameraUnavailableLabel.isHidden { if !cameraUnavailableLabel.isHidden {
UIView.animate(withDuration: 0.25, UIView.animate(withDuration: 0.25,
animations: { [unowned self] in animations: {
self.cameraUnavailableLabel.alpha = 0 self.cameraUnavailableLabel.alpha = 0
}, completion: { [unowned self] finished in }, completion: { _ in
self.cameraUnavailableLabel.isHidden = true self.cameraUnavailableLabel.isHidden = true
} }
) )
@ -901,33 +962,31 @@ class CameraViewController: UIViewController, AVCaptureFileOutputRecordingDelega
} }
} }
extension UIDeviceOrientation { extension AVCaptureVideoOrientation {
var videoOrientation: AVCaptureVideoOrientation? { init?(deviceOrientation: UIDeviceOrientation) {
switch self { switch deviceOrientation {
case .portrait: return .portrait case .portrait: self = .portrait
case .portraitUpsideDown: return .portraitUpsideDown case .portraitUpsideDown: self = .portraitUpsideDown
case .landscapeLeft: return .landscapeRight case .landscapeLeft: self = .landscapeRight
case .landscapeRight: return .landscapeLeft case .landscapeRight: self = .landscapeLeft
default: return nil
}
}
init?(interfaceOrientation: UIInterfaceOrientation) {
switch interfaceOrientation {
case .portrait: self = .portrait
case .portraitUpsideDown: self = .portraitUpsideDown
case .landscapeLeft: self = .landscapeLeft
case .landscapeRight: self = .landscapeRight
default: return nil default: return nil
} }
} }
} }
extension UIInterfaceOrientation { extension AVCaptureDevice.DiscoverySession {
var videoOrientation: AVCaptureVideoOrientation? { var uniqueDevicePositionsCount: Int {
switch self { var uniqueDevicePositions: [AVCaptureDevice.Position] = []
case .portrait: return .portrait
case .portraitUpsideDown: return .portraitUpsideDown
case .landscapeLeft: return .landscapeLeft
case .landscapeRight: return .landscapeRight
default: return nil
}
}
}
extension AVCaptureDeviceDiscoverySession {
func uniqueDevicePositionsCount() -> Int {
var uniqueDevicePositions = [AVCaptureDevicePosition]()
for device in devices { for device in devices {
if !uniqueDevicePositions.contains(device.position) { if !uniqueDevicePositions.contains(device.position) {

View File

@ -1,32 +1,34 @@
/* /*
Copyright (C) 2016 Apple Inc. All Rights Reserved. See LICENSE.txt for this samples licensing information.
See LICENSE.txt for this samples licensing information
Abstract: Abstract:
Photo capture delegate. Photo capture delegate.
*/ */
import AVFoundation import AVFoundation
import Photos import Photos
class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate { class PhotoCaptureProcessor: NSObject {
private(set) var requestedPhotoSettings: AVCapturePhotoSettings private(set) var requestedPhotoSettings: AVCapturePhotoSettings
private let willCapturePhotoAnimation: () -> () private let willCapturePhotoAnimation: () -> Void
private let capturingLivePhoto: (Bool) -> () private let livePhotoCaptureHandler: (Bool) -> Void
private let completed: (PhotoCaptureDelegate) -> () private let completionHandler: (PhotoCaptureProcessor) -> Void
private var photoData: Data? = nil private var photoData: Data?
private var livePhotoCompanionMovieURL: URL? = nil private var livePhotoCompanionMovieURL: URL?
init(with requestedPhotoSettings: AVCapturePhotoSettings, willCapturePhotoAnimation: @escaping () -> (), capturingLivePhoto: @escaping (Bool) -> (), completed: @escaping (PhotoCaptureDelegate) -> ()) { init(with requestedPhotoSettings: AVCapturePhotoSettings,
willCapturePhotoAnimation: @escaping () -> Void,
livePhotoCaptureHandler: @escaping (Bool) -> Void,
completionHandler: @escaping (PhotoCaptureProcessor) -> Void) {
self.requestedPhotoSettings = requestedPhotoSettings self.requestedPhotoSettings = requestedPhotoSettings
self.willCapturePhotoAnimation = willCapturePhotoAnimation self.willCapturePhotoAnimation = willCapturePhotoAnimation
self.capturingLivePhoto = capturingLivePhoto self.livePhotoCaptureHandler = livePhotoCaptureHandler
self.completed = completed self.completionHandler = completionHandler
} }
private func didFinish() { private func didFinish() {
@ -34,50 +36,53 @@ class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate {
if FileManager.default.fileExists(atPath: livePhotoCompanionMoviePath) { if FileManager.default.fileExists(atPath: livePhotoCompanionMoviePath) {
do { do {
try FileManager.default.removeItem(atPath: livePhotoCompanionMoviePath) try FileManager.default.removeItem(atPath: livePhotoCompanionMoviePath)
} } catch {
catch {
print("Could not remove file at url: \(livePhotoCompanionMoviePath)") print("Could not remove file at url: \(livePhotoCompanionMoviePath)")
} }
} }
} }
completed(self) completionHandler(self)
} }
func capture(_ captureOutput: AVCapturePhotoOutput, willBeginCaptureForResolvedSettings resolvedSettings: AVCaptureResolvedPhotoSettings) { }
extension PhotoCaptureProcessor: AVCapturePhotoCaptureDelegate {
/*
This extension includes all the delegate callbacks for AVCapturePhotoCaptureDelegate protocol
*/
func photoOutput(_ output: AVCapturePhotoOutput, willBeginCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings) {
if resolvedSettings.livePhotoMovieDimensions.width > 0 && resolvedSettings.livePhotoMovieDimensions.height > 0 { if resolvedSettings.livePhotoMovieDimensions.width > 0 && resolvedSettings.livePhotoMovieDimensions.height > 0 {
capturingLivePhoto(true) livePhotoCaptureHandler(true)
} }
} }
func capture(_ captureOutput: AVCapturePhotoOutput, willCapturePhotoForResolvedSettings resolvedSettings: AVCaptureResolvedPhotoSettings) { func photoOutput(_ output: AVCapturePhotoOutput, willCapturePhotoFor resolvedSettings: AVCaptureResolvedPhotoSettings) {
willCapturePhotoAnimation() willCapturePhotoAnimation()
} }
func capture(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?, previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?) { func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
if let photoSampleBuffer = photoSampleBuffer {
photoData = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: photoSampleBuffer, previewPhotoSampleBuffer: previewPhotoSampleBuffer) if let error = error {
}
else {
print("Error capturing photo: \(error)") print("Error capturing photo: \(error)")
} else {
photoData = photo.fileDataRepresentation()
}
}
func photoOutput(_ output: AVCapturePhotoOutput, didFinishRecordingLivePhotoMovieForEventualFileAt outputFileURL: URL, resolvedSettings: AVCaptureResolvedPhotoSettings) {
livePhotoCaptureHandler(false)
}
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingLivePhotoToMovieFileAt outputFileURL: URL, duration: CMTime, photoDisplayTime: CMTime, resolvedSettings: AVCaptureResolvedPhotoSettings, error: Error?) {
if error != nil {
print("Error processing live photo companion movie: \(String(describing: error))")
return return
} }
}
func capture(_ captureOutput: AVCapturePhotoOutput, didFinishRecordingLivePhotoMovieForEventualFileAt outputFileURL: URL, resolvedSettings: AVCaptureResolvedPhotoSettings) {
capturingLivePhoto(false)
}
func capture(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingLivePhotoToMovieFileAt outputFileURL: URL, duration: CMTime, photoDisplay photoDisplayTime: CMTime, resolvedSettings: AVCaptureResolvedPhotoSettings, error: Error?) {
if let _ = error {
print("Error processing live photo companion movie: \(error)")
return
}
livePhotoCompanionMovieURL = outputFileURL livePhotoCompanionMovieURL = outputFileURL
} }
func capture(_ captureOutput: AVCapturePhotoOutput, didFinishCaptureForResolvedSettings resolvedSettings: AVCaptureResolvedPhotoSettings, error: Error?) { func photoOutput(_ output: AVCapturePhotoOutput, didFinishCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings, error: Error?) {
if let error = error { if let error = error {
print("Error capturing photo: \(error)") print("Error capturing photo: \(error)")
didFinish() didFinish()
@ -90,11 +95,13 @@ class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate {
return return
} }
PHPhotoLibrary.requestAuthorization { [unowned self] status in PHPhotoLibrary.requestAuthorization { status in
if status == .authorized { if status == .authorized {
PHPhotoLibrary.shared().performChanges({ [unowned self] in PHPhotoLibrary.shared().performChanges({
let options = PHAssetResourceCreationOptions()
let creationRequest = PHAssetCreationRequest.forAsset() let creationRequest = PHAssetCreationRequest.forAsset()
creationRequest.addResource(with: .photo, data: photoData, options: nil) options.uniformTypeIdentifier = self.requestedPhotoSettings.processedFileType.map { $0.rawValue }
creationRequest.addResource(with: .photo, data: photoData, options: options)
if let livePhotoCompanionMovieURL = self.livePhotoCompanionMovieURL { if let livePhotoCompanionMovieURL = self.livePhotoCompanionMovieURL {
let livePhotoCompanionMovieFileResourceOptions = PHAssetResourceCreationOptions() let livePhotoCompanionMovieFileResourceOptions = PHAssetResourceCreationOptions()
@ -102,7 +109,7 @@ class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate {
creationRequest.addResource(with: .pairedVideo, fileURL: livePhotoCompanionMovieURL, options: livePhotoCompanionMovieFileResourceOptions) creationRequest.addResource(with: .pairedVideo, fileURL: livePhotoCompanionMovieURL, options: livePhotoCompanionMovieFileResourceOptions)
} }
}, completionHandler: { [unowned self] success, error in }, completionHandler: { _, error in
if let error = error { if let error = error {
print("Error occurered while saving photo to photo library: \(error)") print("Error occurered while saving photo to photo library: \(error)")
} }
@ -110,8 +117,7 @@ class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate {
self.didFinish() self.didFinish()
} }
) )
} } else {
else {
self.didFinish() self.didFinish()
} }
} }

View File

@ -1,9 +1,8 @@
/* /*
Copyright (C) 2016 Apple Inc. All Rights Reserved. See LICENSE.txt for this samples licensing information.
See LICENSE.txt for this samples licensing information
Abstract: Abstract:
Application preview view. Application preview view.
*/ */
import UIKit import UIKit
@ -11,7 +10,11 @@ import AVFoundation
class PreviewView: UIView { class PreviewView: UIView {
var videoPreviewLayer: AVCaptureVideoPreviewLayer { var videoPreviewLayer: AVCaptureVideoPreviewLayer {
return layer as! AVCaptureVideoPreviewLayer guard let layer = layer as? AVCaptureVideoPreviewLayer else {
fatalError("Expected `AVCaptureVideoPreviewLayer` type for layer. Check PreviewView.layerClass implementation.")
}
return layer
} }
var session: AVCaptureSession? { var session: AVCaptureSession? {

Some files were not shown because too many files have changed in this diff Show More