├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── swift.yml ├── .gitignore ├── .swift-version ├── CHANGELOG.md ├── Cartfile ├── Cartfile.private ├── Cartfile.resolved ├── Example ├── CustomPickerController.swift ├── CustomRow.xib ├── Example.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── Example │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── ViewController.swift ├── ImageRow.gif └── Playground.playground │ ├── Contents.swift │ └── contents.xcplayground ├── ImageRow.podspec ├── ImageRow.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── ImageRow.xcscheme ├── ImageRow.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── ImageCell.swift ├── ImagePickerController.swift ├── ImageRow.h ├── ImageRow.swift └── Info.plist └── Tests ├── ImageRowTests.swift └── Info.plist /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing Guidelines 2 | -------------------------------------------------- 3 | 4 | This document provides general guidelines about how to contribute to the project. Keep in mind these important things before you start contributing. 5 | 6 | ### Asking Questions 7 | 8 | We do not use github issues for general library support. We think this questions should be posted on stack overflow using [ImageRow](http://http://stackoverflow.com/questions/tagged/ImageRow) tag. 9 | 10 | ### Reporting issues 11 | 12 | * Use [github issues](https://github.com/EurekaCommunity/ImageRow/issues) to report a bug. 13 | * Before creating a new issue: 14 | * Make sure you are using the [latest release](https://github.com/EurekaCommunity/ImageRow/releases). 15 | * Check if the issue was [already reported or fixed](https://github.com/EurekaCommunity/ImageRow/issues?utf8=%E2%9C%93&q=is%3Aissue). Notice that it may not be released yet. 16 | * If you found a match add a brief comment "I have the same problem" or "+1". This helps prioritize the issues addressing the most common and critical first. If possible add additional information to help us reproduce and fix the issue. Please use your best judgement. 17 | * Reporting issues: 18 | * Please include the following information to help maintainers to fix the problem faster: 19 | * Xcode version you are using. 20 | * iOS version you are targeting. 21 | * Full Xcode console output of stack trace or code compilation error. 22 | * Any other additional detail you think it would be useful to understand and solve the problem. 23 | 24 | 25 | ### Pull requests 26 | 27 | The easiest way to start contributing is searching open issues by `help wanted` tag. We also add a `difficulty` tag (difficulty: easy, difficulty: moderate, difficulty: hard) in order to give an idea of how complex it can be to implement the feature according maintainers project experience. 28 | 29 | * Add test coverage to the feature or fix. We only accept new feature pull requests that have related test coverage. This allows us to keep the library stable as we move forward. 30 | * Remember to document the new feature. We do not accept new feature pull requests without its associated documentation. 31 | * In case of a new feature please update the example project showing the feature. 32 | * Please only one fix or feature per pull request. This will increase the chances your feature will be merged. 33 | 34 | 35 | ###### Suggested git workflow to contribute 36 | 37 | 1. Fork the ImageRow repository. 38 | 2. Clone your forked project into your developer machine: `git clone git@github.com:/ImageRow.git` 39 | 3. Add the original project repo as upstream repository in your forked project: `git remote add upstream git@github.com:EurekaCommunity/ImageRow.git` 40 | 4. Before starting a new feature make sure your forked master branch is synchronized upstream master branch. Considering you do not mere your pull request into master you can run: `git checkout master` and then `git pull upstream master`. Optionally `git push origin master`. 41 | 5. Create a new branch. Note that the starting point is the upstream master branch HEAD. `git checkout -b my-feature-name` 42 | 6. Stage all your changes `git add .` and commit them `git commit -m "Your commit message"` 43 | 7. Make sure your branch is up to date with upstream master, `git pull --rebase upstream master`, resolve conflicts if necessary. This will move your commit to the top of git stack. 44 | 8. Squash your commits into one commit. `git rebase -i HEAD~6` considering you did 6 commits. 45 | 9. Push your branch into your forked remote repository. 46 | 10. Create a new pull request adding any useful comment. 47 | 48 | 49 | ###### Code style and conventions 50 | 51 | We try to follow our [swift style guide](https://github.com/EurekaCommunity/Swift-Style-Guide). Following it is not strictly necessary to contribute and to have a pull request accepted but project maintainers try to follow it. We would love to hear your ideas to improve our code style and conventions. Feel free to contribute. 52 | 53 | 54 | ### Feature proposal 55 | 56 | We would love to hear your ideas and make a discussions about it. 57 | 58 | * Use github issues to make feature proposals. 59 | * We use `type: feature request` label to mark all [feature request issues](https://github.com/EurekaCommunity/ImageRow/labels/type%3A%20feature%20request). 60 | * Before submitting your proposal make sure there is no similar feature request. If you found a match feel free to join the discussion or just add a brief "+1" if you think the feature is worth implementing. 61 | * Be as specific as possible providing a precise explanation of feature request so anyone can understand the problem and the benefits of solving it. 62 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Before submitting issues ... 2 | 3 | - Make sure you are using ImageRow's [latest release](https://github.com/xmartlabs/ImageRow/releases) or master branch version. 4 | - Make sure your Xcode version is the latest stable one. 5 | - Check if the issue was [already reported or fixed](https://github.com/xmartlabs/ImageRow/issues?utf8=%E2%9C%93&q=is%3Aissue). We add labels to each issue in order to easily find related issues. If you found a match add a brief comment "I have the same problem" or "+1". 6 | 7 | When submitting issues, please provide the following information to help maintainers to fix the problem faster: 8 | 9 | - Environment: ImageRow, Xcode and iOS version you are using. 10 | - In case of reporting errors, provide Xcode console output of stack trace or code compilation error. 11 | - Any other additional detail such as your eureka form/section/row code that you think it would be useful to understand and solve the problem. 12 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes #issue(s) . 2 | 3 | Changes proposed in this request: 4 | * 5 | * 6 | * 7 | 8 | 9 | -------------------------------------------------------------------------------- /.github/workflows/swift.yml: -------------------------------------------------------------------------------- 1 | name: ImageRow Swift Package 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: macOS-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Generate xcodeproj 13 | run: swift package generate-xcodeproj 14 | - name: Run tests 15 | run: xcodebuild build test -destination 'name=iPhone 11' -scheme 'ImageRow-Package' 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## OS X Finder 2 | .DS_Store 3 | 4 | ## Build generated 5 | build/ 6 | DerivedData 7 | 8 | ## Various settings 9 | *.pbxuser 10 | !default.pbxuser 11 | *.mode1v3 12 | !default.mode1v3 13 | *.mode2v3 14 | !default.mode2v3 15 | *.perspectivev3 16 | !default.perspectivev3 17 | xcuserdata 18 | 19 | ## Other 20 | *.xccheckout 21 | *.moved-aside 22 | *.xcuserstate 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | 29 | ## Playgrounds 30 | timeline.xctimeline 31 | playground.xcworkspace 32 | 33 | # Swift Package Manager 34 | # 35 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 36 | # Packages/ 37 | .build/ 38 | 39 | # CocoaPods 40 | # 41 | # We recommend against adding the Pods directory to your .gitignore. However 42 | # you should judge for yourself, the pros and cons are mentioned at: 43 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 44 | # 45 | Pods/ 46 | 47 | # Carthage 48 | # 49 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 50 | Carthage/ 51 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5.0 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to ImageRow will be documented in this file. 3 | 4 | ### [4.1.0](https://github.com/EurekaCommunity/ImageRow/releases/tag/4.1.0) 5 | 6 | 7 | * Allows setting a thumbnail image (#69) 8 | * Added possibility to change the default action sheet tint color 9 | * Add support for Swift Package Manager + Add Github Actions support to… 10 | * Update to Eureka version 5.3.5 (#87) 11 | * Several fixes (#84, #73, #70) 12 | 13 | ### [4.0.0](https://github.com/EurekaCommunity/ImageRow/releases/tag/4.0.0) 14 | 15 | 16 | * Xcode 10.2 and Swift 5 compatibility 17 | * Eureka 5 support (#62) 18 | * Default the ImagePickerController's status bar to black (.default) (#57) 19 | * Fixed issues which inhibited subclassing. (#58) 20 | 21 | ### [3.2.0](https://github.com/EurekaCommunity/ImageRow/releases/tag/3.2.0) 22 | 23 | 24 | * Xcode 10 and Swift 4.2 compatibility 25 | 26 | ### [3.1.0](https://github.com/EurekaCommunity/ImageRow/releases/tag/3.1.0) 27 | 28 | 29 | * Add Option to crop image and use edited image (#36) 30 | 31 | ### [3.0.0](https://github.com/EurekaCommunity/ImageRow/releases/tag/3.0.0) 32 | 33 | 34 | * Eureka 4.0.0 support. 35 | 36 | ### [1.0.1](https://github.com/EurekaCommunity/ImageRow/releases/tag/1.0.1) 37 | 38 | 39 | * Localized Actionssheet strings (#9) 40 | * Set iOS Deployment Target to 8.0 (#2) 41 | 42 | ### [1.0.0](https://github.com/EurekaCommunity/ImageRow/releases/tag/1.0.0) 43 | 44 | 45 | * This is the initial version. 46 | 47 | [xmartlabs]: https://xmartlabs.com 48 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "xmartlabs/Eureka" ~> 5.3.5 2 | -------------------------------------------------------------------------------- /Cartfile.private: -------------------------------------------------------------------------------- 1 | github "Quick/Quick" 2 | github "Quick/Nimble" 3 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Quick/Nimble" "v9.2.1" 2 | github "Quick/Quick" "v4.0.0" 3 | github "xmartlabs/Eureka" "5.3.5" 4 | -------------------------------------------------------------------------------- /Example/CustomPickerController.swift: -------------------------------------------------------------------------------- 1 | // CustomPickerController.swift 2 | // ImageRow ( https://github.com/EurekaCommunity/ImageRow ) 3 | // 4 | // Copyright (c) 2016 Xmartlabs SRL ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Eureka 26 | import UIKit 27 | 28 | open class CustomPickerController: UIImagePickerController, TypedRowControllerType, UIImagePickerControllerDelegate, UINavigationControllerDelegate { 29 | 30 | /// The row that pushed or presented this controller 31 | public var row: RowOf! 32 | 33 | /// A closure to be called when the controller disappears 34 | public var onDismissCallback : ((UIViewController) -> ())? 35 | 36 | open override func viewDidLoad() { 37 | super.viewDidLoad() 38 | delegate = self 39 | 40 | // Allow edition 41 | allowsEditing = true 42 | } 43 | open func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { 44 | (row as? MyImageRow)?.imageURL = info[UIImagePickerController.InfoKey.referenceURL] as? URL 45 | row.value = info[UIImagePickerController.InfoKey.originalImage] as? UIImage 46 | onDismissCallback?(self) 47 | } 48 | 49 | open func imagePickerControllerDidCancel(_ picker: UIImagePickerController){ 50 | onDismissCallback?(self) 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /Example/CustomRow.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 28BDF6141DA43255000518EB /* Eureka.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28BDF6101DA4323E000518EB /* Eureka.framework */; }; 11 | 28BDF6151DA43255000518EB /* Eureka.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 28BDF6101DA4323E000518EB /* Eureka.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 12 | 28F828D01C4B714D00330CF4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28F828CF1C4B714D00330CF4 /* AppDelegate.swift */; }; 13 | 28F828D21C4B714D00330CF4 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28F828D11C4B714D00330CF4 /* ViewController.swift */; }; 14 | 28F828D51C4B714D00330CF4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 28F828D31C4B714D00330CF4 /* Main.storyboard */; }; 15 | 28F828D71C4B714D00330CF4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 28F828D61C4B714D00330CF4 /* Assets.xcassets */; }; 16 | 28F828DA1C4B714D00330CF4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 28F828D81C4B714D00330CF4 /* LaunchScreen.storyboard */; }; 17 | 8FE3CC2D2165096C00A85D97 /* ImageRow.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8F9CAB221D89C756006E1824 /* ImageRow.framework */; }; 18 | 8FE3CC2E2165096C00A85D97 /* ImageRow.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8F9CAB221D89C756006E1824 /* ImageRow.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 19 | 9A61F6D11EB9211100CBE062 /* CustomRow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9A61F6D01EB9211100CBE062 /* CustomRow.xib */; }; 20 | 9A61F6D51EB9227500CBE062 /* CustomPickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A61F6D41EB9227500CBE062 /* CustomPickerController.swift */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | 8F9CAB211D89C756006E1824 /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = 287D0A741C4B7B26004566D6 /* ImageRow.xcodeproj */; 27 | proxyType = 2; 28 | remoteGlobalIDString = 28F8287D1C494B2C00330CF4; 29 | remoteInfo = ImageRow; 30 | }; 31 | 8F9CAB231D89C756006E1824 /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = 287D0A741C4B7B26004566D6 /* ImageRow.xcodeproj */; 34 | proxyType = 2; 35 | remoteGlobalIDString = 28F828871C494B2C00330CF4; 36 | remoteInfo = ImageRowTests; 37 | }; 38 | 8FE3CC2F2165096C00A85D97 /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = 287D0A741C4B7B26004566D6 /* ImageRow.xcodeproj */; 41 | proxyType = 1; 42 | remoteGlobalIDString = 28F8287C1C494B2C00330CF4; 43 | remoteInfo = ImageRow; 44 | }; 45 | /* End PBXContainerItemProxy section */ 46 | 47 | /* Begin PBXCopyFilesBuildPhase section */ 48 | 28BDF6161DA43255000518EB /* Embed Frameworks */ = { 49 | isa = PBXCopyFilesBuildPhase; 50 | buildActionMask = 2147483647; 51 | dstPath = ""; 52 | dstSubfolderSpec = 10; 53 | files = ( 54 | 28BDF6151DA43255000518EB /* Eureka.framework in Embed Frameworks */, 55 | 8FE3CC2E2165096C00A85D97 /* ImageRow.framework in Embed Frameworks */, 56 | ); 57 | name = "Embed Frameworks"; 58 | runOnlyForDeploymentPostprocessing = 0; 59 | }; 60 | /* End PBXCopyFilesBuildPhase section */ 61 | 62 | /* Begin PBXFileReference section */ 63 | 287D0A741C4B7B26004566D6 /* ImageRow.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ImageRow.xcodeproj; path = ../ImageRow.xcodeproj; sourceTree = ""; }; 64 | 28BDF6101DA4323E000518EB /* Eureka.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Eureka.framework; path = ../Carthage/Build/iOS/Eureka.framework; sourceTree = ""; }; 65 | 28F828CC1C4B714D00330CF4 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 28F828CF1C4B714D00330CF4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Example/AppDelegate.swift; sourceTree = ""; }; 67 | 28F828D11C4B714D00330CF4 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ViewController.swift; path = Example/ViewController.swift; sourceTree = ""; }; 68 | 28F828D41C4B714D00330CF4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 69 | 28F828D61C4B714D00330CF4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Example/Assets.xcassets; sourceTree = ""; }; 70 | 28F828D91C4B714D00330CF4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 71 | 28F828DB1C4B714D00330CF4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Example/Info.plist; sourceTree = ""; }; 72 | 9A61F6D01EB9211100CBE062 /* CustomRow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CustomRow.xib; sourceTree = ""; }; 73 | 9A61F6D41EB9227500CBE062 /* CustomPickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomPickerController.swift; sourceTree = ""; }; 74 | /* End PBXFileReference section */ 75 | 76 | /* Begin PBXFrameworksBuildPhase section */ 77 | 28F828C91C4B714D00330CF4 /* Frameworks */ = { 78 | isa = PBXFrameworksBuildPhase; 79 | buildActionMask = 2147483647; 80 | files = ( 81 | 28BDF6141DA43255000518EB /* Eureka.framework in Frameworks */, 82 | 8FE3CC2D2165096C00A85D97 /* ImageRow.framework in Frameworks */, 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | /* End PBXFrameworksBuildPhase section */ 87 | 88 | /* Begin PBXGroup section */ 89 | 28BDF6071DA42A90000518EB /* Dependencies */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 28BDF6101DA4323E000518EB /* Eureka.framework */, 93 | ); 94 | name = Dependencies; 95 | sourceTree = ""; 96 | }; 97 | 28F828C31C4B714D00330CF4 = { 98 | isa = PBXGroup; 99 | children = ( 100 | 28F828CE1C4B714D00330CF4 /* Example */, 101 | 28F828CD1C4B714D00330CF4 /* Products */, 102 | 28BDF6071DA42A90000518EB /* Dependencies */, 103 | 287D0A741C4B7B26004566D6 /* ImageRow.xcodeproj */, 104 | ); 105 | sourceTree = ""; 106 | }; 107 | 28F828CD1C4B714D00330CF4 /* Products */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 28F828CC1C4B714D00330CF4 /* Example.app */, 111 | ); 112 | name = Products; 113 | sourceTree = ""; 114 | }; 115 | 28F828CE1C4B714D00330CF4 /* Example */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 28F828CF1C4B714D00330CF4 /* AppDelegate.swift */, 119 | 28F828D11C4B714D00330CF4 /* ViewController.swift */, 120 | 28F828D31C4B714D00330CF4 /* Main.storyboard */, 121 | 28F828D61C4B714D00330CF4 /* Assets.xcassets */, 122 | 28F828D81C4B714D00330CF4 /* LaunchScreen.storyboard */, 123 | 28F828DB1C4B714D00330CF4 /* Info.plist */, 124 | 9A61F6D01EB9211100CBE062 /* CustomRow.xib */, 125 | 9A61F6D41EB9227500CBE062 /* CustomPickerController.swift */, 126 | ); 127 | name = Example; 128 | sourceTree = ""; 129 | }; 130 | 8F9CAB1D1D89C755006E1824 /* Products */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 8F9CAB221D89C756006E1824 /* ImageRow.framework */, 134 | 8F9CAB241D89C756006E1824 /* ImageRowTests.xctest */, 135 | ); 136 | name = Products; 137 | sourceTree = ""; 138 | }; 139 | /* End PBXGroup section */ 140 | 141 | /* Begin PBXNativeTarget section */ 142 | 28F828CB1C4B714D00330CF4 /* Example */ = { 143 | isa = PBXNativeTarget; 144 | buildConfigurationList = 28F828E91C4B714D00330CF4 /* Build configuration list for PBXNativeTarget "Example" */; 145 | buildPhases = ( 146 | 28F828C81C4B714D00330CF4 /* Sources */, 147 | 28F828C91C4B714D00330CF4 /* Frameworks */, 148 | 28F828CA1C4B714D00330CF4 /* Resources */, 149 | 28BDF6001DA429C2000518EB /* ShellScript */, 150 | 28BDF6161DA43255000518EB /* Embed Frameworks */, 151 | ); 152 | buildRules = ( 153 | ); 154 | dependencies = ( 155 | 8FE3CC302165096C00A85D97 /* PBXTargetDependency */, 156 | ); 157 | name = Example; 158 | productName = Example; 159 | productReference = 28F828CC1C4B714D00330CF4 /* Example.app */; 160 | productType = "com.apple.product-type.application"; 161 | }; 162 | /* End PBXNativeTarget section */ 163 | 164 | /* Begin PBXProject section */ 165 | 28F828C41C4B714D00330CF4 /* Project object */ = { 166 | isa = PBXProject; 167 | attributes = { 168 | LastSwiftUpdateCheck = 0720; 169 | LastUpgradeCheck = 0940; 170 | TargetAttributes = { 171 | 28F828CB1C4B714D00330CF4 = { 172 | CreatedOnToolsVersion = 7.2; 173 | LastSwiftMigration = 0800; 174 | }; 175 | }; 176 | }; 177 | buildConfigurationList = 28F828C71C4B714D00330CF4 /* Build configuration list for PBXProject "Example" */; 178 | compatibilityVersion = "Xcode 3.2"; 179 | developmentRegion = en; 180 | hasScannedForEncodings = 0; 181 | knownRegions = ( 182 | en, 183 | Base, 184 | ); 185 | mainGroup = 28F828C31C4B714D00330CF4; 186 | productRefGroup = 28F828CD1C4B714D00330CF4 /* Products */; 187 | projectDirPath = ""; 188 | projectReferences = ( 189 | { 190 | ProductGroup = 8F9CAB1D1D89C755006E1824 /* Products */; 191 | ProjectRef = 287D0A741C4B7B26004566D6 /* ImageRow.xcodeproj */; 192 | }, 193 | ); 194 | projectRoot = ""; 195 | targets = ( 196 | 28F828CB1C4B714D00330CF4 /* Example */, 197 | ); 198 | }; 199 | /* End PBXProject section */ 200 | 201 | /* Begin PBXReferenceProxy section */ 202 | 8F9CAB221D89C756006E1824 /* ImageRow.framework */ = { 203 | isa = PBXReferenceProxy; 204 | fileType = wrapper.framework; 205 | path = ImageRow.framework; 206 | remoteRef = 8F9CAB211D89C756006E1824 /* PBXContainerItemProxy */; 207 | sourceTree = BUILT_PRODUCTS_DIR; 208 | }; 209 | 8F9CAB241D89C756006E1824 /* ImageRowTests.xctest */ = { 210 | isa = PBXReferenceProxy; 211 | fileType = wrapper.cfbundle; 212 | path = ImageRowTests.xctest; 213 | remoteRef = 8F9CAB231D89C756006E1824 /* PBXContainerItemProxy */; 214 | sourceTree = BUILT_PRODUCTS_DIR; 215 | }; 216 | /* End PBXReferenceProxy section */ 217 | 218 | /* Begin PBXResourcesBuildPhase section */ 219 | 28F828CA1C4B714D00330CF4 /* Resources */ = { 220 | isa = PBXResourcesBuildPhase; 221 | buildActionMask = 2147483647; 222 | files = ( 223 | 28F828DA1C4B714D00330CF4 /* LaunchScreen.storyboard in Resources */, 224 | 9A61F6D11EB9211100CBE062 /* CustomRow.xib in Resources */, 225 | 28F828D71C4B714D00330CF4 /* Assets.xcassets in Resources */, 226 | 28F828D51C4B714D00330CF4 /* Main.storyboard in Resources */, 227 | ); 228 | runOnlyForDeploymentPostprocessing = 0; 229 | }; 230 | /* End PBXResourcesBuildPhase section */ 231 | 232 | /* Begin PBXShellScriptBuildPhase section */ 233 | 28BDF6001DA429C2000518EB /* ShellScript */ = { 234 | isa = PBXShellScriptBuildPhase; 235 | buildActionMask = 2147483647; 236 | files = ( 237 | ); 238 | inputPaths = ( 239 | "$(SRCROOT)/../Carthage/Build/iOS/Eureka.framework", 240 | ); 241 | outputPaths = ( 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | shellPath = /bin/sh; 245 | shellScript = "/usr/local/bin/carthage copy-frameworks"; 246 | }; 247 | /* End PBXShellScriptBuildPhase section */ 248 | 249 | /* Begin PBXSourcesBuildPhase section */ 250 | 28F828C81C4B714D00330CF4 /* Sources */ = { 251 | isa = PBXSourcesBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | 9A61F6D51EB9227500CBE062 /* CustomPickerController.swift in Sources */, 255 | 28F828D21C4B714D00330CF4 /* ViewController.swift in Sources */, 256 | 28F828D01C4B714D00330CF4 /* AppDelegate.swift in Sources */, 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | /* End PBXSourcesBuildPhase section */ 261 | 262 | /* Begin PBXTargetDependency section */ 263 | 8FE3CC302165096C00A85D97 /* PBXTargetDependency */ = { 264 | isa = PBXTargetDependency; 265 | name = ImageRow; 266 | targetProxy = 8FE3CC2F2165096C00A85D97 /* PBXContainerItemProxy */; 267 | }; 268 | /* End PBXTargetDependency section */ 269 | 270 | /* Begin PBXVariantGroup section */ 271 | 28F828D31C4B714D00330CF4 /* Main.storyboard */ = { 272 | isa = PBXVariantGroup; 273 | children = ( 274 | 28F828D41C4B714D00330CF4 /* Base */, 275 | ); 276 | name = Main.storyboard; 277 | path = Example; 278 | sourceTree = ""; 279 | }; 280 | 28F828D81C4B714D00330CF4 /* LaunchScreen.storyboard */ = { 281 | isa = PBXVariantGroup; 282 | children = ( 283 | 28F828D91C4B714D00330CF4 /* Base */, 284 | ); 285 | name = LaunchScreen.storyboard; 286 | path = Example; 287 | sourceTree = ""; 288 | }; 289 | /* End PBXVariantGroup section */ 290 | 291 | /* Begin XCBuildConfiguration section */ 292 | 28F828E71C4B714D00330CF4 /* Debug */ = { 293 | isa = XCBuildConfiguration; 294 | buildSettings = { 295 | ALWAYS_SEARCH_USER_PATHS = NO; 296 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 297 | CLANG_CXX_LIBRARY = "libc++"; 298 | CLANG_ENABLE_MODULES = YES; 299 | CLANG_ENABLE_OBJC_ARC = YES; 300 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 301 | CLANG_WARN_BOOL_CONVERSION = YES; 302 | CLANG_WARN_COMMA = YES; 303 | CLANG_WARN_CONSTANT_CONVERSION = YES; 304 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 305 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 306 | CLANG_WARN_EMPTY_BODY = YES; 307 | CLANG_WARN_ENUM_CONVERSION = YES; 308 | CLANG_WARN_INFINITE_RECURSION = YES; 309 | CLANG_WARN_INT_CONVERSION = YES; 310 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 311 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 312 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 313 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 314 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 315 | CLANG_WARN_STRICT_PROTOTYPES = YES; 316 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 317 | CLANG_WARN_UNREACHABLE_CODE = YES; 318 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 319 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 320 | COPY_PHASE_STRIP = NO; 321 | DEBUG_INFORMATION_FORMAT = dwarf; 322 | ENABLE_STRICT_OBJC_MSGSEND = YES; 323 | ENABLE_TESTABILITY = YES; 324 | GCC_C_LANGUAGE_STANDARD = gnu99; 325 | GCC_DYNAMIC_NO_PIC = NO; 326 | GCC_NO_COMMON_BLOCKS = YES; 327 | GCC_OPTIMIZATION_LEVEL = 0; 328 | GCC_PREPROCESSOR_DEFINITIONS = ( 329 | "DEBUG=1", 330 | "$(inherited)", 331 | ); 332 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 333 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 334 | GCC_WARN_UNDECLARED_SELECTOR = YES; 335 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 336 | GCC_WARN_UNUSED_FUNCTION = YES; 337 | GCC_WARN_UNUSED_VARIABLE = YES; 338 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 339 | MTL_ENABLE_DEBUG_INFO = YES; 340 | ONLY_ACTIVE_ARCH = YES; 341 | SDKROOT = iphoneos; 342 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 343 | }; 344 | name = Debug; 345 | }; 346 | 28F828E81C4B714D00330CF4 /* Release */ = { 347 | isa = XCBuildConfiguration; 348 | buildSettings = { 349 | ALWAYS_SEARCH_USER_PATHS = NO; 350 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 351 | CLANG_CXX_LIBRARY = "libc++"; 352 | CLANG_ENABLE_MODULES = YES; 353 | CLANG_ENABLE_OBJC_ARC = YES; 354 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 355 | CLANG_WARN_BOOL_CONVERSION = YES; 356 | CLANG_WARN_COMMA = YES; 357 | CLANG_WARN_CONSTANT_CONVERSION = YES; 358 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 359 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 360 | CLANG_WARN_EMPTY_BODY = YES; 361 | CLANG_WARN_ENUM_CONVERSION = YES; 362 | CLANG_WARN_INFINITE_RECURSION = YES; 363 | CLANG_WARN_INT_CONVERSION = YES; 364 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 365 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 366 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 367 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 368 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 369 | CLANG_WARN_STRICT_PROTOTYPES = YES; 370 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 371 | CLANG_WARN_UNREACHABLE_CODE = YES; 372 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 373 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 374 | COPY_PHASE_STRIP = NO; 375 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 376 | ENABLE_NS_ASSERTIONS = NO; 377 | ENABLE_STRICT_OBJC_MSGSEND = YES; 378 | GCC_C_LANGUAGE_STANDARD = gnu99; 379 | GCC_NO_COMMON_BLOCKS = YES; 380 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 381 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 382 | GCC_WARN_UNDECLARED_SELECTOR = YES; 383 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 384 | GCC_WARN_UNUSED_FUNCTION = YES; 385 | GCC_WARN_UNUSED_VARIABLE = YES; 386 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 387 | MTL_ENABLE_DEBUG_INFO = NO; 388 | SDKROOT = iphoneos; 389 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 390 | VALIDATE_PRODUCT = YES; 391 | }; 392 | name = Release; 393 | }; 394 | 28F828EA1C4B714D00330CF4 /* Debug */ = { 395 | isa = XCBuildConfiguration; 396 | buildSettings = { 397 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 398 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 399 | DEVELOPMENT_TEAM = ""; 400 | FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../Carthage/Build/iOS"; 401 | INFOPLIST_FILE = "$(SRCROOT)/Example/Info.plist"; 402 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks $(SRCROOT)/../Carthage/Build/iOS"; 403 | PRODUCT_BUNDLE_IDENTIFIER = com.xmartlabs.Example; 404 | PRODUCT_NAME = "$(TARGET_NAME)"; 405 | SWIFT_VERSION = 4.2; 406 | }; 407 | name = Debug; 408 | }; 409 | 28F828EB1C4B714D00330CF4 /* Release */ = { 410 | isa = XCBuildConfiguration; 411 | buildSettings = { 412 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 413 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 414 | DEVELOPMENT_TEAM = ""; 415 | FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../Carthage/Build/iOS"; 416 | INFOPLIST_FILE = "$(SRCROOT)/Example/Info.plist"; 417 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks $(SRCROOT)/../Carthage/Build/iOS"; 418 | PRODUCT_BUNDLE_IDENTIFIER = com.xmartlabs.Example; 419 | PRODUCT_NAME = "$(TARGET_NAME)"; 420 | SWIFT_VERSION = 4.2; 421 | }; 422 | name = Release; 423 | }; 424 | /* End XCBuildConfiguration section */ 425 | 426 | /* Begin XCConfigurationList section */ 427 | 28F828C71C4B714D00330CF4 /* Build configuration list for PBXProject "Example" */ = { 428 | isa = XCConfigurationList; 429 | buildConfigurations = ( 430 | 28F828E71C4B714D00330CF4 /* Debug */, 431 | 28F828E81C4B714D00330CF4 /* Release */, 432 | ); 433 | defaultConfigurationIsVisible = 0; 434 | defaultConfigurationName = Release; 435 | }; 436 | 28F828E91C4B714D00330CF4 /* Build configuration list for PBXNativeTarget "Example" */ = { 437 | isa = XCConfigurationList; 438 | buildConfigurations = ( 439 | 28F828EA1C4B714D00330CF4 /* Debug */, 440 | 28F828EB1C4B714D00330CF4 /* Release */, 441 | ); 442 | defaultConfigurationIsVisible = 0; 443 | defaultConfigurationName = Release; 444 | }; 445 | /* End XCConfigurationList section */ 446 | }; 447 | rootObject = 28F828C41C4B714D00330CF4 /* Project object */; 448 | } 449 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example 4 | // 5 | // Copyright © 2016 Xmartlabs SRL. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | @UIApplicationMain 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | 16 | private func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | func applicationWillResignActive(_ application: UIApplication) { 22 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 23 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 24 | } 25 | 26 | func applicationDidEnterBackground(_ application: UIApplication) { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | func applicationWillEnterForeground(_ application: UIApplication) { 32 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 33 | } 34 | 35 | func applicationDidBecomeActive(_ application: UIApplication) { 36 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 37 | } 38 | 39 | func applicationWillTerminate(_ application: UIApplication) { 40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 41 | } 42 | 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /Example/Example/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" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /Example/Example/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 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Example/Example/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Example/Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSCameraUsageDescription 6 | Use camera to take photos 7 | NSPhotoLibraryUsageDescription 8 | Show Image row 9 | CFBundleDevelopmentRegion 10 | en 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleSignature 24 | ???? 25 | CFBundleVersion 26 | 1 27 | LSRequiresIPhoneOS 28 | 29 | UILaunchStoryboardName 30 | LaunchScreen 31 | UIMainStoryboardFile 32 | Main 33 | UIRequiredDeviceCapabilities 34 | 35 | armv7 36 | 37 | UISupportedInterfaceOrientations 38 | 39 | UIInterfaceOrientationPortrait 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Example/Example/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Example 4 | // 5 | // Copyright © 2016 Xmartlabs SRL. All rights reserved. 6 | // 7 | 8 | import ImageRow 9 | import Eureka 10 | 11 | class ViewController: FormViewController { 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | 15 | form +++ Section() 16 | <<< ImageRow() { row in 17 | row.title = "Image Row 1" 18 | row.sourceTypes = [.PhotoLibrary, .SavedPhotosAlbum] 19 | row.clearAction = .yes(style: UIAlertAction.Style.destructive) 20 | } 21 | +++ 22 | Section() 23 | <<< ImageRow() { 24 | $0.title = "Image Row 2" 25 | $0.sourceTypes = .PhotoLibrary 26 | $0.clearAction = .no 27 | } 28 | .cellUpdate { cell, row in 29 | cell.accessoryView?.layer.cornerRadius = 17 30 | cell.accessoryView?.frame.size = CGSize(width: 34, height: 34) 31 | } 32 | +++ 33 | Section() 34 | <<< MyImageRow() { 35 | $0.title = "Image Row 3" 36 | $0.sourceTypes = [.PhotoLibrary, .SavedPhotosAlbum] 37 | $0.clearAction = .yes(style: .default) 38 | } 39 | } 40 | } 41 | 42 | /// CustomPickerController: a selector row where the user can pick an image and edit it then of the selection 43 | public final class MyImageRow: _ImageRow>, RowType { 44 | public required init(tag: String?) { 45 | super.init(tag: tag) 46 | 47 | // Set a nib file to the cell privider 48 | cellProvider = CellProvider>(nibName: "CustomRow", bundle: Bundle.main) 49 | } 50 | } 51 | 52 | /// Definition of a custom cell 53 | public final class MyImageCell: PushSelectorCell { 54 | 55 | /// xib outlets 56 | @IBOutlet weak public var myImageView: UIImageView! 57 | @IBOutlet weak var myLabel: UILabel! 58 | 59 | /// Required inits 60 | public required init?(coder aDecoder: NSCoder) { 61 | super.init(coder: aDecoder) 62 | } 63 | 64 | public required init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 65 | fatalError("init(style:reuseIdentifier:) has not been implemented") 66 | } 67 | 68 | /// Override the setup to change the cell height 69 | public override func setup() { 70 | super.setup() 71 | 72 | height = { return 80 } 73 | } 74 | 75 | /// Cell update - The image selected in the row will appear in the imageView (outlet) 76 | public override func update() { 77 | super.update() 78 | 79 | if let image = row.value { 80 | myImageView.image = image 81 | } 82 | 83 | myLabel.text = row.title 84 | textLabel?.text = nil 85 | detailTextLabel?.text = nil 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /Example/ImageRow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EurekaCommunity/ImageRow/5682215c037bc5ff9b9b9ddb219f906f078e4c25/Example/ImageRow.gif -------------------------------------------------------------------------------- /Example/Playground.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Playground.playground 3 | // ImageRow 4 | // 5 | // Copyright © 2016 Xmartlabs SRL. All rights reserved. 6 | // 7 | 8 | //: Playground - noun: a place where people can play 9 | 10 | import UIKit 11 | import ImageRow 12 | 13 | var str = "Hello, playground" 14 | -------------------------------------------------------------------------------- /Example/Playground.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ImageRow.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "ImageRow" 3 | s.version = "4.1.0" 4 | s.summary = "Eureka row that allows us to take or select a picture." 5 | s.homepage = "https://github.com/EurekaCommunity/ImageRow" 6 | s.license = { type: 'MIT', file: 'LICENSE' } 7 | s.author = { "Xmartlabs SRL" => "swift@xmartlabs.com" } 8 | s.source = { git: "https://github.com/EurekaCommunity/ImageRow.git", tag: s.version.to_s } 9 | s.social_media_url = 'https://twitter.com/EurekaCommunity' 10 | s.ios.deployment_target = '9.3' 11 | s.requires_arc = true 12 | s.ios.source_files = 'Sources/**/*.{swift}' 13 | s.dependency 'Eureka', '~> 5.3.5' 14 | s.swift_version = "5.0" 15 | end 16 | -------------------------------------------------------------------------------- /ImageRow.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 28BDF5F51DA40763000518EB /* ImagePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28BDF5F41DA40763000518EB /* ImagePickerController.swift */; }; 11 | 28BDF5F91DA4078C000518EB /* ImageRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28BDF5F81DA4078C000518EB /* ImageRow.swift */; }; 12 | 28BDF5FB1DA4079B000518EB /* ImageRowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28BDF5FA1DA4079B000518EB /* ImageRowTests.swift */; }; 13 | 28BDF5FE1DA42427000518EB /* Eureka.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28BDF5FD1DA42427000518EB /* Eureka.framework */; }; 14 | 28BDF5FF1DA42427000518EB /* Eureka.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28BDF5FD1DA42427000518EB /* Eureka.framework */; }; 15 | 28F828811C494B2C00330CF4 /* ImageRow.h in Headers */ = {isa = PBXBuildFile; fileRef = 28F828801C494B2C00330CF4 /* ImageRow.h */; settings = {ATTRIBUTES = (Public, ); }; }; 16 | 28F828881C494B2C00330CF4 /* ImageRow.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28F8287D1C494B2C00330CF4 /* ImageRow.framework */; }; 17 | 9A61F6D71EBA133800CBE062 /* ImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A61F6D61EBA133800CBE062 /* ImageCell.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | 28F828891C494B2C00330CF4 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = 28F828741C494B2C00330CF4 /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = 28F8287C1C494B2C00330CF4; 26 | remoteInfo = ImageRow; 27 | }; 28 | /* End PBXContainerItemProxy section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 28BDF5F41DA40763000518EB /* ImagePickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImagePickerController.swift; sourceTree = ""; }; 32 | 28BDF5F61DA4076A000518EB /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 28BDF5F81DA4078C000518EB /* ImageRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageRow.swift; sourceTree = ""; }; 34 | 28BDF5FA1DA4079B000518EB /* ImageRowTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageRowTests.swift; path = Tests/ImageRowTests.swift; sourceTree = SOURCE_ROOT; }; 35 | 28BDF5FD1DA42427000518EB /* Eureka.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Eureka.framework; path = Carthage/Build/iOS/Eureka.framework; sourceTree = ""; }; 36 | 28F8287D1C494B2C00330CF4 /* ImageRow.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ImageRow.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 28F828801C494B2C00330CF4 /* ImageRow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImageRow.h; sourceTree = ""; }; 38 | 28F828871C494B2C00330CF4 /* ImageRowTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ImageRowTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 28F8288E1C494B2C00330CF4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | 28F8289B1C494BF100330CF4 /* Playground.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = Playground.playground; path = Example/Playground.playground; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 41 | 9A61F6D61EBA133800CBE062 /* ImageCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCell.swift; sourceTree = ""; }; 42 | /* End PBXFileReference section */ 43 | 44 | /* Begin PBXFrameworksBuildPhase section */ 45 | 28F828791C494B2C00330CF4 /* Frameworks */ = { 46 | isa = PBXFrameworksBuildPhase; 47 | buildActionMask = 2147483647; 48 | files = ( 49 | 28BDF5FE1DA42427000518EB /* Eureka.framework in Frameworks */, 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | 28F828841C494B2C00330CF4 /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | 28F828881C494B2C00330CF4 /* ImageRow.framework in Frameworks */, 58 | 28BDF5FF1DA42427000518EB /* Eureka.framework in Frameworks */, 59 | ); 60 | runOnlyForDeploymentPostprocessing = 0; 61 | }; 62 | /* End PBXFrameworksBuildPhase section */ 63 | 64 | /* Begin PBXGroup section */ 65 | 28BDF5FC1DA422B5000518EB /* Dependencies */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | 28BDF5FD1DA42427000518EB /* Eureka.framework */, 69 | ); 70 | name = Dependencies; 71 | sourceTree = ""; 72 | }; 73 | 28F828731C494B2C00330CF4 = { 74 | isa = PBXGroup; 75 | children = ( 76 | 28F8289B1C494BF100330CF4 /* Playground.playground */, 77 | 28F828971C494B4200330CF4 /* Sources */, 78 | 28F8288B1C494B2C00330CF4 /* Tests */, 79 | 28F8287E1C494B2C00330CF4 /* Products */, 80 | 28BDF5FC1DA422B5000518EB /* Dependencies */, 81 | ); 82 | sourceTree = ""; 83 | }; 84 | 28F8287E1C494B2C00330CF4 /* Products */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 28F8287D1C494B2C00330CF4 /* ImageRow.framework */, 88 | 28F828871C494B2C00330CF4 /* ImageRowTests.xctest */, 89 | ); 90 | name = Products; 91 | sourceTree = ""; 92 | }; 93 | 28F8288B1C494B2C00330CF4 /* Tests */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 28F8288E1C494B2C00330CF4 /* Info.plist */, 97 | 28BDF5FA1DA4079B000518EB /* ImageRowTests.swift */, 98 | ); 99 | path = Tests; 100 | sourceTree = ""; 101 | }; 102 | 28F828971C494B4200330CF4 /* Sources */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 28BDF5F41DA40763000518EB /* ImagePickerController.swift */, 106 | 28BDF5F81DA4078C000518EB /* ImageRow.swift */, 107 | 9A61F6D61EBA133800CBE062 /* ImageCell.swift */, 108 | 28BDF5F61DA4076A000518EB /* Info.plist */, 109 | 28F828801C494B2C00330CF4 /* ImageRow.h */, 110 | ); 111 | path = Sources; 112 | sourceTree = ""; 113 | }; 114 | /* End PBXGroup section */ 115 | 116 | /* Begin PBXHeadersBuildPhase section */ 117 | 28F8287A1C494B2C00330CF4 /* Headers */ = { 118 | isa = PBXHeadersBuildPhase; 119 | buildActionMask = 2147483647; 120 | files = ( 121 | 28F828811C494B2C00330CF4 /* ImageRow.h in Headers */, 122 | ); 123 | runOnlyForDeploymentPostprocessing = 0; 124 | }; 125 | /* End PBXHeadersBuildPhase section */ 126 | 127 | /* Begin PBXNativeTarget section */ 128 | 28F8287C1C494B2C00330CF4 /* ImageRow */ = { 129 | isa = PBXNativeTarget; 130 | buildConfigurationList = 28F828911C494B2C00330CF4 /* Build configuration list for PBXNativeTarget "ImageRow" */; 131 | buildPhases = ( 132 | 28F828781C494B2C00330CF4 /* Sources */, 133 | 28F828791C494B2C00330CF4 /* Frameworks */, 134 | 28F8287A1C494B2C00330CF4 /* Headers */, 135 | 28F8287B1C494B2C00330CF4 /* Resources */, 136 | ); 137 | buildRules = ( 138 | ); 139 | dependencies = ( 140 | ); 141 | name = ImageRow; 142 | productName = ImageRow; 143 | productReference = 28F8287D1C494B2C00330CF4 /* ImageRow.framework */; 144 | productType = "com.apple.product-type.framework"; 145 | }; 146 | 28F828861C494B2C00330CF4 /* ImageRowTests */ = { 147 | isa = PBXNativeTarget; 148 | buildConfigurationList = 28F828941C494B2C00330CF4 /* Build configuration list for PBXNativeTarget "ImageRowTests" */; 149 | buildPhases = ( 150 | 28F828831C494B2C00330CF4 /* Sources */, 151 | 28F828841C494B2C00330CF4 /* Frameworks */, 152 | 28F828851C494B2C00330CF4 /* Resources */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | 28F8288A1C494B2C00330CF4 /* PBXTargetDependency */, 158 | ); 159 | name = ImageRowTests; 160 | productName = ImageRowTests; 161 | productReference = 28F828871C494B2C00330CF4 /* ImageRowTests.xctest */; 162 | productType = "com.apple.product-type.bundle.unit-test"; 163 | }; 164 | /* End PBXNativeTarget section */ 165 | 166 | /* Begin PBXProject section */ 167 | 28F828741C494B2C00330CF4 /* Project object */ = { 168 | isa = PBXProject; 169 | attributes = { 170 | LastSwiftUpdateCheck = 0720; 171 | LastUpgradeCheck = 0940; 172 | TargetAttributes = { 173 | 28F8287C1C494B2C00330CF4 = { 174 | CreatedOnToolsVersion = 7.2; 175 | LastSwiftMigration = 1020; 176 | }; 177 | 28F828861C494B2C00330CF4 = { 178 | CreatedOnToolsVersion = 7.2; 179 | LastSwiftMigration = 1020; 180 | }; 181 | }; 182 | }; 183 | buildConfigurationList = 28F828771C494B2C00330CF4 /* Build configuration list for PBXProject "ImageRow" */; 184 | compatibilityVersion = "Xcode 3.2"; 185 | developmentRegion = en; 186 | hasScannedForEncodings = 0; 187 | knownRegions = ( 188 | en, 189 | ); 190 | mainGroup = 28F828731C494B2C00330CF4; 191 | productRefGroup = 28F8287E1C494B2C00330CF4 /* Products */; 192 | projectDirPath = ""; 193 | projectRoot = ""; 194 | targets = ( 195 | 28F8287C1C494B2C00330CF4 /* ImageRow */, 196 | 28F828861C494B2C00330CF4 /* ImageRowTests */, 197 | ); 198 | }; 199 | /* End PBXProject section */ 200 | 201 | /* Begin PBXResourcesBuildPhase section */ 202 | 28F8287B1C494B2C00330CF4 /* Resources */ = { 203 | isa = PBXResourcesBuildPhase; 204 | buildActionMask = 2147483647; 205 | files = ( 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | 28F828851C494B2C00330CF4 /* Resources */ = { 210 | isa = PBXResourcesBuildPhase; 211 | buildActionMask = 2147483647; 212 | files = ( 213 | ); 214 | runOnlyForDeploymentPostprocessing = 0; 215 | }; 216 | /* End PBXResourcesBuildPhase section */ 217 | 218 | /* Begin PBXSourcesBuildPhase section */ 219 | 28F828781C494B2C00330CF4 /* Sources */ = { 220 | isa = PBXSourcesBuildPhase; 221 | buildActionMask = 2147483647; 222 | files = ( 223 | 28BDF5F91DA4078C000518EB /* ImageRow.swift in Sources */, 224 | 28BDF5F51DA40763000518EB /* ImagePickerController.swift in Sources */, 225 | 9A61F6D71EBA133800CBE062 /* ImageCell.swift in Sources */, 226 | ); 227 | runOnlyForDeploymentPostprocessing = 0; 228 | }; 229 | 28F828831C494B2C00330CF4 /* Sources */ = { 230 | isa = PBXSourcesBuildPhase; 231 | buildActionMask = 2147483647; 232 | files = ( 233 | 28BDF5FB1DA4079B000518EB /* ImageRowTests.swift in Sources */, 234 | ); 235 | runOnlyForDeploymentPostprocessing = 0; 236 | }; 237 | /* End PBXSourcesBuildPhase section */ 238 | 239 | /* Begin PBXTargetDependency section */ 240 | 28F8288A1C494B2C00330CF4 /* PBXTargetDependency */ = { 241 | isa = PBXTargetDependency; 242 | target = 28F8287C1C494B2C00330CF4 /* ImageRow */; 243 | targetProxy = 28F828891C494B2C00330CF4 /* PBXContainerItemProxy */; 244 | }; 245 | /* End PBXTargetDependency section */ 246 | 247 | /* Begin XCBuildConfiguration section */ 248 | 28F8288F1C494B2C00330CF4 /* Debug */ = { 249 | isa = XCBuildConfiguration; 250 | buildSettings = { 251 | ALWAYS_SEARCH_USER_PATHS = NO; 252 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 253 | CLANG_CXX_LIBRARY = "libc++"; 254 | CLANG_ENABLE_MODULES = YES; 255 | CLANG_ENABLE_OBJC_ARC = YES; 256 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 257 | CLANG_WARN_BOOL_CONVERSION = YES; 258 | CLANG_WARN_COMMA = YES; 259 | CLANG_WARN_CONSTANT_CONVERSION = YES; 260 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 261 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 262 | CLANG_WARN_EMPTY_BODY = YES; 263 | CLANG_WARN_ENUM_CONVERSION = YES; 264 | CLANG_WARN_INFINITE_RECURSION = YES; 265 | CLANG_WARN_INT_CONVERSION = YES; 266 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 267 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 268 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 269 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 270 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 271 | CLANG_WARN_STRICT_PROTOTYPES = YES; 272 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 273 | CLANG_WARN_UNREACHABLE_CODE = YES; 274 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 275 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 276 | COPY_PHASE_STRIP = NO; 277 | CURRENT_PROJECT_VERSION = 1; 278 | DEBUG_INFORMATION_FORMAT = dwarf; 279 | ENABLE_STRICT_OBJC_MSGSEND = YES; 280 | ENABLE_TESTABILITY = YES; 281 | GCC_C_LANGUAGE_STANDARD = gnu99; 282 | GCC_DYNAMIC_NO_PIC = NO; 283 | GCC_NO_COMMON_BLOCKS = YES; 284 | GCC_OPTIMIZATION_LEVEL = 0; 285 | GCC_PREPROCESSOR_DEFINITIONS = ( 286 | "DEBUG=1", 287 | "$(inherited)", 288 | ); 289 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 290 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 291 | GCC_WARN_UNDECLARED_SELECTOR = YES; 292 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 293 | GCC_WARN_UNUSED_FUNCTION = YES; 294 | GCC_WARN_UNUSED_VARIABLE = YES; 295 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 296 | MTL_ENABLE_DEBUG_INFO = YES; 297 | ONLY_ACTIVE_ARCH = YES; 298 | SDKROOT = iphoneos; 299 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 300 | TARGETED_DEVICE_FAMILY = "1,2"; 301 | VERSIONING_SYSTEM = "apple-generic"; 302 | VERSION_INFO_PREFIX = ""; 303 | }; 304 | name = Debug; 305 | }; 306 | 28F828901C494B2C00330CF4 /* Release */ = { 307 | isa = XCBuildConfiguration; 308 | buildSettings = { 309 | ALWAYS_SEARCH_USER_PATHS = NO; 310 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 311 | CLANG_CXX_LIBRARY = "libc++"; 312 | CLANG_ENABLE_MODULES = YES; 313 | CLANG_ENABLE_OBJC_ARC = YES; 314 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 315 | CLANG_WARN_BOOL_CONVERSION = YES; 316 | CLANG_WARN_COMMA = YES; 317 | CLANG_WARN_CONSTANT_CONVERSION = YES; 318 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 319 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 320 | CLANG_WARN_EMPTY_BODY = YES; 321 | CLANG_WARN_ENUM_CONVERSION = YES; 322 | CLANG_WARN_INFINITE_RECURSION = YES; 323 | CLANG_WARN_INT_CONVERSION = YES; 324 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 325 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 326 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 327 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 328 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 329 | CLANG_WARN_STRICT_PROTOTYPES = YES; 330 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 331 | CLANG_WARN_UNREACHABLE_CODE = YES; 332 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 333 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 334 | COPY_PHASE_STRIP = NO; 335 | CURRENT_PROJECT_VERSION = 1; 336 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 337 | ENABLE_NS_ASSERTIONS = NO; 338 | ENABLE_STRICT_OBJC_MSGSEND = YES; 339 | GCC_C_LANGUAGE_STANDARD = gnu99; 340 | GCC_NO_COMMON_BLOCKS = YES; 341 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 342 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 343 | GCC_WARN_UNDECLARED_SELECTOR = YES; 344 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 345 | GCC_WARN_UNUSED_FUNCTION = YES; 346 | GCC_WARN_UNUSED_VARIABLE = YES; 347 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 348 | MTL_ENABLE_DEBUG_INFO = NO; 349 | SDKROOT = iphoneos; 350 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 351 | TARGETED_DEVICE_FAMILY = "1,2"; 352 | VALIDATE_PRODUCT = YES; 353 | VERSIONING_SYSTEM = "apple-generic"; 354 | VERSION_INFO_PREFIX = ""; 355 | }; 356 | name = Release; 357 | }; 358 | 28F828921C494B2C00330CF4 /* Debug */ = { 359 | isa = XCBuildConfiguration; 360 | buildSettings = { 361 | CLANG_ENABLE_MODULES = YES; 362 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 363 | DEFINES_MODULE = YES; 364 | DYLIB_COMPATIBILITY_VERSION = 1; 365 | DYLIB_CURRENT_VERSION = 1; 366 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 367 | FRAMEWORK_SEARCH_PATHS = ( 368 | "$(inherited)", 369 | "$(PROJECT_DIR)/Carthage/Build/iOS", 370 | ); 371 | INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; 372 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 373 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 374 | MARKETING_VERSION = 4.1.0; 375 | PRODUCT_BUNDLE_IDENTIFIER = com.xmartlabs.ImageRow; 376 | PRODUCT_NAME = "$(TARGET_NAME)"; 377 | SKIP_INSTALL = YES; 378 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 379 | SWIFT_VERSION = 5.0; 380 | }; 381 | name = Debug; 382 | }; 383 | 28F828931C494B2C00330CF4 /* Release */ = { 384 | isa = XCBuildConfiguration; 385 | buildSettings = { 386 | CLANG_ENABLE_MODULES = YES; 387 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 388 | DEFINES_MODULE = YES; 389 | DYLIB_COMPATIBILITY_VERSION = 1; 390 | DYLIB_CURRENT_VERSION = 1; 391 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 392 | FRAMEWORK_SEARCH_PATHS = ( 393 | "$(inherited)", 394 | "$(PROJECT_DIR)/Carthage/Build/iOS", 395 | ); 396 | INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; 397 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 398 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 399 | MARKETING_VERSION = 4.1.0; 400 | PRODUCT_BUNDLE_IDENTIFIER = com.xmartlabs.ImageRow; 401 | PRODUCT_NAME = "$(TARGET_NAME)"; 402 | SKIP_INSTALL = YES; 403 | SWIFT_VERSION = 5.0; 404 | }; 405 | name = Release; 406 | }; 407 | 28F828951C494B2C00330CF4 /* Debug */ = { 408 | isa = XCBuildConfiguration; 409 | buildSettings = { 410 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 411 | CLANG_ENABLE_MODULES = YES; 412 | FRAMEWORK_SEARCH_PATHS = ( 413 | "$(inherited)", 414 | "$(PROJECT_DIR)/Carthage/Build/iOS", 415 | ); 416 | INFOPLIST_FILE = Tests/Info.plist; 417 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 418 | PRODUCT_BUNDLE_IDENTIFIER = com.xmartlabs.ImageRowTests; 419 | PRODUCT_NAME = "$(TARGET_NAME)"; 420 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 421 | SWIFT_VERSION = 5.0; 422 | }; 423 | name = Debug; 424 | }; 425 | 28F828961C494B2C00330CF4 /* Release */ = { 426 | isa = XCBuildConfiguration; 427 | buildSettings = { 428 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 429 | CLANG_ENABLE_MODULES = YES; 430 | FRAMEWORK_SEARCH_PATHS = ( 431 | "$(inherited)", 432 | "$(PROJECT_DIR)/Carthage/Build/iOS", 433 | ); 434 | INFOPLIST_FILE = Tests/Info.plist; 435 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 436 | PRODUCT_BUNDLE_IDENTIFIER = com.xmartlabs.ImageRowTests; 437 | PRODUCT_NAME = "$(TARGET_NAME)"; 438 | SWIFT_VERSION = 5.0; 439 | }; 440 | name = Release; 441 | }; 442 | /* End XCBuildConfiguration section */ 443 | 444 | /* Begin XCConfigurationList section */ 445 | 28F828771C494B2C00330CF4 /* Build configuration list for PBXProject "ImageRow" */ = { 446 | isa = XCConfigurationList; 447 | buildConfigurations = ( 448 | 28F8288F1C494B2C00330CF4 /* Debug */, 449 | 28F828901C494B2C00330CF4 /* Release */, 450 | ); 451 | defaultConfigurationIsVisible = 0; 452 | defaultConfigurationName = Release; 453 | }; 454 | 28F828911C494B2C00330CF4 /* Build configuration list for PBXNativeTarget "ImageRow" */ = { 455 | isa = XCConfigurationList; 456 | buildConfigurations = ( 457 | 28F828921C494B2C00330CF4 /* Debug */, 458 | 28F828931C494B2C00330CF4 /* Release */, 459 | ); 460 | defaultConfigurationIsVisible = 0; 461 | defaultConfigurationName = Release; 462 | }; 463 | 28F828941C494B2C00330CF4 /* Build configuration list for PBXNativeTarget "ImageRowTests" */ = { 464 | isa = XCConfigurationList; 465 | buildConfigurations = ( 466 | 28F828951C494B2C00330CF4 /* Debug */, 467 | 28F828961C494B2C00330CF4 /* Release */, 468 | ); 469 | defaultConfigurationIsVisible = 0; 470 | defaultConfigurationName = Release; 471 | }; 472 | /* End XCConfigurationList section */ 473 | }; 474 | rootObject = 28F828741C494B2C00330CF4 /* Project object */; 475 | } 476 | -------------------------------------------------------------------------------- /ImageRow.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ImageRow.xcodeproj/xcshareddata/xcschemes/ImageRow.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /ImageRow.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ImageRow.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Eureka Community 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 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "ImageRow", 6 | platforms: [.iOS(.v9)], 7 | products: [ 8 | .library(name: "ImageRow", targets: ["ImageRow"]) 9 | ], 10 | dependencies: [ 11 | .package(url: "https://github.com/xmartlabs/Eureka.git", from: "5.3.5"), 12 | ], 13 | targets: [ 14 | .target( 15 | name: "ImageRow", 16 | dependencies: ["Eureka"], 17 | path: "Sources" 18 | ), 19 | .testTarget( 20 | name: "ImageRowTests", 21 | dependencies: ["ImageRow"], 22 | path: "Tests" 23 | ) 24 | ] 25 | ) 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Build status 3 | Platform iOS 4 | Swift 4 compatible 5 | Carthage compatible 6 | CocoaPods compatible 7 | License: MIT 8 |

9 | 10 | By [Xmartlabs SRL](http://EurekaCommunity.com). 11 | 12 | ## Introduction 13 | 14 | ImageRow is a Eureka custom row that allows us to take or choose a picture. 15 | 16 | 17 | 18 | ## Usage 19 | 20 | ```swift 21 | import ImageRow 22 | import Eureka 23 | 24 | class ViewController: FormViewController { 25 | 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | 29 | 30 | form +++ Section() 31 | <<< ImageRow() { row in 32 | row.title = "Image Row 1" 33 | row.sourceTypes = [.PhotoLibrary, .SavedPhotosAlbum] 34 | row.clearAction = .yes(style: UIAlertAction.Style.destructive) 35 | } 36 | +++ 37 | Section() 38 | <<< ImageRow() { 39 | $0.title = "Image Row 2" 40 | $0.sourceTypes = .PhotoLibrary 41 | $0.clearAction = .no 42 | } 43 | .cellUpdate { cell, row in 44 | cell.accessoryView?.layer.cornerRadius = 17 45 | cell.accessoryView?.frame = CGRect(x: 0, y: 0, width: 34, height: 34) 46 | } 47 | +++ 48 | Section() 49 | <<< ImageRow() { 50 | $0.title = "Image Row 3" 51 | $0.sourceTypes = [.PhotoLibrary, .SavedPhotosAlbum] 52 | $0.clearAction = .yes(style: .default) 53 | } 54 | } 55 | } 56 | 57 | ``` 58 | 59 | You must add the **NSPhotoLibraryUsageDescription** and **NSCameraUsageDescription** keys to your info.plist to use the Photo Library and camera. For more information, take a look at apple [documentation](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html). 60 | 61 | ## Requirements 62 | 63 | * iOS 9.3+ 64 | * Xcode 10.2+ 65 | * Eureka ~> 5.0 66 | 67 | ## Getting involved 68 | 69 | * If you **want to contribute** please feel free to **submit pull requests**. 70 | * If you **have a feature request** please **open an issue**. 71 | * If you **found a bug** or **need help** please **check older issues, [FAQ](#faq) before submitting an issue.**. 72 | 73 | Before contribute check the [CONTRIBUTING](https://github.com/EurekaCommunity/ImageRow/blob/master/CONTRIBUTING.md) file for more info. 74 | 75 | If you use **ImageRow** in your app We would love to hear about it! Drop us a line on [twitter](https://twitter.com/xmartlabs). 76 | 77 | ## Examples 78 | 79 | Follow these 3 steps to run Example project: Clone ImageRow repository, run `carthage update` from project root folder, open ImageRow workspace and run the *Example* project. 80 | 81 | You can also experiment and learn with the *ImageRow Playground* which is contained in *ImageRow.workspace*. 82 | 83 | ## Installation 84 | 85 | #### CocoaPods 86 | 87 | [CocoaPods](https://cocoapods.org/) is a dependency manager for Cocoa projects. 88 | 89 | To install ImageRow, simply add the following line to your Podfile: 90 | 91 | ```ruby 92 | pod 'ImageRow', '~> 4.1' 93 | ``` 94 | 95 | #### Carthage 96 | 97 | [Carthage](https://github.com/Carthage/Carthage) is a simple, decentralized dependency manager for Cocoa. 98 | 99 | To install ImageRow, simply add the following line to your Cartfile: 100 | 101 | ```ogdl 102 | github "EurekaCommunity/ImageRow" ~> 4.1 103 | ``` 104 | 105 | ## Customization 106 | 107 | ImageRow has 5 properties to customize: 108 | 109 | * `sourceTypes` which allows us to specify the source of the picture. It could be .PhotoLibrary, .Camera, .SavedPhotosAlbum, or any combination of the previous values since `sourceTypes` property type is `ImageRowSourceTypes` which conforms to `OptionSet`. 110 | * `clearAction` let's us add a clear action sheet option and configure its style. Possible values are: `.no` or `.yes(style: UIAlertActionStyle)`. Notice that .yes value requires we pass a `UIAlertActionStyle` style. 111 | * `allowEditor` tells the `ImagePickerController` to use the standard system image editor after a Image is selected. Possible values are: `true` or `false`. The default value for this property is `false`. 112 | * `useEditedImage` tells the `ImageRow` to use the edited Image from the editor instead of the original one. Possible values are: `true` or `false`. The default value for this property is `false`. 113 | * `userPickerInfo` this property holds the `info` properties of the `ImagePickerController` after a edited Image is selected, this can be used to further customization or information usage of the selected image. By default this property is `nil`. 114 | 115 | To localize the Actionsheet strings just add the keys `"Take photo", "Photo Library", "Saved Photos", "Cancel", "Clear Photo"` to your Localizable.strings file 116 | 117 | ## Author 118 | 119 | * [Xmartlabs SRL](https://github.com/xmartlabs) ([@xmartlabs](https://twitter.com/xmartlabs)) 120 | 121 | # Change Log 122 | 123 | This can be found in the [CHANGELOG.md](CHANGELOG.md) file. 124 | -------------------------------------------------------------------------------- /Sources/ImageCell.swift: -------------------------------------------------------------------------------- 1 | // ImageCell.swift 2 | // ImageRow ( https://github.com/EurekaCommunity/ImageRow ) 3 | // 4 | // Copyright (c) 2016 Xmartlabs SRL ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Eureka 26 | import UIKit 27 | 28 | public final class ImageCell: PushSelectorCell { 29 | public override func setup() { 30 | super.setup() 31 | 32 | accessoryType = .none 33 | editingAccessoryView = .none 34 | 35 | let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 44, height: 44)) 36 | imageView.contentMode = .scaleAspectFill 37 | imageView.clipsToBounds = true 38 | 39 | accessoryView = imageView 40 | editingAccessoryView = imageView 41 | } 42 | 43 | public override func update() { 44 | super.update() 45 | 46 | selectionStyle = row.isDisabled ? .none : .default 47 | (accessoryView as? UIImageView)?.image = row.value ?? (row as? ImageRowProtocol)?.thumbnailImage ?? (row as? ImageRowProtocol)?.placeholderImage 48 | (editingAccessoryView as? UIImageView)?.image = row.value ?? (row as? ImageRowProtocol)?.thumbnailImage ?? (row as? ImageRowProtocol)?.placeholderImage 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/ImagePickerController.swift: -------------------------------------------------------------------------------- 1 | // ImagePickerController.swift 2 | // ImageRow ( https://github.com/EurekaCommunity/ImageRow ) 3 | // 4 | // Copyright (c) 2016 Xmartlabs SRL ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Eureka 26 | import Foundation 27 | import UIKit 28 | 29 | public protocol ImagePickerProtocol: class { 30 | var allowEditor: Bool { get set } 31 | 32 | var imageURL: URL? { get set } 33 | 34 | var useEditedImage: Bool { get set } 35 | 36 | var userPickerInfo: [UIImagePickerController.InfoKey:Any]? { get set } 37 | } 38 | 39 | /// Selector Controller used to pick an image 40 | open class ImagePickerController: UIImagePickerController, TypedRowControllerType, UIImagePickerControllerDelegate, UINavigationControllerDelegate { 41 | 42 | /// The row that pushed or presented this controller 43 | public var row: RowOf! 44 | 45 | /// A closure to be called when the controller disappears. 46 | public var onDismissCallback: ((UIViewController) -> ())? 47 | 48 | override open var preferredStatusBarStyle: UIStatusBarStyle { 49 | return .default 50 | } 51 | 52 | open override func viewDidLoad() { 53 | super.viewDidLoad() 54 | delegate = self 55 | } 56 | 57 | open func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { 58 | (row as? ImagePickerProtocol)?.imageURL = info[UIImagePickerController.InfoKey.referenceURL] as? URL 59 | 60 | var finalImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage 61 | 62 | let editedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage 63 | 64 | if(((row as? ImagePickerProtocol)?.useEditedImage ?? false) && editedImage != nil) { 65 | finalImage = editedImage 66 | } 67 | 68 | (row as? ImagePickerProtocol)?.userPickerInfo = info 69 | row.value = finalImage 70 | onDismissCallback?(self) 71 | } 72 | 73 | open func imagePickerControllerDidCancel(_ picker: UIImagePickerController){ 74 | onDismissCallback?(self) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Sources/ImageRow.h: -------------------------------------------------------------------------------- 1 | // 2 | // ImageRow.h 3 | // ImageRow 4 | // 5 | // Copyright © 2016 Xmartlabs SRL. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | //! Project version number for ImageRow. 11 | FOUNDATION_EXPORT double ImageRowVersionNumber; 12 | 13 | //! Project version string for ImageRow. 14 | FOUNDATION_EXPORT const unsigned char ImageRowVersionString[]; 15 | 16 | // In this header, you should import all the public headers of your framework using statements like #import 17 | 18 | 19 | -------------------------------------------------------------------------------- /Sources/ImageRow.swift: -------------------------------------------------------------------------------- 1 | // ImageRow.swift 2 | // ImageRow ( https://github.com/EurekaCommunity/ImageRow ) 3 | // 4 | // Copyright (c) 2016 Xmartlabs SRL ( http://xmartlabs.com ) 5 | // 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | import Eureka 26 | import Foundation 27 | import UIKit 28 | 29 | public struct ImageRowSourceTypes: OptionSet { 30 | public let rawValue: Int 31 | public var imagePickerControllerSourceTypeRawValue: Int { return self.rawValue >> 1 } 32 | 33 | public init(rawValue: Int) { self.rawValue = rawValue } 34 | init(_ sourceType: UIImagePickerController.SourceType) { self.init(rawValue: 1 << sourceType.rawValue) } 35 | 36 | public static let PhotoLibrary = ImageRowSourceTypes(.photoLibrary) 37 | public static let Camera = ImageRowSourceTypes(.camera) 38 | public static let SavedPhotosAlbum = ImageRowSourceTypes(.savedPhotosAlbum) 39 | public static let All: ImageRowSourceTypes = [Camera, PhotoLibrary, SavedPhotosAlbum] 40 | } 41 | 42 | public extension ImageRowSourceTypes { 43 | var localizedString: String { 44 | switch self { 45 | case ImageRowSourceTypes.Camera: 46 | return NSLocalizedString("Take Photo", comment: "") 47 | case ImageRowSourceTypes.PhotoLibrary: 48 | return NSLocalizedString("Photo Library", comment: "") 49 | case ImageRowSourceTypes.SavedPhotosAlbum: 50 | return NSLocalizedString("Saved Photos", comment: "") 51 | default: 52 | return "" 53 | } 54 | } 55 | } 56 | 57 | public enum ImageClearAction { 58 | case no 59 | case yes(style: UIAlertAction.Style) 60 | } 61 | 62 | public protocol ImageRowProtocol { 63 | var placeholderImage: UIImage? { get } 64 | var thumbnailImage: UIImage? { get } 65 | } 66 | 67 | //MARK: Row 68 | 69 | open class _ImageRow: OptionsRow, PresenterRowType, ImageRowProtocol, ImagePickerProtocol where Cell: BaseCell, Cell.Value == UIImage { 70 | public typealias PresenterRow = ImagePickerController 71 | 72 | /// Defines how the view controller will be presented, pushed, etc. 73 | open var presentationMode: PresentationMode? 74 | 75 | /// Will be called before the presentation occurs. 76 | open var onPresentCallback: ((FormViewController, PresenterRow) -> Void)? 77 | 78 | open var sourceTypes: ImageRowSourceTypes 79 | open var imageURL: URL? 80 | open var clearAction = ImageClearAction.yes(style: .destructive) 81 | open var placeholderImage: UIImage? 82 | open var thumbnailImage: UIImage? 83 | open var actionTintColor: UIColor 84 | 85 | open var userPickerInfo : [UIImagePickerController.InfoKey:Any]? 86 | open var allowEditor : Bool 87 | open var useEditedImage : Bool 88 | 89 | private var _sourceType: UIImagePickerController.SourceType = .camera 90 | 91 | public required init(tag: String?) { 92 | sourceTypes = .All 93 | userPickerInfo = nil 94 | allowEditor = false 95 | useEditedImage = false 96 | actionTintColor = .systemBlue 97 | 98 | super.init(tag: tag) 99 | 100 | presentationMode = .presentModally(controllerProvider: 101 | ControllerProvider.callback { [weak self] in 102 | let controller = ImagePickerController() 103 | controller.allowsEditing = self?.allowEditor ?? false 104 | return controller 105 | 106 | }, onDismiss: { [weak self] vc in 107 | self?.select() 108 | vc.dismiss(animated: true) 109 | }) 110 | 111 | self.displayValueFor = nil 112 | } 113 | 114 | // copy over the existing logic from the SelectorRow 115 | func displayImagePickerController(_ sourceType: UIImagePickerController.SourceType) { 116 | if let presentationMode = presentationMode, !isDisabled { 117 | if let controller = presentationMode.makeController(){ 118 | controller.row = self 119 | controller.sourceType = sourceType 120 | onPresentCallback?(cell.formViewController()!, controller) 121 | presentationMode.present(controller, row: self, presentingController: cell.formViewController()!) 122 | } else { 123 | _sourceType = sourceType 124 | presentationMode.present(nil, row: self, presentingController: cell.formViewController()!) 125 | } 126 | } 127 | } 128 | 129 | /// Extends `didSelect` method 130 | /// Selecting the Image Row cell will open a popup to choose where to source the photo from, 131 | /// based on the `sourceTypes` configured and the available sources. 132 | open override func customDidSelect() { 133 | guard !isDisabled else { 134 | super.customDidSelect() 135 | return 136 | } 137 | 138 | deselect() 139 | selectSource() 140 | } 141 | 142 | open func selectSource() { 143 | var availableSources: ImageRowSourceTypes = [] 144 | 145 | if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) { 146 | let _ = availableSources.insert(.PhotoLibrary) 147 | } 148 | if UIImagePickerController.isSourceTypeAvailable(.camera) { 149 | let _ = availableSources.insert(.Camera) 150 | } 151 | if UIImagePickerController.isSourceTypeAvailable(.savedPhotosAlbum) { 152 | let _ = availableSources.insert(.SavedPhotosAlbum) 153 | } 154 | 155 | sourceTypes.formIntersection(availableSources) 156 | 157 | if sourceTypes.isEmpty { 158 | super.customDidSelect() 159 | guard let presentationMode = presentationMode else { return } 160 | 161 | if let controller = presentationMode.makeController() { 162 | controller.row = self 163 | controller.title = selectorTitle ?? controller.title 164 | onPresentCallback?(cell.formViewController()!, controller) 165 | presentationMode.present(controller, row: self, presentingController: self.cell.formViewController()!) 166 | } else { 167 | presentationMode.present(nil, row: self, presentingController: self.cell.formViewController()!) 168 | } 169 | 170 | return 171 | } 172 | 173 | // Now that we know the number of sources aren't empty, let the user select the source 174 | let sourceActionSheet = UIAlertController(title: nil, message: selectorTitle, preferredStyle: .actionSheet) 175 | sourceActionSheet.view.tintColor = actionTintColor 176 | 177 | guard let tableView = cell.formViewController()?.tableView else { fatalError() } 178 | 179 | if let popView = sourceActionSheet.popoverPresentationController { 180 | popView.sourceView = tableView 181 | popView.sourceRect = tableView.convert(cell.accessoryView?.frame ?? cell.contentView.frame, from: cell) 182 | } 183 | 184 | createOptionsForAlertController(sourceActionSheet) 185 | 186 | // if thumbnail or value is set, offer clear action 187 | if case .yes(let style) = clearAction, value != nil || thumbnailImage != nil { 188 | let clearPhotoOption = UIAlertAction(title: NSLocalizedString("Clear Photo", comment: ""), style: style) { [weak self] _ in 189 | self?.value = nil 190 | self?.thumbnailImage = nil 191 | self?.imageURL = nil 192 | self?.updateCell() 193 | } 194 | 195 | sourceActionSheet.addAction(clearPhotoOption) 196 | } 197 | 198 | if sourceActionSheet.actions.count == 1 { 199 | if let imagePickerSourceType = UIImagePickerController.SourceType(rawValue: sourceTypes.imagePickerControllerSourceTypeRawValue) { 200 | displayImagePickerController(imagePickerSourceType) 201 | } 202 | } else { 203 | let cancelOption = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: nil) 204 | 205 | sourceActionSheet.addAction(cancelOption) 206 | 207 | if let presentingViewController = cell.formViewController() { 208 | presentingViewController.present(sourceActionSheet, animated: true) 209 | } 210 | } 211 | } 212 | 213 | /** 214 | Prepares the pushed row setting its title and completion callback. 215 | */ 216 | open override func prepare(for segue: UIStoryboardSegue) { 217 | super.prepare(for: segue) 218 | guard let rowVC = segue.destination as? PresenterRow else { return } 219 | rowVC.title = selectorTitle ?? rowVC.title 220 | rowVC.onDismissCallback = presentationMode?.onDismissCallback ?? rowVC.onDismissCallback 221 | onPresentCallback?(cell.formViewController()!, rowVC) 222 | rowVC.row = self 223 | rowVC.sourceType = _sourceType 224 | } 225 | } 226 | 227 | public extension _ImageRow { 228 | func createOptionForAlertController(_ alertController: UIAlertController, sourceType: ImageRowSourceTypes) { 229 | guard let pickerSourceType = UIImagePickerController.SourceType(rawValue: sourceType.imagePickerControllerSourceTypeRawValue), sourceTypes.contains(sourceType) else { return } 230 | 231 | let option = UIAlertAction(title: NSLocalizedString(sourceType.localizedString, comment: ""), style: .default) { [weak self] _ in 232 | self?.displayImagePickerController(pickerSourceType) 233 | } 234 | 235 | alertController.addAction(option) 236 | } 237 | 238 | func createOptionsForAlertController(_ alertController: UIAlertController) { 239 | createOptionForAlertController(alertController, sourceType: .Camera) 240 | createOptionForAlertController(alertController, sourceType: .PhotoLibrary) 241 | createOptionForAlertController(alertController, sourceType: .SavedPhotosAlbum) 242 | } 243 | } 244 | 245 | /// A selector row where the user can pick an image 246 | public final class ImageRow: _ImageRow, RowType { 247 | public required init(tag: String?) { 248 | super.init(tag: tag) 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /Sources/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 | $(MARKETING_VERSION) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Tests/ImageRowTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageRowTests.swift 3 | // Eureka 4 | // 5 | // Created by Florian Fittschen on 13/01/16. 6 | // Copyright © 2016 Xmartlabs. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | @testable import ImageRow 12 | import Eureka 13 | 14 | class ImageRowTests: XCTestCase { 15 | 16 | var formVC = FormViewController() 17 | 18 | var availableSources: ImageRowSourceTypes { 19 | var result: ImageRowSourceTypes = [] 20 | 21 | if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) { 22 | result.insert(ImageRowSourceTypes.PhotoLibrary) 23 | } 24 | if UIImagePickerController.isSourceTypeAvailable(.camera) { 25 | result.insert(ImageRowSourceTypes.Camera) 26 | } 27 | if UIImagePickerController.isSourceTypeAvailable(.savedPhotosAlbum) { 28 | result.insert(ImageRowSourceTypes.SavedPhotosAlbum) 29 | } 30 | return result 31 | } 32 | 33 | override func setUp() { 34 | super.setUp() 35 | 36 | let form = Form() 37 | 38 | form +++ Section() 39 | <<< ImageRow("DefaultImageRow") { 40 | $0.title = $0.tag 41 | // Default sourceTypes == .All 42 | } 43 | <<< ImageRow("SingleSourceImageRow") { (row: ImageRow) in 44 | row.title = row.tag 45 | row.sourceTypes = .Camera 46 | } 47 | <<< ImageRow("TwoSourcesImageRow") { (row: ImageRow) in 48 | row.title = row.tag 49 | row.sourceTypes = [.SavedPhotosAlbum, .PhotoLibrary] 50 | } 51 | formVC.form = form 52 | 53 | // load the view to test the cells 54 | formVC.view.frame = CGRect(x: 0, y: 0, width: 375, height: 3000) 55 | formVC.tableView?.frame = formVC.view.frame 56 | } 57 | 58 | override func tearDown() { 59 | super.tearDown() 60 | } 61 | 62 | func testEmptyImageRowSourceTypes() { 63 | let result = ImageRowSourceTypes() 64 | XCTAssertTrue(result.isEmpty) 65 | XCTAssertFalse(result.contains(.PhotoLibrary)) 66 | XCTAssertFalse(result.contains(.Camera)) 67 | XCTAssertFalse(result.contains(.SavedPhotosAlbum)) 68 | } 69 | 70 | func testImagePickerControllerSourceTypeRawValue() { 71 | XCTAssert(UIImagePickerController.SourceType.photoLibrary.rawValue == ImageRowSourceTypes.PhotoLibrary.imagePickerControllerSourceTypeRawValue) 72 | XCTAssert(UIImagePickerController.SourceType.camera.rawValue == ImageRowSourceTypes.Camera.imagePickerControllerSourceTypeRawValue) 73 | XCTAssert(UIImagePickerController.SourceType.savedPhotosAlbum.rawValue == ImageRowSourceTypes.SavedPhotosAlbum.imagePickerControllerSourceTypeRawValue) 74 | } 75 | 76 | func testImageRow() { 77 | guard let defaultImageRow = formVC.form.rowBy(tag: "DefaultImageRow") as? ImageRow else { 78 | XCTFail() 79 | return 80 | } 81 | 82 | guard let singleSourceImageRow = formVC.form.rowBy(tag: "SingleSourceImageRow") as? ImageRow else { 83 | XCTFail() 84 | return 85 | } 86 | 87 | guard let twoSourcesImageRow = formVC.form.rowBy(tag: "TwoSourcesImageRow") as? ImageRow else { 88 | XCTFail() 89 | return 90 | } 91 | 92 | XCTAssert(defaultImageRow.sourceTypes == ImageRowSourceTypes.All) 93 | XCTAssert(singleSourceImageRow.sourceTypes == ImageRowSourceTypes.Camera) 94 | XCTAssert(twoSourcesImageRow.sourceTypes == ImageRowSourceTypes([ImageRowSourceTypes.SavedPhotosAlbum, ImageRowSourceTypes.PhotoLibrary])) 95 | } 96 | 97 | 98 | 99 | } 100 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | --------------------------------------------------------------------------------