├── .gitignore
├── .swift-version
├── .travis.yml
├── Example
├── Podfile
├── Podfile.lock
├── Pods
│ ├── Local Podspecs
│ │ └── RAImagePicker.podspec.json
│ ├── Manifest.lock
│ ├── Pods.xcodeproj
│ │ └── project.pbxproj
│ └── Target Support Files
│ │ ├── Pods-RAImagePicker_Example-RAImagePicker_Tests
│ │ ├── Info.plist
│ │ ├── Pods-RAImagePicker_Example-RAImagePicker_Tests-acknowledgements.markdown
│ │ ├── Pods-RAImagePicker_Example-RAImagePicker_Tests-acknowledgements.plist
│ │ ├── Pods-RAImagePicker_Example-RAImagePicker_Tests-dummy.m
│ │ ├── Pods-RAImagePicker_Example-RAImagePicker_Tests-frameworks.sh
│ │ ├── Pods-RAImagePicker_Example-RAImagePicker_Tests-resources.sh
│ │ ├── Pods-RAImagePicker_Example-RAImagePicker_Tests-umbrella.h
│ │ ├── Pods-RAImagePicker_Example-RAImagePicker_Tests.debug.xcconfig
│ │ ├── Pods-RAImagePicker_Example-RAImagePicker_Tests.modulemap
│ │ └── Pods-RAImagePicker_Example-RAImagePicker_Tests.release.xcconfig
│ │ ├── Pods-RAImagePicker_Example
│ │ ├── Info.plist
│ │ ├── Pods-RAImagePicker_Example-acknowledgements.markdown
│ │ ├── Pods-RAImagePicker_Example-acknowledgements.plist
│ │ ├── Pods-RAImagePicker_Example-dummy.m
│ │ ├── Pods-RAImagePicker_Example-frameworks.sh
│ │ ├── Pods-RAImagePicker_Example-resources.sh
│ │ ├── Pods-RAImagePicker_Example-umbrella.h
│ │ ├── Pods-RAImagePicker_Example.debug.xcconfig
│ │ ├── Pods-RAImagePicker_Example.modulemap
│ │ └── Pods-RAImagePicker_Example.release.xcconfig
│ │ └── RAImagePicker
│ │ ├── Info.plist
│ │ ├── RAImagePicker-dummy.m
│ │ ├── RAImagePicker-prefix.pch
│ │ ├── RAImagePicker-umbrella.h
│ │ ├── RAImagePicker.modulemap
│ │ ├── RAImagePicker.xcconfig
│ │ └── ResourceBundle-RAImagePicker-Info.plist
├── RAImagePicker.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── RAImagePicker-Example.xcscheme
├── RAImagePicker.xcworkspace
│ └── contents.xcworkspacedata
├── RAImagePicker
│ ├── ActionItemCell.swift
│ ├── ActionItemCell.xib
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ ├── LaunchScreen.xib
│ │ └── Main.storyboard
│ ├── GalleryViewCell.swift
│ ├── GalleryViewCell.xib
│ ├── Images.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── camera.imageset
│ │ │ ├── Contents.json
│ │ │ └── camera.png
│ │ ├── depth.imageset
│ │ │ ├── Contents.json
│ │ │ └── depth.png
│ │ ├── gallery.imageset
│ │ │ ├── Contents.json
│ │ │ └── gallery.png
│ │ ├── live.imageset
│ │ │ ├── Contents.json
│ │ │ └── live.png
│ │ └── panorama.imageset
│ │ │ ├── Contents.json
│ │ │ └── panorama.png
│ ├── Info.plist
│ ├── VideosViewCell.swift
│ ├── VideosViewCell.xib
│ └── ViewController.swift
└── Tests
│ ├── Info.plist
│ └── Tests.swift
├── LICENSE
├── RAImagePicker.podspec
├── RAImagePicker
├── Assets
│ ├── .gitkeep
│ └── Media.xcassets
│ │ ├── Contents.json
│ │ ├── background-rounded.imageset
│ │ ├── Contents.json
│ │ └── background-rounded.pdf
│ │ ├── gradient.imageset
│ │ ├── Contents.json
│ │ ├── gradient.png
│ │ ├── gradient@2x.png
│ │ └── gradient@3x.png
│ │ ├── icon-badge-livephoto.imageset
│ │ ├── Contents.json
│ │ └── icon-badge-livephoto.pdf
│ │ ├── icon-badge-video.imageset
│ │ ├── Contents.json
│ │ └── icon-badge-video.pdf
│ │ ├── icon-camera.imageset
│ │ ├── Contents.json
│ │ ├── icon-camera.png
│ │ ├── icon-camera@2x.png
│ │ └── icon-camera@3x.png
│ │ ├── icon-check-background.imageset
│ │ ├── Contents.json
│ │ └── icon-ckeck-background.pdf
│ │ ├── icon-check.imageset
│ │ ├── Contents.json
│ │ └── icon-check.pdf
│ │ ├── icon-flip-camera.imageset
│ │ ├── Contents.json
│ │ └── flipCamera.pdf
│ │ ├── icon-gallery.imageset
│ │ ├── Contents.json
│ │ ├── icon-gallery.png
│ │ ├── icon-gallery@2x.png
│ │ └── icon-gallery@3x.png
│ │ ├── icon-live-off.imageset
│ │ ├── Contents.json
│ │ └── icon-live-off.pdf
│ │ └── icon-live-on.imageset
│ │ ├── Contents.json
│ │ └── icon-live-on.pdf
└── Classes
│ ├── .gitkeep
│ ├── Media
│ ├── Camera
│ │ └── RAPhotoCaptureDelegate.swift
│ ├── RACaptureSession.swift
│ └── Video
│ │ ├── RAAVPreviewView.swift
│ │ ├── RAVideoCaptureDelegate.swift
│ │ └── RAVideoDataOuptutSampleBufferDelegate.swift
│ ├── Processing
│ ├── RAImagePicker-Bridging-Header.h
│ ├── RAImagePickerAssetModel.swift
│ ├── RAImagePickerDataSource.swift
│ ├── RAImagePickerDelegate.swift
│ ├── RAImagePickerLayout.swift
│ ├── RAImagePickerSelectionPolicy.swift
│ ├── RALayoutModel.swift
│ ├── UIImageEffects.h
│ └── UIImageEffects.m
│ ├── Public
│ ├── Layout
│ │ ├── RAAppearance.swift
│ │ ├── RACameraCollectionViewCell.swift
│ │ ├── RACellRegistrator.swift
│ │ └── RALayoutConfiguration.swift
│ ├── RACaptureSettings.swift
│ └── RAImagePickerController.swift
│ └── Views
│ ├── Cells
│ ├── RAActionCell.swift
│ ├── RAActionCell.xib
│ ├── RAAssetCell.swift
│ ├── RAImagePickerView.swift
│ ├── RAImagePickerView.xib
│ ├── RALivePhotoCameraCell.swift
│ ├── RALivePhotoCameraCell.xib
│ ├── RAVideoCameraCell.swift
│ └── RAVideoCameraCell.xib
│ ├── RACarvedLabel.swift
│ ├── RARecordButton.swift
│ ├── RARecordDurationLabel.swift
│ ├── RAShutterButton.swift
│ └── RAStationaryButton.swift
├── README.md
└── _Pods.xcodeproj
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS X
2 | .DS_Store
3 |
4 | # Xcode
5 | build/
6 | *.pbxuser
7 | !default.pbxuser
8 | *.mode1v3
9 | !default.mode1v3
10 | *.mode2v3
11 | !default.mode2v3
12 | *.perspectivev3
13 | !default.perspectivev3
14 | xcuserdata/
15 | *.xccheckout
16 | profile
17 | *.moved-aside
18 | DerivedData
19 | *.hmap
20 | *.ipa
21 |
22 | # Bundler
23 | .bundle
24 |
25 | Carthage
26 | # We recommend against adding the Pods directory to your .gitignore. However
27 | # you should judge for yourself, the pros and cons are mentioned at:
28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
29 | #
30 | # Note: if you ignore the Pods directory, make sure to uncomment
31 | # `pod install` in .travis.yml
32 | #
33 | # Pods/
34 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 4.0
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # references:
2 | # * http://www.objc.io/issue-6/travis-ci.html
3 | # * https://github.com/supermarin/xcpretty#usage
4 |
5 | osx_image: xcode7.3
6 | language: objective-c
7 | # cache: cocoapods
8 | # podfile: Example/Podfile
9 | # before_install:
10 | # - gem install cocoapods # Since Travis is not always on latest version
11 | # - pod install --project-directory=Example
12 | script:
13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/RAImagePicker.xcworkspace -scheme RAImagePicker-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty
14 | - pod lib lint
15 |
--------------------------------------------------------------------------------
/Example/Podfile:
--------------------------------------------------------------------------------
1 | use_frameworks!
2 |
3 | target 'RAImagePicker_Example' do
4 | pod 'RAImagePicker', :path => '../'
5 |
6 | target 'RAImagePicker_Tests' do
7 |
8 | pod 'RAImagePicker', :path => '../'
9 | end
10 | end
11 |
12 |
--------------------------------------------------------------------------------
/Example/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - RAImagePicker (0.1.0)
3 |
4 | DEPENDENCIES:
5 | - RAImagePicker (from `../`)
6 |
7 | EXTERNAL SOURCES:
8 | RAImagePicker:
9 | :path: ../
10 |
11 | SPEC CHECKSUMS:
12 | RAImagePicker: fd260a8299008bb122eebaae1ce3cba12854637a
13 |
14 | PODFILE CHECKSUM: 754044b7d178675e6b62794ca3c054387173c240
15 |
16 | COCOAPODS: 1.3.1
17 |
--------------------------------------------------------------------------------
/Example/Pods/Local Podspecs/RAImagePicker.podspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "RAImagePicker",
3 | "version": "0.1.0",
4 | "summary": "iMessage-like image picker for iOS +10.0",
5 | "description": "RAImagePicker is a protocol-oriented framework that provides custom features from the built-in Image Picker Edit.",
6 | "homepage": "https://github.com/rallahaseh/RAImagePicker",
7 | "license": {
8 | "type": "MIT",
9 | "file": "LICENSE"
10 | },
11 | "authors": {
12 | "rallahaseh": "rallahaseh@gmail.com"
13 | },
14 | "source": {
15 | "git": "https://github.com/rallahaseh/RAImagePicker.git",
16 | "tag": "0.1.0"
17 | },
18 | "social_media_url": "https://twitter.com/rallahaseh",
19 | "platforms": {
20 | "ios": "10.0"
21 | },
22 | "pod_target_xcconfig": {
23 | "SWIFT_VERSION": "4.0"
24 | },
25 | "source_files": "RAImagePicker/Classes/**/*",
26 | "resources": [
27 | "RAImagePicker/Assets/*"
28 | ],
29 | "frameworks": [
30 | "UIKit",
31 | "Photos",
32 | "Foundation",
33 | "AVFoundation"
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/Example/Pods/Manifest.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - RAImagePicker (0.1.0)
3 |
4 | DEPENDENCIES:
5 | - RAImagePicker (from `../`)
6 |
7 | EXTERNAL SOURCES:
8 | RAImagePicker:
9 | :path: ../
10 |
11 | SPEC CHECKSUMS:
12 | RAImagePicker: fd260a8299008bb122eebaae1ce3cba12854637a
13 |
14 | PODFILE CHECKSUM: 754044b7d178675e6b62794ca3c054387173c240
15 |
16 | COCOAPODS: 1.3.1
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example-RAImagePicker_Tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example-RAImagePicker_Tests/Pods-RAImagePicker_Example-RAImagePicker_Tests-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 |
4 | ## RAImagePicker
5 |
6 | Copyright (c) 2017 rallahaseh
7 |
8 | Permission is hereby granted, free of charge, to any person obtaining a copy
9 | of this software and associated documentation files (the "Software"), to deal
10 | in the Software without restriction, including without limitation the rights
11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | copies of the Software, and to permit persons to whom the Software is
13 | furnished to do so, subject to the following conditions:
14 |
15 | The above copyright notice and this permission notice shall be included in
16 | all copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | THE SOFTWARE.
25 |
26 | Generated by CocoaPods - https://cocoapods.org
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example-RAImagePicker_Tests/Pods-RAImagePicker_Example-RAImagePicker_Tests-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | Copyright (c) 2017 rallahaseh <rallahaseh@gmail.com>
18 |
19 | Permission is hereby granted, free of charge, to any person obtaining a copy
20 | of this software and associated documentation files (the "Software"), to deal
21 | in the Software without restriction, including without limitation the rights
22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | copies of the Software, and to permit persons to whom the Software is
24 | furnished to do so, subject to the following conditions:
25 |
26 | The above copyright notice and this permission notice shall be included in
27 | all copies or substantial portions of the Software.
28 |
29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
35 | THE SOFTWARE.
36 |
37 | License
38 | MIT
39 | Title
40 | RAImagePicker
41 | Type
42 | PSGroupSpecifier
43 |
44 |
45 | FooterText
46 | Generated by CocoaPods - https://cocoapods.org
47 | Title
48 |
49 | Type
50 | PSGroupSpecifier
51 |
52 |
53 | StringsTable
54 | Acknowledgements
55 | Title
56 | Acknowledgements
57 |
58 |
59 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example-RAImagePicker_Tests/Pods-RAImagePicker_Example-RAImagePicker_Tests-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_RAImagePicker_Example_RAImagePicker_Tests : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_RAImagePicker_Example_RAImagePicker_Tests
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example-RAImagePicker_Tests/Pods-RAImagePicker_Example-RAImagePicker_Tests-frameworks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
6 |
7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
8 |
9 | # This protects against multiple targets copying the same framework dependency at the same time. The solution
10 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
11 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
12 |
13 | install_framework()
14 | {
15 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
16 | local source="${BUILT_PRODUCTS_DIR}/$1"
17 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
18 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
19 | elif [ -r "$1" ]; then
20 | local source="$1"
21 | fi
22 |
23 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
24 |
25 | if [ -L "${source}" ]; then
26 | echo "Symlinked..."
27 | source="$(readlink "${source}")"
28 | fi
29 |
30 | # Use filter instead of exclude so missing patterns don't throw errors.
31 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
32 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
33 |
34 | local basename
35 | basename="$(basename -s .framework "$1")"
36 | binary="${destination}/${basename}.framework/${basename}"
37 | if ! [ -r "$binary" ]; then
38 | binary="${destination}/${basename}"
39 | fi
40 |
41 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
42 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
43 | strip_invalid_archs "$binary"
44 | fi
45 |
46 | # Resign the code if required by the build settings to avoid unstable apps
47 | code_sign_if_enabled "${destination}/$(basename "$1")"
48 |
49 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
50 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
51 | local swift_runtime_libs
52 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
53 | for lib in $swift_runtime_libs; do
54 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
55 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
56 | code_sign_if_enabled "${destination}/${lib}"
57 | done
58 | fi
59 | }
60 |
61 | # Copies the dSYM of a vendored framework
62 | install_dsym() {
63 | local source="$1"
64 | if [ -r "$source" ]; then
65 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DWARF_DSYM_FOLDER_PATH}\""
66 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DWARF_DSYM_FOLDER_PATH}"
67 | fi
68 | }
69 |
70 | # Signs a framework with the provided identity
71 | code_sign_if_enabled() {
72 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
73 | # Use the current code_sign_identitiy
74 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
75 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'"
76 |
77 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
78 | code_sign_cmd="$code_sign_cmd &"
79 | fi
80 | echo "$code_sign_cmd"
81 | eval "$code_sign_cmd"
82 | fi
83 | }
84 |
85 | # Strip invalid architectures
86 | strip_invalid_archs() {
87 | binary="$1"
88 | # Get architectures for current file
89 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
90 | stripped=""
91 | for arch in $archs; do
92 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then
93 | # Strip non-valid architectures in-place
94 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1
95 | stripped="$stripped $arch"
96 | fi
97 | done
98 | if [[ "$stripped" ]]; then
99 | echo "Stripped $binary of architectures:$stripped"
100 | fi
101 | }
102 |
103 |
104 | if [[ "$CONFIGURATION" == "Debug" ]]; then
105 | install_framework "${BUILT_PRODUCTS_DIR}/RAImagePicker/RAImagePicker.framework"
106 | fi
107 | if [[ "$CONFIGURATION" == "Release" ]]; then
108 | install_framework "${BUILT_PRODUCTS_DIR}/RAImagePicker/RAImagePicker.framework"
109 | fi
110 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
111 | wait
112 | fi
113 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example-RAImagePicker_Tests/Pods-RAImagePicker_Example-RAImagePicker_Tests-resources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
5 |
6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
7 | > "$RESOURCES_TO_COPY"
8 |
9 | XCASSET_FILES=()
10 |
11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution
12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
14 |
15 | case "${TARGETED_DEVICE_FAMILY}" in
16 | 1,2)
17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
18 | ;;
19 | 1)
20 | TARGET_DEVICE_ARGS="--target-device iphone"
21 | ;;
22 | 2)
23 | TARGET_DEVICE_ARGS="--target-device ipad"
24 | ;;
25 | 3)
26 | TARGET_DEVICE_ARGS="--target-device tv"
27 | ;;
28 | 4)
29 | TARGET_DEVICE_ARGS="--target-device watch"
30 | ;;
31 | *)
32 | TARGET_DEVICE_ARGS="--target-device mac"
33 | ;;
34 | esac
35 |
36 | install_resource()
37 | {
38 | if [[ "$1" = /* ]] ; then
39 | RESOURCE_PATH="$1"
40 | else
41 | RESOURCE_PATH="${PODS_ROOT}/$1"
42 | fi
43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then
44 | cat << EOM
45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
46 | EOM
47 | exit 1
48 | fi
49 | case $RESOURCE_PATH in
50 | *.storyboard)
51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
53 | ;;
54 | *.xib)
55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
57 | ;;
58 | *.framework)
59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
63 | ;;
64 | *.xcdatamodel)
65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true
66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
67 | ;;
68 | *.xcdatamodeld)
69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true
70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
71 | ;;
72 | *.xcmappingmodel)
73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true
74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
75 | ;;
76 | *.xcassets)
77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
79 | ;;
80 | *)
81 | echo "$RESOURCE_PATH" || true
82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
83 | ;;
84 | esac
85 | }
86 |
87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
92 | fi
93 | rm -f "$RESOURCES_TO_COPY"
94 |
95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
96 | then
97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets).
98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
99 | while read line; do
100 | if [[ $line != "${PODS_ROOT}*" ]]; then
101 | XCASSET_FILES+=("$line")
102 | fi
103 | done <<<"$OTHER_XCASSETS"
104 |
105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
106 | fi
107 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example-RAImagePicker_Tests/Pods-RAImagePicker_Example-RAImagePicker_Tests-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double Pods_RAImagePicker_Example_RAImagePicker_TestsVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char Pods_RAImagePicker_Example_RAImagePicker_TestsVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example-RAImagePicker_Tests/Pods-RAImagePicker_Example-RAImagePicker_Tests.debug.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/RAImagePicker"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/RAImagePicker/RAImagePicker.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "RAImagePicker"
7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
8 | PODS_BUILD_DIR = $BUILD_DIR
9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
11 | PODS_ROOT = ${SRCROOT}/Pods
12 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example-RAImagePicker_Tests/Pods-RAImagePicker_Example-RAImagePicker_Tests.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_RAImagePicker_Example_RAImagePicker_Tests {
2 | umbrella header "Pods-RAImagePicker_Example-RAImagePicker_Tests-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example-RAImagePicker_Tests/Pods-RAImagePicker_Example-RAImagePicker_Tests.release.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/RAImagePicker"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/RAImagePicker/RAImagePicker.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "RAImagePicker"
7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
8 | PODS_BUILD_DIR = $BUILD_DIR
9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
11 | PODS_ROOT = ${SRCROOT}/Pods
12 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example/Pods-RAImagePicker_Example-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 |
4 | ## RAImagePicker
5 |
6 | Copyright (c) 2017 rallahaseh
7 |
8 | Permission is hereby granted, free of charge, to any person obtaining a copy
9 | of this software and associated documentation files (the "Software"), to deal
10 | in the Software without restriction, including without limitation the rights
11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | copies of the Software, and to permit persons to whom the Software is
13 | furnished to do so, subject to the following conditions:
14 |
15 | The above copyright notice and this permission notice shall be included in
16 | all copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | THE SOFTWARE.
25 |
26 | Generated by CocoaPods - https://cocoapods.org
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example/Pods-RAImagePicker_Example-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | Copyright (c) 2017 rallahaseh <rallahaseh@gmail.com>
18 |
19 | Permission is hereby granted, free of charge, to any person obtaining a copy
20 | of this software and associated documentation files (the "Software"), to deal
21 | in the Software without restriction, including without limitation the rights
22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | copies of the Software, and to permit persons to whom the Software is
24 | furnished to do so, subject to the following conditions:
25 |
26 | The above copyright notice and this permission notice shall be included in
27 | all copies or substantial portions of the Software.
28 |
29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
35 | THE SOFTWARE.
36 |
37 | License
38 | MIT
39 | Title
40 | RAImagePicker
41 | Type
42 | PSGroupSpecifier
43 |
44 |
45 | FooterText
46 | Generated by CocoaPods - https://cocoapods.org
47 | Title
48 |
49 | Type
50 | PSGroupSpecifier
51 |
52 |
53 | StringsTable
54 | Acknowledgements
55 | Title
56 | Acknowledgements
57 |
58 |
59 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example/Pods-RAImagePicker_Example-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_RAImagePicker_Example : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_RAImagePicker_Example
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example/Pods-RAImagePicker_Example-frameworks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
6 |
7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
8 |
9 | # This protects against multiple targets copying the same framework dependency at the same time. The solution
10 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
11 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
12 |
13 | install_framework()
14 | {
15 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
16 | local source="${BUILT_PRODUCTS_DIR}/$1"
17 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
18 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
19 | elif [ -r "$1" ]; then
20 | local source="$1"
21 | fi
22 |
23 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
24 |
25 | if [ -L "${source}" ]; then
26 | echo "Symlinked..."
27 | source="$(readlink "${source}")"
28 | fi
29 |
30 | # Use filter instead of exclude so missing patterns don't throw errors.
31 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
32 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
33 |
34 | local basename
35 | basename="$(basename -s .framework "$1")"
36 | binary="${destination}/${basename}.framework/${basename}"
37 | if ! [ -r "$binary" ]; then
38 | binary="${destination}/${basename}"
39 | fi
40 |
41 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
42 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
43 | strip_invalid_archs "$binary"
44 | fi
45 |
46 | # Resign the code if required by the build settings to avoid unstable apps
47 | code_sign_if_enabled "${destination}/$(basename "$1")"
48 |
49 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
50 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
51 | local swift_runtime_libs
52 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
53 | for lib in $swift_runtime_libs; do
54 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
55 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
56 | code_sign_if_enabled "${destination}/${lib}"
57 | done
58 | fi
59 | }
60 |
61 | # Copies the dSYM of a vendored framework
62 | install_dsym() {
63 | local source="$1"
64 | if [ -r "$source" ]; then
65 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DWARF_DSYM_FOLDER_PATH}\""
66 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DWARF_DSYM_FOLDER_PATH}"
67 | fi
68 | }
69 |
70 | # Signs a framework with the provided identity
71 | code_sign_if_enabled() {
72 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
73 | # Use the current code_sign_identitiy
74 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
75 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'"
76 |
77 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
78 | code_sign_cmd="$code_sign_cmd &"
79 | fi
80 | echo "$code_sign_cmd"
81 | eval "$code_sign_cmd"
82 | fi
83 | }
84 |
85 | # Strip invalid architectures
86 | strip_invalid_archs() {
87 | binary="$1"
88 | # Get architectures for current file
89 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
90 | stripped=""
91 | for arch in $archs; do
92 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then
93 | # Strip non-valid architectures in-place
94 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1
95 | stripped="$stripped $arch"
96 | fi
97 | done
98 | if [[ "$stripped" ]]; then
99 | echo "Stripped $binary of architectures:$stripped"
100 | fi
101 | }
102 |
103 |
104 | if [[ "$CONFIGURATION" == "Debug" ]]; then
105 | install_framework "${BUILT_PRODUCTS_DIR}/RAImagePicker/RAImagePicker.framework"
106 | fi
107 | if [[ "$CONFIGURATION" == "Release" ]]; then
108 | install_framework "${BUILT_PRODUCTS_DIR}/RAImagePicker/RAImagePicker.framework"
109 | fi
110 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
111 | wait
112 | fi
113 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example/Pods-RAImagePicker_Example-resources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
5 |
6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
7 | > "$RESOURCES_TO_COPY"
8 |
9 | XCASSET_FILES=()
10 |
11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution
12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
14 |
15 | case "${TARGETED_DEVICE_FAMILY}" in
16 | 1,2)
17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
18 | ;;
19 | 1)
20 | TARGET_DEVICE_ARGS="--target-device iphone"
21 | ;;
22 | 2)
23 | TARGET_DEVICE_ARGS="--target-device ipad"
24 | ;;
25 | 3)
26 | TARGET_DEVICE_ARGS="--target-device tv"
27 | ;;
28 | 4)
29 | TARGET_DEVICE_ARGS="--target-device watch"
30 | ;;
31 | *)
32 | TARGET_DEVICE_ARGS="--target-device mac"
33 | ;;
34 | esac
35 |
36 | install_resource()
37 | {
38 | if [[ "$1" = /* ]] ; then
39 | RESOURCE_PATH="$1"
40 | else
41 | RESOURCE_PATH="${PODS_ROOT}/$1"
42 | fi
43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then
44 | cat << EOM
45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
46 | EOM
47 | exit 1
48 | fi
49 | case $RESOURCE_PATH in
50 | *.storyboard)
51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
53 | ;;
54 | *.xib)
55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
57 | ;;
58 | *.framework)
59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
63 | ;;
64 | *.xcdatamodel)
65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true
66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
67 | ;;
68 | *.xcdatamodeld)
69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true
70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
71 | ;;
72 | *.xcmappingmodel)
73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true
74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
75 | ;;
76 | *.xcassets)
77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
79 | ;;
80 | *)
81 | echo "$RESOURCE_PATH" || true
82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
83 | ;;
84 | esac
85 | }
86 |
87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
92 | fi
93 | rm -f "$RESOURCES_TO_COPY"
94 |
95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
96 | then
97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets).
98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
99 | while read line; do
100 | if [[ $line != "${PODS_ROOT}*" ]]; then
101 | XCASSET_FILES+=("$line")
102 | fi
103 | done <<<"$OTHER_XCASSETS"
104 |
105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
106 | fi
107 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example/Pods-RAImagePicker_Example-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double Pods_RAImagePicker_ExampleVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char Pods_RAImagePicker_ExampleVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example/Pods-RAImagePicker_Example.debug.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/RAImagePicker"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/RAImagePicker/RAImagePicker.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "RAImagePicker"
7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
8 | PODS_BUILD_DIR = $BUILD_DIR
9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
11 | PODS_ROOT = ${SRCROOT}/Pods
12 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example/Pods-RAImagePicker_Example.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_RAImagePicker_Example {
2 | umbrella header "Pods-RAImagePicker_Example-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-RAImagePicker_Example/Pods-RAImagePicker_Example.release.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/RAImagePicker"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/RAImagePicker/RAImagePicker.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "RAImagePicker"
7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
8 | PODS_BUILD_DIR = $BUILD_DIR
9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
11 | PODS_ROOT = ${SRCROOT}/Pods
12 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/RAImagePicker/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 0.1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/RAImagePicker/RAImagePicker-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_RAImagePicker : NSObject
3 | @end
4 | @implementation PodsDummy_RAImagePicker
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/RAImagePicker/RAImagePicker-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/RAImagePicker/RAImagePicker-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 | #import "RAImagePicker-Bridging-Header.h"
14 | #import "UIImageEffects.h"
15 |
16 | FOUNDATION_EXPORT double RAImagePickerVersionNumber;
17 | FOUNDATION_EXPORT const unsigned char RAImagePickerVersionString[];
18 |
19 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/RAImagePicker/RAImagePicker.modulemap:
--------------------------------------------------------------------------------
1 | framework module RAImagePicker {
2 | umbrella header "RAImagePicker-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/RAImagePicker/RAImagePicker.xcconfig:
--------------------------------------------------------------------------------
1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/RAImagePicker
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
4 | OTHER_LDFLAGS = -framework "AVFoundation" -framework "Foundation" -framework "Photos" -framework "UIKit"
5 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
6 | PODS_BUILD_DIR = $BUILD_DIR
7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
8 | PODS_ROOT = ${SRCROOT}
9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../..
10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
11 | SKIP_INSTALL = YES
12 | SWIFT_VERSION = 4.0
13 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/RAImagePicker/ResourceBundle-RAImagePicker-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleIdentifier
8 | ${PRODUCT_BUNDLE_IDENTIFIER}
9 | CFBundleInfoDictionaryVersion
10 | 6.0
11 | CFBundleName
12 | ${PRODUCT_NAME}
13 | CFBundlePackageType
14 | BNDL
15 | CFBundleShortVersionString
16 | 0.1.0
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Example/RAImagePicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/RAImagePicker.xcodeproj/xcshareddata/xcschemes/RAImagePicker-Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
45 |
46 |
48 |
54 |
55 |
56 |
57 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
80 |
82 |
88 |
89 |
90 |
91 |
92 |
93 |
99 |
101 |
107 |
108 |
109 |
110 |
112 |
113 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/Example/RAImagePicker.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Example/RAImagePicker/ActionItemCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ActionItemCell.swift
3 | // RAImagePicker_Example
4 | //
5 | // Created by Rashed Al Lahaseh on 12/5/17.
6 | // Copyright © 2017 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ActionItemCell: UICollectionViewCell {
12 |
13 | @IBOutlet weak var title: UILabel!
14 | @IBOutlet weak var icon: UIImageView!
15 | @IBOutlet weak var topOffset: NSLayoutConstraint!
16 | @IBOutlet weak var bottomOffset: NSLayoutConstraint!
17 |
18 | private var originalBC: UIColor?
19 |
20 | override func awakeFromNib() {
21 | super.awakeFromNib()
22 | // Initialization code
23 |
24 | originalBC = backgroundColor
25 | icon.backgroundColor = UIColor.clear
26 | }
27 |
28 | override var isHighlighted: Bool {
29 | didSet {
30 | if isHighlighted {
31 | backgroundColor = UIColor.red
32 | }
33 | else {
34 | backgroundColor = originalBC
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Example/RAImagePicker/ActionItemCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/Example/RAImagePicker/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // RAImagePicker
4 | //
5 | // Created by rallahaseh on 11/29/2017.
6 | // Copyright (c) 2017 rallahaseh. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/Example/RAImagePicker/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Example/RAImagePicker/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/Example/RAImagePicker/GalleryViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GalleryViewCell.swift
3 | // RAImagePicker_Example
4 | //
5 | // Created by Rashed Al Lahaseh on 12/5/17.
6 | // Copyright © 2017 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Photos
11 | import RAImagePicker
12 |
13 | class GalleryViewCell: UICollectionViewCell, RAImagePickerAssetCell {
14 |
15 | @IBOutlet weak var imageView: UIImageView!
16 |
17 | @IBOutlet weak var subtypeImageView: UIImageView!
18 | @IBOutlet weak var selectedView: UIView!
19 |
20 | var representedAssetIdentifier: String?
21 |
22 | override func awakeFromNib() {
23 | super.awakeFromNib()
24 | // Initialization code
25 |
26 | subtypeImageView.backgroundColor = UIColor.clear
27 | selectedView.isHidden = !isSelected
28 | }
29 |
30 | override func prepareForReuse() {
31 | super.prepareForReuse()
32 | // Initialization code
33 |
34 | imageView.image = nil
35 | subtypeImageView.image = nil
36 | }
37 |
38 | override var isSelected: Bool {
39 | didSet {
40 | selectedView.isHidden = !isSelected
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Example/RAImagePicker/GalleryViewCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/Example/RAImagePicker/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ios-marketing",
45 | "size" : "1024x1024",
46 | "scale" : "1x"
47 | }
48 | ],
49 | "info" : {
50 | "version" : 1,
51 | "author" : "xcode"
52 | }
53 | }
--------------------------------------------------------------------------------
/Example/RAImagePicker/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Example/RAImagePicker/Images.xcassets/camera.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "camera.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/RAImagePicker/Images.xcassets/camera.imageset/camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/Example/RAImagePicker/Images.xcassets/camera.imageset/camera.png
--------------------------------------------------------------------------------
/Example/RAImagePicker/Images.xcassets/depth.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "depth.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/RAImagePicker/Images.xcassets/depth.imageset/depth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/Example/RAImagePicker/Images.xcassets/depth.imageset/depth.png
--------------------------------------------------------------------------------
/Example/RAImagePicker/Images.xcassets/gallery.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "gallery.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/RAImagePicker/Images.xcassets/gallery.imageset/gallery.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/Example/RAImagePicker/Images.xcassets/gallery.imageset/gallery.png
--------------------------------------------------------------------------------
/Example/RAImagePicker/Images.xcassets/live.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "live.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/RAImagePicker/Images.xcassets/live.imageset/live.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/Example/RAImagePicker/Images.xcassets/live.imageset/live.png
--------------------------------------------------------------------------------
/Example/RAImagePicker/Images.xcassets/panorama.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "panorama.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/RAImagePicker/Images.xcassets/panorama.imageset/panorama.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/Example/RAImagePicker/Images.xcassets/panorama.imageset/panorama.png
--------------------------------------------------------------------------------
/Example/RAImagePicker/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 |
38 | NSCameraUsageDescription
39 | Need Access to Camera to Capture Photos and Record Videos
40 | NSMicrophoneUsageDescription
41 | Need Access to Microphone for Recording Videos
42 | NSPhotoLibraryUsageDescription
43 | Need Access to Photos for Picking Images
44 |
45 |
46 |
--------------------------------------------------------------------------------
/Example/RAImagePicker/VideosViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VideosViewCell.swift
3 | // RAImagePicker_Example
4 | //
5 | // Created by Rashed Al Lahaseh on 12/5/17.
6 | // Copyright © 2017 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class VideosViewCell: UICollectionViewCell {
12 |
13 | @IBOutlet weak var imageView: UIImageView!
14 | @IBOutlet weak var label: UILabel!
15 |
16 | var representedAssetIdentifier: String?
17 |
18 | override func awakeFromNib() {
19 | super.awakeFromNib()
20 | // Initialization code
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/Example/RAImagePicker/VideosViewCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/Example/Tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Example/Tests/Tests.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import XCTest
3 | import RAImagePicker
4 |
5 | class Tests: XCTestCase {
6 |
7 | override func setUp() {
8 | super.setUp()
9 | // Put setup code here. This method is called before the invocation of each test method in the class.
10 | }
11 |
12 | override func tearDown() {
13 | // Put teardown code here. This method is called after the invocation of each test method in the class.
14 | super.tearDown()
15 | }
16 |
17 | func testExample() {
18 | // This is an example of a functional test case.
19 | XCTAssert(true, "Pass")
20 | }
21 |
22 | func testPerformanceExample() {
23 | // This is an example of a performance test case.
24 | self.measure() {
25 | // Put the code you want to measure the time of here.
26 | }
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 rallahaseh
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/RAImagePicker.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod lib lint RAImagePicker.podspec' to ensure this is a
3 | # valid spec before submitting.
4 | #
5 | # Any lines starting with a # are optional, but their use is encouraged
6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
7 | #
8 |
9 | Pod::Spec.new do |s|
10 | s.name = 'RAImagePicker'
11 | s.version = '0.1.0'
12 | s.summary = 'iMessage-like image picker for iOS +10.0'
13 | s.description = <<-DESC
14 | RAImagePicker is a protocol-oriented framework that provides custom features from the built-in Image Picker Edit.
15 | DESC
16 | s.homepage = 'https://github.com/rallahaseh/RAImagePicker'
17 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
18 | s.license = { :type => 'MIT', :file => 'LICENSE' }
19 | s.author = { 'rallahaseh' => 'rallahaseh@gmail.com' }
20 | s.source = { :git => 'https://github.com/rallahaseh/RAImagePicker.git', :tag => s.version.to_s }
21 | s.social_media_url = 'https://twitter.com/rallahaseh'
22 |
23 | s.ios.deployment_target = '10.0'
24 |
25 | s.pod_target_xcconfig = { 'SWIFT_VERSION' => '4.0' }
26 |
27 | s.source_files = 'RAImagePicker/Classes/**/*'
28 |
29 | s.resources = ['RAImagePicker/Assets/*']
30 |
31 | s.frameworks = 'UIKit', 'Photos', 'Foundation', 'AVFoundation'
32 |
33 | end
34 |
--------------------------------------------------------------------------------
/RAImagePicker/Assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/.gitkeep
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/background-rounded.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "background-rounded.pdf",
6 | "resizing" : {
7 | "mode" : "9-part",
8 | "center" : {
9 | "mode" : "tile",
10 | "width" : 1,
11 | "height" : 1
12 | },
13 | "cap-insets" : {
14 | "bottom" : 12,
15 | "top" : 12,
16 | "right" : 12,
17 | "left" : 12
18 | }
19 | }
20 | }
21 | ],
22 | "info" : {
23 | "version" : 1,
24 | "author" : "xcode"
25 | },
26 | "properties" : {
27 | "template-rendering-intent" : "template"
28 | }
29 | }
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/background-rounded.imageset/background-rounded.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/Media.xcassets/background-rounded.imageset/background-rounded.pdf
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/gradient.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "gradient.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "gradient@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "gradient@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | },
23 | "properties" : {
24 | "compression-type" : "gpu-optimized-smallest",
25 | "template-rendering-intent" : "original"
26 | }
27 | }
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/gradient.imageset/gradient.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/Media.xcassets/gradient.imageset/gradient.png
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/gradient.imageset/gradient@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/Media.xcassets/gradient.imageset/gradient@2x.png
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/gradient.imageset/gradient@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/Media.xcassets/gradient.imageset/gradient@3x.png
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-badge-livephoto.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-badge-livephoto.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | },
12 | "properties" : {
13 | "template-rendering-intent" : "template"
14 | }
15 | }
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-badge-livephoto.imageset/icon-badge-livephoto.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/Media.xcassets/icon-badge-livephoto.imageset/icon-badge-livephoto.pdf
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-badge-video.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-badge-video.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | },
12 | "properties" : {
13 | "template-rendering-intent" : "template"
14 | }
15 | }
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-badge-video.imageset/icon-badge-video.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/Media.xcassets/icon-badge-video.imageset/icon-badge-video.pdf
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-camera.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-camera.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-camera@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "icon-camera@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-camera.imageset/icon-camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/Media.xcassets/icon-camera.imageset/icon-camera.png
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-camera.imageset/icon-camera@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/Media.xcassets/icon-camera.imageset/icon-camera@2x.png
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-camera.imageset/icon-camera@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/Media.xcassets/icon-camera.imageset/icon-camera@3x.png
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-check-background.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-ckeck-background.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | },
12 | "properties" : {
13 | "template-rendering-intent" : "original"
14 | }
15 | }
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-check-background.imageset/icon-ckeck-background.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/Media.xcassets/icon-check-background.imageset/icon-ckeck-background.pdf
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-check.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-check.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | },
12 | "properties" : {
13 | "template-rendering-intent" : "template"
14 | }
15 | }
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-check.imageset/icon-check.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/Media.xcassets/icon-check.imageset/icon-check.pdf
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-flip-camera.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "flipCamera.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | },
12 | "properties" : {
13 | "template-rendering-intent" : "template"
14 | }
15 | }
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-flip-camera.imageset/flipCamera.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/Media.xcassets/icon-flip-camera.imageset/flipCamera.pdf
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-gallery.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-gallery.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-gallery@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "icon-gallery@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-gallery.imageset/icon-gallery.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/Media.xcassets/icon-gallery.imageset/icon-gallery.png
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-gallery.imageset/icon-gallery@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/Media.xcassets/icon-gallery.imageset/icon-gallery@2x.png
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-gallery.imageset/icon-gallery@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/Media.xcassets/icon-gallery.imageset/icon-gallery@3x.png
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-live-off.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-live-off.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | },
12 | "properties" : {
13 | "template-rendering-intent" : "template"
14 | }
15 | }
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-live-off.imageset/icon-live-off.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/Media.xcassets/icon-live-off.imageset/icon-live-off.pdf
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-live-on.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-live-on.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | },
12 | "properties" : {
13 | "template-rendering-intent" : "template"
14 | }
15 | }
--------------------------------------------------------------------------------
/RAImagePicker/Assets/Media.xcassets/icon-live-on.imageset/icon-live-on.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Assets/Media.xcassets/icon-live-on.imageset/icon-live-on.pdf
--------------------------------------------------------------------------------
/RAImagePicker/Classes/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rallahaseh/RAImagePicker/b6109e774856ba4c31408c1b68f24146f32e101b/RAImagePicker/Classes/.gitkeep
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Media/Camera/RAPhotoCaptureDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RAPhotoCaptureDelegate.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 11/29/17.
6 | //
7 |
8 | import AVFoundation
9 | import Photos
10 |
11 | final class RAPhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate {
12 |
13 | // MARK: - Public Methods
14 |
15 | /// set this to false if you dont wish to save taken picture to photo library
16 | var savesPhotoToLibrary = true
17 |
18 | /// this contains photo data when taken
19 | private(set) var photoData: Data? = nil
20 |
21 | private(set) var requestedPhotoSettings: AVCapturePhotoSettings
22 |
23 | /// not nil if error occured during capturing
24 | private(set) var processError: Error?
25 |
26 | // MARK: Private Methods
27 |
28 | private let willCapturePhotoAnimation: () -> ()
29 | private let capturingLivePhoto: (Bool) -> ()
30 | private let completed: (RAPhotoCaptureDelegate) -> ()
31 | private var livePhotoCompanionMovieURL: URL? = nil
32 |
33 | init(with requestedPhotoSettings: AVCapturePhotoSettings, willCapturePhotoAnimation: @escaping () -> (), capturingLivePhoto: @escaping (Bool) -> (), completed: @escaping (RAPhotoCaptureDelegate) -> ()) {
34 | self.requestedPhotoSettings = requestedPhotoSettings
35 | self.willCapturePhotoAnimation = willCapturePhotoAnimation
36 | self.capturingLivePhoto = capturingLivePhoto
37 | self.completed = completed
38 | }
39 |
40 | private func didFinish() {
41 | if let livePhotoCompanionMoviePath = livePhotoCompanionMovieURL?.path {
42 | if FileManager.default.fileExists(atPath: livePhotoCompanionMoviePath) {
43 | do {
44 | try FileManager.default.removeItem(atPath: livePhotoCompanionMoviePath)
45 | }
46 | catch {
47 | print("photo capture delegate: Could not remove file at url: \(livePhotoCompanionMoviePath)")
48 | }
49 | }
50 | }
51 |
52 | completed(self)
53 | }
54 |
55 | func photoOutput(_ captureOutput: AVCapturePhotoOutput, willBeginCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings) {
56 | if resolvedSettings.livePhotoMovieDimensions.width > 0 && resolvedSettings.livePhotoMovieDimensions.height > 0 {
57 | capturingLivePhoto(true)
58 | }
59 | }
60 |
61 | func photoOutput(_ captureOutput: AVCapturePhotoOutput, willCapturePhotoFor resolvedSettings: AVCaptureResolvedPhotoSettings) {
62 | willCapturePhotoAnimation()
63 | }
64 |
65 | @available(iOS 10.0, *)
66 | func photoOutput(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhoto photoSampleBuffer: CMSampleBuffer?, previewPhoto previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?) {
67 | if let photoSampleBuffer = photoSampleBuffer {
68 | photoData = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: photoSampleBuffer, previewPhotoSampleBuffer: previewPhotoSampleBuffer)
69 | }
70 | else if let error = error {
71 | print("photo capture delegate: error capturing photo: \(error)")
72 | processError = error
73 | return
74 | }
75 | }
76 |
77 | @available(iOS 10.0, *)
78 | func photoOutput(_ captureOutput: AVCapturePhotoOutput, didFinishRecordingLivePhotoMovieForEventualFileAt outputFileURL: URL, resolvedSettings: AVCaptureResolvedPhotoSettings) {
79 | capturingLivePhoto(false)
80 | }
81 |
82 | @available(iOS 10.0, *)
83 | func photoOutput(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingLivePhotoToMovieFileAt outputFileURL: URL, duration: CMTime, photoDisplayTime: CMTime, resolvedSettings: AVCaptureResolvedPhotoSettings, error: Error?) {
84 | if let error = error {
85 | print("photo capture delegate: error processing live photo companion movie: \(error)")
86 | return
87 | }
88 |
89 | livePhotoCompanionMovieURL = outputFileURL
90 | }
91 |
92 | @available(iOS 10.0, *)
93 | func photoOutput(_ output: AVCapturePhotoOutput, didCapturePhotoFor resolvedSettings: AVCaptureResolvedPhotoSettings, error: Error?) {
94 |
95 | if let error = error {
96 | print("photo capture delegate: Error capturing photo: \(error)")
97 | didFinish()
98 | return
99 | }
100 |
101 | guard let photoData = photoData else {
102 | print("photo capture delegate: No photo data resource")
103 | didFinish()
104 | return
105 | }
106 |
107 | guard savesPhotoToLibrary == true else {
108 | print("photo capture delegate: photo did finish without saving to photo library")
109 | didFinish()
110 | return
111 | }
112 |
113 | PHPhotoLibrary.requestAuthorization { [unowned self] status in
114 | if status == .authorized {
115 | PHPhotoLibrary.shared().performChanges({ [unowned self] in
116 | let creationRequest = PHAssetCreationRequest.forAsset()
117 | creationRequest.addResource(with: .photo, data: photoData, options: nil)
118 |
119 | if let livePhotoCompanionMovieURL = self.livePhotoCompanionMovieURL {
120 | let livePhotoCompanionMovieFileResourceOptions = PHAssetResourceCreationOptions()
121 | livePhotoCompanionMovieFileResourceOptions.shouldMoveFile = true
122 | creationRequest.addResource(with: .pairedVideo, fileURL: livePhotoCompanionMovieURL, options: livePhotoCompanionMovieFileResourceOptions)
123 | }
124 |
125 | }, completionHandler: { [unowned self] success, error in
126 | if let error = error {
127 | print("photo capture delegate: Error occurered while saving photo to photo library: \(error)")
128 | }
129 | self.didFinish()
130 | }
131 | )
132 | }
133 | else {
134 | self.didFinish()
135 | }
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Media/Video/RAAVPreviewView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RAAVPreviewView.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 11/29/17.
6 | //
7 |
8 | import UIKit
9 | import Foundation
10 | import AVFoundation
11 |
12 | enum VideoDisplayMode {
13 |
14 | // Preserve aspect ration, fit within layer bounds
15 | case aspectFit
16 | // Preserve aspect ratio, fill view bounds
17 | case aspectFill
18 | // Stretch to fill layer bounds
19 | case resize
20 | }
21 |
22 | // View whose layer is AVCaptureVideoPreviewLayer to preview the output of a captured session
23 | final class AVPreviewView: UIView {
24 |
25 | // MARK: - Public Methods
26 |
27 | var previewLayer: AVCaptureVideoPreviewLayer {
28 | return layer as! AVCaptureVideoPreviewLayer
29 | }
30 |
31 | var session: AVCaptureSession? {
32 | get {
33 | return previewLayer.session
34 | }
35 | set {
36 | if previewLayer.session === newValue {
37 | return
38 | }
39 | previewLayer.session = newValue
40 | }
41 | }
42 |
43 | var displayMode: VideoDisplayMode = .aspectFill {
44 | didSet {
45 | applyVideoDisplayMode()
46 | }
47 | }
48 |
49 | override class var layerClass: AnyClass {
50 | return AVCaptureVideoPreviewLayer.self
51 | }
52 |
53 | override init(frame: CGRect) {
54 | super.init(frame: frame)
55 | applyVideoDisplayMode()
56 | }
57 |
58 | required init?(coder aDecoder: NSCoder) {
59 | super.init(coder: aDecoder)
60 | applyVideoDisplayMode()
61 | }
62 |
63 | // MARK: - Private Methods
64 |
65 | private func applyVideoDisplayMode() {
66 | switch displayMode {
67 | case .aspectFill: previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
68 | case .aspectFit: previewLayer.videoGravity = AVLayerVideoGravity.resizeAspect
69 | case .resize: previewLayer.videoGravity = AVLayerVideoGravity.resize
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Media/Video/RAVideoCaptureDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RAVideoCaptureDelegate.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 11/29/17.
6 | //
7 |
8 | import AVFoundation
9 | import Photos
10 |
11 | final class RAVideoCaptureDelegate: NSObject, AVCaptureFileOutputRecordingDelegate {
12 |
13 | // MARK: - Public Methods
14 |
15 | // Save videos to library by default true
16 | var savesVideoToLibrary = true
17 |
18 | // Flag to detect when canceling recording(stope without saving)
19 | var isBeingCancelled = false
20 |
21 | // Flag to detect when system inturrupts recording due to various reasons (phone call, background, empty space, ...)
22 | var recordingWasInterrupted = false
23 |
24 | // Recording errors (nil if cancelled and none if failed or interrupted)
25 | private(set) var recordingError: Error?
26 |
27 | init(didStart: @escaping ()->(), didFinish: @escaping (RAVideoCaptureDelegate)->(), didFail: @escaping (RAVideoCaptureDelegate, Error)->()) {
28 | self.didStart = didStart
29 | self.didFinish = didFinish
30 | self.didFail = didFail
31 |
32 | if UIDevice.current.isMultitaskingSupported {
33 | /*
34 | Setup background task.
35 | - This is needed because the `capture(_:, didFinishRecordingToOutputFileAt:, fromConnections:, error:)`
36 | callback is not received until AVCam returns to the foreground unless you request background
37 | execution time.
38 | - This also ensures that there will be time to write the file to the photo library when
39 | AVCam is backgrounded.
40 | - To conclude this background execution, endBackgroundTask(_:) is called in
41 | `capture(_:, didFinishRecordingToOutputFileAt:, fromConnections:, error:)`
42 | after the recorded file has been saved.
43 | */
44 | self.backgroundRecordingID = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
45 | }
46 | }
47 |
48 | // MARK: - Private Methods
49 |
50 | private var backgroundRecordingID: UIBackgroundTaskIdentifier? = nil
51 | private var didStart: ()->()
52 | private var didFinish: (RAVideoCaptureDelegate)->()
53 | private var didFail: (RAVideoCaptureDelegate, Error)->()
54 |
55 | private func cleanUp(deleteFile: Bool, saveToAssets: Bool, outputFileURL: URL) {
56 |
57 | func deleteFileIfNeeded() {
58 | guard deleteFile == true else { return }
59 | let path = outputFileURL.path
60 | if FileManager.default.fileExists(atPath: path) {
61 | do {
62 | try FileManager.default.removeItem(atPath: path)
63 | }
64 | catch let error {
65 | print("capture session: could not remove recording at url: \(outputFileURL)")
66 | print("capture session: error: \(error)")
67 | }
68 | }
69 | }
70 |
71 | if let currentBackgroundRecordingID = backgroundRecordingID {
72 | backgroundRecordingID = UIBackgroundTaskInvalid
73 | if currentBackgroundRecordingID != UIBackgroundTaskInvalid {
74 | UIApplication.shared.endBackgroundTask(currentBackgroundRecordingID)
75 | }
76 | }
77 |
78 | if saveToAssets {
79 | PHPhotoLibrary.requestAuthorization { status in
80 | if status == .authorized {
81 | PHPhotoLibrary.shared().performChanges({
82 | let creationRequest = PHAssetCreationRequest.forAsset()
83 | let videoResourceOptions = PHAssetResourceCreationOptions()
84 | videoResourceOptions.shouldMoveFile = true
85 | creationRequest.addResource(with: .video, fileURL: outputFileURL, options: videoResourceOptions)
86 | }, completionHandler: { success, error in
87 | if let error = error {
88 | print("capture session: Error occurered while saving video to photo library: \(error)")
89 | deleteFileIfNeeded()
90 | }
91 | })
92 | }
93 | else {
94 | deleteFileIfNeeded()
95 | }
96 | }
97 | }
98 | else {
99 | deleteFileIfNeeded()
100 | }
101 | }
102 |
103 | // MARK: - AVCaptureFileOutputRecordingDelegate Methods
104 |
105 | func fileOutput(_ captureOutput: AVCaptureFileOutput, didStartRecordingTo fileURL: URL, from connections: [AVCaptureConnection]) {
106 | didStart()
107 | }
108 |
109 | func fileOutput(_ captureOutput: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
110 |
111 | if let error = error {
112 |
113 | recordingError = error
114 |
115 | print("capture session: movie recording failed error: \(error)")
116 |
117 | // Flag to check the video can be delivered even if the recording stopped due to a reason (no space, ..)
118 | let successfullyFinished = (((error as NSError).userInfo[AVErrorRecordingSuccessfullyFinishedKey] as AnyObject).boolValue) ?? false
119 |
120 | if successfullyFinished {
121 | recordingWasInterrupted = true
122 | cleanUp(deleteFile: true, saveToAssets: savesVideoToLibrary, outputFileURL: outputFileURL)
123 | didFail(self, error)
124 | }
125 | else {
126 | cleanUp(deleteFile: true, saveToAssets: false, outputFileURL: outputFileURL)
127 | didFail(self, error)
128 | }
129 | }
130 | else if isBeingCancelled == true {
131 | cleanUp(deleteFile: true, saveToAssets: false, outputFileURL: outputFileURL)
132 | didFinish(self)
133 | }
134 | else {
135 | cleanUp(deleteFile: true, saveToAssets: savesVideoToLibrary, outputFileURL: outputFileURL)
136 | didFinish(self)
137 | }
138 | }
139 | }
140 |
141 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Media/Video/RAVideoDataOuptutSampleBufferDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RAVideoDataOuptutSampleBufferDelegate.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 11/29/17.
6 | //
7 |
8 | import Foundation
9 | import AVFoundation
10 |
11 |
12 | // Note: if video file output is provided, video data output is not working!!! there must be only 1 output at the same time
13 | final class RAVideoOutputSampleBufferDelegate : NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {
14 |
15 | let processQueue = DispatchQueue(label: "video-output-sample-buffer-delegate.queue")
16 |
17 | var latestImage: UIImage? {
18 | return latestSampleBuffer?.imageRepresentation
19 | }
20 |
21 | private var latestSampleBuffer: CMSampleBuffer?
22 |
23 | @nonobjc func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
24 | latestSampleBuffer = sampleBuffer
25 | }
26 |
27 | @nonobjc func captureOutput(captureOutput: AVCaptureFileOutput, didFinishRecordingToOutputFileAtURL outputFileURL: NSURL, fromConnections connections: [AnyObject], error: NSError!) {
28 | }
29 | }
30 |
31 | extension CMSampleBuffer {
32 |
33 | static let context = CIContext(options: [kCIContextUseSoftwareRenderer: false])
34 |
35 | // Converts sample buffer to UIImage with backing CGImage
36 | fileprivate var imageRepresentation: UIImage? {
37 |
38 | guard let pixelBuffer = CMSampleBufferGetImageBuffer(self) else {
39 | return nil
40 | }
41 |
42 | let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
43 |
44 | // Down Scale Image
45 | let filter = CIFilter(name: "CILanczosScaleTransform")!
46 | filter.setValue(ciImage, forKey: "inputImage")
47 | filter.setValue(0.25, forKey: "inputScale")
48 | filter.setValue(1.0, forKey: "inputAspectRatio")
49 | let resizedCiImage = filter.value(forKey: "outputImage") as! CIImage
50 |
51 |
52 | // We need to convert CIImage to CGImage because we are using Apples blurring functions
53 | // This conversion is very expensive, use it only when you really need it
54 | if let cgImage = CMSampleBuffer.context.createCGImage(resizedCiImage, from: resizedCiImage.extent) {
55 | return UIImage(cgImage: cgImage)
56 | }
57 |
58 | return nil
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Processing/RAImagePicker-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Header.h
3 | // Pods
4 | //
5 | // Created by Rashed Al Lahaseh on 12/6/17.
6 | //
7 |
8 | #import
9 |
10 | #import "UIImageEffects.h"
11 |
12 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Processing/RAImagePickerAssetModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RAImagePickerAssetModel.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 12/1/17.
6 | //
7 |
8 | import Foundation
9 | import Photos
10 |
11 |
12 | // Model that is used when accessing an caching PHAsset objects
13 | final class RAImagePickerAssetModel {
14 |
15 | var fetchResult: PHFetchResult! {
16 | set { userDefinedFetchResult = newValue }
17 | get { return userDefinedFetchResult ?? defaultFetchResult }
18 | }
19 |
20 | lazy var imageManager = PHCachingImageManager()
21 | var thumbnailSize: CGSize?
22 |
23 | /*
24 | Tries to access smart album .smartAlbumUserLibrary that should be `Camera Roll` and uses just fetchAssets as fallback
25 | */
26 | private lazy var defaultFetchResult: PHFetchResult = {
27 |
28 | let assetsOptions = PHFetchOptions()
29 | assetsOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
30 | assetsOptions.fetchLimit = 1000
31 |
32 | let collections = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumUserLibrary, options: nil)
33 | if let cameraRoll = collections.firstObject {
34 | return PHAsset.fetchAssets(in: cameraRoll, options: assetsOptions)
35 | }
36 | else {
37 | return PHAsset.fetchAssets(with: assetsOptions)
38 | }
39 | }()
40 |
41 | private var userDefinedFetchResult: PHFetchResult?
42 |
43 | //will be use for caching
44 | var previousPreheatRect = CGRect.zero
45 |
46 | func updateCachedAssets(collectionView: UICollectionView) {
47 |
48 | // Paradoxly, using this precaching the scrolling of images is more laggy than if there is no precaching
49 |
50 | guard let thumbnailSize = thumbnailSize else {
51 | return print("Asset Model: update cache assets - thumbnail size is nil")
52 | }
53 |
54 | guard let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
55 | return print("Asset Model: update cache assets - collection view layout is not flow layout")
56 | }
57 |
58 | // The preheat window is twice the height of the visible rect.
59 | let visibleRect = CGRect(origin: collectionView.contentOffset, size: collectionView.bounds.size)
60 |
61 | var preheatRect: CGRect
62 |
63 | switch layout.scrollDirection {
64 | case .vertical:
65 |
66 | preheatRect = visibleRect.insetBy(dx: 0, dy: -0.75 * visibleRect.height)
67 |
68 | // Update only if the visible area is significantly different from the last preheated area.
69 | let delta = abs(preheatRect.midY - previousPreheatRect.midY)
70 | guard delta > collectionView.bounds.height / 3 else {
71 | return
72 | }
73 |
74 | case .horizontal:
75 |
76 | preheatRect = visibleRect.insetBy(dx: -0.75 * visibleRect.width, dy: 0)
77 |
78 | // Update only if the visible area is significantly different from the last preheated area.
79 | let delta = abs(preheatRect.midX - previousPreheatRect.midX)
80 | guard delta > collectionView.bounds.width / 3 else {
81 | return
82 | }
83 | }
84 |
85 | // Compute the assets to start caching and to stop caching.
86 | let (addedRects, removedRects) = differencesBetweenRects(previousPreheatRect, preheatRect, layout.scrollDirection)
87 | let addedAssets = addedRects
88 | .flatMap { rect in collectionView.indexPathsForElements(in: rect) }
89 | .map { indexPath in fetchResult.object(at: indexPath.item) }
90 | let removedAssets = removedRects
91 | .flatMap { rect in collectionView.indexPathsForElements(in: rect) }
92 | .map { indexPath in fetchResult.object(at: indexPath.item) }
93 |
94 | // Update the assets the PHCachingImageManager is caching.
95 | imageManager.startCachingImages(for: addedAssets, targetSize: thumbnailSize, contentMode: .aspectFill, options: nil)
96 | debugPrint("Asset Model: caching, size \(thumbnailSize), preheat rect \(preheatRect), items \(addedAssets.count)")
97 |
98 | imageManager.stopCachingImages(for: removedAssets, targetSize: thumbnailSize, contentMode: .aspectFill, options: nil)
99 | debugPrint("Asset Model: uncaching, preheat rect \(preheatRect), items \(removedAssets.count)")
100 |
101 | // Store the preheat rect to compare against in the future.
102 | previousPreheatRect = preheatRect
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Processing/RAImagePickerDataSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RAImagePickerDataSource.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 12/1/17.
6 | //
7 |
8 | import Foundation
9 | import Photos
10 |
11 |
12 | // Datasource for a collection view that is used by RAImagePickerViewController.
13 |
14 | final class RAImagePickerDataSource : NSObject, UICollectionViewDataSource {
15 |
16 | var layoutModel = RALayoutModel.empty
17 | var cellRegistrator: RACellRegistrator?
18 | var assetsModel: RAImagePickerAssetModel
19 |
20 | init(assetsModel: RAImagePickerAssetModel) {
21 | self.assetsModel = assetsModel
22 | super.init()
23 | }
24 |
25 | func numberOfSections(in collectionView: UICollectionView) -> Int {
26 | return layoutModel.numberOfSections
27 | }
28 |
29 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
30 | return layoutModel.numberOfItems(in: section)
31 | }
32 |
33 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
34 |
35 | guard let cellsRegistrator = cellRegistrator else {
36 | fatalError("cells registrator must be set at this moment")
37 | }
38 |
39 | //TODO: change these hardcoded section numbers to those defined in layoutModel.layoutConfiguration
40 | switch indexPath.section {
41 | case 0:
42 | guard let id = cellsRegistrator.cellIdentifier(forActionItemAt: indexPath.row) else {
43 | fatalError("there is an action item at index \(indexPath.row) but no cell is registered.")
44 | }
45 | return collectionView.dequeueReusableCell(withReuseIdentifier: id, for: indexPath)
46 |
47 | case 1:
48 | let id = cellsRegistrator.cameraItemIdentifierPrefix
49 | guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: id, for: indexPath) as? RACameraCollectionViewCell else {
50 | fatalError("there is a camera item but no cell class `CameraCollectionViewCell` is registered.")
51 | }
52 | return cell
53 |
54 | case 2:
55 |
56 | let asset = assetsModel.fetchResult.object(at: indexPath.item)
57 | let cellId = cellsRegistrator.cellIdentifier(forAsset: asset.mediaType) ?? cellsRegistrator.cellIdentifierForAssetItems
58 |
59 | guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as? RAImagePickerAssetCell else {
60 | fatalError("asset item cell must conform to \(RAImagePickerAssetCell.self) protocol")
61 | }
62 |
63 | let thumbnailSize = assetsModel.thumbnailSize ?? .zero
64 |
65 | // Request an image for the asset from the PHCachingImageManager.
66 | cell.representedAssetIdentifier = asset.localIdentifier
67 | assetsModel.imageManager.requestImage(for: asset, targetSize: thumbnailSize, contentMode: .aspectFill, options: nil, resultHandler: { image, _ in
68 | // The cell may have been recycled by the time this handler gets called;
69 | // set the cell's thumbnail image only if it's still showing the same asset.
70 | if cell.representedAssetIdentifier == asset.localIdentifier && image != nil {
71 | cell.imageView.image = image
72 | }
73 | })
74 |
75 | return cell as! UICollectionViewCell
76 |
77 | default: fatalError("only 3 sections are supported")
78 | }
79 | }
80 |
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Processing/RAImagePickerDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RAImagePickerDelegate.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 12/1/17.
6 | //
7 |
8 | import Foundation
9 |
10 | protocol ImagePickerDelegate : class {
11 |
12 | // Selects one of action items
13 | func imagePicker(delegate: RAImagePickerDelegate, didSelectActionItemAt index: Int)
14 |
15 | // Selects one of asset items
16 | func imagePicker(delegate: RAImagePickerDelegate, didSelectAssetItemAt index: Int)
17 |
18 | // Deselects one of selected asset items
19 | func imagePicker(delegate: RAImagePickerDelegate, didDeselectAssetItemAt index: Int)
20 |
21 | // Action item is about to be displayed
22 | func imagePicker(delegate: RAImagePickerDelegate, willDisplayActionCell cell: UICollectionViewCell, at index: Int)
23 |
24 | // Camera item is about to be displayed
25 | func imagePicker(delegate: RAImagePickerDelegate, willDisplayCameraCell cell: RACameraCollectionViewCell)
26 |
27 | // Camera item ended displaying
28 | func imagePicker(delegate: RAImagePickerDelegate, didEndDisplayingCameraCell cell: RACameraCollectionViewCell)
29 |
30 | func imagePicker(delegate: RAImagePickerDelegate, willDisplayAssetCell cell: RAImagePickerAssetCell, at index: Int)
31 |
32 | func imagePicker(delegate: RAImagePickerDelegate, didScroll scrollView: UIScrollView)
33 | }
34 |
35 | final class RAImagePickerDelegate : NSObject, UICollectionViewDelegateFlowLayout {
36 |
37 | var layout: RAImagePickerLayout?
38 | weak var delegate: ImagePickerDelegate?
39 |
40 | private let selectionPolicy = RAImagePickerSelectionPolicy()
41 |
42 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
43 | return layout?.collectionView(collectionView, layout: collectionViewLayout, sizeForItemAt: indexPath) ?? .zero
44 | }
45 |
46 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
47 | return layout?.collectionView(collectionView, layout: collectionViewLayout, insetForSectionAt: section) ?? .zero
48 | }
49 |
50 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
51 | if indexPath.section == layout?.configuration.sectionIndexForAssets {
52 | delegate?.imagePicker(delegate: self, didSelectAssetItemAt: indexPath.row)
53 | }
54 | }
55 |
56 | func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
57 | if indexPath.section == layout?.configuration.sectionIndexForAssets {
58 | delegate?.imagePicker(delegate: self, didDeselectAssetItemAt: indexPath.row)
59 | }
60 | }
61 |
62 | func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
63 | guard let configuration = layout?.configuration else { return false }
64 | return selectionPolicy.shouldSelectItem(atSection: indexPath.section, layoutConfiguration: configuration)
65 | }
66 |
67 | func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
68 | guard let configuration = layout?.configuration else { return false }
69 | return selectionPolicy.shouldHighlightItem(atSection: indexPath.section, layoutConfiguration: configuration)
70 | }
71 |
72 | func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath) {
73 | if indexPath.section == layout?.configuration.sectionIndexForActions {
74 | delegate?.imagePicker(delegate: self, didSelectActionItemAt: indexPath.row)
75 | }
76 | }
77 |
78 | func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
79 |
80 | guard let configuration = layout?.configuration else { return }
81 |
82 | switch indexPath.section {
83 | case configuration.sectionIndexForActions: delegate?.imagePicker(delegate: self, willDisplayActionCell: cell, at: indexPath.row)
84 | case configuration.sectionIndexForCamera: delegate?.imagePicker(delegate: self, willDisplayCameraCell: cell as! RACameraCollectionViewCell)
85 | case configuration.sectionIndexForAssets: delegate?.imagePicker(delegate: self, willDisplayAssetCell: cell as! RAImagePickerAssetCell, at: indexPath.row)
86 | default: fatalError("index path not supported")
87 | }
88 | }
89 |
90 | func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
91 |
92 | guard let configuration = layout?.configuration else { return }
93 |
94 | switch indexPath.section {
95 | case configuration.sectionIndexForCamera: delegate?.imagePicker(delegate: self, didEndDisplayingCameraCell: cell as! RACameraCollectionViewCell)
96 | case configuration.sectionIndexForActions, configuration.sectionIndexForAssets: break
97 | default: fatalError("index path not supported")
98 | }
99 | }
100 |
101 | func scrollViewDidScroll(_ scrollView: UIScrollView) {
102 | delegate?.imagePicker(delegate: self, didScroll: scrollView)
103 | }
104 |
105 | @available(iOS 11.0, *)
106 | func scrollViewDidChangeAdjustedContentInset(_ scrollView: UIScrollView) {
107 | print("XXX: \(scrollView.adjustedContentInset)")
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Processing/RAImagePickerLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RAImagePickerLayout.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 12/1/17.
6 | //
7 |
8 | import UIKit
9 |
10 | /*
11 | Helper class that contains logic doing layout of collection view cells. UICollectionViewFlowLayout Workaround
12 | */
13 |
14 | final class RAImagePickerLayout {
15 |
16 | var configuration: RALayoutConfiguration
17 |
18 | init(configuration: RALayoutConfiguration) {
19 | self.configuration = configuration
20 | }
21 |
22 | /*
23 | Returns the size for item considering the number of rows and scroll direction.
24 | if preferredWidthOrHeight == nil, then square size is returned
25 | */
26 | func sizeForItem(numberOfItemsInRow: Int, preferredWidthOrHeight: CGFloat?, collectionView: UICollectionView, scrollDirection: UICollectionViewScrollDirection) -> CGSize {
27 |
28 | switch scrollDirection {
29 | case .horizontal:
30 | var itemHeight = collectionView.frame.height
31 | itemHeight -= (collectionView.contentInset.top + collectionView.contentInset.bottom)
32 | itemHeight -= (CGFloat(numberOfItemsInRow) - 1) * configuration.interitemSpacing
33 | itemHeight /= CGFloat(numberOfItemsInRow)
34 | return CGSize(width: preferredWidthOrHeight ?? itemHeight, height: itemHeight)
35 |
36 | case .vertical:
37 | var itemWidth = collectionView.frame.width
38 | itemWidth -= (collectionView.contentInset.left + collectionView.contentInset.right)
39 | itemWidth -= (CGFloat(numberOfItemsInRow) - 1) * configuration.interitemSpacing
40 | itemWidth /= CGFloat(numberOfItemsInRow)
41 | return CGSize(width: itemWidth, height: preferredWidthOrHeight ?? itemWidth)
42 | }
43 | }
44 |
45 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
46 |
47 | guard let layout = collectionViewLayout as? UICollectionViewFlowLayout else {
48 | fatalError("currently only UICollectionViewFlowLayout is supported")
49 | }
50 |
51 | let layoutModel = RALayoutModel(configuration: configuration, assets: 0)
52 |
53 | switch indexPath.section {
54 | case configuration.sectionIndexForActions:
55 | /*
56 | This will make sure that action item is either square if there are 2 items or rectangle if there is only 1 item
57 | */
58 | //let width = sizeForItem(numberOfItemsInRow: 2, preferredWidthOrHeight: nil, collectionView: collectionView, scrollDirection: layout.scrollDirection).width
59 | let ratio: CGFloat = 0.25
60 | let width = collectionView.frame.width * ratio
61 | return sizeForItem(numberOfItemsInRow: layoutModel.numberOfItems(in: configuration.sectionIndexForActions), preferredWidthOrHeight: width, collectionView: collectionView, scrollDirection: layout.scrollDirection)
62 |
63 | case configuration.sectionIndexForCamera:
64 | //lets keep this ratio so camera item is a nice rectangle
65 |
66 | let traitCollection = collectionView.traitCollection
67 |
68 | var ratio: CGFloat = 160/212
69 |
70 | // For landscape we need different ratio
71 | switch traitCollection.userInterfaceIdiom {
72 | case .phone:
73 | switch (traitCollection.horizontalSizeClass, traitCollection.verticalSizeClass) {
74 | // Sizes for iPhone Landscape
75 | case (.unspecified, .compact):
76 | fallthrough
77 | // Sizes for iPhone+ Landscape
78 | case (.regular, .compact):
79 | fallthrough
80 | // Sizes for iPhones in Landscape except iPhone+
81 | case (.compact, .compact):
82 | ratio = 1/ratio
83 | default: break
84 | }
85 |
86 | default:
87 | break
88 | }
89 |
90 | let widthOrHeight: CGFloat = collectionView.frame.height * ratio
91 | return sizeForItem(numberOfItemsInRow: layoutModel.numberOfItems(in: configuration.sectionIndexForCamera), preferredWidthOrHeight: widthOrHeight, collectionView: collectionView, scrollDirection: layout.scrollDirection)
92 |
93 | case configuration.sectionIndexForAssets:
94 | // Make sure there is at least 1 item, othewise invalid layout
95 | assert(configuration.numberOfAssetItemsInRow > 0, "invalid layout - numberOfAssetItemsInRow must be > 0, check your layout configuration ")
96 | return sizeForItem(numberOfItemsInRow: configuration.numberOfAssetItemsInRow, preferredWidthOrHeight: nil, collectionView: collectionView, scrollDirection: layout.scrollDirection)
97 |
98 | default:
99 | fatalError("unexpected sections count")
100 | }
101 |
102 | }
103 |
104 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
105 |
106 | guard let layout = collectionViewLayout as? UICollectionViewFlowLayout else {
107 | fatalError("currently only UICollectionViewFlowLayout is supported")
108 | }
109 |
110 | /// helper method that creates edge insets considering scroll direction
111 | func sectionInsets(_ inset: CGFloat) -> UIEdgeInsets {
112 | switch layout.scrollDirection {
113 | case .horizontal: return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: inset)
114 | case .vertical: return UIEdgeInsets(top: 0, left: 0, bottom: inset, right: 0)
115 | }
116 | }
117 |
118 | let layoutModel = RALayoutModel(configuration: configuration, assets: 0)
119 |
120 | switch section {
121 | case 0 where layoutModel.numberOfItems(in: section) > 0:
122 | return sectionInsets(configuration.actionSectionSpacing)
123 | case 1 where layoutModel.numberOfItems(in: section) > 0:
124 | return sectionInsets(configuration.cameraSectionSpacing)
125 | default:
126 | return .zero
127 | }
128 | }
129 |
130 | }
131 |
132 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Processing/RAImagePickerSelectionPolicy.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImagePickerSelectionPolicy.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 12/1/17.
6 | //
7 |
8 | import Foundation
9 |
10 | /*
11 | Determines which cells are selected, multiple selected or highlighted. Allow selecting only asset items, action items are only highlighted, camera item is untouched.
12 | */
13 | struct RAImagePickerSelectionPolicy {
14 |
15 | func shouldSelectItem(atSection section: Int, layoutConfiguration: RALayoutConfiguration) -> Bool {
16 | switch section {
17 | case layoutConfiguration.sectionIndexForActions, layoutConfiguration.sectionIndexForCamera:
18 | return false
19 | default:
20 | return true
21 | }
22 | }
23 |
24 | func shouldHighlightItem(atSection section: Int, layoutConfiguration: RALayoutConfiguration) -> Bool {
25 | switch section {
26 | case layoutConfiguration.sectionIndexForCamera:
27 | return false
28 | default:
29 | return true
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Processing/RALayoutModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RALayoutModel.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 12/1/17.
6 | //
7 |
8 | import UIKit
9 |
10 | /*
11 | A model that contains info that is used by layout code and collection view data source when figuring out layout structure.
12 | Contains three sections:
13 | 1. for actions (supports up to 2 action items).
14 | 2. for camera (1 camera item).
15 | 3. for image assets (any number of image asset items).
16 |
17 | Note: Each section can be empty.
18 | */
19 |
20 | struct RALayoutModel {
21 |
22 | private var sections: [Int] = [0, 0, 0]
23 |
24 | init(configuration: RALayoutConfiguration, assets: Int) {
25 | var actionItems: Int = configuration.showsDefaultCameraItem ? 1 : 0
26 | actionItems += configuration.showsDefaultGalleryItem ? 1 : 0
27 | sections[configuration.sectionIndexForActions] = actionItems
28 | sections[configuration.sectionIndexForCamera] = configuration.showsCameraItem ? 1 : 0
29 | sections[configuration.sectionIndexForAssets] = assets
30 | }
31 |
32 | var numberOfSections: Int {
33 | return sections.count
34 | }
35 |
36 | func numberOfItems(in section: Int) -> Int {
37 | return sections[section]
38 | }
39 |
40 | static var empty: RALayoutModel {
41 | let emptyConfiguration = RALayoutConfiguration(showsDefaultCameraItem: false, showsDefaultGalleryItem: false, showsCameraItem: false, scrollDirection: .horizontal, numberOfAssetItemsInRow: 0, interitemSpacing: 0, actionSectionSpacing: 0, cameraSectionSpacing: 0)
42 | return RALayoutModel(configuration: emptyConfiguration, assets: 0)
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Processing/UIImageEffects.h:
--------------------------------------------------------------------------------
1 | /*
2 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
3 | Inc. ("Apple") in consideration of your agreement to the following
4 | terms, and your use, installation, modification or redistribution of
5 | this Apple software constitutes acceptance of these terms. If you do
6 | not agree with these terms, please do not use, install, modify or
7 | redistribute this Apple software.
8 |
9 | In consideration of your agreement to abide by the following terms, and
10 | subject to these terms, Apple grants you a personal, non-exclusive
11 | license, under Apple's copyrights in this original Apple software (the
12 | "Apple Software"), to use, reproduce, modify and redistribute the Apple
13 | Software, with or without modifications, in source and/or binary forms;
14 | provided that if you redistribute the Apple Software in its entirety and
15 | without modifications, you must retain this notice and the following
16 | text and disclaimers in all such redistributions of the Apple Software.
17 | Neither the name, trademarks, service marks or logos of Apple Inc. may
18 | be used to endorse or promote products derived from the Apple Software
19 | without specific prior written permission from Apple. Except as
20 | expressly stated in this notice, no other rights or licenses, express or
21 | implied, are granted by Apple herein, including but not limited to any
22 | patent rights that may be infringed by your derivative works or by other
23 | works in which the Apple Software may be incorporated.
24 |
25 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE
26 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
27 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
28 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
29 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
30 |
31 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
32 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
35 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
36 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
37 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
38 | POSSIBILITY OF SUCH DAMAGE.
39 |
40 | Copyright (C) 2014 Apple Inc. All Rights Reserved.
41 |
42 | */
43 |
44 | #import
45 | #import
46 |
47 | @interface UIImageEffects : NSObject
48 |
49 | + (UIImage*)imageByApplyingLightEffectToImage:(UIImage*)inputImage;
50 | + (UIImage*)imageByApplyingExtraLightEffectToImage:(UIImage*)inputImage;
51 | + (UIImage*)imageByApplyingDarkEffectToImage:(UIImage*)inputImage;
52 | + (UIImage*)imageByApplyingTintEffectWithColor:(UIColor *)tintColor toImage:(UIImage*)inputImage;
53 | + (UIImage*)imageByApplyingBlurToImage:(UIImage*)inputImage
54 | withRadius:(CGFloat)blurRadius
55 | tintColor:(UIColor *)tintColor
56 | saturationDeltaFactor:(CGFloat)saturationDeltaFactor
57 | maskImage:(UIImage *)maskImage;
58 |
59 | @end
60 |
61 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Public/Layout/RAAppearance.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RAAppearance.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 11/29/17.
6 | //
7 |
8 | import UIKit
9 | import AVFoundation
10 |
11 | // Provides access to styling attributes of ImagePicker
12 |
13 | public class RAAppearance {
14 |
15 | // ImagePicker background color
16 | public var backgroundColor: UIColor = UIColor(red: 208/255, green: 213/255, blue: 218/255, alpha: 1)
17 | }
18 |
19 | extension UIInterfaceOrientation {
20 |
21 | var captureVideoOrientation: AVCaptureVideoOrientation {
22 | switch self {
23 | case .portrait, .unknown: return .portrait
24 | case .portraitUpsideDown: return .portraitUpsideDown
25 | case .landscapeRight: return .landscapeRight
26 | case .landscapeLeft: return .landscapeLeft
27 | }
28 | }
29 | }
30 | extension UIInterfaceOrientation : CustomDebugStringConvertible {
31 |
32 | public var debugDescription: String {
33 | switch self {
34 | case .unknown: return "unknown"
35 | case .portrait: return "portrait"
36 | case .portraitUpsideDown: return "portrait upside down"
37 | case .landscapeRight: return "landscape right"
38 | case .landscapeLeft: return "landscape left"
39 | }
40 | }
41 | }
42 |
43 | extension UICollectionView {
44 |
45 | func indexPathsForElements(in rect: CGRect) -> [IndexPath] {
46 | let allLayoutAttributes = collectionViewLayout.layoutAttributesForElements(in: rect)!
47 | let paths = allLayoutAttributes.map { $0.indexPath }
48 | return paths
49 | }
50 | }
51 |
52 | // MARK: - Functions
53 | func differencesBetweenRects(_ old: CGRect, _ new: CGRect, _ scrollDirection: UICollectionViewScrollDirection) -> (added: [CGRect], removed: [CGRect]) {
54 | switch scrollDirection {
55 | case .horizontal: return differencesBetweenRectsHorizontal(old, new)
56 | case .vertical: return differencesBetweenRectsVertical(old, new)
57 | }
58 | }
59 |
60 | func differencesBetweenRectsVertical(_ old: CGRect, _ new: CGRect) -> (added: [CGRect], removed: [CGRect]) {
61 | if old.intersects(new) {
62 | var added = [CGRect]()
63 | if new.maxY > old.maxY {
64 | added += [CGRect(x: new.origin.x, y: old.maxY, width: new.width, height: new.maxY - old.maxY)]
65 | }
66 | if old.minY > new.minY {
67 | added += [CGRect(x: new.origin.x, y: new.minY, width: new.width, height: old.minY - new.minY)]
68 | }
69 | var removed = [CGRect]()
70 | if new.maxY < old.maxY {
71 | removed += [CGRect(x: new.origin.x, y: new.maxY, width: new.width, height: old.maxY - new.maxY)]
72 | }
73 | if old.minY < new.minY {
74 | removed += [CGRect(x: new.origin.x, y: old.minY, width: new.width, height: new.minY - old.minY)]
75 | }
76 | return (added, removed)
77 | }
78 | else {
79 | return ([new], [old])
80 | }
81 | }
82 |
83 | func differencesBetweenRectsHorizontal(_ old: CGRect, _ new: CGRect) -> (added: [CGRect], removed: [CGRect]) {
84 | if old.intersects(new) {
85 | var added = [CGRect]()
86 | if new.maxX > old.maxX {
87 | added += [CGRect(x: old.maxX, y: old.origin.y, width: new.maxX - old.maxX, height: old.height)]
88 | }
89 | if old.minX > new.minX {
90 | added += [CGRect(x: new.minX, y: old.origin.y, width: old.maxX - new.maxX, height: old.height)]
91 | }
92 | var removed = [CGRect]()
93 | if new.maxX < old.maxX {
94 | removed += [CGRect(x: new.maxX, y: old.origin.y, width: old.maxX - new.maxX, height: old.height)]
95 | }
96 | if old.minX < new.minX {
97 | removed += [CGRect(x: old.minX, y: old.origin.y, width: new.maxX - old.maxX, height: old.height)]
98 | }
99 | return (added, removed)
100 | }
101 | else {
102 | return ([new], [old])
103 | }
104 | }
105 |
106 | extension UIImage {
107 | static func fromWrappedBundleImage(_ wrappedImage: WrappedBundleImage) -> UIImage {
108 | return wrappedImage.image
109 | }
110 | }
111 | struct WrappedBundleImage: _ExpressibleByImageLiteral {
112 | let image: UIImage
113 |
114 | init(imageLiteralResourceName name: String) {
115 | let bundle = Bundle(for: RAActionCell.self)
116 | image = UIImage(named: name, in: bundle, compatibleWith: nil)!
117 | }
118 | }
119 |
120 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Public/Layout/RACameraCollectionViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RACameraCollectionViewCell.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 11/29/17.
6 | //
7 |
8 | import UIKit
9 | import Foundation
10 | import AVFoundation
11 |
12 | protocol RACameraCollectionViewCellDelegate : class {
13 | func takePicture()
14 | func takeLivePhoto()
15 | func startVideoRecording()
16 | func stopVideoRecording()
17 | func flipCamera(_ completion: (() -> Void)?)
18 | }
19 |
20 | /*
21 | Base class for cutom camera cells. [Custom Cell --inherit--> RACameraCollectionViewCell]
22 | */
23 | open class RACameraCollectionViewCell: UICollectionViewCell {
24 |
25 | // Video Preview Layer
26 | var previewView: AVPreviewView = {
27 | let view = AVPreviewView(frame: .zero)
28 | view.backgroundColor = UIColor.black
29 | return view
30 | }()
31 | // Adding an image above the blur view so the image can hide the black background
32 | var imageView: UIImageView = {
33 | let view = UIImageView(frame: .zero)
34 | view.contentMode = .scaleAspectFill
35 | return view
36 | }()
37 |
38 | var blurView: UIVisualEffectView?
39 | var isVisualEffectViewUsedForBlurring = false
40 | weak var delegate: RACameraCollectionViewCellDelegate?
41 |
42 | // MARK: - Initialization
43 | required public init?(coder aDecoder: NSCoder) {
44 | super.init(coder: aDecoder)
45 | backgroundView = previewView
46 | previewView.addSubview(imageView)
47 | }
48 | public override init(frame: CGRect) {
49 | super.init(frame: frame)
50 | backgroundView = previewView
51 | previewView.addSubview(imageView)
52 | }
53 | open override func layoutSubviews() {
54 | super.layoutSubviews()
55 | imageView.frame = previewView.bounds
56 | blurView?.frame = previewView.bounds
57 | }
58 |
59 | // MARK: - Public Methods
60 |
61 | // Camera authorization status delegate. [Update UI based on the value of `authorizationStatus` property]
62 | open func updateCameraAuthorizationStatus() {}
63 |
64 | // Cell can have multiple visual states based on autorization status.
65 | public internal(set) var authorizationStatus: AVAuthorizationStatus? {
66 | didSet {
67 | updateCameraAuthorizationStatus()
68 | }
69 | }
70 |
71 | // Live Photos delegate.
72 | // - isProcessing: check if there is at least one live photo being processed/captured.
73 | // - shouldAnimate: check if the UI change should be animated or not.
74 | open func updateLivePhotoStatus(isProcessing: Bool, shouldAnimate: Bool) {}
75 |
76 | // Video recording delegate. [Override this method to update UI based on recording status]
77 | // - isRecording: check if the video is recording or not.
78 | // - shouldAnimate: check if the UI change should be animated or not.
79 | open func updateRecordingVideoStatus(isRecording: Bool, shouldAnimate: Bool) {}
80 | // Video became ready
81 | open func videoRecodingDidBecomeReady() {}
82 |
83 | /*
84 | Camera Functionalities
85 | */
86 | // Camera Flipping
87 | @objc public func flipCamera(_ completion: (() -> Void)? = nil) {
88 | delegate?.flipCamera(completion)
89 | }
90 | // Capture Photos
91 | @objc public func takePicture() {
92 | delegate?.takePicture()
93 | }
94 | // Capture Live Photos
95 | @objc public func takeLivePhoto() {
96 | delegate?.takeLivePhoto()
97 | }
98 | // Record Video
99 | @objc public func startVideoRecording() {
100 | delegate?.startVideoRecording()
101 | }
102 | @objc public func stopVideoRecording() {
103 | delegate?.stopVideoRecording()
104 | }
105 |
106 | /*
107 | Blur Effect
108 | */
109 | func blurEffect(blurImage: UIImage?, animated: Bool, completion: ((Bool) -> Void)?) {
110 | var view: UIView
111 | if isVisualEffectViewUsedForBlurring == false {
112 | guard imageView.image == nil else {
113 | return
114 | }
115 | imageView.image = blurImage
116 | view = imageView
117 | }
118 | else {
119 | if blurView == nil {
120 | blurView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
121 | previewView.addSubview(blurView!)
122 | }
123 | view = blurView!
124 | view.frame = previewView.bounds
125 | }
126 | view.alpha = 0
127 | if animated == false {
128 | view.alpha = 1
129 | completion?(true)
130 | }
131 | else {
132 | UIView.animate(withDuration: 0.1, delay: 0, options: .allowAnimatedContent, animations: {
133 | view.alpha = 1
134 | }, completion: completion)
135 | }
136 | }
137 | func unblurEffect(unblurImage: UIImage?, animated: Bool, completion: ((Bool) -> Void)?) {
138 | var animationBlock: () -> ()
139 | var animationCompletionBlock: (Bool) -> ()
140 | if isVisualEffectViewUsedForBlurring == false {
141 | guard imageView.image != nil else {
142 | return
143 | }
144 | if let image = unblurImage {
145 | imageView.image = image
146 | }
147 | animationBlock = {
148 | self.imageView.alpha = 0
149 | }
150 | animationCompletionBlock = { finished in
151 | self.imageView.image = nil
152 | completion?(finished)
153 | }
154 | }
155 | else {
156 | animationBlock = {
157 | self.blurView?.alpha = 0
158 | }
159 | animationCompletionBlock = { finished in
160 | completion?(finished)
161 | }
162 | }
163 | if animated == false {
164 | animationBlock()
165 | animationCompletionBlock(true)
166 | }
167 | else {
168 | UIView.animate(withDuration: 0.1, delay: 0, options: .allowAnimatedContent, animations: animationBlock, completion: animationCompletionBlock)
169 | }
170 | }
171 |
172 | // Tap press determining wether is should take a photo or not.
173 | func touchIsCaptureEffective(point: CGPoint) -> Bool {
174 | // Finding topmost view that detected the touch at a point and check if its not any anything other than contentView.
175 | if bounds.contains(point), let testedView = hitTest(point, with: nil), testedView === contentView {
176 | return true
177 | }
178 | return false
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Public/Layout/RALayoutConfiguration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RALayoutConfiguration.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 11/29/17.
6 | //
7 |
8 | import Foundation
9 |
10 |
11 | // Helper struct thats used with the ImagePickerLayout when configuring and laying out CollectionView items.
12 | public struct RALayoutConfiguration {
13 |
14 | public var showsDefaultCameraItem = true
15 | public var showsDefaultGalleryItem = true
16 | public var showsCameraItem = true
17 |
18 | let showsAssetItems = true
19 |
20 | // Scroll and Layout Direction
21 | public var scrollDirection: UICollectionViewScrollDirection = .horizontal
22 |
23 | // Define the number of image assets will be in a row
24 | public var numberOfAssetItemsInRow: Int = 2
25 |
26 | // Space between items within a section
27 | public var interitemSpacing: CGFloat = 1
28 |
29 | // Spacing between actions section and camera section
30 | public var actionSectionSpacing: CGFloat = 1
31 |
32 | // Spacing between camera section and assets section
33 | public var cameraSectionSpacing: CGFloat = 10
34 | }
35 |
36 | extension RALayoutConfiguration {
37 |
38 | var hasAnyAction: Bool {
39 | return showsDefaultCameraItem || showsDefaultGalleryItem
40 | }
41 | var sectionIndexForActions: Int {
42 | return 0
43 | }
44 | var sectionIndexForCamera: Int {
45 | return 1
46 | }
47 | var sectionIndexForAssets: Int {
48 | return 2
49 | }
50 |
51 | public static var `default` = RALayoutConfiguration()
52 | }
53 |
54 | extension UICollectionView {
55 |
56 | // Method for convenienet access to camera cell
57 | func cameraCell(layout: RALayoutConfiguration) -> RACameraCollectionViewCell? {
58 | return cellForItem(at: IndexPath(row: 0, section: layout.sectionIndexForCamera)) as? RACameraCollectionViewCell
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Public/RACaptureSettings.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RACaptureSettings.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 11/29/17.
6 | //
7 |
8 | import UIKit
9 |
10 | /*
11 | Capture Structure Configuration
12 | */
13 | public struct RACaptureSettings {
14 |
15 | public enum CameraMode {
16 |
17 | // Support Photos Only [Default]
18 | case photo
19 |
20 | // Support Videos Only
21 | case video
22 |
23 | // Support Photos & Live Photos
24 | case photoAndLivePhoto
25 |
26 | // Support Videos & Photos
27 | case photoAndVideo
28 | }
29 |
30 |
31 | // Preset of media types needed to support [Can not be changed at the runtime]
32 | public var cameraMode: CameraMode
33 |
34 | // Check if saving photos flag is turned on [(Photos Only), Videos and Live Photos is always true]
35 | // save photo in library => true, do not save => false
36 | public var savesCapturedPhotosToPhotoLibrary: Bool
37 |
38 | let savesCapturedLivePhotosToPhotoLibrary: Bool = true
39 | let savesCapturedVideosToPhotoLibrary: Bool = true
40 |
41 | // Default Configuration
42 | public static var `default`: RACaptureSettings {
43 | return RACaptureSettings(
44 | cameraMode: .photo,
45 | savesCapturedPhotosToPhotoLibrary: false
46 | )
47 | }
48 | }
49 |
50 | extension RACaptureSettings.CameraMode {
51 | /// transforms user related enum to specific internal capture session enum
52 |
53 | var captureSessionPresetConfiguration: RACaptureSession.SessionPresetConfiguration {
54 | switch self {
55 | case .photo: return .photos
56 | case .video: return .videos
57 | case .photoAndLivePhoto: return .livePhotos
58 | case .photoAndVideo: return .videos
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Views/Cells/RAActionCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RAActionCell.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 12/1/17.
6 | //
7 |
8 | import UIKit
9 | import Foundation
10 |
11 |
12 | final class RAActionCell: UICollectionViewCell {
13 |
14 | @IBOutlet weak var titleLabel: UILabel!
15 | @IBOutlet weak var imageView: UIImageView!
16 |
17 | @IBOutlet var leadingOffset: NSLayoutConstraint!
18 | @IBOutlet var trailingOffset: NSLayoutConstraint!
19 | @IBOutlet var topOffset: NSLayoutConstraint!
20 | @IBOutlet var bottomOffset: NSLayoutConstraint!
21 |
22 | override func awakeFromNib() {
23 | super.awakeFromNib()
24 | // Initialization code
25 |
26 | imageView.backgroundColor = UIColor.clear
27 | }
28 | }
29 |
30 | extension RAActionCell {
31 |
32 | func updateCell(cameraText:String, cameraIconColor: UIColor,
33 | galleryText: String, galleryIconColor: UIColor, index: Int)
34 | {
35 | titleLabel.textColor = .black
36 | switch index {
37 | case 0:
38 | titleLabel.text = cameraText
39 | imageView.image = imageView.image!.withRenderingMode(.alwaysTemplate)
40 | imageView.tintColor = cameraIconColor
41 | break
42 | case 1:
43 | titleLabel.text = galleryText
44 | imageView.image = imageView.image!.withRenderingMode(.alwaysTemplate)
45 | imageView.tintColor = galleryIconColor
46 | break
47 | default:
48 | break
49 | }
50 | }
51 |
52 | func update(withIndex index: Int, layoutConfiguration: RALayoutConfiguration) {
53 |
54 | // Layout
55 | let layoutModel = RALayoutModel(configuration: layoutConfiguration, assets: 0)
56 | let actionCount = layoutModel.numberOfItems(in: layoutConfiguration.sectionIndexForActions)
57 |
58 | titleLabel.textColor = .black
59 | switch index {
60 | case 0:
61 | titleLabel.text = "Camera"
62 | imageView.image = UIImage.fromWrappedBundleImage(#imageLiteral(resourceName: "icon-camera"))
63 | imageView.image = imageView.image!.withRenderingMode(.alwaysTemplate)
64 | imageView.tintColor = .black
65 | break
66 | case 1:
67 | titleLabel.text = "Gallery"
68 | imageView.image = UIImage.fromWrappedBundleImage(#imageLiteral(resourceName: "icon-gallery"))
69 | imageView.image = imageView.image!.withRenderingMode(.alwaysTemplate)
70 | imageView.tintColor = .black
71 | break
72 | default:
73 | break
74 | }
75 |
76 | let isFirst = index == 0
77 | let isLast = index == actionCount - 1
78 |
79 | switch layoutConfiguration.scrollDirection {
80 | case .horizontal:
81 | topOffset.constant = isFirst ? 10 : 5
82 | bottomOffset.constant = isLast ? 10 : 5
83 | leadingOffset.constant = 5
84 | trailingOffset.constant = 5
85 | case .vertical:
86 | topOffset.constant = 5
87 | bottomOffset.constant = 5
88 | leadingOffset.constant = isFirst ? 10 : 5
89 | trailingOffset.constant = isLast ? 10 : 5
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Views/Cells/RAActionCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Views/Cells/RAAssetCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RAAssetCell.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 12/1/17.
6 | //
7 |
8 |
9 | import Foundation
10 | import Photos
11 |
12 | // Each image picker asset cell must conform to this protocol.
13 | public protocol RAImagePickerAssetCell : class {
14 |
15 | // This image view will be used when setting an asset's image.
16 | var imageView: UIImageView! { get }
17 |
18 | // This is a helper identifier that is used when properly displaying cells asynchronously.
19 | var representedAssetIdentifier: String? { get set }
20 | }
21 |
22 | /*
23 | Default collection view cell that represents asset item. It supports:
24 | - Shows Image View of Image Thumbnail.
25 | - Icon and Duration for Videos.
26 | - Selected Icon when "isSelected" is true.
27 | */
28 | class VideoAssetCell : RAAssetCell {
29 |
30 | var durationLabel: UILabel
31 | var iconView: UIImageView
32 | var gradientView: UIImageView
33 |
34 | override init(frame: CGRect) {
35 |
36 | durationLabel = UILabel(frame: .zero)
37 | gradientView = UIImageView(frame: .zero)
38 | iconView = UIImageView(frame: .zero)
39 |
40 | super.init(frame: frame)
41 |
42 | gradientView.isHidden = true
43 |
44 | iconView.tintColor = UIColor.white
45 | iconView.contentMode = .center
46 |
47 | durationLabel.textColor = UIColor.white
48 | durationLabel.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
49 | durationLabel.textAlignment = .right
50 |
51 | contentView.addSubview(gradientView)
52 | contentView.addSubview(durationLabel)
53 | contentView.addSubview(iconView)
54 | }
55 |
56 | required init?(coder aDecoder: NSCoder) {
57 | fatalError("init(coder:) has not been implemented")
58 | }
59 |
60 | override func layoutSubviews() {
61 | super.layoutSubviews()
62 |
63 | gradientView.frame.size = CGSize(width: bounds.width, height: 40)
64 | gradientView.frame.origin = CGPoint(x: 0, y: bounds.height-40)
65 |
66 | let margin: CGFloat = 5
67 | durationLabel.frame.size = CGSize(width: 50, height: 20)
68 | durationLabel.frame.origin = CGPoint(
69 | x: contentView.bounds.width - durationLabel.frame.size.width - margin,
70 | y: contentView.bounds.height - durationLabel.frame.size.height - margin
71 | )
72 | iconView.frame.size = CGSize(width: 21, height: 21)
73 | iconView.frame.origin = CGPoint(
74 | x: margin,
75 | y: contentView.bounds.height - iconView.frame.height - margin
76 | )
77 | }
78 |
79 | static let durationFormatter: DateComponentsFormatter = {
80 | let formatter = DateComponentsFormatter()
81 | formatter.unitsStyle = .positional
82 | formatter.allowedUnits = [.minute, .second]
83 | formatter.zeroFormattingBehavior = .pad
84 | return formatter
85 | }()
86 |
87 | func update(with asset: PHAsset) {
88 |
89 | switch asset.mediaType {
90 | case .image:
91 | if asset.mediaSubtypes.contains(.photoLive) {
92 | gradientView.isHidden = false
93 | gradientView.image = UIImage(named: "gradient", in: Bundle(for: type(of: self)), compatibleWith: nil)?.resizableImage(withCapInsets: .zero, resizingMode: .stretch)
94 | iconView.isHidden = false
95 | durationLabel.isHidden = true
96 | iconView.image = UIImage(named: "icon-badge-livephoto", in: Bundle(for: type(of: self)), compatibleWith: nil)
97 | }
98 | else {
99 | gradientView.isHidden = true
100 | iconView.isHidden = true
101 | durationLabel.isHidden = true
102 | }
103 | case .video:
104 | gradientView.isHidden = false
105 | gradientView.image = UIImage(named: "gradient", in: Bundle(for: type(of: self)), compatibleWith: nil)?.resizableImage(withCapInsets: .zero, resizingMode: .stretch)
106 | iconView.isHidden = false
107 | durationLabel.isHidden = false
108 | iconView.image = UIImage(named: "icon-badge-video", in: Bundle(for: type(of: self)), compatibleWith: nil)
109 | durationLabel.text = VideoAssetCell.durationFormatter.string(from: asset.duration)
110 | default: break
111 | }
112 | }
113 | }
114 |
115 | /*
116 | A default implementation of `ImagePickerAssetCell`. If user does not register a custom cell, Image Picker will use this one. Also contains default icon for selected state.
117 | */
118 | class RAAssetCell : UICollectionViewCell, RAImagePickerAssetCell {
119 |
120 | var imageView: UIImageView! = UIImageView(frame: .zero)
121 | fileprivate var selectedImageView = CheckView(frame: .zero)
122 |
123 | var representedAssetIdentifier: String?
124 |
125 | override var isSelected: Bool {
126 | didSet {
127 | selectedImageView.isHidden = !isSelected
128 | if selectedImageView.isHidden == false {
129 | selectedImageView.image = UIImage(named: "icon-check-background", in: Bundle(for: type(of: self)), compatibleWith: nil)
130 | selectedImageView.foregroundImage = UIImage(named: "icon-check", in: Bundle(for: type(of: self)), compatibleWith: nil)
131 | }
132 | }
133 | }
134 |
135 | override init(frame: CGRect) {
136 | super.init(frame: frame)
137 | imageView.contentMode = .scaleAspectFill
138 | imageView.clipsToBounds = true
139 | contentView.addSubview(imageView)
140 |
141 | selectedImageView.frame = CGRect(x: 0, y: 0, width: 31, height: 31)
142 |
143 | contentView.addSubview(selectedImageView)
144 | selectedImageView.isHidden = true
145 | }
146 |
147 | required init?(coder aDecoder: NSCoder) {
148 | fatalError("init(coder:) has not been implemented")
149 | }
150 |
151 | override func prepareForReuse() {
152 | super.prepareForReuse()
153 | imageView.image = nil
154 | }
155 |
156 | override func layoutSubviews() {
157 | super.layoutSubviews()
158 | imageView.frame = bounds
159 | let margin: CGFloat = 5
160 | selectedImageView.frame.origin = CGPoint(
161 | x: bounds.width - selectedImageView.frame.width - margin,
162 | y: margin
163 | )
164 | }
165 |
166 | }
167 |
168 | private final class CheckView : UIImageView {
169 |
170 | var foregroundImage: UIImage? {
171 | get { return foregroundView.image }
172 | set { foregroundView.image = newValue }
173 | }
174 |
175 | private let foregroundView = UIImageView(frame: .zero)
176 |
177 | override init(frame: CGRect) {
178 | super.init(frame: frame)
179 | addSubview(foregroundView)
180 | contentMode = .center
181 | foregroundView.contentMode = .center
182 | }
183 |
184 | required init?(coder aDecoder: NSCoder) {
185 | fatalError("init(coder:) has not been implemented")
186 | }
187 |
188 | override func layoutSubviews() {
189 | super.layoutSubviews()
190 | foregroundView.frame = bounds
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Views/Cells/RAImagePickerView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RAImagePickerView.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 12/1/17.
6 | //
7 |
8 | import UIKit
9 |
10 | final class RAImagePickerView : UIView {
11 |
12 | @IBOutlet weak var collectionView: UICollectionView!
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Views/Cells/RAImagePickerView.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Views/Cells/RALivePhotoCameraCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RALivePhotoCameraCell.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 12/1/17.
6 | //
7 |
8 | import UIKit
9 | import Foundation
10 |
11 | class RALivePhotoCameraCell : RACameraCollectionViewCell {
12 |
13 | @IBOutlet weak var snapButton: UIButton!
14 | @IBOutlet weak var enableLivePhotosButton: RAStationaryButton!
15 | @IBOutlet weak var liveIndicator: RACarvedLabel!
16 |
17 | override func awakeFromNib() {
18 | super.awakeFromNib()
19 | liveIndicator.alpha = 0
20 | liveIndicator.tintColor = UIColor(red: 245/255, green: 203/255, blue: 47/255, alpha: 1)
21 |
22 | enableLivePhotosButton.unselectedTintColor = UIColor.white
23 | enableLivePhotosButton.selectedTintColor = UIColor(red: 245/255, green: 203/255, blue: 47/255, alpha: 1)
24 | }
25 |
26 | @IBAction func snapButtonTapped(_ sender: UIButton) {
27 | if enableLivePhotosButton.isSelected {
28 | takeLivePhoto()
29 | }
30 | else {
31 | takePicture()
32 | }
33 | }
34 |
35 | @IBAction func flipButtonTapped(_ sender: UIButton) {
36 | flipCamera()
37 | }
38 |
39 | func updateWithCameraMode(_ mode: RACaptureSettings.CameraMode) {
40 | switch mode {
41 | case .photo:
42 | liveIndicator.isHidden = true
43 | enableLivePhotosButton.isHidden = true
44 | case .photoAndLivePhoto:
45 | liveIndicator.isHidden = false
46 | enableLivePhotosButton.isHidden = false
47 | default:
48 | fatalError("Image Picker - unsupported camera mode for \(type(of: self))")
49 | }
50 | }
51 |
52 | override func updateLivePhotoStatus(isProcessing: Bool, shouldAnimate: Bool) {
53 |
54 | let updates: () -> Void = {
55 | self.liveIndicator.alpha = isProcessing ? 1 : 0
56 | }
57 |
58 | shouldAnimate ? UIView.animate(withDuration: 0.25, animations: updates) : updates()
59 | }
60 |
61 | }
62 |
63 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Views/Cells/RAVideoCameraCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RAVideoCameraCell.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 12/1/17.
6 | //
7 |
8 | import UIKit
9 | import Foundation
10 |
11 |
12 | // TODO: add a recording indicator (red dot with timer)
13 | class RAVideoCameraCell: RACameraCollectionViewCell {
14 |
15 | @IBOutlet weak var recordLabel: RARecordDurationLabel!
16 | @IBOutlet weak var recordButton: RARecordButton!
17 | @IBOutlet weak var flipButton: UIButton!
18 |
19 | @IBAction func recordButtonTapped(_ sender: UIButton) {
20 | if sender.isSelected {
21 | stopVideoRecording()
22 | }
23 | else {
24 | startVideoRecording()
25 | }
26 | }
27 |
28 | @IBAction func flipButtonTapped(_ sender: UIButton) {
29 | flipCamera()
30 | }
31 |
32 | override func awakeFromNib() {
33 | super.awakeFromNib()
34 | // Initialization code
35 |
36 | recordButton.isEnabled = false
37 | recordButton.alpha = 0.5
38 | }
39 |
40 | override func updateRecordingVideoStatus(isRecording: Bool, shouldAnimate: Bool) {
41 |
42 | // Update button state
43 | recordButton.isSelected = isRecording
44 |
45 | // Update duration label
46 | isRecording ? recordLabel.start() : recordLabel.stop()
47 |
48 | // Update other buttons
49 | let updates: () -> Void = {
50 | self.flipButton.alpha = isRecording ? 0 : 1
51 | }
52 |
53 | shouldAnimate ? UIView.animate(withDuration: 0.25, animations: updates) : updates()
54 | }
55 |
56 | override func videoRecodingDidBecomeReady() {
57 | recordButton.isEnabled = true
58 | UIView.animate(withDuration: 0.25) {
59 | self.recordButton.alpha = 1.0
60 | }
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Views/Cells/RAVideoCameraCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
33 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Views/RACarvedLabel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RACarvedLabel.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 12/1/17.
6 | //
7 |
8 | import UIKit
9 |
10 | fileprivate typealias TextAttributes = [NSAttributedStringKey: Any]
11 |
12 | /*
13 | RACarvedLabel: a label whose transparent text is carved into solid color.
14 | Note: text is always aligned to center.
15 | */
16 | @IBDesignable
17 | final class RACarvedLabel : UIView {
18 |
19 | @IBInspectable var text: String? {
20 | didSet {
21 | invalidateIntrinsicContentSize()
22 | setNeedsDisplay()
23 | }
24 | }
25 |
26 | var font: UIFont? {
27 | didSet {
28 | invalidateIntrinsicContentSize()
29 | setNeedsDisplay()
30 | }
31 | }
32 |
33 | @IBInspectable var cornerRadius: CGFloat = 0 {
34 | didSet { setNeedsDisplay() }
35 | }
36 |
37 | @IBInspectable var verticalInset: CGFloat = 0 {
38 | didSet {
39 | invalidateIntrinsicContentSize()
40 | setNeedsDisplay()
41 | }
42 | }
43 |
44 | @IBInspectable var horizontalInset: CGFloat = 0 {
45 | didSet {
46 | invalidateIntrinsicContentSize()
47 | setNeedsDisplay()
48 | }
49 |
50 | }
51 |
52 | override init(frame: CGRect) {
53 | super.init(frame: frame)
54 | _ = backgroundColor
55 | isOpaque = false
56 | }
57 |
58 | required init?(coder aDecoder: NSCoder) {
59 | super.init(coder: aDecoder)
60 | _ = backgroundColor
61 | isOpaque = false
62 | }
63 |
64 | override var backgroundColor: UIColor? {
65 | get { return UIColor.clear }
66 | set { super.backgroundColor = UIColor.clear }
67 | }
68 |
69 | fileprivate var textAttributes: TextAttributes {
70 | let activeFont = font ?? UIFont.systemFont(ofSize: 12, weight: .regular)
71 | return [
72 | NSAttributedStringKey.font: activeFont
73 | ]
74 | }
75 |
76 | fileprivate var attributedString: NSAttributedString {
77 | return NSAttributedString(string: text ?? "", attributes: textAttributes)
78 | }
79 |
80 | override func draw(_ rect: CGRect) {
81 | let color = tintColor!
82 | color.setFill()
83 |
84 | let path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius)
85 | path.fill()
86 |
87 | guard let context = UIGraphicsGetCurrentContext(), (text?.count ?? 0) > 0 else {
88 | return
89 | }
90 |
91 | let attributedString = self.attributedString
92 | let stringSize = attributedString.size()
93 |
94 | let xOrigin: CGFloat = max(horizontalInset, (rect.width - stringSize.width)/2)
95 | let yOrigin: CGFloat = max(verticalInset, (rect.height - stringSize.height)/2)
96 |
97 | context.saveGState()
98 | context.setBlendMode(.destinationOut)
99 | attributedString.draw(at: CGPoint(x: xOrigin, y: yOrigin))
100 | context.restoreGState()
101 | }
102 |
103 | override func sizeThatFits(_ size: CGSize) -> CGSize {
104 | let stringSize = attributedString.size()
105 | return CGSize(width: stringSize.width + horizontalInset*2, height: stringSize.height + verticalInset*2)
106 | }
107 |
108 | override var intrinsicContentSize: CGSize {
109 | return sizeThatFits(.zero)
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Views/RARecordButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RARecordButton.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 12/1/17.
6 | //
7 |
8 | import Foundation
9 |
10 | /*
11 | RARecordButton: a rounded button with 2 circles where middle circle animates based on 3 states - initial, pressed, recording.
12 | */
13 | class RARecordButton : RAStationaryButton {
14 |
15 | var outerBorderWidth: CGFloat = 3 { didSet { setNeedsUpdateCircleLayers() } }
16 | var innerBorderWidth: CGFloat = 1.5 { didSet { setNeedsUpdateCircleLayers() } }
17 | var pressDepthFactor: CGFloat = 0.9 { didSet { setNeedsUpdateCircleLayers() } }
18 |
19 | override var isHighlighted: Bool {
20 | get { return super.isHighlighted }
21 | set {
22 | if isSelected == false && newValue != isHighlighted && newValue == true {
23 | updateCircleLayers(state: .pressed, animated: true)
24 | }
25 | super.isHighlighted = newValue
26 | }
27 | }
28 |
29 | override func selectionDidChange(animated: Bool) {
30 | super.selectionDidChange(animated: animated)
31 |
32 | if isSelected {
33 | updateCircleLayers(state: .recording, animated: animated)
34 | }
35 | else {
36 | updateCircleLayers(state: .initial, animated: animated)
37 | }
38 | }
39 |
40 | private var innerCircleLayerInset: CGFloat {
41 | return outerBorderWidth + innerBorderWidth
42 | }
43 |
44 | private var needsUpdateCircleLayers = true
45 | private var outerCircleLayer: CALayer
46 | private var innerCircleLayer: CALayer
47 |
48 | private enum State: String {
49 | case initial
50 | case pressed
51 | case recording
52 | }
53 |
54 | private var layersState: State = .initial
55 |
56 | required init?(coder aDecoder: NSCoder) {
57 | outerCircleLayer = CALayer()
58 | innerCircleLayer = CALayer()
59 | super.init(coder: aDecoder)
60 | backgroundColor = UIColor.clear
61 | layer.addSublayer(outerCircleLayer)
62 | layer.addSublayer(innerCircleLayer)
63 | CATransaction.setDisableActions(true)
64 |
65 | outerCircleLayer.backgroundColor = UIColor.clear.cgColor
66 | outerCircleLayer.cornerRadius = bounds.width/2
67 | outerCircleLayer.borderWidth = outerBorderWidth
68 | outerCircleLayer.borderColor = tintColor.cgColor
69 |
70 | innerCircleLayer.backgroundColor = UIColor.red.cgColor
71 |
72 | CATransaction.commit()
73 | }
74 |
75 | override func layoutSubviews() {
76 | super.layoutSubviews()
77 | if needsUpdateCircleLayers {
78 | CATransaction.setDisableActions(true)
79 | outerCircleLayer.frame = bounds
80 | innerCircleLayer.frame = bounds.insetBy(dx: innerCircleLayerInset, dy: innerCircleLayerInset)
81 | innerCircleLayer.cornerRadius = bounds.insetBy(dx: innerCircleLayerInset, dy: innerCircleLayerInset).width/2
82 | needsUpdateCircleLayers = false
83 | CATransaction.commit()
84 | }
85 | }
86 |
87 | private func setNeedsUpdateCircleLayers() {
88 | needsUpdateCircleLayers = true
89 | setNeedsLayout()
90 | }
91 |
92 | private func updateCircleLayers(state: State, animated: Bool) {
93 | guard layersState != state else { return }
94 |
95 | layersState = state
96 |
97 | switch layersState {
98 | case .initial:
99 | setInnerLayer(recording: false, animated: animated)
100 | case .pressed:
101 | setInnerLayerPressed(animated: animated)
102 | case .recording:
103 | setInnerLayer(recording: true, animated: animated)
104 | }
105 | }
106 |
107 | private func setInnerLayerPressed(animated: Bool) {
108 |
109 | if animated {
110 | innerCircleLayer.add(transformAnimation(to: pressDepthFactor, duration: 0.25), forKey: nil)
111 | }
112 | else {
113 | CATransaction.setDisableActions(true)
114 | innerCircleLayer.setValue(pressDepthFactor, forKeyPath: "transform.scale")
115 | CATransaction.commit()
116 | }
117 | }
118 |
119 | private func setInnerLayer(recording: Bool, animated: Bool) {
120 |
121 | if recording {
122 | innerCircleLayer.add(transformAnimation(to: 0.5, duration: 0.15), forKey: nil)
123 | innerCircleLayer.cornerRadius = 8
124 | }
125 | else {
126 | innerCircleLayer.add(transformAnimation(to: 1, duration: 0.25), forKey: nil)
127 | innerCircleLayer.cornerRadius = bounds.insetBy(dx: innerCircleLayerInset, dy: innerCircleLayerInset).width/2
128 | }
129 |
130 | }
131 |
132 | private func transformAnimation(to value: CGFloat, duration: CFTimeInterval) -> CAAnimation {
133 | let animation = CABasicAnimation()
134 | animation.keyPath = "transform.scale"
135 | animation.fromValue = innerCircleLayer.presentation()?.value(forKeyPath: "transform.scale")
136 | animation.toValue = value
137 | animation.duration = duration
138 | animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
139 | animation.beginTime = CACurrentMediaTime()
140 | animation.fillMode = kCAFillModeForwards
141 | animation.isRemovedOnCompletion = false
142 | return animation
143 | }
144 |
145 | }
146 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Views/RARecordDurationLabel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RARecordDurationLabel.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 12/1/17.
6 | //
7 |
8 | import UIKit
9 |
10 | /*
11 | RARecordDurationLabel: Label that can be used to show duration during recording or just any duration in general.
12 | */
13 | final class RARecordDurationLabel : UILabel {
14 |
15 | private var indicatorLayer: CALayer = {
16 | let layer = CALayer()
17 | layer.masksToBounds = true
18 | layer.backgroundColor = UIColor(red: 234/255, green: 53/255, blue: 52/255, alpha: 1).cgColor
19 | layer.frame.size = CGSize(width: 6, height: 6)
20 | layer.cornerRadius = layer.frame.width/2
21 | layer.opacity = 0 //by default hidden
22 | return layer
23 | }()
24 |
25 | override init(frame: CGRect) {
26 | super.init(frame: frame)
27 | commonInit()
28 | }
29 |
30 | required init?(coder aDecoder: NSCoder) {
31 | super.init(coder: aDecoder)
32 | commonInit()
33 | }
34 |
35 | override func layoutSubviews() {
36 | super.layoutSubviews()
37 | indicatorLayer.position = CGPoint(x: -7, y: bounds.height/2)
38 | }
39 |
40 | // MARK: - Public Methods
41 | private var backingSeconds: TimeInterval = 10000 {
42 | didSet {
43 | updateLabel()
44 | }
45 | }
46 |
47 | func start() {
48 |
49 | guard secondTimer == nil else {
50 | return
51 | }
52 |
53 | secondTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { [weak self] (timer) in
54 | self?.backingSeconds += 1
55 | })
56 | secondTimer?.tolerance = 0.1
57 |
58 | indicatorTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { [weak self] (timer) in
59 | self?.updateIndicator(appearDelay: 0.2)
60 | })
61 | indicatorTimer?.tolerance = 0.1
62 |
63 | updateIndicator(appearDelay: 0)
64 | }
65 |
66 | func stop() {
67 | secondTimer?.invalidate()
68 | secondTimer = nil
69 | backingSeconds = 0
70 | updateLabel()
71 |
72 | indicatorTimer?.invalidate()
73 | indicatorTimer = nil
74 | indicatorLayer.removeAllAnimations()
75 | indicatorLayer.opacity = 0
76 | }
77 |
78 | // MARK: - Private Methods
79 | private var secondTimer: Timer?
80 | private var indicatorTimer: Timer?
81 |
82 | private func updateLabel() {
83 |
84 | /*
85 | DateComponentsFormatter, because it does not pad zero to hours component so it regurns pattern 0:00:00, we need 00:00:00
86 | */
87 | let hours = Int(backingSeconds) / 3600
88 | let minutes = Int(backingSeconds) / 60 % 60
89 | let seconds = Int(backingSeconds) % 60
90 | text = String(format:"%02i:%02i:%02i", hours, minutes, seconds)
91 | }
92 |
93 | private func updateIndicator(appearDelay: CFTimeInterval = 0) {
94 |
95 | let disappearDelay: CFTimeInterval = 0.25
96 |
97 | let appear = appearAnimation(delay: appearDelay)
98 | let disappear = disappearAnimation(delay: appear.beginTime + appear.duration + disappearDelay)
99 |
100 | let animation = CAAnimationGroup()
101 | animation.animations = [appear, disappear]
102 | animation.duration = appear.duration + disappear.duration + appearDelay + disappearDelay
103 | animation.isRemovedOnCompletion = true
104 |
105 | indicatorLayer.add(animation, forKey: "blinkAnimationKey")
106 | }
107 |
108 | private func commonInit() {
109 | layer.addSublayer(indicatorLayer)
110 | clipsToBounds = false
111 | }
112 |
113 | private func appearAnimation(delay: CFTimeInterval = 0) -> CAAnimation {
114 | let appear = CABasicAnimation(keyPath: "opacity")
115 | appear.fromValue = indicatorLayer.presentation()?.opacity
116 | appear.toValue = 1
117 | appear.duration = 0.15
118 | appear.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
119 | appear.beginTime = delay
120 | appear.fillMode = kCAFillModeForwards
121 | return appear
122 | }
123 |
124 | private func disappearAnimation(delay: CFTimeInterval = 0) -> CAAnimation {
125 | let disappear = CABasicAnimation(keyPath: "opacity")
126 | disappear.fromValue = indicatorLayer.presentation()?.opacity
127 | disappear.toValue = 0
128 | disappear.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
129 | disappear.beginTime = delay
130 | disappear.duration = 0.25
131 | return disappear
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Views/RAShutterButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RAShutterButton.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 12/1/17.
6 | //
7 |
8 | import Foundation
9 |
10 | /*
11 | RAShutterButton: a rounded button that has a circle inside and is used when taking pictures.
12 | */
13 | class RAShutterButton : UIButton {
14 |
15 | var outerBorderWidth: CGFloat = 3
16 | var innerBorderWidth: CGFloat = 1.5
17 | var pressDepthFactor: CGFloat = 0.9
18 |
19 | override var isHighlighted: Bool {
20 | didSet { setInnerLayer(tapped: isHighlighted, animated: true) }
21 | }
22 |
23 | private var innerCircleLayerInset: CGFloat {
24 | return outerBorderWidth + innerBorderWidth
25 | }
26 |
27 | private var outerCircleLayer: CALayer
28 | private var innerCircleLayer: CALayer
29 |
30 | required init?(coder aDecoder: NSCoder) {
31 | outerCircleLayer = CALayer()
32 | innerCircleLayer = CALayer()
33 | super.init(coder: aDecoder)
34 | backgroundColor = UIColor.clear
35 | layer.addSublayer(outerCircleLayer)
36 | layer.addSublayer(innerCircleLayer)
37 |
38 | CATransaction.setDisableActions(true)
39 |
40 | outerCircleLayer.backgroundColor = UIColor.clear.cgColor
41 | outerCircleLayer.cornerRadius = bounds.width/2
42 | outerCircleLayer.borderWidth = outerBorderWidth
43 | outerCircleLayer.borderColor = tintColor.cgColor
44 |
45 | innerCircleLayer.backgroundColor = tintColor.cgColor
46 |
47 | CATransaction.commit()
48 | }
49 |
50 | func setInnerLayer(tapped: Bool, animated: Bool) {
51 |
52 | if animated {
53 | let animation = CABasicAnimation()
54 | animation.keyPath = "transform.scale"
55 |
56 | if tapped {
57 | animation.fromValue = innerCircleLayer.presentation()?.value(forKeyPath: "transform.scale")
58 | animation.toValue = pressDepthFactor
59 | animation.duration = 0.25
60 | }
61 | else {
62 | animation.fromValue = pressDepthFactor
63 | animation.toValue = 1.0
64 | animation.duration = 0.25
65 | }
66 |
67 | animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
68 | animation.beginTime = CACurrentMediaTime()
69 | animation.fillMode = kCAFillModeForwards
70 | animation.isRemovedOnCompletion = false
71 |
72 | innerCircleLayer.add(animation, forKey: nil)
73 | }
74 | else {
75 | CATransaction.setDisableActions(true)
76 | if tapped {
77 | innerCircleLayer.setValue(pressDepthFactor, forKeyPath: "transform.scale")
78 | }
79 | else {
80 | innerCircleLayer.setValue(CGFloat(1), forKeyPath: "transform.scale")
81 | }
82 | CATransaction.commit()
83 | }
84 | }
85 |
86 | override func layoutSubviews() {
87 | super.layoutSubviews()
88 |
89 | CATransaction.setDisableActions(true)
90 | outerCircleLayer.frame = bounds
91 | innerCircleLayer.frame = bounds.insetBy(dx: innerCircleLayerInset, dy: innerCircleLayerInset)
92 | innerCircleLayer.cornerRadius = bounds.insetBy(dx: innerCircleLayerInset, dy: innerCircleLayerInset).width/2
93 | CATransaction.commit()
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/RAImagePicker/Classes/Views/RAStationaryButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RAStationaryButton.swift
3 | // RAImagePicker
4 | //
5 | // Created by Rashed Al Lahaseh on 12/1/17.
6 | //
7 |
8 | import Foundation
9 |
10 | /*
11 | RAStationaryButton: a button that keeps selected state when selected.
12 | */
13 | class RAStationaryButton : UIButton {
14 |
15 | var unselectedTintColor: UIColor?
16 | var selectedTintColor: UIColor?
17 |
18 | open override var isSelected: Bool {
19 | get { return super.isSelected }
20 | set { setSelected(newValue, animated: false) }
21 | }
22 |
23 | open override var isHighlighted: Bool {
24 | didSet {
25 | if isHighlighted == false {
26 | setSelected(!isSelected, animated: true)
27 | }
28 | }
29 | }
30 |
31 | public func setSelected(_ selected: Bool, animated: Bool) {
32 |
33 | guard isSelected != selected else {
34 | return
35 | }
36 |
37 | super.isSelected = selected
38 | selectionDidChange(animated: animated)
39 | }
40 |
41 | open override func awakeFromNib() {
42 | super.awakeFromNib()
43 | updateTint()
44 | }
45 |
46 | // Note: Override this method to track when button's state is selected or deselected. You dont need to call super, default implementation does nothing.
47 | open func selectionDidChange(animated: Bool) {
48 | updateTint()
49 | }
50 |
51 | private func updateTint() {
52 | if isSelected {
53 | tintColor = selectedTintColor
54 | }
55 | else {
56 | tintColor = unselectedTintColor
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/_Pods.xcodeproj:
--------------------------------------------------------------------------------
1 | Example/Pods/Pods.xcodeproj
--------------------------------------------------------------------------------