Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
Material
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Dmitriy Stepanets
Material
Commits
b46db5a2
Unverified
Commit
b46db5a2
authored
Jun 26, 2017
by
Daniel Dahan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
development: removed Capture, Events, and Photos
parent
6fbfcb75
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
13 additions
and
3073 deletions
+13
-3073
Material.xcodeproj/project.pbxproj
+0
-66
Sources/iOS/Capture/Capture.swift
+0
-1272
Sources/iOS/Capture/CaptureController.swift
+0
-95
Sources/iOS/Capture/CapturePreview.swift
+0
-79
Sources/iOS/CollectionViewLayout.swift
+13
-5
Sources/iOS/Events/Events.swift
+0
-665
Sources/iOS/Events/EventsController.swift
+0
-82
Sources/iOS/Photos/PhotoLibrary.swift
+0
-727
Sources/iOS/Photos/PhotoLibraryController.swift
+0
-82
No files found.
Material.xcodeproj/project.pbxproj
View file @
b46db5a2
...
@@ -14,12 +14,7 @@
...
@@ -14,12 +14,7 @@
9617B07E1DFCA8CF00410F8F
/* Card.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB75D1CB40DC500C806FE
/* Card.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B07E1DFCA8CF00410F8F
/* Card.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB75D1CB40DC500C806FE
/* Card.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B07F1DFCA8CF00410F8F
/* ImageCard.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7621CB40DC500C806FE
/* ImageCard.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B07F1DFCA8CF00410F8F
/* ImageCard.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7621CB40DC500C806FE
/* ImageCard.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B0801DFCA8CF00410F8F
/* PresenterCard.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9631A7C01D95E3AC00CFB109
/* PresenterCard.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B0801DFCA8CF00410F8F
/* PresenterCard.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9631A7C01D95E3AC00CFB109
/* PresenterCard.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B0811DFCA8CF00410F8F
/* Capture.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96717B0D1DBE6AF600DA84DB
/* Capture.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B0821DFCA8CF00410F8F
/* CapturePreview.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96717B0F1DBE6AF600DA84DB
/* CapturePreview.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B0831DFCA8CF00410F8F
/* CaptureController.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96717B0E1DBE6AF600DA84DB
/* CaptureController.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B0861DFCA8CF00410F8F
/* HeightPreset.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9626CB9A1DAD3D1D003E2611
/* HeightPreset.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B0861DFCA8CF00410F8F
/* HeightPreset.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9626CB9A1DAD3D1D003E2611
/* HeightPreset.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B0881DFCA8CF00410F8F
/* PhotoLibrary.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96717B161DBE6B1800DA84DB
/* PhotoLibrary.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B0891DFCA8CF00410F8F
/* PhotoLibraryController.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96717B171DBE6B1800DA84DB
/* PhotoLibraryController.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B08A1DFCA8CF00410F8F
/* DisplayStyle.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9626CA961DAB53A8003E2611
/* DisplayStyle.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B08A1DFCA8CF00410F8F
/* DisplayStyle.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9626CA961DAB53A8003E2611
/* DisplayStyle.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B08B1DFCA8CF00410F8F
/* Screen.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
961E6BE11DDA2AF3004E6C93
/* Screen.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B08B1DFCA8CF00410F8F
/* Screen.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
961E6BE11DDA2AF3004E6C93
/* Screen.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B08C1DFCA8CF00410F8F
/* SearchBar.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7951CB40DC500C806FE
/* SearchBar.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9617B08C1DFCA8CF00410F8F
/* SearchBar.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7951CB40DC500C806FE
/* SearchBar.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
...
@@ -37,10 +32,6 @@
...
@@ -37,10 +32,6 @@
96328B9E1E05C24E009A4C90
/* TableView.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96328B961E05C0BB009A4C90
/* TableView.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
96328B9E1E05C24E009A4C90
/* TableView.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96328B961E05C0BB009A4C90
/* TableView.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
96328B9F1E05C24E009A4C90
/* TableViewController.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96328B981E05C0CE009A4C90
/* TableViewController.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
96328B9F1E05C24E009A4C90
/* TableViewController.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96328B981E05C0CE009A4C90
/* TableViewController.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
96334EF61C8B84660083986B
/* Assets.xcassets in Resources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96334EF51C8B84660083986B
/* Assets.xcassets */
;
};
96334EF61C8B84660083986B
/* Assets.xcassets in Resources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96334EF51C8B84660083986B
/* Assets.xcassets */
;
};
9639526C1EC3882F004BA9DE
/* Events.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9639526A1EC3882F004BA9DE
/* Events.swift */
;
};
9639526D1EC3882F004BA9DE
/* EventsController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9639526B1EC3882F004BA9DE
/* EventsController.swift */
;
};
964335B71EC9432400FA9954
/* Events.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9639526A1EC3882F004BA9DE
/* Events.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
964335B81EC9432400FA9954
/* EventsController.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9639526B1EC3882F004BA9DE
/* EventsController.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
964335BA1EC9432400FA9954
/* TabsController.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9606CFAB1E957AC3006B4E74
/* TabsController.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
964335BA1EC9432400FA9954
/* TabsController.swift in Headers */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9606CFAB1E957AC3006B4E74
/* TabsController.swift */
;
settings
=
{
ATTRIBUTES
=
(
Public
,
);
};
};
9656895F1F002F16001C656D
/* CardCollectionViewCell.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9656895E1F002F16001C656D
/* CardCollectionViewCell.swift */
;
};
9656895F1F002F16001C656D
/* CardCollectionViewCell.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9656895E1F002F16001C656D
/* CardCollectionViewCell.swift */
;
};
965689611F002F4C001C656D
/* CardCollectionViewController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
965689601F002F4C001C656D
/* CardCollectionViewController.swift */
;
};
965689611F002F4C001C656D
/* CardCollectionViewController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
965689601F002F4C001C656D
/* CardCollectionViewController.swift */
;
};
...
@@ -85,9 +76,6 @@
...
@@ -85,9 +76,6 @@
965E80FD1DD4D59500D61E4B
/* Toolbar.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB79F1CB40DC500C806FE
/* Toolbar.swift */
;
};
965E80FD1DD4D59500D61E4B
/* Toolbar.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB79F1CB40DC500C806FE
/* Toolbar.swift */
;
};
965E80FE1DD4D59500D61E4B
/* ToolbarController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7A01CB40DC500C806FE
/* ToolbarController.swift */
;
};
965E80FE1DD4D59500D61E4B
/* ToolbarController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7A01CB40DC500C806FE
/* ToolbarController.swift */
;
};
965E80FF1DD4D5C800D61E4B
/* BottomNavigationController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7581CB40DC500C806FE
/* BottomNavigationController.swift */
;
};
965E80FF1DD4D5C800D61E4B
/* BottomNavigationController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7581CB40DC500C806FE
/* BottomNavigationController.swift */
;
};
965E81001DD4D5C800D61E4B
/* Capture.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96717B0D1DBE6AF600DA84DB
/* Capture.swift */
;
};
965E81011DD4D5C800D61E4B
/* CapturePreview.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96717B0F1DBE6AF600DA84DB
/* CapturePreview.swift */
;
};
965E81021DD4D5C800D61E4B
/* CaptureController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96717B0E1DBE6AF600DA84DB
/* CaptureController.swift */
;
};
965E81031DD4D5C800D61E4B
/* CollectionView.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7711CB40DC500C806FE
/* CollectionView.swift */
;
};
965E81031DD4D5C800D61E4B
/* CollectionView.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7711CB40DC500C806FE
/* CollectionView.swift */
;
};
965E81041DD4D5C800D61E4B
/* CollectionViewCell.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7721CB40DC500C806FE
/* CollectionViewCell.swift */
;
};
965E81041DD4D5C800D61E4B
/* CollectionViewCell.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7721CB40DC500C806FE
/* CollectionViewCell.swift */
;
};
965E81071DD4D5C800D61E4B
/* CollectionViewLayout.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7751CB40DC500C806FE
/* CollectionViewLayout.swift */
;
};
965E81071DD4D5C800D61E4B
/* CollectionViewLayout.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7751CB40DC500C806FE
/* CollectionViewLayout.swift */
;
};
...
@@ -100,8 +88,6 @@
...
@@ -100,8 +88,6 @@
965E81111DD4D5C800D61E4B
/* NavigationController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7911CB40DC500C806FE
/* NavigationController.swift */
;
};
965E81111DD4D5C800D61E4B
/* NavigationController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7911CB40DC500C806FE
/* NavigationController.swift */
;
};
965E81121DD4D5C800D61E4B
/* NavigationItem.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7921CB40DC500C806FE
/* NavigationItem.swift */
;
};
965E81121DD4D5C800D61E4B
/* NavigationItem.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7921CB40DC500C806FE
/* NavigationItem.swift */
;
};
965E81131DD4D5C800D61E4B
/* NavigationDrawerController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7971CB40DC500C806FE
/* NavigationDrawerController.swift */
;
};
965E81131DD4D5C800D61E4B
/* NavigationDrawerController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7971CB40DC500C806FE
/* NavigationDrawerController.swift */
;
};
965E81141DD4D5C800D61E4B
/* PhotoLibrary.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96717B161DBE6B1800DA84DB
/* PhotoLibrary.swift */
;
};
965E81151DD4D5C800D61E4B
/* PhotoLibraryController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96717B171DBE6B1800DA84DB
/* PhotoLibraryController.swift */
;
};
965E81161DD4D5C800D61E4B
/* DisplayStyle.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9626CA961DAB53A8003E2611
/* DisplayStyle.swift */
;
};
965E81161DD4D5C800D61E4B
/* DisplayStyle.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
9626CA961DAB53A8003E2611
/* DisplayStyle.swift */
;
};
965E81171DD4D5C800D61E4B
/* RootController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7991CB40DC500C806FE
/* RootController.swift */
;
};
965E81171DD4D5C800D61E4B
/* RootController.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
96BCB7991CB40DC500C806FE
/* RootController.swift */
;
};
965E81181DD4D5C800D61E4B
/* Snackbar.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
963FBEFC1D669510008F8512
/* Snackbar.swift */
;
};
965E81181DD4D5C800D61E4B
/* Snackbar.swift in Sources */
=
{
isa
=
PBXBuildFile
;
fileRef
=
963FBEFC1D669510008F8512
/* Snackbar.swift */
;
};
...
@@ -220,8 +206,6 @@
...
@@ -220,8 +206,6 @@
96328B981E05C0CE009A4C90
/* TableViewController.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
TableViewController.swift
;
sourceTree
=
"<group>"
;
};
96328B981E05C0CE009A4C90
/* TableViewController.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
TableViewController.swift
;
sourceTree
=
"<group>"
;
};
96334EF51C8B84660083986B
/* Assets.xcassets */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
folder.assetcatalog
;
path
=
Assets.xcassets
;
sourceTree
=
"<group>"
;
};
96334EF51C8B84660083986B
/* Assets.xcassets */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
folder.assetcatalog
;
path
=
Assets.xcassets
;
sourceTree
=
"<group>"
;
};
963832361B88DFD80015F710
/* Material.framework */
=
{
isa
=
PBXFileReference
;
explicitFileType
=
wrapper.framework
;
includeInIndex
=
0
;
path
=
Material.framework
;
sourceTree
=
BUILT_PRODUCTS_DIR
;
};
963832361B88DFD80015F710
/* Material.framework */
=
{
isa
=
PBXFileReference
;
explicitFileType
=
wrapper.framework
;
includeInIndex
=
0
;
path
=
Material.framework
;
sourceTree
=
BUILT_PRODUCTS_DIR
;
};
9639526A1EC3882F004BA9DE
/* Events.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
Events.swift
;
sourceTree
=
"<group>"
;
};
9639526B1EC3882F004BA9DE
/* EventsController.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
EventsController.swift
;
sourceTree
=
"<group>"
;
};
963FBEFC1D669510008F8512
/* Snackbar.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
Snackbar.swift
;
sourceTree
=
"<group>"
;
};
963FBEFC1D669510008F8512
/* Snackbar.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
Snackbar.swift
;
sourceTree
=
"<group>"
;
};
965532281E47E388005C2792
/* SpringAnimation.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
SpringAnimation.swift
;
sourceTree
=
"<group>"
;
};
965532281E47E388005C2792
/* SpringAnimation.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
SpringAnimation.swift
;
sourceTree
=
"<group>"
;
};
9656895E1F002F16001C656D
/* CardCollectionViewCell.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
CardCollectionViewCell.swift
;
sourceTree
=
"<group>"
;
};
9656895E1F002F16001C656D
/* CardCollectionViewCell.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
CardCollectionViewCell.swift
;
sourceTree
=
"<group>"
;
};
...
@@ -229,11 +213,6 @@
...
@@ -229,11 +213,6 @@
9658F2161CD6FA4700B902C1
/* IconButton.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
IconButton.swift
;
sourceTree
=
"<group>"
;
};
9658F2161CD6FA4700B902C1
/* IconButton.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
IconButton.swift
;
sourceTree
=
"<group>"
;
};
966A7F191EEC5D5000A2DAAC
/* Motion.xcodeproj */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
"wrapper.pb-project"
;
name
=
Motion.xcodeproj
;
path
=
Frameworks/Motion/Motion.xcodeproj
;
sourceTree
=
"<group>"
;
};
966A7F191EEC5D5000A2DAAC
/* Motion.xcodeproj */
=
{
isa
=
PBXFileReference
;
lastKnownFileType
=
"wrapper.pb-project"
;
name
=
Motion.xcodeproj
;
path
=
Frameworks/Motion/Motion.xcodeproj
;
sourceTree
=
"<group>"
;
};
966ECF291CF4C20100BB0BDF
/* CollectionReusableView.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
CollectionReusableView.swift
;
sourceTree
=
"<group>"
;
};
966ECF291CF4C20100BB0BDF
/* CollectionReusableView.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
CollectionReusableView.swift
;
sourceTree
=
"<group>"
;
};
96717B0D1DBE6AF600DA84DB
/* Capture.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
Capture.swift
;
sourceTree
=
"<group>"
;
};
96717B0E1DBE6AF600DA84DB
/* CaptureController.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
CaptureController.swift
;
sourceTree
=
"<group>"
;
};
96717B0F1DBE6AF600DA84DB
/* CapturePreview.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
CapturePreview.swift
;
sourceTree
=
"<group>"
;
};
96717B161DBE6B1800DA84DB
/* PhotoLibrary.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
PhotoLibrary.swift
;
sourceTree
=
"<group>"
;
};
96717B171DBE6B1800DA84DB
/* PhotoLibraryController.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
PhotoLibraryController.swift
;
sourceTree
=
"<group>"
;
};
967A48181D0F425A00B8CEB7
/* StatusBarController.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
StatusBarController.swift
;
sourceTree
=
"<group>"
;
};
967A48181D0F425A00B8CEB7
/* StatusBarController.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
StatusBarController.swift
;
sourceTree
=
"<group>"
;
};
968C99461D377849000074FF
/* Offset.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
Offset.swift
;
sourceTree
=
"<group>"
;
};
968C99461D377849000074FF
/* Offset.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
Offset.swift
;
sourceTree
=
"<group>"
;
};
96A183621E0C6CE200083C30
/* FABMenu.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
FABMenu.swift
;
sourceTree
=
"<group>"
;
};
96A183621E0C6CE200083C30
/* FABMenu.swift */
=
{
isa
=
PBXFileReference
;
fileEncoding
=
4
;
lastKnownFileType
=
sourcecode.swift
;
path
=
FABMenu.swift
;
sourceTree
=
"<group>"
;
};
...
@@ -411,15 +390,6 @@
...
@@ -411,15 +390,6 @@
name
=
Products
;
name
=
Products
;
sourceTree
=
"<group>"
;
sourceTree
=
"<group>"
;
};
};
963952691EC3882F004BA9DE
/* Events */
=
{
isa
=
PBXGroup
;
children
=
(
9639526A1EC3882F004BA9DE
/* Events.swift */
,
9639526B1EC3882F004BA9DE
/* EventsController.swift */
,
);
path
=
Events
;
sourceTree
=
"<group>"
;
};
963FBEFB1D6694E8008F8512
/* Snackbar */
=
{
963FBEFB1D6694E8008F8512
/* Snackbar */
=
{
isa
=
PBXGroup
;
isa
=
PBXGroup
;
children
=
(
children
=
(
...
@@ -508,25 +478,6 @@
...
@@ -508,25 +478,6 @@
name
=
Table
;
name
=
Table
;
sourceTree
=
"<group>"
;
sourceTree
=
"<group>"
;
};
};
96717B0C1DBE6AF600DA84DB
/* Capture */
=
{
isa
=
PBXGroup
;
children
=
(
96717B0D1DBE6AF600DA84DB
/* Capture.swift */
,
96717B0F1DBE6AF600DA84DB
/* CapturePreview.swift */
,
96717B0E1DBE6AF600DA84DB
/* CaptureController.swift */
,
);
path
=
Capture
;
sourceTree
=
"<group>"
;
};
96717B151DBE6B1800DA84DB
/* Photos */
=
{
isa
=
PBXGroup
;
children
=
(
96717B161DBE6B1800DA84DB
/* PhotoLibrary.swift */
,
96717B171DBE6B1800DA84DB
/* PhotoLibraryController.swift */
,
);
path
=
Photos
;
sourceTree
=
"<group>"
;
};
967A48171D0F424B00B8CEB7
/* StatusBar */
=
{
967A48171D0F424B00B8CEB7
/* StatusBar */
=
{
isa
=
PBXGroup
;
isa
=
PBXGroup
;
children
=
(
children
=
(
...
@@ -552,13 +503,11 @@
...
@@ -552,13 +503,11 @@
962DDD081D6FBBD0001C307C
/* BottomTabBar */
,
962DDD081D6FBBD0001C307C
/* BottomTabBar */
,
96BCB8031CB40F4B00C806FE
/* Button */
,
96BCB8031CB40F4B00C806FE
/* Button */
,
96BCB8021CB40F3B00C806FE
/* Card */
,
96BCB8021CB40F3B00C806FE
/* Card */
,
96717B0C1DBE6AF600DA84DB
/* Capture */
,
96BCB8051CB40F9C00C806FE
/* Collection */
,
96BCB8051CB40F9C00C806FE
/* Collection */
,
96BCB8001CB40F0300C806FE
/* Color */
,
96BCB8001CB40F0300C806FE
/* Color */
,
96328B9A1E05C135009A4C90
/* Data */
,
96328B9A1E05C135009A4C90
/* Data */
,
96BCB80B1CB410CC00C806FE
/* Device */
,
96BCB80B1CB410CC00C806FE
/* Device */
,
96230AB61D6A51FD00AF47DC
/* Divider */
,
96230AB61D6A51FD00AF47DC
/* Divider */
,
963952691EC3882F004BA9DE
/* Events */
,
96BCB80A1CB410A100C806FE
/* Extension */
,
96BCB80A1CB410A100C806FE
/* Extension */
,
963FBF021D6696D0008F8512
/* FABMenu */
,
963FBF021D6696D0008F8512
/* FABMenu */
,
96BCB8071CB4101C00C806FE
/* Font */
,
96BCB8071CB4101C00C806FE
/* Font */
,
...
@@ -570,7 +519,6 @@
...
@@ -570,7 +519,6 @@
96BCB8011CB40F1700C806FE
/* Navigation */
,
96BCB8011CB40F1700C806FE
/* Navigation */
,
961E6BEF1DDA4B04004E6C93
/* NavigationDrawer */
,
961E6BEF1DDA4B04004E6C93
/* NavigationDrawer */
,
96DE566D1EF1B6E3006DA70E
/* Pan */
,
96DE566D1EF1B6E3006DA70E
/* Pan */
,
96717B151DBE6B1800DA84DB
/* Photos */
,
9626CA951DAB5370003E2611
/* Root */
,
9626CA951DAB5370003E2611
/* Root */
,
961E6BE01DDA2ADD004E6C93
/* Screen */
,
961E6BE01DDA2ADD004E6C93
/* Screen */
,
963FBF031D6696EF008F8512
/* SearchBar */
,
963FBF031D6696EF008F8512
/* SearchBar */
,
...
@@ -850,12 +798,7 @@
...
@@ -850,12 +798,7 @@
9617B07E1DFCA8CF00410F8F
/* Card.swift in Headers */
,
9617B07E1DFCA8CF00410F8F
/* Card.swift in Headers */
,
9617B07F1DFCA8CF00410F8F
/* ImageCard.swift in Headers */
,
9617B07F1DFCA8CF00410F8F
/* ImageCard.swift in Headers */
,
9617B0801DFCA8CF00410F8F
/* PresenterCard.swift in Headers */
,
9617B0801DFCA8CF00410F8F
/* PresenterCard.swift in Headers */
,
9617B0811DFCA8CF00410F8F
/* Capture.swift in Headers */
,
9617B0821DFCA8CF00410F8F
/* CapturePreview.swift in Headers */
,
9617B0831DFCA8CF00410F8F
/* CaptureController.swift in Headers */
,
9617B0861DFCA8CF00410F8F
/* HeightPreset.swift in Headers */
,
9617B0861DFCA8CF00410F8F
/* HeightPreset.swift in Headers */
,
9617B0881DFCA8CF00410F8F
/* PhotoLibrary.swift in Headers */
,
9617B0891DFCA8CF00410F8F
/* PhotoLibraryController.swift in Headers */
,
9617B08A1DFCA8CF00410F8F
/* DisplayStyle.swift in Headers */
,
9617B08A1DFCA8CF00410F8F
/* DisplayStyle.swift in Headers */
,
9617B08B1DFCA8CF00410F8F
/* Screen.swift in Headers */
,
9617B08B1DFCA8CF00410F8F
/* Screen.swift in Headers */
,
9617B08C1DFCA8CF00410F8F
/* SearchBar.swift in Headers */
,
9617B08C1DFCA8CF00410F8F
/* SearchBar.swift in Headers */
,
...
@@ -870,8 +813,6 @@
...
@@ -870,8 +813,6 @@
961409B11E43D15C00E7BA99
/* FABMenu.swift in Headers */
,
961409B11E43D15C00E7BA99
/* FABMenu.swift in Headers */
,
961409B21E43D15C00E7BA99
/* FABMenuController.swift in Headers */
,
961409B21E43D15C00E7BA99
/* FABMenuController.swift in Headers */
,
96BFC16F1E63C10A0075DE1F
/* SpringAnimation.swift in Headers */
,
96BFC16F1E63C10A0075DE1F
/* SpringAnimation.swift in Headers */
,
964335B71EC9432400FA9954
/* Events.swift in Headers */
,
964335B81EC9432400FA9954
/* EventsController.swift in Headers */
,
964335BA1EC9432400FA9954
/* TabsController.swift in Headers */
,
964335BA1EC9432400FA9954
/* TabsController.swift in Headers */
,
);
);
runOnlyForDeploymentPostprocessing
=
0
;
runOnlyForDeploymentPostprocessing
=
0
;
...
@@ -975,9 +916,6 @@
...
@@ -975,9 +916,6 @@
961E6BE21DDA2AF3004E6C93
/* Screen.swift in Sources */
,
961E6BE21DDA2AF3004E6C93
/* Screen.swift in Sources */
,
965E81261DD4D7C800D61E4B
/* CharacterAttribute.swift in Sources */
,
965E81261DD4D7C800D61E4B
/* CharacterAttribute.swift in Sources */
,
965E80FF1DD4D5C800D61E4B
/* BottomNavigationController.swift in Sources */
,
965E80FF1DD4D5C800D61E4B
/* BottomNavigationController.swift in Sources */
,
965E81001DD4D5C800D61E4B
/* Capture.swift in Sources */
,
965E81011DD4D5C800D61E4B
/* CapturePreview.swift in Sources */
,
965E81021DD4D5C800D61E4B
/* CaptureController.swift in Sources */
,
965E81031DD4D5C800D61E4B
/* CollectionView.swift in Sources */
,
965E81031DD4D5C800D61E4B
/* CollectionView.swift in Sources */
,
965E81041DD4D5C800D61E4B
/* CollectionViewCell.swift in Sources */
,
965E81041DD4D5C800D61E4B
/* CollectionViewCell.swift in Sources */
,
965E81071DD4D5C800D61E4B
/* CollectionViewLayout.swift in Sources */
,
965E81071DD4D5C800D61E4B
/* CollectionViewLayout.swift in Sources */
,
...
@@ -991,8 +929,6 @@
...
@@ -991,8 +929,6 @@
965E81111DD4D5C800D61E4B
/* NavigationController.swift in Sources */
,
965E81111DD4D5C800D61E4B
/* NavigationController.swift in Sources */
,
965E81121DD4D5C800D61E4B
/* NavigationItem.swift in Sources */
,
965E81121DD4D5C800D61E4B
/* NavigationItem.swift in Sources */
,
965E81131DD4D5C800D61E4B
/* NavigationDrawerController.swift in Sources */
,
965E81131DD4D5C800D61E4B
/* NavigationDrawerController.swift in Sources */
,
965E81141DD4D5C800D61E4B
/* PhotoLibrary.swift in Sources */
,
965E81151DD4D5C800D61E4B
/* PhotoLibraryController.swift in Sources */
,
9656895F1F002F16001C656D
/* CardCollectionViewCell.swift in Sources */
,
9656895F1F002F16001C656D
/* CardCollectionViewCell.swift in Sources */
,
965E81161DD4D5C800D61E4B
/* DisplayStyle.swift in Sources */
,
965E81161DD4D5C800D61E4B
/* DisplayStyle.swift in Sources */
,
965E81171DD4D5C800D61E4B
/* RootController.swift in Sources */
,
965E81171DD4D5C800D61E4B
/* RootController.swift in Sources */
,
...
@@ -1001,11 +937,9 @@
...
@@ -1001,11 +937,9 @@
965E81191DD4D5C800D61E4B
/* SnackbarController.swift in Sources */
,
965E81191DD4D5C800D61E4B
/* SnackbarController.swift in Sources */
,
965E811A1DD4D5C800D61E4B
/* StatusBarController.swift in Sources */
,
965E811A1DD4D5C800D61E4B
/* StatusBarController.swift in Sources */
,
965E811B1DD4D5C800D61E4B
/* Switch.swift in Sources */
,
965E811B1DD4D5C800D61E4B
/* Switch.swift in Sources */
,
9639526C1EC3882F004BA9DE
/* Events.swift in Sources */
,
965E811C1DD4D5C800D61E4B
/* TabBar.swift in Sources */
,
965E811C1DD4D5C800D61E4B
/* TabBar.swift in Sources */
,
965E811D1DD4D5C800D61E4B
/* TableViewCell.swift in Sources */
,
965E811D1DD4D5C800D61E4B
/* TableViewCell.swift in Sources */
,
965E811E1DD4D5C800D61E4B
/* TextField.swift in Sources */
,
965E811E1DD4D5C800D61E4B
/* TextField.swift in Sources */
,
9639526D1EC3882F004BA9DE
/* EventsController.swift in Sources */
,
965E811F1DD4D5C800D61E4B
/* ErrorTextField.swift in Sources */
,
965E811F1DD4D5C800D61E4B
/* ErrorTextField.swift in Sources */
,
965E81211DD4D5C800D61E4B
/* TextStorage.swift in Sources */
,
965E81211DD4D5C800D61E4B
/* TextStorage.swift in Sources */
,
965E81221DD4D5C800D61E4B
/* TextView.swift in Sources */
,
965E81221DD4D5C800D61E4B
/* TextView.swift in Sources */
,
...
...
Sources/iOS/Capture/Capture.swift
deleted
100644 → 0
View file @
6fbfcb75
/*
* Copyright (C) 2015 - 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import
UIKit
import
AVFoundation
@objc(CaptureMode)
public
enum
CaptureMode
:
Int
{
case
photo
case
video
}
fileprivate
var
CaptureAdjustingExposureContext
:
UInt8
=
0
@objc(CapturePreset)
public
enum
CapturePreset
:
Int
{
case
presetPhoto
case
presetHigh
case
presetMedium
case
presetLow
case
preset352x288
case
preset640x480
case
preset1280x720
case
preset1920x1080
case
preset3840x2160
case
presetiFrame960x540
case
presetiFrame1280x720
case
presetInputPriority
}
/**
Converts a given CaptureSessionPreset to a String value.
- Parameter preset: A CaptureSessionPreset to convert.
*/
public
func
CapturePresetToString
(
preset
:
CapturePreset
)
->
String
{
switch
preset
{
case
.
presetPhoto
:
return
AVCaptureSessionPresetPhoto
case
.
presetHigh
:
return
AVCaptureSessionPresetHigh
case
.
presetMedium
:
return
AVCaptureSessionPresetMedium
case
.
presetLow
:
return
AVCaptureSessionPresetLow
case
.
preset352x288
:
return
AVCaptureSessionPreset352x288
case
.
preset640x480
:
return
AVCaptureSessionPreset640x480
case
.
preset1280x720
:
return
AVCaptureSessionPreset1280x720
case
.
preset1920x1080
:
return
AVCaptureSessionPreset1920x1080
case
.
preset3840x2160
:
if
#available(iOS 9.0, *)
{
return
AVCaptureSessionPreset3840x2160
}
else
{
return
AVCaptureSessionPresetHigh
}
case
.
presetiFrame960x540
:
return
AVCaptureSessionPresetiFrame960x540
case
.
presetiFrame1280x720
:
return
AVCaptureSessionPresetiFrame1280x720
case
.
presetInputPriority
:
return
AVCaptureSessionPresetInputPriority
}
}
@objc(CaptureDelegate)
public
protocol
CaptureDelegate
{
/**
A delegation method that is executed when the captureSesstion failes with an error.
- Parameter capture: A reference to the calling capture.
- Parameter error: A Error corresponding to the error.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
failedWith
error
:
Error
)
/**
A delegation method that is executed when the record timer has started.
- Parameter capture: A reference to the calling capture.
- Parameter didStartRecord timer: A Timer.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
didStartRecord
timer
:
Timer
)
/**
A delegation method that is executed when the record timer was updated.
- Parameter capture: A reference to the calling capture.
- Parameter didUpdateRecord timer: A Timer.
- Parameter hours: An integer representing hours.
- Parameter minutes: An integer representing minutes.
- Parameter seconds: An integer representing seconds.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
didUpdateRecord
timer
:
Timer
,
hours
:
Int
,
minutes
:
Int
,
seconds
:
Int
)
/**
A delegation method that is executed when the record timer has stopped.
- Parameter capture: A reference to the calling capture.
- Parameter didStopRecord timer: A Timer.
- Parameter hours: An integer representing hours.
- Parameter minutes: An integer representing minutes.
- Parameter seconds: An integer representing seconds.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
didStopRecord
time
:
Timer
,
hours
:
Int
,
minutes
:
Int
,
seconds
:
Int
)
/**
A delegation method that is executed when the user tapped to adjust the focus.
- Parameter capture: A reference to the calling capture.
- Parameter didTapToFocusAt point: CGPoint that the user tapped at.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
didTapToFocusAt
point
:
CGPoint
)
/**
A delegation method that is executed when the user tapped to adjust the exposure.
- Parameter capture: A reference to the calling capture.
- Parameter didTapToExposeAt point: CGPoint that the user tapped at.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
didTapToExposeAt
point
:
CGPoint
)
/**
A delegation method that is executed when the user tapped to reset.
- Parameter capture: A reference to the calling capture.
- Parameter didTapToResetAt point: CGPoint that the user tapped at.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
didTapToResetAt
point
:
CGPoint
)
/**
A delegation method that is executed when the user pressed the change mode button.
- Parameter capture: A reference to the calling capture.
- Parameter didPressChangeMode button: A reference to the UIButton that the user pressed.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
didPressChangeMode
button
:
UIButton
)
/**
A delegation method that is executed when the user pressed the change camera button.
- Parameter capture: A reference to the calling capture.
- Parameter didPressChangeCamera button: A reference to the UIButton that the user pressed.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
didPressChangeCamera
button
:
UIButton
)
/**
A delegation method that is executed when the user pressed capture button.
- Parameter capture: A reference to the calling capture.
- Parameter didPressCapture button: A reference to the UIButton that the user pressed.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
didPressCapture
button
:
UIButton
)
/**
A delegation method that is fired when the user pressed the flash button.
- Parameter capture: A reference to the calling capture.
- Parameter didPressFlash button: A reference to the UIButton that the user pressed.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
didPressFlash
button
:
UIButton
)
/**
A delegation method that is executed before the camera has been changed to another mode.
- Parameter capture: A reference to the calling capture.
- Parameter mode: A CaptureMode.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
willChange
mode
:
CaptureMode
)
/**
A delegation method that is executed after the camera has been changed to another mode.
- Parameter capture: A reference to the calling capture.
- Parameter mode: A CaptureMode.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
didChange
mode
:
CaptureMode
)
/**
A delegation method that is executed before the camera has been changed to another.
- Parameter capture: A reference to the calling capture.
- Parameter willChangeCamera devicePosition: An AVCaptureDevicePosition that the camera will change to.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
willChangeCamera
devicePosition
:
AVCaptureDevicePosition
)
/**
A delegation method that is executed when the camera has been changed to another.
- Parameter capture: A reference to the calling capture.
- Parameter didChangeCamera devicePosition: An AVCaptureDevicePosition that the camera has changed to.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
didChangeCamera
devicePosition
:
AVCaptureDevicePosition
)
/**
A delegation method that is executed when the device orientation changes.
- Parameter capture: A reference to the calling capture.
- Paremeter didChange videoOrientation: An AVCaptureVideoOrientation value.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
didChangeFrom
previousVideoOrientation
:
AVCaptureVideoOrientation
,
to
videoOrientation
:
AVCaptureVideoOrientation
)
/**
A delegation method that is executed when an image has been captured asynchronously.
- Parameter capture: A reference to the calling capture.
- Parameter asynchronouslyStill image: An image that has been captured.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
asynchronouslyStill
image
:
UIImage
)
/**
A delegation method that is executed when capturing an image asynchronously has failed.
- Parameter capture: A reference to the calling capture.
- Parameter asynchronouslyStillImageFailedWith error: A Error corresponding to the error.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
asynchronouslyStillImageFailedWith
error
:
Error
)
/**
A delegation method that is executed when creating a movie file has failed.
- Parameter capture: A reference to the calling capture.
- Parameter createMovieFileFailedWith error: A Error corresponding to the error.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
createMovieFileFailedWith
error
:
Error
)
/**
A delegation method that is executed when a session started recording and writing
to a file.
- Parameter capture: A reference to the calling capture.
- Parameter captureOutput: An AVCaptureFileOutput.
- Parameter didStartRecordingToOutputFileAt fileURL: A file URL.
- Parameter fromConnections: An array of Anys.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
captureOutput
:
AVCaptureFileOutput
,
didStartRecordingToOutputFileAt
fileURL
:
NSURL
,
fromConnections
connections
:
[
Any
])
/**
A delegation method that is executed when a session finished recording and writing
to a file.
- Parameter capture: A reference to the calling capture.
- Parameter captureOutput: An AVCaptureFileOutput.
- Parameter didFinishRecordingToOutputFileAt outputFileURL: A file URL.
- Parameter fromConnections: An array of Anys.
- Parameter error: A Error corresponding to an error.
*/
@objc
optional
func
capture
(
capture
:
Capture
,
captureOutput
:
AVCaptureFileOutput
,
didFinishRecordingToOutputFileAt
outputFileURL
:
NSURL
,
fromConnections
connections
:
[
Any
],
error
:
Error
!
)
}
open
class
Capture
:
View
{
/// A reference to the capture mode.
open
var
mode
=
CaptureMode
.
photo
/// Delegation handler.
open
weak
var
delegate
:
CaptureDelegate
?
/// A reference to the CapturePreview view.
open
let
preview
=
CapturePreview
()
/// A Timer reference for when recording is enabled.
open
fileprivate
(
set
)
var
timer
:
Timer
?
/// A tap gesture reference for focus events.
fileprivate
var
tapToFocusGesture
:
UITapGestureRecognizer
?
/// A tap gesture reference for exposure events.
fileprivate
var
tapToExposeGesture
:
UITapGestureRecognizer
?
/// A tap gesture reference for reset events.
fileprivate
var
tapToResetGesture
:
UITapGestureRecognizer
?
/// A reference to the session DispatchQueue.
fileprivate
var
sessionQueue
:
DispatchQueue
!
/// A reference to the active video input.
fileprivate
var
activeVideoInput
:
AVCaptureDeviceInput
?
/// A reference to the active audio input.
fileprivate
var
activeAudioInput
:
AVCaptureDeviceInput
?
/// A reference to the image output.
fileprivate
var
imageOutput
:
AVCaptureStillImageOutput
!
/// A reference to the movie output.
fileprivate
var
movieOutput
:
AVCaptureMovieFileOutput
!
/// A reference to the movie output URL.
fileprivate
var
movieOutputURL
:
URL
?
/// A reference to the AVCaptureSession.
fileprivate
var
session
:
AVCaptureSession
!
/// A boolean indicating if the session is running.
open
fileprivate
(
set
)
var
isRunning
=
false
/// A boolean indicating if the session is recording.
open
fileprivate
(
set
)
var
isRecording
=
false
/// A reference to the recorded time duration.
open
var
recordedDuration
:
CMTime
{
return
movieOutput
.
recordedDuration
}
/// An optional reference to the active camera if one exists.
open
var
activeCamera
:
AVCaptureDevice
?
{
return
activeVideoInput
?
.
device
}
/// An optional reference to the inactive camera if one exists.
open
var
inactiveCamera
:
AVCaptureDevice
?
{
var
device
:
AVCaptureDevice
?
if
1
<
cameraCount
{
if
activeCamera
?
.
position
==
.
back
{
device
=
camera
(
at
:
.
front
)
}
else
{
device
=
camera
(
at
:
.
back
)
}
}
return
device
}
/// Available number of cameras.
open
var
cameraCount
:
Int
{
return
AVCaptureDevice
.
devices
(
withMediaType
:
AVMediaTypeVideo
)
.
count
}
/// A boolean indicating whether the camera can change to another.
open
var
canChangeCamera
:
Bool
{
return
1
<
cameraCount
}
/// A booealn indicating whether the camrea supports focus.
open
var
isFocusPointOfInterestSupported
:
Bool
{
return
nil
==
activeCamera
?
false
:
activeCamera
!.
isFocusPointOfInterestSupported
}
/// A booealn indicating whether the camrea supports exposure.
open
var
isExposurePointOfInterestSupported
:
Bool
{
return
nil
==
activeCamera
?
false
:
activeCamera
!.
isExposurePointOfInterestSupported
}
/// A boolean indicating if the active camera has flash.
open
var
isFlashAvailable
:
Bool
{
return
nil
==
activeCamera
?
false
:
activeCamera
!.
hasFlash
}
/// A boolean indicating if the active camera has a torch.
open
var
isTorchAvailable
:
Bool
{
return
nil
==
activeCamera
?
false
:
activeCamera
!.
hasTorch
}
/// A reference to the active camera position if the active camera exists.
open
var
devicePosition
:
AVCaptureDevicePosition
?
{
return
activeCamera
?
.
position
}
/// A reference to the focusMode.
open
var
focusMode
:
AVCaptureFocusMode
{
get
{
return
activeCamera
!.
focusMode
}
set
(
value
)
{
var
error
:
NSError
?
if
isFocusModeSupported
(
focusMode
:
focusMode
)
{
do
{
let
device
=
activeCamera
!
try
device
.
lockForConfiguration
()
device
.
focusMode
=
value
device
.
unlockForConfiguration
()
}
catch
let
e
as
NSError
{
error
=
e
}
}
else
{
var
userInfo
:
Dictionary
<
String
,
Any
>
=
Dictionary
<
String
,
Any
>
()
userInfo
[
NSLocalizedDescriptionKey
]
=
"[Material Error: Unsupported focusMode.]"
userInfo
[
NSLocalizedFailureReasonErrorKey
]
=
"[Material Error: Unsupported focusMode.]"
error
=
NSError
(
domain
:
"com.cosmicmind.material.capture"
,
code
:
0001
,
userInfo
:
userInfo
)
userInfo
[
NSUnderlyingErrorKey
]
=
error
}
if
let
e
=
error
{
delegate
?
.
capture
?(
capture
:
self
,
failedWith
:
e
)
}
}
}
/// A reference to the flashMode.
open
var
flashMode
:
AVCaptureFlashMode
{
get
{
return
activeCamera
!.
flashMode
}
set
(
value
)
{
var
error
:
NSError
?
if
isFlashModeSupported
(
flashMode
:
flashMode
)
{
do
{
let
device
=
activeCamera
!
try
device
.
lockForConfiguration
()
device
.
flashMode
=
value
device
.
unlockForConfiguration
()
}
catch
let
e
as
NSError
{
error
=
e
}
}
else
{
var
userInfo
:
Dictionary
<
String
,
Any
>
=
Dictionary
<
String
,
Any
>
()
userInfo
[
NSLocalizedDescriptionKey
]
=
"[Material Error: Unsupported flashMode.]"
userInfo
[
NSLocalizedFailureReasonErrorKey
]
=
"[Material Error: Unsupported flashMode.]"
error
=
NSError
(
domain
:
"com.cosmicmind.material.capture"
,
code
:
0002
,
userInfo
:
userInfo
)
userInfo
[
NSUnderlyingErrorKey
]
=
error
}
if
let
e
=
error
{
delegate
?
.
capture
?(
capture
:
self
,
failedWith
:
e
)
}
}
}
/// A reference to the torchMode.
open
var
torchMode
:
AVCaptureTorchMode
{
get
{
return
activeCamera
!.
torchMode
}
set
(
value
)
{
var
error
:
NSError
?
if
isTorchModeSupported
(
torchMode
:
torchMode
)
{
do
{
let
device
:
AVCaptureDevice
=
activeCamera
!
try
device
.
lockForConfiguration
()
device
.
torchMode
=
value
device
.
unlockForConfiguration
()
}
catch
let
e
as
NSError
{
error
=
e
}
}
else
{
var
userInfo
:
Dictionary
<
String
,
Any
>
=
Dictionary
<
String
,
Any
>
()
userInfo
[
NSLocalizedDescriptionKey
]
=
"[Material Error: Unsupported torchMode.]"
userInfo
[
NSLocalizedFailureReasonErrorKey
]
=
"[Material Error: Unsupported torchMode.]"
error
=
NSError
(
domain
:
"com.cosmicmind.material.capture"
,
code
:
0003
,
userInfo
:
userInfo
)
userInfo
[
NSUnderlyingErrorKey
]
=
error
}
if
let
e
=
error
{
delegate
?
.
capture
?(
capture
:
self
,
failedWith
:
e
)
}
}
}
/// The session quality preset.
open
var
capturePreset
=
CapturePreset
.
presetPhoto
{
didSet
{
session
.
sessionPreset
=
CapturePresetToString
(
preset
:
capturePreset
)
}
}
/// A reference to the previous AVCaptureVideoOrientation.
open
fileprivate
(
set
)
var
previousVideoOrientation
:
AVCaptureVideoOrientation
!
/// The capture video orientation.
open
var
videoOrientation
:
AVCaptureVideoOrientation
{
var
orientation
:
AVCaptureVideoOrientation
switch
UIDevice
.
current
.
orientation
{
case
.
portrait
:
orientation
=
.
portrait
case
.
landscapeRight
:
orientation
=
.
landscapeLeft
case
.
portraitUpsideDown
:
orientation
=
.
portraitUpsideDown
default
:
orientation
=
.
landscapeRight
}
return
orientation
}
/// A reference to the captureButton.
@IBInspectable
open
var
captureButton
:
UIButton
?
{
didSet
{
prepareCaptureButton
()
}
}
/// A reference to the changeModeButton.
@IBInspectable
open
var
changeModeButton
:
UIButton
?
{
didSet
{
prepareChangeModeButton
()
}
}
/// A reference to the changeCameraButton.
@IBInspectable
open
var
changeCameraButton
:
UIButton
?
{
didSet
{
prepareChangeCameraButton
()
}
}
/// A reference to the flashButton.
@IBInspectable
open
var
flashButton
:
UIButton
?
{
didSet
{
prepareFlashButton
()
}
}
/// A boolean indicating whether to enable tap to focus.
@IBInspectable
open
var
isTapToFocusEnabled
=
false
{
didSet
{
guard
isTapToFocusEnabled
else
{
removeTapGesture
(
gesture
:
&
tapToFocusGesture
)
return
}
isTapToResetEnabled
=
true
prepareTapGesture
(
gesture
:
&
tapToFocusGesture
,
numberOfTapsRequired
:
1
,
numberOfTouchesRequired
:
1
,
selector
:
#selector(
handleTapToFocusGesture
)
)
if
let
v
=
tapToExposeGesture
{
tapToFocusGesture
!.
require
(
toFail
:
v
)
}
}
}
/// A boolean indicating whether to enable tap to expose.
@IBInspectable
open
var
isTapToExposeEnabled
=
false
{
didSet
{
guard
isTapToExposeEnabled
else
{
removeTapGesture
(
gesture
:
&
tapToExposeGesture
)
return
}
isTapToResetEnabled
=
true
prepareTapGesture
(
gesture
:
&
tapToExposeGesture
,
numberOfTapsRequired
:
2
,
numberOfTouchesRequired
:
1
,
selector
:
#selector(
handleTapToExposeGesture
)
)
if
let
v
=
tapToFocusGesture
{
v
.
require
(
toFail
:
tapToExposeGesture
!
)
}
}
}
/// A boolean indicating whether to enable tap to reset.
@IBInspectable
open
var
isTapToResetEnabled
=
false
{
didSet
{
guard
isTapToResetEnabled
else
{
removeTapGesture
(
gesture
:
&
tapToResetGesture
)
return
}
prepareTapGesture
(
gesture
:
&
tapToResetGesture
,
numberOfTapsRequired
:
2
,
numberOfTouchesRequired
:
2
,
selector
:
#selector(
handleTapToResetGesture
)
)
if
let
v
=
tapToFocusGesture
{
v
.
require
(
toFail
:
tapToResetGesture
!
)
}
if
let
v
=
tapToExposeGesture
{
v
.
require
(
toFail
:
tapToResetGesture
!
)
}
}
}
deinit
{
removeOrientationNotifications
()
}
/// A convenience initializer.
public
convenience
init
()
{
self
.
init
(
frame
:
.
zero
)
}
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open
override
func
prepare
()
{
super
.
prepare
()
backgroundColor
=
.
black
prepareSession
()
prepareSessionQueue
()
prepareActiveVideoInput
()
prepareActiveAudioInput
()
prepareImageOutput
()
prepareMovieOutput
()
preparePreview
()
prepareOrientationNotifications
()
previousVideoOrientation
=
videoOrientation
isTapToFocusEnabled
=
true
isTapToExposeEnabled
=
true
}
}
extension
Capture
{
/// Prepares self to observe orientation change notifications.
fileprivate
func
prepareOrientationNotifications
()
{
UIDevice
.
current
.
beginGeneratingDeviceOrientationNotifications
()
NotificationCenter
.
default
.
addObserver
(
self
,
selector
:
#selector(
handleOrientationNotifications(_:)
)
,
name
:
NSNotification
.
Name
.
UIDeviceOrientationDidChange
,
object
:
nil
)
}
/// Removes self from observing orientation change notifications.
fileprivate
func
removeOrientationNotifications
()
{
UIDevice
.
current
.
endGeneratingDeviceOrientationNotifications
()
NotificationCenter
.
default
.
removeObserver
(
self
)
}
/**
Handler for the captureButton.
- Parameter button: A UIButton that is associated with the event.
*/
@objc
fileprivate
func
handleOrientationNotifications
(
_
notification
:
Notification
)
{
delegate
?
.
capture
?(
capture
:
self
,
didChangeFrom
:
previousVideoOrientation
,
to
:
videoOrientation
)
previousVideoOrientation
=
videoOrientation
}
}
extension
Capture
{
/// Prepares the preview.
fileprivate
func
preparePreview
()
{
layout
(
preview
)
.
edges
()
(
preview
.
layer
as!
AVCaptureVideoPreviewLayer
)
.
session
=
session
startSession
()
}
/// Prepares the captureButton.
fileprivate
func
prepareCaptureButton
()
{
captureButton
?
.
addTarget
(
self
,
action
:
#selector(
handleCaptureButton(button:)
)
,
for
:
.
touchUpInside
)
}
/// Prepares the cameraButton.
fileprivate
func
prepareChangeModeButton
()
{
changeModeButton
?
.
addTarget
(
self
,
action
:
#selector(
handleChangeModeButton(button:)
)
,
for
:
.
touchUpInside
)
}
/// Prepares the changeCameraButton.
fileprivate
func
prepareChangeCameraButton
()
{
changeCameraButton
?
.
addTarget
(
self
,
action
:
#selector(
handleChangeCameraButton(button:)
)
,
for
:
.
touchUpInside
)
}
/// Prepares the flashButton.
fileprivate
func
prepareFlashButton
()
{
flashButton
?
.
addTarget
(
self
,
action
:
#selector(
handleFlashButton(button:)
)
,
for
:
.
touchUpInside
)
}
/// Prepares the sessionQueue.
fileprivate
func
prepareSessionQueue
()
{
sessionQueue
=
DispatchQueue
(
label
:
"com.cosmicmind.material.capture"
,
attributes
:
.
concurrent
,
target
:
nil
)
}
/// Prepares the session.
fileprivate
func
prepareSession
()
{
session
=
AVCaptureSession
()
}
/// Prepares the activeVideoInput.
fileprivate
func
prepareActiveVideoInput
()
{
do
{
activeVideoInput
=
try
AVCaptureDeviceInput
(
device
:
AVCaptureDevice
.
defaultDevice
(
withMediaType
:
AVMediaTypeVideo
))
guard
session
.
canAddInput
(
activeVideoInput
)
else
{
return
}
session
.
addInput
(
activeVideoInput
)
}
catch
let
e
as
NSError
{
delegate
?
.
capture
?(
capture
:
self
,
failedWith
:
e
)
}
}
/// Prepares the activeAudioInput.
fileprivate
func
prepareActiveAudioInput
()
{
do
{
activeAudioInput
=
try
AVCaptureDeviceInput
(
device
:
AVCaptureDevice
.
defaultDevice
(
withMediaType
:
AVMediaTypeAudio
))
guard
session
.
canAddInput
(
activeAudioInput
)
else
{
return
}
session
.
addInput
(
activeAudioInput
)
}
catch
let
e
as
NSError
{
delegate
?
.
capture
?(
capture
:
self
,
failedWith
:
e
)
}
}
/// Prepares the imageOutput.
fileprivate
func
prepareImageOutput
()
{
imageOutput
=
AVCaptureStillImageOutput
()
guard
session
.
canAddOutput
(
imageOutput
)
else
{
return
}
imageOutput
.
outputSettings
=
[
AVVideoCodecKey
:
AVVideoCodecJPEG
]
session
.
addOutput
(
imageOutput
)
}
/// Prepares the movieOutput.
fileprivate
func
prepareMovieOutput
()
{
movieOutput
=
AVCaptureMovieFileOutput
()
guard
session
.
canAddOutput
(
movieOutput
)
else
{
return
}
session
.
addOutput
(
movieOutput
)
}
}
extension
Capture
{
/// Starts the session.
open
func
startSession
()
{
guard
!
isRunning
else
{
return
}
sessionQueue
.
async
()
{
[
weak
self
]
in
self
?
.
session
.
startRunning
()
}
}
/// Stops the session.
open
func
stopSession
()
{
guard
isRunning
else
{
return
}
sessionQueue
.
async
()
{
[
weak
self
]
in
self
?
.
session
.
stopRunning
()
}
}
/// Changees the camera if possible.
open
func
changeCamera
()
{
guard
canChangeCamera
else
{
return
}
do
{
guard
let
v
=
devicePosition
else
{
return
}
delegate
?
.
capture
?(
capture
:
self
,
willChangeCamera
:
v
)
let
videoInput
=
try
AVCaptureDeviceInput
(
device
:
inactiveCamera
!
)
session
.
beginConfiguration
()
session
.
removeInput
(
activeVideoInput
)
if
session
.
canAddInput
(
videoInput
)
{
session
.
addInput
(
videoInput
)
activeVideoInput
=
videoInput
}
else
{
session
.
addInput
(
activeVideoInput
)
}
session
.
commitConfiguration
()
delegate
?
.
capture
?(
capture
:
self
,
didChangeCamera
:
v
)
}
catch
let
e
as
NSError
{
delegate
?
.
capture
?(
capture
:
self
,
failedWith
:
e
)
}
}
/// Changees the mode.
open
func
changeMode
()
{
delegate
?
.
capture
?(
capture
:
self
,
willChange
:
mode
)
mode
=
.
photo
==
mode
?
.
video
:
.
photo
delegate
?
.
capture
?(
capture
:
self
,
didChange
:
mode
)
}
/**
Checks if a given focus mode is supported.
- Parameter focusMode: An AVCaptureFocusMode.
- Returns: A boolean of the result, true if supported, false otherwise.
*/
open
func
isFocusModeSupported
(
focusMode
:
AVCaptureFocusMode
)
->
Bool
{
return
activeCamera
!.
isFocusModeSupported
(
focusMode
)
}
/**
Checks if a given exposure mode is supported.
- Parameter exposureMode: An AVCaptureExposureMode.
- Returns: A boolean of the result, true if supported, false otherwise.
*/
open
func
isExposureModeSupported
(
exposureMode
:
AVCaptureExposureMode
)
->
Bool
{
return
activeCamera
!.
isExposureModeSupported
(
exposureMode
)
}
/**
Checks if a given flash mode is supported.
- Parameter flashMode: An AVCaptureFlashMode.
- Returns: A boolean of the result, true if supported, false otherwise.
*/
open
func
isFlashModeSupported
(
flashMode
:
AVCaptureFlashMode
)
->
Bool
{
return
activeCamera
!.
isFlashModeSupported
(
flashMode
)
}
/**
Checks if a given torch mode is supported.
- Parameter torchMode: An AVCaptureTorchMode.
- Returns: A boolean of the result, true if supported, false otherwise.
*/
open
func
isTorchModeSupported
(
torchMode
:
AVCaptureTorchMode
)
->
Bool
{
return
activeCamera
!.
isTorchModeSupported
(
torchMode
)
}
/**
Focuses the camera at a given point.
- Parameter at: A CGPoint to focus at.
*/
open
func
focus
(
at
point
:
CGPoint
)
{
var
error
:
NSError
?
if
isFocusPointOfInterestSupported
&&
isFocusModeSupported
(
focusMode
:
.
autoFocus
)
{
do
{
let
device
=
activeCamera
!
try
device
.
lockForConfiguration
()
device
.
focusPointOfInterest
=
point
device
.
focusMode
=
.
autoFocus
device
.
unlockForConfiguration
()
}
catch
let
e
as
NSError
{
error
=
e
}
}
else
{
var
userInfo
=
[
String
:
Any
]()
userInfo
[
NSLocalizedDescriptionKey
]
=
"[Material Error: Unsupported focus.]"
userInfo
[
NSLocalizedFailureReasonErrorKey
]
=
"[Material Error: Unsupported focus.]"
error
=
NSError
(
domain
:
"com.cosmicmind.material.capture"
,
code
:
0004
,
userInfo
:
userInfo
)
userInfo
[
NSUnderlyingErrorKey
]
=
error
}
if
let
e
=
error
{
delegate
?
.
capture
?(
capture
:
self
,
failedWith
:
e
)
}
}
/**
Exposes the camera at a given point.
- Parameter at: A CGPoint to expose at.
*/
open
func
expose
(
at
point
:
CGPoint
)
{
var
error
:
NSError
?
if
isExposurePointOfInterestSupported
&&
isExposureModeSupported
(
exposureMode
:
.
continuousAutoExposure
)
{
do
{
let
device
=
activeCamera
!
try
device
.
lockForConfiguration
()
device
.
exposurePointOfInterest
=
point
device
.
exposureMode
=
.
continuousAutoExposure
if
device
.
isExposureModeSupported
(
.
locked
)
{
device
.
addObserver
(
self
,
forKeyPath
:
"adjustingExposure"
,
options
:
.
new
,
context
:
&
CaptureAdjustingExposureContext
)
}
device
.
unlockForConfiguration
()
}
catch
let
e
as
NSError
{
error
=
e
}
}
else
{
var
userInfo
=
[
String
:
Any
]()
userInfo
[
NSLocalizedDescriptionKey
]
=
"[Material Error: Unsupported expose.]"
userInfo
[
NSLocalizedFailureReasonErrorKey
]
=
"[Material Error: Unsupported expose.]"
error
=
NSError
(
domain
:
"com.cosmicmind.material.capture"
,
code
:
0005
,
userInfo
:
userInfo
)
userInfo
[
NSUnderlyingErrorKey
]
=
error
}
if
let
e
=
error
{
delegate
?
.
capture
?(
capture
:
self
,
failedWith
:
e
)
}
}
open
override
func
observeValue
(
forKeyPath
keyPath
:
String
?,
of
object
:
Any
?,
change
:
[
NSKeyValueChangeKey
:
Any
]?,
context
:
UnsafeMutableRawPointer
?)
{
if
context
==
&
CaptureAdjustingExposureContext
{
let
device
=
object
as!
AVCaptureDevice
if
!
device
.
isAdjustingExposure
&&
device
.
isExposureModeSupported
(
.
locked
)
{
(
object
!
as
AnyObject
)
.
removeObserver
(
self
,
forKeyPath
:
"adjustingExposure"
,
context
:
&
CaptureAdjustingExposureContext
)
DispatchQueue
.
main
.
async
{
[
weak
self
]
in
do
{
try
device
.
lockForConfiguration
()
device
.
exposureMode
=
.
locked
device
.
unlockForConfiguration
()
}
catch
let
e
as
NSError
{
guard
let
s
=
self
else
{
return
}
s
.
delegate
?
.
capture
?(
capture
:
s
,
failedWith
:
e
)
}
}
}
}
else
{
super
.
observeValue
(
forKeyPath
:
keyPath
,
of
:
object
,
change
:
change
,
context
:
context
)
}
}
/**
Resets the camera focus and exposure.
- Parameter focus: A boolean indicating to reset the focus.
- Parameter exposure: A boolean indicating to reset the exposure.
*/
open
func
reset
(
focus
:
Bool
=
true
,
exposure
:
Bool
=
true
)
{
let
device
=
activeCamera
!
let
canResetFocus
=
device
.
isFocusPointOfInterestSupported
&&
device
.
isFocusModeSupported
(
.
continuousAutoFocus
)
let
canResetExposure
=
device
.
isExposurePointOfInterestSupported
&&
device
.
isExposureModeSupported
(
.
continuousAutoExposure
)
let
centerPoint
=
CGPoint
(
x
:
0.5
,
y
:
0.5
)
do
{
try
device
.
lockForConfiguration
()
if
canResetFocus
&&
focus
{
device
.
focusMode
=
.
continuousAutoFocus
device
.
focusPointOfInterest
=
centerPoint
}
if
canResetExposure
&&
exposure
{
device
.
exposureMode
=
.
continuousAutoExposure
device
.
exposurePointOfInterest
=
centerPoint
}
device
.
unlockForConfiguration
()
}
catch
let
e
as
NSError
{
delegate
?
.
capture
?(
capture
:
self
,
failedWith
:
e
)
}
}
/// Captures a still image.
open
func
captureStillImage
()
{
sessionQueue
.
async
()
{
[
weak
self
]
in
guard
let
s
=
self
else
{
return
}
guard
let
v
=
s
.
imageOutput
.
connection
(
withMediaType
:
AVMediaTypeVideo
)
else
{
return
}
v
.
videoOrientation
=
s
.
videoOrientation
s
.
imageOutput
.
captureStillImageAsynchronously
(
from
:
v
)
{
[
weak
self
]
(
sampleBuffer
:
CMSampleBuffer
?,
error
:
Error
?)
->
Void
in
guard
let
s
=
self
else
{
return
}
var
captureError
=
error
if
nil
==
captureError
{
let
data
=
AVCaptureStillImageOutput
.
jpegStillImageNSDataRepresentation
(
sampleBuffer
)
!
if
let
image1
=
UIImage
(
data
:
data
)
{
if
let
image2
=
image1
.
adjustOrientation
()
{
s
.
delegate
?
.
capture
?(
capture
:
s
,
asynchronouslyStill
:
image2
)
}
else
{
var
userInfo
=
[
String
:
Any
]()
userInfo
[
NSLocalizedDescriptionKey
]
=
"[Material Error: Cannot fix image orientation.]"
userInfo
[
NSLocalizedFailureReasonErrorKey
]
=
"[Material Error: Cannot fix image orientation.]"
captureError
=
NSError
(
domain
:
"com.cosmicmind.material.capture"
,
code
:
0006
,
userInfo
:
userInfo
)
userInfo
[
NSUnderlyingErrorKey
]
=
error
}
}
else
{
var
userInfo
=
[
String
:
Any
]()
userInfo
[
NSLocalizedDescriptionKey
]
=
"[Material Error: Cannot capture image from data.]"
userInfo
[
NSLocalizedFailureReasonErrorKey
]
=
"[Material Error: Cannot capture image from data.]"
captureError
=
NSError
(
domain
:
"com.cosmicmind.material.capture"
,
code
:
0007
,
userInfo
:
userInfo
)
userInfo
[
NSUnderlyingErrorKey
]
=
error
}
}
if
let
e
=
captureError
{
s
.
delegate
?
.
capture
?(
capture
:
s
,
asynchronouslyStillImageFailedWith
:
e
)
}
}
}
}
/// Starts recording.
open
func
startRecording
()
{
if
!
isRecording
{
sessionQueue
.
async
()
{
[
weak
self
]
in
guard
let
s
=
self
else
{
return
}
if
let
v
=
s
.
movieOutput
.
connection
(
withMediaType
:
AVMediaTypeVideo
)
{
v
.
videoOrientation
=
s
.
videoOrientation
v
.
preferredVideoStabilizationMode
=
.
auto
}
guard
let
v
=
s
.
activeCamera
else
{
return
}
if
v
.
isSmoothAutoFocusSupported
{
do
{
try
v
.
lockForConfiguration
()
v
.
isSmoothAutoFocusEnabled
=
true
v
.
unlockForConfiguration
()
}
catch
let
e
as
NSError
{
s
.
delegate
?
.
capture
?(
capture
:
s
,
failedWith
:
e
)
}
}
s
.
movieOutputURL
=
s
.
uniqueURL
()
if
let
v
=
s
.
movieOutputURL
{
s
.
movieOutput
.
startRecording
(
toOutputFileURL
:
v
as
URL
!
,
recordingDelegate
:
s
)
}
}
}
}
/// Stops recording.
open
func
stopRecording
()
{
guard
isRecording
else
{
return
}
movieOutput
.
stopRecording
()
}
/**
A reference to the camera at a given position, if one exists.
- Parameter at: An AVCaptureDevicePosition.
- Returns: An AVCaptureDevice if one exists, or nil otherwise.
*/
fileprivate
func
camera
(
at
position
:
AVCaptureDevicePosition
)
->
AVCaptureDevice
?
{
let
devices
=
AVCaptureDevice
.
devices
(
withMediaType
:
AVMediaTypeVideo
)
as!
[
AVCaptureDevice
]
for
device
in
devices
{
if
device
.
position
==
position
{
return
device
}
}
return
nil
}
/**
Creates a unique URL if possible.
- Returns: A NSURL if it is possible to create one.
*/
fileprivate
func
uniqueURL
()
->
URL
?
{
do
{
let
directory
=
try
FileManager
.
default
.
url
(
for
:
.
documentDirectory
,
in
:
.
userDomainMask
,
appropriateFor
:
nil
,
create
:
true
)
let
dateFormatter
=
DateFormatter
()
dateFormatter
.
dateStyle
=
.
full
dateFormatter
.
timeStyle
=
.
full
return
directory
.
appendingPathComponent
(
dateFormatter
.
string
(
from
:
NSDate
()
as
Date
)
+
".mov"
)
}
catch
let
e
as
NSError
{
delegate
?
.
capture
?(
capture
:
self
,
createMovieFileFailedWith
:
e
)
}
return
nil
}
}
extension
Capture
{
/**
Handler for the captureButton.
- Parameter button: A UIButton that is associated with the event.
*/
@objc
fileprivate
func
handleCaptureButton
(
button
:
UIButton
)
{
switch
mode
{
case
.
photo
:
captureStillImage
()
case
.
video
:
if
isRecording
{
stopRecording
()
stopTimer
()
}
else
{
startRecording
()
startTimer
()
}
}
delegate
?
.
capture
?(
capture
:
self
,
didPressCapture
:
button
)
}
/**
Handler for the changeModeButton.
- Parameter button: A UIButton that is associated with the event.
*/
@objc
fileprivate
func
handleChangeModeButton
(
button
:
UIButton
)
{
changeMode
()
delegate
?
.
capture
?(
capture
:
self
,
didPressChangeMode
:
button
)
}
/**
Handler for the changeCameraButton.
- Parameter button: A UIButton that is associated with the event.
*/
@objc
fileprivate
func
handleChangeCameraButton
(
button
:
UIButton
)
{
DispatchQueue
.
main
.
async
{
[
weak
self
]
in
self
?
.
changeCamera
()
}
delegate
?
.
capture
?(
capture
:
self
,
didPressChangeCamera
:
button
)
}
/**
Handler for the flashButton.
- Parameter button: A UIButton that is associated with the event.
*/
@objc
fileprivate
func
handleFlashButton
(
button
:
UIButton
)
{
delegate
?
.
capture
?(
capture
:
self
,
didPressFlash
:
button
)
}
/**
Handler for the tapToFocusGesture.
- Parameter recognizer: A UITapGestureRecognizer that is associated with the event.
*/
@objc
fileprivate
func
handleTapToFocusGesture
(
recognizer
:
UITapGestureRecognizer
)
{
guard
isTapToFocusEnabled
&&
isFocusPointOfInterestSupported
else
{
return
}
let
point
=
recognizer
.
location
(
in
:
self
)
focus
(
at
:
preview
.
captureDevicePointOfInterestForPoint
(
point
:
point
))
delegate
?
.
capture
?(
capture
:
self
,
didTapToFocusAt
:
point
)
}
/**
Handler for the tapToExposeGesture.
- Parameter recognizer: A UITapGestureRecognizer that is associated with the event.
*/
@objc
fileprivate
func
handleTapToExposeGesture
(
recognizer
:
UITapGestureRecognizer
)
{
guard
isTapToExposeEnabled
&&
isExposurePointOfInterestSupported
else
{
return
}
let
point
=
recognizer
.
location
(
in
:
self
)
expose
(
at
:
preview
.
captureDevicePointOfInterestForPoint
(
point
:
point
))
delegate
?
.
capture
?(
capture
:
self
,
didTapToExposeAt
:
point
)
}
/**
Handler for the tapToResetGesture.
- Parameter recognizer: A UITapGestureRecognizer that is associated with the event.
*/
@objc
fileprivate
func
handleTapToResetGesture
(
recognizer
:
UITapGestureRecognizer
)
{
guard
isTapToResetEnabled
else
{
return
}
reset
()
let
point
=
preview
.
pointForCaptureDevicePointOfInterest
(
point
:
CGPoint
(
x
:
0.5
,
y
:
0.5
))
delegate
?
.
capture
?(
capture
:
self
,
didTapToResetAt
:
point
)
}
}
extension
Capture
{
/// Starts the timer for recording.
fileprivate
func
startTimer
()
{
timer
?
.
invalidate
()
timer
=
Timer
(
timeInterval
:
0.5
,
target
:
self
,
selector
:
#selector(
updateTimer
)
,
userInfo
:
nil
,
repeats
:
true
)
RunLoop
.
main
.
add
(
timer
!
,
forMode
:
.
commonModes
)
delegate
?
.
capture
?(
capture
:
self
,
didStartRecord
:
timer
!
)
}
/// Updates the timer when recording.
@objc
fileprivate
func
updateTimer
()
{
let
duration
=
recordedDuration
let
time
=
CMTimeGetSeconds
(
duration
)
let
hours
=
Int
(
time
/
3600
)
let
minutes
=
Int
((
time
/
60
)
.
truncatingRemainder
(
dividingBy
:
60
))
let
seconds
=
Int
(
time
.
truncatingRemainder
(
dividingBy
:
60
))
delegate
?
.
capture
?(
capture
:
self
,
didUpdateRecord
:
timer
!
,
hours
:
hours
,
minutes
:
minutes
,
seconds
:
seconds
)
}
/// Stops the timer when recording.
fileprivate
func
stopTimer
()
{
let
duration
=
recordedDuration
let
time
=
CMTimeGetSeconds
(
duration
)
let
hours
=
Int
(
time
/
3600
)
let
minutes
=
Int
((
time
/
60
)
.
truncatingRemainder
(
dividingBy
:
60
))
let
seconds
=
Int
(
time
.
truncatingRemainder
(
dividingBy
:
60
))
timer
?
.
invalidate
()
delegate
?
.
capture
?(
capture
:
self
,
didStopRecord
:
timer
!
,
hours
:
hours
,
minutes
:
minutes
,
seconds
:
seconds
)
timer
=
nil
}
}
extension
Capture
:
UIGestureRecognizerDelegate
{
/**
Prepares a given tap gesture.
- Parameter gesture: An optional UITapGestureRecognizer to prepare.
- Parameter numberOfTapsRequired: An integer of the number of taps required
to activate the gesture.
- Parameter numberOfTouchesRequired: An integer of the number of touches, fingers,
required to activate the gesture.
- Parameter selector: A Selector to handle the event.
*/
fileprivate
func
prepareTapGesture
(
gesture
:
inout
UITapGestureRecognizer
?,
numberOfTapsRequired
:
Int
,
numberOfTouchesRequired
:
Int
,
selector
:
Selector
)
{
guard
nil
==
gesture
else
{
return
}
gesture
=
UITapGestureRecognizer
(
target
:
self
,
action
:
selector
)
gesture
!.
delegate
=
self
gesture
!.
numberOfTapsRequired
=
numberOfTapsRequired
gesture
!.
numberOfTouchesRequired
=
numberOfTouchesRequired
addGestureRecognizer
(
gesture
!
)
}
/**
Removes a given tap gesture.
- Parameter gesture: An optional UITapGestureRecognizer to remove.
*/
fileprivate
func
removeTapGesture
(
gesture
:
inout
UITapGestureRecognizer
?)
{
guard
let
v
=
gesture
else
{
return
}
removeGestureRecognizer
(
v
)
gesture
=
nil
}
}
extension
Capture
:
AVCaptureFileOutputRecordingDelegate
{
public
func
capture
(
_
captureOutput
:
AVCaptureFileOutput
!
,
didStartRecordingToOutputFileAt
fileURL
:
URL
!
,
fromConnections
connections
:
[
Any
]
!
)
{
isRecording
=
true
delegate
?
.
capture
?(
capture
:
self
,
captureOutput
:
captureOutput
,
didStartRecordingToOutputFileAt
:
fileURL
as
NSURL
,
fromConnections
:
connections
)
}
public
func
capture
(
_
captureOutput
:
AVCaptureFileOutput
!
,
didFinishRecordingToOutputFileAt
outputFileURL
:
URL
!
,
fromConnections
connections
:
[
Any
]
!
,
error
:
Error
!
)
{
isRecording
=
false
delegate
?
.
capture
?(
capture
:
self
,
captureOutput
:
captureOutput
,
didFinishRecordingToOutputFileAt
:
outputFileURL
as
NSURL
,
fromConnections
:
connections
,
error
:
error
)
}
}
Sources/iOS/Capture/CaptureController.swift
deleted
100644 → 0
View file @
6fbfcb75
/*
* Copyright (C) 2015 - 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import
UIKit
import
AVFoundation
extension
UIViewController
{
/**
A convenience property that provides access to the CaptureController.
This is the recommended method of accessing the CaptureController
through child UIViewControllers.
*/
public
var
captureController
:
CaptureController
?
{
var
viewController
:
UIViewController
?
=
self
while
nil
!=
viewController
{
if
viewController
is
CaptureController
{
return
viewController
as?
CaptureController
}
viewController
=
viewController
?
.
parent
}
return
nil
}
}
open
class
CaptureController
:
ToolbarController
{
/// A reference to the Capture instance.
@IBInspectable
open
let
capture
=
Capture
()
open
override
var
supportedInterfaceOrientations
:
UIInterfaceOrientationMask
{
return
UIInterfaceOrientationMask
.
portrait
}
open
override
var
preferredInterfaceOrientationForPresentation
:
UIInterfaceOrientation
{
return
UIInterfaceOrientation
.
portrait
}
open
override
func
prepare
()
{
super
.
prepare
()
displayStyle
=
.
full
view
.
backgroundColor
=
.
black
prepareStatusBar
()
prepareToolbar
()
prepareCapture
()
}
}
extension
CaptureController
{
/// Prepares the statusBar.
fileprivate
func
prepareStatusBar
()
{
statusBar
.
backgroundColor
=
.
clear
}
/// Prepares the toolbar.
fileprivate
func
prepareToolbar
()
{
toolbar
.
backgroundColor
=
.
clear
toolbar
.
depthPreset
=
.
none
}
/// Prepares capture.
fileprivate
func
prepareCapture
()
{
capture
.
delegate
=
self
capture
.
flashMode
=
.
auto
}
}
extension
CaptureController
:
CaptureDelegate
{}
Sources/iOS/Capture/CapturePreview.swift
deleted
100644 → 0
View file @
6fbfcb75
/*
* Copyright (C) 2015 - 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import
UIKit
import
AVFoundation
open
class
CapturePreview
:
View
{
open
override
class
var
layerClass
:
AnyClass
{
return
AVCaptureVideoPreviewLayer
.
self
}
/**
Converts a point in layer coordinates to a point of interest
in the coordinate space of the capture device providing input
to the layer.
- Parameter point: A CGPoint.
- Returns: A CGPoint that is converted.
*/
open
func
captureDevicePointOfInterestForPoint
(
point
:
CGPoint
)
->
CGPoint
{
return
(
layer
as!
AVCaptureVideoPreviewLayer
)
.
captureDevicePointOfInterest
(
for
:
point
)
}
/**
Converts a point of interest in the coordinate space of the
capture device providing input to the layer to a point in
layer coordinates.
- Parameter point: A CGPoint.
- Returns: A CGPoint that is converted.
*/
open
func
pointForCaptureDevicePointOfInterest
(
point
:
CGPoint
)
->
CGPoint
{
return
(
layer
as!
AVCaptureVideoPreviewLayer
)
.
pointForCaptureDevicePoint
(
ofInterest
:
point
)
}
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open
override
func
prepare
()
{
super
.
prepare
()
preparePreviewLayer
()
}
/// Prepares the previewLayer.
private
func
preparePreviewLayer
()
{
layer
.
backgroundColor
=
Color
.
black
.
cgColor
layer
.
masksToBounds
=
true
(
layer
as!
AVCaptureVideoPreviewLayer
)
.
videoGravity
=
AVLayerVideoGravityResizeAspectFill
}
}
Sources/iOS/CollectionViewLayout.swift
View file @
b46db5a2
...
@@ -115,12 +115,20 @@ extension CollectionViewLayout {
...
@@ -115,12 +115,20 @@ extension CollectionViewLayout {
offset
.
x
+=
interimSpace
offset
.
x
+=
interimSpace
offset
.
y
+=
interimSpace
offset
.
y
+=
interimSpace
if
0
<
itemSize
.
width
&&
0
<
itemSize
.
height
{
if
nil
!=
item
.
width
{
offset
.
x
+=
item
.
width
!
offset
.
x
+=
item
.
width
!
offset
.
y
+=
item
.
height
!
}
else
if
let
v
=
item
.
data
as?
UIView
,
0
<
v
.
bounds
.
width
{
}
else
if
let
v
=
item
.
data
as?
UIView
{
offset
.
x
+=
v
.
bounds
.
width
offset
.
x
+=
v
.
bounds
.
width
}
else
{
offset
.
x
+=
itemSize
.
width
}
if
nil
!=
item
.
height
{
offset
.
y
+=
item
.
height
!
}
else
if
let
v
=
item
.
data
as?
UIView
,
0
<
v
.
bounds
.
height
{
offset
.
y
+=
v
.
bounds
.
height
offset
.
y
+=
v
.
bounds
.
height
}
else
{
offset
.
y
+=
itemSize
.
height
}
}
}
}
...
@@ -148,7 +156,7 @@ extension CollectionViewLayout {
...
@@ -148,7 +156,7 @@ extension CollectionViewLayout {
if
let
h
=
dataSourceItem
.
height
{
if
let
h
=
dataSourceItem
.
height
{
attributes
.
frame
=
CGRect
(
x
:
contentEdgeInsets
.
left
,
y
:
offset
.
y
,
width
:
collectionView
!.
bounds
.
width
-
contentEdgeInsets
.
left
-
contentEdgeInsets
.
right
,
height
:
h
)
attributes
.
frame
=
CGRect
(
x
:
contentEdgeInsets
.
left
,
y
:
offset
.
y
,
width
:
collectionView
!.
bounds
.
width
-
contentEdgeInsets
.
left
-
contentEdgeInsets
.
right
,
height
:
h
)
}
else
if
let
v
=
dataSourceItem
.
data
as?
UIView
{
}
else
if
let
v
=
dataSourceItem
.
data
as?
UIView
,
0
<
v
.
bounds
.
height
{
v
.
setNeedsLayout
()
v
.
setNeedsLayout
()
v
.
layoutIfNeeded
()
v
.
layoutIfNeeded
()
...
@@ -161,7 +169,7 @@ extension CollectionViewLayout {
...
@@ -161,7 +169,7 @@ extension CollectionViewLayout {
if
let
w
=
dataSourceItem
.
width
{
if
let
w
=
dataSourceItem
.
width
{
attributes
.
frame
=
CGRect
(
x
:
offset
.
x
,
y
:
contentEdgeInsets
.
top
,
width
:
w
,
height
:
collectionView
!.
bounds
.
height
-
contentEdgeInsets
.
top
-
contentEdgeInsets
.
bottom
)
attributes
.
frame
=
CGRect
(
x
:
offset
.
x
,
y
:
contentEdgeInsets
.
top
,
width
:
w
,
height
:
collectionView
!.
bounds
.
height
-
contentEdgeInsets
.
top
-
contentEdgeInsets
.
bottom
)
}
else
if
let
v
=
dataSourceItem
.
data
as?
UIView
{
}
else
if
let
v
=
dataSourceItem
.
data
as?
UIView
,
0
<
v
.
bounds
.
width
{
v
.
setNeedsLayout
()
v
.
setNeedsLayout
()
v
.
layoutIfNeeded
()
v
.
layoutIfNeeded
()
...
...
Sources/iOS/Events/Events.swift
deleted
100644 → 0
View file @
6fbfcb75
/*
* Copyright (C) 2015 - 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import
EventKit
import
CoreData
@objc(EventsReminderAuthorizationStatus)
public
enum
EventsReminderAuthorizationStatus
:
Int
{
case
authorized
case
denied
}
@objc(EventsReminderPriority)
public
enum
EventsReminderPriority
:
Int
{
case
none
case
high
=
1
case
medium
=
5
case
low
=
9
}
@objc(EventsDelegate)
public
protocol
EventsDelegate
{
/**
A delegation method that is executed when the reminder authorization
status changes.
- Parameter events: A reference to the Events instance.
- Parameter status: A reference to the EventReminderAuthorizationStatus.
*/
@objc
optional
func
events
(
events
:
Events
,
status
:
EventsReminderAuthorizationStatus
)
/**
A delegation method that is fired when changes to the event store occur.
- Parameter events: A reference to the Events instance.
*/
@objc
optional
func
eventsShouldRefresh
(
events
:
Events
)
/**
A delegation method that is executed when events authorization is authorized.
- Parameter events: A reference to the Events instance.
*/
@objc
optional
func
eventsAuthorizedForReminders
(
events
:
Events
)
/**
A delegation method that is executed when events authorization is denied.
- Parameter events: A reference to the Events instance.
*/
@objc
optional
func
eventsDeniedForReminders
(
events
:
Events
)
/**
A delegation method that is executed when a new calendar is created.
- Parameter events: A reference to the Events instance.
- Parameter createdCalendar calendar: An optional reference to the calendar created.
- Parameter error: An optional error if the calendar failed to be created.
*/
@objc
optional
func
events
(
events
:
Events
,
createdCalendar
calendar
:
EKCalendar
?,
error
:
Error
?)
/**
A delegation method that is executed when a calendar is updated.
- Parameter events: A reference to the Events instance.
- Parameter updatedCalendar calendar: A reference to the updated calendar.
- Parameter error: An optional error if the calendar failed to be updated.
*/
@objc
optional
func
events
(
events
:
Events
,
updatedCalendar
calendar
:
EKCalendar
,
error
:
Error
?)
/**
A delegation method that is executed when a calendar is removed.
- Parameter events: A reference to the Events instance.
- Parameter removedCalendar calendar: A reference to the calendar removed.
- Parameter error: An optional error if the calendar failed to be removed.
*/
@objc
optional
func
events
(
events
:
Events
,
removedCalendar
calendar
:
EKCalendar
,
error
:
Error
?)
/**
A delegation method that is executed when a new reminder is created.
- Parameter events: A reference to the Events instance.
- Parameter createdReminder reminder: An optional reference to the reminder created.
- Parameter error: An optional error if the reminder failed to be created.
*/
@objc
optional
func
events
(
events
:
Events
,
createdReminder
reminder
:
EKReminder
?,
error
:
Error
?)
/**
A delegation method that is executed when a reminder is updated.
- Parameter events: A reference to the Events instance.
- Parameter updatedReminder reminder: A reference to the updated reminder.
- Parameter error: An optional error if the reminder failed to be updated.
*/
@objc
optional
func
events
(
events
:
Events
,
updatedReminder
reminder
:
EKReminder
,
error
:
Error
?)
/**
A delegation method that is executed when a reminder is removed.
- Parameter events: A reference to the Events instance.
- Parameter removedReminder reminder: A reference to the removed reminder.
- Parameter error: An optional error if the reminder failed to be removed.
*/
@objc
optional
func
events
(
events
:
Events
,
removedReminder
reminder
:
EKReminder
,
error
:
Error
?)
}
@objc(Events)
open
class
Events
:
NSObject
{
/// A cache of calendars.
open
fileprivate
(
set
)
var
cacheForCalendars
=
[
AnyHashable
:
EKCalendar
]()
/// A cache of reminders.
open
fileprivate
(
set
)
var
cacheForReminders
=
[
AnyHashable
:
EKReminder
]()
/// A boolean indicating whether to commit saves or not.
fileprivate
var
isCommitted
=
true
/// A reference to the eventsStore.
fileprivate
let
eventStore
=
EKEventStore
()
/// The current EventsReminderAuthorizationStatus.
open
var
authorizationStatusForReminders
:
EventsReminderAuthorizationStatus
{
return
.
authorized
==
EKEventStore
.
authorizationStatus
(
for
:
.
reminder
)
?
.
authorized
:
.
denied
}
/// A reference to an EventsDelegate.
open
weak
var
delegate
:
EventsDelegate
?
/// Denitializer.
deinit
{
NotificationCenter
.
default
.
removeObserver
(
self
)
}
/**
Requests authorization for reminders.
- Parameter completion: An optional completion callback.
*/
open
func
requestAuthorizationForReminders
(
completion
:
((
EventsReminderAuthorizationStatus
)
->
Void
)?
=
nil
)
{
eventStore
.
requestAccess
(
to
:
.
reminder
)
{
[
weak
self
,
completion
=
completion
]
(
isAuthorized
,
_
)
in
DispatchQueue
.
main
.
async
{
[
weak
self
,
completion
=
completion
]
in
guard
let
s
=
self
else
{
return
}
guard
isAuthorized
else
{
completion
?(
.
denied
)
s
.
delegate
?
.
events
?(
events
:
s
,
status
:
.
denied
)
s
.
delegate
?
.
eventsDeniedForReminders
?(
events
:
s
)
return
}
s
.
prepareNotification
()
completion
?(
.
authorized
)
s
.
delegate
?
.
events
?(
events
:
s
,
status
:
.
authorized
)
s
.
delegate
?
.
eventsAuthorizedForReminders
?(
events
:
s
)
}
}
}
}
extension
Events
{
/// Prepares the notification handlers.
fileprivate
func
prepareNotification
()
{
NotificationCenter
.
default
.
addObserver
(
self
,
selector
:
#selector(
handleEventStoreChange(_:)
)
,
name
:
NSNotification
.
Name
.
EKEventStoreChanged
,
object
:
eventStore
)
}
}
extension
Events
{
/**
Handler for event store changes.
- Parameter _ notification: A Notification.
*/
@objc
fileprivate
func
handleEventStoreChange
(
_
notification
:
Notification
)
{
delegate
?
.
eventsShouldRefresh
?(
events
:
self
)
}
}
extension
Events
{
/// Begins a storage transaction.
open
func
begin
()
{
isCommitted
=
false
}
/// Resets the storage transaction state.
open
func
reset
()
{
isCommitted
=
true
}
/**
Commits the storage transaction.
- Parameter completion: A completion call back.
*/
open
func
commit
(
_
completion
:
((
Bool
,
Error
?)
->
Void
))
{
reset
()
var
success
=
false
var
error
:
Error
?
do
{
try
eventStore
.
commit
()
success
=
true
}
catch
let
e
{
error
=
e
}
completion
(
success
,
error
)
}
}
extension
Events
{
/**
Creates a predicate for the events Array of calendars.
- Parameter in calendars: An optional Array of EKCalendars.
*/
open
func
predicateForReminders
(
in
calendars
:
[
EKCalendar
])
->
NSPredicate
{
return
eventStore
.
predicateForReminders
(
in
:
calendars
)
}
/**
Creates a predicate with a given start and end date for
incomplete reminders. Providing a calendars Array narrows
the search.
- Parameter starting: A Date.
- Parameter ending: A Date.
- Parameter calendars: An optional Array of [EKCalendar].
*/
open
func
predicateForIncompleteReminders
(
starting
:
Date
,
ending
:
Date
,
calendars
:
[
EKCalendar
]?
=
nil
)
->
NSPredicate
{
return
eventStore
.
predicateForIncompleteReminders
(
withDueDateStarting
:
starting
,
ending
:
ending
,
calendars
:
calendars
)
}
/**
Creates a predicate with a given start and end date for
completed reminders. Providing a calendars Array narrows
the search.
- Parameter starting: A Date.
- Parameter ending: A Date.
- Parameter calendars: An optional Array of [EKCalendar].
*/
open
func
predicateForCompletedReminders
(
starting
:
Date
,
ending
:
Date
,
calendars
:
[
EKCalendar
]?
=
nil
)
->
NSPredicate
{
return
eventStore
.
predicateForCompletedReminders
(
withCompletionDateStarting
:
starting
,
ending
:
ending
,
calendars
:
calendars
)
}
}
extension
Events
{
/**
Fetches all calendars for a given reminder.
- Parameter completion: A completion call back
*/
open
func
fetchCalendarsForReminders
(
_
completion
:
@escaping
([
EKCalendar
])
->
Void
)
{
DispatchQueue
.
global
(
qos
:
.
default
)
.
async
{
[
weak
self
,
completion
=
completion
]
in
guard
let
s
=
self
else
{
return
}
let
calendars
=
s
.
eventStore
.
calendars
(
for
:
.
reminder
)
.
sorted
(
by
:
{
(
a
,
b
)
->
Bool
in
return
a
.
title
<
b
.
title
})
for
calendar
in
calendars
{
s
.
cacheForCalendars
[
calendar
.
calendarIdentifier
]
=
calendar
}
DispatchQueue
.
main
.
async
{
[
calendars
=
calendars
,
completion
=
completion
]
in
completion
(
calendars
)
}
}
}
/**
Fetches all reminders matching a given predicate.
- Parameter predicate: A NSPredicate.
- Parameter completion: A completion call back.
- Returns: A fetch events request identifier.
*/
@discardableResult
open
func
fetchReminders
(
matching
predicate
:
NSPredicate
,
completion
:
@escaping
([
EKReminder
])
->
Void
)
->
Any
{
return
eventStore
.
fetchReminders
(
matching
:
predicate
,
completion
:
{
[
weak
self
,
completion
=
completion
]
(
reminders
)
in
guard
let
s
=
self
else
{
return
}
let
r
=
reminders
??
[]
for
reminder
in
r
{
s
.
cacheForReminders
[
reminder
.
calendarItemIdentifier
]
=
reminder
}
DispatchQueue
.
main
.
async
{
[
completion
=
completion
]
in
completion
(
r
)
}
})
}
/**
Fetch all the events in a given Array of calendars.
- Parameter in calendars: An Array of EKCalendars.
- Parameter completion: A completion call back.
- Returns: A fetch events request identifier.
*/
@discardableResult
open
func
fetchReminders
(
in
calendars
:
[
EKCalendar
],
completion
:
@escaping
([
EKReminder
])
->
Void
)
->
Any
{
return
fetchReminders
(
matching
:
predicateForReminders
(
in
:
calendars
),
completion
:
completion
)
}
/**
Fetch all the events in a given Array of calendars that
are incomplete, given a start and end date.
- Parameter starting: A Date.
- Parameter ending: A Date.
- Parameter calendars: An Array of EKCalendars.
- Parameter completion: A completion call back.
- Returns: A fetch events request identifier.
*/
@discardableResult
open
func
fetchIncompleteReminders
(
starting
:
Date
,
ending
:
Date
,
calendars
:
[
EKCalendar
]?
=
nil
,
completion
:
@escaping
([
EKReminder
])
->
Void
)
->
Any
{
return
fetchReminders
(
matching
:
predicateForIncompleteReminders
(
starting
:
starting
,
ending
:
ending
,
calendars
:
calendars
),
completion
:
completion
)
}
/**
Fetch all the events in a given Array of calendars that
are completed, given a start and end date.
- Parameter starting: A Date.
- Parameter ending: A Date.
- Parameter calendars: An Array of EKCalendars.
- Parameter completion: A completion call back.
- Returns: A fetch events request identifier.
*/
@discardableResult
open
func
fetchCompletedReminders
(
starting
:
Date
,
ending
:
Date
,
calendars
:
[
EKCalendar
]?
=
nil
,
completion
:
@escaping
([
EKReminder
])
->
Void
)
->
Any
{
return
fetchReminders
(
matching
:
predicateForCompletedReminders
(
starting
:
starting
,
ending
:
ending
,
calendars
:
calendars
),
completion
:
completion
)
}
/**
Cancels an active events request.
- Parameter _ identifier: An identifier.
*/
open
func
cancelFetchRequest
(
_
identifier
:
Any
)
{
eventStore
.
cancelFetchRequest
(
identifier
)
}
}
extension
Events
{
/**
Creates a new reminder calendar.
- Parameter calendar title: the name of the list.
- Parameter completion: An optional completion call back.
*/
open
func
createCalendarForReminders
(
title
:
String
,
completion
:
((
EKCalendar
?,
Error
?)
->
Void
)?
=
nil
)
{
DispatchQueue
.
global
(
qos
:
.
default
)
.
async
{
[
weak
self
,
completion
=
completion
]
in
guard
let
s
=
self
else
{
return
}
let
calendar
=
EKCalendar
(
for
:
.
reminder
,
eventStore
:
s
.
eventStore
)
calendar
.
title
=
title
calendar
.
source
=
s
.
eventStore
.
defaultCalendarForNewReminders
()
.
source
var
success
=
false
var
error
:
Error
?
do
{
try
s
.
eventStore
.
saveCalendar
(
calendar
,
commit
:
s
.
isCommitted
)
success
=
true
s
.
cacheForCalendars
[
calendar
.
calendarIdentifier
]
=
calendar
}
catch
let
e
{
error
=
e
}
DispatchQueue
.
main
.
async
{
[
weak
self
,
calendar
=
calendar
,
error
=
error
,
completion
=
completion
]
in
guard
let
s
=
self
else
{
return
}
completion
?(
success
?
calendar
:
nil
,
error
)
s
.
delegate
?
.
events
?(
events
:
s
,
createdCalendar
:
success
?
calendar
:
nil
,
error
:
error
)
}
}
}
/**
Updates a given calendar.
- Parameter calendar: An EKCalendar.
- Parameter completion: An optional completion call back.
*/
open
func
update
(
calendar
:
EKCalendar
,
completion
:
((
Bool
,
Error
?)
->
Void
)?
=
nil
)
{
DispatchQueue
.
global
(
qos
:
.
default
)
.
async
{
[
weak
self
,
calendar
=
calendar
,
completion
=
completion
]
in
guard
let
s
=
self
else
{
return
}
var
success
=
false
var
error
:
Error
?
do
{
try
s
.
eventStore
.
saveCalendar
(
calendar
,
commit
:
s
.
isCommitted
)
success
=
true
s
.
cacheForCalendars
[
calendar
.
calendarIdentifier
]
=
calendar
}
catch
let
e
{
error
=
e
}
DispatchQueue
.
main
.
async
{
[
weak
self
,
calendar
=
calendar
,
error
=
error
,
completion
=
completion
]
in
guard
let
s
=
self
else
{
return
}
completion
?(
success
,
error
)
s
.
delegate
?
.
events
?(
events
:
s
,
updatedCalendar
:
calendar
,
error
:
error
)
}
}
}
/**
Removes an existing calendar,
- Parameter calendar identifier: The EKCalendar identifier String.
- Parameter completion: An optional completion call back.
*/
open
func
removeCalendar
(
identifier
:
String
,
completion
:
((
Bool
,
Error
?)
->
Void
)?
=
nil
)
{
DispatchQueue
.
global
(
qos
:
.
default
)
.
async
{
[
weak
self
,
completion
=
completion
]
in
guard
let
s
=
self
else
{
return
}
var
success
=
false
var
error
:
Error
?
guard
let
calendar
=
s
.
eventStore
.
calendar
(
withIdentifier
:
identifier
)
else
{
var
userInfo
=
[
String
:
Any
]()
userInfo
[
NSLocalizedDescriptionKey
]
=
"[Material Error: Cannot remove calendar with identifier
\(
identifier
)
.]"
userInfo
[
NSLocalizedFailureReasonErrorKey
]
=
"[Material Error: Cannot remove calendar with identifier
\(
identifier
)
.]"
error
=
NSError
(
domain
:
"com.cosmicmind.material.events"
,
code
:
0001
,
userInfo
:
userInfo
)
completion
?(
success
,
error
)
return
}
do
{
let
calendarIdentifier
=
calendar
.
calendarIdentifier
try
s
.
eventStore
.
removeCalendar
(
calendar
,
commit
:
s
.
isCommitted
)
success
=
true
s
.
cacheForCalendars
[
calendarIdentifier
]
=
nil
}
catch
let
e
{
error
=
e
}
DispatchQueue
.
main
.
async
{
[
weak
self
,
calendar
=
calendar
,
error
=
error
,
completion
=
completion
]
in
guard
let
s
=
self
else
{
return
}
completion
?(
success
,
error
)
s
.
delegate
?
.
events
?(
events
:
s
,
removedCalendar
:
calendar
,
error
:
error
)
}
}
}
}
extension
Events
{
/**
Adds a new reminder to an optionally existing list.
if the list does not exist it will be added to the default events list.
- Parameter title: A String.
- Parameter calendar: An EKCalendar.
- Parameter startDateComponents: An optional DateComponents.
- Parameter dueDateComponents: An optional DateComponents.
- Parameter priority: An optional EventsReminderPriority.
- Parameter completion: An optional completion call back.
*/
open
func
createReminder
(
title
:
String
,
calendar
:
EKCalendar
,
startDateComponents
:
DateComponents
?
=
nil
,
dueDateComponents
:
DateComponents
?
=
nil
,
priority
:
EventsReminderPriority
?
=
.
none
,
notes
:
String
?,
completion
:
((
EKReminder
?,
Error
?)
->
Void
)?
=
nil
)
{
DispatchQueue
.
global
(
qos
:
.
default
)
.
async
{
[
weak
self
,
calendar
=
calendar
,
completion
=
completion
]
in
guard
let
s
=
self
else
{
return
}
let
reminder
=
EKReminder
(
eventStore
:
s
.
eventStore
)
reminder
.
title
=
title
reminder
.
calendar
=
calendar
reminder
.
startDateComponents
=
startDateComponents
reminder
.
dueDateComponents
=
dueDateComponents
reminder
.
priority
=
priority
?
.
rawValue
??
EventsReminderPriority
.
none
.
rawValue
reminder
.
notes
=
notes
var
success
=
false
var
error
:
Error
?
do
{
try
s
.
eventStore
.
save
(
reminder
,
commit
:
s
.
isCommitted
)
success
=
true
s
.
cacheForReminders
[
reminder
.
calendarItemIdentifier
]
=
reminder
}
catch
let
e
{
error
=
e
}
DispatchQueue
.
main
.
async
{
[
weak
self
,
reminder
=
reminder
,
error
=
error
,
completion
=
completion
]
in
guard
let
s
=
self
else
{
return
}
completion
?(
success
?
reminder
:
nil
,
error
)
s
.
delegate
?
.
events
?(
events
:
s
,
createdReminder
:
success
?
reminder
:
nil
,
error
:
error
)
}
}
}
/**
Updates a given reminder.
- Parameter reminder: An EKReminder.
- Parameter completion: An optional completion call back.
*/
open
func
update
(
reminder
:
EKReminder
,
completion
:
((
Bool
,
Error
?)
->
Void
)?
=
nil
)
{
DispatchQueue
.
global
(
qos
:
.
default
)
.
async
{
[
weak
self
,
reminder
=
reminder
,
completion
=
completion
]
in
guard
let
s
=
self
else
{
return
}
var
success
=
false
var
error
:
Error
?
do
{
try
s
.
eventStore
.
save
(
reminder
,
commit
:
s
.
isCommitted
)
success
=
true
s
.
cacheForReminders
[
reminder
.
calendarItemIdentifier
]
=
reminder
}
catch
let
e
{
error
=
e
}
DispatchQueue
.
main
.
async
{
[
weak
self
,
reminder
=
reminder
,
error
=
error
,
completion
=
completion
]
in
guard
let
s
=
self
else
{
return
}
completion
?(
success
,
error
)
s
.
delegate
?
.
events
?(
events
:
s
,
updatedReminder
:
reminder
,
error
:
error
)
}
}
}
/**
Removes an existing reminder,
- Parameter reminder identifier: The EKReminders identifier String.
- Parameter completion: An optional completion call back.
*/
open
func
removeReminder
(
identifier
:
String
,
completion
:
((
Bool
,
Error
?)
->
Void
)?
=
nil
)
{
DispatchQueue
.
global
(
qos
:
.
default
)
.
async
{
[
weak
self
,
completion
=
completion
]
in
guard
let
s
=
self
else
{
return
}
var
success
=
false
var
error
:
Error
?
guard
let
reminder
=
s
.
eventStore
.
calendarItem
(
withIdentifier
:
identifier
)
as?
EKReminder
else
{
var
userInfo
=
[
String
:
Any
]()
userInfo
[
NSLocalizedDescriptionKey
]
=
"[Material Error: Cannot remove reminder with identifier
\(
identifier
)
.]"
userInfo
[
NSLocalizedFailureReasonErrorKey
]
=
"[Material Error: Cannot remove reminder with identifier
\(
identifier
)
.]"
error
=
NSError
(
domain
:
"com.cosmicmind.material.events"
,
code
:
0002
,
userInfo
:
userInfo
)
completion
?(
success
,
error
)
return
}
do
{
let
calendarItemIdentifier
=
reminder
.
calendarItemIdentifier
try
s
.
eventStore
.
remove
(
reminder
,
commit
:
s
.
isCommitted
)
success
=
true
s
.
cacheForReminders
[
calendarItemIdentifier
]
=
nil
}
catch
let
e
{
error
=
e
}
DispatchQueue
.
main
.
async
{
[
weak
self
,
reminder
=
reminder
,
error
=
error
,
completion
=
completion
]
in
guard
let
s
=
self
else
{
return
}
completion
?(
success
,
error
)
s
.
delegate
?
.
events
?(
events
:
s
,
removedReminder
:
reminder
,
error
:
error
)
}
}
}
}
extension
Events
{
/**
Creates an alarm using the current time plus a given timeInterval.
- Parameter timeIntervalSinceNow: A TimeInterval.
- Returns: An EKAlarm.
*/
open
func
createAlarm
(
timeIntervalSinceNow
:
TimeInterval
)
->
EKAlarm
{
return
EKAlarm
(
absoluteDate
:
Date
(
timeIntervalSinceNow
:
timeIntervalSinceNow
))
}
/**
Creates an alarm using given date components.
- Parameter day: An optional Int.
- Parameter month: An optional Int.
- Parameter year: An optional Int.
- Parameter hour: An optional Int.
- Parameter minute: An optional Int.
- Parameter second: An optional Int.
- Returns: An optional EKAlarm.
*/
open
func
createAlarm
(
day
:
Int
?
=
nil
,
month
:
Int
?
=
nil
,
year
:
Int
?
=
nil
,
hour
:
Int
?
=
nil
,
minute
:
Int
?
=
nil
,
second
:
Int
?
=
nil
)
->
EKAlarm
{
var
dateComponents
=
DateComponents
()
dateComponents
.
calendar
=
Calendar
.
current
dateComponents
.
day
=
day
dateComponents
.
month
=
month
dateComponents
.
year
=
year
dateComponents
.
hour
=
hour
dateComponents
.
minute
=
minute
dateComponents
.
second
=
second
return
EKAlarm
(
absoluteDate
:
dateComponents
.
date
!
)
}
/**
Creates an alarm using a relative offset from the start date.
- Parameter relativeOffset offset: A TimeInterval.
- Returns: An EKAlarm.
*/
open
func
createAlarm
(
relativeOffset
offset
:
TimeInterval
)
->
EKAlarm
{
return
EKAlarm
(
relativeOffset
:
offset
)
}
}
Sources/iOS/Events/EventsController.swift
deleted
100644 → 0
View file @
6fbfcb75
/*
* Copyright (C) 2015 - 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import
UIKit
extension
UIViewController
{
/**
A convenience property that provides access to the EventsController.
This is the recommended method of accessing the EventsController
through child UIViewControllers.
*/
public
var
eventsController
:
EventsController
?
{
var
viewController
:
UIViewController
?
=
self
while
nil
!=
viewController
{
if
viewController
is
EventsController
{
return
viewController
as?
EventsController
}
viewController
=
viewController
?
.
parent
}
return
nil
}
}
open
class
EventsController
:
UIViewController
{
/// A reference to an Events instance.
open
let
events
=
Events
()
open
override
func
viewDidLoad
()
{
super
.
viewDidLoad
()
prepare
()
}
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepareView method
to initialize property values and other setup operations.
The super.prepareView method should always be called immediately
when subclassing.
*/
open
func
prepare
()
{
view
.
clipsToBounds
=
true
view
.
backgroundColor
=
.
white
view
.
contentScaleFactor
=
Screen
.
scale
prepareEvents
()
}
}
extension
EventsController
{
/// Prepares the events instance.
fileprivate
func
prepareEvents
()
{
events
.
delegate
=
self
}
}
extension
EventsController
:
EventsDelegate
{}
Sources/iOS/Photos/PhotoLibrary.swift
deleted
100644 → 0
View file @
6fbfcb75
/*
* Copyright (C) 2015 - 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import
Photos
@objc(PhotoLibraryMove)
public
class
PhotoLibraryMove
:
NSObject
{
/// An index that is being moved from.
public
private(set)
var
from
:
Int
/// An index that is being moved to.
public
private(set)
var
to
:
Int
/**
An initializer that accepts a `from` and `to` Int value.
- Parameter from: An Int.
- Parameter to: An Int.
*/
public
init
(
from
:
Int
,
to
:
Int
)
{
self
.
from
=
from
self
.
to
=
to
}
}
public
struct
PhotoLibraryFetchResultDataSource
{
/// A reference to the PHFetchResults.
public
internal(set)
var
fetchResult
:
PHFetchResult
<
PHObject
>
/// A reference to the objects associated with the PHFetchResult.
public
internal(set)
var
objects
:
[
PHObject
]
}
@objc(PhotoLibraryDelegate)
public
protocol
PhotoLibraryDelegate
{
/**
A delegation method that is executed when the PhotoLibrary status is updated.
- Parameter photoLibrary: A reference to the PhotoLibrary.
- Parameter status: A reference to the PHAuthorizationStatus.
*/
@objc
optional
func
photoLibrary
(
photoLibrary
:
PhotoLibrary
,
status
:
PHAuthorizationStatus
)
/**
A delegation method that is executed when the PhotoLibrary is authorized.
- Parameter photoLibrary: A reference to the PhotoLibrary.
*/
@objc
optional
func
photoLibrary
(
authorized
photoLibrary
:
PhotoLibrary
)
/**
A delegation method that is executed when the PhotoLibrary is denied.
- Parameter photoLibrary: A reference to the PhotoLibrary.
*/
@objc
optional
func
photoLibrary
(
denied
photoLibrary
:
PhotoLibrary
)
/**
A delegation method that is executed when the PhotoLibrary is not determined.
- Parameter photoLibrary: A reference to the PhotoLibrary.
*/
@objc
optional
func
photoLibrary
(
notDetermined
photoLibrary
:
PhotoLibrary
)
/**
A delegation method that is executed when the PhotoLibrary is restricted.
- Parameter photoLibrary: A reference to the PhotoLibrary.
*/
@objc
optional
func
photoLibrary
(
restricted
photoLibrary
:
PhotoLibrary
)
/**
A delegation method that is executed when the PhotoLibrary has changes,
locally or remotely.
- Parameter photoLibrary: A reference to the PhotoLibrary.
- Parameter changeInfo: A reference to a PHChange object.
*/
@objc
optional
func
photoLibrary
(
photoLibrary
:
PhotoLibrary
,
didChange
changeInfo
:
PHChange
)
/**
A delegation method that is executed when changes are detected.
- Parameter photoLibrary: A reference to the PhotoLibrary.
- Parameter beforeChanges: A PHObject before changes.
- Parameter afterChanges: A PHObject after changes.
- Parameter assetContentChanged: A Bool that is true if the image or video content for this
object has changed.
- Parameter objectWasDeleted: A Bool that is true if the object was deleted.
*/
@objc
optional
func
photoLibrary
(
photoLibrary
:
PhotoLibrary
,
beforeChanges
:
PHObject
,
afterChanges
:
PHObject
,
assetContentChanged
:
Bool
,
objectWasDeleted
:
Bool
)
/**
A delegation method that is executed when there is a change in the
fetchResult object.
- Parameter photoLibrary: A reference to the PhotoLibrary.
- Parameter fetchBeforeChanges: A PHFetchResult<PHObject> before changes.
- Parameter fetchAfterChanges: A PHFetchResult<PHObject> after changes.
changes exist. True if yes, false otherwise.
*/
@objc
optional
func
photoLibrary
(
photoLibrary
:
PhotoLibrary
,
fetchBeforeChanges
:
PHFetchResult
<
PHObject
>
,
fetchAfterChanges
:
PHFetchResult
<
PHObject
>
)
/**
A delegation method that is executed when there are moved objects.
- Parameter photoLibrary: A reference to the PhotoLibrary.
- Parameter removed indexes: An IndexSet of the removed indexes.
- Parameter for objects: An Array of PHObjects that have been removed.
*/
@objc
optional
func
photoLibrary
(
photoLibrary
:
PhotoLibrary
,
removed
indexes
:
IndexSet
,
for
objects
:
[
PHObject
])
/**
A delegation method that is executed when there are newly inserted objects.
- Parameter photoLibrary: A reference to the PhotoLibrary.
- Parameter inserted indexes: An IndexSet of the inserted indexes.
- Parameter for objects: An Array of PHObjects that have been inserted.
*/
@objc
optional
func
photoLibrary
(
photoLibrary
:
PhotoLibrary
,
inserted
indexes
:
IndexSet
,
for
objects
:
[
PHObject
])
/**
A delegation method that is executed when there are changed objects.
- Parameter photoLibrary: A reference to the PhotoLibrary.
- Parameter changed indexes: An IndexSet of the changed indexes.
- Parameter for objects: An Array of PHObjects that have been changed.
*/
@objc
optional
func
photoLibrary
(
photoLibrary
:
PhotoLibrary
,
changed
indexes
:
IndexSet
,
for
objects
:
[
PHObject
])
/**
A delegation method that is executed describing the removed, inserted
and changed indexes.
- Parameter photoLibrary: A reference to the PhotoLibrary.
- Parameter removedIndexes: An IndexSet of the changed indexes.
- Parameter insertedIndexes: An IndexSet of the inserted indexes.
- Parameter changedIndexes: An IndexSet of the changed indexes.
- Parameter has moves: An Array of move coordinates.
*/
@objc
optional
func
photoLibrary
(
photoLibrary
:
PhotoLibrary
,
removedIndexes
:
IndexSet
?,
insertedIndexes
:
IndexSet
?,
changedIndexes
:
IndexSet
?,
has
moves
:
[
PhotoLibraryMove
])
}
@objc(PhotoLibrary)
public
class
PhotoLibrary
:
NSObject
{
/// A reference to the PHCachingImageManager.
public
internal(set)
lazy
var
cachingImageManager
=
PHCachingImageManager
()
/// A reference to all current PHFetchResults.
public
internal(set)
lazy
var
fetchResultsDataSource
=
[
String
:
PhotoLibraryFetchResultDataSource
]()
/// A reference to a PhotoLibraryDelegate.
open
weak
var
delegate
:
PhotoLibraryDelegate
?
/// The current PHAuthorizationStatus.
public
var
authorizationStatus
:
PHAuthorizationStatus
{
return
PHPhotoLibrary
.
authorizationStatus
()
}
/// Deinitializer that unregisters itself from watching changes in the PHPhotoLibrary.
deinit
{
PHPhotoLibrary
.
shared
()
.
unregisterChangeObserver
(
self
)
}
/// An initializer that prepares the PhotoLibrary.
public
override
init
()
{
super
.
init
()
prepare
()
}
/// Prepare the instance object.
open
func
prepare
()
{
prepareChangeObservers
()
}
/// Prepares the PHPhotoLibrary change observation.
private
func
prepareChangeObservers
()
{
PHPhotoLibrary
.
shared
()
.
register
(
self
)
}
}
/// Authorization.
extension
PhotoLibrary
{
/**
A method to request authorization from the user to enable photo library access. In order
for this to work, set the "Privacy - Photo Library Usage Description" value in the
application's info.plist.
- Parameter _ completion: A completion block that passes in a PHAuthorizationStatus
enum that describes the response for the authorization request.
*/
public
func
requestAuthorization
(
_
completion
:
((
PHAuthorizationStatus
)
->
Void
)?
=
nil
)
{
PHPhotoLibrary
.
requestAuthorization
{
[
weak
self
,
completion
=
completion
]
(
status
)
in
DispatchQueue
.
main
.
async
{
[
weak
self
,
completion
=
completion
]
in
guard
let
s
=
self
else
{
return
}
switch
status
{
case
.
authorized
:
s
.
delegate
?
.
photoLibrary
?(
photoLibrary
:
s
,
status
:
.
authorized
)
s
.
delegate
?
.
photoLibrary
?(
authorized
:
s
)
completion
?(
.
authorized
)
case
.
denied
:
s
.
delegate
?
.
photoLibrary
?(
photoLibrary
:
s
,
status
:
.
denied
)
s
.
delegate
?
.
photoLibrary
?(
denied
:
s
)
completion
?(
.
denied
)
case
.
notDetermined
:
s
.
delegate
?
.
photoLibrary
?(
photoLibrary
:
s
,
status
:
.
notDetermined
)
s
.
delegate
?
.
photoLibrary
?(
notDetermined
:
s
)
completion
?(
.
notDetermined
)
case
.
restricted
:
s
.
delegate
?
.
photoLibrary
?(
photoLibrary
:
s
,
status
:
.
restricted
)
s
.
delegate
?
.
photoLibrary
?(
restricted
:
s
)
completion
?(
.
restricted
)
}
}
}
}
}
/// Fetch.
extension
PhotoLibrary
{
/**
Fetches generic type T for a given PHFetchResult<T>.
- Parameter fetchResult: A PHFetchResult<T>.
- Parameter completion: A completion block.
*/
@discardableResult
internal
func
fetch
<
T
:
PHObject
,
U
:
PHFetchResult
<
T
>>
(
caller
:
String
,
result
:
U
,
completion
:
(([
T
],
U
)
->
Void
)?
=
nil
)
->
[
T
]
{
var
objects
=
[
T
]()
result
.
enumerateObjects
({
(
collection
,
_
,
_
)
in
objects
.
append
(
collection
)
})
// Used in change observation.
fetchResultsDataSource
[
caller
]
=
PhotoLibraryFetchResultDataSource
(
fetchResult
:
result
as!
PHFetchResult
<
PHObject
>
,
objects
:
objects
)
if
Thread
.
isMainThread
{
completion
?(
objects
,
result
)
}
else
{
DispatchQueue
.
main
.
async
{
[
objects
=
objects
,
result
=
result
,
completion
=
completion
]
in
completion
?(
objects
,
result
)
}
}
return
objects
}
}
/// PHCollectionList.
extension
PhotoLibrary
{
/**
A PHAssetCollectionMoment collection type will be contained
by a PHCollectionListSubtypeMomentListCluster and a
PHCollectionListSubtypeMomentListYear. Non-moment PHAssetCollections
will only be contained by a single collection list.
- Parameter _ collection: A PHCollection.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion callback.
*/
@discardableResult
public
func
fetchCollectionListsContaining
(
_
collection
:
PHCollection
,
options
:
PHFetchOptions
?,
completion
:
(([
PHCollectionList
],
PHFetchResult
<
PHCollectionList
>
)
->
Void
)?
=
nil
)
->
[
PHCollectionList
]
{
return
fetch
(
caller
:
#function
,
result
:
PHCollectionList
.
fetchCollectionListsContaining
(
collection
,
options
:
options
),
completion
:
completion
)
}
/**
Fetch PHCollectionLists based on a type and subtype.
- Parameter with type: A PHCollectionListType.
- Parameter subtype: A PHCollectionListSubtype.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion callback.
*/
@discardableResult
public
func
fetchCollectionList
(
with
type
:
PHCollectionListType
,
subtype
:
PHCollectionListSubtype
,
options
:
PHFetchOptions
?,
completion
:
(([
PHCollectionList
],
PHFetchResult
<
PHCollectionList
>
)
->
Void
)?
=
nil
)
->
[
PHCollectionList
]
{
return
fetch
(
caller
:
#function
,
result
:
PHCollectionList
.
fetchCollectionLists
(
with
:
type
,
subtype
:
subtype
,
options
:
options
),
completion
:
completion
)
}
/**
Fetch collection lists of a single type matching the
provided local identifiers (type is inferred from the
local identifiers).
- Parameter withLocalIdentifier identifiers: An Array
of String identifiers.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion callback.
*/
@discardableResult
public
func
fetchCollectionLists
(
withLocalIdentifiers
identifiers
:
[
String
],
options
:
PHFetchOptions
?,
completion
:
(([
PHCollectionList
],
PHFetchResult
<
PHCollectionList
>
)
->
Void
)?
=
nil
)
->
[
PHCollectionList
]
{
return
fetch
(
caller
:
#function
,
result
:
PHCollectionList
.
fetchCollectionLists
(
withLocalIdentifiers
:
identifiers
,
options
:
options
),
completion
:
completion
)
}
/**
Fetch asset collections of a single type and subtype
provided (use PHCollectionListSubtypeAny to match all
subtypes).
- Parameter with collectionListType: A PHCollectionListType.
- Parameter subtype: A PHCollectionListSubtype.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion callback.
*/
@discardableResult
public
func
fetchCollectionLists
(
with
collectionListType
:
PHCollectionListType
,
subtype
:
PHCollectionListSubtype
,
options
:
PHFetchOptions
?,
completion
:
(([
PHCollectionList
],
PHFetchResult
<
PHCollectionList
>
)
->
Void
)?
=
nil
)
->
[
PHCollectionList
]
{
return
fetch
(
caller
:
#function
,
result
:
PHCollectionList
.
fetchCollectionLists
(
with
:
collectionListType
,
subtype
:
subtype
,
options
:
options
),
completion
:
completion
)
}
/**
Fetch moment lists containing a given moment.
- Parameter with momentListSubtype: A PHCollectionListSubtype.
- Parameter containingMoment moment: A PHAssetCollection.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion callback.
*/
@discardableResult
public
func
fetchMomentLists
(
with
momentListSubtype
:
PHCollectionListSubtype
,
containingMoment
moment
:
PHAssetCollection
,
options
:
PHFetchOptions
?,
completion
:
(([
PHCollectionList
],
PHFetchResult
<
PHCollectionList
>
)
->
Void
)?
=
nil
)
->
[
PHCollectionList
]
{
return
fetch
(
caller
:
#function
,
result
:
PHCollectionList
.
fetchMomentLists
(
with
:
momentListSubtype
,
containingMoment
:
moment
,
options
:
options
),
completion
:
completion
)
}
/**
Fetch moment lists.
- Parameter with momentListSubtype: A PHCollectionListSubtype.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion callback.
*/
@discardableResult
public
func
fetchMomentLists
(
with
momentListSubtype
:
PHCollectionListSubtype
,
options
:
PHFetchOptions
?,
completion
:
(([
PHCollectionList
],
PHFetchResult
<
PHCollectionList
>
)
->
Void
)?
=
nil
)
->
[
PHCollectionList
]
{
return
fetch
(
caller
:
#function
,
result
:
PHCollectionList
.
fetchMomentLists
(
with
:
momentListSubtype
,
options
:
options
),
completion
:
completion
)
}
}
/// PHCollection.
extension
PhotoLibrary
{
/**
Fetches PHCollections in a given PHCollectionList.
- Parameter in collectionList: A PHCollectionList.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion callback.
*/
@discardableResult
public
func
fetchCollections
(
in
collectionList
:
PHCollectionList
,
options
:
PHFetchOptions
?,
completion
:
(([
PHCollection
],
PHFetchResult
<
PHCollection
>
)
->
Void
)?
=
nil
)
->
[
PHCollection
]
{
return
fetch
(
caller
:
#function
,
result
:
PHCollection
.
fetchCollections
(
in
:
collectionList
,
options
:
options
),
completion
:
completion
)
}
/**
Fetches PHCollections based on a type and subtype.
- Parameter with type: A PHCollectionListType.
- Parameter subtype: A PHCollectionListSubtype.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion callback.
*/
@discardableResult
public
func
fetchTopLevelUserCollections
(
with
options
:
PHFetchOptions
?,
completion
:
(([
PHCollection
],
PHFetchResult
<
PHCollection
>
)
->
Void
)?
=
nil
)
->
[
PHCollection
]
{
return
fetch
(
caller
:
#function
,
result
:
PHCollection
.
fetchTopLevelUserCollections
(
with
:
nil
),
completion
:
completion
)
}
}
/// PHAssetCollection.
extension
PhotoLibrary
{
/**
Fetch asset collections of a single type matching the provided
local identifiers (type is inferred from the local identifiers).
- Parameter withLocalIdentifiers identifiers: An Array of Strings.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion block.
*/
@discardableResult
public
func
fetchAssetCollections
(
withLocalIdentifiers
identifiers
:
[
String
],
options
:
PHFetchOptions
?,
completion
:
(([
PHAssetCollection
],
PHFetchResult
<
PHAssetCollection
>
)
->
Void
)?
=
nil
)
->
[
PHAssetCollection
]
{
return
fetch
(
caller
:
#function
,
result
:
PHAssetCollection
.
fetchAssetCollections
(
withLocalIdentifiers
:
identifiers
,
options
:
options
),
completion
:
completion
)
}
/**
Fetch asset collections of a single type and subtype provided
(use PHAssetCollectionSubtypeAny to match all subtypes).
- Parameter with type: A PHAssetCollection.
- Parameter subtype: A PHAssetCollectionSubtype.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion block.
*/
@discardableResult
public
func
fetchAssetCollections
(
with
type
:
PHAssetCollectionType
,
subtype
:
PHAssetCollectionSubtype
,
options
:
PHFetchOptions
?,
completion
:
(([
PHAssetCollection
],
PHFetchResult
<
PHAssetCollection
>
)
->
Void
)?
=
nil
)
->
[
PHAssetCollection
]
{
return
fetch
(
caller
:
#function
,
result
:
PHAssetCollection
.
fetchAssetCollections
(
with
:
type
,
subtype
:
subtype
,
options
:
options
),
completion
:
completion
)
}
/**
Smart Albums are not supported, only Albums and Moments.
- Parameter asset: A PHAsset.
- Parameter with type: A PHAssetCollection.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion block.
*/
@discardableResult
public
func
fetchAssetCollectionsContaining
(
_
asset
:
PHAsset
,
with
type
:
PHAssetCollectionType
,
options
:
PHFetchOptions
?,
completion
:
(([
PHAssetCollection
],
PHFetchResult
<
PHAssetCollection
>
)
->
Void
)?
=
nil
)
->
[
PHAssetCollection
]
{
return
fetch
(
caller
:
#function
,
result
:
PHAssetCollection
.
fetchAssetCollectionsContaining
(
asset
,
with
:
type
,
options
:
options
),
completion
:
completion
)
}
/**
AssetGroupURLs are URLs retrieved from ALAssetGroup's
ALAssetsGroupPropertyURL.
- Parameter withALAssetGroupURLs assetGroupURLs: An Array
of URLs.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion block.
*/
@discardableResult
public
func
fetchAssetCollections
(
withALAssetGroupURLs
assetGroupURLs
:
[
URL
],
options
:
PHFetchOptions
?,
completion
:
(([
PHAssetCollection
],
PHFetchResult
<
PHAssetCollection
>
)
->
Void
)?
=
nil
)
->
[
PHAssetCollection
]
{
return
fetch
(
caller
:
#function
,
result
:
PHAssetCollection
.
fetchAssetCollections
(
withALAssetGroupURLs
:
assetGroupURLs
,
options
:
options
),
completion
:
completion
)
}
/**
Fetches moments in a given moment list.
- Parameter inMomentList momentList: A PHCollectionList.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion block.
*/
@discardableResult
public
func
fetchMoments
(
inMomentList
momentList
:
PHCollectionList
,
options
:
PHFetchOptions
?,
completion
:
(([
PHAssetCollection
],
PHFetchResult
<
PHAssetCollection
>
)
->
Void
)?
=
nil
)
->
[
PHAssetCollection
]
{
return
fetch
(
caller
:
#function
,
result
:
PHAssetCollection
.
fetchMoments
(
inMomentList
:
momentList
,
options
:
options
),
completion
:
completion
)
}
/**
Fetches moments.
- Parameter with options: An optional PHFetchOptions object.
- Parameter completion: A completion block.
*/
@discardableResult
public
func
fetchMoments
(
with
options
:
PHFetchOptions
?,
completion
:
(([
PHAssetCollection
],
PHFetchResult
<
PHAssetCollection
>
)
->
Void
)?
=
nil
)
->
[
PHAssetCollection
]
{
return
fetch
(
caller
:
#function
,
result
:
PHAssetCollection
.
fetchMoments
(
with
:
options
),
completion
:
completion
)
}
}
/// PHAsset.
extension
PhotoLibrary
{
/**
Fetch the PHAssets in a given PHAssetCollection.
- Parameter in assetCollection: A PHAssetCollection.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion block.
*/
@discardableResult
public
func
fetchAssets
(
in
assetCollection
:
PHAssetCollection
,
options
:
PHFetchOptions
?,
completion
:
(([
PHAsset
],
PHFetchResult
<
PHAsset
>
)
->
Void
)?
=
nil
)
->
[
PHAsset
]
{
return
fetch
(
caller
:
#function
,
result
:
PHAsset
.
fetchAssets
(
in
:
assetCollection
,
options
:
options
),
completion
:
completion
)
}
/**
Fetch the PHAssets with a given Array of identifiers.
- Parameter withLocalIdentifiers identifiers: A Array of
String identifiers.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion block.
*/
@discardableResult
public
func
fetchAssets
(
withLocalIdentifiers
identifiers
:
[
String
],
options
:
PHFetchOptions
?,
completion
:
(([
PHAsset
],
PHFetchResult
<
PHAsset
>
)
->
Void
)?
=
nil
)
->
[
PHAsset
]
{
return
fetch
(
caller
:
#function
,
result
:
PHAsset
.
fetchAssets
(
withLocalIdentifiers
:
identifiers
,
options
:
options
),
completion
:
completion
)
}
/**
Fetch key assets.
- Parameter in assetCollection: A PHAssetCollection.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion block.
- Returns: An optional PHFetchResult<PHAsset> object.
*/
@discardableResult
public
func
fetchKeyAssets
(
in
assetCollection
:
PHAssetCollection
,
options
:
PHFetchOptions
?,
completion
:
(([
PHAsset
],
PHFetchResult
<
PHAsset
>
)
->
Void
)?
=
nil
)
->
PHFetchResult
<
PHAsset
>
?
{
guard
let
fetchResult
=
PHAsset
.
fetchKeyAssets
(
in
:
assetCollection
,
options
:
options
)
else
{
return
nil
}
let
_
:
[
PHAsset
]
=
fetch
(
caller
:
#function
,
result
:
fetchResult
,
completion
:
completion
)
return
fetchResult
}
/**
Fetch a burst asset with a given burst identifier.
- Parameter withBurstIdentifier burstIdentifier: A
PHAssetCollection.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion block.
*/
@discardableResult
public
func
fetchAssets
(
withBurstIdentifier
burstIdentifier
:
String
,
options
:
PHFetchOptions
?,
completion
:
(([
PHAsset
],
PHFetchResult
<
PHAsset
>
)
->
Void
)?
=
nil
)
->
[
PHAsset
]
{
return
fetch
(
caller
:
#function
,
result
:
PHAsset
.
fetchAssets
(
withBurstIdentifier
:
burstIdentifier
,
options
:
options
),
completion
:
completion
)
}
/**
Fetches PHAssetSourceTypeUserLibrary assets by default (use
includeAssetSourceTypes option to override).
- Parameter with options: An optional PHFetchOptions object.
- Parameter completion: A completion block.
*/
@discardableResult
public
func
fetchAssets
(
with
options
:
PHFetchOptions
?,
completion
:
(([
PHAsset
],
PHFetchResult
<
PHAsset
>
)
->
Void
)?
=
nil
)
->
[
PHAsset
]
{
return
fetch
(
caller
:
#function
,
result
:
PHAsset
.
fetchAssets
(
with
:
options
),
completion
:
completion
)
}
/**
Fetch the PHAssets with a given media type.
- Parameter in mediaType: A PHAssetMediaType.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion block.
*/
@discardableResult
public
func
fetchAssets
(
with
mediaType
:
PHAssetMediaType
,
options
:
PHFetchOptions
?,
completion
:
(([
PHAsset
],
PHFetchResult
<
PHAsset
>
)
->
Void
)?
=
nil
)
->
[
PHAsset
]
{
return
fetch
(
caller
:
#function
,
result
:
PHAsset
.
fetchAssets
(
with
:
mediaType
,
options
:
options
),
completion
:
completion
)
}
/**
AssetURLs are URLs retrieved from ALAsset's
ALAssetPropertyAssetURL.
- Parameter withALAssetURLs assetURLs: An Array of URLs.
- Parameter options: An optional PHFetchOptions object.
- Parameter completion: A completion block.
*/
@discardableResult
public
func
fetchAssets
(
withALAssetURLs
assetURLs
:
[
URL
],
options
:
PHFetchOptions
?,
completion
:
(([
PHAsset
],
PHFetchResult
<
PHAsset
>
)
->
Void
)?
=
nil
)
->
[
PHAsset
]
{
return
fetch
(
caller
:
#function
,
result
:
PHAsset
.
fetchAssets
(
withALAssetURLs
:
assetURLs
,
options
:
options
),
completion
:
completion
)
}
}
/// PHImageManager.
extension
PhotoLibrary
{
/**
Retrieves an optional UIImage for a given PHAsset that allows for a targetSize
and contentMode.
- Parameter for asset: A PHAsset.
- Parameter targetSize: A CGSize.
- Parameter contentMode: A PHImageContentMode.
- Parameter options: A PHImageRequestOptions.
- Parameter completion: A completion block.
- Returns: A PHImageRequestID.
*/
@discardableResult
public
func
requestImage
(
for
asset
:
PHAsset
,
targetSize
:
CGSize
,
contentMode
:
PHImageContentMode
,
options
:
PHImageRequestOptions
?,
completion
:
@escaping
(
UIImage
?,
[
AnyHashable
:
Any
]?)
->
Void
)
->
PHImageRequestID
{
return
PHImageManager
.
default
()
.
requestImage
(
for
:
asset
,
targetSize
:
targetSize
,
contentMode
:
contentMode
,
options
:
options
,
resultHandler
:
completion
)
}
/**
Retrieves an optional Data object for a given PHAsset.
- Parameter for asset: A PHAsset.
- Parameter options: A PHImageRequestOptions.
- Parameter completion: A completion block.
- Returns: A PHImageRequestID.
*/
@discardableResult
public
func
requestImageData
(
for
asset
:
PHAsset
,
options
:
PHImageRequestOptions
?,
completion
:
@escaping
(
Data
?,
String
?,
UIImageOrientation
,
[
AnyHashable
:
Any
]?)
->
Void
)
->
PHImageRequestID
{
return
PHImageManager
.
default
()
.
requestImageData
(
for
:
asset
,
options
:
options
,
resultHandler
:
completion
)
}
/**
Cancels an image request for a given PHImageRequestID.
- Parameter for requestID: A PHImageRequestID.
*/
public
func
cancelImageRequest
(
for
requestID
:
PHImageRequestID
)
{
PHImageManager
.
default
()
.
cancelImageRequest
(
requestID
)
}
/**
Requests a live photo representation of the asset. With
oportunistic (or if no
options are specified), the resultHandler block may be
called more than once (the first call may occur before
the method returns). The PHImageResultIsDegradedKey key
in the result handler's info parameter indicates when a
temporary low-quality live photo is provided.
- Parameter for asset: A PHAsset.
- Parameter targetSize: A CGSize.
- Parameter contentMode: A PHImageContentMode.
- Parameter options: A PHImageRequestOptions.
- Parameter completion: A completion block.
- Returns: A PHImageRequestID.
*/
@available(iOS 9.1, *)
@discardableResult
public
func
requestLivePhoto
(
for
asset
:
PHAsset
,
targetSize
:
CGSize
,
contentMode
:
PHImageContentMode
,
options
:
PHLivePhotoRequestOptions
?,
completion
:
@escaping
(
PHLivePhoto
?,
[
AnyHashable
:
Any
]?)
->
Void
)
->
PHImageRequestID
{
return
PHImageManager
.
default
()
.
requestLivePhoto
(
for
:
asset
,
targetSize
:
targetSize
,
contentMode
:
contentMode
,
options
:
options
,
resultHandler
:
completion
)
}
/**
For playback only.
- Parameter forVideo asset: A PHAsset.
- Parameter options: A PHImageRequestOptions.
- Parameter completion: A completion block.
- Returns: A PHImageRequestID.
*/
@discardableResult
public
func
requestPlayerItem
(
forVideo
asset
:
PHAsset
,
options
:
PHVideoRequestOptions
?,
completion
:
@escaping
(
AVPlayerItem
?,
[
AnyHashable
:
Any
]?)
->
Swift
.
Void
)
->
PHImageRequestID
{
return
PHImageManager
.
default
()
.
requestPlayerItem
(
forVideo
:
asset
,
options
:
options
,
resultHandler
:
completion
)
}
/**
Export.
- Parameter forVideo asset: A PHAsset.
- Parameter options: A PHImageRequestOptions.
- Parameter completion: A completion block.
- Returns: A PHImageRequestID.
*/
@discardableResult
public
func
requestExportSession
(
forVideo
asset
:
PHAsset
,
options
:
PHVideoRequestOptions
?,
exportPreset
:
String
,
completion
:
@escaping
(
AVAssetExportSession
?,
[
AnyHashable
:
Any
]?)
->
Void
)
->
PHImageRequestID
{
return
PHImageManager
.
default
()
.
requestExportSession
(
forVideo
:
asset
,
options
:
options
,
exportPreset
:
exportPreset
,
resultHandler
:
completion
)
}
/**
For all other requests.
- Parameter forVideo asset: A PHAsset.
- Parameter options: A PHImageRequestOptions.
- Parameter completion: A completion block.
- Returns: A PHImageRequestID.
*/
@discardableResult
public
func
requestAVAsset
(
forVideo
asset
:
PHAsset
,
options
:
PHVideoRequestOptions
?,
completion
:
@escaping
(
AVAsset
?,
AVAudioMix
?,
[
AnyHashable
:
Any
]?)
->
Void
)
->
PHImageRequestID
{
return
PHImageManager
.
default
()
.
requestAVAsset
(
forVideo
:
asset
,
options
:
options
,
resultHandler
:
completion
)
}
}
/// PHPhotoLibraryChangeObserver extension.
extension
PhotoLibrary
:
PHPhotoLibraryChangeObserver
{
/**
A delegation method that is fired when changes are made in the photo library.
- Parameter _ changeInstance: A PHChange obejct describing the changes in the
photo library.
*/
public
func
photoLibraryDidChange
(
_
changeInfo
:
PHChange
)
{
DispatchQueue
.
main
.
async
{
[
weak
self
,
changeInfo
=
changeInfo
]
in
guard
let
s
=
self
else
{
return
}
s
.
delegate
?
.
photoLibrary
?(
photoLibrary
:
s
,
didChange
:
changeInfo
)
for
(
_
,
var
dataSource
)
in
s
.
fetchResultsDataSource
{
for
i
in
0
..<
dataSource
.
objects
.
count
{
let
object
=
dataSource
.
objects
[
i
]
guard
let
details
=
changeInfo
.
changeDetails
(
for
:
object
)
else
{
continue
}
guard
let
afterChanges
=
details
.
objectAfterChanges
else
{
continue
}
dataSource
.
objects
[
i
]
=
afterChanges
s
.
delegate
?
.
photoLibrary
?(
photoLibrary
:
s
,
beforeChanges
:
details
.
objectBeforeChanges
,
afterChanges
:
afterChanges
,
assetContentChanged
:
details
.
assetContentChanged
,
objectWasDeleted
:
details
.
objectWasDeleted
)
}
if
let
details
=
changeInfo
.
changeDetails
(
for
:
dataSource
.
fetchResult
)
{
s
.
delegate
?
.
photoLibrary
?(
photoLibrary
:
s
,
fetchBeforeChanges
:
details
.
fetchResultBeforeChanges
,
fetchAfterChanges
:
details
.
fetchResultAfterChanges
)
dataSource
.
fetchResult
=
details
.
fetchResultAfterChanges
guard
details
.
hasIncrementalChanges
else
{
continue
}
let
removedIndexes
=
details
.
removedIndexes
let
insertedIndexes
=
details
.
insertedIndexes
let
changedIndexes
=
details
.
changedIndexes
if
nil
!=
removedIndexes
{
s
.
delegate
?
.
photoLibrary
?(
photoLibrary
:
s
,
removed
:
removedIndexes
!
,
for
:
details
.
removedObjects
)
}
if
nil
!=
insertedIndexes
{
s
.
delegate
?
.
photoLibrary
?(
photoLibrary
:
s
,
inserted
:
insertedIndexes
!
,
for
:
details
.
insertedObjects
)
}
if
nil
!=
changedIndexes
{
s
.
delegate
?
.
photoLibrary
?(
photoLibrary
:
s
,
changed
:
changedIndexes
!
,
for
:
details
.
changedObjects
)
}
var
moves
=
[
PhotoLibraryMove
]()
if
details
.
hasMoves
{
details
.
enumerateMoves
{
(
from
,
to
)
in
moves
.
append
(
PhotoLibraryMove
(
from
:
from
,
to
:
to
))
}
}
s
.
delegate
?
.
photoLibrary
?(
photoLibrary
:
s
,
removedIndexes
:
removedIndexes
,
insertedIndexes
:
insertedIndexes
,
changedIndexes
:
changedIndexes
,
has
:
moves
)
}
}
}
}
}
/// Requesting changes.
extension
PhotoLibrary
{
/**
Performes an asynchronous change to the PHPhotoLibrary database.
- Parameter _ block: A transactional block that ensures that
all changes to the PHPhotoLibrary are atomic.
- Parameter completion: A completion block that is executed once the
transaction has been completed.
*/
public
func
performChanges
(
_
block
:
@escaping
()
->
Void
,
completion
:
((
Bool
,
Error
?)
->
Void
)?
=
nil
)
{
PHPhotoLibrary
.
shared
()
.
performChanges
(
block
,
completionHandler
:
completion
)
}
}
Sources/iOS/Photos/PhotoLibraryController.swift
deleted
100644 → 0
View file @
6fbfcb75
/*
* Copyright (C) 2015 - 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import
UIKit
extension
UIViewController
{
/**
A convenience property that provides access to the PhotoLibraryController.
This is the recommended method of accessing the PhotoLibraryController
through child UIViewControllers.
*/
public
var
photoLibraryController
:
PhotoLibraryController
?
{
var
viewController
:
UIViewController
?
=
self
while
nil
!=
viewController
{
if
viewController
is
PhotoLibraryController
{
return
viewController
as?
PhotoLibraryController
}
viewController
=
viewController
?
.
parent
}
return
nil
}
}
open
class
PhotoLibraryController
:
UIViewController
{
/// A reference to a PhotoLibrary.
open
let
photoLibrary
=
PhotoLibrary
()
open
override
func
viewDidLoad
()
{
super
.
viewDidLoad
()
prepare
()
}
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open
func
prepare
()
{
view
.
clipsToBounds
=
true
view
.
backgroundColor
=
.
white
view
.
contentScaleFactor
=
Screen
.
scale
preparePhotoLibrary
()
}
}
extension
PhotoLibraryController
{
/// Prepares the photoLibrary.
fileprivate
func
preparePhotoLibrary
()
{
photoLibrary
.
delegate
=
self
}
}
extension
PhotoLibraryController
:
PhotoLibraryDelegate
{}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment