├── .gitignore ├── .swiftlint.yml ├── CHANGELOG.MD ├── LICENSE ├── OpalImagePicker.podspec ├── OpalImagePicker.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── OpalImagePicker ├── Source │ ├── Bundle+Helper.swift │ ├── ImagePickerCollectionViewCell.swift │ ├── OpalImagePickerCollectionViewLayout.swift │ ├── OpalImagePickerConfiguration.swift │ ├── OpalImagePickerController.swift │ ├── OpalImagePickerRootViewController.swift │ ├── TimeInterval+String.swift │ ├── UIView+Helper.swift │ └── UIViewController+OpalImagePicker.swift └── SupportingFiles │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── checkmark.imageset │ │ ├── Contents.json │ │ └── checkmark.pdf │ └── gradient.imageset │ │ ├── Contents.json │ │ └── gradient.pdf │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── ViewController.swift ├── OpalImagePickerDemo ├── .swiftlint.yml ├── OpalImagePickerDemo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── OpalImagePickerDemo.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── Kris.xcuserdatad │ │ └── IDEFindNavigatorScopes.plist ├── OpalImagePickerDemo │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── DelegateExampleViewController.swift │ ├── Info.plist │ └── ViewController.swift ├── OpalImagePickerDemoTests │ ├── Info.plist │ └── OpalImagePickerDemoTests.swift ├── Podfile ├── Podfile.lock └── Pods │ ├── Local Podspecs │ └── OpalImagePicker.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ └── project.pbxproj │ └── Target Support Files │ ├── OpalImagePicker │ ├── Info.plist │ ├── OpalImagePicker-Info.plist │ ├── OpalImagePicker-dummy.m │ ├── OpalImagePicker-prefix.pch │ ├── OpalImagePicker-umbrella.h │ ├── OpalImagePicker.modulemap │ ├── OpalImagePicker.xcconfig │ ├── ResourceBundle-OpalImagePickerResources-Info.plist │ └── ResourceBundle-OpalImagePickerResources-OpalImagePicker-Info.plist │ ├── Pods-OpalImagePickerDemo │ ├── Info.plist │ ├── Pods-OpalImagePickerDemo-Info.plist │ ├── Pods-OpalImagePickerDemo-acknowledgements.markdown │ ├── Pods-OpalImagePickerDemo-acknowledgements.plist │ ├── Pods-OpalImagePickerDemo-dummy.m │ ├── Pods-OpalImagePickerDemo-frameworks.sh │ ├── Pods-OpalImagePickerDemo-resources.sh │ ├── Pods-OpalImagePickerDemo-umbrella.h │ ├── Pods-OpalImagePickerDemo.debug.xcconfig │ ├── Pods-OpalImagePickerDemo.modulemap │ └── Pods-OpalImagePickerDemo.release.xcconfig │ └── Pods-OpalImagePickerDemoTests │ ├── Info.plist │ ├── Pods-OpalImagePickerDemoTests-Info.plist │ ├── Pods-OpalImagePickerDemoTests-acknowledgements.markdown │ ├── Pods-OpalImagePickerDemoTests-acknowledgements.plist │ ├── Pods-OpalImagePickerDemoTests-dummy.m │ ├── Pods-OpalImagePickerDemoTests-frameworks.sh │ ├── Pods-OpalImagePickerDemoTests-resources.sh │ ├── Pods-OpalImagePickerDemoTests-umbrella.h │ ├── Pods-OpalImagePickerDemoTests.debug.xcconfig │ ├── Pods-OpalImagePickerDemoTests.modulemap │ └── Pods-OpalImagePickerDemoTests.release.xcconfig ├── OpalImagePickerTests ├── Info.plist ├── MockOpalImagePickerControllerDelegate.swift ├── OpalImagePickerControllerTest.swift └── OpalImagePickerRootViewControllerTest.swift ├── Package.swift ├── README.md └── Resources └── OpalImagePickerPresentation.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | **/*contents.xcworkspacedata 2 | *.xcuserstate 3 | *.xcscheme 4 | xcschememanagement.plist 5 | *.xcbkptlist 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - line_length 3 | - trailing_whitespace 4 | - function_parameter_count 5 | - type_body_length 6 | - file_length 7 | - weak_delegate -------------------------------------------------------------------------------- /CHANGELOG.MD: -------------------------------------------------------------------------------- 1 | ## [3.0.0](https://github.com/opalorange/OpalImagePicker/releases/tag/3.0.0) 2 | Released on 2020-05-17 3 | 4 | - Bugfix: PHAssetMediaSubtype.photoScreenshot not accessible in iOS 12.4 version #60 5 | - Bugfix: Deprecated function #56 6 | 7 | 8 | ## [2.1.2](https://github.com/opalorange/OpalImagePicker/releases/tag/2.1.2) 9 | Released on 2019-12-31 10 | 11 | - The OpalImagePickerConfiguration isn't usable from Objective-C because the properties aren't defined with @objc. 12 | 13 | ## [2.1.1](https://github.com/opalorange/OpalImagePicker/releases/tag/2.1.1) 14 | Released on 2019-12-31 15 | 16 | - Deprecate: UIImage based APIs. Instead users will need to use PHAsset and handle downloading from iCloud. 17 | 18 | 19 | ## [2.1.0](https://github.com/opalorange/OpalImagePicker/releases/tag/2.1.0) 20 | Released on 2019-07-11 21 | 22 | - Enhancement: Support for Swift 5.0 and Xcode 10.2.1. Support for Cocoapods 1.7.0 23 | 24 | ## [2.0.0](https://github.com/opalorange/OpalImagePicker/releases/tag/2.0.0) 25 | Released on 2018-10-02 26 | 27 | - Enhancement: Support for Swift 4.2 and Xcode 10.0 28 | 29 | ## [1.7.2](https://github.com/opalorange/OpalImagePicker/releases/tag/1.7.2) 30 | Released on 2018-04-07 31 | 32 | - Bug fix: Live photos load slowly, can cause photos to become jumbled/out of order when scrolling quickly #24 33 | 34 | ## [1.7.1](https://github.com/opalorange/OpalImagePicker/releases/tag/1.7.1) 35 | Released on 2018-01-19 36 | 37 | - SwiftLint for coding standards improvement 38 | 39 | #### Fixed 40 | - Bug fix: Retrieve full images for imagePicker(_:didFinishPickingImages:) 41 | 42 | 43 | ## [1.7.0](https://github.com/opalorange/OpalImagePicker/releases/tag/1.7.0) 44 | Released on 2018-01-19 45 | 46 | #### Added 47 | - Enhancement: Add time label for video assets #14 48 | - Enhancement: Exposed OpalImagePickerController methods and properties to ObjC 49 | 50 | ## [1.6.0](https://github.com/opalorange/OpalImagePicker/releases/tag/1.6.0) 51 | Released on 2017-09-24 52 | 53 | #### Added 54 | - Enhancement: Add support for iOS 8.0 55 | 56 | ## [1.5.0](https://github.com/opalorange/OpalImagePicker/releases/tag/1.5.0) 57 | Released on 2017-09-24 58 | 59 | #### Added 60 | - Enhancement: Convert OpalImagePicker and OpalImagePickerDemo to Swift 4.0 61 | 62 | #### Fixed 63 | - Bug fix: Issue: navigation bar item localize #11 64 | - Bug fix: Issue: Order of assets in completion handler #12 65 | - Bug fix: External Images crash on loading image by URL. 66 | 67 | ## [1.4.0](https://github.com/opalorange/OpalImagePicker/releases/tag/1.4.0) 68 | Released on 2017-08-09 69 | 70 | #### Added 71 | - Enhancement: Allow users to use external images using URLs with new optional delegate methods (requested for use with social media). 72 | - Enhancement: Allow users to configure localized strings from new `OpalImagePickerConfiguration` class. 73 | 74 | ## [1.3.0](https://github.com/opalorange/OpalImagePicker/releases/tag/1.3.0) 75 | Released on 2017-04-09 76 | 77 | #### Added 78 | - Enhancement: Allow users to mark maximum allowed photos, and select allowed media types and sub types. 79 | 80 | ## [1.2.0](https://github.com/opalorange/OpalImagePicker/releases/tag/1.2.0) 81 | Released on 2017-02-17 82 | 83 | #### Added 84 | - Enhancement: Allow users to change the status bar style 85 | 86 | #### Fixed 87 | - Bug fix: Fix spinners before images have loaded from library. 88 | 89 | ## [1.1.3](https://github.com/opalorange/OpalImagePicker/releases/tag/1.1.3) 90 | Released on 2017-02-13 91 | 92 | #### Fixed 93 | - Bug fix: More improvement - Sometimes images don't appear as sharp as they should be. 94 | 95 | ## [1.1.2](https://github.com/opalorange/OpalImagePicker/releases/tag/1.1.2) 96 | Released on 2017-02-05. 97 | 98 | #### Fixed 99 | - Bug fix: Sometimes images don't appear as sharp as they should be. 100 | 101 | ## [1.1.1](https://github.com/opalorange/OpalImagePicker/releases/tag/1.1.1) 102 | Released on 2017-01-19. 103 | 104 | #### Fixed 105 | - Bug fix: Issue: Images Array Problem #1 106 | 107 | ## [1.1.0](https://github.com/opalorange/OpalImagePicker/releases/tag/1.1.0) 108 | Released on 2017-01-16. 109 | 110 | #### Fixed 111 | - Only get UIImage from PHAsset after selecting if the delegate method is implemented to increase efficiency. 112 | 113 | #### Updated 114 | - Updated README for github display. 115 | - Updated Documentation 116 | - Updated Unit Tests 117 | 118 | ## [1.0.0](https://github.com/opalorange/OpalImagePicker/releases/tag/1.0.0) 119 | Released on 2017-01-15. 120 | 121 | #### Added 122 | - Initial release of OpalImagePicker. 123 | - Added by [Kris Katsanevas](https://github.com/opalorange). -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 opalorange 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /OpalImagePicker.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "OpalImagePicker" 3 | s.version = "3.0.0" 4 | s.summary = "Multiple Selection Image Picker for iOS written in Swift" 5 | s.homepage = "https://github.com/opalorange/OpalImagePicker" 6 | s.license = { :type => "MIT", :file => "LICENSE" } 7 | s.author = { "opalorange" => "kris@opalorange.com" } 8 | s.requires_arc = true 9 | s.ios.deployment_target = '10.0' 10 | s.source = { :git => "https://github.com/opalorange/OpalImagePicker.git", :tag => s.version } 11 | s.source_files = "OpalImagePicker/Source/*.swift" 12 | s.resource_bundles = { 'OpalImagePickerResources' => ['OpalImagePicker/SupportingFiles/**/*.xcassets'] } 13 | s.swift_version = '5.0' 14 | end 15 | -------------------------------------------------------------------------------- /OpalImagePicker.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 010B06532013B5E6002811B6 /* TimeInterval+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 010B06522013B5E6002811B6 /* TimeInterval+String.swift */; }; 11 | 0181CB5B1E2DB2AE00928D85 /* OpalImagePickerControllerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0181CB5A1E2DB2AE00928D85 /* OpalImagePickerControllerTest.swift */; }; 12 | 0181CB5D1E2DB31E00928D85 /* OpalImagePickerRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0181CB5C1E2DB31E00928D85 /* OpalImagePickerRootViewController.swift */; }; 13 | 0181CB5F1E2DB3CF00928D85 /* MockOpalImagePickerControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0181CB5E1E2DB3CF00928D85 /* MockOpalImagePickerControllerDelegate.swift */; }; 14 | 0181CB651E2DB83100928D85 /* OpalImagePickerRootViewControllerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0181CB641E2DB83100928D85 /* OpalImagePickerRootViewControllerTest.swift */; }; 15 | 01AE6CB31F3BBC9500222A6A /* OpalImagePickerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01AE6CB21F3BBC9500222A6A /* OpalImagePickerConfiguration.swift */; }; 16 | 01B2D5F31F805D89005D2473 /* UIView+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B2D5F21F805D89005D2473 /* UIView+Helper.swift */; }; 17 | 01D2F91D1E2B6BC70038A37C /* Bundle+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D2F91C1E2B6BC70038A37C /* Bundle+Helper.swift */; }; 18 | 01DD8B581E2B5DED0081B1FC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DD8B551E2B5DED0081B1FC /* AppDelegate.swift */; }; 19 | 01DD8B5A1E2B5DEE0081B1FC /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DD8B571E2B5DED0081B1FC /* ViewController.swift */; }; 20 | 01DD8B5C1E2B5DF50081B1FC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01DD8B5B1E2B5DF50081B1FC /* Assets.xcassets */; }; 21 | 01DD8B611E2B5E010081B1FC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 01DD8B5D1E2B5E010081B1FC /* LaunchScreen.storyboard */; }; 22 | 01DD8B621E2B5E010081B1FC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 01DD8B5F1E2B5E010081B1FC /* Main.storyboard */; }; 23 | 01DD8B641E2B5E2F0081B1FC /* UIViewController+OpalImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DD8B631E2B5E2F0081B1FC /* UIViewController+OpalImagePicker.swift */; }; 24 | 01DD8B661E2B5E6C0081B1FC /* OpalImagePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DD8B651E2B5E6C0081B1FC /* OpalImagePickerController.swift */; }; 25 | 01DD8B681E2B5EA20081B1FC /* ImagePickerCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DD8B671E2B5EA20081B1FC /* ImagePickerCollectionViewCell.swift */; }; 26 | 01DD8B6A1E2B5EDB0081B1FC /* OpalImagePickerCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DD8B691E2B5EDB0081B1FC /* OpalImagePickerCollectionViewLayout.swift */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXContainerItemProxy section */ 30 | 01DD8B451E2B5CB90081B1FC /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 01DD8B281E2B5CB90081B1FC /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = 01DD8B2F1E2B5CB90081B1FC; 35 | remoteInfo = OpalImagePicker; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 010B06522013B5E6002811B6 /* TimeInterval+String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TimeInterval+String.swift"; sourceTree = ""; }; 41 | 0181CB5A1E2DB2AE00928D85 /* OpalImagePickerControllerTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpalImagePickerControllerTest.swift; sourceTree = ""; }; 42 | 0181CB5C1E2DB31E00928D85 /* OpalImagePickerRootViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpalImagePickerRootViewController.swift; sourceTree = ""; }; 43 | 0181CB5E1E2DB3CF00928D85 /* MockOpalImagePickerControllerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockOpalImagePickerControllerDelegate.swift; sourceTree = ""; }; 44 | 0181CB641E2DB83100928D85 /* OpalImagePickerRootViewControllerTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpalImagePickerRootViewControllerTest.swift; sourceTree = ""; }; 45 | 01AE6CB21F3BBC9500222A6A /* OpalImagePickerConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpalImagePickerConfiguration.swift; sourceTree = ""; }; 46 | 01B2D5F21F805D89005D2473 /* UIView+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Helper.swift"; sourceTree = ""; }; 47 | 01D2F91C1E2B6BC70038A37C /* Bundle+Helper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bundle+Helper.swift"; sourceTree = ""; }; 48 | 01DD8B301E2B5CB90081B1FC /* OpalImagePicker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OpalImagePicker.app; sourceTree = BUILT_PRODUCTS_DIR; }; 49 | 01DD8B441E2B5CB90081B1FC /* OpalImagePickerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OpalImagePickerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | 01DD8B4A1E2B5CB90081B1FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51 | 01DD8B551E2B5DED0081B1FC /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 52 | 01DD8B561E2B5DED0081B1FC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | 01DD8B571E2B5DED0081B1FC /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 54 | 01DD8B5B1E2B5DF50081B1FC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 55 | 01DD8B5E1E2B5E010081B1FC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 56 | 01DD8B601E2B5E010081B1FC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 57 | 01DD8B631E2B5E2F0081B1FC /* UIViewController+OpalImagePicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+OpalImagePicker.swift"; sourceTree = ""; }; 58 | 01DD8B651E2B5E6C0081B1FC /* OpalImagePickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpalImagePickerController.swift; sourceTree = ""; }; 59 | 01DD8B671E2B5EA20081B1FC /* ImagePickerCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImagePickerCollectionViewCell.swift; sourceTree = ""; }; 60 | 01DD8B691E2B5EDB0081B1FC /* OpalImagePickerCollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpalImagePickerCollectionViewLayout.swift; sourceTree = ""; }; 61 | /* End PBXFileReference section */ 62 | 63 | /* Begin PBXFrameworksBuildPhase section */ 64 | 01DD8B2D1E2B5CB90081B1FC /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | 01DD8B411E2B5CB90081B1FC /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | /* End PBXFrameworksBuildPhase section */ 79 | 80 | /* Begin PBXGroup section */ 81 | 01DD8B271E2B5CB90081B1FC = { 82 | isa = PBXGroup; 83 | children = ( 84 | 01DD8B321E2B5CB90081B1FC /* OpalImagePicker */, 85 | 01DD8B471E2B5CB90081B1FC /* OpalImagePickerTests */, 86 | 01DD8B311E2B5CB90081B1FC /* Products */, 87 | ); 88 | sourceTree = ""; 89 | }; 90 | 01DD8B311E2B5CB90081B1FC /* Products */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 01DD8B301E2B5CB90081B1FC /* OpalImagePicker.app */, 94 | 01DD8B441E2B5CB90081B1FC /* OpalImagePickerTests.xctest */, 95 | ); 96 | name = Products; 97 | sourceTree = ""; 98 | }; 99 | 01DD8B321E2B5CB90081B1FC /* OpalImagePicker */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 01DD8B541E2B5CD80081B1FC /* SupportingFiles */, 103 | 01DD8B531E2B5CC50081B1FC /* Source */, 104 | ); 105 | path = OpalImagePicker; 106 | sourceTree = ""; 107 | }; 108 | 01DD8B471E2B5CB90081B1FC /* OpalImagePickerTests */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 0181CB5E1E2DB3CF00928D85 /* MockOpalImagePickerControllerDelegate.swift */, 112 | 0181CB5A1E2DB2AE00928D85 /* OpalImagePickerControllerTest.swift */, 113 | 0181CB641E2DB83100928D85 /* OpalImagePickerRootViewControllerTest.swift */, 114 | 01DD8B4A1E2B5CB90081B1FC /* Info.plist */, 115 | ); 116 | path = OpalImagePickerTests; 117 | sourceTree = ""; 118 | }; 119 | 01DD8B531E2B5CC50081B1FC /* Source */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 01DD8B631E2B5E2F0081B1FC /* UIViewController+OpalImagePicker.swift */, 123 | 01AE6CB21F3BBC9500222A6A /* OpalImagePickerConfiguration.swift */, 124 | 01DD8B651E2B5E6C0081B1FC /* OpalImagePickerController.swift */, 125 | 0181CB5C1E2DB31E00928D85 /* OpalImagePickerRootViewController.swift */, 126 | 01DD8B691E2B5EDB0081B1FC /* OpalImagePickerCollectionViewLayout.swift */, 127 | 01DD8B671E2B5EA20081B1FC /* ImagePickerCollectionViewCell.swift */, 128 | 01D2F91C1E2B6BC70038A37C /* Bundle+Helper.swift */, 129 | 01B2D5F21F805D89005D2473 /* UIView+Helper.swift */, 130 | 010B06522013B5E6002811B6 /* TimeInterval+String.swift */, 131 | ); 132 | path = Source; 133 | sourceTree = ""; 134 | }; 135 | 01DD8B541E2B5CD80081B1FC /* SupportingFiles */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 01DD8B5D1E2B5E010081B1FC /* LaunchScreen.storyboard */, 139 | 01DD8B5F1E2B5E010081B1FC /* Main.storyboard */, 140 | 01DD8B5B1E2B5DF50081B1FC /* Assets.xcassets */, 141 | 01DD8B551E2B5DED0081B1FC /* AppDelegate.swift */, 142 | 01DD8B561E2B5DED0081B1FC /* Info.plist */, 143 | 01DD8B571E2B5DED0081B1FC /* ViewController.swift */, 144 | ); 145 | path = SupportingFiles; 146 | sourceTree = ""; 147 | }; 148 | /* End PBXGroup section */ 149 | 150 | /* Begin PBXNativeTarget section */ 151 | 01DD8B2F1E2B5CB90081B1FC /* OpalImagePicker */ = { 152 | isa = PBXNativeTarget; 153 | buildConfigurationList = 01DD8B4D1E2B5CB90081B1FC /* Build configuration list for PBXNativeTarget "OpalImagePicker" */; 154 | buildPhases = ( 155 | 01DD8B2C1E2B5CB90081B1FC /* Sources */, 156 | 01DD8B2D1E2B5CB90081B1FC /* Frameworks */, 157 | 01DD8B2E1E2B5CB90081B1FC /* Resources */, 158 | 010B06542013BA30002811B6 /* ShellScript */, 159 | ); 160 | buildRules = ( 161 | ); 162 | dependencies = ( 163 | ); 164 | name = OpalImagePicker; 165 | productName = OpalImagePicker; 166 | productReference = 01DD8B301E2B5CB90081B1FC /* OpalImagePicker.app */; 167 | productType = "com.apple.product-type.application"; 168 | }; 169 | 01DD8B431E2B5CB90081B1FC /* OpalImagePickerTests */ = { 170 | isa = PBXNativeTarget; 171 | buildConfigurationList = 01DD8B501E2B5CB90081B1FC /* Build configuration list for PBXNativeTarget "OpalImagePickerTests" */; 172 | buildPhases = ( 173 | 01DD8B401E2B5CB90081B1FC /* Sources */, 174 | 01DD8B411E2B5CB90081B1FC /* Frameworks */, 175 | 01DD8B421E2B5CB90081B1FC /* Resources */, 176 | ); 177 | buildRules = ( 178 | ); 179 | dependencies = ( 180 | 01DD8B461E2B5CB90081B1FC /* PBXTargetDependency */, 181 | ); 182 | name = OpalImagePickerTests; 183 | productName = OpalImagePickerTests; 184 | productReference = 01DD8B441E2B5CB90081B1FC /* OpalImagePickerTests.xctest */; 185 | productType = "com.apple.product-type.bundle.unit-test"; 186 | }; 187 | /* End PBXNativeTarget section */ 188 | 189 | /* Begin PBXProject section */ 190 | 01DD8B281E2B5CB90081B1FC /* Project object */ = { 191 | isa = PBXProject; 192 | attributes = { 193 | LastSwiftUpdateCheck = 0820; 194 | LastUpgradeCheck = 1000; 195 | ORGANIZATIONNAME = "Opal Orange LLC"; 196 | TargetAttributes = { 197 | 01DD8B2F1E2B5CB90081B1FC = { 198 | CreatedOnToolsVersion = 8.2; 199 | LastSwiftMigration = 1020; 200 | ProvisioningStyle = Automatic; 201 | }; 202 | 01DD8B431E2B5CB90081B1FC = { 203 | CreatedOnToolsVersion = 8.2; 204 | LastSwiftMigration = 1020; 205 | ProvisioningStyle = Automatic; 206 | TestTargetID = 01DD8B2F1E2B5CB90081B1FC; 207 | }; 208 | }; 209 | }; 210 | buildConfigurationList = 01DD8B2B1E2B5CB90081B1FC /* Build configuration list for PBXProject "OpalImagePicker" */; 211 | compatibilityVersion = "Xcode 3.2"; 212 | developmentRegion = en; 213 | hasScannedForEncodings = 0; 214 | knownRegions = ( 215 | en, 216 | Base, 217 | ); 218 | mainGroup = 01DD8B271E2B5CB90081B1FC; 219 | productRefGroup = 01DD8B311E2B5CB90081B1FC /* Products */; 220 | projectDirPath = ""; 221 | projectRoot = ""; 222 | targets = ( 223 | 01DD8B2F1E2B5CB90081B1FC /* OpalImagePicker */, 224 | 01DD8B431E2B5CB90081B1FC /* OpalImagePickerTests */, 225 | ); 226 | }; 227 | /* End PBXProject section */ 228 | 229 | /* Begin PBXResourcesBuildPhase section */ 230 | 01DD8B2E1E2B5CB90081B1FC /* Resources */ = { 231 | isa = PBXResourcesBuildPhase; 232 | buildActionMask = 2147483647; 233 | files = ( 234 | 01DD8B621E2B5E010081B1FC /* Main.storyboard in Resources */, 235 | 01DD8B611E2B5E010081B1FC /* LaunchScreen.storyboard in Resources */, 236 | 01DD8B5C1E2B5DF50081B1FC /* Assets.xcassets in Resources */, 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | 01DD8B421E2B5CB90081B1FC /* Resources */ = { 241 | isa = PBXResourcesBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | ); 245 | runOnlyForDeploymentPostprocessing = 0; 246 | }; 247 | /* End PBXResourcesBuildPhase section */ 248 | 249 | /* Begin PBXShellScriptBuildPhase section */ 250 | 010B06542013BA30002811B6 /* ShellScript */ = { 251 | isa = PBXShellScriptBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | ); 255 | inputPaths = ( 256 | ); 257 | outputPaths = ( 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | shellPath = /bin/sh; 261 | shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; 262 | }; 263 | /* End PBXShellScriptBuildPhase section */ 264 | 265 | /* Begin PBXSourcesBuildPhase section */ 266 | 01DD8B2C1E2B5CB90081B1FC /* Sources */ = { 267 | isa = PBXSourcesBuildPhase; 268 | buildActionMask = 2147483647; 269 | files = ( 270 | 01DD8B641E2B5E2F0081B1FC /* UIViewController+OpalImagePicker.swift in Sources */, 271 | 01AE6CB31F3BBC9500222A6A /* OpalImagePickerConfiguration.swift in Sources */, 272 | 01DD8B681E2B5EA20081B1FC /* ImagePickerCollectionViewCell.swift in Sources */, 273 | 0181CB5D1E2DB31E00928D85 /* OpalImagePickerRootViewController.swift in Sources */, 274 | 01B2D5F31F805D89005D2473 /* UIView+Helper.swift in Sources */, 275 | 010B06532013B5E6002811B6 /* TimeInterval+String.swift in Sources */, 276 | 01DD8B5A1E2B5DEE0081B1FC /* ViewController.swift in Sources */, 277 | 01DD8B581E2B5DED0081B1FC /* AppDelegate.swift in Sources */, 278 | 01DD8B6A1E2B5EDB0081B1FC /* OpalImagePickerCollectionViewLayout.swift in Sources */, 279 | 01D2F91D1E2B6BC70038A37C /* Bundle+Helper.swift in Sources */, 280 | 01DD8B661E2B5E6C0081B1FC /* OpalImagePickerController.swift in Sources */, 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | }; 284 | 01DD8B401E2B5CB90081B1FC /* Sources */ = { 285 | isa = PBXSourcesBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | 0181CB5F1E2DB3CF00928D85 /* MockOpalImagePickerControllerDelegate.swift in Sources */, 289 | 0181CB5B1E2DB2AE00928D85 /* OpalImagePickerControllerTest.swift in Sources */, 290 | 0181CB651E2DB83100928D85 /* OpalImagePickerRootViewControllerTest.swift in Sources */, 291 | ); 292 | runOnlyForDeploymentPostprocessing = 0; 293 | }; 294 | /* End PBXSourcesBuildPhase section */ 295 | 296 | /* Begin PBXTargetDependency section */ 297 | 01DD8B461E2B5CB90081B1FC /* PBXTargetDependency */ = { 298 | isa = PBXTargetDependency; 299 | target = 01DD8B2F1E2B5CB90081B1FC /* OpalImagePicker */; 300 | targetProxy = 01DD8B451E2B5CB90081B1FC /* PBXContainerItemProxy */; 301 | }; 302 | /* End PBXTargetDependency section */ 303 | 304 | /* Begin PBXVariantGroup section */ 305 | 01DD8B5D1E2B5E010081B1FC /* LaunchScreen.storyboard */ = { 306 | isa = PBXVariantGroup; 307 | children = ( 308 | 01DD8B5E1E2B5E010081B1FC /* Base */, 309 | ); 310 | name = LaunchScreen.storyboard; 311 | sourceTree = ""; 312 | }; 313 | 01DD8B5F1E2B5E010081B1FC /* Main.storyboard */ = { 314 | isa = PBXVariantGroup; 315 | children = ( 316 | 01DD8B601E2B5E010081B1FC /* Base */, 317 | ); 318 | name = Main.storyboard; 319 | sourceTree = ""; 320 | }; 321 | /* End PBXVariantGroup section */ 322 | 323 | /* Begin XCBuildConfiguration section */ 324 | 01DD8B4B1E2B5CB90081B1FC /* Debug */ = { 325 | isa = XCBuildConfiguration; 326 | buildSettings = { 327 | ALWAYS_SEARCH_USER_PATHS = NO; 328 | CLANG_ANALYZER_NONNULL = YES; 329 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 330 | CLANG_CXX_LIBRARY = "libc++"; 331 | CLANG_ENABLE_MODULES = YES; 332 | CLANG_ENABLE_OBJC_ARC = YES; 333 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 334 | CLANG_WARN_BOOL_CONVERSION = YES; 335 | CLANG_WARN_COMMA = YES; 336 | CLANG_WARN_CONSTANT_CONVERSION = YES; 337 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 338 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 339 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 340 | CLANG_WARN_EMPTY_BODY = YES; 341 | CLANG_WARN_ENUM_CONVERSION = YES; 342 | CLANG_WARN_INFINITE_RECURSION = YES; 343 | CLANG_WARN_INT_CONVERSION = YES; 344 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 345 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 346 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 347 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 348 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 349 | CLANG_WARN_STRICT_PROTOTYPES = YES; 350 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 351 | CLANG_WARN_UNREACHABLE_CODE = YES; 352 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 353 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 354 | COPY_PHASE_STRIP = NO; 355 | DEBUG_INFORMATION_FORMAT = dwarf; 356 | ENABLE_STRICT_OBJC_MSGSEND = YES; 357 | ENABLE_TESTABILITY = YES; 358 | GCC_C_LANGUAGE_STANDARD = gnu99; 359 | GCC_DYNAMIC_NO_PIC = NO; 360 | GCC_NO_COMMON_BLOCKS = YES; 361 | GCC_OPTIMIZATION_LEVEL = 0; 362 | GCC_PREPROCESSOR_DEFINITIONS = ( 363 | "DEBUG=1", 364 | "$(inherited)", 365 | ); 366 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 367 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 368 | GCC_WARN_UNDECLARED_SELECTOR = YES; 369 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 370 | GCC_WARN_UNUSED_FUNCTION = YES; 371 | GCC_WARN_UNUSED_VARIABLE = YES; 372 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 373 | MTL_ENABLE_DEBUG_INFO = YES; 374 | ONLY_ACTIVE_ARCH = YES; 375 | SDKROOT = iphoneos; 376 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 377 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 378 | SWIFT_VERSION = 5.0; 379 | TARGETED_DEVICE_FAMILY = "1,2"; 380 | }; 381 | name = Debug; 382 | }; 383 | 01DD8B4C1E2B5CB90081B1FC /* Release */ = { 384 | isa = XCBuildConfiguration; 385 | buildSettings = { 386 | ALWAYS_SEARCH_USER_PATHS = NO; 387 | CLANG_ANALYZER_NONNULL = YES; 388 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 389 | CLANG_CXX_LIBRARY = "libc++"; 390 | CLANG_ENABLE_MODULES = YES; 391 | CLANG_ENABLE_OBJC_ARC = YES; 392 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 393 | CLANG_WARN_BOOL_CONVERSION = YES; 394 | CLANG_WARN_COMMA = YES; 395 | CLANG_WARN_CONSTANT_CONVERSION = YES; 396 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 397 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 398 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 399 | CLANG_WARN_EMPTY_BODY = YES; 400 | CLANG_WARN_ENUM_CONVERSION = YES; 401 | CLANG_WARN_INFINITE_RECURSION = YES; 402 | CLANG_WARN_INT_CONVERSION = YES; 403 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 404 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 405 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 406 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 407 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 408 | CLANG_WARN_STRICT_PROTOTYPES = YES; 409 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 410 | CLANG_WARN_UNREACHABLE_CODE = YES; 411 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 412 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 413 | COPY_PHASE_STRIP = NO; 414 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 415 | ENABLE_NS_ASSERTIONS = NO; 416 | ENABLE_STRICT_OBJC_MSGSEND = YES; 417 | GCC_C_LANGUAGE_STANDARD = gnu99; 418 | GCC_NO_COMMON_BLOCKS = YES; 419 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 420 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 421 | GCC_WARN_UNDECLARED_SELECTOR = YES; 422 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 423 | GCC_WARN_UNUSED_FUNCTION = YES; 424 | GCC_WARN_UNUSED_VARIABLE = YES; 425 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 426 | MTL_ENABLE_DEBUG_INFO = NO; 427 | SDKROOT = iphoneos; 428 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 429 | SWIFT_VERSION = 5.0; 430 | TARGETED_DEVICE_FAMILY = "1,2"; 431 | VALIDATE_PRODUCT = YES; 432 | }; 433 | name = Release; 434 | }; 435 | 01DD8B4E1E2B5CB90081B1FC /* Debug */ = { 436 | isa = XCBuildConfiguration; 437 | buildSettings = { 438 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 439 | CLANG_ENABLE_MODULES = YES; 440 | DEVELOPMENT_TEAM = ""; 441 | INFOPLIST_FILE = "$(SRCROOT)/OpalImagePicker/SupportingFiles/Info.plist"; 442 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 443 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 444 | PRODUCT_BUNDLE_IDENTIFIER = com.opalorange.OpalImagePicker; 445 | PRODUCT_NAME = "$(TARGET_NAME)"; 446 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 447 | SWIFT_VERSION = 5.0; 448 | TARGETED_DEVICE_FAMILY = "1,2"; 449 | }; 450 | name = Debug; 451 | }; 452 | 01DD8B4F1E2B5CB90081B1FC /* Release */ = { 453 | isa = XCBuildConfiguration; 454 | buildSettings = { 455 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 456 | CLANG_ENABLE_MODULES = YES; 457 | DEVELOPMENT_TEAM = ""; 458 | INFOPLIST_FILE = "$(SRCROOT)/OpalImagePicker/SupportingFiles/Info.plist"; 459 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 460 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 461 | PRODUCT_BUNDLE_IDENTIFIER = com.opalorange.OpalImagePicker; 462 | PRODUCT_NAME = "$(TARGET_NAME)"; 463 | SWIFT_VERSION = 5.0; 464 | TARGETED_DEVICE_FAMILY = "1,2"; 465 | }; 466 | name = Release; 467 | }; 468 | 01DD8B511E2B5CB90081B1FC /* Debug */ = { 469 | isa = XCBuildConfiguration; 470 | buildSettings = { 471 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 472 | BUNDLE_LOADER = "$(TEST_HOST)"; 473 | INFOPLIST_FILE = OpalImagePickerTests/Info.plist; 474 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 475 | PRODUCT_BUNDLE_IDENTIFIER = com.opalorange.OpalImagePickerTests; 476 | PRODUCT_NAME = "$(TARGET_NAME)"; 477 | SWIFT_VERSION = 5.0; 478 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/OpalImagePicker.app/OpalImagePicker"; 479 | }; 480 | name = Debug; 481 | }; 482 | 01DD8B521E2B5CB90081B1FC /* Release */ = { 483 | isa = XCBuildConfiguration; 484 | buildSettings = { 485 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 486 | BUNDLE_LOADER = "$(TEST_HOST)"; 487 | INFOPLIST_FILE = OpalImagePickerTests/Info.plist; 488 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 489 | PRODUCT_BUNDLE_IDENTIFIER = com.opalorange.OpalImagePickerTests; 490 | PRODUCT_NAME = "$(TARGET_NAME)"; 491 | SWIFT_VERSION = 5.0; 492 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/OpalImagePicker.app/OpalImagePicker"; 493 | }; 494 | name = Release; 495 | }; 496 | /* End XCBuildConfiguration section */ 497 | 498 | /* Begin XCConfigurationList section */ 499 | 01DD8B2B1E2B5CB90081B1FC /* Build configuration list for PBXProject "OpalImagePicker" */ = { 500 | isa = XCConfigurationList; 501 | buildConfigurations = ( 502 | 01DD8B4B1E2B5CB90081B1FC /* Debug */, 503 | 01DD8B4C1E2B5CB90081B1FC /* Release */, 504 | ); 505 | defaultConfigurationIsVisible = 0; 506 | defaultConfigurationName = Release; 507 | }; 508 | 01DD8B4D1E2B5CB90081B1FC /* Build configuration list for PBXNativeTarget "OpalImagePicker" */ = { 509 | isa = XCConfigurationList; 510 | buildConfigurations = ( 511 | 01DD8B4E1E2B5CB90081B1FC /* Debug */, 512 | 01DD8B4F1E2B5CB90081B1FC /* Release */, 513 | ); 514 | defaultConfigurationIsVisible = 0; 515 | defaultConfigurationName = Release; 516 | }; 517 | 01DD8B501E2B5CB90081B1FC /* Build configuration list for PBXNativeTarget "OpalImagePickerTests" */ = { 518 | isa = XCConfigurationList; 519 | buildConfigurations = ( 520 | 01DD8B511E2B5CB90081B1FC /* Debug */, 521 | 01DD8B521E2B5CB90081B1FC /* Release */, 522 | ); 523 | defaultConfigurationIsVisible = 0; 524 | defaultConfigurationName = Release; 525 | }; 526 | /* End XCConfigurationList section */ 527 | }; 528 | rootObject = 01DD8B281E2B5CB90081B1FC /* Project object */; 529 | } 530 | -------------------------------------------------------------------------------- /OpalImagePicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /OpalImagePicker.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /OpalImagePicker/Source/Bundle+Helper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bundle+Helper.swift 3 | // OpalImagePicker 4 | // 5 | // Created by Kris Katsanevas on 1/15/17. 6 | // Copyright © 2017 Opal Orange LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Bundle { 12 | static func podBundle(forClass: AnyClass) -> Bundle? { 13 | 14 | let bundleForClass = Bundle(for: forClass) 15 | 16 | if let bundleURL = bundleForClass.url(forResource: "OpalImagePickerResources", withExtension: "bundle"), 17 | let bundle = Bundle(url: bundleURL) { 18 | return bundle 19 | } 20 | return nil 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /OpalImagePicker/Source/ImagePickerCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImagePickerCollectionViewCell.swift 3 | // OpalImagePicker 4 | // 5 | // Created by Kris Katsanevas on 1/15/17. 6 | // Copyright © 2017 Opal Orange LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Photos 11 | 12 | class ImagePickerCollectionViewCell: UICollectionViewCell { 13 | 14 | static let scale: CGFloat = 3 15 | static let reuseId = String(describing: ImagePickerCollectionViewCell.self) 16 | 17 | var photoAsset: PHAsset? { 18 | didSet { 19 | loadPhotoAssetIfNeeded() 20 | } 21 | } 22 | 23 | var size: CGSize? { 24 | didSet { 25 | loadPhotoAssetIfNeeded() 26 | } 27 | } 28 | 29 | var url: URL? { 30 | didSet { 31 | loadURLIfNeeded() 32 | } 33 | } 34 | 35 | var indexPath: IndexPath? { 36 | didSet { 37 | loadURLIfNeeded() 38 | } 39 | } 40 | 41 | var timeText: String? { 42 | didSet { 43 | let isTimeHidden = timeText == nil 44 | timeLabel.text = timeText 45 | timeOverlay.isHidden = isTimeHidden 46 | timeLabel.isHidden = isTimeHidden 47 | } 48 | } 49 | 50 | var cache: NSCache? 51 | 52 | var selectionTintColor: UIColor = UIColor.black.withAlphaComponent(0.8) { 53 | didSet { 54 | overlayView?.backgroundColor = selectionTintColor 55 | } 56 | } 57 | 58 | open var selectionImageTintColor: UIColor = .white { 59 | didSet { 60 | overlayImageView?.tintColor = selectionImageTintColor 61 | } 62 | } 63 | 64 | open var selectionImage: UIImage? { 65 | didSet { 66 | overlayImageView?.image = selectionImage?.withRenderingMode(.alwaysTemplate) 67 | } 68 | } 69 | 70 | override var isSelected: Bool { 71 | set { 72 | setSelected(newValue, animated: true) 73 | } 74 | get { 75 | return super.isSelected 76 | } 77 | } 78 | 79 | func setSelected(_ isSelected: Bool, animated: Bool) { 80 | super.isSelected = isSelected 81 | updateSelected(animated) 82 | } 83 | 84 | lazy var imageView: UIImageView = { 85 | let imageView = UIImageView(frame: frame) 86 | imageView.translatesAutoresizingMaskIntoConstraints = false 87 | imageView.contentMode = .scaleAspectFill 88 | imageView.clipsToBounds = true 89 | return imageView 90 | }() 91 | 92 | lazy var activityIndicator: UIActivityIndicatorView = { 93 | let activityIndicator = UIActivityIndicatorView(style: .whiteLarge) 94 | activityIndicator.translatesAutoresizingMaskIntoConstraints = false 95 | activityIndicator.hidesWhenStopped = true 96 | return activityIndicator 97 | }() 98 | 99 | lazy var timeOverlay: UIImageView = { 100 | let timeOverlay = UIImageView() 101 | timeOverlay.translatesAutoresizingMaskIntoConstraints = false 102 | let timeOverlayImage = UIImage(named: "gradient", 103 | in: Bundle.podBundle(forClass: type(of: self).self), 104 | compatibleWith: nil) 105 | timeOverlay.image = timeOverlayImage?.resizableImage(withCapInsets: .zero, 106 | resizingMode: .stretch) 107 | timeOverlay.isHidden = true 108 | return timeOverlay 109 | }() 110 | 111 | lazy var timeLabel: UILabel = { 112 | let timeLabel = UILabel() 113 | timeLabel.translatesAutoresizingMaskIntoConstraints = false 114 | timeLabel.font = UIFont.boldSystemFont(ofSize: 10) 115 | timeLabel.textColor = .white 116 | timeLabel.isHidden = true 117 | return timeLabel 118 | }() 119 | 120 | weak var overlayView: UIView? 121 | weak var overlayImageView: UIImageView? 122 | private var imageRequestID: PHImageRequestID? 123 | private var urlDataTask: URLSessionTask? 124 | 125 | override init(frame: CGRect) { 126 | super.init(frame: frame) 127 | backgroundColor = .lightGray 128 | 129 | contentView.addSubview(activityIndicator) 130 | contentView.addSubview(imageView) 131 | contentView.addSubview(timeOverlay) 132 | contentView.addSubview(timeLabel) 133 | 134 | let heightConstraint = NSLayoutConstraint(item: timeOverlay, 135 | attribute: .height, 136 | relatedBy: .equal, 137 | toItem: nil, 138 | attribute: .notAnAttribute, 139 | multiplier: 1, 140 | constant: 25) 141 | 142 | NSLayoutConstraint.activate([ 143 | timeLabel.constraintEqualTo(with: contentView, attribute: .right, constant: -5), 144 | timeLabel.constraintEqualTo(with: contentView, attribute: .bottom, constant: -5), 145 | timeOverlay.constraintEqualTo(with: contentView, attribute: .left), 146 | timeOverlay.constraintEqualTo(with: contentView, attribute: .right), 147 | timeOverlay.constraintEqualTo(with: contentView, attribute: .bottom), 148 | heightConstraint 149 | ]) 150 | 151 | let constraintsToFill = contentView.constraintsToFill(otherView: imageView) 152 | let constraintsToCenter = contentView.constraintsToCenter(otherView: activityIndicator) 153 | NSLayoutConstraint.activate(constraintsToFill + constraintsToCenter) 154 | layoutIfNeeded() 155 | } 156 | 157 | required init?(coder aDecoder: NSCoder) { 158 | fatalError("init(coder:) has not been implemented") 159 | } 160 | 161 | override func prepareForReuse() { 162 | super.prepareForReuse() 163 | imageView.image = nil 164 | 165 | timeLabel.isHidden = true 166 | timeOverlay.isHidden = true 167 | 168 | //Cancel requests if needed 169 | urlDataTask?.cancel() 170 | let manager = PHImageManager.default() 171 | guard let imageRequestID = self.imageRequestID else { return } 172 | manager.cancelImageRequest(imageRequestID) 173 | self.imageRequestID = nil 174 | 175 | //Remove selection 176 | setSelected(false, animated: false) 177 | } 178 | 179 | private func loadPhotoAssetIfNeeded() { 180 | guard let indexPath = self.indexPath, 181 | let asset = photoAsset, let size = self.size else { return } 182 | 183 | let options = PHImageRequestOptions() 184 | options.deliveryMode = .highQualityFormat 185 | options.resizeMode = .fast 186 | options.isSynchronous = false 187 | options.isNetworkAccessAllowed = true 188 | 189 | timeText = asset.duration > 0 ? asset.duration.string() : nil 190 | 191 | let manager = PHImageManager.default() 192 | let newSize = CGSize(width: size.width * type(of: self).scale, 193 | height: size.height * type(of: self).scale) 194 | activityIndicator.startAnimating() 195 | imageRequestID = manager.requestImage(for: asset, targetSize: newSize, contentMode: .aspectFill, options: options, resultHandler: { [weak self] (result, _) in 196 | guard self?.indexPath?.item == indexPath.item else { return } 197 | self?.activityIndicator.stopAnimating() 198 | self?.imageRequestID = nil 199 | self?.imageView.image = result 200 | }) 201 | } 202 | 203 | private func loadURLIfNeeded() { 204 | guard let url = self.url, 205 | let indexPath = self.indexPath else { 206 | activityIndicator.stopAnimating() 207 | imageView.image = nil 208 | return 209 | } 210 | 211 | //Check cache first to avoid downloading image. 212 | if let imageData = cache?.object(forKey: indexPath as NSIndexPath) as Data?, 213 | let image = UIImage(data: imageData) { 214 | activityIndicator.stopAnimating() 215 | imageView.image = image 216 | return 217 | } 218 | 219 | activityIndicator.startAnimating() 220 | urlDataTask = URLSession.shared.dataTask(with: url) { [weak self] (data, response, error) in 221 | guard indexPath == self?.indexPath else { return } 222 | DispatchQueue.main.async { [weak self] in 223 | self?.activityIndicator.stopAnimating() 224 | 225 | guard let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200, 226 | let mimeType = response?.mimeType, mimeType.hasPrefix("image"), 227 | let data = data, error == nil, 228 | let image = UIImage(data: data) else { 229 | //broken link image 230 | self?.imageView.image = UIImage() 231 | return 232 | } 233 | 234 | self?.cache?.setObject(data as NSData, 235 | forKey: indexPath as NSIndexPath, 236 | cost: data.count) 237 | 238 | self?.imageView.image = image 239 | } 240 | } 241 | urlDataTask?.resume() 242 | } 243 | 244 | private func updateSelected(_ animated: Bool) { 245 | if isSelected { 246 | addOverlay(animated) 247 | } else { 248 | removeOverlay(animated) 249 | } 250 | } 251 | 252 | private func addOverlay(_ animated: Bool) { 253 | guard self.overlayView == nil && self.overlayImageView == nil else { return } 254 | 255 | let overlayView = UIView(frame: frame) 256 | overlayView.translatesAutoresizingMaskIntoConstraints = false 257 | overlayView.backgroundColor = selectionTintColor 258 | contentView.addSubview(overlayView) 259 | self.overlayView = overlayView 260 | 261 | let overlayImageView = UIImageView(frame: frame) 262 | overlayImageView.translatesAutoresizingMaskIntoConstraints = false 263 | overlayImageView.contentMode = .center 264 | overlayImageView.image = selectionImage ?? UIImage(named: "checkmark", in: Bundle.podBundle(forClass: type(of: self).self), compatibleWith: nil)?.withRenderingMode(.alwaysTemplate) 265 | overlayImageView.tintColor = selectionImageTintColor 266 | overlayImageView.alpha = 0 267 | contentView.addSubview(overlayImageView) 268 | self.overlayImageView = overlayImageView 269 | 270 | let overlayViewConstraints = overlayView.constraintsToFill(otherView: contentView) 271 | let overlayImageViewConstraints = overlayImageView.constraintsToFill(otherView: contentView) 272 | NSLayoutConstraint.activate(overlayImageViewConstraints + overlayViewConstraints) 273 | layoutIfNeeded() 274 | 275 | let duration = animated ? 0.2 : 0.0 276 | UIView.animate(withDuration: duration, animations: { 277 | overlayView.alpha = 0.7 278 | overlayImageView.alpha = 1 279 | }) 280 | } 281 | 282 | private func removeOverlay(_ animated: Bool) { 283 | guard let overlayView = self.overlayView, 284 | let overlayImageView = self.overlayImageView else { 285 | self.overlayView?.removeFromSuperview() 286 | self.overlayImageView?.removeFromSuperview() 287 | return 288 | } 289 | 290 | let duration = animated ? 0.2 : 0.0 291 | UIView.animate(withDuration: duration, animations: { 292 | overlayView.alpha = 0 293 | overlayImageView.alpha = 0 294 | }, completion: { (_) in 295 | overlayView.removeFromSuperview() 296 | overlayImageView.removeFromSuperview() 297 | }) 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /OpalImagePicker/Source/OpalImagePickerCollectionViewLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpalImagePickerCollectionViewLayout.swift 3 | // OpalImagePicker 4 | // 5 | // Created by Kris Katsanevas on 1/15/17. 6 | // Copyright © 2017 Opal Orange LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Collection View Layout that evenly lays out the images in the Image Picker. 12 | open class OpalImagePickerCollectionViewLayout: UICollectionViewLayout { 13 | 14 | /// Estimated Image Size. Used as a minimum image size to determine how many images should be go across to cover the width. You can override for different display preferences. Assumed to be greater than 0. 15 | open var estimatedImageSize: CGFloat { 16 | guard let collectionView = self.collectionView else { return 80 } 17 | return collectionView.traitCollection.horizontalSizeClass == .regular ? 160 : 80 18 | } 19 | 20 | var sizeOfItem: CGFloat = 0 21 | private var cellLayoutInfo: [IndexPath: UICollectionViewLayoutAttributes] = [:] 22 | 23 | /// Prepare for Collection View Update 24 | /// 25 | /// - Parameter updateItems: Items to update 26 | open override func prepare(forCollectionViewUpdates updateItems: [UICollectionViewUpdateItem]) { 27 | updateItemSizes() 28 | } 29 | 30 | /// Prepare the layout 31 | open override func prepare() { 32 | updateItemSizes() 33 | } 34 | 35 | /// Returns `Bool` telling should invalidate layout 36 | /// 37 | /// - Parameter newBounds: the new bounds 38 | /// - Returns: Returns a `Bool` telling should invalidate layout 39 | open override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { 40 | return true 41 | } 42 | 43 | /// Returns layout attributes for indexPath 44 | /// 45 | /// - Parameter indexPath: the `IndexPath` 46 | /// - Returns: Returns layout attributes 47 | open override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { 48 | return cellLayoutInfo[indexPath] 49 | } 50 | 51 | /// Returns a list of layout attributes for items in rect 52 | /// 53 | /// - Parameter rect: the Rect 54 | /// - Returns: Returns a list of layout attributes 55 | open override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 56 | guard let collectionView = self.collectionView else { return nil } 57 | let numberOfItems = collectionView.numberOfItems(inSection: 0) 58 | return cellLayoutInfo.filter { (indexPath, layoutAttribute) -> Bool in 59 | return layoutAttribute.frame.intersects(rect) && indexPath.item < numberOfItems 60 | }.map { $0.value } 61 | } 62 | 63 | /// Collection View Content Size 64 | open override var collectionViewContentSize: CGSize { 65 | guard let collectionView = self.collectionView else { return CGSize.zero } 66 | let numberOfItemsAcross = Int(collectionView.bounds.width/estimatedImageSize) 67 | 68 | guard numberOfItemsAcross > 0 else { return CGSize.zero } 69 | let widthOfItem = collectionView.bounds.width/CGFloat(numberOfItemsAcross) 70 | sizeOfItem = widthOfItem 71 | 72 | var totalNumberOfItems = 0 73 | for section in 0.. 0 else { return } 88 | let widthOfItem = collectionView.bounds.width/CGFloat(numberOfItemsAcross) 89 | sizeOfItem = widthOfItem 90 | 91 | cellLayoutInfo = [:] 92 | 93 | var yPosition: CGFloat = 0 94 | 95 | for section in 0.. Void)? 15 | 16 | /// Localized navigation title. Defaults to "Photos". 17 | @objc public var navigationTitle: String? { 18 | didSet { 19 | updateStrings?(self) 20 | } 21 | } 22 | 23 | /// Localized library segment title. Only displays when using external URLs in `UISegmentedControl`. 24 | @objc public var librarySegmentTitle: String? { 25 | didSet { 26 | updateStrings?(self) 27 | } 28 | } 29 | 30 | /// Localized 'Cancel' button text. 31 | @objc public var cancelButtonTitle: String? { 32 | didSet { 33 | updateStrings?(self) 34 | } 35 | } 36 | 37 | /// Localized 'Done' button text. 38 | @objc public var doneButtonTitle: String? { 39 | didSet { 40 | updateStrings?(self) 41 | } 42 | } 43 | 44 | /// Localized maximum selections allowed error message displayed to the user. 45 | @objc public var maximumSelectionsAllowedMessage: String? 46 | 47 | /// Localized "OK" string. 48 | @objc public var okayString: String? 49 | } 50 | -------------------------------------------------------------------------------- /OpalImagePicker/Source/OpalImagePickerController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpalImagePickerController.swift 3 | // OpalImagePicker 4 | // 5 | // Created by Kris Katsanevas on 1/15/17. 6 | // Copyright © 2017 Opal Orange LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Photos 11 | 12 | /// Image Picker Controller Delegate. Notifies when images are selected or image picker is cancelled. 13 | @objc public protocol OpalImagePickerControllerDelegate: class { 14 | 15 | /// Image Picker did finish picking images. Provides an array of `PHAsset` selected. 16 | /// 17 | /// - Parameters: 18 | /// - picker: the `OpalImagePickerController` 19 | /// - assets: the array of `PHAsset` 20 | @objc optional func imagePicker(_ picker: OpalImagePickerController, didFinishPickingAssets assets: [PHAsset]) 21 | 22 | /// Image Picker did cancel. 23 | /// 24 | /// - Parameter picker: the `OpalImagePickerController` 25 | @objc optional func imagePickerDidCancel(_ picker: OpalImagePickerController) 26 | 27 | /// Image Picker Number of External items. Optional use to provide items from external 28 | /// sources e.g. (Facebook, Twitter, or Instagram). 29 | /// 30 | /// - Parameter picker: the `OpalImagePickerController` 31 | /// - Returns: an `Int` describing the number of available external image `URL` 32 | @objc optional func imagePickerNumberOfExternalItems(_ picker: OpalImagePickerController) -> Int 33 | 34 | /// Image Picker returns the `URL` for an image at an `Int` index. Optional use to provide items from external 35 | /// sources e.g. (Facebook, Twitter, or Instagram). 36 | /// 37 | /// - Parameters: 38 | /// - picker: the `OpalImagePickerController` 39 | /// - index: the `Int` index for the image 40 | /// - Returns: the `URL` 41 | @objc optional func imagePicker(_ picker: OpalImagePickerController, imageURLforExternalItemAtIndex index: Int) -> URL? 42 | 43 | /// Image Picker external title. This will appear in the `UISegmentedControl`. Make sure to provide a Localized String. 44 | /// 45 | /// - Parameter picker: the `OpalImagePickerController` 46 | /// - Returns: the `String`. This should be a Localized String. 47 | @objc optional func imagePickerTitleForExternalItems(_ picker: OpalImagePickerController) -> String 48 | 49 | /// Image Picker did finish picking external images. Provides an array of `URL` selected. 50 | /// 51 | /// - Parameters: 52 | /// - picker: the `OpalImagePickerController` 53 | /// - urls: the array of `URL` 54 | @objc optional func imagePicker(_ picker: OpalImagePickerController, didFinishPickingExternalURLs urls: [URL]) 55 | } 56 | 57 | /// Image Picker Controller. Displays images from the Photo Library. Must check Photo Library permissions before attempting to display this controller. 58 | open class OpalImagePickerController: UINavigationController { 59 | 60 | /// Image Picker Delegate. Notifies when images are selected or image picker is cancelled. 61 | @objc open weak var imagePickerDelegate: OpalImagePickerControllerDelegate? { 62 | didSet { 63 | let rootVC = viewControllers.first as? OpalImagePickerRootViewController 64 | rootVC?.delegate = imagePickerDelegate 65 | } 66 | } 67 | 68 | /// Configuration to change Localized Strings 69 | @objc open var configuration: OpalImagePickerConfiguration? { 70 | didSet { 71 | let rootVC = viewControllers.first as? OpalImagePickerRootViewController 72 | rootVC?.configuration = configuration 73 | } 74 | } 75 | 76 | /// Custom Tint Color for overlay of selected images. 77 | @objc open var selectionTintColor: UIColor? { 78 | didSet { 79 | let rootVC = viewControllers.first as? OpalImagePickerRootViewController 80 | rootVC?.selectionTintColor = selectionTintColor 81 | } 82 | } 83 | 84 | /// Custom Tint Color for selection image (checkmark). 85 | @objc open var selectionImageTintColor: UIColor? { 86 | didSet { 87 | let rootVC = viewControllers.first as? OpalImagePickerRootViewController 88 | rootVC?.selectionImageTintColor = selectionImageTintColor 89 | } 90 | } 91 | 92 | /// Custom selection image (checkmark). 93 | @objc open var selectionImage: UIImage? { 94 | didSet { 95 | let rootVC = viewControllers.first as? OpalImagePickerRootViewController 96 | rootVC?.selectionImage = selectionImage 97 | } 98 | } 99 | 100 | /// Maximum photo selections allowed in picker (zero or fewer means unlimited). 101 | @objc open var maximumSelectionsAllowed: Int = -1 { 102 | didSet { 103 | let rootVC = viewControllers.first as? OpalImagePickerRootViewController 104 | rootVC?.maximumSelectionsAllowed = maximumSelectionsAllowed 105 | } 106 | } 107 | 108 | /// Allowed Media Types that can be fetched. See `PHAssetMediaType` 109 | open var allowedMediaTypes: Set? { 110 | didSet { 111 | let rootVC = viewControllers.first as? OpalImagePickerRootViewController 112 | rootVC?.allowedMediaTypes = allowedMediaTypes 113 | } 114 | } 115 | 116 | /// Allowed MediaSubtype that can be fetched. Can be applied as `OptionSet`. See `PHAssetMediaSubtype` 117 | open var allowedMediaSubtypes: PHAssetMediaSubtype? { 118 | didSet { 119 | let rootVC = viewControllers.first as? OpalImagePickerRootViewController 120 | rootVC?.allowedMediaSubtypes = allowedMediaSubtypes 121 | } 122 | } 123 | 124 | /// Status Bar Preference (defaults to `default`) 125 | @objc open var statusBarPreference = UIStatusBarStyle.default 126 | 127 | /// External `UIToolbar` barTintColor. 128 | @objc open var externalToolbarBarTintColor: UIColor? { 129 | didSet { 130 | let rootVC = viewControllers.first as? OpalImagePickerRootViewController 131 | rootVC?.toolbar.barTintColor = externalToolbarBarTintColor 132 | } 133 | } 134 | 135 | /// External `UIToolbar` and `UISegmentedControl` tint color. 136 | @objc open var externalToolbarTintColor: UIColor? { 137 | didSet { 138 | let rootVC = viewControllers.first as? OpalImagePickerRootViewController 139 | rootVC?.toolbar.tintColor = externalToolbarTintColor 140 | rootVC?.tabSegmentedControl.tintColor = externalToolbarTintColor 141 | } 142 | } 143 | 144 | /// Initializer 145 | public required init() { 146 | super.init(rootViewController: OpalImagePickerRootViewController()) 147 | } 148 | 149 | /// Initializer (Do not use this controller in Interface Builder) 150 | /// 151 | /// - Parameters: 152 | /// - nibNameOrNil: the nib name 153 | /// - nibBundleOrNil: the nib `Bundle` 154 | public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 155 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 156 | } 157 | 158 | /// Initializer (Do not use this controller in Interface Builder) 159 | public required init?(coder aDecoder: NSCoder) { 160 | fatalError("Cannot init \(String(describing: OpalImagePickerController.self)) from Interface Builder") 161 | } 162 | 163 | open override var preferredStatusBarStyle: UIStatusBarStyle { 164 | return statusBarPreference 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /OpalImagePicker/Source/OpalImagePickerRootViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpalImagePickerRootViewController.swift 3 | // OpalImagePicker 4 | // 5 | // Created by Kristos Katsanevas on 1/16/17. 6 | // Copyright © 2017 Opal Orange LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Photos 11 | 12 | /// Image Picker Root View Controller contains the logic for selecting images. The images are displayed in a `UICollectionView`, and multiple images can be selected. 13 | open class OpalImagePickerRootViewController: UIViewController { 14 | 15 | /// Delegate for Image Picker. Notifies when images are selected (done is tapped) or when the Image Picker is cancelled. 16 | open weak var delegate: OpalImagePickerControllerDelegate? 17 | 18 | /// Configuration to change Localized Strings 19 | open var configuration: OpalImagePickerConfiguration? { 20 | didSet { 21 | configuration?.updateStrings = configurationChanged 22 | if let configuration = self.configuration { 23 | configurationChanged(configuration) 24 | } 25 | } 26 | } 27 | 28 | /// `UICollectionView` for displaying photo library images 29 | open weak var collectionView: UICollectionView? 30 | 31 | /// `UICollectionView` for displaying external images 32 | open weak var externalCollectionView: UICollectionView? 33 | 34 | /// `UIToolbar` to switch between Photo Library and External Images. 35 | open lazy var toolbar: UIToolbar = { 36 | let toolbar = UIToolbar() 37 | toolbar.translatesAutoresizingMaskIntoConstraints = false 38 | return toolbar 39 | }() 40 | 41 | /// `UISegmentedControl` to switch between Photo Library and External Images. 42 | open lazy var tabSegmentedControl: UISegmentedControl = { 43 | let tabSegmentedControl = UISegmentedControl(items: [NSLocalizedString("Library", comment: "Library"), NSLocalizedString("External", comment: "External")]) 44 | tabSegmentedControl.addTarget(self, action: #selector(segmentTapped(_:)), for: .valueChanged) 45 | tabSegmentedControl.selectedSegmentIndex = 0 46 | return tabSegmentedControl 47 | }() 48 | 49 | /// Custom Tint Color for overlay of selected images. 50 | open var selectionTintColor: UIColor? { 51 | didSet { 52 | collectionView?.reloadData() 53 | } 54 | } 55 | 56 | /// Custom Tint Color for selection image (checkmark). 57 | open var selectionImageTintColor: UIColor? { 58 | didSet { 59 | collectionView?.reloadData() 60 | } 61 | } 62 | 63 | /// Custom selection image (checkmark). 64 | open var selectionImage: UIImage? { 65 | didSet { 66 | collectionView?.reloadData() 67 | } 68 | } 69 | 70 | /// Allowed Media Types that can be fetched. See `PHAssetMediaType` 71 | open var allowedMediaTypes: Set? { 72 | didSet { 73 | updateFetchOptionPredicate() 74 | } 75 | } 76 | 77 | /// Allowed MediaSubtype that can be fetched. Can be applied as `OptionSet`. See `PHAssetMediaSubtype` 78 | open var allowedMediaSubtypes: PHAssetMediaSubtype? { 79 | didSet { 80 | updateFetchOptionPredicate() 81 | } 82 | } 83 | 84 | /// Maximum photo selections allowed in picker (zero or fewer means unlimited). 85 | open var maximumSelectionsAllowed: Int = -1 86 | 87 | /// Page size for paging through the Photo Assets in the Photo Library. Defaults to 100. Must override to change this value. Only works in iOS 9.0+ 88 | public let pageSize = 100 89 | 90 | var photoAssets: PHFetchResult = PHFetchResult() 91 | weak var doneButton: UIBarButtonItem? 92 | weak var cancelButton: UIBarButtonItem? 93 | 94 | internal var collectionViewLayout: OpalImagePickerCollectionViewLayout? { 95 | return collectionView?.collectionViewLayout as? OpalImagePickerCollectionViewLayout 96 | } 97 | 98 | internal lazy var fetchOptions: PHFetchOptions = { 99 | let fetchOptions = PHFetchOptions() 100 | fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] 101 | return fetchOptions 102 | }() 103 | 104 | @available(iOS 9.0, *) 105 | internal var fetchLimit: Int { 106 | get { 107 | return fetchOptions.fetchLimit 108 | } 109 | set { 110 | fetchOptions.fetchLimit = newValue 111 | } 112 | } 113 | 114 | internal var shouldShowTabs: Bool { 115 | guard let imagePicker = navigationController as? OpalImagePickerController else { return false } 116 | return delegate?.imagePickerNumberOfExternalItems?(imagePicker) != nil 117 | } 118 | 119 | private var isCompleted = false 120 | private var photosCompleted = 0 121 | private var savedImages: [UIImage] = [] 122 | private var showExternalImages = false 123 | private var selectedIndexPaths: [IndexPath] = [] 124 | private var externalSelectedIndexPaths: [IndexPath] = [] 125 | 126 | private lazy var cache: NSCache = { 127 | let cache = NSCache() 128 | cache.totalCostLimit = 128000000 //128 MB 129 | cache.countLimit = 100 // 100 images 130 | return cache 131 | }() 132 | 133 | private weak var rightExternalCollectionViewConstraint: NSLayoutConstraint? 134 | 135 | /// Initializer 136 | public required init() { 137 | super.init(nibName: nil, bundle: nil) 138 | } 139 | 140 | /// Initializer (Do not use this View Controller in Interface Builder) 141 | public required init?(coder aDecoder: NSCoder) { 142 | fatalError("Cannot init \(String(describing: OpalImagePickerRootViewController.self)) from Interface Builder") 143 | } 144 | 145 | private func setup() { 146 | guard let view = view else { return } 147 | fetchPhotos() 148 | 149 | let collectionView = UICollectionView(frame: view.frame, collectionViewLayout: OpalImagePickerCollectionViewLayout()) 150 | setup(collectionView: collectionView) 151 | view.addSubview(collectionView) 152 | self.collectionView = collectionView 153 | 154 | var constraints: [NSLayoutConstraint] = [] 155 | if shouldShowTabs { 156 | setupTabs() 157 | let externalCollectionView = UICollectionView(frame: view.frame, collectionViewLayout: OpalImagePickerCollectionViewLayout()) 158 | setup(collectionView: externalCollectionView) 159 | view.addSubview(externalCollectionView) 160 | self.externalCollectionView = externalCollectionView 161 | 162 | constraints += [externalCollectionView.constraintEqualTo(with: collectionView, attribute: .top)] 163 | constraints += [externalCollectionView.constraintEqualTo(with: collectionView, attribute: .bottom)] 164 | constraints += [externalCollectionView.constraintEqualTo(with: collectionView, receiverAttribute: .left, otherAttribute: .right)] 165 | constraints += [collectionView.constraintEqualTo(with: view, attribute: .width)] 166 | constraints += [externalCollectionView.constraintEqualTo(with: view, attribute: .width)] 167 | constraints += [toolbar.constraintEqualTo(with: collectionView, receiverAttribute: .bottom, otherAttribute: .top)] 168 | } else { 169 | constraints += [view.constraintEqualTo(with: collectionView, attribute: .top)] 170 | constraints += [view.constraintEqualTo(with: collectionView, attribute: .right)] 171 | } 172 | 173 | //Lower priority to override left constraint for animations 174 | let leftCollectionViewConstraint = view.constraintEqualTo(with: collectionView, attribute: .left) 175 | leftCollectionViewConstraint.priority = UILayoutPriority(rawValue: 999) 176 | constraints += [leftCollectionViewConstraint] 177 | 178 | constraints += [view.constraintEqualTo(with: collectionView, attribute: .bottom)] 179 | NSLayoutConstraint.activate(constraints) 180 | view.layoutIfNeeded() 181 | } 182 | 183 | private func setup(collectionView: UICollectionView) { 184 | collectionView.translatesAutoresizingMaskIntoConstraints = false 185 | collectionView.allowsMultipleSelection = true 186 | if #available(iOS 13.0, *) { 187 | collectionView.backgroundColor = .systemBackground 188 | } else { 189 | collectionView.backgroundColor = .white 190 | } 191 | collectionView.dataSource = self 192 | collectionView.delegate = self 193 | collectionView.register(ImagePickerCollectionViewCell.self, forCellWithReuseIdentifier: ImagePickerCollectionViewCell.reuseId) 194 | } 195 | 196 | private func setupTabs() { 197 | guard let view = view else { return } 198 | 199 | edgesForExtendedLayout = UIRectEdge() 200 | navigationController?.navigationBar.isTranslucent = false 201 | toolbar.isTranslucent = false 202 | 203 | view.addSubview(toolbar) 204 | let flexItem1 = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) 205 | let flexItem2 = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) 206 | let barButtonItem = UIBarButtonItem(customView: tabSegmentedControl) 207 | toolbar.setItems([flexItem1, barButtonItem, flexItem2], animated: false) 208 | 209 | if let imagePicker = navigationController as? OpalImagePickerController, 210 | let title = delegate?.imagePickerTitleForExternalItems?(imagePicker) { 211 | tabSegmentedControl.setTitle(title, forSegmentAt: 1) 212 | } 213 | 214 | NSLayoutConstraint.activate([ 215 | toolbar.constraintEqualTo(with: topLayoutGuide, receiverAttribute: .top, otherAttribute: .bottom), 216 | toolbar.constraintEqualTo(with: view, attribute: .left), 217 | toolbar.constraintEqualTo(with: view, attribute: .right) 218 | ]) 219 | } 220 | 221 | private func fetchPhotos() { 222 | requestPhotoAccessIfNeeded(PHPhotoLibrary.authorizationStatus()) 223 | 224 | if #available(iOS 9.0, *) { 225 | fetchOptions.fetchLimit = pageSize 226 | } 227 | photoAssets = PHAsset.fetchAssets(with: fetchOptions) 228 | collectionView?.reloadData() 229 | } 230 | 231 | private func updateFetchOptionPredicate() { 232 | var predicates: [NSPredicate] = [] 233 | if let allowedMediaTypes = self.allowedMediaTypes { 234 | let mediaTypesPredicates = allowedMediaTypes.map { NSPredicate(format: "mediaType = %d", $0.rawValue) } 235 | let allowedMediaTypesPredicate = NSCompoundPredicate(orPredicateWithSubpredicates: mediaTypesPredicates) 236 | predicates += [allowedMediaTypesPredicate] 237 | } 238 | 239 | if let allowedMediaSubtypes = self.allowedMediaSubtypes { 240 | let mediaSubtypes = NSPredicate(format: "(mediaSubtype & %d) == 0", allowedMediaSubtypes.rawValue) 241 | predicates += [mediaSubtypes] 242 | } 243 | 244 | if predicates.count > 0 { 245 | let predicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicates) 246 | fetchOptions.predicate = predicate 247 | } else { 248 | fetchOptions.predicate = nil 249 | } 250 | fetchPhotos() 251 | } 252 | 253 | /// Load View 254 | open override func loadView() { 255 | view = UIView() 256 | } 257 | 258 | open override func viewDidLoad() { 259 | super.viewDidLoad() 260 | setup() 261 | 262 | navigationItem.title = configuration?.navigationTitle ?? NSLocalizedString("Photos", comment: "") 263 | 264 | let cancelButtonTitle = configuration?.cancelButtonTitle ?? NSLocalizedString("Cancel", comment: "") 265 | let cancelButton = UIBarButtonItem(title: cancelButtonTitle, style: .plain, target: self, action: #selector(cancelTapped)) 266 | navigationItem.leftBarButtonItem = cancelButton 267 | self.cancelButton = cancelButton 268 | 269 | let doneButtonTitle = configuration?.doneButtonTitle ?? NSLocalizedString("Done", comment: "") 270 | let doneButton = UIBarButtonItem(title: doneButtonTitle, style: .done, target: self, action: #selector(doneTapped)) 271 | navigationItem.rightBarButtonItem = doneButton 272 | self.doneButton = doneButton 273 | } 274 | 275 | open override func viewWillAppear(_ animated: Bool) { 276 | super.viewWillAppear(animated) 277 | isCompleted = false 278 | } 279 | 280 | @objc func cancelTapped() { 281 | dismiss(animated: true) { [weak self] in 282 | guard let imagePicker = self?.navigationController as? OpalImagePickerController else { return } 283 | self?.delegate?.imagePickerDidCancel?(imagePicker) 284 | } 285 | } 286 | 287 | @objc func doneTapped() { 288 | guard let imagePicker = navigationController as? OpalImagePickerController, 289 | !isCompleted else { return } 290 | 291 | let indexPathsForSelectedItems = selectedIndexPaths 292 | let externalIndexPaths = externalSelectedIndexPaths 293 | guard indexPathsForSelectedItems.count + externalIndexPaths.count > 0 else { 294 | cancelTapped() 295 | return 296 | } 297 | 298 | var photoAssets: [PHAsset] = [] 299 | for indexPath in indexPathsForSelectedItems { 300 | guard indexPath.item < self.photoAssets.count else { continue } 301 | photoAssets += [self.photoAssets.object(at: indexPath.item)] 302 | } 303 | delegate?.imagePicker?(imagePicker, didFinishPickingAssets: photoAssets) 304 | 305 | var selectedURLs: [URL] = [] 306 | for indexPath in externalIndexPaths { 307 | guard let url = delegate?.imagePicker?(imagePicker, imageURLforExternalItemAtIndex: indexPath.item) else { continue } 308 | selectedURLs += [url] 309 | } 310 | delegate?.imagePicker?(imagePicker, didFinishPickingExternalURLs: selectedURLs) 311 | } 312 | 313 | private func set(image: UIImage?, indexPath: IndexPath, isExternal: Bool) { 314 | update(isSelected: image != nil, isExternal: isExternal, for: indexPath) 315 | } 316 | 317 | private func update(isSelected: Bool, isExternal: Bool, for indexPath: IndexPath) { 318 | if isSelected && isExternal { 319 | externalSelectedIndexPaths += [indexPath] 320 | } else if !isSelected && isExternal { 321 | externalSelectedIndexPaths = externalSelectedIndexPaths.filter { $0 != indexPath } 322 | } else if isSelected && !isExternal { 323 | selectedIndexPaths += [indexPath] 324 | } else { 325 | selectedIndexPaths = selectedIndexPaths.filter { $0 != indexPath } 326 | } 327 | } 328 | 329 | @available(iOS 9.0, *) 330 | private func fetchNextPageIfNeeded(indexPath: IndexPath) { 331 | guard indexPath.item == fetchLimit-1 else { return } 332 | 333 | let oldFetchLimit = fetchLimit 334 | fetchLimit += pageSize 335 | photoAssets = PHAsset.fetchAssets(with: fetchOptions) 336 | 337 | var indexPaths: [IndexPath] = [] 338 | for item in oldFetchLimit.. Bool { 410 | guard let cell = collectionView.cellForItem(at: indexPath) as? ImagePickerCollectionViewCell, 411 | cell.imageView.image != nil else { return false } 412 | guard maximumSelectionsAllowed > 0 else { return true } 413 | 414 | let collectionViewItems = self.collectionView?.indexPathsForSelectedItems?.count ?? 0 415 | let externalCollectionViewItems = self.externalCollectionView?.indexPathsForSelectedItems?.count ?? 0 416 | 417 | if maximumSelectionsAllowed <= collectionViewItems + externalCollectionViewItems { 418 | //We exceeded maximum allowed, so alert user. Don't allow selection 419 | let message = configuration?.maximumSelectionsAllowedMessage ?? NSLocalizedString("You cannot select more than \(maximumSelectionsAllowed) images. Please deselect another image before trying to select again.", comment: "You cannot select more than (x) images. Please deselect another image before trying to select again. (OpalImagePicker)") 420 | let alert = UIAlertController(title: "", message: message, preferredStyle: .alert) 421 | let okayString = configuration?.okayString ?? NSLocalizedString("OK", comment: "OK") 422 | let action = UIAlertAction(title: okayString, style: .cancel, handler: nil) 423 | alert.addAction(action) 424 | present(alert, animated: true, completion: nil) 425 | return false 426 | } 427 | return true 428 | } 429 | } 430 | 431 | // MARK: - Collection View Data Source 432 | 433 | extension OpalImagePickerRootViewController: UICollectionViewDataSource { 434 | 435 | /// Returns Collection View Cell for item at `IndexPath` 436 | /// 437 | /// - Parameters: 438 | /// - collectionView: the `UICollectionView` 439 | /// - indexPath: the `IndexPath` 440 | /// - Returns: Returns the `UICollectionViewCell` 441 | public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 442 | 443 | if collectionView == self.collectionView { 444 | return photoAssetCollectionView(collectionView, cellForItemAt: indexPath) 445 | } else { 446 | return externalCollectionView(collectionView, cellForItemAt: indexPath) 447 | } 448 | } 449 | 450 | private func photoAssetCollectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 451 | if #available(iOS 9.0, *) { 452 | fetchNextPageIfNeeded(indexPath: indexPath) 453 | } 454 | 455 | guard let layoutAttributes = collectionView.collectionViewLayout.layoutAttributesForItem(at: indexPath), 456 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ImagePickerCollectionViewCell.reuseId, for: indexPath) as? ImagePickerCollectionViewCell else { return UICollectionViewCell() } 457 | let photoAsset = photoAssets.object(at: indexPath.item) 458 | cell.indexPath = indexPath 459 | cell.photoAsset = photoAsset 460 | cell.size = layoutAttributes.frame.size 461 | 462 | if let selectionTintColor = self.selectionTintColor { 463 | cell.selectionTintColor = selectionTintColor 464 | } 465 | if let selectionImageTintColor = self.selectionImageTintColor { 466 | cell.selectionImageTintColor = selectionImageTintColor 467 | } 468 | if let selectionImage = self.selectionImage { 469 | cell.selectionImage = selectionImage 470 | } 471 | 472 | return cell 473 | } 474 | 475 | private func externalCollectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 476 | guard let imagePicker = navigationController as? OpalImagePickerController, 477 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ImagePickerCollectionViewCell.reuseId, for: indexPath) as? ImagePickerCollectionViewCell else { return UICollectionViewCell() } 478 | if let url = delegate?.imagePicker?(imagePicker, imageURLforExternalItemAtIndex: indexPath.item) { 479 | cell.cache = cache 480 | cell.url = url 481 | cell.indexPath = indexPath 482 | } else { 483 | assertionFailure("You need to implement `imagePicker(_:imageURLForExternalItemAtIndex:)` in your delegate.") 484 | } 485 | 486 | if let selectionTintColor = self.selectionTintColor { 487 | cell.selectionTintColor = selectionTintColor 488 | } 489 | if let selectionImageTintColor = self.selectionImageTintColor { 490 | cell.selectionImageTintColor = selectionImageTintColor 491 | } 492 | if let selectionImage = self.selectionImage { 493 | cell.selectionImage = selectionImage 494 | } 495 | 496 | return cell 497 | } 498 | 499 | /// Returns the number of items in a given section 500 | /// 501 | /// - Parameters: 502 | /// - collectionView: the `UICollectionView` 503 | /// - section: the given section of the `UICollectionView` 504 | /// - Returns: Returns an `Int` for the number of rows. 505 | public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 506 | if collectionView == self.collectionView { 507 | return photoAssets.count 508 | } else if let imagePicker = navigationController as? OpalImagePickerController, 509 | let numberOfItems = delegate?.imagePickerNumberOfExternalItems?(imagePicker) { 510 | return numberOfItems 511 | } else { 512 | assertionFailure("You need to implement `imagePickerNumberOfExternalItems(_:)` in your delegate.") 513 | return 0 514 | } 515 | } 516 | } 517 | -------------------------------------------------------------------------------- /OpalImagePicker/Source/TimeInterval+String.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimeInterval+String.swift 3 | // OpalImagePicker 4 | // 5 | // Created by Kristos Katsanevas on 1/20/18. 6 | // Copyright © 2018 Opal Orange LLC. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension TimeInterval { 12 | func string() -> String { 13 | let interval = Int(self) 14 | let seconds = interval % 60 15 | let minutes = (interval / 60) % 60 16 | let hours = (interval / 3600) 17 | if hours == 0 { 18 | return String(format: "%d:%02d", minutes, seconds) 19 | } 20 | return String(format: "%d:%02d:%02d", hours, minutes, seconds) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /OpalImagePicker/Source/UIView+Helper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Helper.swift 3 | // OpalImagePicker 4 | // 5 | // Created by Kristos Katsanevas on 9/30/17. 6 | // Copyright © 2017 Opal Orange LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIView { 12 | 13 | func constraintsToFill(otherView: Any) -> [NSLayoutConstraint] { 14 | return [ 15 | NSLayoutConstraint(item: self, attribute: .left, relatedBy: .equal, toItem: otherView, attribute: .left, multiplier: 1.0, constant: 0.0), 16 | NSLayoutConstraint(item: self, attribute: .right, relatedBy: .equal, toItem: otherView, attribute: .right, multiplier: 1.0, constant: 0.0), 17 | NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: otherView, attribute: .top, multiplier: 1.0, constant: 0.0), 18 | NSLayoutConstraint(item: self, attribute: .bottom, relatedBy: .equal, toItem: otherView, attribute: .bottom, multiplier: 1.0, constant: 0.0) 19 | ] 20 | } 21 | 22 | func constraintsToCenter(otherView: Any) -> [NSLayoutConstraint] { 23 | return [ 24 | NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: otherView, attribute: .centerX, multiplier: 1.0, constant: 0.0), 25 | NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: otherView, attribute: .centerY, multiplier: 1.0, constant: 0.0) 26 | ] 27 | } 28 | 29 | func constraintEqualTo(with otherView: Any, attribute: NSLayoutConstraint.Attribute, constant: CGFloat = 0) -> NSLayoutConstraint { 30 | return NSLayoutConstraint(item: self, attribute: attribute, relatedBy: .equal, toItem: otherView, attribute: attribute, multiplier: 1.0, constant: constant) 31 | } 32 | 33 | func constraintEqualTo(with otherView: Any, receiverAttribute: NSLayoutConstraint.Attribute, otherAttribute: NSLayoutConstraint.Attribute) -> NSLayoutConstraint { 34 | return NSLayoutConstraint(item: self, attribute: receiverAttribute, relatedBy: .equal, toItem: otherView, attribute: otherAttribute, multiplier: 1.0, constant: 0.0) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /OpalImagePicker/Source/UIViewController+OpalImagePicker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+OpalImagePicker.swift 3 | // OpalImagePicker 4 | // 5 | // Created by Kris Katsanevas on 1/15/17. 6 | // Copyright © 2017 Opal Orange LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Photos 11 | 12 | public extension UIViewController { 13 | 14 | /// Present Image Picker using closures rather than delegation. 15 | /// 16 | /// - Parameters: 17 | /// - imagePicker: the `OpalImagePickerController` 18 | /// - animated: is presentation animated 19 | /// - select: notifies when selection of `[PHAsset]` has been made 20 | /// - cancel: notifies when Image Picker has been cancelled by user 21 | /// - completion: notifies when the Image Picker finished presenting 22 | func presentOpalImagePickerController(_ imagePicker: OpalImagePickerController, animated: Bool, select: @escaping (([PHAsset]) -> Void), cancel: @escaping (() -> Void), completion: (() -> Void)? = nil) { 23 | let manager = OpalImagePickerManager.shared 24 | manager.selectAssets = select 25 | manager.cancel = cancel 26 | imagePicker.imagePickerDelegate = manager 27 | present(imagePicker, animated: animated, completion: completion) 28 | } 29 | 30 | /// Present Image Picker with External Images using closures rather than delegation. 31 | /// 32 | /// - Parameters: 33 | /// - imagePicker: the `OpalImagePickerController` 34 | /// - animated: is presentation animated 35 | /// - maximumSelectionsAllowed: the maximum number of image selections allowed for the user. Defaults to 10. This is advised to limit the amount of memory to store all the images. 36 | /// - numberOfExternalItems: the number of external items 37 | /// - externalItemsTitle: the localized title for display in the `UISegmentedControl` 38 | /// - externalURLForIndex: the `URL` for an external item at the given index. 39 | /// - selectImages: notifies that image selections have completed with an array of `UIImage` 40 | /// - selectAssets: notifies that image selections have completed with an array of `PHAsset` 41 | /// - selectExternalURLs: notifies that image selections have completed with an array of `URL` 42 | /// - cancel: notifies when Image Picker has been cancelled by user 43 | /// - completion: notifies when the Image Picker finished presenting 44 | func presentOpalImagePickerController(_ imagePicker: OpalImagePickerController, animated: Bool, maximumSelectionsAllowed: Int = 10, numberOfExternalItems: Int, externalItemsTitle: String, externalURLForIndex: @escaping (Int) -> URL?, selectImages: (([UIImage]) -> Void)? = nil, selectAssets: (([PHAsset]) -> Void)? = nil, selectExternalURLs: (([URL]) -> Void)? = nil, cancel: @escaping () -> Void, completion: (() -> Void)? = nil) { 45 | let manager = OpalImagePickerWithExternalItemsManager.sharedWithExternalItems 46 | 47 | if selectImages != nil { 48 | print("The `selectImages` parameter will be removed in future versions. Please switch to using `selectAssets`, and download `UIImage` using the `PHAsset` APIs.") 49 | } 50 | 51 | manager.selectImages = selectImages 52 | manager.selectAssets = selectAssets 53 | manager.selectURLs = selectExternalURLs 54 | manager.cancel = cancel 55 | manager.numberOfExternalItems = numberOfExternalItems 56 | manager.externalItemsTitle = externalItemsTitle 57 | manager.externalURLForIndex = externalURLForIndex 58 | imagePicker.imagePickerDelegate = manager 59 | imagePicker.maximumSelectionsAllowed = maximumSelectionsAllowed 60 | present(imagePicker, animated: animated, completion: completion) 61 | } 62 | } 63 | 64 | class OpalImagePickerWithExternalItemsManager: OpalImagePickerManager { 65 | var selectImages: (([UIImage]) -> Void)? 66 | var selectURLs: (([URL]) -> Void)? 67 | var externalURLForIndex: ((Int) -> URL?)? 68 | var numberOfExternalItems = 0 69 | var externalItemsTitle = NSLocalizedString("External", comment: "External (Segmented Control Title)") 70 | 71 | static let sharedWithExternalItems = OpalImagePickerWithExternalItemsManager() 72 | 73 | override init() { } 74 | 75 | func imagePickerNumberOfExternalItems(_ picker: OpalImagePickerController) -> Int { 76 | return numberOfExternalItems 77 | } 78 | 79 | func imagePickerTitleForExternalItems(_ picker: OpalImagePickerController) -> String { 80 | return externalItemsTitle 81 | } 82 | 83 | func imagePicker(_ picker: OpalImagePickerController, imageURLforExternalItemAtIndex index: Int) -> URL? { 84 | return externalURLForIndex?(index) 85 | } 86 | 87 | func imagePicker(_ picker: OpalImagePickerController, didFinishPickingExternalURLs urls: [URL]) { 88 | selectURLs?(urls) 89 | } 90 | 91 | func imagePickerDidFinishPickingImages(_ picker: OpalImagePickerController, images: [UIImage]) { 92 | selectImages?(images) 93 | } 94 | } 95 | 96 | class OpalImagePickerManager: NSObject { 97 | var selectAssets: (([PHAsset]) -> Void)? 98 | var cancel: (() -> Void)? 99 | 100 | static var shared = OpalImagePickerManager() 101 | override init() { } 102 | } 103 | 104 | extension OpalImagePickerManager: OpalImagePickerControllerDelegate { 105 | func imagePicker(_ picker: OpalImagePickerController, didFinishPickingAssets assets: [PHAsset]) { 106 | selectAssets?(assets) 107 | } 108 | 109 | func imagePickerDidCancel(_ picker: OpalImagePickerController) { 110 | cancel?() 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /OpalImagePicker/SupportingFiles/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // OpalImagePicker 4 | // 5 | // Created by Kristos Katsanevas on 1/15/17. 6 | // Copyright © 2017 Opal Orange LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /OpalImagePicker/SupportingFiles/Assets.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" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /OpalImagePicker/SupportingFiles/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /OpalImagePicker/SupportingFiles/Assets.xcassets/checkmark.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "checkmark.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /OpalImagePicker/SupportingFiles/Assets.xcassets/checkmark.imageset/checkmark.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opalorange/OpalImagePicker/c19658b1571b87e8426241e63d17791a4d92be32/OpalImagePicker/SupportingFiles/Assets.xcassets/checkmark.imageset/checkmark.pdf -------------------------------------------------------------------------------- /OpalImagePicker/SupportingFiles/Assets.xcassets/gradient.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "gradient.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "original" 14 | } 15 | } -------------------------------------------------------------------------------- /OpalImagePicker/SupportingFiles/Assets.xcassets/gradient.imageset/gradient.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opalorange/OpalImagePicker/c19658b1571b87e8426241e63d17791a4d92be32/OpalImagePicker/SupportingFiles/Assets.xcassets/gradient.imageset/gradient.pdf -------------------------------------------------------------------------------- /OpalImagePicker/SupportingFiles/Base.lproj/LaunchScreen.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 | 27 | 28 | -------------------------------------------------------------------------------- /OpalImagePicker/SupportingFiles/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 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /OpalImagePicker/SupportingFiles/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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | NSPhotoLibraryUsageDescription 45 | This app needs to use Photo Library 46 | 47 | 48 | -------------------------------------------------------------------------------- /OpalImagePicker/SupportingFiles/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // OpalImagePicker 4 | // 5 | // Created by Kristos Katsanevas on 1/15/17. 6 | // Copyright © 2017 Opal Orange LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | override func didReceiveMemoryWarning() { 19 | super.didReceiveMemoryWarning() 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | @IBAction func launchPicker(_ sender: Any) { 24 | let imagePicker = OpalImagePickerController() 25 | presentOpalImagePickerController(imagePicker, animated: true, 26 | select: { (assets) in 27 | //Select Assets 28 | self.dismiss(animated: true, completion: nil) 29 | }, cancel: { 30 | //Cancel 31 | }) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - line_length 3 | - trailing_whitespace 4 | - function_parameter_count 5 | - type_body_length 6 | - file_length -------------------------------------------------------------------------------- /OpalImagePickerDemo/OpalImagePickerDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 012C9C1C1F780C2100B2150A /* DelegateExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012C9C1B1F780C2100B2150A /* DelegateExampleViewController.swift */; }; 11 | 01A158D11E2B65E100AC7299 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01A158D01E2B65E100AC7299 /* AppDelegate.swift */; }; 12 | 01A158D31E2B65E100AC7299 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01A158D21E2B65E100AC7299 /* ViewController.swift */; }; 13 | 01A158D61E2B65E100AC7299 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 01A158D41E2B65E100AC7299 /* Main.storyboard */; }; 14 | 01A158D81E2B65E100AC7299 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01A158D71E2B65E100AC7299 /* Assets.xcassets */; }; 15 | 01A158DB1E2B65E100AC7299 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 01A158D91E2B65E100AC7299 /* LaunchScreen.storyboard */; }; 16 | 01A158E61E2B65E100AC7299 /* OpalImagePickerDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01A158E51E2B65E100AC7299 /* OpalImagePickerDemoTests.swift */; }; 17 | 2387886876E9E3634F6DF765 /* Pods_OpalImagePickerDemoTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04290C3291D376D5CF2528D1 /* Pods_OpalImagePickerDemoTests.framework */; }; 18 | 7E4BE18D023A99779525A5CF /* Pods_OpalImagePickerDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 600655EE521C1331CEFD2000 /* Pods_OpalImagePickerDemo.framework */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | 01A158E21E2B65E100AC7299 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = 01A158C51E2B65E100AC7299 /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = 01A158CC1E2B65E100AC7299; 27 | remoteInfo = OpalImagePickerDemo; 28 | }; 29 | /* End PBXContainerItemProxy section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 012C9C1B1F780C2100B2150A /* DelegateExampleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelegateExampleViewController.swift; sourceTree = ""; }; 33 | 01A158CD1E2B65E100AC7299 /* OpalImagePickerDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OpalImagePickerDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 01A158D01E2B65E100AC7299 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 35 | 01A158D21E2B65E100AC7299 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 36 | 01A158D51E2B65E100AC7299 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 37 | 01A158D71E2B65E100AC7299 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 38 | 01A158DA1E2B65E100AC7299 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 39 | 01A158DC1E2B65E100AC7299 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | 01A158E11E2B65E100AC7299 /* OpalImagePickerDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OpalImagePickerDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 01A158E51E2B65E100AC7299 /* OpalImagePickerDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpalImagePickerDemoTests.swift; sourceTree = ""; }; 42 | 01A158E71E2B65E100AC7299 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 43 | 04290C3291D376D5CF2528D1 /* Pods_OpalImagePickerDemoTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OpalImagePickerDemoTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 08C9DE5138B309785B02C777 /* Pods-OpalImagePickerDemoTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OpalImagePickerDemoTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-OpalImagePickerDemoTests/Pods-OpalImagePickerDemoTests.release.xcconfig"; sourceTree = ""; }; 45 | 5FD1CC494ED525AE7451B58F /* Pods-OpalImagePickerDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OpalImagePickerDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-OpalImagePickerDemo/Pods-OpalImagePickerDemo.release.xcconfig"; sourceTree = ""; }; 46 | 600655EE521C1331CEFD2000 /* Pods_OpalImagePickerDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OpalImagePickerDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 882FCFE3E2CCC26B3BCA5174 /* Pods-OpalImagePickerDemoTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OpalImagePickerDemoTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-OpalImagePickerDemoTests/Pods-OpalImagePickerDemoTests.debug.xcconfig"; sourceTree = ""; }; 48 | 89DB162343DD175D4C40F6ED /* Pods-OpalImagePickerDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OpalImagePickerDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-OpalImagePickerDemo/Pods-OpalImagePickerDemo.debug.xcconfig"; sourceTree = ""; }; 49 | /* End PBXFileReference section */ 50 | 51 | /* Begin PBXFrameworksBuildPhase section */ 52 | 01A158CA1E2B65E100AC7299 /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | 7E4BE18D023A99779525A5CF /* Pods_OpalImagePickerDemo.framework in Frameworks */, 57 | ); 58 | runOnlyForDeploymentPostprocessing = 0; 59 | }; 60 | 01A158DE1E2B65E100AC7299 /* Frameworks */ = { 61 | isa = PBXFrameworksBuildPhase; 62 | buildActionMask = 2147483647; 63 | files = ( 64 | 2387886876E9E3634F6DF765 /* Pods_OpalImagePickerDemoTests.framework in Frameworks */, 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | /* End PBXFrameworksBuildPhase section */ 69 | 70 | /* Begin PBXGroup section */ 71 | 01A158C41E2B65E100AC7299 = { 72 | isa = PBXGroup; 73 | children = ( 74 | 01A158CF1E2B65E100AC7299 /* OpalImagePickerDemo */, 75 | 01A158E41E2B65E100AC7299 /* OpalImagePickerDemoTests */, 76 | 01A158CE1E2B65E100AC7299 /* Products */, 77 | E29D52C7549C82665DB1B486 /* Pods */, 78 | 1B2ABC7CC8F65E285CEC376A /* Frameworks */, 79 | ); 80 | sourceTree = ""; 81 | }; 82 | 01A158CE1E2B65E100AC7299 /* Products */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 01A158CD1E2B65E100AC7299 /* OpalImagePickerDemo.app */, 86 | 01A158E11E2B65E100AC7299 /* OpalImagePickerDemoTests.xctest */, 87 | ); 88 | name = Products; 89 | sourceTree = ""; 90 | }; 91 | 01A158CF1E2B65E100AC7299 /* OpalImagePickerDemo */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 01A158D01E2B65E100AC7299 /* AppDelegate.swift */, 95 | 01A158D21E2B65E100AC7299 /* ViewController.swift */, 96 | 012C9C1B1F780C2100B2150A /* DelegateExampleViewController.swift */, 97 | 01A158D41E2B65E100AC7299 /* Main.storyboard */, 98 | 01A158D71E2B65E100AC7299 /* Assets.xcassets */, 99 | 01A158D91E2B65E100AC7299 /* LaunchScreen.storyboard */, 100 | 01A158DC1E2B65E100AC7299 /* Info.plist */, 101 | ); 102 | path = OpalImagePickerDemo; 103 | sourceTree = ""; 104 | }; 105 | 01A158E41E2B65E100AC7299 /* OpalImagePickerDemoTests */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 01A158E51E2B65E100AC7299 /* OpalImagePickerDemoTests.swift */, 109 | 01A158E71E2B65E100AC7299 /* Info.plist */, 110 | ); 111 | path = OpalImagePickerDemoTests; 112 | sourceTree = ""; 113 | }; 114 | 1B2ABC7CC8F65E285CEC376A /* Frameworks */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 600655EE521C1331CEFD2000 /* Pods_OpalImagePickerDemo.framework */, 118 | 04290C3291D376D5CF2528D1 /* Pods_OpalImagePickerDemoTests.framework */, 119 | ); 120 | name = Frameworks; 121 | sourceTree = ""; 122 | }; 123 | E29D52C7549C82665DB1B486 /* Pods */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 89DB162343DD175D4C40F6ED /* Pods-OpalImagePickerDemo.debug.xcconfig */, 127 | 5FD1CC494ED525AE7451B58F /* Pods-OpalImagePickerDemo.release.xcconfig */, 128 | 882FCFE3E2CCC26B3BCA5174 /* Pods-OpalImagePickerDemoTests.debug.xcconfig */, 129 | 08C9DE5138B309785B02C777 /* Pods-OpalImagePickerDemoTests.release.xcconfig */, 130 | ); 131 | name = Pods; 132 | sourceTree = ""; 133 | }; 134 | /* End PBXGroup section */ 135 | 136 | /* Begin PBXNativeTarget section */ 137 | 01A158CC1E2B65E100AC7299 /* OpalImagePickerDemo */ = { 138 | isa = PBXNativeTarget; 139 | buildConfigurationList = 01A158EA1E2B65E100AC7299 /* Build configuration list for PBXNativeTarget "OpalImagePickerDemo" */; 140 | buildPhases = ( 141 | 1D93313F525300518FA86C34 /* [CP] Check Pods Manifest.lock */, 142 | 01A158C91E2B65E100AC7299 /* Sources */, 143 | 01A158CA1E2B65E100AC7299 /* Frameworks */, 144 | 01A158CB1E2B65E100AC7299 /* Resources */, 145 | 70B832BC2383541ED2DCF3A0 /* [CP] Embed Pods Frameworks */, 146 | 010B06552013BA58002811B6 /* ShellScript */, 147 | ); 148 | buildRules = ( 149 | ); 150 | dependencies = ( 151 | ); 152 | name = OpalImagePickerDemo; 153 | productName = OpalImagePickerDemo; 154 | productReference = 01A158CD1E2B65E100AC7299 /* OpalImagePickerDemo.app */; 155 | productType = "com.apple.product-type.application"; 156 | }; 157 | 01A158E01E2B65E100AC7299 /* OpalImagePickerDemoTests */ = { 158 | isa = PBXNativeTarget; 159 | buildConfigurationList = 01A158ED1E2B65E100AC7299 /* Build configuration list for PBXNativeTarget "OpalImagePickerDemoTests" */; 160 | buildPhases = ( 161 | 3466E0B712338F6A8C2A8416 /* [CP] Check Pods Manifest.lock */, 162 | 01A158DD1E2B65E100AC7299 /* Sources */, 163 | 01A158DE1E2B65E100AC7299 /* Frameworks */, 164 | 01A158DF1E2B65E100AC7299 /* Resources */, 165 | ); 166 | buildRules = ( 167 | ); 168 | dependencies = ( 169 | 01A158E31E2B65E100AC7299 /* PBXTargetDependency */, 170 | ); 171 | name = OpalImagePickerDemoTests; 172 | productName = OpalImagePickerDemoTests; 173 | productReference = 01A158E11E2B65E100AC7299 /* OpalImagePickerDemoTests.xctest */; 174 | productType = "com.apple.product-type.bundle.unit-test"; 175 | }; 176 | /* End PBXNativeTarget section */ 177 | 178 | /* Begin PBXProject section */ 179 | 01A158C51E2B65E100AC7299 /* Project object */ = { 180 | isa = PBXProject; 181 | attributes = { 182 | LastSwiftUpdateCheck = 0820; 183 | LastUpgradeCheck = 1020; 184 | ORGANIZATIONNAME = "Opal Orange LLC"; 185 | TargetAttributes = { 186 | 01A158CC1E2B65E100AC7299 = { 187 | CreatedOnToolsVersion = 8.2.1; 188 | LastSwiftMigration = 1020; 189 | ProvisioningStyle = Automatic; 190 | }; 191 | 01A158E01E2B65E100AC7299 = { 192 | CreatedOnToolsVersion = 8.2.1; 193 | LastSwiftMigration = 1020; 194 | ProvisioningStyle = Automatic; 195 | TestTargetID = 01A158CC1E2B65E100AC7299; 196 | }; 197 | }; 198 | }; 199 | buildConfigurationList = 01A158C81E2B65E100AC7299 /* Build configuration list for PBXProject "OpalImagePickerDemo" */; 200 | compatibilityVersion = "Xcode 3.2"; 201 | developmentRegion = en; 202 | hasScannedForEncodings = 0; 203 | knownRegions = ( 204 | en, 205 | Base, 206 | ); 207 | mainGroup = 01A158C41E2B65E100AC7299; 208 | productRefGroup = 01A158CE1E2B65E100AC7299 /* Products */; 209 | projectDirPath = ""; 210 | projectRoot = ""; 211 | targets = ( 212 | 01A158CC1E2B65E100AC7299 /* OpalImagePickerDemo */, 213 | 01A158E01E2B65E100AC7299 /* OpalImagePickerDemoTests */, 214 | ); 215 | }; 216 | /* End PBXProject section */ 217 | 218 | /* Begin PBXResourcesBuildPhase section */ 219 | 01A158CB1E2B65E100AC7299 /* Resources */ = { 220 | isa = PBXResourcesBuildPhase; 221 | buildActionMask = 2147483647; 222 | files = ( 223 | 01A158DB1E2B65E100AC7299 /* LaunchScreen.storyboard in Resources */, 224 | 01A158D81E2B65E100AC7299 /* Assets.xcassets in Resources */, 225 | 01A158D61E2B65E100AC7299 /* Main.storyboard in Resources */, 226 | ); 227 | runOnlyForDeploymentPostprocessing = 0; 228 | }; 229 | 01A158DF1E2B65E100AC7299 /* Resources */ = { 230 | isa = PBXResourcesBuildPhase; 231 | buildActionMask = 2147483647; 232 | files = ( 233 | ); 234 | runOnlyForDeploymentPostprocessing = 0; 235 | }; 236 | /* End PBXResourcesBuildPhase section */ 237 | 238 | /* Begin PBXShellScriptBuildPhase section */ 239 | 010B06552013BA58002811B6 /* ShellScript */ = { 240 | isa = PBXShellScriptBuildPhase; 241 | buildActionMask = 2147483647; 242 | files = ( 243 | ); 244 | inputPaths = ( 245 | ); 246 | outputPaths = ( 247 | ); 248 | runOnlyForDeploymentPostprocessing = 0; 249 | shellPath = /bin/sh; 250 | shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi"; 251 | }; 252 | 1D93313F525300518FA86C34 /* [CP] Check Pods Manifest.lock */ = { 253 | isa = PBXShellScriptBuildPhase; 254 | buildActionMask = 2147483647; 255 | files = ( 256 | ); 257 | inputPaths = ( 258 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 259 | "${PODS_ROOT}/Manifest.lock", 260 | ); 261 | name = "[CP] Check Pods Manifest.lock"; 262 | outputPaths = ( 263 | "$(DERIVED_FILE_DIR)/Pods-OpalImagePickerDemo-checkManifestLockResult.txt", 264 | ); 265 | runOnlyForDeploymentPostprocessing = 0; 266 | shellPath = /bin/sh; 267 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 268 | showEnvVarsInLog = 0; 269 | }; 270 | 3466E0B712338F6A8C2A8416 /* [CP] Check Pods Manifest.lock */ = { 271 | isa = PBXShellScriptBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | ); 275 | inputPaths = ( 276 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 277 | "${PODS_ROOT}/Manifest.lock", 278 | ); 279 | name = "[CP] Check Pods Manifest.lock"; 280 | outputPaths = ( 281 | "$(DERIVED_FILE_DIR)/Pods-OpalImagePickerDemoTests-checkManifestLockResult.txt", 282 | ); 283 | runOnlyForDeploymentPostprocessing = 0; 284 | shellPath = /bin/sh; 285 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 286 | showEnvVarsInLog = 0; 287 | }; 288 | 70B832BC2383541ED2DCF3A0 /* [CP] Embed Pods Frameworks */ = { 289 | isa = PBXShellScriptBuildPhase; 290 | buildActionMask = 2147483647; 291 | files = ( 292 | ); 293 | inputPaths = ( 294 | "${PODS_ROOT}/Target Support Files/Pods-OpalImagePickerDemo/Pods-OpalImagePickerDemo-frameworks.sh", 295 | "${BUILT_PRODUCTS_DIR}/OpalImagePicker/OpalImagePicker.framework", 296 | ); 297 | name = "[CP] Embed Pods Frameworks"; 298 | outputPaths = ( 299 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpalImagePicker.framework", 300 | ); 301 | runOnlyForDeploymentPostprocessing = 0; 302 | shellPath = /bin/sh; 303 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-OpalImagePickerDemo/Pods-OpalImagePickerDemo-frameworks.sh\"\n"; 304 | showEnvVarsInLog = 0; 305 | }; 306 | /* End PBXShellScriptBuildPhase section */ 307 | 308 | /* Begin PBXSourcesBuildPhase section */ 309 | 01A158C91E2B65E100AC7299 /* Sources */ = { 310 | isa = PBXSourcesBuildPhase; 311 | buildActionMask = 2147483647; 312 | files = ( 313 | 012C9C1C1F780C2100B2150A /* DelegateExampleViewController.swift in Sources */, 314 | 01A158D31E2B65E100AC7299 /* ViewController.swift in Sources */, 315 | 01A158D11E2B65E100AC7299 /* AppDelegate.swift in Sources */, 316 | ); 317 | runOnlyForDeploymentPostprocessing = 0; 318 | }; 319 | 01A158DD1E2B65E100AC7299 /* Sources */ = { 320 | isa = PBXSourcesBuildPhase; 321 | buildActionMask = 2147483647; 322 | files = ( 323 | 01A158E61E2B65E100AC7299 /* OpalImagePickerDemoTests.swift in Sources */, 324 | ); 325 | runOnlyForDeploymentPostprocessing = 0; 326 | }; 327 | /* End PBXSourcesBuildPhase section */ 328 | 329 | /* Begin PBXTargetDependency section */ 330 | 01A158E31E2B65E100AC7299 /* PBXTargetDependency */ = { 331 | isa = PBXTargetDependency; 332 | target = 01A158CC1E2B65E100AC7299 /* OpalImagePickerDemo */; 333 | targetProxy = 01A158E21E2B65E100AC7299 /* PBXContainerItemProxy */; 334 | }; 335 | /* End PBXTargetDependency section */ 336 | 337 | /* Begin PBXVariantGroup section */ 338 | 01A158D41E2B65E100AC7299 /* Main.storyboard */ = { 339 | isa = PBXVariantGroup; 340 | children = ( 341 | 01A158D51E2B65E100AC7299 /* Base */, 342 | ); 343 | name = Main.storyboard; 344 | sourceTree = ""; 345 | }; 346 | 01A158D91E2B65E100AC7299 /* LaunchScreen.storyboard */ = { 347 | isa = PBXVariantGroup; 348 | children = ( 349 | 01A158DA1E2B65E100AC7299 /* Base */, 350 | ); 351 | name = LaunchScreen.storyboard; 352 | sourceTree = ""; 353 | }; 354 | /* End PBXVariantGroup section */ 355 | 356 | /* Begin XCBuildConfiguration section */ 357 | 01A158E81E2B65E100AC7299 /* Debug */ = { 358 | isa = XCBuildConfiguration; 359 | buildSettings = { 360 | ALWAYS_SEARCH_USER_PATHS = NO; 361 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 362 | CLANG_ANALYZER_NONNULL = YES; 363 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 364 | CLANG_CXX_LIBRARY = "libc++"; 365 | CLANG_ENABLE_MODULES = YES; 366 | CLANG_ENABLE_OBJC_ARC = YES; 367 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 368 | CLANG_WARN_BOOL_CONVERSION = YES; 369 | CLANG_WARN_COMMA = YES; 370 | CLANG_WARN_CONSTANT_CONVERSION = YES; 371 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 372 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 373 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 374 | CLANG_WARN_EMPTY_BODY = YES; 375 | CLANG_WARN_ENUM_CONVERSION = YES; 376 | CLANG_WARN_INFINITE_RECURSION = YES; 377 | CLANG_WARN_INT_CONVERSION = YES; 378 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 379 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 380 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 381 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 382 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 383 | CLANG_WARN_STRICT_PROTOTYPES = YES; 384 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 385 | CLANG_WARN_UNREACHABLE_CODE = YES; 386 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 387 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 388 | COPY_PHASE_STRIP = NO; 389 | DEBUG_INFORMATION_FORMAT = dwarf; 390 | ENABLE_STRICT_OBJC_MSGSEND = YES; 391 | ENABLE_TESTABILITY = YES; 392 | GCC_C_LANGUAGE_STANDARD = gnu99; 393 | GCC_DYNAMIC_NO_PIC = NO; 394 | GCC_NO_COMMON_BLOCKS = YES; 395 | GCC_OPTIMIZATION_LEVEL = 0; 396 | GCC_PREPROCESSOR_DEFINITIONS = ( 397 | "DEBUG=1", 398 | "$(inherited)", 399 | ); 400 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 401 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 402 | GCC_WARN_UNDECLARED_SELECTOR = YES; 403 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 404 | GCC_WARN_UNUSED_FUNCTION = YES; 405 | GCC_WARN_UNUSED_VARIABLE = YES; 406 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 407 | MTL_ENABLE_DEBUG_INFO = YES; 408 | ONLY_ACTIVE_ARCH = YES; 409 | SDKROOT = iphoneos; 410 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 411 | SWIFT_INSTALL_OBJC_HEADER = NO; 412 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 413 | SWIFT_VERSION = 5.0; 414 | TARGETED_DEVICE_FAMILY = "1,2"; 415 | }; 416 | name = Debug; 417 | }; 418 | 01A158E91E2B65E100AC7299 /* Release */ = { 419 | isa = XCBuildConfiguration; 420 | buildSettings = { 421 | ALWAYS_SEARCH_USER_PATHS = NO; 422 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 423 | CLANG_ANALYZER_NONNULL = YES; 424 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 425 | CLANG_CXX_LIBRARY = "libc++"; 426 | CLANG_ENABLE_MODULES = YES; 427 | CLANG_ENABLE_OBJC_ARC = YES; 428 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 429 | CLANG_WARN_BOOL_CONVERSION = YES; 430 | CLANG_WARN_COMMA = YES; 431 | CLANG_WARN_CONSTANT_CONVERSION = YES; 432 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 433 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 434 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 435 | CLANG_WARN_EMPTY_BODY = YES; 436 | CLANG_WARN_ENUM_CONVERSION = YES; 437 | CLANG_WARN_INFINITE_RECURSION = YES; 438 | CLANG_WARN_INT_CONVERSION = YES; 439 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 440 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 441 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 442 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 443 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 444 | CLANG_WARN_STRICT_PROTOTYPES = YES; 445 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 446 | CLANG_WARN_UNREACHABLE_CODE = YES; 447 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 448 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 449 | COPY_PHASE_STRIP = NO; 450 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 451 | ENABLE_NS_ASSERTIONS = NO; 452 | ENABLE_STRICT_OBJC_MSGSEND = YES; 453 | GCC_C_LANGUAGE_STANDARD = gnu99; 454 | GCC_NO_COMMON_BLOCKS = YES; 455 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 456 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 457 | GCC_WARN_UNDECLARED_SELECTOR = YES; 458 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 459 | GCC_WARN_UNUSED_FUNCTION = YES; 460 | GCC_WARN_UNUSED_VARIABLE = YES; 461 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 462 | MTL_ENABLE_DEBUG_INFO = NO; 463 | SDKROOT = iphoneos; 464 | SWIFT_INSTALL_OBJC_HEADER = NO; 465 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 466 | SWIFT_VERSION = 5.0; 467 | TARGETED_DEVICE_FAMILY = "1,2"; 468 | VALIDATE_PRODUCT = YES; 469 | }; 470 | name = Release; 471 | }; 472 | 01A158EB1E2B65E100AC7299 /* Debug */ = { 473 | isa = XCBuildConfiguration; 474 | baseConfigurationReference = 89DB162343DD175D4C40F6ED /* Pods-OpalImagePickerDemo.debug.xcconfig */; 475 | buildSettings = { 476 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 477 | CLANG_ENABLE_MODULES = YES; 478 | DEVELOPMENT_TEAM = ""; 479 | INFOPLIST_FILE = OpalImagePickerDemo/Info.plist; 480 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 481 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 482 | PRODUCT_BUNDLE_IDENTIFIER = com.opalorange.OpalImagePickerDemo; 483 | PRODUCT_NAME = "$(TARGET_NAME)"; 484 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 485 | SWIFT_VERSION = 5.0; 486 | }; 487 | name = Debug; 488 | }; 489 | 01A158EC1E2B65E100AC7299 /* Release */ = { 490 | isa = XCBuildConfiguration; 491 | baseConfigurationReference = 5FD1CC494ED525AE7451B58F /* Pods-OpalImagePickerDemo.release.xcconfig */; 492 | buildSettings = { 493 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 494 | CLANG_ENABLE_MODULES = YES; 495 | DEVELOPMENT_TEAM = ""; 496 | INFOPLIST_FILE = OpalImagePickerDemo/Info.plist; 497 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 498 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 499 | PRODUCT_BUNDLE_IDENTIFIER = com.opalorange.OpalImagePickerDemo; 500 | PRODUCT_NAME = "$(TARGET_NAME)"; 501 | SWIFT_VERSION = 5.0; 502 | }; 503 | name = Release; 504 | }; 505 | 01A158EE1E2B65E100AC7299 /* Debug */ = { 506 | isa = XCBuildConfiguration; 507 | baseConfigurationReference = 882FCFE3E2CCC26B3BCA5174 /* Pods-OpalImagePickerDemoTests.debug.xcconfig */; 508 | buildSettings = { 509 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 510 | BUNDLE_LOADER = "$(TEST_HOST)"; 511 | INFOPLIST_FILE = OpalImagePickerDemoTests/Info.plist; 512 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 513 | PRODUCT_BUNDLE_IDENTIFIER = com.opalorange.OpalImagePickerDemoTests; 514 | PRODUCT_NAME = "$(TARGET_NAME)"; 515 | SWIFT_VERSION = 5.0; 516 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/OpalImagePickerDemo.app/OpalImagePickerDemo"; 517 | }; 518 | name = Debug; 519 | }; 520 | 01A158EF1E2B65E100AC7299 /* Release */ = { 521 | isa = XCBuildConfiguration; 522 | baseConfigurationReference = 08C9DE5138B309785B02C777 /* Pods-OpalImagePickerDemoTests.release.xcconfig */; 523 | buildSettings = { 524 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 525 | BUNDLE_LOADER = "$(TEST_HOST)"; 526 | INFOPLIST_FILE = OpalImagePickerDemoTests/Info.plist; 527 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 528 | PRODUCT_BUNDLE_IDENTIFIER = com.opalorange.OpalImagePickerDemoTests; 529 | PRODUCT_NAME = "$(TARGET_NAME)"; 530 | SWIFT_VERSION = 5.0; 531 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/OpalImagePickerDemo.app/OpalImagePickerDemo"; 532 | }; 533 | name = Release; 534 | }; 535 | /* End XCBuildConfiguration section */ 536 | 537 | /* Begin XCConfigurationList section */ 538 | 01A158C81E2B65E100AC7299 /* Build configuration list for PBXProject "OpalImagePickerDemo" */ = { 539 | isa = XCConfigurationList; 540 | buildConfigurations = ( 541 | 01A158E81E2B65E100AC7299 /* Debug */, 542 | 01A158E91E2B65E100AC7299 /* Release */, 543 | ); 544 | defaultConfigurationIsVisible = 0; 545 | defaultConfigurationName = Release; 546 | }; 547 | 01A158EA1E2B65E100AC7299 /* Build configuration list for PBXNativeTarget "OpalImagePickerDemo" */ = { 548 | isa = XCConfigurationList; 549 | buildConfigurations = ( 550 | 01A158EB1E2B65E100AC7299 /* Debug */, 551 | 01A158EC1E2B65E100AC7299 /* Release */, 552 | ); 553 | defaultConfigurationIsVisible = 0; 554 | defaultConfigurationName = Release; 555 | }; 556 | 01A158ED1E2B65E100AC7299 /* Build configuration list for PBXNativeTarget "OpalImagePickerDemoTests" */ = { 557 | isa = XCConfigurationList; 558 | buildConfigurations = ( 559 | 01A158EE1E2B65E100AC7299 /* Debug */, 560 | 01A158EF1E2B65E100AC7299 /* Release */, 561 | ); 562 | defaultConfigurationIsVisible = 0; 563 | defaultConfigurationName = Release; 564 | }; 565 | /* End XCConfigurationList section */ 566 | }; 567 | rootObject = 01A158C51E2B65E100AC7299 /* Project object */; 568 | } 569 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/OpalImagePickerDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/OpalImagePickerDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/OpalImagePickerDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/OpalImagePickerDemo.xcworkspace/xcuserdata/Kris.xcuserdatad/IDEFindNavigatorScopes.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/OpalImagePickerDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // OpalImagePickerDemo 4 | // 5 | // Created by Kris Katsanevas on 1/15/17. 6 | // Copyright © 2017 Opal Orange LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/OpalImagePickerDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /OpalImagePickerDemo/OpalImagePickerDemo/Base.lproj/LaunchScreen.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 | 27 | 28 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/OpalImagePickerDemo/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 | 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 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/OpalImagePickerDemo/DelegateExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DelegateExampleViewController.swift 3 | // OpalImagePickerDemo 4 | // 5 | // Created by Kristos Katsanevas on 9/24/17. 6 | // Copyright © 2017 Opal Orange LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import OpalImagePicker 11 | import Photos 12 | 13 | class DelegateExampleViewController: UIViewController { 14 | @IBAction func photoLibraryTapped(_ sender: UIButton) { 15 | //Example instantiating OpalImagePickerController with delegate 16 | let imagePicker = OpalImagePickerController() 17 | imagePicker.imagePickerDelegate = self 18 | imagePicker.maximumSelectionsAllowed = 10 19 | present(imagePicker, animated: true, completion: nil) 20 | } 21 | } 22 | 23 | extension DelegateExampleViewController: OpalImagePickerControllerDelegate { 24 | func imagePickerDidCancel(_ picker: OpalImagePickerController) { 25 | //Cancel action? 26 | } 27 | 28 | func imagePicker(_ picker: OpalImagePickerController, didFinishPickingAssets assets: [PHAsset]) { 29 | //Save Images, update UI 30 | 31 | //Dismiss Controller 32 | presentedViewController?.dismiss(animated: true, completion: nil) 33 | } 34 | 35 | func imagePickerNumberOfExternalItems(_ picker: OpalImagePickerController) -> Int { 36 | return 1 37 | } 38 | 39 | func imagePickerTitleForExternalItems(_ picker: OpalImagePickerController) -> String { 40 | return NSLocalizedString("External", comment: "External (title for UISegmentedControl)") 41 | } 42 | 43 | func imagePicker(_ picker: OpalImagePickerController, imageURLforExternalItemAtIndex index: Int) -> URL? { 44 | return URL(string: "https://placeimg.com/500/500/nature") 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/OpalImagePickerDemo/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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSAppTransportSecurity 24 | 25 | NSAllowsArbitraryLoads 26 | 27 | 28 | NSPhotoLibraryUsageDescription 29 | Photos are used to display example OpalImagePicker. 30 | UILaunchStoryboardName 31 | LaunchScreen 32 | UIMainStoryboardFile 33 | Main 34 | UIRequiredDeviceCapabilities 35 | 36 | armv7 37 | 38 | UISupportedInterfaceOrientations 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UISupportedInterfaceOrientations~ipad 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationPortraitUpsideDown 48 | UIInterfaceOrientationLandscapeLeft 49 | UIInterfaceOrientationLandscapeRight 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/OpalImagePickerDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // OpalImagePickerDemo 4 | // 5 | // Created by Kris Katsanevas on 1/15/17. 6 | // Copyright © 2017 Opal Orange LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import OpalImagePicker 11 | import Photos 12 | 13 | class ViewController: UIViewController { 14 | 15 | @IBAction func photoLibraryTapped(_ sender: Any) { 16 | guard UIImagePickerController.isSourceTypeAvailable(.photoLibrary) else { 17 | //Show error to user? 18 | return 19 | } 20 | 21 | //Example Instantiating OpalImagePickerController with Closures 22 | let imagePicker = OpalImagePickerController() 23 | 24 | //Present Image Picker 25 | presentOpalImagePickerController(imagePicker, animated: true, select: { (_) in 26 | //Save Images, update UI 27 | 28 | //Dismiss Controller 29 | imagePicker.dismiss(animated: true, completion: nil) 30 | }, cancel: { 31 | //Cancel action? 32 | 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/OpalImagePickerDemoTests/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 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/OpalImagePickerDemoTests/OpalImagePickerDemoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpalImagePickerDemoTests.swift 3 | // OpalImagePickerDemoTests 4 | // 5 | // Created by Kris Katsanevas on 1/15/17. 6 | // Copyright © 2017 Opal Orange LLC. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import OpalImagePickerDemo 11 | 12 | class OpalImagePickerDemoTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.0' 2 | 3 | target 'OpalImagePickerDemo' do 4 | use_frameworks! 5 | 6 | pod 'OpalImagePicker', path: '..' 7 | 8 | target 'OpalImagePickerDemoTests' do 9 | inherit! :search_paths 10 | # Pods for testing 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - OpalImagePicker (2.1.0) 3 | 4 | DEPENDENCIES: 5 | - OpalImagePicker (from `..`) 6 | 7 | EXTERNAL SOURCES: 8 | OpalImagePicker: 9 | :path: ".." 10 | 11 | SPEC CHECKSUMS: 12 | OpalImagePicker: 40e9d4c27018538a704d8da34b2ff3a2a54a3dc8 13 | 14 | PODFILE CHECKSUM: 08249eb930de38e6448df592a8e8fc302822439d 15 | 16 | COCOAPODS: 1.8.4 17 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Local Podspecs/OpalImagePicker.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OpalImagePicker", 3 | "version": "2.1.0", 4 | "summary": "Multiple Selection Image Picker for iOS written in Swift", 5 | "homepage": "https://github.com/opalorange/OpalImagePicker", 6 | "license": { 7 | "type": "MIT", 8 | "file": "LICENSE" 9 | }, 10 | "authors": { 11 | "opalorange": "kris@opalorange.com" 12 | }, 13 | "requires_arc": true, 14 | "platforms": { 15 | "ios": "8.0" 16 | }, 17 | "source": { 18 | "git": "https://github.com/opalorange/OpalImagePicker.git", 19 | "tag": "2.1.0" 20 | }, 21 | "source_files": "OpalImagePicker/Source/*.swift", 22 | "resource_bundles": { 23 | "OpalImagePickerResources": [ 24 | "OpalImagePicker/SupportingFiles/**/*.xcassets" 25 | ] 26 | }, 27 | "swift_versions": "5.0", 28 | "swift_version": "5.0" 29 | } 30 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - OpalImagePicker (2.1.0) 3 | 4 | DEPENDENCIES: 5 | - OpalImagePicker (from `..`) 6 | 7 | EXTERNAL SOURCES: 8 | OpalImagePicker: 9 | :path: ".." 10 | 11 | SPEC CHECKSUMS: 12 | OpalImagePicker: 40e9d4c27018538a704d8da34b2ff3a2a54a3dc8 13 | 14 | PODFILE CHECKSUM: 08249eb930de38e6448df592a8e8fc302822439d 15 | 16 | COCOAPODS: 1.8.4 17 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/OpalImagePicker/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.7.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/OpalImagePicker/OpalImagePicker-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 | 2.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/OpalImagePicker/OpalImagePicker-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_OpalImagePicker : NSObject 3 | @end 4 | @implementation PodsDummy_OpalImagePicker 5 | @end 6 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/OpalImagePicker/OpalImagePicker-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 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/OpalImagePicker/OpalImagePicker-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 OpalImagePickerVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char OpalImagePickerVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/OpalImagePicker/OpalImagePicker.modulemap: -------------------------------------------------------------------------------- 1 | framework module OpalImagePicker { 2 | umbrella header "OpalImagePicker-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/OpalImagePicker/OpalImagePicker.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/OpalImagePicker 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/OpalImagePicker/ResourceBundle-OpalImagePickerResources-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 | 1.7.2 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/OpalImagePicker/ResourceBundle-OpalImagePickerResources-OpalImagePicker-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 | 2.1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemo/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 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemo/Pods-OpalImagePickerDemo-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 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemo/Pods-OpalImagePickerDemo-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## OpalImagePicker 5 | 6 | MIT License 7 | 8 | Copyright (c) 2017 opalorange 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | Generated by CocoaPods - https://cocoapods.org 29 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemo/Pods-OpalImagePickerDemo-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 | MIT License 18 | 19 | Copyright (c) 2017 opalorange 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | License 40 | MIT 41 | Title 42 | OpalImagePicker 43 | Type 44 | PSGroupSpecifier 45 | 46 | 47 | FooterText 48 | Generated by CocoaPods - https://cocoapods.org 49 | Title 50 | 51 | Type 52 | PSGroupSpecifier 53 | 54 | 55 | StringsTable 56 | Acknowledgements 57 | Title 58 | Acknowledgements 59 | 60 | 61 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemo/Pods-OpalImagePickerDemo-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_OpalImagePickerDemo : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_OpalImagePickerDemo 5 | @end 6 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemo/Pods-OpalImagePickerDemo-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | 23 | # Used as a return value for each invocation of `strip_invalid_archs` function. 24 | STRIP_BINARY_RETVAL=0 25 | 26 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 27 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 28 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 29 | 30 | # Copies and strips a vendored framework 31 | install_framework() 32 | { 33 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 34 | local source="${BUILT_PRODUCTS_DIR}/$1" 35 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 36 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 37 | elif [ -r "$1" ]; then 38 | local source="$1" 39 | fi 40 | 41 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 42 | 43 | if [ -L "${source}" ]; then 44 | echo "Symlinked..." 45 | source="$(readlink "${source}")" 46 | fi 47 | 48 | # Use filter instead of exclude so missing patterns don't throw errors. 49 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 50 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 51 | 52 | local basename 53 | basename="$(basename -s .framework "$1")" 54 | binary="${destination}/${basename}.framework/${basename}" 55 | 56 | if ! [ -r "$binary" ]; then 57 | binary="${destination}/${basename}" 58 | elif [ -L "${binary}" ]; then 59 | echo "Destination binary is symlinked..." 60 | dirname="$(dirname "${binary}")" 61 | binary="${dirname}/$(readlink "${binary}")" 62 | fi 63 | 64 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 65 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 66 | strip_invalid_archs "$binary" 67 | fi 68 | 69 | # Resign the code if required by the build settings to avoid unstable apps 70 | code_sign_if_enabled "${destination}/$(basename "$1")" 71 | 72 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 73 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 74 | local swift_runtime_libs 75 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 76 | for lib in $swift_runtime_libs; do 77 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 78 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 79 | code_sign_if_enabled "${destination}/${lib}" 80 | done 81 | fi 82 | } 83 | 84 | # Copies and strips a vendored dSYM 85 | install_dsym() { 86 | local source="$1" 87 | if [ -r "$source" ]; then 88 | # Copy the dSYM into a the targets temp dir. 89 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 90 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 91 | 92 | local basename 93 | basename="$(basename -s .framework.dSYM "$source")" 94 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 95 | 96 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 97 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then 98 | strip_invalid_archs "$binary" 99 | fi 100 | 101 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 102 | # Move the stripped file into its final destination. 103 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 104 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 105 | else 106 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 107 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 108 | fi 109 | fi 110 | } 111 | 112 | # Copies the bcsymbolmap files of a vendored framework 113 | install_bcsymbolmap() { 114 | local bcsymbolmap_path="$1" 115 | local destination="${BUILT_PRODUCTS_DIR}" 116 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" 117 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" 118 | } 119 | 120 | # Signs a framework with the provided identity 121 | code_sign_if_enabled() { 122 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 123 | # Use the current code_sign_identity 124 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 125 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 126 | 127 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 128 | code_sign_cmd="$code_sign_cmd &" 129 | fi 130 | echo "$code_sign_cmd" 131 | eval "$code_sign_cmd" 132 | fi 133 | } 134 | 135 | # Strip invalid architectures 136 | strip_invalid_archs() { 137 | binary="$1" 138 | # Get architectures for current target binary 139 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 140 | # Intersect them with the architectures we are building for 141 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 142 | # If there are no archs supported by this binary then warn the user 143 | if [[ -z "$intersected_archs" ]]; then 144 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 145 | STRIP_BINARY_RETVAL=0 146 | return 147 | fi 148 | stripped="" 149 | for arch in $binary_archs; do 150 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 151 | # Strip non-valid architectures in-place 152 | lipo -remove "$arch" -output "$binary" "$binary" 153 | stripped="$stripped $arch" 154 | fi 155 | done 156 | if [[ "$stripped" ]]; then 157 | echo "Stripped $binary of architectures:$stripped" 158 | fi 159 | STRIP_BINARY_RETVAL=1 160 | } 161 | 162 | 163 | if [[ "$CONFIGURATION" == "Debug" ]]; then 164 | install_framework "${BUILT_PRODUCTS_DIR}/OpalImagePicker/OpalImagePicker.framework" 165 | fi 166 | if [[ "$CONFIGURATION" == "Release" ]]; then 167 | install_framework "${BUILT_PRODUCTS_DIR}/OpalImagePicker/OpalImagePicker.framework" 168 | fi 169 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 170 | wait 171 | fi 172 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemo/Pods-OpalImagePickerDemo-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | 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 60 | 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} 61 | ;; 62 | *.xib) 63 | 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 64 | 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} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | 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}" 115 | else 116 | 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}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemo/Pods-OpalImagePickerDemo-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_OpalImagePickerDemoVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_OpalImagePickerDemoVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemo/Pods-OpalImagePickerDemo.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/OpalImagePicker" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/OpalImagePicker/OpalImagePicker.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "OpalImagePicker" 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 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemo/Pods-OpalImagePickerDemo.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_OpalImagePickerDemo { 2 | umbrella header "Pods-OpalImagePickerDemo-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemo/Pods-OpalImagePickerDemo.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/OpalImagePicker" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/OpalImagePicker/OpalImagePicker.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "OpalImagePicker" 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 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemoTests/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 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemoTests/Pods-OpalImagePickerDemoTests-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 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemoTests/Pods-OpalImagePickerDemoTests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemoTests/Pods-OpalImagePickerDemoTests-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 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemoTests/Pods-OpalImagePickerDemoTests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_OpalImagePickerDemoTests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_OpalImagePickerDemoTests 5 | @end 6 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemoTests/Pods-OpalImagePickerDemoTests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 7 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # frameworks to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 13 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 14 | 15 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 16 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 17 | 18 | # Used as a return value for each invocation of `strip_invalid_archs` function. 19 | STRIP_BINARY_RETVAL=0 20 | 21 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 22 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 23 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 24 | 25 | # Copies and strips a vendored framework 26 | install_framework() 27 | { 28 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 29 | local source="${BUILT_PRODUCTS_DIR}/$1" 30 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 31 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 32 | elif [ -r "$1" ]; then 33 | local source="$1" 34 | fi 35 | 36 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 37 | 38 | if [ -L "${source}" ]; then 39 | echo "Symlinked..." 40 | source="$(readlink "${source}")" 41 | fi 42 | 43 | # Use filter instead of exclude so missing patterns don't throw errors. 44 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 45 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 46 | 47 | local basename 48 | basename="$(basename -s .framework "$1")" 49 | binary="${destination}/${basename}.framework/${basename}" 50 | if ! [ -r "$binary" ]; then 51 | binary="${destination}/${basename}" 52 | fi 53 | 54 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 55 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 56 | strip_invalid_archs "$binary" 57 | fi 58 | 59 | # Resign the code if required by the build settings to avoid unstable apps 60 | code_sign_if_enabled "${destination}/$(basename "$1")" 61 | 62 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 63 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 64 | local swift_runtime_libs 65 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 66 | for lib in $swift_runtime_libs; do 67 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 68 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 69 | code_sign_if_enabled "${destination}/${lib}" 70 | done 71 | fi 72 | } 73 | 74 | # Copies and strips a vendored dSYM 75 | install_dsym() { 76 | local source="$1" 77 | if [ -r "$source" ]; then 78 | # Copy the dSYM into a the targets temp dir. 79 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 80 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 81 | 82 | local basename 83 | basename="$(basename -s .framework.dSYM "$source")" 84 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 85 | 86 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 87 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 88 | strip_invalid_archs "$binary" 89 | fi 90 | 91 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 92 | # Move the stripped file into its final destination. 93 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 94 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 95 | else 96 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 97 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 98 | fi 99 | fi 100 | } 101 | 102 | # Signs a framework with the provided identity 103 | code_sign_if_enabled() { 104 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 105 | # Use the current code_sign_identitiy 106 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 107 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 108 | 109 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 110 | code_sign_cmd="$code_sign_cmd &" 111 | fi 112 | echo "$code_sign_cmd" 113 | eval "$code_sign_cmd" 114 | fi 115 | } 116 | 117 | # Strip invalid architectures 118 | strip_invalid_archs() { 119 | binary="$1" 120 | # Get architectures for current target binary 121 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 122 | # Intersect them with the architectures we are building for 123 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 124 | # If there are no archs supported by this binary then warn the user 125 | if [[ -z "$intersected_archs" ]]; then 126 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 127 | STRIP_BINARY_RETVAL=0 128 | return 129 | fi 130 | stripped="" 131 | for arch in $binary_archs; do 132 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 133 | # Strip non-valid architectures in-place 134 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 135 | stripped="$stripped $arch" 136 | fi 137 | done 138 | if [[ "$stripped" ]]; then 139 | echo "Stripped $binary of architectures:$stripped" 140 | fi 141 | STRIP_BINARY_RETVAL=1 142 | } 143 | 144 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 145 | wait 146 | fi 147 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemoTests/Pods-OpalImagePickerDemoTests-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | 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 60 | 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} 61 | ;; 62 | *.xib) 63 | 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 64 | 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} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | 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}" 115 | else 116 | 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}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemoTests/Pods-OpalImagePickerDemoTests-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_OpalImagePickerDemoTestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_OpalImagePickerDemoTestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemoTests/Pods-OpalImagePickerDemoTests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/OpalImagePicker" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/OpalImagePicker/OpalImagePicker.framework/Headers" 4 | OTHER_LDFLAGS = $(inherited) -framework "OpalImagePicker" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 10 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemoTests/Pods-OpalImagePickerDemoTests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_OpalImagePickerDemoTests { 2 | umbrella header "Pods-OpalImagePickerDemoTests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /OpalImagePickerDemo/Pods/Target Support Files/Pods-OpalImagePickerDemoTests/Pods-OpalImagePickerDemoTests.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/OpalImagePicker" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/OpalImagePicker/OpalImagePicker.framework/Headers" 4 | OTHER_LDFLAGS = $(inherited) -framework "OpalImagePicker" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 10 | -------------------------------------------------------------------------------- /OpalImagePickerTests/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 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /OpalImagePickerTests/MockOpalImagePickerControllerDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MockDelegate.swift 3 | // OpalImagePicker 4 | // 5 | // Created by Kristos Katsanevas on 1/16/17. 6 | // Copyright © 2017 Opal Orange LLC. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Photos 11 | import OpalImagePicker 12 | 13 | class MockOpalImagePickerControllerDelegate: OpalImagePickerControllerDelegate { 14 | 15 | var isDidFinishPickingAssetsCalled = false 16 | var isDidFinishPickingExternalCalled = false 17 | var isCancelledCalled = false 18 | 19 | func imagePicker(_ picker: OpalImagePickerController, didFinishPickingImages images: [UIImage]) { 20 | 21 | } 22 | 23 | func imagePicker(_ picker: OpalImagePickerController, didFinishPickingAssets assets: [PHAsset]) { 24 | isDidFinishPickingAssetsCalled = true 25 | } 26 | 27 | func imagePickerDidCancel(_ picker: OpalImagePickerController) { 28 | isCancelledCalled = true 29 | } 30 | 31 | func imagePickerNumberOfExternalItems(_ picker: OpalImagePickerController) -> Int { 32 | return 1 33 | } 34 | 35 | func imagePickerTitleForExternalItems(_ picker: OpalImagePickerController) -> String { 36 | return "External" 37 | } 38 | 39 | func imagePicker(_ picker: OpalImagePickerController, imageURLforExternalItemAtIndex index: Int) -> URL? { 40 | return nil 41 | } 42 | 43 | func imagePicker(_ picker: OpalImagePickerController, didFinishPickingExternalURLs urls: [URL]) { 44 | isDidFinishPickingExternalCalled = true 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /OpalImagePickerTests/OpalImagePickerControllerTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpalImagePickerControllerTest.swift 3 | // OpalImagePicker 4 | // 5 | // Created by Kristos Katsanevas on 1/16/17. 6 | // Copyright © 2017 Opal Orange LLC. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import OpalImagePicker 11 | 12 | class OpalImagePickerControllerTest: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testSetImagePickerDelegate() { 25 | let mockDelegate = MockOpalImagePickerControllerDelegate() 26 | let imagePicker = OpalImagePickerController() 27 | imagePicker.imagePickerDelegate = mockDelegate 28 | 29 | let rootVC = imagePicker.viewControllers[0] as? OpalImagePickerRootViewController 30 | XCTAssert(mockDelegate === rootVC?.delegate) 31 | } 32 | 33 | func testSetSelectionTintColor() { 34 | let tintColor = UIColor.brown 35 | let imagePicker = OpalImagePickerController() 36 | imagePicker.selectionTintColor = tintColor 37 | 38 | let rootVC = imagePicker.viewControllers[0] as? OpalImagePickerRootViewController 39 | XCTAssertEqual(tintColor, rootVC?.selectionTintColor) 40 | } 41 | 42 | func testSetSelectionImageTintColor() { 43 | let tintColor = UIColor.brown 44 | let imagePicker = OpalImagePickerController() 45 | imagePicker.selectionImageTintColor = tintColor 46 | 47 | let rootVC = imagePicker.viewControllers[0] as? OpalImagePickerRootViewController 48 | XCTAssertEqual(tintColor, rootVC?.selectionImageTintColor) 49 | } 50 | 51 | func testSetSelectionImage() { 52 | let image = UIImage() 53 | let imagePicker = OpalImagePickerController() 54 | imagePicker.selectionImage = image 55 | 56 | let rootVC = imagePicker.viewControllers[0] as? OpalImagePickerRootViewController 57 | XCTAssertEqual(image, rootVC?.selectionImage) 58 | } 59 | 60 | func testInit() { 61 | let imagePicker = OpalImagePickerController() 62 | XCTAssert(imagePicker.viewControllers[0] is OpalImagePickerRootViewController) 63 | } 64 | 65 | func testInitNib() { 66 | let imagePicker = OpalImagePickerController(nibName: "", bundle: nil) 67 | XCTAssertNotNil(imagePicker) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /OpalImagePickerTests/OpalImagePickerRootViewControllerTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpalImagePickerRootViewControllerTest.swift 3 | // OpalImagePicker 4 | // 5 | // Created by Kristos Katsanevas on 1/16/17. 6 | // Copyright © 2017 Opal Orange LLC. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Photos 11 | @testable import OpalImagePicker 12 | 13 | class OpalImagePickerRootViewControllerTest: XCTestCase { 14 | 15 | override func setUp() { 16 | super.setUp() 17 | // Put setup code here. This method is called before the invocation of each test method in the class. 18 | } 19 | 20 | override func tearDown() { 21 | // Put teardown code here. This method is called after the invocation of each test method in the class. 22 | super.tearDown() 23 | } 24 | 25 | func testSetCustomUI() { 26 | let image = UIImage() 27 | let imagePicker = OpalImagePickerRootViewController() 28 | imagePicker.selectionTintColor = UIColor.brown 29 | imagePicker.selectionImageTintColor = UIColor.yellow 30 | imagePicker.selectionImage = image 31 | 32 | XCTAssertEqual(UIColor.brown, imagePicker.selectionTintColor) 33 | XCTAssertEqual(UIColor.yellow, imagePicker.selectionImageTintColor) 34 | XCTAssert(imagePicker.selectionImage === image) 35 | } 36 | 37 | func testGetCollectionViewLayout() { 38 | let imagePicker = OpalImagePickerRootViewController() 39 | imagePicker.loadViewIfNeeded() 40 | XCTAssertNotNil(imagePicker.collectionViewLayout) 41 | } 42 | 43 | func testGetFetchOptions() { 44 | let imagePicker = OpalImagePickerRootViewController() 45 | imagePicker.loadViewIfNeeded() 46 | let fetchOptions = imagePicker.fetchOptions 47 | let sortDescriptors = fetchOptions.sortDescriptors?.filter { $0.key == "creationDate" } 48 | XCTAssertEqual(sortDescriptors?.count, 1) 49 | XCTAssertEqual(sortDescriptors?.first?.ascending, false) 50 | } 51 | 52 | func testGetFetchLimit() { 53 | let imagePicker = OpalImagePickerRootViewController() 54 | imagePicker.loadViewIfNeeded() 55 | imagePicker.fetchOptions.fetchLimit = 5 56 | XCTAssertEqual(imagePicker.fetchLimit, 5) 57 | } 58 | 59 | func testSetFetchLimit() { 60 | let imagePicker = OpalImagePickerRootViewController() 61 | imagePicker.loadViewIfNeeded() 62 | imagePicker.fetchLimit = 5 63 | XCTAssertEqual(imagePicker.fetchOptions.fetchLimit, 5) 64 | } 65 | 66 | func testInit() { 67 | let imagePicker = OpalImagePickerRootViewController() 68 | XCTAssertNotNil(imagePicker) 69 | } 70 | 71 | func testSetup() { 72 | let imagePicker = OpalImagePickerRootViewController() 73 | imagePicker.loadViewIfNeeded() 74 | 75 | XCTAssertNotNil(imagePicker.collectionView) 76 | XCTAssertNotNil(imagePicker.collectionViewLayout) 77 | XCTAssertEqual(imagePicker.fetchLimit, imagePicker.pageSize) 78 | } 79 | 80 | func testDoneTapped() { 81 | let mockDelegate = MockOpalImagePickerControllerDelegate() 82 | let imagePicker = OpalImagePickerController() 83 | imagePicker.imagePickerDelegate = mockDelegate 84 | let rootVC = imagePicker.viewControllers[0] as? OpalImagePickerRootViewController 85 | rootVC?.doneTapped() 86 | XCTAssertFalse(mockDelegate.isDidFinishPickingAssetsCalled) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package( 4 | name: "OpalImagePicker" 5 | ) 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![OpalImagePicker](https://github.com/opalorange/OpalImagePicker/blob/master/Resources/OpalImagePickerPresentation.jpg) 2 | 3 | [![Version](https://img.shields.io/cocoapods/v/OpalImagePicker.svg?style=flat)](http://cocoadocs.org/docsets/OpalImagePicker) 4 | [![License](https://img.shields.io/cocoapods/l/OpalImagePicker.svg?style=flat)](http://cocoadocs.org/docsets/OpalImagePicker) 5 | 6 | 7 | 8 | ## Description 9 | 10 | **OpalImagePicker** is a multiple selection Image Picker for iOS written in Swift. Meant to be a drop in replacement for UIImagePickerController. Compatible with both Swift and Objective-C. 11 | 12 | ## Swift 4 and 5 Compatibility 13 | - **Swift 5.0**: >= 2.1.0 - Latest is 2.1.0 [Download here](https://github.com/opalorange/OpalImagePicker/releases/tag/2.1.0). 14 | - **Swift 4.2**: >= 2.0.0 - Latest is 2.0.0 [Download here](https://github.com/opalorange/OpalImagePicker/releases/tag/2.0.0). 15 | - **Swift 4.0**: >= 1.5.0 - Latest is 1.7.1 [Download here](https://github.com/opalorange/OpalImagePicker/releases/tag/1.7.1). 16 | CocoaPods be sure to fix the release (`pod 'OpalImagePicker', '~> 2.1.0'`) 17 | 18 | ## Usage 19 | 20 | **OpalImagePicker** is presented much like UIImagePickerController. As a normal View Controller. 21 | 22 | ```swift 23 | let imagePicker = OpalImagePickerController() 24 | presentOpalImagePickerController(imagePicker, animated: true, 25 | select: { (assets) in 26 | //Select Assets 27 | }, cancel: { 28 | //Cancel 29 | }) 30 | ``` 31 | 32 | Or 33 | 34 | ```swift 35 | let imagePicker = OpalImagePickerController() 36 | imagePicker.imagePickerDelegate = self 37 | present(imagePicker, animated: true, completion: nil) 38 | ``` 39 | **OpalImagePicker** has three delegate methods to notify you when images have been selected, or the picker has been cancelled. This is only necessary if you choose not to use the **presentOpalImagePickerController(_:animated:select:cancel:completion:)** function. 40 | 41 | ```swift 42 | optional func imagePicker(_ picker: OpalImagePickerController, didFinishPickingAssets assets: [PHAsset]) 43 | optional func imagePickerDidCancel(_ picker: OpalImagePickerController) 44 | ``` 45 | 46 | **OpalImagePicker** also allows you to use external images. You may want to use images from Facebook, Instagram, or Twitter for example. You can do this either using the delegate methods below or the following function in Swift **presentOpalImagePickerController(_: animated: maximumSelectionsAllowed: numberOfExternalItems: externalItemsTitle: externalURLForIndex: selectAssets: selectExternalURLs: cancel: completion:)** function. 47 | 48 | ```swift 49 | optional func imagePickerNumberOfExternalItems(_ picker: OpalImagePickerController) -> Int 50 | optional func imagePicker(_ picker: OpalImagePickerController, imageURLforExternalItemAtIndex index: Int) -> URL? 51 | optional func imagePickerTitleForExternalItems(_ picker: OpalImagePickerController) -> String 52 | optional func imagePicker(_ picker: OpalImagePickerController, didFinishPickingExternalURLs urls: [URL]) 53 | ``` 54 | 55 | **OpalImagePicker** supports allowing users to customize user interface features 56 | 57 | ```swift 58 | let imagePicker = OpalImagePickerController() 59 | 60 | //Change color of selection overlay to white 61 | imagePicker.selectionTintColor = UIColor.white.withAlphaComponent(0.7) 62 | 63 | //Change color of image tint to black 64 | imagePicker.selectionImageTintColor = UIColor.black 65 | 66 | //Change image to X rather than checkmark 67 | imagePicker.selectionImage = UIImage(named: "x_image") 68 | 69 | //Change status bar style 70 | imagePicker.statusBarPreference = UIStatusBarStyle.lightContent 71 | 72 | //Limit maximum allowed selections to 5 73 | imagePicker.maximumSelectionsAllowed = 5 74 | 75 | //Only allow image media type assets 76 | imagePicker.allowedMediaTypes = Set([PHAssetMediaType.image]) 77 | 78 | //Change default localized strings displayed to the user 79 | let configuration = OpalImagePickerConfiguration() 80 | configuration.maximumSelectionsAllowedMessage = NSLocalizedString("You cannot select that many images!", comment: "") 81 | imagePicker.configuration = configuration 82 | ``` 83 | 84 | ## Installation 85 | 86 | **OpalImagePicker** is available through [CocoaPods](http://cocoapods.org). To install 87 | it, simply add the following line to your Podfile: 88 | 89 | ```ruby 90 | pod 'OpalImagePicker' 91 | ``` 92 | 93 | ## Requirements 94 | 95 | - iOS 8.0+ 96 | - Xcode 9.0+ 97 | - Swift 4 to 5.0 98 | 99 | ## Author 100 | 101 | [OpalOrange](http://opalorange.com) made this with ❤ 102 | 103 | ## Contribute 104 | 105 | We would love you to contribute to **OpalImagePicker** 106 | 107 | ## License 108 | 109 | **OpalImagePicker** is available under the MIT license. See the [LICENSE](https://github.com/opalorange/OpalImagePicker/blob/master/LICENSE.md) file for more info. -------------------------------------------------------------------------------- /Resources/OpalImagePickerPresentation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opalorange/OpalImagePicker/c19658b1571b87e8426241e63d17791a4d92be32/Resources/OpalImagePickerPresentation.jpg --------------------------------------------------------------------------------