Merge pull request #10 from Lax/update
|
@ -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}
|
|
@ -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.
|
||||||
|
|
|
@ -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>
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
|
|
@ -1,9 +1,8 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2016 Apple Inc. All Rights Reserved.
|
See LICENSE.txt for this sample’s licensing information.
|
||||||
See LICENSE.txt for this sample’s licensing information
|
|
||||||
|
|
||||||
Abstract:
|
Abstract:
|
||||||
Application delegate.
|
Application delegate.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import UIKit;
|
@import UIKit;
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2016 Apple Inc. All Rights Reserved.
|
See LICENSE.txt for this sample’s licensing information.
|
||||||
See LICENSE.txt for this sample’s licensing information
|
|
||||||
|
|
||||||
Abstract:
|
Abstract:
|
||||||
Application delegate.
|
Application delegate.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "AVCamAppDelegate.h"
|
#import "AVCamAppDelegate.h"
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2016 Apple Inc. All Rights Reserved.
|
See LICENSE.txt for this sample’s licensing information.
|
||||||
See LICENSE.txt for this sample’s licensing information
|
|
||||||
|
|
||||||
Abstract:
|
Abstract:
|
||||||
View controller for camera interface.
|
View controller for camera interface.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import UIKit;
|
@import UIKit;
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2016 Apple Inc. All Rights Reserved.
|
See LICENSE.txt for this sample’s licensing information.
|
||||||
See LICENSE.txt for this sample’s 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 {
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2016 Apple Inc. All Rights Reserved.
|
See LICENSE.txt for this sample’s licensing information.
|
||||||
See LICENSE.txt for this sample’s 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;
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2016 Apple Inc. All Rights Reserved.
|
See LICENSE.txt for this sample’s licensing information.
|
||||||
See LICENSE.txt for this sample’s 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];
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2016 Apple Inc. All Rights Reserved.
|
See LICENSE.txt for this sample’s licensing information.
|
||||||
See LICENSE.txt for this sample’s licensing information
|
|
||||||
|
|
||||||
Abstract:
|
Abstract:
|
||||||
Application preview view.
|
Application preview view.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import UIKit;
|
@import UIKit;
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2016 Apple Inc. All Rights Reserved.
|
See LICENSE.txt for this sample’s licensing information.
|
||||||
See LICENSE.txt for this sample’s licensing information
|
|
||||||
|
|
||||||
Abstract:
|
Abstract:
|
||||||
Application preview view.
|
Application preview view.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import AVFoundation;
|
@import AVFoundation;
|
||||||
|
|
||||||
#import "AVCamPreviewView.h"
|
#import "AVCamPreviewView.h"
|
||||||
|
|
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 7.5 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 8.1 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 8.1 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 12 KiB |
|
@ -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" : {
|
||||||
|
|
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 13 KiB |
|
@ -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"/>
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2016 Apple Inc. All Rights Reserved.
|
See LICENSE.txt for this sample’s licensing information.
|
||||||
See LICENSE.txt for this sample’s licensing information
|
|
||||||
|
|
||||||
Abstract:
|
Abstract:
|
||||||
Main application entry point.
|
Main application entry point.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import UIKit;
|
@import UIKit;
|
||||||
|
|
|
@ -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.
|
|
||||||
|
|
|
@ -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>
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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>
|
|
|
@ -1,9 +1,8 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2016 Apple Inc. All Rights Reserved.
|
See LICENSE.txt for this sample’s licensing information.
|
||||||
See LICENSE.txt for this sample’s licensing information
|
|
||||||
|
|
||||||
Abstract:
|
Abstract:
|
||||||
Application delegate.
|
Application delegate.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 7.5 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 8.1 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 8.1 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 12 KiB |
|
@ -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" : {
|
||||||
|
|
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 13 KiB |
|
@ -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"/>
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2016 Apple Inc. All Rights Reserved.
|
See LICENSE.txt for this sample’s licensing information.
|
||||||
See LICENSE.txt for this sample’s 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) {
|
||||||
|
|
|
@ -1,32 +1,34 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2016 Apple Inc. All Rights Reserved.
|
See LICENSE.txt for this sample’s licensing information.
|
||||||
See LICENSE.txt for this sample’s 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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2016 Apple Inc. All Rights Reserved.
|
See LICENSE.txt for this sample’s licensing information.
|
||||||
See LICENSE.txt for this sample’s 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? {
|
||||||
|
|