Merge remote-tracking branch 'upstream/master' into xi-sample-photo-app

This commit is contained in:
Mykyta Bondarenko 2018-03-09 10:22:52 +02:00
Родитель 87b65cce07 80bc5fa6f3
Коммит c35dc62b3e
131 изменённых файлов: 6287 добавлений и 51 удалений

Просмотреть файл

@ -3,7 +3,8 @@ iOS/tvOS/watchOS Samples
The samples in this directory use the Xamarin.iOS toolchain and
Xamarin Studio project files to illustrate the basics of how to
work with iOS and the SDK.
work with iOS and the SDK. Visit the [iOS Sample Gallery](https://developer.xamarin.com/samples/ios/all/)
to download individual samples.
License
-------
@ -23,13 +24,18 @@ iOS 9 features live in the ios9 directory and the iOS 10 feature lives
in the ios10 directory, and so on for iOS 11.
WatchKit
watchOS
--------
WatchKit samples are in the WatchKit directory, there are a couple of them:
watchOS samples are in the watchOS directory, there are a couple of them:
* [WatchKit/WatchKitCatalog](https://github.com/xamarin/monotouch-samples/tree/master/WatchKit/WatchKitCatalog): contains a sample that shows all the UI elements available in WatchKit.
* [WatchKit/GpsWatch](https://github.com/xamarin/monotouch-samples/tree/master/WatchKit/GpsWatch): a GPS app for the Apple Watch.
* [WatchKit/WatchTables](https://github.com/xamarin/monotouch-samples/tree/master/WatchKit/WatchTables): the sample app used in our tutorials to show how to build WatchKit apps.
* [WatchKit/WatchKitCatalog](https://github.com/xamarin/ios-samples/tree/master/WatchKit/WatchKitCatalog): contains a sample that shows all the UI elements available in WatchKit.
* [WatchKit/GpsWatch](https://github.com/xamarin/ios-samples/tree/master/WatchKit/GpsWatch): a GPS app for the Apple Watch.
* [WatchKit/WatchTables](https://github.com/xamarin/ios-samples/tree/master/WatchKit/WatchTables): the sample app used in our tutorials to show how to build WatchKit apps.
tvOS
----
tvOS samples are in the tvos directory, including the [tvOS UI Catalog](https://github.com/xamarin/ios-samples/tree/master/tvos/UICatalog).
Contributing
------------
@ -45,7 +51,7 @@ Samples Submission Guidelines
## Galleries
We love samples! Application samples show off our platform and provide a great way for people to learn our stuff. And we even promote them as a first-class feature of the docs site. You can find our two sample galleries here:
We love samples! Application samples show off our platform and provide a great way for people to learn our stuff. And we even promote them as a first-class feature of the docs site. You can find the sample galleries here:
* [Xamarin Forms Samples](http://developer.xamarin.com/samples/xamarin-forms/all/)
@ -57,13 +63,13 @@ We love samples! Application samples show off our platform and provide a great w
## Sample GitHub Repositories
These sample galleries are populated by samples in our six sample GitHub repos:
These sample galleries are populated by samples in these GitHub repos:
* [https://github.com/xamarin/xamarin-forms-samples](https://github.com/xamarin/xamarin-forms-samples)
* [https://github.com/xamarin/mobile-samples](https://github.com/xamarin/mobile-samples)
* [https://github.com/xamarin/monotouch-samples](https://github.com/xamarin/monotouch-samples)
* [https://github.com/xamarin/monotouch-samples](https://github.com/xamarin/ios-samples)
* [https://github.com/xamarin/mac-samples](https://github.com/xamarin/mac-samples)
@ -76,9 +82,9 @@ The [mac-ios-samples](https://github.com/xamarin/mac-ios-samples) repository is
## Sample Requirements
We welcome sample submissions. Please ping Nat or Miguel for repo commit access.
We welcome sample submissions, please start by creating an issue with your proposal.
However, because the sample galleries are powered by the github sample repos, each sample needs to have the following things:
Because the sample galleries are powered by the github sample repos, each sample needs to have the following things:
* **Screenshots** - a folder called Screenshots that has at least one screen shot of the sample (preferably a screen shot for every page or every major functionality piece, people really key off these things). for the xplat samples, the folder should be split into platform folders, e.g. iOS, Android, Windows. see[ https://github.com/xamarin/mobile-samples/tree/master/Tasky/Screenshots](https://github.com/xamarin/mobile-samples/tree/master/Tasky/Screenshots) for an example of this.
@ -129,7 +135,7 @@ However, because the sample galleries are powered by the github sample repos, ea
A good example of this stuff is here in the drawing sample:[ https://github.com/xamarin/monotouch-samples/tree/master/Drawing](https://github.com/xamarin/monotouch-samples/tree/master/Drawing)
For a x-platform sample, please see: https://github.com/xamarin/mobile-samples/tree/master/Tasky
For a cross-platform sample, please see: https://github.com/xamarin/mobile-samples/tree/master/Tasky
## GitHub Integration

Просмотреть файл

@ -5,7 +5,7 @@
<Level>Beginner</Level>
<Tags>iOS10, Media</Tags>
<SupportedPlatforms>iOS</SupportedPlatforms>
<Gallery>true</Gallery>
<Gallery>false</Gallery>
<MinimumLicenseRequirement>Starter</MinimumLicenseRequirement>
<Brief>Using AVFoundation to Capture Photos and Movies</Brief>
</SampleMetadata>

Просмотреть файл

@ -5,7 +5,7 @@
<Level>Intermediate</Level>
<Tags>iOS10, Graphics, Media, Platform Features</Tags>
<SupportedPlatforms>iOS</SupportedPlatforms>
<Gallery>true</Gallery>
<Gallery>false</Gallery>
<MinimumLicenseRequirement>Starter</MinimumLicenseRequirement>
<Brief>Using AVFoundation to Detect Barcodes and Faces</Brief>
</SampleMetadata>

Просмотреть файл

@ -1,6 +1,8 @@
AVCamBarcode
============
> [!NOTE] This sample replaced by the [iOS 11 version](https://github.com/xamarin/ios-samples/tree/master/ios11/AVCamBarcode)
`AVCamBarcode` demonstrates how to use the `AVFoundation` capture API to detect barcodes and faces.
## Machine Readable Codes

Просмотреть файл

@ -26,7 +26,7 @@
<MtouchUseSGen>true</MtouchUseSGen>
<MtouchUseRefCounting>true</MtouchUseRefCounting>
<MtouchLink>None</MtouchLink>
<MtouchArch>i386, x86_64</MtouchArch>
<MtouchArch>x86_64</MtouchArch>
<MtouchHttpClientHandler>HttpClientHandler</MtouchHttpClientHandler>
<MtouchTlsProvider>Default</MtouchTlsProvider>
<PlatformTarget>x86</PlatformTarget>
@ -43,7 +43,7 @@
<MtouchFloat32>true</MtouchFloat32>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<MtouchLink>SdkOnly</MtouchLink>
<MtouchArch>ARMv7, ARM64</MtouchArch>
<MtouchArch>ARM64</MtouchArch>
<MtouchHttpClientHandler>HttpClientHandler</MtouchHttpClientHandler>
<MtouchTlsProvider>Default</MtouchTlsProvider>
<PlatformTarget>x86</PlatformTarget>
@ -58,7 +58,7 @@
<MtouchUseSGen>true</MtouchUseSGen>
<MtouchUseRefCounting>true</MtouchUseRefCounting>
<MtouchLink>None</MtouchLink>
<MtouchArch>i386, x86_64</MtouchArch>
<MtouchArch>x86_64</MtouchArch>
<MtouchHttpClientHandler>HttpClientHandler</MtouchHttpClientHandler>
<MtouchTlsProvider>Default</MtouchTlsProvider>
<PlatformTarget>x86</PlatformTarget>
@ -82,7 +82,7 @@
<MtouchFloat32>true</MtouchFloat32>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<MtouchLink>SdkOnly</MtouchLink>
<MtouchArch>ARMv7, ARM64</MtouchArch>
<MtouchArch>ARM64</MtouchArch>
<MtouchHttpClientHandler>HttpClientHandler</MtouchHttpClientHandler>
<MtouchTlsProvider>Default</MtouchTlsProvider>
<PlatformTarget>x86</PlatformTarget>

Просмотреть файл

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@ -26,7 +26,7 @@
<MtouchUseSGen>true</MtouchUseSGen>
<MtouchUseRefCounting>true</MtouchUseRefCounting>
<MtouchLink>None</MtouchLink>
<MtouchArch>i386, x86_64</MtouchArch>
<MtouchArch>x86_64</MtouchArch>
<MtouchHttpClientHandler>HttpClientHandler</MtouchHttpClientHandler>
<MtouchTlsProvider>Default</MtouchTlsProvider>
<DeviceSpecificBuild>false</DeviceSpecificBuild>
@ -44,7 +44,7 @@
<MtouchFloat32>true</MtouchFloat32>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<MtouchLink>SdkOnly</MtouchLink>
<MtouchArch>ARMv7, ARM64</MtouchArch>
<MtouchArch>ARM64</MtouchArch>
<MtouchHttpClientHandler>HttpClientHandler</MtouchHttpClientHandler>
<MtouchTlsProvider>Default</MtouchTlsProvider>
</PropertyGroup>
@ -59,7 +59,7 @@
<MtouchUseSGen>true</MtouchUseSGen>
<MtouchUseRefCounting>true</MtouchUseRefCounting>
<MtouchLink>None</MtouchLink>
<MtouchArch>i386</MtouchArch>
<MtouchArch>x86_64</MtouchArch>
<MtouchHttpClientHandler>HttpClientHandler</MtouchHttpClientHandler>
<MtouchTlsProvider>Default</MtouchTlsProvider>
</PropertyGroup>
@ -83,7 +83,7 @@
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<IOSDebuggerPort>10001</IOSDebuggerPort>
<MtouchLink>SdkOnly</MtouchLink>
<MtouchArch>ARMv7, ARMv7s, ARM64</MtouchArch>
<MtouchArch>ARM64</MtouchArch>
<MtouchHttpClientHandler>HttpClientHandler</MtouchHttpClientHandler>
<MtouchTlsProvider>Default</MtouchTlsProvider>
</PropertyGroup>

Просмотреть файл

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27205.2004
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ARKitAudio", "ARKitAudio\ARKitAudio.csproj", "{BC14A9A0-F771-4C15-BCD7-7974A2A71260}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|iPhone = Debug|iPhone
Debug|iPhoneSimulator = Debug|iPhoneSimulator
Release|iPhone = Release|iPhone
Release|iPhoneSimulator = Release|iPhoneSimulator
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{BC14A9A0-F771-4C15-BCD7-7974A2A71260}.Debug|iPhone.ActiveCfg = Debug|iPhone
{BC14A9A0-F771-4C15-BCD7-7974A2A71260}.Debug|iPhone.Build.0 = Debug|iPhone
{BC14A9A0-F771-4C15-BCD7-7974A2A71260}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{BC14A9A0-F771-4C15-BCD7-7974A2A71260}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{BC14A9A0-F771-4C15-BCD7-7974A2A71260}.Release|iPhone.ActiveCfg = Release|iPhone
{BC14A9A0-F771-4C15-BCD7-7974A2A71260}.Release|iPhone.Build.0 = Release|iPhone
{BC14A9A0-F771-4C15-BCD7-7974A2A71260}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{BC14A9A0-F771-4C15-BCD7-7974A2A71260}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {04A35DA2-FEED-451C-B35B-3CDFD32AE9FB}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
<ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<ProjectGuid>{BC14A9A0-F771-4C15-BCD7-7974A2A71260}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>ARKitAudio</RootNamespace>
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
<AssemblyName>ARKitAudio</AssemblyName>
<UseMSBuildEngine>True</UseMSBuildEngine>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhoneSimulator\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<MtouchArch>x86_64</MtouchArch>
<MtouchLink>None</MtouchLink>
<MtouchDebug>true</MtouchDebug>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
<DebugType>full</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhoneSimulator\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<MtouchLink>None</MtouchLink>
<MtouchArch>x86_64</MtouchArch>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhone\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<MtouchArch>ARM64</MtouchArch>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<MtouchDebug>true</MtouchDebug>
<CodesignKey>iPhone Developer</CodesignKey>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
<DebugType>full</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhone\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<MtouchArch>ARM64</MtouchArch>
<ConsolePause>false</ConsolePause>
<CodesignKey>iPhone Developer</CodesignKey>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="Xamarin.iOS" />
</ItemGroup>
<ItemGroup>
<InterfaceDefinition Include="Resources\LaunchScreen.storyboard" />
<SceneKitAsset Include="art.scnassets\sharedImages\environment_blur.exr" />
<SceneKitAsset Include="art.scnassets\Scene.scn" />
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
<SceneKitAsset Include="art.scnassets\candle\candle.scn" />
<SceneKitAsset Include="art.scnassets\candle\textures\candle_AO.png" />
<SceneKitAsset Include="art.scnassets\candle\textures\candle_DIFFUSE.png" />
<SceneKitAsset Include="art.scnassets\candle\textures\candle_METALLIC.png" />
<SceneKitAsset Include="art.scnassets\candle\textures\candle_NORMAL.png" />
<SceneKitAsset Include="art.scnassets\candle\textures\candle_ROUGHNESS.png" />
<SceneKitAsset Include="art.scnassets\candle\textures\candle_SHADOW.png" />
<SceneKitAsset Include="art.scnassets\candle\textures\flame_PARTICLE.png" />
</ItemGroup>
<ItemGroup>
<Compile Include="PreviewNode.cs" />
<Compile Include="ViewController.cs" />
<Compile Include="ViewController.designer.cs">
<DependentUpon>ViewController.cs</DependentUpon>
</Compile>
<Compile Include="Extensions\ARSCNViewExtensions.cs" />
<Compile Include="Extensions\Utilities.cs" />
<Compile Include="Main.cs" />
<Compile Include="AppDelegate.cs" />
<ImageAsset Include="Resources\Images.xcassets\AppIcons.appiconset\Contents.json">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Images.xcassets\AppIcons.appiconset\icon-app-60@2x.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Images.xcassets\AppIcons.appiconset\Icon-app-60@3x.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Images.xcassets\AppIcons.appiconset\icon-app-76.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Images.xcassets\AppIcons.appiconset\icon-app-76@2x.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Images.xcassets\AppIcons.appiconset\icon-spotlight-29.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Images.xcassets\AppIcons.appiconset\icon-spotlight-29@2x.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Images.xcassets\AppIcons.appiconset\icon-spotlight-40.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Images.xcassets\AppIcons.appiconset\icon-spotlight-40@2x.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Images.xcassets\AppIcons.appiconset\icon-spotlight-40@3x.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Images.xcassets\AppIcons.appiconset\Icon-app-83.5%402x.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Images.xcassets\AppIcons.appiconset\app-store-logo.png">
<Visible>false</Visible>
</ImageAsset>
</ItemGroup>
<ItemGroup>
<InterfaceDefinition Include="Resources\Main.storyboard" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
<ItemGroup>
<SceneKitAsset Include="art.scnassets\sharedImages\environment.jpg" />
</ItemGroup>
<ItemGroup>
<SceneKitAsset Include="art.scnassets\ping.aif" />
</ItemGroup>
<ItemGroup>
<Folder Include="art.scnassets\candle\" />
<Folder Include="art.scnassets\candle\textures\" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,52 @@

namespace ARKitAudio
{
using Foundation;
using UIKit;
// The UIApplicationDelegate for the application. This class is responsible for launching the
// User Interface of the application, as well as listening (and optionally responding) to
// application events from iOS.
[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
// class-level declarations
public override UIWindow Window {
get;
set;
}
public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
// Override point for customization after application launch.
return true;
}
//
// This method is invoked when the application is about to move from active to inactive state.
//
// OpenGL applications should use this method to pause.
//
public override void OnResignActivation (UIApplication application)
{
}
// This method should be used to release shared resources and it should store the application state.
// If your application supports background exection this method is called instead of WillTerminate
// when the user quits.
public override void DidEnterBackground (UIApplication application)
{
}
// This method is called as part of the transiton from background to active state.
public override void WillEnterForeground (UIApplication application)
{
}
// This method is called when the application is about to terminate. Save data, if needed.
public override void WillTerminate (UIApplication application)
{
}
}
}

Просмотреть файл

@ -0,0 +1,6 @@
<?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">
<dict>
</dict>
</plist>

Просмотреть файл

@ -0,0 +1,228 @@

namespace ARKitAudio
{
using ARKit;
using CoreGraphics;
using OpenTK;
using SceneKit;
using System;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// Convenience extensions on ARSCNView for hit testing
/// </summary>
public static class ARSCNViewExtensions
{
public static HitTestRay? HitTestRayFromScreenPosition(this ARSCNView view, CGPoint point)
{
HitTestRay? result = null;
using (var frame = view.Session.CurrentFrame)
{
if (frame != null)
{
var cameraPosition = frame.Camera.Transform.GetTranslation();
// Note: z: 1.0 will unproject() the screen position to the far clipping plane.
var positionVector = new SCNVector3((float)point.X, (float)point.Y, 1f);
var screenPositionOnFarClippingPlane = view.UnprojectPoint(positionVector);
var rayDirection = SCNVector3.Normalize(screenPositionOnFarClippingPlane - cameraPosition);
result = new HitTestRay { Origin = cameraPosition, Direction = rayDirection };
}
}
return result;
}
public static SCNVector3? HitTestWithInfiniteHorizontalPlane(this ARSCNView view, CGPoint point, SCNVector3 pointOnPlane)
{
SCNVector3? result = null;
var ray = view.HitTestRayFromScreenPosition(point);
if (ray.HasValue)
{
// Do not intersect with planes above the camera or if the ray is almost parallel to the plane.
if (ray.Value.Direction.Y <= -0.03f)
{
// Return the intersection of a ray from the camera through the screen position with a horizontal plane
// at height (Y axis).
result = Utilities.RayIntersectionWithHorizontalPlane(ray.Value.Origin, ray.Value.Direction, pointOnPlane.Y);
}
}
return result;
}
public static IList<FeatureHitTestResult> HitTestWithFeatures(this ARSCNView view,
CGPoint point,
float coneOpeningAngleInDegrees,
float minDistance = 0,
float maxDistance = float.MaxValue,
int maxResults = 1)
{
var results = new List<FeatureHitTestResult>();
ARPointCloud features = null;
using(var frame = view.Session.CurrentFrame)
{
features = frame?.RawFeaturePoints;
}
if (features != null)
{
var ray = view.HitTestRayFromScreenPosition(point);
if (ray.HasValue)
{
var maxAngleInDegrees = Math.Min(coneOpeningAngleInDegrees, 360f) / 2f;
var maxAngle = (maxAngleInDegrees / 180f) * Math.PI;
var points = features.Points;
for (nuint j = 0; j < features.Count; j++)
{
var feature = points[j];
var featurePosition = new SCNVector3((Vector3)feature);
var originToFeature = featurePosition - ray.Value.Origin;
var crossProduct = SCNVector3.Cross(originToFeature, ray.Value.Direction);
var featureDistanceFromResult = crossProduct.Length;
var hitTestResult = ray.Value.Origin + (ray.Value.Direction * SCNVector3.Dot(ray.Value.Direction, originToFeature));
var hitTestResultDistance = (hitTestResult - ray.Value.Origin).Length;
if (hitTestResultDistance < minDistance || hitTestResultDistance > maxDistance)
{
// Skip this feature - it is too close or too far away.
continue;
}
var originToFeatureNormalized = SCNVector3.Normalize(originToFeature);
var angleBetweenRayAndFeature = Math.Acos(SCNVector3.Dot(ray.Value.Direction, originToFeatureNormalized));
if (angleBetweenRayAndFeature > maxAngle)
{
// Skip this feature - is outside of the hit test cone.
continue;
}
// All tests passed: Add the hit against this feature to the results.
results.Add(new FeatureHitTestResult
{
Position = hitTestResult,
DistanceToRayOrigin = hitTestResultDistance,
FeatureHit = featurePosition,
FeatureDistanceToHitResult = featureDistanceFromResult
});
}
// Sort the results by feature distance to the ray.
results = results.OrderBy(result => result.DistanceToRayOrigin).ToList();
// Cap the list to maxResults.
var cappedResults = new List<FeatureHitTestResult>();
var i = 0;
while (i < maxResults && i < results.Count)
{
cappedResults.Add(results[i]);
i += 1;
}
results = cappedResults;
}
}
return results;
}
public static IList<FeatureHitTestResult> HitTestWithFeatures(this ARSCNView view, CGPoint point)
{
var results = new List<FeatureHitTestResult>();
var ray = view.HitTestRayFromScreenPosition(point);
if (ray.HasValue)
{
var result = view.HitTestFromOrigin(ray.Value.Origin, ray.Value.Direction);
if (result.HasValue)
{
results.Add(result.Value);
}
}
return results;
}
public static FeatureHitTestResult? HitTestFromOrigin(this ARSCNView view, SCNVector3 origin, SCNVector3 direction)
{
FeatureHitTestResult? result = null;
ARPointCloud features = null;
using (var frame = view.Session.CurrentFrame)
{
features = frame?.RawFeaturePoints;
}
if (features != null)
{
var points = features.Points;
// Determine the point from the whole point cloud which is closest to the hit test ray.
var closestFeaturePoint = origin;
var minDistance = float.MaxValue; // Float.greatestFiniteMagnitude
for (nuint i = 0; i < features.Count; i++)
{
var feature = points[i];
var featurePosition = new SCNVector3((Vector3)feature);
var originVector = origin - featurePosition;
var crossProduct = SCNVector3.Cross(originVector, direction);
var featureDistanceFromResult = crossProduct.Length;
if (featureDistanceFromResult < minDistance)
{
closestFeaturePoint = featurePosition;
minDistance = featureDistanceFromResult;
}
}
// Compute the point along the ray that is closest to the selected feature.
var originToFeature = closestFeaturePoint - origin;
var hitTestResult = origin + (direction * SCNVector3.Dot(direction, originToFeature));
var hitTestResultDistance = (hitTestResult - origin).Length;
result = new FeatureHitTestResult
{
Position = hitTestResult,
DistanceToRayOrigin = hitTestResultDistance,
FeatureHit = closestFeaturePoint,
FeatureDistanceToHitResult = minDistance
};
}
return result;
}
}
public struct HitTestRay
{
public SCNVector3 Origin { get; set; }
public SCNVector3 Direction { get; set; }
}
public struct FeatureHitTestResult
{
public SCNVector3 Position { get; set; }
public float DistanceToRayOrigin { get; set; }
public SCNVector3 FeatureHit { get; set; }
public float FeatureDistanceToHitResult { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,162 @@

namespace ARKitAudio
{
using ARKit;
using CoreGraphics;
using SceneKit;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// Utility functions and type extensions used throughout the projects.
/// </summary>
public static class Utilities
{
public static SCNVector3? GetAverage(this IList<SCNVector3> vectors)
{
SCNVector3? result = null;
if (vectors != null && vectors.Count > 0)
{
var sum = vectors.Aggregate(SCNVector3.Zero, (vector1, vector2) => vector1 + vector2);
result = sum / vectors.Count;
}
return result;
}
public static void KeepLast<T>(this List<T> list, int elementsToKeep)
{
if (list.Count > elementsToKeep)
{
list.RemoveRange(0, list.Count - elementsToKeep);
}
}
/// <summary>
/// Treats matrix as a (right-hand column-major convention) transform matrix
/// and factors out the translation component of the transform.
/// </summary>
public static OpenTK.Vector3 GetTranslation(this OpenTK.NMatrix4 matrix)
{
var translation = matrix.Column3;
return new OpenTK.Vector3(translation.X, translation.Y, translation.Z);
}
#region Math
public static SCNVector3? RayIntersectionWithHorizontalPlane(SCNVector3 rayOrigin, SCNVector3 direction, float planeY)
{
direction = SCNVector3.Normalize(direction);
// Special case handling: Check if the ray is horizontal as well.
if (direction.Y == 0)
{
if (rayOrigin.Y == planeY)
{
// The ray is horizontal and on the plane, thus all points on the ray intersect with the plane.
// Therefore we simply return the ray origin.
return rayOrigin;
}
else
{
// The ray is parallel to the plane and never intersects.
return null;
}
}
// The distance from the ray's origin to the intersection point on the plane is:
// (pointOnPlane - rayOrigin) dot planeNormal
// --------------------------------------------
// direction dot planeNormal
// Since we know that horizontal planes have normal (0, 1, 0), we can simplify this to:
var dist = (planeY - rayOrigin.Y) / direction.Y;
// Do not return intersections behind the ray's origin.
if (dist < 0)
{
return null;
}
// Return the intersection point.
return rayOrigin + (direction * dist);
}
public static (SCNVector3?, ARPlaneAnchor, bool) WorldPositionFromScreenPosition(CGPoint position,
ARSCNView sceneView,
SCNVector3? objectPos,
bool infinitePlane = false)
{
// -------------------------------------------------------------------------------
// 1. Always do a hit test against existing plane anchors first.
// (If any such anchors exist & only within their extents.)
var result = sceneView.HitTest(position, ARHitTestResultType.ExistingPlaneUsingExtent)?.FirstOrDefault();
if (result != null)
{
var planeHitTestPosition = result.WorldTransform.GetTranslation();
var planeAnchor = result.Anchor;
// Return immediately - this is the best possible outcome.
return (planeHitTestPosition, planeAnchor as ARPlaneAnchor, true);
}
// -------------------------------------------------------------------------------
// 2. Collect more information about the environment by hit testing against
// the feature point cloud, but do not return the result yet.
SCNVector3? featureHitTestPosition = null;
var highQualityFeatureHitTestResult = false;
var highQualityfeatureHitTestResults = sceneView.HitTestWithFeatures(position, 18f, 0.2f, 2f);
if (highQualityfeatureHitTestResults.Any())
{
featureHitTestPosition = highQualityfeatureHitTestResults[0].Position;
highQualityFeatureHitTestResult = true;
}
// -------------------------------------------------------------------------------
// 3. If desired or necessary (no good feature hit test result): Hit test
// against an infinite, horizontal plane (ignoring the real world).
if (infinitePlane || !highQualityFeatureHitTestResult)
{
if (objectPos.HasValue)
{
var pointOnInfinitePlane = sceneView.HitTestWithInfiniteHorizontalPlane(position, objectPos.Value);
if (pointOnInfinitePlane.HasValue)
{
return (pointOnInfinitePlane, null, true);
}
}
}
// -------------------------------------------------------------------------------
// 4. If available, return the result of the hit test against high quality
// features if the hit tests against infinite planes were skipped or no
// infinite plane was hit.
if (highQualityFeatureHitTestResult)
{
return (featureHitTestPosition, null, false);
}
// -------------------------------------------------------------------------------
// 5. As a last resort, perform a second, unfiltered hit test against features.
// If there are no features in the scene, the result returned here will be nil.
var unfilteredFeatureHitTestResults = sceneView.HitTestWithFeatures(position);
if (unfilteredFeatureHitTestResults.Any())
{
var first = unfilteredFeatureHitTestResults[0];
return (first.Position, null, false);
}
return (null, null, false);
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,55 @@
<?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">
<dict>
<key>CFBundleDisplayName</key>
<string>ARKitAudio</string>
<key>CFBundleIdentifier</key>
<string>com.xamarin.ARKitAudio</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>This application will use the camera for Augmented Reality.</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIMainStoryboardFile~ipad</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
<string>arkit</string>
</array>
<key>UIStatusBarHidden</key>
<true/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>XSAppIconAssets</key>
<string>Resources/Images.xcassets/AppIcons.appiconset</string>
<key>CFBundleName</key>
<string>ARKitAudio</string>
</dict>
</plist>

Просмотреть файл

@ -0,0 +1,16 @@

namespace ARKitAudio
{
using UIKit;
public class Application
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, "AppDelegate");
}
}
}

Просмотреть файл

@ -0,0 +1,67 @@

namespace ARKitAudio
{
using ARKit;
using Foundation;
using SceneKit;
using System;
using System.Collections.Generic;
/// <summary>
/// SceneKit node wrapper that estimates an object's final placement
/// </summary>
public class PreviewNode : SCNNode
{
// Use average of recent positions to avoid jitter.
private readonly List<SCNVector3> recentPreviewNodePositions = new List<SCNVector3>();
// Saved positions that help smooth the movement of the preview
private SCNVector3 lastPositionOnPlane;
// Saved positions that help smooth the movement of the preview
private SCNVector3 lastPosition;
public PreviewNode(IntPtr handle) : base(handle) { }
public PreviewNode(NSCoder coder)
{
throw new NotImplementedException("init(coder:) has not been implemented");
}
public PreviewNode(SCNNode node) : base()
{
this.Opacity = 0.5f;
this.AddChildNode(node);
}
// Appearance
public void Update(SCNVector3 position, ARPlaneAnchor planeAnchor, ARCamera camera)
{
this.lastPosition = position;
if (planeAnchor != null)
{
this.lastPositionOnPlane = position;
}
this.UpdateTransform(position, camera);
}
private void UpdateTransform(SCNVector3 position, ARCamera camera)
{
// Add to the list of recent positions.
this.recentPreviewNodePositions.Add(position);
// Remove anything older than the last 8 positions.
this.recentPreviewNodePositions.KeepLast(8);
// Move to average of recent positions to avoid jitter.
var average = this.recentPreviewNodePositions.GetAverage();
if (average.HasValue)
{
this.Position = average.Value;
}
}
}
}

Просмотреть файл

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ARKitAudio")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ARKitAudio")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("bc14a9a0-f771-4c15-bcd7-7974a2a71260")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Просмотреть файл

@ -0,0 +1,216 @@
{
"images": [
{
"filename": "icon-spotlight-40.png",
"size": "20x20",
"scale": "2x",
"idiom": "iphone"
},
{
"size": "20x20",
"scale": "3x",
"idiom": "iphone"
},
{
"size": "29x29",
"scale": "2x",
"idiom": "iphone"
},
{
"size": "29x29",
"scale": "3x",
"idiom": "iphone"
},
{
"filename": "icon-spotlight-40@2x.png",
"size": "40x40",
"scale": "2x",
"idiom": "iphone"
},
{
"filename": "icon-spotlight-40@3x.png",
"size": "40x40",
"scale": "3x",
"idiom": "iphone"
},
{
"filename": "icon-app-60@2x.png",
"size": "60x60",
"scale": "2x",
"idiom": "iphone"
},
{
"filename": "Icon-app-60@3x.png",
"size": "60x60",
"scale": "3x",
"idiom": "iphone"
},
{
"size": "20x20",
"scale": "1x",
"idiom": "ipad"
},
{
"filename": "icon-spotlight-40.png",
"size": "20x20",
"scale": "2x",
"idiom": "ipad"
},
{
"filename": "icon-spotlight-29.png",
"size": "29x29",
"scale": "1x",
"idiom": "ipad"
},
{
"filename": "icon-spotlight-29@2x.png",
"size": "29x29",
"scale": "2x",
"idiom": "ipad"
},
{
"filename": "icon-spotlight-40.png",
"size": "40x40",
"scale": "1x",
"idiom": "ipad"
},
{
"filename": "icon-spotlight-40@2x.png",
"size": "40x40",
"scale": "2x",
"idiom": "ipad"
},
{
"filename": "Icon-app-83.5@2x.png",
"size": "83.5x83.5",
"scale": "2x",
"idiom": "ipad"
},
{
"filename": "icon-app-76.png",
"size": "76x76",
"scale": "1x",
"idiom": "ipad"
},
{
"filename": "icon-app-76@2x.png",
"size": "76x76",
"scale": "2x",
"idiom": "ipad"
},
{
"filename": "app-store-logo.png",
"size": "1024x1024",
"scale": "1x",
"idiom": "ios-marketing"
},
{
"role": "notificationCenter",
"size": "24x24",
"subtype": "38mm",
"scale": "2x",
"idiom": "watch"
},
{
"role": "notificationCenter",
"size": "27.5x27.5",
"subtype": "42mm",
"scale": "2x",
"idiom": "watch"
},
{
"role": "companionSettings",
"size": "29x29",
"scale": "2x",
"idiom": "watch"
},
{
"role": "companionSettings",
"size": "29x29",
"scale": "3x",
"idiom": "watch"
},
{
"role": "appLauncher",
"size": "40x40",
"subtype": "38mm",
"scale": "2x",
"idiom": "watch"
},
{
"role": "longLook",
"size": "44x44",
"subtype": "42mm",
"scale": "2x",
"idiom": "watch"
},
{
"role": "quickLook",
"size": "86x86",
"subtype": "38mm",
"scale": "2x",
"idiom": "watch"
},
{
"role": "quickLook",
"size": "98x98",
"subtype": "42mm",
"scale": "2x",
"idiom": "watch"
},
{
"size": "16x16",
"scale": "1x",
"idiom": "mac"
},
{
"size": "16x16",
"scale": "2x",
"idiom": "mac"
},
{
"size": "32x32",
"scale": "1x",
"idiom": "mac"
},
{
"size": "32x32",
"scale": "2x",
"idiom": "mac"
},
{
"size": "128x128",
"scale": "1x",
"idiom": "mac"
},
{
"size": "128x128",
"scale": "2x",
"idiom": "mac"
},
{
"size": "256x256",
"scale": "1x",
"idiom": "mac"
},
{
"size": "256x256",
"scale": "2x",
"idiom": "mac"
},
{
"size": "512x512",
"scale": "1x",
"idiom": "mac"
},
{
"size": "512x512",
"scale": "2x",
"idiom": "mac"
}
],
"info": {
"version": 1,
"author": "xcode"
}
}

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.0 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 8.6 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 22 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.3 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.0 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.3 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 900 B

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.8 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.2 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.3 KiB

Просмотреть файл

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13189.4" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13165.3"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

Просмотреть файл

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BV1-FR-VrT">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Game View Controller-->
<scene sceneID="tXr-a1-R10">
<objects>
<viewController id="BV1-FR-VrT" customClass="ViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="220"/>
<viewControllerLayoutGuide type="bottom" id="221"/>
</layoutGuides>
<view contentMode="scaleToFill" id="450" key="view">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<subviews>
<arscnView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="451" translatesAutoresizingMaskIntoConstraints="NO" ibSceneName="Scene.scn">
<rect key="frame" x="0.0" y="20" width="414" height="716"/>
</arscnView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="452" translatesAutoresizingMaskIntoConstraints="NO" misplaced="YES" usesAttributedText="YES">
<rect key="frame" x="20" y="695.5" width="374" height="20.5"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<attributedString key="attributedText">
<fragment content="">
<attributes>
<font key="NSFont" name="HelveticaNeue" size="17"/>
</attributes>
</fragment>
</attributedString>
<accessibility key="accessibilityConfiguration" identifier="sessionInfoLabel"/>
</label>
</subviews>
<constraints>
<constraint id="894" firstItem="451" firstAttribute="top" secondItem="220" secondAttribute="bottom"/>
<constraint id="895" firstItem="450" firstAttribute="leading" secondItem="451" secondAttribute="leading"/>
<constraint id="896" firstItem="451" firstAttribute="trailing" secondItem="450" secondAttribute="trailing"/>
<constraint id="897" firstItem="221" firstAttribute="top" secondItem="451" secondAttribute="bottom"/>
<constraint id="898" firstItem="451" firstAttribute="bottom" secondItem="452" secondAttribute="bottom" constant="20"/>
<constraint id="899" firstItem="452" firstAttribute="leading" secondItem="450" secondAttribute="leadingMargin"/>
<constraint id="900" firstItem="450" firstAttribute="trailingMargin" secondItem="452" secondAttribute="trailing"/>
</constraints>
</view>
<connections>
<outlet property="sceneView" destination="451" id="name-outlet-451"/>
<outlet property="sessionInfoLabel" destination="452" id="name-outlet-452"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="SZV-WD-TEh" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="0.0" y="0.0"/>
</scene>
</scenes>
</document>

Просмотреть файл

@ -0,0 +1,363 @@

namespace ARKitAudio
{
using ARKit;
using AVFoundation;
using CoreGraphics;
using Foundation;
using OpenTK;
using SceneKit;
using System;
using System.Linq;
using UIKit;
/// <summary>
/// Main view controller for the AR experience.
/// </summary>
public partial class ViewController : UIViewController, IARSCNViewDelegate
{
private CGPoint screenCenter = CGPoint.Empty;
// Shows a preview of the object to be placed and hovers over estimated planes.
private PreviewNode previewNode;
// Contains the cup model that is shared by the preview and final nodes.
private SCNNode cupNode = new SCNNode();
// Audio source for positional audio feedback.
private SCNAudioSource source;
public ViewController(IntPtr handle) : base(handle) { }
#region View Life Cycle
public override void ViewDidLoad()
{
base.ViewDidLoad();
this.sceneView.Delegate = this;
// Show statistics such as FPS and timing information.
this.sceneView.ShowsStatistics = true;
// Setup environment mapping.
var environmentMap = UIImage.FromBundle("art.scnassets/sharedImages/environment_blur.exr");
this.sceneView.Scene.LightingEnvironment.Contents = environmentMap;
// Complete rendering setup of ARSCNView.
this.sceneView.AntialiasingMode = SCNAntialiasingMode.Multisampling4X;
this.sceneView.AutomaticallyUpdatesLighting = false;
this.sceneView.ContentScaleFactor = 1.3f;
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
// Preload the audio file.
this.source = SCNAudioSource.FromFile("art.scnassets/ping.aif");
this.source.Loops = true;
this.source.Load();
if (ARConfiguration.IsSupported)
{
// Start the ARSession.
var configuration = new ARWorldTrackingConfiguration { PlaneDetection = ARPlaneDetection.Horizontal };
this.sceneView.Session.Run(configuration, default(ARSessionRunOptions));
this.screenCenter = new CGPoint(this.sceneView.Bounds.GetMidX(), this.sceneView.Bounds.GetMidY());
// Prevent the screen from being dimmed after a while as users will likely have
// long periods of interaction without touching the screen or buttons.
UIApplication.SharedApplication.IdleTimerDisabled = true;
}
else
{
this.ShowUnsupportedDeviceError();
}
}
public override void ViewWillDisappear(bool animated)
{
base.ViewWillDisappear(animated);
this.cupNode.RemoveAllAudioPlayers();
// Pause the view's session.
this.sceneView.Session.Pause();
}
public override void ViewWillTransitionToSize(CGSize toSize, IUIViewControllerTransitionCoordinator coordinator)
{
base.ViewWillTransitionToSize(toSize, coordinator);
// The screen's center point changes on orientation switch, so recalculate `screenCenter`.
this.screenCenter = new CGPoint(toSize.Width / 2f, toSize.Height / 2f);
}
#endregion
#region Internal methods
private void ShowUnsupportedDeviceError()
{
// This device does not support 6DOF world tracking.
var alertController = UIAlertController.Create("ARKit is not available on this device.",
"This app requires world tracking, which is available only on iOS devices with the A9 processor or later.",
UIAlertControllerStyle.Alert);
alertController.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, null));
this.PresentModalViewController(alertController, true);
}
/// <summary>
/// Check the light estimate from the current ARFrame and update the scene.
/// </summary>
private void UpdateLightEstimate()
{
using (var frame = this.sceneView.Session.CurrentFrame)
{
var lightEstimate = frame?.LightEstimate;
if (lightEstimate != null)
{
this.sceneView.Scene.LightingEnvironment.Intensity = lightEstimate.AmbientIntensity / 40f;
}
else
{
this.sceneView.Scene.LightingEnvironment.Intensity = 40f;
}
}
}
private void ResetTracking()
{
var configuration = new ARWorldTrackingConfiguration { PlaneDetection = ARPlaneDetection.Horizontal };
this.sceneView.Session.Run(configuration, ARSessionRunOptions.ResetTracking | ARSessionRunOptions.RemoveExistingAnchors);
// Reset preview state.
this.cupNode.RemoveFromParentNode();
this.cupNode.Dispose();
this.cupNode = new SCNNode();
this.previewNode?.RemoveFromParentNode();
this.previewNode?.Dispose();
this.previewNode = null;
this.PlaySound();
}
private void SetNewVirtualObjectToAnchor(SCNNode node, ARAnchor anchor, NMatrix4 cameraTransform)
{
var cameraWorldPosition = cameraTransform.GetTranslation();
var cameraToPosition = anchor.Transform.GetTranslation() - cameraWorldPosition;
// Limit the distance of the object from the camera to a maximum of 10 meters.
if (cameraToPosition.Length > 10f)
{
cameraToPosition = Vector3.Normalize(cameraToPosition);
cameraToPosition *= 10f;
}
node.Position = cameraWorldPosition + cameraToPosition;
}
#endregion
#region ARSCNViewDelegate
/// <summary>
/// UpdateAudioPlayback
/// </summary>
[Export("renderer:updateAtTime:")]
public void Update(ISCNSceneRenderer renderer, double timeInSeconds)
{
if (this.cupNode.ParentNode == null && this.previewNode == null)
{
// If our model hasn't been placed and we lack a preview for placement then setup a preview.
this.SetupPreviewNode();
this.UpdatePreviewNode();
}
else
{
this.UpdatePreviewNode();
}
this.UpdateLightEstimate();
this.CutVolumeIfPlacedObjectIsInView();
}
/// <summary>
/// PlaceARContent
/// </summary>
[Export("renderer:didAddNode:forAnchor:")]
public void DidAddNode(ISCNSceneRenderer renderer, SCNNode node, ARAnchor anchor)
{
// Place content only for anchors found by plane detection.
if (anchor is ARPlaneAnchor && this.previewNode != null)
{
// Stop showing a preview version of the object to be placed.
this.cupNode.RemoveFromParentNode();
this.previewNode?.RemoveFromParentNode();
this.previewNode?.Dispose();
this.previewNode = null;
// Add the cupNode to the scene's root node using the anchor's position.
var cameraTransform = this.sceneView.Session.CurrentFrame?.Camera?.Transform;
if (cameraTransform.HasValue)
{
this.SetNewVirtualObjectToAnchor(this.cupNode, anchor, cameraTransform.Value);
this.sceneView.Scene.RootNode.AddChildNode(this.cupNode);
// Disable plane detection after the model has been added.
var configuration = new ARWorldTrackingConfiguration { PlaneDetection = ARPlaneDetection.Horizontal };
this.sceneView.Session.Run(configuration, default(ARSessionRunOptions));
// Set up positional audio to play in case the object moves offscreen.
this.PlaySound();
}
}
}
/// <summary>
/// PlaceARContent
/// </summary>
[Export("session:cameraDidChangeTrackingState:")]
private void CameraDidChangeTrackingState(ARSession session, ARCamera camera)
{
var message = string.Empty;
// Inform the user of their camera tracking state.
switch (camera.TrackingState)
{
case ARTrackingState.NotAvailable:
message = "Tracking unavailable";
break;
case ARTrackingState.Limited:
switch (camera.TrackingStateReason)
{
case ARTrackingStateReason.ExcessiveMotion:
message = "Tracking limited - Too much camera movement";
break;
case ARTrackingStateReason.InsufficientFeatures:
message = "Tracking limited - Not enough surface detail";
break;
case ARTrackingStateReason.Initializing:
message = "Initializing AR Session";
break;
}
break;
case ARTrackingState.Normal:
message = "Tracking normal";
break;
}
this.sessionInfoLabel.Text = message;
}
[Export("session:didFailWithError:")]
public void DidFail(ARSession session, NSError error)
{
// Present an error message to the user.
this.sessionInfoLabel.Text = $"Session failed: {error.LocalizedDescription}";
this.ResetTracking();
}
[Export("sessionWasInterrupted:")]
public void WasInterrupted(ARSession session)
{
// Inform the user that the session has been interrupted, for example, by presenting an overlay.
this.sessionInfoLabel.Text = "Session was interrupted";
this.ResetTracking();
}
[Export("sessionInterruptionEnded:")]
public void InterruptionEnded(ARSession session)
{
// Reset tracking and/or remove existing anchors if consistent tracking is required.
this.sessionInfoLabel.Text = "Session interruption ended";
this.ResetTracking();
}
#endregion
#region Preview Node
/// <summary>
/// Loads the cup model (`cupNode`) that is used for the duration of the app.
/// Initializes a `PreviewNode` that contains the `cupNode` and adds it to the node hierarchy.
/// </summary>
private void SetupPreviewNode()
{
if (this.cupNode.FindChildNode("candle", false) == null)
{
// Load the cup scene from the bundle only once.
var modelScene = SCNScene.FromFile("art.scnassets/candle/candle.scn");
// Get a handle to the cup model.
var cup = modelScene.RootNode.FindChildNode("candle", true);
// Set the cup model onto `cupNode`.
this.cupNode.AddChildNode(cup);
}
// Initialize `previewNode` to display the cup model.
this.previewNode = new PreviewNode(this.cupNode);
// Add `previewNode` to the node hierarchy.
this.sceneView.Scene.RootNode.AddChildNode(this.previewNode);
}
/// <summary>
/// `previewNode` exists when ARKit is finding a plane. During this time, get a world position for the areas closest to the scene's point of view that ARKit believes might be a plane, and use it to update the `previewNode` position.
/// </summary>
private void UpdatePreviewNode()
{
if (this.previewNode != null)
{
var (worldPosition, planeAnchor, _) = Utilities.WorldPositionFromScreenPosition(this.screenCenter, this.sceneView, this.previewNode.Position);
if (worldPosition.HasValue)
{
this.previewNode.Update(worldPosition.Value, planeAnchor, this.sceneView.Session.CurrentFrame?.Camera);
}
}
}
#endregion
#region Sound
/// <summary>
/// Determines whether the `cupNode` is visible. If the `cupNode` isn't visible, a sound is played using
/// SceneKit's positional audio functionality to locate the `cupNode`.
/// </summary>
private void CutVolumeIfPlacedObjectIsInView()
{
if (this.previewNode == null && this.sceneView.PointOfView != null)
{
var player = this.cupNode.AudioPlayers?.FirstOrDefault();
var avNode = player?.AudioNode as AVAudioPlayerNode;
if (player != null && avNode != null)
{
var placedObjectIsInView = this.sceneView.IsNodeInsideFrustum(this.cupNode, this.sceneView.PointOfView);
avNode.Volume = placedObjectIsInView ? 0f : 1f;
}
}
}
/// <summary>
/// Plays a sound on the cupNode using SceneKit's positional audio.
/// </summary>
private void PlaySound()
{
// ensure there is only one audio player
this.cupNode.RemoveAllAudioPlayers();
if (this.cupNode.AudioPlayers == null || !this.cupNode.AudioPlayers.Any())
{
this.cupNode.AddAudioPlayer(new SCNAudioPlayer(this.source));
}
}
#endregion
}
}

38
ios11/ARKitAudio/ARKitAudio/ViewController.designer.cs сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,38 @@
// WARNING
//
// This file has been generated automatically by Visual Studio from the outlets and
// actions declared in your storyboard file.
// Manual changes to this file will not be maintained.
//
using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
namespace ARKitAudio
{
[Register ("ViewController")]
partial class ViewController
{
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
ARKit.ARSCNView sceneView { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UILabel sessionInfoLabel { get; set; }
void ReleaseDesignerOutlets ()
{
if (sceneView != null) {
sceneView.Dispose ();
sceneView = null;
}
if (sessionInfoLabel != null) {
sessionInfoLabel.Dispose ();
sessionInfoLabel = null;
}
}
}
}

Двоичные данные
ios11/ARKitAudio/ARKitAudio/art.scnassets/Scene.scn Normal file

Двоичный файл не отображается.

Двоичные данные
ios11/ARKitAudio/ARKitAudio/art.scnassets/candle/candle.scn Normal file

Двоичный файл не отображается.

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 214 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 824 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 395 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 437 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 361 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 84 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 148 KiB

Двоичные данные
ios11/ARKitAudio/ARKitAudio/art.scnassets/ping.aif Normal file

Двоичный файл не отображается.

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.2 MiB

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<SampleMetadata>
<ID>140d20be-f5a4-4040-b585-d3877b4457a4</ID>
<IsFullApplication>false</IsFullApplication>
<Brief>Runs an ARKit world tracking session with content displayed in a SceneKit view. To demonstrate plane detection, the app simply places a 3D model onto the first plane that ARKit detects. If the model's position is outside the current field of view of the camera, the app uses SceneKit's positional audio feature to indicate which direction to turn the device to see the model.</Brief>
<Level>Intermediate</Level>
<Tags>Graphics, Media, Platform Features, Device Features, SceneKit, ARKit, iOS11</Tags>
<LicenseRequirement>Starter</LicenseRequirement>
<Gallery>true</Gallery>
</SampleMetadata>

Просмотреть файл

@ -0,0 +1,32 @@
ARKitAudio
============
This sample runs an ARKit world tracking session with content displayed in a SceneKit view. To demonstrate plane detection, the app simply places a 3D model onto the first plane that ARKit detects. If the model's position is outside the current field of view of the camera, the app uses SceneKit's positional audio feature to indicate which direction to turn the device to see the model.
![Placed object](Screenshots/screenshots_2.png)
Build Requirements
-------
Xcode 9.0 or later; iOS 11.0 SDK or later
Refs
-------
- [Original sample](https://developer.apple.com/library/content/samplecode/AudioInARKit/Introduction/Intro.html)
- [Documentation](https://developer.apple.com/documentation/arkit/)
Target
-------
This sample is runnable on iPhone/iPad devices since it requires a real camera.
License
-------
Xamarin port changes are released under the MIT license.
Author
------
Ported to Xamarin.iOS by Mykyta Bondarenko

Двоичные данные
ios11/ARKitAudio/Screenshots/screenshots_1.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.3 MiB

Двоичные данные
ios11/ARKitAudio/Screenshots/screenshots_2.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.3 MiB

Двоичные данные
ios11/ARKitAudio/Screenshots/screenshots_3.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 54 KiB

11
ios11/AVCam/Metadata.xml Normal file
Просмотреть файл

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<SampleMetadata>
<ID>795043ED-ACFA-47D0-88EB-52A22827A8A9</ID>
<IsFullApplication>false</IsFullApplication>
<Level>Beginner</Level>
<Tags>iOS11, Media</Tags>
<SupportedPlatforms>iOS</SupportedPlatforms>
<Gallery>true</Gallery>
<MinimumLicenseRequirement>Starter</MinimumLicenseRequirement>
<Brief>Using AVFoundation to Capture Photos and Movies</Brief>
</SampleMetadata>

17
ios11/AVCam/README.md Normal file
Просмотреть файл

@ -0,0 +1,17 @@
AVCam
=====
### Using AVFoundation to Capture Images and Movies
AVCam demonstrates how to use the `AVFoundation` capture API to record movies and capture still images. The sample has a record button for recording movies, a camera button for switching between front and back cameras (on supported devices), and a still button for capturing still images. AVCam runs only on an actual device, either an `iPad` or `iPhone`, and cannot be run in `Simulator`.
### Build Requirements
Xcode 9.0, iOS 11.0 SDK
### Runtime
iOS 11.0 or later
### Author
Xamarin port changes are released under the MIT license
Ported to Xamarin.iOS by Rustam Zaitov, Bill Holmes

Просмотреть файл

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
<ProjectGuid>{DD06F0B6-6C44-4D32-B70B-06597BB017BD}</ProjectGuid>
<ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Exe</OutputType>
<RootNamespace>AVCam</RootNamespace>
<AssemblyName>AVCam</AssemblyName>
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhoneSimulator\Debug</OutputPath>
<DefineConstants>DEBUG;ENABLE_TEST_CLOUD;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodesignKey>iPhone Developer</CodesignKey>
<MtouchDebug>true</MtouchDebug>
<MtouchNoSymbolStrip>true</MtouchNoSymbolStrip>
<MtouchFastDev>true</MtouchFastDev>
<MtouchProfiling>true</MtouchProfiling>
<IOSDebuggerPort>40594</IOSDebuggerPort>
<MtouchLink>None</MtouchLink>
<MtouchArch>x86_64</MtouchArch>
<MtouchHttpClientHandler>HttpClientHandler</MtouchHttpClientHandler>
<PlatformTarget>x86</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhone\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodesignKey>iPhone Developer</CodesignKey>
<MtouchFloat32>true</MtouchFloat32>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<MtouchLink>SdkOnly</MtouchLink>
<MtouchArch>ARM64</MtouchArch>
<MtouchHttpClientHandler>HttpClientHandler</MtouchHttpClientHandler>
<PlatformTarget>x86</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhoneSimulator\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodesignKey>iPhone Developer</CodesignKey>
<MtouchNoSymbolStrip>true</MtouchNoSymbolStrip>
<MtouchLink>None</MtouchLink>
<MtouchArch>x86_64</MtouchArch>
<MtouchHttpClientHandler>HttpClientHandler</MtouchHttpClientHandler>
<PlatformTarget>x86</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhone\Debug</OutputPath>
<DefineConstants>DEBUG;ENABLE_TEST_CLOUD;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodesignKey>iPhone Developer</CodesignKey>
<DeviceSpecificBuild>true</DeviceSpecificBuild>
<MtouchDebug>true</MtouchDebug>
<MtouchNoSymbolStrip>true</MtouchNoSymbolStrip>
<MtouchFastDev>true</MtouchFastDev>
<MtouchProfiling>true</MtouchProfiling>
<MtouchFloat32>true</MtouchFloat32>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<IOSDebuggerPort>53419</IOSDebuggerPort>
<MtouchLink>SdkOnly</MtouchLink>
<MtouchArch>ARM64</MtouchArch>
<MtouchHttpClientHandler>HttpClientHandler</MtouchHttpClientHandler>
<PlatformTarget>x86</PlatformTarget>
<MtouchExtraArgs></MtouchExtraArgs>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="Xamarin.iOS" />
</ItemGroup>
<ItemGroup>
<ImageAsset Include="Assets.xcassets\Contents.json" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Contents.json" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\AVCam_Icon_29x29%402x.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\AVCam_Icon_29x29%403x.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\AVCam_Icon_40x40%402x.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\AVCam_Icon_40x40%403x.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\AVCam_Icon_60x60%403x.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\AVCam_Icon_40x40.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\AVCam_Icon_29x29.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\AVCam_Icon_76x76.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\AVCam_Icon_76x76%402x.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\AVCam_Icon_83.5x83.5.png" />
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\" />
</ItemGroup>
<ItemGroup>
<InterfaceDefinition Include="LaunchScreen.storyboard" />
<InterfaceDefinition Include="Main.storyboard" />
</ItemGroup>
<ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
</ItemGroup>
<ItemGroup>
<Compile Include="Main.cs" />
<Compile Include="AppDelegate.cs" />
<Compile Include="AVCamCameraViewController.cs" />
<Compile Include="AVCamCameraViewController.designer.cs">
<DependentUpon>AVCamCameraViewController.cs</DependentUpon>
</Compile>
<Compile Include="AVCamPreviewView.cs" />
<Compile Include="AVCamPreviewView.designer.cs">
<DependentUpon>AVCamPreviewView.cs</DependentUpon>
</Compile>
<Compile Include="AVCamPhotoCaptureDelegate.cs" />
<Compile Include="MissingBindings.cs" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
</Project>

23
ios11/AVCam/src/AVCam.sln Normal file
Просмотреть файл

@ -0,0 +1,23 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AVCam", "AVCam.csproj", "{DD06F0B6-6C44-4D32-B70B-06597BB017BD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|iPhoneSimulator = Debug|iPhoneSimulator
Release|iPhone = Release|iPhone
Release|iPhoneSimulator = Release|iPhoneSimulator
Debug|iPhone = Debug|iPhone
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DD06F0B6-6C44-4D32-B70B-06597BB017BD}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{DD06F0B6-6C44-4D32-B70B-06597BB017BD}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{DD06F0B6-6C44-4D32-B70B-06597BB017BD}.Release|iPhone.ActiveCfg = Release|iPhone
{DD06F0B6-6C44-4D32-B70B-06597BB017BD}.Release|iPhone.Build.0 = Release|iPhone
{DD06F0B6-6C44-4D32-B70B-06597BB017BD}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{DD06F0B6-6C44-4D32-B70B-06597BB017BD}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{DD06F0B6-6C44-4D32-B70B-06597BB017BD}.Debug|iPhone.ActiveCfg = Debug|iPhone
{DD06F0B6-6C44-4D32-B70B-06597BB017BD}.Debug|iPhone.Build.0 = Debug|iPhone
EndGlobalSection
EndGlobal

Разница между файлами не показана из-за своего большого размера Загрузить разницу

122
ios11/AVCam/src/AVCamCameraViewController.designer.cs сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,122 @@
// WARNING
//
// This file has been generated automatically by Visual Studio to store outlets and
// actions made in the UI designer. If it is removed, they will be lost.
// Manual changes to this file may not be handled correctly.
//
using Foundation;
using System.CodeDom.Compiler;
namespace AVCam
{
[Register ("AVCamCameraViewController")]
partial class AVCamCameraViewController
{
[Outlet]
UIKit.UIButton CameraButton { get; set; }
[Outlet]
UIKit.UILabel CameraUnavailableLabel { get; set; }
[Outlet]
UIKit.UISegmentedControl CaptureModeControl { get; set; }
[Outlet]
UIKit.UILabel CapturingLivePhotoLabel { get; set; }
[Outlet]
UIKit.UIButton DepthDataDeliveryButton { get; set; }
[Outlet]
UIKit.UIButton LivePhotoModeButton { get; set; }
[Outlet]
UIKit.UIButton PhotoButton { get; set; }
[Outlet]
AVCam.AVCamPreviewView PreviewView { get; set; }
[Outlet]
UIKit.UIButton RecordButton { get; set; }
[Outlet]
UIKit.UIButton ResumeButton { get; set; }
[Action ("CapturePhoto:")]
partial void CapturePhoto (Foundation.NSObject sender);
[Action ("ChangeCamera:")]
partial void ChangeCamera (Foundation.NSObject sender);
[Action ("FocusAndExposeTap:")]
partial void FocusAndExposeTap (UIKit.UIGestureRecognizer gestureRecognizer);
[Action ("ResumeInterruptedSession:")]
partial void ResumeInterruptedSession (Foundation.NSObject sender);
[Action ("ToggleCaptureMode:")]
partial void ToggleCaptureMode (UIKit.UISegmentedControl captureModeControl);
[Action ("ToggleDepthDataDeliveryMode:")]
partial void ToggleDepthDataDeliveryMode (UIKit.UIButton depthDataDeliveryButton);
[Action ("ToggleLivePhotoMode:")]
partial void ToggleLivePhotoMode (UIKit.UIButton livePhotoModeButton);
[Action ("ToggleMovieRecording:")]
partial void ToggleMovieRecording (Foundation.NSObject sender);
void ReleaseDesignerOutlets ()
{
if (CameraButton != null) {
CameraButton.Dispose ();
CameraButton = null;
}
if (CameraUnavailableLabel != null) {
CameraUnavailableLabel.Dispose ();
CameraUnavailableLabel = null;
}
if (CaptureModeControl != null) {
CaptureModeControl.Dispose ();
CaptureModeControl = null;
}
if (CapturingLivePhotoLabel != null) {
CapturingLivePhotoLabel.Dispose ();
CapturingLivePhotoLabel = null;
}
if (DepthDataDeliveryButton != null) {
DepthDataDeliveryButton.Dispose ();
DepthDataDeliveryButton = null;
}
if (LivePhotoModeButton != null) {
LivePhotoModeButton.Dispose ();
LivePhotoModeButton = null;
}
if (PhotoButton != null) {
PhotoButton.Dispose ();
PhotoButton = null;
}
if (PreviewView != null) {
PreviewView.Dispose ();
PreviewView = null;
}
if (RecordButton != null) {
RecordButton.Dispose ();
RecordButton = null;
}
if (ResumeButton != null) {
ResumeButton.Dispose ();
ResumeButton = null;
}
}
}
}

Просмотреть файл

@ -0,0 +1,134 @@
using System;
using AVFoundation;
using CoreMedia;
using Foundation;
namespace AVCam
{
public class AVCamPhotoCaptureDelegate : NSObject, IAVCapturePhotoCaptureDelegate
{
public AVCapturePhotoSettings RequestedPhotoSettings { get; set; }
Action WillCapturePhotoAnimation { get; set; }
Action<bool> LivePhotoCaptureHandler { get; set; }
Action<AVCamPhotoCaptureDelegate> CompletionHandler { get; set; }
NSData PhotoData { get; set; }
NSUrl LivePhotoCompanionMovieUrl { get; set; }
public AVCamPhotoCaptureDelegate (AVCapturePhotoSettings requestedPhotoSettings, Action willCapturePhotoAnimation, Action<bool> livePhotoCaptureHandler, Action<AVCamPhotoCaptureDelegate> completionHandler)
{
RequestedPhotoSettings = requestedPhotoSettings;
WillCapturePhotoAnimation = willCapturePhotoAnimation;
LivePhotoCaptureHandler = livePhotoCaptureHandler;
CompletionHandler = completionHandler;
}
void DidFinish ()
{
if (LivePhotoCompanionMovieUrl != null && NSFileManager.DefaultManager.FileExists (LivePhotoCompanionMovieUrl.Path))
{
NSError error;
NSFileManager.DefaultManager.Remove (LivePhotoCompanionMovieUrl.Path, out error);
if (error != null)
Console.WriteLine ($"Could not remove file at url: {LivePhotoCompanionMovieUrl.Path}");
}
CompletionHandler (this);
}
[Export ("captureOutput:willBeginCaptureForResolvedSettings:")]
public virtual void WillBeginCapture (AVCapturePhotoOutput captureOutput, AVCaptureResolvedPhotoSettings resolvedSettings)
{
if ((resolvedSettings.LivePhotoMovieDimensions.Width > 0) && (resolvedSettings.LivePhotoMovieDimensions.Height > 0))
{
LivePhotoCaptureHandler (true);
}
}
[Export ("captureOutput:willCapturePhotoForResolvedSettings:")]
public virtual void WillCapturePhoto (AVCapturePhotoOutput captureOutput, AVCaptureResolvedPhotoSettings resolvedSettings)
{
WillCapturePhotoAnimation ();
}
[Export ("captureOutput:didFinishProcessingPhoto:error:")]
public virtual void DidFinishProcessingPhoto (AVCapturePhotoOutput captureOutput, AVCapturePhoto photo, NSError error)
{
if (error != null)
{
Console.WriteLine ($"Error capturing photo: {error}", error);
return;
}
PhotoData = photo.FileDataRepresentation ();
}
[Export ("captureOutput:didFinishRecordingLivePhotoMovieForEventualFileAtURL:resolvedSettings:")]
public virtual void DidFinishRecordingLivePhotoMovie (AVCapturePhotoOutput captureOutput, NSUrl outputFileUrl, AVCaptureResolvedPhotoSettings resolvedSettings)
{
LivePhotoCaptureHandler (false);
}
[Export ("captureOutput:didFinishProcessingLivePhotoToMovieFileAtURL:duration:photoDisplayTime:resolvedSettings:error:")]
public virtual void DidFinishProcessingLivePhotoMovie (AVCapturePhotoOutput captureOutput, NSUrl outputFileUrl, CMTime duration, CMTime photoDisplayTime, AVCaptureResolvedPhotoSettings resolvedSettings, NSError error)
{
if (error != null)
{
Console.WriteLine ($"Error processing live photo companion movie: {error}", error);
return;
}
LivePhotoCompanionMovieUrl = outputFileUrl;
}
[Export ("captureOutput:didFinishCaptureForResolvedSettings:error:")]
public virtual async void DidFinishCapture (AVCapturePhotoOutput captureOutput, AVCaptureResolvedPhotoSettings resolvedSettings, NSError error)
{
if (error != null)
{
Console.WriteLine ($"Error capturing photo: {error}", error);
DidFinish ();
return;
}
if (PhotoData == null)
{
Console.WriteLine ("No photo data resource");
DidFinish ();
return;
}
var status = await Photos.PHPhotoLibrary.RequestAuthorizationAsync ();
if (status == Photos.PHAuthorizationStatus.Authorized)
{
Photos.PHPhotoLibrary.SharedPhotoLibrary.PerformChanges (() =>
{
var options = new Photos.PHAssetResourceCreationOptions ();
options.UniformTypeIdentifier = RequestedPhotoSettings.ProcessedFileType ();
var creationRequest = Photos.PHAssetCreationRequest.CreationRequestForAsset ();
creationRequest.AddResource (Photos.PHAssetResourceType.Photo, PhotoData, options);
if (LivePhotoCompanionMovieUrl != null)
{
var livePhotoCompanionMovieResourceOptions = new Photos.PHAssetResourceCreationOptions ();
livePhotoCompanionMovieResourceOptions.ShouldMoveFile = true;
creationRequest.AddResource (Photos.PHAssetResourceType.PairedVideo, LivePhotoCompanionMovieUrl, livePhotoCompanionMovieResourceOptions);
}
}, (success, completeError) =>
{
if (!success)
{
Console.WriteLine ($"Error occurred while saving photo to photo library: {error}");
}
DidFinish ();
});
}
else
{
Console.WriteLine (@"Not authorized to save photo");
DidFinish ();
}
}
}
}

Просмотреть файл

@ -0,0 +1,41 @@
// This file has been autogenerated from a class added in the UI designer.
using System;
using AVFoundation;
using Foundation;
using ObjCRuntime;
using UIKit;
namespace AVCam
{
public partial class AVCamPreviewView : UIView
{
public AVCamPreviewView (IntPtr handle) : base (handle)
{
}
[Export ("layerClass")]
public static Class LayerClass ()
{
return new Class (typeof (AVCaptureVideoPreviewLayer));
}
public AVCaptureVideoPreviewLayer VideoPreviewLayer {
get {
return (Layer as AVCaptureVideoPreviewLayer);
}
}
public AVCaptureSession Session {
get {
var videoPreviewLayer = VideoPreviewLayer;
return videoPreviewLayer == null ? null : videoPreviewLayer.Session;
}
set{
var videoPreviewLayer = VideoPreviewLayer;
if (videoPreviewLayer != null)
videoPreviewLayer.Session = value;
}
}
}
}

20
ios11/AVCam/src/AVCamPreviewView.designer.cs сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,20 @@
// WARNING
//
// This file has been generated automatically by Visual Studio to store outlets and
// actions made in the UI designer. If it is removed, they will be lost.
// Manual changes to this file may not be handled correctly.
//
using Foundation;
using System.CodeDom.Compiler;
namespace AVCam
{
[Register ("AVCamPreviewView")]
partial class AVCamPreviewView
{
void ReleaseDesignerOutlets ()
{
}
}
}

Просмотреть файл

@ -0,0 +1,59 @@
using Foundation;
using UIKit;
namespace AVCam
{
// The UIApplicationDelegate for the application. This class is responsible for launching the
// User Interface of the application, as well as listening (and optionally responding) to application events from iOS.
[Register ("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{
// class-level declarations
public override UIWindow Window
{
get;
set;
}
public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
// Override point for customization after application launch.
// If not required for your application you can safely delete this method
return true;
}
public override void OnResignActivation (UIApplication application)
{
// Invoked when the application is about to move from active to inactive state.
// This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message)
// or when the user quits the application and it begins the transition to the background state.
// Games should use this method to pause the game.
}
public override void DidEnterBackground (UIApplication application)
{
// Use this method to release shared resources, save user data, invalidate timers and store the application state.
// If your application supports background exection this method is called instead of WillTerminate when the user quits.
}
public override void WillEnterForeground (UIApplication application)
{
// Called as part of the transiton from background to active state.
// Here you can undo many of the changes made on entering the background.
}
public override void OnActivated (UIApplication application)
{
// Restart any tasks that were paused (or not yet started) while the application was inactive.
// If the application was previously in the background, optionally refresh the user interface.
}
public override void WillTerminate (UIApplication application)
{
// Called when the application is about to terminate. Save data, if needed. See also DidEnterBackground.
}
}
}

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.3 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.1 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 7.5 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.0 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.9 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 8.1 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 20 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.1 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 16 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 12 KiB

Просмотреть файл

@ -0,0 +1,216 @@
{
"images": [
{
"size": "20x20",
"scale": "2x",
"idiom": "iphone"
},
{
"size": "20x20",
"scale": "3x",
"idiom": "iphone"
},
{
"filename": "AVCam_Icon_29x29@2x.png",
"size": "29x29",
"scale": "2x",
"idiom": "iphone"
},
{
"filename": "AVCam_Icon_29x29@3x.png",
"size": "29x29",
"scale": "3x",
"idiom": "iphone"
},
{
"filename": "AVCam_Icon_40x40@2x.png",
"size": "40x40",
"scale": "2x",
"idiom": "iphone"
},
{
"filename": "AVCam_Icon_40x40@3x.png",
"size": "40x40",
"scale": "3x",
"idiom": "iphone"
},
{
"filename": "AVCam_Icon_40x40@3x.png",
"size": "60x60",
"scale": "2x",
"idiom": "iphone"
},
{
"filename": "AVCam_Icon_60x60@3x.png",
"size": "60x60",
"scale": "3x",
"idiom": "iphone"
},
{
"size": "20x20",
"scale": "1x",
"idiom": "ipad"
},
{
"filename": "AVCam_Icon_40x40.png",
"size": "20x20",
"scale": "2x",
"idiom": "ipad"
},
{
"filename": "AVCam_Icon_29x29.png",
"size": "29x29",
"scale": "1x",
"idiom": "ipad"
},
{
"filename": "AVCam_Icon_29x29@2x.png",
"size": "29x29",
"scale": "2x",
"idiom": "ipad"
},
{
"filename": "AVCam_Icon_40x40.png",
"size": "40x40",
"scale": "1x",
"idiom": "ipad"
},
{
"filename": "AVCam_Icon_40x40@2x.png",
"size": "40x40",
"scale": "2x",
"idiom": "ipad"
},
{
"filename": "AVCam_Icon_83.5x83.5.png",
"size": "83.5x83.5",
"scale": "2x",
"idiom": "ipad"
},
{
"filename": "AVCam_Icon_76x76.png",
"size": "76x76",
"scale": "1x",
"idiom": "ipad"
},
{
"filename": "AVCam_Icon_76x76@2x.png",
"size": "76x76",
"scale": "2x",
"idiom": "ipad"
},
{
"size": "1024x1024",
"scale": "1x",
"idiom": "ios-marketing"
},
{
"role": "notificationCenter",
"size": "24x24",
"subtype": "38mm",
"scale": "2x",
"idiom": "watch"
},
{
"role": "notificationCenter",
"size": "27.5x27.5",
"subtype": "42mm",
"scale": "2x",
"idiom": "watch"
},
{
"role": "companionSettings",
"size": "29x29",
"scale": "2x",
"idiom": "watch"
},
{
"role": "companionSettings",
"size": "29x29",
"scale": "3x",
"idiom": "watch"
},
{
"role": "appLauncher",
"size": "40x40",
"subtype": "38mm",
"scale": "2x",
"idiom": "watch"
},
{
"role": "longLook",
"size": "44x44",
"subtype": "42mm",
"scale": "2x",
"idiom": "watch"
},
{
"role": "quickLook",
"size": "86x86",
"subtype": "38mm",
"scale": "2x",
"idiom": "watch"
},
{
"role": "quickLook",
"size": "98x98",
"subtype": "42mm",
"scale": "2x",
"idiom": "watch"
},
{
"size": "16x16",
"scale": "1x",
"idiom": "mac"
},
{
"size": "16x16",
"scale": "2x",
"idiom": "mac"
},
{
"size": "32x32",
"scale": "1x",
"idiom": "mac"
},
{
"size": "32x32",
"scale": "2x",
"idiom": "mac"
},
{
"size": "128x128",
"scale": "1x",
"idiom": "mac"
},
{
"size": "128x128",
"scale": "2x",
"idiom": "mac"
},
{
"size": "256x256",
"scale": "1x",
"idiom": "mac"
},
{
"size": "256x256",
"scale": "2x",
"idiom": "mac"
},
{
"size": "512x512",
"scale": "1x",
"idiom": "mac"
},
{
"size": "512x512",
"scale": "2x",
"idiom": "mac"
}
],
"info": {
"version": 1,
"author": "xcode"
}
}

Просмотреть файл

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Просмотреть файл

@ -0,0 +1,6 @@
<?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">
<dict>
</dict>
</plist>

Просмотреть файл

@ -0,0 +1,58 @@
<?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">
<dict>
<key>CFBundleName</key>
<string>AVCam</string>
<key>CFBundleIdentifier</key>
<string>com.xamarindemos.AVCam</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>MinimumOSVersion</key>
<string>11.0</string>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
</array>
<key>NSCameraUsageDescription</key>
<string>to take photos and videos</string>
<key>NSMicrophoneUsageDescription</key>
<string>to record Live Photos and movies</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>to save photos and videos to your Photo Library</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIRequiresFullScreen</key>
<true/>
<key>UIStatusBarHidden</key>
<true/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/AppIcon.appiconset</string>
</dict>
</plist>

Просмотреть файл

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9532" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS" />
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9530" />
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb" />
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok" />
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600" />
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" />
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite" />
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder" />
</objects>
<point key="canvasLocation" x="53" y="375" />
</scene>
</scenes>
</document>

15
ios11/AVCam/src/Main.cs Normal file
Просмотреть файл

@ -0,0 +1,15 @@
using UIKit;
namespace AVCam
{
public class Application
{
// This is the main entry point of the application.
static void Main (string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main (args, null, "AppDelegate");
}
}
}

Просмотреть файл

@ -0,0 +1,217 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13189.4" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13165.3"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Cam Camera View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="AVCamCameraViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3eR-Rn-XpZ" userLabel="Preview" customClass="AVCamPreviewView">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<gestureRecognizers/>
<connections>
<outletCollection property="gestureRecognizers" destination="fY6-qX-ntV" appends="YES" id="G6D-dx-xU8"/>
</connections>
</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">
<rect key="frame" x="83.5" y="319" width="208" height="29"/>
<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"/>
<color key="textColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<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="105" y="314" width="165" height="39"/>
<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"/>
<inset key="contentEdgeInsets" minX="10" minY="5" maxX="10" maxY="5"/>
<state key="normal" title="Tap to resume">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="4"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="ResumeInterruptedSession:" destination="BYZ-38-t0r" eventType="touchUpInside" id="42K-1B-qJd"/>
</connections>
</button>
<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="47.5" y="617" width="80" height="30"/>
<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"/>
<state key="normal" title="Record">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="4"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="ToggleMovieRecording:" destination="BYZ-38-t0r" eventType="touchUpInside" id="9R7-Ok-FpB"/>
</connections>
</button>
<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="147.5" y="617" width="80" height="30"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="NtC-UN-gTs"/>
<constraint firstAttribute="width" constant="80" id="dxU-UP-4Ae"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="20"/>
<state key="normal" title="Photo">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="4"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="CapturePhoto:" destination="BYZ-38-t0r" eventType="touchUpInside" id="o5K-SC-fYn"/>
</connections>
</button>
<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="247.5" y="617" width="80" height="30"/>
<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"/>
<state key="normal" title="Camera">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="4"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="ChangeCamera:" destination="BYZ-38-t0r" eventType="touchUpInside" id="3W0-h3-6fc"/>
</connections>
</button>
<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="136" y="569" width="103" height="29"/>
<segments>
<segment title="Photo"/>
<segment title="Movie"/>
</segments>
<connections>
<action selector="ToggleCaptureMode:" destination="BYZ-38-t0r" eventType="valueChanged" id="SKd-67-ZHh"/>
</connections>
</segmentedControl>
<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="82.5" y="8" width="210" height="25"/>
<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="96.5" y="41" width="182" height="25"/>
<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="om7-Gh-HVl"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="20"/>
<state key="normal" title="Live Photo Mode: On"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="4"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="ToggleLivePhotoMode:" destination="BYZ-38-t0r" eventType="touchUpInside" id="JqX-wJ-Xf1"/>
</connections>
</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" userLabel="Capturing Live Photo">
<rect key="frame" x="172" y="74" width="31" height="20.5"/>
<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="17"/>
<color key="textColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="4"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</label>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="3eR-Rn-XpZ" firstAttribute="centerX" secondItem="mjw-BI-djO" 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="eI6-gV-W7d" firstAttribute="centerX" secondItem="mjw-BI-djO" 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="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 firstItem="uCj-6P-mHF" firstAttribute="top" secondItem="rUJ-G6-RPv" secondAttribute="top" id="NFm-e8-abT"/>
<constraint firstItem="FZr-Ip-7WL" firstAttribute="centerX" secondItem="mjw-BI-djO" secondAttribute="centerX" id="OaZ-uO-vXY"/>
<constraint firstItem="FAC-co-10c" firstAttribute="centerX" secondItem="mjw-BI-djO" secondAttribute="centerX" id="Oow-A6-mDp"/>
<constraint firstItem="9i1-NX-Qxg" firstAttribute="top" secondItem="8bC-Xf-vdC" 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="Pii-2r-R2l" firstAttribute="centerX" secondItem="mjw-BI-djO" secondAttribute="centerX" id="SXi-MU-H9D"/>
<constraint firstItem="zf0-db-esM" firstAttribute="centerX" secondItem="mjw-BI-djO" 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="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="mjw-BI-djO" firstAttribute="bottom" secondItem="uCj-6P-mHF" secondAttribute="bottom" constant="20" id="eWs-co-Aaz"/>
<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="mjw-BI-djO" 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="3eR-Rn-XpZ" firstAttribute="width" secondItem="8bC-Xf-vdC" secondAttribute="width" id="pSC-xP-dl0"/>
<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="9i1-NX-Qxg" firstAttribute="centerX" secondItem="mjw-BI-djO" 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"/>
</constraints>
<viewLayoutGuide key="safeArea" id="mjw-BI-djO"/>
</view>
<extendedEdge key="edgesForExtendedLayout"/>
<nil key="simulatedStatusBarMetrics"/>
<connections>
<outlet property="CameraButton" destination="rUJ-G6-RPv" id="dAV-WS-N1p"/>
<outlet property="CameraUnavailableLabel" destination="zf0-db-esM" id="P9W-lb-Pb8"/>
<outlet property="CaptureModeControl" destination="FAC-co-10c" id="KXj-wg-BvS"/>
<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="PhotoButton" destination="uCj-6P-mHF" id="Ha8-ua-hxy"/>
<outlet property="PreviewView" destination="3eR-Rn-XpZ" id="e7I-nu-L6j"/>
<outlet property="RecordButton" destination="eRT-dK-6dM" id="iqk-en-NsW"/>
<outlet property="ResumeButton" destination="FZr-Ip-7WL" id="tX5-Sx-rQK"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
<tapGestureRecognizer id="fY6-qX-ntV">
<connections>
<action selector="FocusAndExposeTap:" destination="BYZ-38-t0r" id="65g-8k-5pv"/>
</connections>
</tapGestureRecognizer>
</objects>
<point key="canvasLocation" x="-656" y="-630"/>
</scene>
</scenes>
<color key="tintColor" red="1" green="1" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</document>

Просмотреть файл

@ -0,0 +1,156 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using AVFoundation;
using Foundation;
using ObjCRuntime;
namespace AVCam
{
public unsafe static partial class AVCapturePhoto_Ext
{
[CompilerGenerated]
static readonly IntPtr class_ptr = Class.GetHandle ("AVCapturePhoto");
[Export ("fileDataRepresentation")]
[CompilerGenerated]
public static NSData FileDataRepresentation (this AVCapturePhoto This)
{
return Runtime.GetNSObject<NSData> (global::ApiDefinition.Messaging.IntPtr_objc_msgSend (This.Handle, Selector.GetHandle ("fileDataRepresentation")));
}
} /* class AVCapturePhoto_Ext */
public unsafe static partial class AVCapturePhotoOutput_AVCapturePhotoOutputDepthDataDeliverySupport
{
[CompilerGenerated]
static readonly IntPtr class_ptr = Class.GetHandle ("AVCapturePhotoOutput");
[Export ("isDepthDataDeliveryEnabled")]
[CompilerGenerated]
public static bool IsDepthDataDeliveryEnabled (this AVCapturePhotoOutput This)
{
return global::ApiDefinition.Messaging.bool_objc_msgSend (This.Handle, Selector.GetHandle ("isDepthDataDeliveryEnabled"));
}
[Export ("setDepthDataDeliveryEnabled:")]
[CompilerGenerated]
public static void IsDepthDataDeliveryEnabled (this AVCapturePhotoOutput This, bool enabled)
{
global::ApiDefinition.Messaging.void_objc_msgSend_bool (This.Handle, Selector.GetHandle ("setDepthDataDeliveryEnabled:"), enabled);
}
[Export ("isDepthDataDeliverySupported")]
[CompilerGenerated]
public static bool IsDepthDataDeliverySupported (this AVCapturePhotoOutput This)
{
return global::ApiDefinition.Messaging.bool_objc_msgSend (This.Handle, Selector.GetHandle ("isDepthDataDeliverySupported"));
}
} /* class AVCapturePhotoOutput_AVCapturePhotoOutputDepthDataDeliverySupport */
public unsafe static partial class AVCapturePhotoSettings_AVCapturePhotoSettings
{
[CompilerGenerated]
static readonly IntPtr class_ptr = Class.GetHandle ("AVCapturePhotoSettings");
[Export ("isDepthDataDeliveryEnabled")]
[CompilerGenerated]
public static bool IsDepthDataDeliveryEnabled (this AVCapturePhotoSettings This)
{
return global::ApiDefinition.Messaging.bool_objc_msgSend (This.Handle, Selector.GetHandle ("isDepthDataDeliveryEnabled"));
}
[Export ("setDepthDataDeliveryEnabled:")]
[CompilerGenerated]
public static void IsDepthDataDeliveryEnabled (this AVCapturePhotoSettings This, bool enabled)
{
global::ApiDefinition.Messaging.void_objc_msgSend_bool (This.Handle, Selector.GetHandle ("setDepthDataDeliveryEnabled:"), enabled);
}
} /* class AVCapturePhotoSettings_AVCapturePhotoSettings */
public unsafe static partial class AVCapturePhotoSettings_AVCapturePhotoSettingsConversions
{
[CompilerGenerated]
static readonly IntPtr class_ptr = Class.GetHandle ("AVCapturePhotoSettings");
[Export ("processedFileType")]
[CompilerGenerated]
public static string ProcessedFileType (this AVCapturePhotoSettings This)
{
return NSString.FromHandle (global::ApiDefinition.Messaging.IntPtr_objc_msgSend (This.Handle, Selector.GetHandle ("processedFileType")));
}
} /* class AVCapturePhotoSettings_AVCapturePhotoSettingsConversions */
public unsafe static partial class AVVideo2
{
[CompilerGenerated]
static NSString _CodecHEVC;
[Field ("AVVideoCodecTypeHEVC", "__Internal")]
public static NSString CodecHEVC
{
get
{
if (_CodecHEVC == null)
_CodecHEVC = Dlfcn.GetStringConstant (Libraries.__Internal.Handle, "AVVideoCodecTypeHEVC");
return _CodecHEVC;
}
}
} /* class AVVideo2 */
public unsafe static partial class NSString_NSStringExt
{
[CompilerGenerated]
static readonly IntPtr class_ptr = Class.GetHandle ("NSString");
[Export ("boolValue")]
[CompilerGenerated]
public static bool BoolValue (this NSString This)
{
return global::ApiDefinition.Messaging.bool_objc_msgSend (This.Handle, Selector.GetHandle ("boolValue"));
}
} /* class NSString_NSStringExt */
}
namespace ApiDefinition
{
partial class Messaging
{
static internal System.Reflection.Assembly this_assembly = typeof (Messaging).Assembly;
const string LIBOBJC_DYLIB = "/usr/lib/libobjc.dylib";
[DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public extern static IntPtr IntPtr_objc_msgSend (IntPtr receiever, IntPtr selector);
[DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
public extern static IntPtr IntPtr_objc_msgSendSuper (IntPtr receiever, IntPtr selector);
[DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public extern static IntPtr IntPtr_objc_msgSend_IntPtr (IntPtr receiever, IntPtr selector, IntPtr arg1);
[DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
public extern static IntPtr IntPtr_objc_msgSendSuper_IntPtr (IntPtr receiever, IntPtr selector, IntPtr arg1);
[DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public extern static bool bool_objc_msgSend (IntPtr receiver, IntPtr selector);
[DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
public extern static bool bool_objc_msgSendSuper (IntPtr receiver, IntPtr selector);
[DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public extern static void void_objc_msgSend_bool (IntPtr receiver, IntPtr selector, bool arg1);
[DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
public extern static void void_objc_msgSendSuper_bool (IntPtr receiver, IntPtr selector, bool arg1);
}
}
namespace ObjCRuntime
{
[CompilerGenerated]
static partial class Libraries
{
static public class __Internal
{
static public readonly IntPtr Handle = Dlfcn.dlopen (null, 0);
}
}
}

40
ios11/AVCamBarcode/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,40 @@
# Autosave files
*~
# build
[Oo]bj/
[Bb]in/
packages/
TestResults/
# globs
Makefile.in
*.DS_Store
*.sln.cache
*.suo
*.cache
*.pidb
*.userprefs
*.usertasks
config.log
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.user
*.tar.gz
tarballs/
test-results/
Thumbs.db
# Mac bundle stuff
*.dmg
*.app
# resharper
*_Resharper.*
*.Resharper
# dotCover
*.dotCover

Просмотреть файл

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27110.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AVCamBarcode", "AVCamBarcode\AVCamBarcode.csproj", "{944AEF71-C52C-4C82-91D5-208131ADFEBF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|iPhone = Debug|iPhone
Debug|iPhoneSimulator = Debug|iPhoneSimulator
Release|iPhone = Release|iPhone
Release|iPhoneSimulator = Release|iPhoneSimulator
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{944AEF71-C52C-4C82-91D5-208131ADFEBF}.Debug|iPhone.ActiveCfg = Debug|iPhone
{944AEF71-C52C-4C82-91D5-208131ADFEBF}.Debug|iPhone.Build.0 = Debug|iPhone
{944AEF71-C52C-4C82-91D5-208131ADFEBF}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{944AEF71-C52C-4C82-91D5-208131ADFEBF}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{944AEF71-C52C-4C82-91D5-208131ADFEBF}.Release|iPhone.ActiveCfg = Release|iPhone
{944AEF71-C52C-4C82-91D5-208131ADFEBF}.Release|iPhone.Build.0 = Release|iPhone
{944AEF71-C52C-4C82-91D5-208131ADFEBF}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{944AEF71-C52C-4C82-91D5-208131ADFEBF}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4D275291-5334-411E-BECC-E0EADA4A7E04}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,134 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
<ProjectGuid>{944AEF71-C52C-4C82-91D5-208131ADFEBF}</ProjectGuid>
<ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Exe</OutputType>
<RootNamespace>AVCamBarcode</RootNamespace>
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
<AssemblyName>AVCamBarcode</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhoneSimulator\Debug</OutputPath>
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<MtouchArch>x86_64</MtouchArch>
<MtouchLink>None</MtouchLink>
<MtouchDebug>true</MtouchDebug>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhoneSimulator\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<MtouchLink>None</MtouchLink>
<MtouchArch>x86_64</MtouchArch>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhone\Debug</OutputPath>
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<MtouchArch>ARM64</MtouchArch>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<CodesignKey>iPhone Developer</CodesignKey>
<MtouchDebug>true</MtouchDebug>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhone\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<MtouchArch>ARM64</MtouchArch>
<ConsolePause>false</ConsolePause>
<CodesignKey>iPhone Developer</CodesignKey>
</PropertyGroup>
<PropertyGroup Condition=" '$(RunConfiguration)' == 'Default' ">
<AppExtensionDebugBundleId />
</PropertyGroup>
<ItemGroup>
<Compile Include="Extensions\EnumExtensions.cs" />
<Compile Include="ItemSelectionViewController.cs" />
<Compile Include="Enums\SessionSetupResult.cs" />
<Compile Include="Extensions\FontExtensions.cs" />
<Compile Include="Extensions\CGRectExtensions.cs" />
<Compile Include="Enums\ControlCorner.cs" />
<Compile Include="Main.cs" />
<Compile Include="AppDelegate.cs" />
<None Include="Info.plist">
<SubType>Designer</SubType>
</None>
<Compile Include="MetadataObjectLayer.cs" />
<Compile Include="PreviewView.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<InterfaceDefinition Include="Resources\LaunchScreen.storyboard" />
</ItemGroup>
<ItemGroup>
<Compile Include="CameraViewController.designer.cs">
<DependentUpon>CameraViewController.cs</DependentUpon>
</Compile>
<Compile Include="CameraViewController.cs" />
<InterfaceDefinition Include="Resources\Main.storyboard" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="Xamarin.iOS" />
</ItemGroup>
<ItemGroup>
<Content Include="Entitlements.plist" />
<BundleResource Include="Resources\xamagon.png" />
</ItemGroup>
<ItemGroup>
<ImageAsset Include="Resources\Media.xcassets\AppIcons.appiconset\Contents.json">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Media.xcassets\AppIcons.appiconset\icon-app-60@2x.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Media.xcassets\AppIcons.appiconset\Icon-app-60@3x.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Media.xcassets\AppIcons.appiconset\icon-app-76.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Media.xcassets\AppIcons.appiconset\icon-app-76@2x.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Media.xcassets\AppIcons.appiconset\icon-spotlight-29.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Media.xcassets\AppIcons.appiconset\icon-spotlight-29@2x.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Media.xcassets\AppIcons.appiconset\icon-spotlight-29@3x.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Media.xcassets\AppIcons.appiconset\icon-spotlight-40.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Media.xcassets\AppIcons.appiconset\icon-spotlight-40@2x.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Media.xcassets\AppIcons.appiconset\icon-spotlight-40@3x.png">
<Visible>false</Visible>
</ImageAsset>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
</Project>

Просмотреть файл

@ -0,0 +1,56 @@

namespace AVCamBarcode
{
using Foundation;
using UIKit;
// The UIApplicationDelegate for the application. This class is responsible for launching the
// User Interface of the application, as well as listening (and optionally responding) to
// application events from iOS.
[Register("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{
// class-level declarations
public override UIWindow Window { get; set; }
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// Override point for customization after application launch.
// If not required for your application you can safely delete this method
return true;
}
public override void OnResignActivation(UIApplication application)
{
// Invoked when the application is about to move from active to inactive state.
// This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message)
// or when the user quits the application and it begins the transition to the background state.
// Games should use this method to pause the game.
}
public override void DidEnterBackground(UIApplication application)
{
// Use this method to release shared resources, save user data, invalidate timers and store the application state.
// If your application supports background exection this method is called instead of WillTerminate when the user quits.
}
public override void WillEnterForeground(UIApplication application)
{
// Called as part of the transiton from background to active state.
// Here you can undo many of the changes made on entering the background.
}
public override void OnActivated(UIApplication application)
{
// Restart any tasks that were paused (or not yet started) while the application was inactive.
// If the application was previously in the background, optionally refresh the user interface.
}
public override void WillTerminate(UIApplication application)
{
// Called when the application is about to terminate. Save data, if needed. See also DidEnterBackground.
}
}
}

Просмотреть файл

@ -0,0 +1,857 @@

namespace AVCamBarcode
{
using AVCamBarcode.Extensions;
using AVFoundation;
using CoreAnimation;
using CoreFoundation;
using CoreGraphics;
using CoreText;
using Foundation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using UIKit;
public partial class CameraViewController : UIViewController, IAVCaptureMetadataOutputObjectsDelegate, IItemSelectionViewControllerDelegate
{
private readonly AVCaptureMetadataOutput metadataOutput = new AVCaptureMetadataOutput();
/// <summary>
/// Communicate with the session and other session objects on this queue.
/// </summary>
private readonly DispatchQueue sessionQueue = new DispatchQueue("session queue");
private readonly AVCaptureSession session = new AVCaptureSession();
private SessionSetupResult setupResult = SessionSetupResult.Success;
private AVCaptureDeviceInput videoDeviceInput;
private bool isSessionRunning;
// KVO and Notifications
private readonly List<MetadataObjectLayer> metadataObjectOverlayLayers = new List<MetadataObjectLayer>();
private UITapGestureRecognizer openBarcodeURLGestureRecognizer;
public CameraViewController(IntPtr handle) : base(handle) { }
protected UITapGestureRecognizer OpenBarcodeURLGestureRecognizer
{
get
{
if (this.openBarcodeURLGestureRecognizer == null)
{
this.openBarcodeURLGestureRecognizer = new UITapGestureRecognizer(this.OpenBarcodeUrl);
}
return this.openBarcodeURLGestureRecognizer;
}
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Disable UI. The UI is enabled if and only if the session starts running.
this.MetadataObjectTypesButton.Enabled = false;
this.SessionPresetsButton.Enabled = false;
this.CameraButton.Enabled = false;
this.ZoomSlider.Enabled = false;
// Add the open barcode gesture recognizer to the region of interest view.
this.PreviewView.AddGestureRecognizer(this.OpenBarcodeURLGestureRecognizer);
// Set up the video preview view.
this.PreviewView.Session = session;
// Check video authorization status. Video access is required and audio
// access is optional. If audio access is denied, audio is not recorded
// during movie recording.
switch (AVCaptureDevice.GetAuthorizationStatus(AVMediaType.Video))
{
case AVAuthorizationStatus.Authorized:
// The user has previously granted access to the camera.
break;
case AVAuthorizationStatus.NotDetermined:
// The user has not yet been presented with the option to grant
// video access. We suspend the session queue to delay session
// setup until the access request has completed.
this.sessionQueue.Suspend();
AVCaptureDevice.RequestAccessForMediaType(AVMediaType.Video, (granted) =>
{
if (!granted)
{
this.setupResult = SessionSetupResult.NotAuthorized;
}
this.sessionQueue.Resume();
});
break;
default:
// The user has previously denied access.
this.setupResult = SessionSetupResult.NotAuthorized;
break;
}
// Setup the capture session.
// In general it is not safe to mutate an AVCaptureSession or any of its
// inputs, outputs, or connections from multiple threads at the same time.
//
// Why not do all of this on the main queue?
// Because AVCaptureSession.StartRunning() is a blocking call which can
// take a long time. We dispatch session setup to the sessionQueue so
// that the main queue isn't blocked, which keeps the UI responsive.
this.sessionQueue.DispatchAsync(this.ConfigureSession);
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
this.sessionQueue.DispatchAsync(() =>
{
switch (this.setupResult)
{
case SessionSetupResult.Success:
// Only setup observers and start the session running if setup succeeded.
this.AddObservers();
this.session.StartRunning();
this.isSessionRunning = session.Running;
break;
case SessionSetupResult.NotAuthorized:
DispatchQueue.MainQueue.DispatchAsync(() =>
{
var message = "AVCamBarcode doesn't have permission to use the camera, please change privacy settings";
var alertController = UIAlertController.Create("AVCamBarcode", message, UIAlertControllerStyle.Alert);
alertController.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Cancel, null));
alertController.AddAction(UIAlertAction.Create("Settings", UIAlertActionStyle.Default, action =>
{
UIApplication.SharedApplication.OpenUrl(new NSUrl(UIApplication.OpenSettingsUrlString));
}));
this.PresentViewController(alertController, true, null);
});
break;
case SessionSetupResult.ConfigurationFailed:
DispatchQueue.MainQueue.DispatchAsync(() =>
{
var message = "Unable to capture media";
var alertController = UIAlertController.Create("AVCamBarcode", message, UIAlertControllerStyle.Alert);
alertController.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Cancel, null));
this.PresentViewController(alertController, true, null);
});
break;
}
});
}
public override void ViewWillDisappear(bool animated)
{
this.sessionQueue.DispatchAsync(() =>
{
if (this.setupResult == SessionSetupResult.Success)
{
this.session.StopRunning();
this.isSessionRunning = this.session.Running;
this.RemoveObservers();
}
});
base.ViewWillDisappear(animated);
}
public override bool ShouldAutorotate()
{
// Do now allow rotation if the region of interest is being resized
return !this.PreviewView.IsResizingRegionOfInterest;
}
public override void ViewWillTransitionToSize(CGSize toSize, IUIViewControllerTransitionCoordinator coordinator)
{
base.ViewWillTransitionToSize(toSize, coordinator);
var videoPreviewLayerConnection = this.PreviewView.VideoPreviewLayer.Connection;
if (videoPreviewLayerConnection != null)
{
var deviceOrientation = UIDevice.CurrentDevice.Orientation;
if (deviceOrientation.IsPortrait() || deviceOrientation.IsLandscape())
{
var newVideoOrientation = this.ConvertOrientation(deviceOrientation);
videoPreviewLayerConnection.VideoOrientation = newVideoOrientation;
// When we transition to the new size, we need to adjust the region
// of interest's origin and size so that it stays anchored relative
// to the camera.
coordinator.AnimateAlongsideTransition((context) => // animate
{
var newRegionOfInterest = this.PreviewView.VideoPreviewLayer.MapToLayerCoordinates(this.metadataOutput.RectOfInterest);
this.PreviewView.SetRegionOfInterestWithProposedRegionOfInterest(newRegionOfInterest);
}, (context) => // completion
{
// Remove the old metadata object overlays
this.RemoveMetadataObjectOverlayLayers();
});
}
}
}
private AVCaptureVideoOrientation ConvertOrientation(UIDeviceOrientation deviceOrientation)
{
var result = default(AVCaptureVideoOrientation);
switch (deviceOrientation)
{
case UIDeviceOrientation.Portrait:
result = AVCaptureVideoOrientation.Portrait;
break;
case UIDeviceOrientation.PortraitUpsideDown:
result = AVCaptureVideoOrientation.PortraitUpsideDown;
break;
// TODO: change to logical naming after it will be fixed (map `LandscapeLeft` to `LandscapeLeft`)
case UIDeviceOrientation.LandscapeLeft:
result = AVCaptureVideoOrientation.LandscapeRight;
break;
case UIDeviceOrientation.LandscapeRight:
result = AVCaptureVideoOrientation.LandscapeLeft;
break;
default:
throw new InvalidProgramException();
}
return result;
}
private void PresentItemSelectionViewController<T>(ItemSelectionViewController<T> itemSelectionViewController)
{
var navigationController = new UINavigationController(itemSelectionViewController);
navigationController.NavigationBar.BarTintColor = UIColor.Black;
navigationController.NavigationBar.TintColor = this.View.TintColor;
this.PresentViewController(navigationController, true, null);
}
#region Session Management
private readonly DispatchQueue metadataObjectsQueue = new DispatchQueue("metadata objects queue");
private void ConfigureSession()
{
if (setupResult == SessionSetupResult.Success)
{
this.session.BeginConfiguration();
// Add video input
// Choose the back wide angle camera if available, otherwise default to the front wide angle camera
AVCaptureDevice defaultVideoDevice = AVCaptureDevice.GetDefaultDevice(AVCaptureDeviceType.BuiltInWideAngleCamera, AVMediaType.Video, AVCaptureDevicePosition.Back) ??
AVCaptureDevice.GetDefaultDevice(AVCaptureDeviceType.BuiltInWideAngleCamera, AVMediaType.Video, AVCaptureDevicePosition.Front) ??
null;
if (defaultVideoDevice == null)
{
Console.WriteLine("Could not get video device");
this.setupResult = SessionSetupResult.ConfigurationFailed;
this.session.CommitConfiguration();
return;
}
NSError error;
var videoDeviceInput = AVCaptureDeviceInput.FromDevice(defaultVideoDevice, out error);
if (this.session.CanAddInput(videoDeviceInput))
{
this.session.AddInput(videoDeviceInput);
this.videoDeviceInput = videoDeviceInput;
DispatchQueue.MainQueue.DispatchAsync(() =>
{
// Why are we dispatching this to the main queue?
// Because AVCaptureVideoPreviewLayer is the backing layer for PreviewView and UIView
// can only be manipulated on the main thread
// Note: As an exception to the above rule, it's not necessary to serialize video orientation changed
// on the AVCaptureVideoPreviewLayer's connection with other session manipulation
//
// Use the status bar orientation as the internal video orientation. Subsequent orientation changes are
// handled by CameraViewController.ViewWillTransition(to:with:).
var initialVideoOrientation = AVCaptureVideoOrientation.Portrait;
var statusBarOrientation = UIApplication.SharedApplication.StatusBarOrientation;
if (statusBarOrientation != UIInterfaceOrientation.Unknown)
{
AVCaptureVideoOrientation videoOrintation;
if (Enum.TryParse(statusBarOrientation.ToString(), out videoOrintation))
{
initialVideoOrientation = videoOrintation;
}
}
this.PreviewView.VideoPreviewLayer.Connection.VideoOrientation = initialVideoOrientation;
});
}
else if (error != null)
{
Console.WriteLine($"Could not create video device input: {error}");
this.setupResult = SessionSetupResult.ConfigurationFailed;
this.session.CommitConfiguration();
return;
}
else
{
Console.WriteLine("Could not add video device input to the session");
this.setupResult = SessionSetupResult.ConfigurationFailed;
this.session.CommitConfiguration();
return;
}
// Add metadata output
if (this.session.CanAddOutput(metadataOutput))
{
this.session.AddOutput(metadataOutput);
// Set this view controller as the delegate for metadata objects
this.metadataOutput.SetDelegate(this, this.metadataObjectsQueue);
this.metadataOutput.MetadataObjectTypes = this.metadataOutput.AvailableMetadataObjectTypes; // Use all metadata object types by default
// Set an initial rect of interest that is 80% of the views's shortest side
// and 25% of the longest side. This means that the region on interest will
// appear in the same spot regardless of whether the app starts in portrait
// or landscape
var width = 0.25;
var height = 0.8;
var x = (1 - width) / 2;
var y = (1 - height) / 2;
var initialRectOfInterest = new CGRect(x, y, width, height);
this.metadataOutput.RectOfInterest = initialRectOfInterest;
DispatchQueue.MainQueue.DispatchAsync(() =>
{
var initialRegionOfInterest = this.PreviewView.VideoPreviewLayer.MapToLayerCoordinates(initialRectOfInterest);
this.PreviewView.SetRegionOfInterestWithProposedRegionOfInterest(initialRegionOfInterest);
});
}
else
{
Console.WriteLine("Could not add metadata output to the session");
this.setupResult = SessionSetupResult.ConfigurationFailed;
this.session.CommitConfiguration();
return;
}
this.session.CommitConfiguration();
}
}
#endregion
#region Presets
partial void SelectSessionPreset(UIButton sender)
{
var controller = new ItemSelectionViewController<NSString>(this,
SessionPresetItemSelectionIdentifier,
new List<NSString>(AvailableSessionPresets()),
new List<NSString> { this.session.SessionPreset },
false);
this.PresentItemSelectionViewController(controller);
}
private NSString[] AvailableSessionPresets()
{
return GetAllSessionPresets().Where(preset => session.CanSetSessionPreset(preset)).ToArray();
}
private static IEnumerable<NSString> GetAllSessionPresets()
{
yield return AVCaptureSession.PresetPhoto;
yield return AVCaptureSession.PresetLow;
yield return AVCaptureSession.PresetMedium;
yield return AVCaptureSession.PresetHigh;
yield return AVCaptureSession.Preset352x288;
yield return AVCaptureSession.Preset640x480;
yield return AVCaptureSession.Preset1280x720;
yield return AVCaptureSession.PresetiFrame960x540;
yield return AVCaptureSession.PresetiFrame1280x720;
yield return AVCaptureSession.Preset1920x1080;
yield return AVCaptureSession.Preset3840x2160;
}
#endregion
#region Device Configuration
partial void ChangeCamera(UIButton sender)
{
this.MetadataObjectTypesButton.Enabled = false;
this.SessionPresetsButton.Enabled = false;
this.CameraButton.Enabled = false;
this.ZoomSlider.Enabled = false;
// Remove the metadata overlay layers, if any.
this.RemoveMetadataObjectOverlayLayers();
DispatchQueue.MainQueue.DispatchAsync(() =>
{
var preferredPosition = AVCaptureDevicePosition.Unspecified;
switch (this.videoDeviceInput.Device.Position)
{
case AVCaptureDevicePosition.Unspecified:
case AVCaptureDevicePosition.Front:
preferredPosition = AVCaptureDevicePosition.Back;
break;
case AVCaptureDevicePosition.Back:
preferredPosition = AVCaptureDevicePosition.Front;
break;
}
var devices = AVCaptureDevice.DevicesWithMediaType(AVMediaType.Video);
var videoDevice = devices.FirstOrDefault(device => device.Position == preferredPosition);
if (videoDevice != null)
{
NSError error;
var captureDeviceInput = AVCaptureDeviceInput.FromDevice(videoDevice, out error);
if (error != null)
{
Console.WriteLine($"Error occurred while creating video device input: {error}");
return;
}
this.session.BeginConfiguration();
// Remove the existing device input first, since using the front and back camera simultaneously is not supported.
this.session.RemoveInput(this.videoDeviceInput);
// When changing devices, a session preset that may be supported
// on one device may not be supported by another. To allow the
// user to successfully switch devices, we must save the previous
// session preset, set the default session preset (High), and
// attempt to restore it after the new video device has been
// added. For example, the 4K session preset is only supported
// by the back device on the iPhone 6s and iPhone 6s Plus. As a
// result, the session will not let us add a video device that
// does not support the current session preset.
var previousSessionPreset = this.session.SessionPreset;
this.session.SessionPreset = AVCaptureSession.PresetHigh;
if (this.session.CanAddInput(captureDeviceInput))
{
this.session.AddInput(captureDeviceInput);
this.videoDeviceInput = captureDeviceInput;
}
else
{
this.session.AddInput(this.videoDeviceInput);
}
// Restore the previous session preset if we can.
if (this.session.CanSetSessionPreset(previousSessionPreset))
{
this.session.SessionPreset = previousSessionPreset;
}
this.session.CommitConfiguration();
}
this.MetadataObjectTypesButton.Enabled = true;
this.SessionPresetsButton.Enabled = true;
this.CameraButton.Enabled = true;
this.ZoomSlider.Enabled = true;
this.ZoomSlider.MaxValue = (float)NMath.Min(this.videoDeviceInput.Device.ActiveFormat.VideoMaxZoomFactor, 8);
this.ZoomSlider.Value = (float)this.videoDeviceInput.Device.VideoZoomFactor;
});
}
partial void ZoomCamera(UISlider sender)
{
var device = this.videoDeviceInput.Device;
NSError error;
this.videoDeviceInput.Device.LockForConfiguration(out error);
if (error == null)
{
device.VideoZoomFactor = this.ZoomSlider.Value;
device.UnlockForConfiguration();
}
else
{
Console.WriteLine($"Could not lock for configuration: {error}");
}
}
#endregion
#region KVO and Notifications
private NSObject interruptionEndedNotificationToken;
private NSObject wasInterruptedNotificationToken;
private NSObject runtimeErrorNotificationToken;
private IDisposable runningChangeToken;
private void AddObservers()
{
this.runningChangeToken = this.session.AddObserver("running", NSKeyValueObservingOptions.New, this.OnRunningChanged);
// Observe the previewView's regionOfInterest to update the AVCaptureMetadataOutput's
// RectOfInterest when the user finishes resizing the region of interest.
this.PreviewView.RegionOfInterestChanged += this.OnRegionOfInterestChanged;
var notificationCenter = NSNotificationCenter.DefaultCenter;
this.runtimeErrorNotificationToken = notificationCenter.AddObserver(AVCaptureSession.RuntimeErrorNotification, this.OnRuntimeErrorNotification, this.session);
// A session can only run when the app is full screen. It will be interrupted
// in a multi-app layout, introduced in iOS 9, see also the documentation of
// AVCaptureSessionInterruptionReason.Add observers to handle these session
// interruptions and show a preview is paused message.See the documentation
// of AVCaptureSessionWasInterruptedNotification for other interruption reasons.
this.wasInterruptedNotificationToken = notificationCenter.AddObserver(AVCaptureSession.WasInterruptedNotification, this.OnSessionWasInterrupted, this.session);
this.interruptionEndedNotificationToken = notificationCenter.AddObserver(AVCaptureSession.InterruptionEndedNotification, this.OnSessionInterruptionEnded, this.session);
}
private void RemoveObservers()
{
this.runningChangeToken?.Dispose();
this.runtimeErrorNotificationToken?.Dispose();
this.wasInterruptedNotificationToken?.Dispose();
this.interruptionEndedNotificationToken?.Dispose();
this.PreviewView.RegionOfInterestChanged -= this.OnRegionOfInterestChanged;
}
private void OnRegionOfInterestChanged(object sender, EventArgs e)
{
var newRegion = (sender as PreviewView).RegionOfInterest;
DispatchQueue.MainQueue.DispatchAsync(() =>
{
// Ensure we are not drawing old metadata object overlays.
this.RemoveMetadataObjectOverlayLayers();
// Translate the preview view's region of interest to the metadata output's coordinate system.
var metadataOutputRectOfInterest = this.PreviewView.VideoPreviewLayer.MapToMetadataOutputCoordinates(newRegion);
this.sessionQueue.DispatchAsync(() =>
{
// Update the AVCaptureMetadataOutput with the new region of interest
metadataOutput.RectOfInterest = metadataOutputRectOfInterest;
});
});
}
private void OnRunningChanged(NSObservedChange change)
{
var isSessionRunning = ((NSNumber)change.NewValue).BoolValue;
DispatchQueue.MainQueue.DispatchAsync(() =>
{
this.CameraButton.Enabled = isSessionRunning && AVCaptureDevice.DevicesWithMediaType(AVMediaType.Video).Length > 1;
this.MetadataObjectTypesButton.Enabled = isSessionRunning;
this.SessionPresetsButton.Enabled = isSessionRunning;
this.ZoomSlider.Enabled = isSessionRunning;
this.ZoomSlider.MaxValue = (float)NMath.Min(this.videoDeviceInput.Device.ActiveFormat.VideoMaxZoomFactor, 8);
this.ZoomSlider.Value = (float)(this.videoDeviceInput.Device.VideoZoomFactor);
// After the session stop running, remove the metadata object overlays,
// if any, so that if the view appears again, the previously displayed
// metadata object overlays are removed.
if (!isSessionRunning)
{
this.RemoveMetadataObjectOverlayLayers();
}
// When the session starts running, the aspect ration of the video preview may also change if a new session present was applied .
// To keep the preview view's region of interest within the visible portion of the video preview, the preview view's region of
// interest will need to be updates.
if (isSessionRunning)
{
this.PreviewView.SetRegionOfInterestWithProposedRegionOfInterest(this.PreviewView.RegionOfInterest);
}
});
}
private void OnRuntimeErrorNotification(NSNotification notification)
{
var args = new AVCaptureSessionRuntimeErrorEventArgs(notification);
if (args.Error != null)
{
var error = (AVError)(long)args.Error.Code;
Console.WriteLine($"Capture session runtime error: {error}");
// Automatically try to restart the session running if media services were
// reset and the last start running succeeded. Otherwise, enable the user
// to try to resume the session running.
if (error == AVError.MediaServicesWereReset)
{
this.sessionQueue.DispatchAsync(() =>
{
if (this.isSessionRunning)
{
this.session.StartRunning();
this.isSessionRunning = session.Running;
}
});
}
}
}
private void OnSessionWasInterrupted(NSNotification notification)
{
// In some scenarios we want to enable the user to resume the session running.
// For example, if music playback is initiated via control center while
// using AVMetadataRecordPlay, then the user can let AVMetadataRecordPlay resume
// the session running, which will stop music playback. Note that stopping
// music playback in control center will not automatically resume the session
// running. Also note that it is not always possible to resume
var reasonIntegerValue = ((NSNumber)notification.UserInfo[AVCaptureSession.InterruptionReasonKey]).Int32Value;
var reason = (AVCaptureSessionInterruptionReason)reasonIntegerValue;
Console.WriteLine($"Capture session was interrupted with reason {reason}");
if (reason == AVCaptureSessionInterruptionReason.VideoDeviceNotAvailableWithMultipleForegroundApps)
{
// Simply fade-in a label to inform the user that the camera is unavailable.
this.CameraUnavailableLabel.Hidden = false;
this.CameraUnavailableLabel.Alpha = 0;
UIView.Animate(0.25d, () => this.CameraUnavailableLabel.Alpha = 1);
}
}
private void OnSessionInterruptionEnded(NSNotification notification)
{
Console.WriteLine("Capture session interruption ended");
if (this.CameraUnavailableLabel.Hidden)
{
UIView.Animate(0.25, () =>
{
this.CameraUnavailableLabel.Alpha = 0;
}, () =>
{
this.CameraUnavailableLabel.Hidden = true;
});
}
}
#endregion
#region Drawing Metadata Object Overlay Layers
private NSTimer removeMetadataObjectOverlayLayersTimer;
partial void SelectMetadataObjectTypes(UIButton sender)
{
var controller = new ItemSelectionViewController<AVMetadataObjectType>(this,
MetadataObjectTypeItemSelectionIdentifier,
this.metadataOutput.AvailableMetadataObjectTypes.GetFlags().ToList(),
this.metadataOutput.MetadataObjectTypes.GetFlags().ToList(),
true);
this.PresentItemSelectionViewController(controller);
}
private MetadataObjectLayer CreateMetadataOverlay(AVMetadataObject metadataObject)
{
// Transform the metadata object so the bounds are updated to reflect those of the video preview layer.
var transformedMetadataObject = this.PreviewView.VideoPreviewLayer.GetTransformedMetadataObject(metadataObject);
// Create the initial metadata object overlay layer that can be used for either machine readable codes or faces.
var metadataObjectOverlayLayer = new MetadataObjectLayer
{
LineWidth = 7,
LineJoin = CAShapeLayer.JoinRound,
MetadataObject = transformedMetadataObject,
FillColor = this.View.TintColor.ColorWithAlpha(0.3f).CGColor,
StrokeColor = this.View.TintColor.ColorWithAlpha(0.7f).CGColor,
};
var barcodeMetadataObject = transformedMetadataObject as AVMetadataMachineReadableCodeObject;
if (barcodeMetadataObject != null)
{
var barcodeOverlayPath = this.BarcodeOverlayPathWithCorners(barcodeMetadataObject.Corners);
metadataObjectOverlayLayer.Path = barcodeOverlayPath;
// If the metadata object has a string value, display it.
string textLayerString = null;
if (!string.IsNullOrEmpty(barcodeMetadataObject.StringValue))
{
textLayerString = barcodeMetadataObject.StringValue;
}
else
{
// TODO: add Descriptor (line 618 in original iOS sample)
}
if (!string.IsNullOrEmpty(textLayerString))
{
var barcodeOverlayBoundingBox = barcodeOverlayPath.BoundingBox;
var font = UIFont.BoldSystemFontOfSize(19).ToCTFont();
var textLayer = new CATextLayer
{
AlignmentMode = CATextLayer.AlignmentCenter,
Bounds = new CGRect(0, 0, barcodeOverlayBoundingBox.Size.Width, barcodeOverlayBoundingBox.Size.Height),
ContentsScale = UIScreen.MainScreen.Scale,
Position = new CGPoint(barcodeOverlayBoundingBox.GetMidX(), barcodeOverlayBoundingBox.GetMidY()),
Wrapped = true,
// Invert the effect of transform of the video preview so the text is oriented with the interface orientation
Transform = CATransform3D.MakeFromAffine(this.PreviewView.Transform).Invert(),
String = textLayerString,
AttributedString = new NSAttributedString(textLayerString, new CTStringAttributes
{
Font = font,
StrokeWidth = -5,
StrokeColor = UIColor.Black.CGColor,
ForegroundColor = UIColor.White.CGColor,
}),
};
textLayer.SetFont(font);
metadataObjectOverlayLayer.AddSublayer(textLayer);
}
}
else if (transformedMetadataObject is AVMetadataFaceObject)
{
metadataObjectOverlayLayer.Path = CGPath.FromRect(transformedMetadataObject.Bounds);
}
return metadataObjectOverlayLayer;
}
private CGPath BarcodeOverlayPathWithCorners(CGPoint[] corners)
{
var path = new CGPath();
if (corners.Any())
{
path.MoveToPoint(CGAffineTransform.MakeIdentity(), corners[0]);
for (int i = 1; i < corners.Length; i++)
{
path.AddLineToPoint(corners[i]);
}
path.CloseSubpath();
}
return path;
}
private void RemoveMetadataObjectOverlayLayers()
{
this.metadataObjectOverlayLayers.ForEach(layer => layer.RemoveFromSuperLayer());
this.metadataObjectOverlayLayers.Clear();
this.removeMetadataObjectOverlayLayersTimer?.Invalidate();
this.removeMetadataObjectOverlayLayersTimer = null;
}
private void AddMetadataOverlayLayers(IEnumerable<MetadataObjectLayer> layers)
{
// Add the metadata object overlays as sublayers of the video preview layer. We disable actions to allow for fast drawing.
CATransaction.Begin();
CATransaction.DisableActions = true;
foreach (var layer in layers)
{
this.PreviewView.VideoPreviewLayer.AddSublayer(layer);
this.metadataObjectOverlayLayers.Add(layer); // Save the new metadata object overlays.
}
CATransaction.Commit();
// Create a timer to destroy the metadata object overlays.
this.removeMetadataObjectOverlayLayersTimer = NSTimer.CreateScheduledTimer(TimeSpan.FromSeconds(1), (param) => this.RemoveMetadataObjectOverlayLayers());
}
private void OpenBarcodeUrl(UITapGestureRecognizer openBarcodeURLGestureRecognizer)
{
foreach (var metadataObjectOverlayLayer in this.metadataObjectOverlayLayers)
{
var location = openBarcodeURLGestureRecognizer.LocationInView(this.PreviewView);
if (metadataObjectOverlayLayer.Path.ContainsPoint(location, false))
{
var barcodeMetadataObject = metadataObjectOverlayLayer.MetadataObject as AVMetadataMachineReadableCodeObject;
if (barcodeMetadataObject != null)
{
if (!string.IsNullOrEmpty(barcodeMetadataObject.StringValue))
{
var url = NSUrl.FromString(barcodeMetadataObject.StringValue);
if (UIApplication.SharedApplication.CanOpenUrl(url))
{
UIApplication.SharedApplication.OpenUrl(url);
}
}
}
}
}
}
#endregion
#region AVCaptureMetadataOutputObjectsDelegate
private readonly AutoResetEvent resetEvent = new AutoResetEvent(true);
[Export("captureOutput:didOutputMetadataObjects:fromConnection:")]
public void DidOutputMetadataObjects(AVCaptureMetadataOutput captureOutput, AVMetadataObject[] metadataObjects, AVCaptureConnection connection)
{
// resetEvent is used to drop new notifications if old ones are still processing, to avoid queuing up a bunch of stale data.
if (this.resetEvent.WaitOne(0))
{
DispatchQueue.MainQueue.DispatchAsync(() =>
{
this.RemoveMetadataObjectOverlayLayers();
this.AddMetadataOverlayLayers(metadataObjects.Select(this.CreateMetadataOverlay));
this.resetEvent.Set();
});
}
}
#endregion
#region ItemSelectionViewControllerDelegate
private const string MetadataObjectTypeItemSelectionIdentifier = "MetadataObjectTypes";
private const string SessionPresetItemSelectionIdentifier = "SessionPreset";
public void ItemSelectionViewController<T>(ItemSelectionViewController<T> itemSelectionViewController, IList<T> selectedItems)
{
var identifier = itemSelectionViewController.Identifier;
if (identifier == MetadataObjectTypeItemSelectionIdentifier)
{
this.sessionQueue.DispatchAsync(() =>
{
this.metadataOutput.MetadataObjectTypes = selectedItems.OfType<AVMetadataObjectType>().Aggregate((t1, t2) => t1 | t2);
});
}
else if (identifier == SessionPresetItemSelectionIdentifier)
{
this.sessionQueue.DispatchAsync(() =>
{
this.session.SessionPreset = selectedItems.OfType<NSString>().FirstOrDefault();
});
}
}
#endregion
}
}

89
ios11/AVCamBarcode/AVCamBarcode/CameraViewController.designer.cs сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,89 @@
// WARNING
//
// This file has been generated automatically by Visual Studio from the outlets and
// actions declared in your storyboard file.
// Manual changes to this file will not be maintained.
//
using Foundation;
using System;
using System.CodeDom.Compiler;
namespace AVCamBarcode
{
[Register ("CameraViewController")]
partial class CameraViewController
{
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIButton CameraButton { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UILabel CameraUnavailableLabel { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIButton MetadataObjectTypesButton { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
AVCamBarcode.PreviewView PreviewView { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UIButton SessionPresetsButton { get; set; }
[Outlet]
[GeneratedCode ("iOS Designer", "1.0")]
UIKit.UISlider ZoomSlider { get; set; }
[Action ("ChangeCamera:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void ChangeCamera (UIKit.UIButton sender);
[Action ("SelectMetadataObjectTypes:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void SelectMetadataObjectTypes (UIKit.UIButton sender);
[Action ("SelectSessionPreset:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void SelectSessionPreset (UIKit.UIButton sender);
[Action ("ZoomCamera:")]
[GeneratedCode ("iOS Designer", "1.0")]
partial void ZoomCamera (UIKit.UISlider sender);
void ReleaseDesignerOutlets ()
{
if (CameraButton != null) {
CameraButton.Dispose ();
CameraButton = null;
}
if (CameraUnavailableLabel != null) {
CameraUnavailableLabel.Dispose ();
CameraUnavailableLabel = null;
}
if (MetadataObjectTypesButton != null) {
MetadataObjectTypesButton.Dispose ();
MetadataObjectTypesButton = null;
}
if (PreviewView != null) {
PreviewView.Dispose ();
PreviewView = null;
}
if (SessionPresetsButton != null) {
SessionPresetsButton.Dispose ();
SessionPresetsButton = null;
}
if (ZoomSlider != null) {
ZoomSlider.Dispose ();
ZoomSlider = null;
}
}
}
}

Просмотреть файл

@ -0,0 +1,6 @@
<?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">
<dict>
</dict>
</plist>

Просмотреть файл

@ -0,0 +1,16 @@

namespace AVCamBarcode
{
public enum ControlCorner
{
None,
TopLeft,
TopRight,
BottomLeft,
BottomRight
}
}

Просмотреть файл

@ -0,0 +1,12 @@

namespace AVCamBarcode
{
public enum SessionSetupResult
{
Success,
NotAuthorized,
ConfigurationFailed
}
}

Просмотреть файл

@ -0,0 +1,29 @@

namespace AVCamBarcode.Extensions
{
using CoreGraphics;
public static class CGRectExtensions
{
public static CGPoint CornerTopLeft(this CGRect rect)
{
return rect.Location;
}
public static CGPoint CornerTopRight(this CGRect rect)
{
rect.X += rect.Width;
return rect.Location;
}
public static CGPoint CornerBottomRight(this CGRect rect)
{
return new CGPoint(rect.GetMaxX(), rect.GetMaxY());
}
public static CGPoint CornerBottomLeft(this CGRect rect)
{
return new CGPoint(rect.GetMinX(), rect.GetMaxY());
}
}
}

Просмотреть файл

@ -0,0 +1,26 @@

namespace AVCamBarcode.Extensions
{
using AVFoundation;
using System.Collections.Generic;
public static class EnumExtensions
{
public static IEnumerable<AVMetadataObjectType> GetFlags(this AVMetadataObjectType metadataObjectType)
{
var shifts = 0;
var value = (ulong)metadataObjectType;
while (value != 0)
{
if ((value & 1) == 1)
{
yield return (AVMetadataObjectType)(1 << shifts);
}
shifts++;
value >>= 1;
}
}
}
}

Просмотреть файл

@ -0,0 +1,14 @@

namespace AVCamBarcode.Extensions
{
using CoreText;
using UIKit;
public static class FontExtensions
{
public static CTFont ToCTFont(this UIFont font)
{
return new CTFont(font.Name, font.PointSize);
}
}
}

Просмотреть файл

@ -0,0 +1,56 @@
<?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">
<dict>
<key>CFBundleDisplayName</key>
<string>AVCamBarcode</string>
<key>CFBundleIdentifier</key>
<string>com.companyname.AVCamBarcode</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>MinimumOSVersion</key>
<string>11.0</string>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>NSCameraUsageDescription</key>
<string>to scan barcodes and faces</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIMainStoryboardFile~ipad</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIStatusBarHidden</key>
<true/>
<key>UIRequiresFullScreen</key>
<true/>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>XSAppIconAssets</key>
<string>Resources/Media.xcassets/AppIcons.appiconset</string>
</dict>
</plist>

Просмотреть файл

@ -0,0 +1,106 @@

namespace AVCamBarcode
{
using Foundation;
using System;
using System.Collections.Generic;
using System.Linq;
using UIKit;
public interface IItemSelectionViewControllerDelegate
{
void ItemSelectionViewController<T>(ItemSelectionViewController<T> itemSelectionViewController, IList<T> selectedItems);
}
public class ItemSelectionViewController<T> : UITableViewController
{
private const string itemCellIdentifier = "Item";
private readonly IItemSelectionViewControllerDelegate @delegate;
private bool isMultipleSelectionAllowed;
private readonly IList<T> selectedItems;
private readonly IList<T> items;
public ItemSelectionViewController(IItemSelectionViewControllerDelegate @delegate,
string identifier,
IList<T> items,
IList<T> selectedItems,
bool isMultipleSelectionAllowed) : base(UITableViewStyle.Grouped)
{
this.@delegate = @delegate;
this.Identifier = identifier;
this.items = items;
this.selectedItems = selectedItems;
this.isMultipleSelectionAllowed = isMultipleSelectionAllowed;
base.NavigationItem.RightBarButtonItem = new UIBarButtonItem(UIBarButtonSystemItem.Done, (sender, args) => this.Done());
this.TableView.RegisterClassForCellReuse(typeof(UITableViewCell), itemCellIdentifier);
}
public string Identifier { get; private set; }
private void Done()
{
// Notify the delegate that selecting items is finished.
this.@delegate?.ItemSelectionViewController(this, this.selectedItems);
// Dismiss the view controller.
this.DismissViewController(true, null);
}
#region UITableView
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
var item = this.items[indexPath.Row];
var cell = tableView.DequeueReusableCell(itemCellIdentifier, indexPath);
cell.TintColor = UIColor.Black;
cell.TextLabel.Text = item.ToString();
cell.Accessory = this.selectedItems.Contains(item) ? UITableViewCellAccessory.Checkmark : UITableViewCellAccessory.None;
return cell;
}
public override nint RowsInSection(UITableView tableView, nint section)
{
return this.items.Count;
}
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
NSIndexPath[] indexPathsToReload = null;
if (this.isMultipleSelectionAllowed)
{
var item = this.items[indexPath.Row];
if (!this.selectedItems.Remove(item))
{
this.selectedItems.Add(item);
}
indexPathsToReload = new NSIndexPath[] { indexPath };
}
else
{
indexPathsToReload = !this.selectedItems.Any()
? new NSIndexPath[] { indexPath }
: this.selectedItems.Select(item => this.items.IndexOf(item))
.Select(index => NSIndexPath.FromRowSection(index, 0))
.Concat(new NSIndexPath[] { indexPath })
.ToArray();
this.selectedItems.Clear();
this.selectedItems.Add(this.items[indexPath.Row]);
}
// Deselect the selected row & reload the table view cells for the old and new items to swap checkmarks.
tableView.DeselectRow(indexPath, true);
tableView.ReloadRows(indexPathsToReload, UITableViewRowAnimation.Automatic);
}
#endregion
}
}

Просмотреть файл

@ -0,0 +1,16 @@

namespace AVCamBarcode
{
using UIKit;
public class Application
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, "AppDelegate");
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@

namespace AVCamBarcode
{
using AVFoundation;
using CoreAnimation;
public class MetadataObjectLayer : CAShapeLayer
{
public AVMetadataObject MetadataObject { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,482 @@

namespace AVCamBarcode
{
using AVCamBarcode.Extensions;
using AVFoundation;
using CoreAnimation;
using CoreGraphics;
using Foundation;
using ObjCRuntime;
using System;
using UIKit;
[Register("PreviewView")]
public class PreviewView : UIView, IUIGestureRecognizerDelegate
{
private static readonly object layerClassLock = new object();
private static Class layerClass;
private const float RegionOfInterestCornerTouchThreshold = 50;
private const float RegionOfInterestControlDiameter = 12;
private readonly CAShapeLayer maskLayer = new CAShapeLayer();
private readonly CAShapeLayer regionOfInterestOutline = new CAShapeLayer();
/// <summary>
/// White dot on the top left of the region of interest.
/// </summary>
private readonly CAShapeLayer topLeftControl = new CAShapeLayer();
/// <summary>
/// White dot on the top right of the region of interest.
/// </summary>
private readonly CAShapeLayer topRightControl = new CAShapeLayer();
/// <summary>
/// White dot on the bottom left of the region of interest.
/// </summary>
private readonly CAShapeLayer bottomLeftControl = new CAShapeLayer();
/// <summary>
/// White dot on the bottom right of the region of interest.
/// </summary>
private readonly CAShapeLayer bottomRightControl = new CAShapeLayer();
/// <summary>
/// Saves a reference to the control corner that the user is using to resize
/// the region of interest in `resizeRegionOfInterestWithGestureRecognizer()`.
/// </summary>
private ControlCorner currentControlCorner = ControlCorner.None;
private UIPanGestureRecognizer resizeRegionOfInterestGestureRecognizer;
public event EventHandler RegionOfInterestChanged;
public PreviewView(CGRect frame) : base(frame)
{
this.Initialize();
}
[Export("initWithCoder:")]
public PreviewView(NSCoder coder) : base(coder)
{
this.Initialize();
}
public static Class LayerClass
{
[Export("layerClass")]
get
{
if (layerClass == null)
{
lock (layerClassLock)
{
if (layerClass == null)
{
layerClass = new Class(typeof(AVCaptureVideoPreviewLayer));
}
}
}
return layerClass;
}
}
// Region of Interest
/// <summary>
/// The minimum region of interest's size cannot be smaller than the corner
/// touch threshold as to avoid control selection conflicts when a user tries
/// to resize the region of interest.
/// </summary>
protected float MinimumRegionOfInterestSize => RegionOfInterestCornerTouchThreshold;
protected float RegionOfInterestControlRadius => RegionOfInterestControlDiameter / 2;
public bool IsResizingRegionOfInterest => this.resizeRegionOfInterestGestureRecognizer.State == UIGestureRecognizerState.Changed;
// This property is set only in `setRegionOfInterestWithProposedRegionOfInterest()`.
// When a user is resizing the region of interest in `resizeRegionOfInterestWithGestureRecognizer()`,
// the KVO notification will be triggered when the resizing is finished.
public CGRect RegionOfInterest { get; private set; }
// AV capture properties
public AVCaptureVideoPreviewLayer VideoPreviewLayer => this.Layer as AVCaptureVideoPreviewLayer;
public AVCaptureSession Session
{
get
{
return this.VideoPreviewLayer.Session;
}
set
{
this.VideoPreviewLayer.Session = value;
}
}
protected UIPanGestureRecognizer ResizeRegionOfInterestGestureRecognizer
{
get
{
if (this.resizeRegionOfInterestGestureRecognizer == null)
{
this.resizeRegionOfInterestGestureRecognizer = new UIPanGestureRecognizer(this.ResizeRegionOfInterestWithGestureRecognizer);
}
return this.resizeRegionOfInterestGestureRecognizer;
}
}
private void Initialize()
{
this.maskLayer.FillRule = CAShapeLayer.FillRuleEvenOdd;
this.maskLayer.FillColor = UIColor.Black.CGColor;
this.maskLayer.Opacity = 0.6f;
this.Layer.AddSublayer(this.maskLayer);
this.regionOfInterestOutline.Path = UIBezierPath.FromRect(this.RegionOfInterest).CGPath;
this.regionOfInterestOutline.FillColor = UIColor.Clear.CGColor;
this.regionOfInterestOutline.StrokeColor = UIColor.Yellow.CGColor;
this.Layer.AddSublayer(this.regionOfInterestOutline);
var controlRect = new CGRect(0, 0, RegionOfInterestControlDiameter, RegionOfInterestControlDiameter);
this.topLeftControl.Path = UIBezierPath.FromOval(controlRect).CGPath;
this.topLeftControl.FillColor = UIColor.White.CGColor;
this.Layer.AddSublayer(this.topLeftControl);
this.topRightControl.Path = UIBezierPath.FromOval(controlRect).CGPath;
this.topRightControl.FillColor = UIColor.White.CGColor;
this.Layer.AddSublayer(this.topRightControl);
this.bottomLeftControl.Path = UIBezierPath.FromOval(controlRect).CGPath;
this.bottomLeftControl.FillColor = UIColor.White.CGColor;
this.Layer.AddSublayer(this.bottomLeftControl);
this.bottomRightControl.Path = UIBezierPath.FromOval(controlRect).CGPath;
this.bottomRightControl.FillColor = UIColor.White.CGColor;
this.Layer.AddSublayer(this.bottomRightControl);
// Add the region of interest gesture recognizer to the region of interest
// view so that the region of interest can be resized and moved. If you
// would like to have a fixed region of interest that cannot be resized
// or moved, do not add the following gesture recognizer. You will simply
// need to set the region of interest once.
this.ResizeRegionOfInterestGestureRecognizer.Delegate = this;
this.AddGestureRecognizer(this.ResizeRegionOfInterestGestureRecognizer);
}
/// <summary>
/// Updates the region of interest with a proposed region of interest ensuring
/// the new region of interest is within the bounds of the video preview. When
/// a new region of interest is set, the region of interest is redrawn.
/// </summary>
/// <param name="proposedRegionOfInterest"></param>
public void SetRegionOfInterestWithProposedRegionOfInterest(CGRect proposedRegionOfInterest)
{
// We standardize to ensure we have positive widths and heights with an origin at the top left.
var videoPreviewRect = this.VideoPreviewLayer.MapToLayerCoordinates(new CGRect(0, 0, 1, 1)).Standardize();
// Intersect the video preview view with the view's frame to only get
// the visible portions of the video preview view.
var visibleVideoPreviewRect = CGRect.Intersect(videoPreviewRect, this.Frame);
var oldRegionOfInterest = this.RegionOfInterest;
var newRegionOfInterest = proposedRegionOfInterest.Standardize();
// Move the region of interest in bounds.
if (this.currentControlCorner == ControlCorner.None)
{
nfloat xOffset = 0;
nfloat yOffset = 0;
if (!visibleVideoPreviewRect.Contains(newRegionOfInterest.CornerTopLeft()))
{
xOffset = NMath.Max(visibleVideoPreviewRect.GetMinX() - newRegionOfInterest.GetMinX(), 0);
yOffset = NMath.Max(visibleVideoPreviewRect.GetMinY() - newRegionOfInterest.GetMinY(), 0);
}
if (!visibleVideoPreviewRect.Contains(visibleVideoPreviewRect.CornerBottomRight()))
{
xOffset = NMath.Min(visibleVideoPreviewRect.GetMaxX() - newRegionOfInterest.GetMaxX(), xOffset);
yOffset = NMath.Min(visibleVideoPreviewRect.GetMaxY() - newRegionOfInterest.GetMaxY(), yOffset);
}
newRegionOfInterest.Offset(xOffset, yOffset);
}
// Clamp the size when the region of interest is being resized.
visibleVideoPreviewRect.Intersect(newRegionOfInterest);
newRegionOfInterest = visibleVideoPreviewRect;
// Fix a minimum width of the region of interest.
if (proposedRegionOfInterest.Size.Width < this.MinimumRegionOfInterestSize)
{
switch (this.currentControlCorner)
{
case ControlCorner.TopLeft:
case ControlCorner.BottomLeft:
var x = oldRegionOfInterest.Location.X + oldRegionOfInterest.Size.Width - this.MinimumRegionOfInterestSize;
newRegionOfInterest.X = x;
newRegionOfInterest.Width = this.MinimumRegionOfInterestSize;
break;
case ControlCorner.TopRight:
newRegionOfInterest.X = oldRegionOfInterest.Location.X;
newRegionOfInterest.Width = this.MinimumRegionOfInterestSize;
break;
default:
newRegionOfInterest.Location = oldRegionOfInterest.Location;
newRegionOfInterest.Width = this.MinimumRegionOfInterestSize;
break;
}
}
// Fix a minimum height of the region of interest.
if (proposedRegionOfInterest.Height < this.MinimumRegionOfInterestSize)
{
switch (currentControlCorner)
{
case ControlCorner.TopLeft:
case ControlCorner.TopRight:
newRegionOfInterest.Y = oldRegionOfInterest.Y + oldRegionOfInterest.Height - this.MinimumRegionOfInterestSize;
newRegionOfInterest.Height = this.MinimumRegionOfInterestSize;
break;
case ControlCorner.BottomLeft:
newRegionOfInterest.Y = oldRegionOfInterest.Y;
newRegionOfInterest.Height = this.MinimumRegionOfInterestSize;
break;
default:
newRegionOfInterest.Location = oldRegionOfInterest.Location;
newRegionOfInterest.Height = this.MinimumRegionOfInterestSize;
break;
}
}
this.RegionOfInterest = newRegionOfInterest;
this.SetNeedsLayout();
}
private void ResizeRegionOfInterestWithGestureRecognizer(UIPanGestureRecognizer gestureRecognizer)
{
var touchLocation = gestureRecognizer.LocationInView(gestureRecognizer.View);
var oldRegionOfInterest = this.RegionOfInterest;
switch (gestureRecognizer.State)
{
case UIGestureRecognizerState.Began:
// When the gesture begins, save the corner that is closes to
// the resize region of interest gesture recognizer's touch location.
this.currentControlCorner = CornerOfRect(oldRegionOfInterest, touchLocation);
break;
case UIGestureRecognizerState.Changed:
var newRegionOfInterest = oldRegionOfInterest;
switch (this.currentControlCorner)
{
case ControlCorner.None:
// Update the new region of interest with the gesture recognizer's translation.
var translation = gestureRecognizer.TranslationInView(gestureRecognizer.View);
// Move the region of interest with the gesture recognizer's translation.
if (this.RegionOfInterest.Contains(touchLocation))
{
newRegionOfInterest.X += translation.X;
newRegionOfInterest.Y += translation.Y;
}
// If the touch location goes outside the preview layer,
// we will only translate the region of interest in the
// plane that is not out of bounds.
var normalizedRect = new CGRect(0, 0, 1, 1);
if (!normalizedRect.Contains(this.VideoPreviewLayer.PointForCaptureDevicePointOfInterest(touchLocation)))
{
if (touchLocation.X < RegionOfInterest.GetMinX() || touchLocation.X > RegionOfInterest.GetMaxX())
{
newRegionOfInterest.Y += translation.Y;
}
else if (touchLocation.Y < RegionOfInterest.GetMinY() || touchLocation.Y > RegionOfInterest.GetMaxY())
{
newRegionOfInterest.X += translation.X;
}
}
// Set the translation to be zero so that the new gesture
// recognizer's translation is in respect to the region of
// interest's new position.
gestureRecognizer.SetTranslation(CGPoint.Empty, gestureRecognizer.View);
break;
case ControlCorner.TopLeft:
newRegionOfInterest = new CGRect(touchLocation.X, touchLocation.Y,
oldRegionOfInterest.Width + oldRegionOfInterest.X - touchLocation.X,
oldRegionOfInterest.Height + oldRegionOfInterest.Y - touchLocation.Y);
break;
case ControlCorner.TopRight:
newRegionOfInterest = new CGRect(newRegionOfInterest.X,
touchLocation.Y,
touchLocation.X - newRegionOfInterest.X,
oldRegionOfInterest.Height + newRegionOfInterest.Y - touchLocation.Y);
break;
case ControlCorner.BottomLeft:
newRegionOfInterest = new CGRect(touchLocation.X,
oldRegionOfInterest.Y,
oldRegionOfInterest.Width + oldRegionOfInterest.X - touchLocation.X,
touchLocation.Y - oldRegionOfInterest.Y);
break;
case ControlCorner.BottomRight:
newRegionOfInterest = new CGRect(oldRegionOfInterest.X,
oldRegionOfInterest.Y,
touchLocation.X - oldRegionOfInterest.X,
touchLocation.Y - oldRegionOfInterest.Y);
break;
}
// Update the region of interest with a valid CGRect.
this.SetRegionOfInterestWithProposedRegionOfInterest(newRegionOfInterest);
break;
case UIGestureRecognizerState.Ended:
this.RegionOfInterestChanged?.Invoke(this, EventArgs.Empty);
// Reset the current corner reference to none now that the resize.
// gesture recognizer has ended.
this.currentControlCorner = ControlCorner.None;
break;
default:
return;
}
}
private ControlCorner CornerOfRect(CGRect rect, CGPoint point)
{
var closestDistance = nfloat.MaxValue;
var closestCorner = ControlCorner.None;
Tuple<ControlCorner, CGPoint>[] corners = {
Tuple (ControlCorner.TopLeft, rect.CornerTopLeft()),
Tuple (ControlCorner.TopRight, rect.CornerTopRight ()),
Tuple (ControlCorner.BottomLeft, rect.CornerBottomLeft ()),
Tuple (ControlCorner.BottomRight, rect.CornerBottomRight())
};
foreach (var tuple in corners)
{
var corner = tuple.Item1;
var cornerPoint = tuple.Item2;
var dX = point.X - cornerPoint.X;
var dY = point.Y - cornerPoint.Y;
var distance = NMath.Sqrt((dX * dX) + (dY * dY));
if (distance < closestDistance)
{
closestDistance = distance;
closestCorner = corner;
}
}
if (closestDistance > RegionOfInterestCornerTouchThreshold)
{
closestCorner = ControlCorner.None;
}
return closestCorner;
}
private static Tuple<ControlCorner, CGPoint> Tuple(ControlCorner corner, CGPoint point)
{
return new Tuple<ControlCorner, CGPoint>(corner, point);
}
#region UIView
public override void LayoutSubviews()
{
base.LayoutSubviews();
// Disable CoreAnimation actions so that the positions of the sublayers immediately move to their new position.
CATransaction.Begin();
CATransaction.DisableActions = true;
// Create the path for the mask layer. We use the even odd fill rule so that the region of interest does not have a fill color.
var path = UIBezierPath.FromRect(new CGRect(0, 0, this.Frame.Width, this.Frame.Height));
path.AppendPath(UIBezierPath.FromRect(this.RegionOfInterest));
path.UsesEvenOddFillRule = true;
this.maskLayer.Path = path.CGPath;
this.regionOfInterestOutline.Path = CGPath.FromRect(this.RegionOfInterest);
var left = this.RegionOfInterest.X - this.RegionOfInterestControlRadius;
var right = this.RegionOfInterest.X + this.RegionOfInterest.Width - this.RegionOfInterestControlRadius;
var top = this.RegionOfInterest.Y - this.RegionOfInterestControlRadius;
var bottom = this.RegionOfInterest.Y + this.RegionOfInterest.Height - this.RegionOfInterestControlRadius;
this.topLeftControl.Position = new CGPoint(left, top);
this.topRightControl.Position = new CGPoint(right, top);
this.bottomLeftControl.Position = new CGPoint(left, bottom);
this.bottomRightControl.Position = new CGPoint(right, bottom);
CATransaction.Commit();
}
#endregion
#region IUIGestureRecognizerDelegate
[Export("gestureRecognizer:shouldReceiveTouch:")]
public bool ShouldReceiveTouch(UIGestureRecognizer gestureRecognizer, UITouch touch)
{
var result = true;
// Ignore drags outside of the region of interest (plus some padding).
if (gestureRecognizer == this.resizeRegionOfInterestGestureRecognizer)
{
var touchLocation = touch.LocationInView(gestureRecognizer.View);
var paddedRegionOfInterest = this.RegionOfInterest.Inset(-RegionOfInterestCornerTouchThreshold, -RegionOfInterestCornerTouchThreshold);
if (!paddedRegionOfInterest.Contains(touchLocation))
{
result = false;
}
}
return result;
}
[Export("gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:")]
public bool ShouldRecognizeSimultaneously(UIGestureRecognizer gestureRecognizer, UIGestureRecognizer otherGestureRecognizer)
{
var result = false;
// Allow multiple gesture recognizers to be recognized simultaneously if and only if the touch location is not within the touch threshold.
if (gestureRecognizer == this.resizeRegionOfInterestGestureRecognizer)
{
var touchLocation = gestureRecognizer.LocationInView(gestureRecognizer.View);
result = this.CornerOfRect(this.RegionOfInterest, touchLocation) == ControlCorner.None;
}
return result;
}
#endregion
}
}

20
ios11/AVCamBarcode/AVCamBarcode/PreviewView.designer.cs сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,20 @@
// WARNING
//
// This file has been generated automatically by Visual Studio from the outlets and
// actions declared in your storyboard file.
// Manual changes to this file will not be maintained.
//
using Foundation;
using System;
using System.CodeDom.Compiler;
namespace AVCamBarcode
{
[Register ("PreviewView")]
partial class PreviewView
{
void ReleaseDesignerOutlets ()
{
}
}
}

Просмотреть файл

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("AVCamBarcode")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AVCamBarcode")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("944aef71-c52c-4c82-91d5-208131adfebf")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Просмотреть файл

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13527"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="248" translatesAutoresizingMaskIntoConstraints="NO" image="xamagon.png" misplaced="YES">
<rect key="frame" x="55" y="216" width="305" height="305"/>
<constraints>
<constraint id="251" firstItem="248" firstAttribute="height" constant="305"/>
<constraint id="252" firstItem="248" firstAttribute="width" constant="305"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="AVCamBarcode" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="249" translatesAutoresizingMaskIntoConstraints="NO" misplaced="YES">
<rect key="frame" x="78" y="566" width="257" height="43"/>
<fontDescription key="fontDescription" type="system" pointSize="36"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint id="250" firstItem="249" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX"/>
<constraint id="253" firstItem="248" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" constant="1"/>
<constraint id="256" firstItem="249" firstAttribute="top" secondItem="248" secondAttribute="bottom" constant="35"/>
<constraint id="744" firstItem="Ze5-6b-2t3" firstAttribute="centerY" secondItem="248" secondAttribute="centerY" constant="27"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="xamagon.png" width="609" height="609"/>
</resources>
</document>

Просмотреть файл

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13527"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="CameraViewController" 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">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" heightSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" id="1811" translatesAutoresizingMaskIntoConstraints="NO" customClass="PreviewView" misplaced="YES">
<rect key="frame" x="0.0" y="0.0" width="414" height="307"/>
<subviews/>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Camera Unavailable" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="4808" translatesAutoresizingMaskIntoConstraints="NO" misplaced="YES" hidden="YES">
<rect key="frame" x="101" y="358" width="210" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="24"/>
<color key="textColor" colorSpace="calibratedRGB" red="1" green="1" blue="0" alpha="1"/>
<nil key="highlightedColor"/>
<color key="tintColor" colorSpace="calibratedRGB" red="1" green="1" blue="0" alpha="1"/>
</label>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="1" maxValue="1" id="5042" translatesAutoresizingMaskIntoConstraints="NO" misplaced="YES">
<rect key="frame" x="18" y="648" width="378" height="31"/>
<connections>
<action selector="ZoomCamera:" destination="BYZ-38-t0r" id="6376" eventType="valueChanged"/>
</connections>
</slider>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="5045" translatesAutoresizingMaskIntoConstraints="NO" misplaced="YES">
<rect key="frame" x="37" y="686" width="120" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="18"/>
<color key="tintColor" colorSpace="calibratedRGB" red="1" green="1" blue="0" alpha="1"/>
<color key="backgroundColor" colorSpace="calibratedRGB" red="0" green="0" blue="0" alpha="0.29803921568627451"/>
<state key="normal" title="Metadata">
<color key="titleColor" colorSpace="calibratedRGB" red="1" green="1" blue="0" alpha="1"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute keyPath="layer.cornerRadius" type="number">
<real key="value" value="4"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="SelectMetadataObjectTypes:" destination="BYZ-38-t0r" id="6942" eventType="touchUpInside"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="5046" translatesAutoresizingMaskIntoConstraints="NO" misplaced="YES">
<rect key="frame" x="156" y="686" width="105" height="30"/>
<color key="tintColor" colorSpace="calibratedRGB" red="1" green="1" blue="0" alpha="1"/>
<color key="backgroundColor" colorSpace="calibratedRGB" red="0" green="0" blue="0" alpha="0.29803921568627451"/>
<fontDescription key="fontDescription" type="system" pointSize="18"/>
<state key="normal" title="Presets">
<color key="titleColor" colorSpace="calibratedRGB" red="1" green="1" blue="0" alpha="1"/>
</state>
<connections>
<action selector="SelectSessionPreset:" destination="BYZ-38-t0r" id="7225" eventType="touchUpInside"/>
</connections>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute keyPath="layer.cornerRadius" type="number">
<real key="value" value="4"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="5047" translatesAutoresizingMaskIntoConstraints="NO" misplaced="YES">
<rect key="frame" x="288" y="686" width="77" height="30"/>
<color key="tintColor" colorSpace="calibratedRGB" red="1" green="1" blue="0" alpha="1"/>
<color key="backgroundColor" colorSpace="calibratedRGB" red="0" green="0" blue="0" alpha="0.29803921568627451"/>
<fontDescription key="fontDescription" type="system" pointSize="18"/>
<state key="normal" title="Camera">
<color key="titleColor" colorSpace="calibratedRGB" red="1" green="1" blue="0" alpha="1"/>
</state>
<connections>
<action selector="ChangeCamera:" destination="BYZ-38-t0r" id="6093" eventType="touchUpInside"/>
</connections>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute keyPath="layer.cornerRadius" type="number">
<real key="value" value="4"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</button>
</subviews>
<color key="backgroundColor" colorSpace="calibratedRGB" red="0" green="0" blue="0" alpha="1"/>
<constraints>
<constraint id="3413" firstItem="1811" firstAttribute="width" secondItem="8bC-Xf-vdC" secondAttribute="width"/>
<constraint id="3641" firstItem="1811" firstAttribute="height" secondItem="8bC-Xf-vdC" secondAttribute="height"/>
<constraint id="4563" firstItem="8bC-Xf-vdC" firstAttribute="trailing" secondItem="1811" secondAttribute="trailing"/>
<constraint id="4564" firstItem="8bC-Xf-vdC" firstAttribute="bottom" secondItem="1811" secondAttribute="bottom"/>
<constraint id="4809" firstItem="4808" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX"/>
<constraint id="4810" firstItem="4808" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY"/>
<constraint id="5043" firstItem="5042" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin"/>
<constraint id="5044" firstItem="8bC-Xf-vdC" firstAttribute="trailingMargin" secondItem="5042" secondAttribute="trailing"/>
<constraint id="5051" firstItem="wfy-db-euE" firstAttribute="top" secondItem="5046" secondAttribute="bottom" constant="20"/>
<constraint id="5053" firstItem="5046" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" constant="2"/>
<constraint id="5067" firstItem="5046" firstAttribute="top" secondItem="5042" secondAttribute="bottom" constant="8"/>
<constraint id="5079" firstItem="5046" firstAttribute="width" secondItem="1811" secondAttribute="width" multiplier="0.25"/>
<constraint id="5359" firstItem="5045" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" constant="31" relation="greaterThanOrEqual"/>
<constraint id="5373" firstItem="5045" firstAttribute="centerY" secondItem="5046" secondAttribute="centerY"/>
<constraint id="5374" firstItem="5045" firstAttribute="width" secondItem="5046" secondAttribute="width"/>
<constraint id="5375" firstItem="5045" firstAttribute="height" secondItem="5046" secondAttribute="height"/>
<constraint id="5376" firstItem="5046" firstAttribute="leading" secondItem="5045" secondAttribute="trailing" symbolic="YES" constant="8"/>
<constraint id="5428" firstItem="8bC-Xf-vdC" firstAttribute="trailing" secondItem="5047" secondAttribute="trailing" constant="31" relation="greaterThanOrEqual"/>
<constraint id="5447" firstItem="5047" firstAttribute="centerY" secondItem="5046" secondAttribute="centerY"/>
<constraint id="5448" firstItem="5047" firstAttribute="width" secondItem="5046" secondAttribute="width"/>
<constraint id="5449" firstItem="5047" firstAttribute="height" secondItem="5046" secondAttribute="height"/>
<constraint id="5810" firstItem="5047" firstAttribute="leading" secondItem="5046" secondAttribute="trailing" symbolic="YES" constant="8"/>
</constraints>
<color key="tintColor" colorSpace="calibratedRGB" red="1" green="1" blue="0" alpha="1"/>
</view>
<connections>
<outlet property="SessionPresetsButton" destination="5046" id="name-outlet-5046"/>
<outlet property="CameraButton" destination="5047" id="name-outlet-5047"/>
<outlet property="MetadataObjectTypesButton" destination="5045" id="name-outlet-5045"/>
<outlet property="ZoomSlider" destination="5042" id="name-outlet-5042"/>
<outlet property="CameraUnavailableLabel" destination="4808" id="name-outlet-4808"/>
<outlet property="PreviewView" destination="1811" id="name-outlet-1811"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="0.0" y="0.0"/>
</scene>
</scenes>
</document>

Просмотреть файл

@ -0,0 +1,112 @@
{
"images": [
{
"scale": "2x",
"size": "20x20",
"idiom": "iphone",
"filename": "icon-spotlight-40.png"
},
{
"scale": "3x",
"size": "20x20",
"idiom": "iphone"
},
{
"scale": "2x",
"size": "29x29",
"idiom": "iphone",
"filename": "icon-spotlight-29@2x.png"
},
{
"scale": "3x",
"size": "29x29",
"idiom": "iphone",
"filename": "icon-spotlight-29@3x.png"
},
{
"scale": "2x",
"size": "40x40",
"idiom": "iphone",
"filename": "icon-spotlight-40@2x.png"
},
{
"scale": "3x",
"size": "40x40",
"idiom": "iphone",
"filename": "icon-spotlight-40@3x.png"
},
{
"scale": "2x",
"size": "60x60",
"idiom": "iphone",
"filename": "icon-app-60@2x.png"
},
{
"scale": "3x",
"size": "60x60",
"idiom": "iphone",
"filename": "Icon-app-60@3x.png"
},
{
"scale": "1x",
"size": "20x20",
"idiom": "ipad"
},
{
"scale": "2x",
"size": "20x20",
"idiom": "ipad"
},
{
"scale": "1x",
"size": "29x29",
"idiom": "ipad",
"filename": "icon-spotlight-29.png"
},
{
"scale": "2x",
"size": "29x29",
"idiom": "ipad",
"filename": "icon-spotlight-29@2x.png"
},
{
"scale": "1x",
"size": "40x40",
"idiom": "ipad",
"filename": "icon-spotlight-40.png"
},
{
"scale": "2x",
"size": "40x40",
"idiom": "ipad",
"filename": "icon-spotlight-40@2x.png"
},
{
"scale": "1x",
"size": "76x76",
"idiom": "ipad",
"filename": "icon-app-76.png"
},
{
"scale": "2x",
"size": "76x76",
"idiom": "ipad",
"filename": "icon-app-76@2x.png"
},
{
"scale": "2x",
"size": "83.5x83.5",
"idiom": "ipad"
},
{
"scale": "1x",
"size": "1024x1024",
"idiom": "ios-marketing"
}
],
"properties": {},
"info": {
"version": 1,
"author": "xcode"
}
}

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.0 KiB

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше