ImageRow.swift 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. // ImageRow.swift
  2. // Eureka ( https://github.com/xmartlabs/Eureka )
  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. import Foundation
  25. import Eureka
  26. public struct ImageRowSourceTypes : OptionSet {
  27. public let rawValue: Int
  28. public var imagePickerControllerSourceTypeRawValue: Int { return self.rawValue >> 1 }
  29. public init(rawValue: Int) { self.rawValue = rawValue }
  30. init(_ sourceType: UIImagePickerController.SourceType) { self.init(rawValue: 1 << sourceType.rawValue) }
  31. public static let PhotoLibrary = ImageRowSourceTypes(.photoLibrary)
  32. public static let Camera = ImageRowSourceTypes(.camera)
  33. public static let SavedPhotosAlbum = ImageRowSourceTypes(.savedPhotosAlbum)
  34. public static let All: ImageRowSourceTypes = [Camera, PhotoLibrary, SavedPhotosAlbum]
  35. }
  36. extension ImageRowSourceTypes {
  37. // MARK: Helpers
  38. var localizedString: String {
  39. switch self {
  40. case ImageRowSourceTypes.Camera:
  41. return "拍照"
  42. case ImageRowSourceTypes.PhotoLibrary:
  43. return "图库"
  44. case ImageRowSourceTypes.SavedPhotosAlbum:
  45. return "相册"
  46. default:
  47. return ""
  48. }
  49. }
  50. }
  51. public enum ImageClearAction {
  52. case no
  53. case yes(style: UIAlertAction.Style)
  54. }
  55. //MARK: Row
  56. open class _ImageRow<Cell: CellType>: OptionsRow<Cell>, PresenterRowType where Cell: BaseCell, Cell.Value == UIImage {
  57. public typealias PresenterRow = ImagePickerController
  58. /// Defines how the view controller will be presented, pushed, etc.
  59. open var presentationMode: PresentationMode<PresenterRow>?
  60. /// Will be called before the presentation occurs.
  61. open var onPresentCallback: ((FormViewController, PresenterRow) -> Void)?
  62. open var sourceTypes: ImageRowSourceTypes
  63. open internal(set) var imageURL: URL?
  64. open var clearAction = ImageClearAction.yes(style: .destructive)
  65. private var _sourceType: UIImagePickerController.SourceType = .camera
  66. public required init(tag: String?) {
  67. sourceTypes = .All
  68. super.init(tag: tag)
  69. presentationMode = .presentModally(controllerProvider: ControllerProvider.callback { return ImagePickerController() }, onDismiss: { [weak self] vc in
  70. self?.select()
  71. vc.dismiss(animated: true)
  72. })
  73. self.displayValueFor = nil
  74. }
  75. // copy over the existing logic from the SelectorRow
  76. func displayImagePickerController(_ sourceType: UIImagePickerController.SourceType) {
  77. if let presentationMode = presentationMode, !isDisabled {
  78. if let controller = presentationMode.makeController(){
  79. controller.row = self
  80. controller.sourceType = sourceType
  81. onPresentCallback?(cell.formViewController()!, controller)
  82. presentationMode.present(controller, row: self, presentingController: cell.formViewController()!)
  83. }
  84. else{
  85. _sourceType = sourceType
  86. presentationMode.present(nil, row: self, presentingController: cell.formViewController()!)
  87. }
  88. }
  89. }
  90. /// Extends `didSelect` method
  91. /// Selecting the Image Row cell will open a popup to choose where to source the photo from,
  92. /// based on the `sourceTypes` configured and the available sources.
  93. open override func customDidSelect() {
  94. guard !isDisabled else {
  95. super.customDidSelect()
  96. return
  97. }
  98. deselect()
  99. var availableSources: ImageRowSourceTypes = []
  100. if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
  101. let _ = availableSources.insert(.PhotoLibrary)
  102. }
  103. if UIImagePickerController.isSourceTypeAvailable(.camera) {
  104. let _ = availableSources.insert(.Camera)
  105. }
  106. if UIImagePickerController.isSourceTypeAvailable(.savedPhotosAlbum) {
  107. let _ = availableSources.insert(.SavedPhotosAlbum)
  108. }
  109. sourceTypes.formIntersection(availableSources)
  110. if sourceTypes.isEmpty {
  111. super.customDidSelect()
  112. guard let presentationMode = presentationMode else { return }
  113. if let controller = presentationMode.makeController() {
  114. controller.row = self
  115. controller.title = selectorTitle ?? controller.title
  116. onPresentCallback?(cell.formViewController()!, controller)
  117. presentationMode.present(controller, row: self, presentingController: self.cell.formViewController()!)
  118. } else {
  119. presentationMode.present(nil, row: self, presentingController: self.cell.formViewController()!)
  120. }
  121. return
  122. }
  123. // Now that we know the number of sources aren't empty, let the user select the source
  124. let sourceActionSheet = UIAlertController(title: nil, message: selectorTitle, preferredStyle: .actionSheet)
  125. guard let tableView = cell.formViewController()?.tableView else { fatalError() }
  126. if let popView = sourceActionSheet.popoverPresentationController {
  127. popView.sourceView = tableView
  128. popView.sourceRect = tableView.convert(cell.accessoryView?.frame ?? cell.contentView.frame, from: cell)
  129. }
  130. createOptionsForAlertController(sourceActionSheet)
  131. if case .yes(let style) = clearAction, value != nil {
  132. let clearPhotoOption = UIAlertAction(title: NSLocalizedString("Clear Photo", comment: ""), style: style, handler: { [weak self] _ in
  133. self?.value = nil
  134. self?.imageURL = nil
  135. self?.updateCell()
  136. })
  137. sourceActionSheet.addAction(clearPhotoOption)
  138. }
  139. if sourceActionSheet.actions.count == 1 {
  140. if let imagePickerSourceType = UIImagePickerController.SourceType(rawValue: sourceTypes.imagePickerControllerSourceTypeRawValue) {
  141. displayImagePickerController(imagePickerSourceType)
  142. }
  143. } else {
  144. let cancelOption = UIAlertAction(title: NSLocalizedString("取消", comment: ""), style: .cancel, handler:nil)
  145. sourceActionSheet.addAction(cancelOption)
  146. if let presentingViewController = cell.formViewController() {
  147. presentingViewController.present(sourceActionSheet, animated: true)
  148. }
  149. }
  150. }
  151. /**
  152. Prepares the pushed row setting its title and completion callback.
  153. */
  154. open override func prepare(for segue: UIStoryboardSegue) {
  155. super.prepare(for: segue)
  156. guard let rowVC = segue.destination as? PresenterRow else { return }
  157. rowVC.title = selectorTitle ?? rowVC.title
  158. rowVC.onDismissCallback = presentationMode?.onDismissCallback ?? rowVC.onDismissCallback
  159. onPresentCallback?(cell.formViewController()!, rowVC)
  160. rowVC.row = self
  161. rowVC.sourceType = _sourceType
  162. }
  163. open override func customUpdateCell() {
  164. super.customUpdateCell()
  165. cell.accessoryType = .none
  166. cell.editingAccessoryView = .none
  167. if let image = self.value {
  168. let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
  169. imageView.contentMode = .scaleAspectFill
  170. imageView.image = image
  171. imageView.clipsToBounds = true
  172. cell.accessoryView = imageView
  173. cell.editingAccessoryView = imageView
  174. } else {
  175. cell.accessoryView = nil
  176. cell.editingAccessoryView = nil
  177. }
  178. }
  179. }
  180. extension _ImageRow {
  181. //MARK: Helpers
  182. func createOptionForAlertController(_ alertController: UIAlertController, sourceType: ImageRowSourceTypes) {
  183. guard let pickerSourceType = UIImagePickerController.SourceType(rawValue: sourceType.imagePickerControllerSourceTypeRawValue), sourceTypes.contains(sourceType) else { return }
  184. let option = UIAlertAction(title: NSLocalizedString(sourceType.localizedString, comment: ""), style: .default, handler: { [weak self] _ in
  185. self?.displayImagePickerController(pickerSourceType)
  186. })
  187. alertController.addAction(option)
  188. }
  189. func createOptionsForAlertController(_ alertController: UIAlertController) {
  190. createOptionForAlertController(alertController, sourceType: .Camera)
  191. createOptionForAlertController(alertController, sourceType: .PhotoLibrary)
  192. createOptionForAlertController(alertController, sourceType: .SavedPhotosAlbum)
  193. }
  194. }
  195. /// A selector row where the user can pick an image
  196. public final class ImageRow : _ImageRow<PushSelectorCell<UIImage>>, RowType {
  197. public required init(tag: String?) {
  198. super.init(tag: tag)
  199. }
  200. }