├── .gitignore ├── .travis.yml ├── EasyMakePhotoPicker.podspec ├── EasyMakePhotoPicker ├── Assets │ ├── .gitkeep │ ├── FacebookPhotoPicker.gif │ ├── KakaoChatPhotoPicker.gif │ └── KakaoPhotoPicker.gif └── Classes │ ├── .gitkeep │ ├── Base+View+VC.swift │ ├── Bundle.swift │ ├── CameraCellViewModel.swift │ ├── LivePhotoCellViewModel.swift │ ├── PhotoAsset.swift │ ├── PhotoAssetCollection.swift │ ├── PhotoCellViewModel.swift │ ├── PhotoCollectionCellViewModel.swift │ ├── PhotoCollectionsView.swift │ ├── PhotoCollectionsViewConfigure.swift │ ├── PhotoCollectionsViewModel.swift │ ├── PhotoManager+Cache.swift │ ├── PhotoManager+Change.swift │ ├── PhotoManager+Fetch.swift │ ├── PhotoManager+Permission.swift │ ├── PhotoManager.swift │ ├── PhotosView.swift │ ├── PhotosViewConfigure.swift │ ├── PhotosViewModel.swift │ ├── UIAlertViewController+Extension.swift │ ├── UICollection+Extension.swift │ ├── UIView+Animation.swift │ ├── UIView+FluidStyleAutoLayout.swift │ └── VideoCellViewModel.swift ├── Example ├── EasyMakePhotoPicker.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── EasyMakePhotoPicker-Example.xcscheme ├── EasyMakePhotoPicker.xcworkspace │ └── contents.xcworkspacedata ├── EasyMakePhotoPicker │ ├── AppDelegate.swift │ ├── Base+View+VC.swift │ ├── Base.lproj │ │ └── LaunchScreen.xib │ ├── CheckImageView.swift │ ├── DurationLabel.swift │ ├── FacebookCameraCell.swift │ ├── FacebookLivePhotoCell.swift │ ├── FacebookNumberLabel.swift │ ├── FacebookPermissionVC.swift │ ├── FacebookPhotoCell.swift │ ├── FacebookPhotoCollectionCell.swift │ ├── FacebookPhotoCollectionLayout.swift │ ├── FacebookPhotoCollectionsViewConfigure.swift │ ├── FacebookPhotoPicker.swift │ ├── FacebookPhotoPickerVC.swift │ ├── FacebookPhotosLayout.swift │ ├── FacebookPhotosViewConfigure.swift │ ├── FacebookVideoCell.swift │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── back.imageset │ │ │ ├── Contents.json │ │ │ ├── back@2x.png │ │ │ └── back@3x.png │ │ ├── camera.imageset │ │ │ ├── Contents.json │ │ │ ├── camera.png │ │ │ ├── camera@x2.png │ │ │ └── camera@x3.png │ │ ├── cameraroll-picker-grip.imageset │ │ │ ├── Contents.json │ │ │ ├── cameraroll-picker-grip@2x.png │ │ │ └── cameraroll-picker-grip@3x.png │ │ ├── straighten-grid.imageset │ │ │ ├── Contents.json │ │ │ ├── straighten-grid@2x.png │ │ │ └── straighten-grid@3x.png │ │ └── video.imageset │ │ │ ├── Contents.json │ │ │ ├── icons8-Video Call_25.png │ │ │ └── icons8-Video Call_50.png │ ├── Info.plist │ ├── InputBar.swift │ ├── KaKaoCameraCell.swift │ ├── KaKaoChatCell.swift │ ├── KaKaoChatPhotoViewsConfigure.swift │ ├── KaKaoChatPhotosLayout.swift │ ├── KaKaoChatVC.swift │ ├── KaKaoCollectionCell.swift │ ├── KaKaoLivePhotoCell.swift │ ├── KaKaoPhotoCell.swift │ ├── KaKaoPhotoCollectionCell.swift │ ├── KaKaoPhotoCollectionLayout.swift │ ├── KaKaoPhotoCollectionsViewConfigure.swift │ ├── KaKaoPhotoPicker.swift │ ├── KaKaoPhotoPickerVC.swift │ ├── KaKaoPhotosLayout.swift │ ├── KaKaoPhotosViewConfigure.swift │ ├── KaKaoVideoCell.swift │ ├── MainVC.swift │ ├── NumberLabel.swift │ ├── PlayerView.swift │ ├── PlusButton.swift │ ├── TextView.swift │ ├── Then.swift │ ├── UIView+FluidStyleAutoLayout.swift │ └── kaKaoPermissionVC.swift ├── Podfile ├── Podfile.lock ├── Pods │ ├── Local Podspecs │ │ └── EasyMakePhotoPicker.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ └── project.pbxproj │ └── Target Support Files │ │ ├── EasyMakePhotoPicker │ │ ├── EasyMakePhotoPicker-dummy.m │ │ ├── EasyMakePhotoPicker-prefix.pch │ │ ├── EasyMakePhotoPicker-umbrella.h │ │ ├── EasyMakePhotoPicker.modulemap │ │ ├── EasyMakePhotoPicker.xcconfig │ │ └── Info.plist │ │ ├── Pods-EasyMakePhotoPicker_Example │ │ ├── Info.plist │ │ ├── Pods-EasyMakePhotoPicker_Example-acknowledgements.markdown │ │ ├── Pods-EasyMakePhotoPicker_Example-acknowledgements.plist │ │ ├── Pods-EasyMakePhotoPicker_Example-dummy.m │ │ ├── Pods-EasyMakePhotoPicker_Example-frameworks.sh │ │ ├── Pods-EasyMakePhotoPicker_Example-resources.sh │ │ ├── Pods-EasyMakePhotoPicker_Example-umbrella.h │ │ ├── Pods-EasyMakePhotoPicker_Example.debug.xcconfig │ │ ├── Pods-EasyMakePhotoPicker_Example.modulemap │ │ └── Pods-EasyMakePhotoPicker_Example.release.xcconfig │ │ └── Pods-EasyMakePhotoPicker_Tests │ │ ├── Info.plist │ │ ├── Pods-EasyMakePhotoPicker_Tests-acknowledgements.markdown │ │ ├── Pods-EasyMakePhotoPicker_Tests-acknowledgements.plist │ │ ├── Pods-EasyMakePhotoPicker_Tests-dummy.m │ │ ├── Pods-EasyMakePhotoPicker_Tests-frameworks.sh │ │ ├── Pods-EasyMakePhotoPicker_Tests-resources.sh │ │ ├── Pods-EasyMakePhotoPicker_Tests-umbrella.h │ │ ├── Pods-EasyMakePhotoPicker_Tests.debug.xcconfig │ │ ├── Pods-EasyMakePhotoPicker_Tests.modulemap │ │ └── Pods-EasyMakePhotoPicker_Tests.release.xcconfig └── Tests │ ├── Info.plist │ └── Tests.swift ├── LICENSE ├── README.md └── _Pods.xcodeproj /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | Carthage 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 29 | # 30 | # Note: if you ignore the Pods directory, make sure to uncomment 31 | # `pod install` in .travis.yml 32 | # 33 | # Pods/ 34 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode7.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -workspace Example/EasyMakePhotoPicker.xcworkspace -scheme EasyMakePhotoPicker-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint EasyMakePhotoPicker.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'EasyMakePhotoPicker' 11 | s.version = '0.1.2' 12 | s.summary = 'EasyPhotoPicker makes it easy to create your own PhotoPicker.' 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | 20 | s.description = <<-DESC 21 | If you need to create your own PhotoPicker, it is not easy to create because you need to implement many of the features (UI, business logic) needed to implement PhotoPicker. So EasyMakePhotoPicker provides an abstraction layer of PhotoPicker. EasyMakePhotoPicker implements all the business logic required for PhotoPicker so you can focus on the UI. 22 | DESC 23 | 24 | s.homepage = 'https://github.com/audrl1010/EasyMakePhotoPicker' 25 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' 26 | s.license = { :type => 'MIT', :file => 'LICENSE' } 27 | s.author = { 'Myung gi son' => 'audrl1010@naver.com' } 28 | s.source = { :git => 'https://github.com/audrl1010/EasyMakePhotoPicker.git', :tag => s.version.to_s } 29 | 30 | s.platform = :ios, '9.1' 31 | s.ios.deployment_target = '9.1' 32 | 33 | s.source_files = 'EasyMakePhotoPicker/Classes/**/*' 34 | 35 | # s.resource_bundles = { 36 | # 'EasyMakePhotoPicker' => ['EasyMakePhotoPicker/Assets/*.png'] 37 | # } 38 | 39 | # s.public_header_files = 'Pod/Classes/**/*.h' 40 | # s.frameworks = 'UIKit', 'Photos', 'PhotosUI' 41 | s.dependency 'RxSwift', '>= 4.0' 42 | s.dependency 'RxCocoa', '>= 4.0' 43 | end 44 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audrl1010/EasyMakePhotoPicker/1cab1640d998c988ac37994a044d3485e745c8a7/EasyMakePhotoPicker/Assets/.gitkeep -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Assets/FacebookPhotoPicker.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audrl1010/EasyMakePhotoPicker/1cab1640d998c988ac37994a044d3485e745c8a7/EasyMakePhotoPicker/Assets/FacebookPhotoPicker.gif -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Assets/KakaoChatPhotoPicker.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audrl1010/EasyMakePhotoPicker/1cab1640d998c988ac37994a044d3485e745c8a7/EasyMakePhotoPicker/Assets/KakaoChatPhotoPicker.gif -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Assets/KakaoPhotoPicker.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audrl1010/EasyMakePhotoPicker/1cab1640d998c988ac37994a044d3485e745c8a7/EasyMakePhotoPicker/Assets/KakaoPhotoPicker.gif -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audrl1010/EasyMakePhotoPicker/1cab1640d998c988ac37994a044d3485e745c8a7/EasyMakePhotoPicker/Classes/.gitkeep -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/Base+View+VC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Base+View+VC.swift 3 | // KaKaoChatInputView 4 | // 5 | // Created by smg on 2017. 5. 13.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class BaseView: UIView { 12 | 13 | // MARK: - View Life Cycle 14 | 15 | override public init(frame: CGRect) { 16 | super.init(frame: frame) 17 | setupViews() 18 | addSubviews() 19 | setupConstraints() 20 | } 21 | 22 | required public init?(coder aDecoder: NSCoder) { 23 | super.init(coder: aDecoder) 24 | setupViews() 25 | addSubviews() 26 | setupConstraints() 27 | } 28 | 29 | // MARK: - Public Methods 30 | 31 | open func setupViews() { } 32 | open func addSubviews() { } 33 | open func setupConstraints() { } 34 | } 35 | 36 | 37 | 38 | open class BaseCollectionViewCell: UICollectionViewCell { 39 | 40 | // MARK: - Properties 41 | 42 | open class var cellIdentifier: String { return "\(self)" } 43 | 44 | // MARK: - View Life Cycle 45 | 46 | override public init(frame: CGRect) { 47 | super.init(frame: frame) 48 | setupViews() 49 | addSubviews() 50 | setupConstraints() 51 | } 52 | 53 | required public init?(coder aDecoder: NSCoder) { 54 | super.init(coder: aDecoder) 55 | setupViews() 56 | addSubviews() 57 | setupConstraints() 58 | } 59 | 60 | // MARK: - Public Methods 61 | 62 | open func setupViews() { } 63 | open func addSubviews() { } 64 | open func setupConstraints() { } 65 | } 66 | 67 | 68 | open class BaseTableViewCell: UITableViewCell { 69 | 70 | // MARK: - Properties 71 | 72 | open class var cellIdentifier: String { return "\(self)" } 73 | 74 | // MARK: - View Life Cycle 75 | 76 | override public init( 77 | style: UITableViewCell.CellStyle, 78 | reuseIdentifier: String?) { 79 | 80 | super.init(style: style, reuseIdentifier: reuseIdentifier) 81 | setupViews() 82 | addSubviews() 83 | setupConstraints() 84 | } 85 | 86 | required public init?(coder aDecoder: NSCoder) { 87 | super.init(coder: aDecoder) 88 | setupViews() 89 | addSubviews() 90 | setupConstraints() 91 | } 92 | 93 | // MARK: - Public Methods 94 | 95 | open func setupViews() { } 96 | open func addSubviews() { } 97 | open func setupConstraints() { } 98 | } 99 | 100 | open class BaseVC: UIViewController { 101 | 102 | // MARK: - View Life Cycle 103 | override open func viewDidLoad() { 104 | super.viewDidLoad() 105 | setupViews() 106 | addSubviews() 107 | setupConstraints() 108 | } 109 | 110 | // MARK: - Public Methods 111 | open func setupViews() { } 112 | open func addSubviews() { } 113 | open func setupConstraints() { } 114 | } 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/Bundle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bundle.swift 3 | // Pods 4 | // 5 | // Created by myung gi son on 2017. 7. 1.. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | class EasyMakePickerBundle { 12 | class func bundleImage(named: String) -> UIImage { 13 | let bundle = Bundle(for: EasyMakePickerBundle.self) 14 | if let image = UIImage(named: named, in: bundle,compatibleWith: nil) { 15 | return image 16 | } 17 | return UIImage(named: "camera")! 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/CameraCellViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CameraCellViewModel.swift 3 | // PhotoTest 4 | // 5 | // Created by myung gi son on 2017. 6. 19.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Photos 11 | import RxSwift 12 | 13 | open class CameraCellViewModel: PhotoCellViewModel { 14 | override public init(photoAsset: PhotoAsset, configure: PhotosViewConfigure) { 15 | super.init(photoAsset: photoAsset, configure: configure) 16 | isSelect.onCompleted() 17 | selectedOrder.onCompleted() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/LivePhotoCellViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LivePhotoCellViewModel.swift 3 | // KaKaoChatInputView 4 | // 5 | // Created by myung gi son on 2017. 5. 29.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import PhotosUI 11 | import RxSwift 12 | 13 | 14 | open class LivePhotoCellViewModel: PhotoCellViewModel { 15 | 16 | // MARK: - Output 17 | open var livePhoto: PHLivePhoto? { 18 | return photoAsset.livePhoto 19 | } 20 | 21 | open var playEvent = PublishSubject() 22 | 23 | open var badgeImage: UIImage { 24 | return PHLivePhotoView.livePhotoBadgeImage(options: .overContent) 25 | } 26 | } 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/PhotoAsset.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotoAsset.swift 3 | // KaKaoChatInputView 4 | // 5 | // Created by myung gi son on 2017. 5. 30.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Photos 11 | import RxSwift 12 | 13 | public enum AssetType { 14 | case camera 15 | case photo 16 | case video 17 | case livePhoto 18 | } 19 | 20 | public struct PhotoAsset: Equatable { 21 | 22 | public var localIdentifier: String { 23 | return asset.localIdentifier 24 | } 25 | 26 | public var asset: PHAsset 27 | 28 | public var isSelected: Bool = false 29 | 30 | public var selectedOrder: Int = 0 31 | 32 | public var playerItem: AVPlayerItem? 33 | 34 | public var livePhoto: PHLivePhoto? 35 | 36 | public var type: AssetType { 37 | if asset.mediaSubtypes.contains(.photoLive) { 38 | return .livePhoto 39 | } 40 | else if asset.mediaType == .video { 41 | return .video 42 | } 43 | else if asset.mediaType == .image { 44 | return .photo 45 | } 46 | else { 47 | return .camera 48 | } 49 | } 50 | 51 | public var disposeBag = DisposeBag() 52 | 53 | public var fullResolutionImage: UIImage { 54 | var fullImage = UIImage() 55 | PhotoManager.shared.fullResolutionImage(for: self.asset) 56 | .subscribeOn(MainScheduler.instance) 57 | .subscribe(onNext: { image in 58 | fullImage = image 59 | }) 60 | .disposed(by: disposeBag) 61 | return fullImage 62 | } 63 | 64 | public var originalFileName: String? { 65 | if let resource = PHAssetResource.assetResources(for: asset).first { 66 | return resource.originalFilename 67 | } 68 | return nil 69 | } 70 | 71 | public init(asset: PHAsset) { 72 | self.asset = asset 73 | } 74 | 75 | public static func == ( 76 | lhs: PhotoAsset, 77 | rhs: PhotoAsset) -> Bool { 78 | return lhs.asset.localIdentifier == 79 | rhs.asset.localIdentifier 80 | } 81 | } 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/PhotoAssetCollection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotoAssetCollection.swift 3 | // KaKaoChatInputView 4 | // 5 | // Created by myung gi son on 2017. 5. 30.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Photos 11 | 12 | public struct PhotoAssetCollection: Equatable { 13 | 14 | public var localIdentifier: String { 15 | return assetCollection.localIdentifier 16 | } 17 | 18 | public var assetCollection: PHAssetCollection 19 | 20 | public var fetchResult: PHFetchResult 21 | 22 | public var thumbnail: UIImage? 23 | 24 | public var title: String { 25 | return assetCollection.localizedTitle ?? "" 26 | } 27 | 28 | public var count: Int { 29 | return fetchResult.count 30 | } 31 | 32 | public var assetsInFetchResult: [PHAsset] { 33 | var assets: [PHAsset] = [] 34 | fetchResult.enumerateObjects({ asset, index, stop in 35 | assets.append(asset) 36 | }) 37 | return assets 38 | } 39 | 40 | public init(collection: PHAssetCollection, 41 | fetchResult: PHFetchResult) { 42 | self.assetCollection = collection 43 | self.fetchResult = fetchResult 44 | } 45 | 46 | public static func == ( 47 | lhs: PhotoAssetCollection, 48 | rhs: PhotoAssetCollection) -> Bool { 49 | return lhs.assetCollection.localIdentifier == 50 | rhs.assetCollection.localIdentifier 51 | } 52 | } 53 | 54 | 55 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/PhotoCellViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotoCellViewModel.swift 3 | // KaKaoChatInputView 4 | // 5 | // Created by myung gi son on 2017. 5. 29.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | 12 | open class PhotoCellViewModel { 13 | 14 | open var photoAsset: PhotoAsset 15 | 16 | open var image = Variable(nil) 17 | open var isSelect = BehaviorSubject(value: false) 18 | open var selectedOrder = BehaviorSubject(value: 0) 19 | open var configure: PhotosViewConfigure 20 | 21 | open var disposeBag = DisposeBag() 22 | 23 | public init( 24 | photoAsset: PhotoAsset, 25 | configure: PhotosViewConfigure) { 26 | self.photoAsset = photoAsset 27 | self.configure = configure 28 | 29 | isSelect.onNext(photoAsset.isSelected) 30 | selectedOrder.onNext(photoAsset.selectedOrder) 31 | 32 | // update photoAsset`s isSelected property 33 | isSelect 34 | .subscribe(onNext: { [weak self] isSelect in 35 | guard let `self` = self else { return } 36 | self.photoAsset.isSelected = isSelect 37 | }) 38 | .disposed(by: disposeBag) 39 | 40 | // update photoAsset`s selectedOrder property 41 | selectedOrder 42 | .subscribe(onNext: { [weak self] selectedOrder in 43 | guard let `self` = self else { return } 44 | self.photoAsset.selectedOrder = selectedOrder 45 | }) 46 | .disposed(by: disposeBag) 47 | } 48 | } 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/PhotoCollectionCellViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotoCollectionCellViewModel.swift 3 | // PhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 6. 27.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxSwift 11 | 12 | open class PhotoCollectionCellViewModel { 13 | open var count = BehaviorSubject(value: 0) 14 | open var thumbnail = BehaviorSubject(value: nil) 15 | open var title = BehaviorSubject(value: "") 16 | open var isSelect = Variable(false) 17 | } 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/PhotoCollectionsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotoCollectionsView.swift 3 | // PhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 6. 27.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | import UIKit 9 | import Photos 10 | import PhotosUI 11 | import RxSwift 12 | 13 | public protocol PhotoCollectionsViewOutput { 14 | var output: PhotoCollectionsViewOutput { get } 15 | 16 | var selectedPhotoCollectionWhenCellDidSelect: 17 | PublishSubject<(IndexPath, PhotoAssetCollection)> { get } 18 | } 19 | 20 | public protocol PhotoCollectionsViewInput { 21 | var input: PhotoCollectionsViewInput { get } 22 | 23 | // force cell selection 24 | var cellDidSelect: PublishSubject { get } 25 | } 26 | 27 | open class PhotoCollectionsView: 28 | BaseView, 29 | PhotoCollectionsViewOutput, 30 | PhotoCollectionsViewInput { 31 | 32 | // MARK: - Properties 33 | open var output: PhotoCollectionsViewOutput { return self } 34 | open var input: PhotoCollectionsViewInput { return self } 35 | 36 | 37 | // MARK: - Outputs 38 | open var selectedPhotoCollectionWhenCellDidSelect: 39 | PublishSubject<(IndexPath, PhotoAssetCollection)> { 40 | return viewModel.outputs.photoCollectionDidSelectWhenCellDidSelect 41 | } 42 | 43 | // MARK: - Inputs 44 | open var cellDidSelect: PublishSubject { 45 | return viewModel.inputs.cellDidSelect 46 | } 47 | 48 | fileprivate var disposeBag = DisposeBag() 49 | 50 | fileprivate var viewModel: PhotoCollectionsViewModelType 51 | 52 | fileprivate var configure: PhotoCollectionsViewConfigure 53 | 54 | public lazy var collectionView: UICollectionView = { [unowned self] in 55 | let cv = UICollectionView( 56 | frame: .zero, 57 | collectionViewLayout: self.configure.layout) 58 | 59 | cv.register(self.configure.photoCollectionCellTypeConverter.cellClass, 60 | forCellWithReuseIdentifier: self.configure.photoCollectionCellTypeConverter.cellIdentifier) 61 | 62 | cv.backgroundColor = .white 63 | cv.dataSource = self 64 | cv.delegate = self 65 | return cv 66 | }() 67 | 68 | 69 | public init(frame: CGRect, configure: PhotoCollectionsViewConfigure) { 70 | self.configure = configure 71 | viewModel = PhotoCollectionsViewModel(configure: configure) 72 | super.init(frame: .zero) 73 | bindViewModel() 74 | } 75 | 76 | public init(configure: PhotoCollectionsViewConfigure) { 77 | self.configure = configure 78 | viewModel = PhotoCollectionsViewModel(configure: configure) 79 | super.init(frame: .zero) 80 | bindViewModel() 81 | } 82 | 83 | required public init?(coder aDecoder: NSCoder) { 84 | fatalError("init(coder:) has not been implemented") 85 | } 86 | 87 | override open func setupViews() { 88 | addSubview(collectionView) 89 | } 90 | 91 | override open func setupConstraints() { 92 | collectionView 93 | .fs_leftAnchor(equalTo: leftAnchor) 94 | .fs_topAnchor(equalTo: topAnchor) 95 | .fs_rightAnchor(equalTo: rightAnchor) 96 | .fs_bottomAnchor(equalTo: bottomAnchor) 97 | .fs_endSetup() 98 | } 99 | 100 | open func bindViewModel() { 101 | viewModel.outputs.cellDidChange 102 | .observeOn(MainScheduler.instance) 103 | .subscribe(onNext: { [weak self] changeEvent in 104 | guard let `self` = self else { return } 105 | switch changeEvent { 106 | case .begin: 107 | self.collectionView.beginUpdates() 108 | case .end: 109 | self.collectionView.endUpdates() 110 | case .reset: 111 | self.collectionView.reloadData() 112 | case .insert(let indexPaths): 113 | self.collectionView.insert(itemsAt: indexPaths) 114 | case .update(let indexPaths): 115 | self.collectionView.reload( 116 | itemsAt: self.collectionView.indexPathsForVisibleItems 117 | .filter { indexPaths.contains($0) }) 118 | case .delete(let indexPaths): 119 | self.collectionView.delete(itemsAt: indexPaths) 120 | case .move(let from, let to): 121 | self.collectionView.moveItem(at: from, to: to) 122 | case .scrollTo(let indexPath): 123 | self.collectionView.scrollToItem( 124 | at: indexPath, 125 | at: .top, 126 | animated: true) 127 | } 128 | }) 129 | .disposed(by: disposeBag) 130 | } 131 | } 132 | 133 | extension PhotoCollectionsView: UICollectionViewDataSource { 134 | open func numberOfSections( 135 | in collectionView: UICollectionView) -> Int { 136 | return viewModel.numberOfSection() 137 | } 138 | 139 | open func collectionView( 140 | _ collectionView: UICollectionView, 141 | numberOfItemsInSection section: Int) -> Int { 142 | return viewModel.numberOfItems() 143 | } 144 | 145 | open func collectionView( 146 | _ collectionView: UICollectionView, 147 | cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 148 | var cell = collectionView.dequeueReusableCell( 149 | withReuseIdentifier: self.configure.photoCollectionCellTypeConverter.cellIdentifier, 150 | for: indexPath) as! PhotoCollectionCellable 151 | cell.viewModel = viewModel.cellViewModel(at: indexPath) 152 | return cell as! UICollectionViewCell 153 | } 154 | } 155 | 156 | extension PhotoCollectionsView: UICollectionViewDelegate { 157 | open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 158 | viewModel.inputs.cellDidSelect.onNext(indexPath) 159 | } 160 | } 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/PhotoCollectionsViewConfigure.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotoCollectionsViewConfigure.swift 3 | // PhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 6. 27.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Photos 11 | 12 | public protocol PhotoCollectionsViewConfigure { 13 | 14 | var fetchOptions: PHFetchOptions { get } 15 | 16 | // to show collection types. 17 | var showsCollectionTypes: [PHAssetCollectionSubtype] { get } 18 | 19 | // size of view to show thumbnailImage in your cell and 20 | // photoCollectionThumbnailSize must be the same 21 | // because get photo collection thumbnail image from PHCachingImageManager 22 | // based on the 'photoCollectionThumbnailSize' 23 | var photoCollectionThumbnailSize: CGSize { get } 24 | 25 | var layout: UICollectionViewFlowLayout { get } 26 | 27 | var photoCollectionCellTypeConverter: PhotoCollectionCellTypeConverter { get } 28 | } 29 | 30 | public struct PhotoCollectionCellTypeConverter: CellTypeConverter { 31 | public typealias Cellable = PhotoCollectionCellable 32 | public var cellIdentifier: String 33 | public var cellClass: AnyClass 34 | 35 | public init(type: C.Type) where C: Cellable { 36 | cellIdentifier = NSStringFromClass(type) 37 | cellClass = type 38 | } 39 | } 40 | 41 | public protocol PhotoCollectionCellable { 42 | var viewModel: PhotoCollectionCellViewModel? { get set } 43 | } 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/PhotoManager+Cache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotoManager+Cache.swift 3 | // ObservableTest 4 | // 5 | // Created by myung gi son on 2017. 6. 6.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import Photos 10 | 11 | // MARK: - Cache 12 | 13 | extension PhotoManager { 14 | 15 | open func startCaching( 16 | assets: [PHAsset], 17 | targetSize: CGSize, 18 | contentMode: PHImageContentMode, 19 | options: PHImageRequestOptions?) { 20 | 21 | imageManager.startCachingImages( 22 | for: assets, 23 | targetSize: targetSize, 24 | contentMode: contentMode, 25 | options: options) 26 | } 27 | 28 | open func stopCaching( 29 | assets: [PHAsset], 30 | targetSize: CGSize, 31 | contentMode: PHImageContentMode, 32 | options: PHImageRequestOptions?){ 33 | 34 | imageManager.stopCachingImages( 35 | for: assets, 36 | targetSize: targetSize, 37 | contentMode: contentMode, 38 | options: options) 39 | } 40 | 41 | open func stopCachingForAllAssets() { 42 | imageManager.stopCachingImagesForAllAssets() 43 | } 44 | 45 | // cancel image request for requestID. 46 | open func cancel(imageRequest requestID: PHImageRequestID) { 47 | imageManager.cancelImageRequest(requestID) 48 | } 49 | } 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/PhotoManager+Change.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotoManager+Change.swift 3 | // ObservableTest 4 | // 5 | // Created by myung gi son on 2017. 6. 6.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import Photos 10 | import RxSwift 11 | 12 | // MARK: - Change 13 | 14 | extension PhotoManager: PHPhotoLibraryChangeObserver { 15 | open func photoLibraryDidChange(_ changeInstance: PHChange) { 16 | photoLibraryChangeEvent.onNext(changeInstance) 17 | } 18 | } 19 | 20 | public enum PerformChangesEvent { 21 | case completion(success: Bool) 22 | } 23 | 24 | extension PhotoManager { 25 | open func performChanges(changeBlock: @escaping () -> Void) -> Observable { 26 | return Observable.create { observer in 27 | PHPhotoLibrary.shared().performChanges({ 28 | changeBlock() 29 | }, completionHandler: { success, error in 30 | if let error = error { 31 | observer.onError(error) 32 | return 33 | } 34 | observer.onNext(.completion(success: success)) 35 | observer.onCompleted() 36 | }) 37 | return Disposables.create() 38 | } 39 | } 40 | 41 | } 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/PhotoManager+Permission.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotoManager+Permission.swift 3 | // ObservableTest 4 | // 5 | // Created by myung gi son on 2017. 6. 6.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import AVFoundation 10 | import Photos 11 | import RxSwift 12 | 13 | 14 | // MARK: - Permission 15 | 16 | extension PhotoManager { 17 | 18 | open func checkPhotoLibraryPermission() -> Observable { 19 | return Observable.create { observer in 20 | if PHPhotoLibrary.authorizationStatus() == .authorized { 21 | observer.onNext(true) 22 | observer.onCompleted() 23 | } 24 | else { 25 | observer.onNext(false) 26 | PHPhotoLibrary.requestAuthorization { newStatus in 27 | observer.onNext(newStatus == .authorized) 28 | observer.onCompleted() 29 | } 30 | } 31 | return Disposables.create() 32 | } 33 | } 34 | 35 | open func checkCameraPermission() -> Observable { 36 | return Observable.create { observer in 37 | let authStatus = 38 | AVCaptureDevice.authorizationStatus(for: AVMediaType.video) 39 | 40 | if authStatus == .authorized { 41 | observer.onNext(true) 42 | observer.onCompleted() 43 | } 44 | else { 45 | observer.onNext(false) 46 | AVCaptureDevice.requestAccess(for: AVMediaType.video) { granted in 47 | observer.onNext(granted) 48 | observer.onCompleted() 49 | } 50 | } 51 | return Disposables.create() 52 | } 53 | } 54 | } 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/PhotoManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AssetManager.swift 3 | // PhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 5. 21.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import Photos 10 | import RxSwift 11 | 12 | 13 | open class PhotoManager: NSObject { 14 | // MARK: - Properties 15 | 16 | open class var shared: PhotoManager { 17 | struct Singleton { 18 | static var sharedInstance = PhotoManager() 19 | } 20 | return Singleton.sharedInstance 21 | } 22 | 23 | open var photoLibraryChangeEvent = 24 | PublishSubject() 25 | 26 | open var imageManager: PHCachingImageManager = PHCachingImageManager() 27 | 28 | open var disposeBag: DisposeBag = DisposeBag() 29 | 30 | // MARK: - Life Cycle 31 | 32 | deinit { 33 | PHPhotoLibrary.shared().unregisterChangeObserver(self) 34 | } 35 | 36 | override public init() { 37 | super.init() 38 | PHPhotoLibrary.shared().register(self) 39 | } 40 | } 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/PhotosViewConfigure.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotosViewConfigure.swift 3 | // PhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 6. 25.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Photos 11 | import PhotosUI 12 | import UIKit 13 | 14 | public protocol PhotosViewConfigure { 15 | 16 | var fetchOptions: PHFetchOptions { get } 17 | 18 | var allowsMultipleSelection: Bool { get } 19 | 20 | var allowsCameraSelection: Bool { get } 21 | 22 | // .video, .livePhoto 23 | var allowsPlayTypes: [AssetType] { get } 24 | 25 | var messageWhenMaxCountSelectedPhotosIsExceeded: String { get } 26 | 27 | var maxCountSelectedPhotos: Int { get } 28 | 29 | // get item image from PHCachingImageManager 30 | // based on the UICollectionViewFlowLayout`s itemSize, 31 | // therefore must set well itemSize in UICollectionViewFlowLayout. 32 | var layout: UICollectionViewFlowLayout { get } 33 | 34 | var photoCellTypeConverter: PhotoCellTypeConverter { get } 35 | 36 | var livePhotoCellTypeConverter: LivePhotoCellTypeConverter { get } 37 | 38 | var videoCellTypeConverter: VideoCellTypeConverter { get } 39 | 40 | var cameraCellTypeConverter: CameraCellTypeConverter { get } 41 | } 42 | 43 | public protocol CellTypeConverter { 44 | associatedtype Cellable 45 | 46 | var cellIdentifier: String { get } 47 | var cellClass: AnyClass { get } 48 | } 49 | 50 | public struct PhotoCellTypeConverter: CellTypeConverter { 51 | public typealias Cellable = PhotoCellable 52 | public var cellIdentifier: String 53 | public var cellClass: AnyClass 54 | 55 | public init(type: C.Type) where C: Cellable { 56 | cellIdentifier = NSStringFromClass(type) 57 | cellClass = type 58 | } 59 | } 60 | 61 | public struct LivePhotoCellTypeConverter: CellTypeConverter { 62 | public typealias Cellable = LivePhotoCellable 63 | public var cellIdentifier: String 64 | public var cellClass: AnyClass 65 | 66 | public init(type: C.Type) where C: Cellable { 67 | cellIdentifier = NSStringFromClass(type) 68 | cellClass = type 69 | } 70 | } 71 | 72 | public struct VideoCellTypeConverter: CellTypeConverter { 73 | public typealias Cellable = VideoCellable 74 | public var cellIdentifier: String 75 | public var cellClass: AnyClass 76 | 77 | public init(type: C.Type) where C: Cellable { 78 | cellIdentifier = NSStringFromClass(type) 79 | cellClass = type 80 | } 81 | } 82 | 83 | public struct CameraCellTypeConverter: CellTypeConverter { 84 | public typealias Cellable = CameraCellable 85 | public var cellIdentifier: String 86 | public var cellClass: AnyClass 87 | 88 | public init(type: C.Type) where C: Cellable { 89 | cellIdentifier = NSStringFromClass(type) 90 | cellClass = type 91 | } 92 | } 93 | 94 | public protocol CameraCellable: class { } 95 | 96 | public protocol PhotoCellable: class { 97 | var viewModel: PhotoCellViewModel? { get set } 98 | } 99 | 100 | public protocol LivePhotoCellable: PhotoCellable { } 101 | 102 | public protocol VideoCellable: PhotoCellable { } 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/UIAlertViewController+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIAlertViewController+Extension.swift 3 | // KaKaoChatInputView 4 | // 5 | // Created by myung gi son on 2017. 5. 27.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIAlertController { 12 | class func topViewController(_ base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? { 13 | if let nav = base as? UINavigationController { 14 | return topViewController(nav.visibleViewController) 15 | } 16 | 17 | if let tab = base as? UITabBarController { 18 | if let selected = tab.selectedViewController { 19 | return topViewController(selected) 20 | } 21 | } 22 | 23 | if let presented = base?.presentedViewController { 24 | return topViewController(presented) 25 | } 26 | 27 | return base 28 | } 29 | 30 | class func show(title: String, message: String) { 31 | let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) 32 | 33 | let action = UIAlertAction(title: "확인", style: .default, handler: nil) 34 | alertController.addAction(action) 35 | 36 | topViewController()?.present(alertController, animated: true, completion: nil) 37 | } 38 | 39 | class func show(_ mainTitle: String, message: String, cancelTitle: String, confirmTitle: String, _ handleComfirm: @escaping (UIAlertAction) -> Void, _ handleCancel: @escaping (UIAlertAction) -> Void) { 40 | let alertController = UIAlertController(title: mainTitle, message: message, preferredStyle: .alert) 41 | 42 | let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel, handler: handleCancel) 43 | 44 | let comfirmAction = UIAlertAction(title: confirmTitle, style: .destructive, handler: handleComfirm) 45 | 46 | alertController.addAction(cancelAction) 47 | alertController.addAction(comfirmAction) 48 | 49 | topViewController()?.present(alertController, animated: true, completion: nil) 50 | } 51 | 52 | class func show(_ itemTitles: [String], itemClosures: [((UIAlertAction) -> Void)], cancelTitle: String, handleCancel: @escaping (UIAlertAction) -> Void) { 53 | let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) 54 | 55 | for i in 0 ..< itemTitles.count { 56 | let action = UIAlertAction(title: itemTitles[i], 57 | style: .default, 58 | handler: itemClosures[i]) 59 | alertController.addAction(action) 60 | } 61 | 62 | let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel, handler: handleCancel) 63 | 64 | alertController.addAction(cancelAction) 65 | topViewController()?.present(alertController, animated: true, completion: nil) 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/UICollection+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UICollection+Extension.swift 3 | // KaKaoChatInputView 4 | // 5 | // Created by myung gi son on 2017. 5. 26.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public enum CellChangeEvent { 12 | case scrollTo(IndexPath) 13 | case insert([IndexPath]) 14 | case update([IndexPath]) 15 | case delete([IndexPath]) 16 | case move(from:IndexPath, to: IndexPath) 17 | case reset 18 | case begin 19 | case end 20 | } 21 | 22 | 23 | extension UICollectionView { 24 | public func indexPathsForElements(in rect: CGRect) -> [IndexPath] { 25 | let allLayoutAttributes = collectionViewLayout.layoutAttributesForElements(in: rect)! 26 | return allLayoutAttributes.map { $0.indexPath } 27 | } 28 | } 29 | 30 | extension UICollectionView 31 | { 32 | fileprivate class SettingBatchContext 33 | { 34 | typealias BlockToExecuteBatch = () -> Void 35 | var isToExecuteBatch: Bool = false 36 | var blocksToExecuteBatch: [BlockToExecuteBatch] = [] 37 | } 38 | 39 | fileprivate struct StaticVariables 40 | { 41 | static var settingBatchContextKey = "settingBatchContextKey" 42 | } 43 | 44 | fileprivate var settingBatchContext: SettingBatchContext 45 | { 46 | if let sbc = objc_getAssociatedObject(self, &StaticVariables.settingBatchContextKey) as? SettingBatchContext 47 | { 48 | return sbc 49 | } 50 | else 51 | { 52 | let sbc: SettingBatchContext = SettingBatchContext() 53 | objc_setAssociatedObject(self, 54 | &StaticVariables.settingBatchContextKey, 55 | sbc, 56 | objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) 57 | return sbc 58 | } 59 | } 60 | 61 | public func beginUpdates() 62 | { 63 | settingBatchContext.isToExecuteBatch = true 64 | } 65 | 66 | public func endUpdates() 67 | { 68 | performBatchUpdates({ [weak self] in 69 | guard let weakSelf = self else { return } 70 | weakSelf.settingBatchContext.blocksToExecuteBatch.forEach({ $0() }) 71 | }, completion: { [weak self] (_) in 72 | guard let weakSelf = self else { return } 73 | weakSelf.settingBatchContext.isToExecuteBatch = false 74 | weakSelf.settingBatchContext.blocksToExecuteBatch.removeAll() 75 | }) 76 | } 77 | 78 | public func reload(itemsAt: [IndexPath]) 79 | { 80 | if settingBatchContext.isToExecuteBatch 81 | { 82 | settingBatchContext.blocksToExecuteBatch.append({ [weak self] in 83 | guard let weakSelf = self else { return } 84 | weakSelf.reloadItems(at: itemsAt) 85 | }) 86 | } 87 | else 88 | { 89 | reloadItems(at: itemsAt) 90 | } 91 | } 92 | 93 | public func insert(itemsAt: [IndexPath]) 94 | { 95 | if settingBatchContext.isToExecuteBatch 96 | { 97 | settingBatchContext.blocksToExecuteBatch.append({ [weak self] in 98 | guard let weakSelf = self else { return } 99 | weakSelf.insertItems(at: itemsAt) 100 | }) 101 | } 102 | else 103 | { 104 | insertItems(at: itemsAt) 105 | } 106 | } 107 | 108 | public func delete(itemsAt: [IndexPath]) 109 | { 110 | if settingBatchContext.isToExecuteBatch 111 | { 112 | settingBatchContext.blocksToExecuteBatch.append({ [weak self] in 113 | guard let weakSelf = self else { return } 114 | weakSelf.deleteItems(at: itemsAt) 115 | }) 116 | } 117 | else 118 | { 119 | deleteItems(at: itemsAt) 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/UIView+Animation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Animation.swift 3 | // UIViewAnimationSyntaxSugar 4 | // 5 | // Created by myung gi son on 2017. 5. 17.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // MARK: - Animatoir (Basic) 12 | 13 | extension UIView { 14 | 15 | public class Animator { 16 | public typealias AnimationsBlock = () -> Void 17 | public typealias CompletionBlock = (Bool) -> Void 18 | public typealias BlockBeforeExcutingAnimations = () -> Void 19 | 20 | fileprivate var _beforeAnimations: BlockBeforeExcutingAnimations 21 | fileprivate var _animations: AnimationsBlock 22 | fileprivate var _completion: CompletionBlock? 23 | fileprivate var _duration: TimeInterval 24 | fileprivate var _delay: TimeInterval 25 | fileprivate var _options: UIView.AnimationOptions 26 | 27 | public init( 28 | duration: TimeInterval, 29 | delay: TimeInterval = 0.0, 30 | options: UIView.AnimationOptions = []) { 31 | 32 | _duration = duration 33 | _delay = delay 34 | _options = options 35 | _beforeAnimations = {} 36 | _animations = {} 37 | _completion = nil 38 | } 39 | 40 | public func delay(_ delay: TimeInterval) -> Self { 41 | _delay = delay 42 | return self 43 | } 44 | 45 | public func options(_ options: UIView.AnimationOptions) -> Self { 46 | _options = options 47 | return self 48 | } 49 | 50 | public func beforeAnimations(_ beforeAnimations: @escaping BlockBeforeExcutingAnimations) -> Self { 51 | _beforeAnimations = beforeAnimations 52 | return self 53 | } 54 | 55 | public func animations(_ animations: @escaping AnimationsBlock) -> Self { 56 | _animations = animations 57 | return self 58 | } 59 | 60 | public func completion(_ completion: @escaping CompletionBlock) -> Self { 61 | _completion = completion 62 | return self 63 | } 64 | 65 | public func animate() { 66 | _beforeAnimations() 67 | UIView.animate( 68 | withDuration: _duration, 69 | delay: _delay, 70 | options: _options, 71 | animations: _animations, 72 | completion: _completion) 73 | } 74 | } 75 | } 76 | 77 | 78 | // MARK: - SpringAnimator 79 | 80 | extension UIView { 81 | 82 | public class SpringAnimator: Animator { 83 | fileprivate var _damping: CGFloat 84 | fileprivate var _velocity: CGFloat 85 | 86 | public init( 87 | duration: TimeInterval, 88 | delay: TimeInterval = 0.0, 89 | damping: CGFloat = 0.1, 90 | velocity: CGFloat = 0.1, 91 | options: UIView.AnimationOptions = []) { 92 | 93 | _damping = damping 94 | _velocity = velocity 95 | 96 | super.init(duration: duration, delay: delay, options: options) 97 | } 98 | 99 | public func damping(_ damping: CGFloat) -> Self { 100 | _damping = damping 101 | return self 102 | } 103 | 104 | public func velocity(_ velocity: CGFloat) -> Self { 105 | _velocity = velocity 106 | return self 107 | } 108 | 109 | override public func animate() { 110 | _beforeAnimations() 111 | UIView.animate( 112 | withDuration: _duration, 113 | delay: _delay, 114 | usingSpringWithDamping: _damping, 115 | initialSpringVelocity: _velocity, 116 | options: _options, 117 | animations: _animations, 118 | completion: _completion) 119 | } 120 | } 121 | } 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /EasyMakePhotoPicker/Classes/VideoCellViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VideoCellViewModel.swift 3 | // KaKaoChatInputView 4 | // 5 | // Created by myung gi son on 2017. 5. 29.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Photos 11 | import RxSwift 12 | 13 | public class VideoCellViewModel: PhotoCellViewModel { 14 | 15 | public var playerItem: AVPlayerItem? { 16 | return photoAsset.playerItem 17 | } 18 | 19 | public var playEvent = PublishSubject() 20 | 21 | public var duration: TimeInterval { 22 | return self.photoAsset.asset.duration 23 | } 24 | } 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker.xcodeproj/xcshareddata/xcschemes/EasyMakePhotoPicker-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 99 | 105 | 106 | 107 | 108 | 110 | 111 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by audrl1010 on 06/29/2017. 6 | // Copyright (c) 2017 audrl1010. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application( 17 | _ application: UIApplication, 18 | didFinishLaunchingWithOptions 19 | launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 20 | window = UIWindow(frame: UIScreen.main.bounds) 21 | window?.rootViewController = MainVC() 22 | window?.makeKeyAndVisible() 23 | return true 24 | } 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Base+View+VC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Base+View+VC.swift 3 | // KaKaoChatInputView 4 | // 5 | // Created by smg on 2017. 5. 13.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class BaseView: UIView { 12 | 13 | // MARK: - View Life Cycle 14 | 15 | override public init(frame: CGRect) { 16 | super.init(frame: frame) 17 | setupViews() 18 | addSubviews() 19 | setupConstraints() 20 | } 21 | 22 | required public init?(coder aDecoder: NSCoder) { 23 | super.init(coder: aDecoder) 24 | setupViews() 25 | addSubviews() 26 | setupConstraints() 27 | } 28 | 29 | // MARK: - Public Methods 30 | 31 | open func setupViews() { } 32 | open func addSubviews() { } 33 | open func setupConstraints() { } 34 | } 35 | 36 | 37 | 38 | open class BaseCollectionViewCell: UICollectionViewCell { 39 | 40 | // MARK: - Properties 41 | 42 | open class var cellIdentifier: String { return "\(self)" } 43 | 44 | // MARK: - View Life Cycle 45 | 46 | override public init(frame: CGRect) { 47 | super.init(frame: frame) 48 | setupViews() 49 | addSubviews() 50 | setupConstraints() 51 | } 52 | 53 | required public init?(coder aDecoder: NSCoder) { 54 | super.init(coder: aDecoder) 55 | setupViews() 56 | addSubviews() 57 | setupConstraints() 58 | } 59 | 60 | // MARK: - Public Methods 61 | 62 | open func setupViews() { } 63 | open func addSubviews() { } 64 | open func setupConstraints() { } 65 | } 66 | 67 | 68 | open class BaseTableViewCell: UITableViewCell { 69 | 70 | // MARK: - Properties 71 | 72 | open class var cellIdentifier: String { return "\(self)" } 73 | 74 | // MARK: - View Life Cycle 75 | 76 | override public init( 77 | style: UITableViewCell.CellStyle, 78 | reuseIdentifier: String?) { 79 | 80 | super.init(style: style, reuseIdentifier: reuseIdentifier) 81 | setupViews() 82 | addSubviews() 83 | setupConstraints() 84 | } 85 | 86 | required public init?(coder aDecoder: NSCoder) { 87 | super.init(coder: aDecoder) 88 | setupViews() 89 | addSubviews() 90 | setupConstraints() 91 | } 92 | 93 | // MARK: - Public Methods 94 | 95 | open func setupViews() { } 96 | open func addSubviews() { } 97 | open func setupConstraints() { } 98 | } 99 | 100 | open class BaseVC: UIViewController { 101 | 102 | // MARK: - View Life Cycle 103 | override open func viewDidLoad() { 104 | super.viewDidLoad() 105 | setupViews() 106 | addSubviews() 107 | setupConstraints() 108 | } 109 | 110 | // MARK: - Public Methods 111 | open func setupViews() { } 112 | open func addSubviews() { } 113 | open func setupConstraints() { } 114 | } 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/CheckImageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CheckImageView.swift 3 | // KaKaoChatInputView 4 | // 5 | // Created by myung gi son on 2017. 5. 27.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class CheckImageView: BaseView { 12 | 13 | struct Color { 14 | static let bgColor = UIColor( 15 | red: 132/255, 16 | green: 132/255, 17 | blue: 132/255, 18 | alpha: 0.6) 19 | 20 | static let checkColor = UIColor.white 21 | } 22 | 23 | struct Constant { 24 | static let lineWidth = CGFloat(2.5) 25 | } 26 | 27 | var bgColor: UIColor = Color.bgColor { 28 | didSet { 29 | setNeedsLayout() 30 | } 31 | } 32 | 33 | var lineWidth: CGFloat = Constant.lineWidth { 34 | didSet { 35 | setNeedsLayout() 36 | } 37 | } 38 | 39 | var checkColor: UIColor = Color.checkColor { 40 | didSet { 41 | setNeedsLayout() 42 | } 43 | } 44 | 45 | override func setupViews() { 46 | super.setupViews() 47 | backgroundColor = Color.bgColor 48 | clipsToBounds = true 49 | } 50 | 51 | override func layoutSubviews() { 52 | super.layoutSubviews() 53 | layer.cornerRadius = bounds.height / 2 54 | } 55 | 56 | override func draw(_ rect: CGRect) { 57 | super.draw(rect) 58 | 59 | let path = UIBezierPath() 60 | path.lineJoinStyle = .round 61 | path.lineCapStyle = .round 62 | path.lineWidth = lineWidth 63 | 64 | checkColor.setStroke() 65 | 66 | path.move( 67 | to: CGPoint(x: rect.midX - 5, y: rect.midY)) 68 | 69 | path.addLine( 70 | to: CGPoint(x: rect.midX - 1, y: rect.midY + 4)) 71 | 72 | path.addLine( 73 | to: CGPoint(x: rect.midX + 6, y: rect.midY - 4)) 74 | 75 | path.stroke() 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/DurationLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DurationLabel.swift 3 | // KaKaoChatInputView 4 | // 5 | // Created by myung gi son on 2017. 5. 30.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | class DurationLabel: UILabel { 13 | 14 | struct Constant { 15 | static let padding = UIEdgeInsets(top: 5, left: 3, bottom: 5, right: 3) 16 | } 17 | 18 | struct Color { 19 | static let bgColor = UIColor( 20 | red: 0/255, 21 | green: 0/255, 22 | blue: 0/255, 23 | alpha: 0.35) 24 | 25 | static let durationLabelTextColor = UIColor.white 26 | } 27 | 28 | struct Font { 29 | static let durationLabelFont = UIFont.systemFont(ofSize: 14) 30 | } 31 | 32 | override init(frame: CGRect) { 33 | super.init(frame: frame) 34 | commonInit() 35 | } 36 | 37 | required init?(coder aDecoder: NSCoder) { 38 | super.init(coder: aDecoder) 39 | commonInit() 40 | } 41 | 42 | func commonInit() { 43 | backgroundColor = Color.bgColor 44 | textColor = Color.durationLabelTextColor 45 | font = Font.durationLabelFont 46 | text = "00:00" 47 | } 48 | 49 | override func drawText(in rect: CGRect) { 50 | super.drawText(in: rect.inset(by: Constant.padding)) 51 | } 52 | 53 | override var intrinsicContentSize: CGSize { 54 | let size = super.intrinsicContentSize 55 | return CGSize( 56 | width: size.width + Constant.padding.left + Constant.padding.right, 57 | height: size.height + Constant.padding.top + Constant.padding.bottom) 58 | } 59 | } 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/FacebookCameraCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FacebookCameraCell.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 6.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import RxSwift 10 | import Photos 11 | import PhotosUI 12 | import EasyMakePhotoPicker 13 | 14 | class FacebookCameraCell: BaseCollectionViewCell, CameraCellable { 15 | var cameraIcon: UIImage { 16 | return #imageLiteral(resourceName: "camera") 17 | } 18 | 19 | var bgColor: UIColor { 20 | return .white 21 | } 22 | 23 | var imageView = UIImageView().then { 24 | $0.contentMode = .scaleAspectFill 25 | $0.clipsToBounds = true 26 | } 27 | 28 | override func layoutSubviews() { 29 | super.layoutSubviews() 30 | imageView.image = centerAtRect( 31 | image: cameraIcon, 32 | rect: frame, 33 | bgColor: bgColor) 34 | } 35 | 36 | fileprivate func centerAtRect( 37 | image: UIImage?, 38 | rect: CGRect, 39 | bgColor: UIColor) -> UIImage? { 40 | guard let image = image else { return nil } 41 | 42 | UIGraphicsBeginImageContextWithOptions(rect.size, false, image.scale) 43 | bgColor.setFill() 44 | UIRectFill( CGRect(x: 0, y: 0, width: rect.size.width, height: rect.size.height)) 45 | 46 | image.draw(in: 47 | CGRect( 48 | x:rect.size.width/2 - image.size.width/2, 49 | y:rect.size.height/2 - image.size.height/2, 50 | width: image.size.width, 51 | height: image.size.height)) 52 | 53 | let result = UIGraphicsGetImageFromCurrentImageContext() 54 | UIGraphicsEndImageContext() 55 | return result 56 | } 57 | 58 | override func addSubviews() { 59 | addSubview(imageView) 60 | } 61 | 62 | override func setupConstraints() { 63 | imageView 64 | .fs_leftAnchor(equalTo: leftAnchor) 65 | .fs_topAnchor(equalTo: topAnchor) 66 | .fs_rightAnchor(equalTo: rightAnchor) 67 | .fs_bottomAnchor(equalTo: bottomAnchor) 68 | .fs_endSetup() 69 | } 70 | } 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/FacebookLivePhotoCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FacebookLivePhotoCell.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 6.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Photos 11 | import PhotosUI 12 | import RxSwift 13 | import EasyMakePhotoPicker 14 | 15 | class FacebookLivePhotoCell: FacebookPhotoCell, LivePhotoCellable { 16 | struct Metric { 17 | static let livePhotoBadgeImageViewWidth = CGFloat(20) 18 | static let livePhotoBadgeImageViewHeight = CGFloat(20) 19 | static let livePhotoBadgeImageViewRight = CGFloat(-10) 20 | static let livePhotoBadgeImageViewBottom = CGFloat(-10) 21 | } 22 | 23 | lazy var livePhotoView: PHLivePhotoView = { [unowned self] in 24 | let lpv = PHLivePhotoView() 25 | lpv.delegate = self 26 | lpv.isHidden = true 27 | return lpv 28 | }() 29 | 30 | var livePhotoBadgeImageView = UIImageView().then { 31 | $0.contentMode = .scaleAspectFit 32 | } 33 | 34 | // MARK: - Life Cycle 35 | 36 | override func prepareForReuse() { 37 | super.prepareForReuse() 38 | livePhotoBadgeImageView.image = nil 39 | livePhotoView.isHidden = true 40 | } 41 | 42 | override func addSubviews() { 43 | super.addSubviews() 44 | insertSubview(livePhotoView, aboveSubview: imageView) 45 | addSubview(livePhotoBadgeImageView) 46 | } 47 | 48 | override func setupConstraints() { 49 | super.setupConstraints() 50 | livePhotoBadgeImageView 51 | .fs_widthAnchor( 52 | equalToConstant: Metric.livePhotoBadgeImageViewWidth) 53 | .fs_heightAnchor( 54 | equalToConstant: Metric.livePhotoBadgeImageViewHeight) 55 | .fs_rightAnchor( 56 | equalTo: rightAnchor, 57 | constant: Metric.livePhotoBadgeImageViewRight) 58 | .fs_bottomAnchor( 59 | equalTo: bottomAnchor, 60 | constant: Metric.livePhotoBadgeImageViewBottom) 61 | .fs_endSetup() 62 | 63 | livePhotoView 64 | .fs_leftAnchor(equalTo: leftAnchor) 65 | .fs_topAnchor(equalTo: topAnchor) 66 | .fs_rightAnchor(equalTo: rightAnchor) 67 | .fs_bottomAnchor(equalTo: bottomAnchor) 68 | .fs_endSetup() 69 | } 70 | 71 | // MARK: - Bind 72 | 73 | override func bind(viewModel: PhotoCellViewModel) { 74 | super.bind(viewModel: viewModel) 75 | if let viewModel = viewModel as? LivePhotoCellViewModel { 76 | livePhotoBadgeImageView.image = viewModel.badgeImage 77 | viewModel.playEvent.asObserver() 78 | .subscribe(onNext: { [weak self] playEvent in 79 | guard let `self` = self else { return } 80 | switch playEvent { 81 | case .play: self.play() 82 | case .stop: self.stop() 83 | } 84 | }) 85 | .disposed(by: disposeBag) 86 | } 87 | } 88 | 89 | fileprivate func play() { 90 | guard let viewModel = viewModel as? LivePhotoCellViewModel, 91 | let livePhoto = viewModel.livePhoto else { return } 92 | 93 | livePhotoView.startPlayback(with: .hint) 94 | livePhotoView.isHidden = false 95 | livePhotoView.livePhoto = livePhoto 96 | } 97 | 98 | fileprivate func stop() { 99 | self.livePhotoView.stopPlayback() 100 | self.livePhotoView.isHidden = true 101 | self.livePhotoView.livePhoto = nil 102 | } 103 | } 104 | 105 | // MARK: - PHLivePhotoViewDelegate 106 | extension FacebookLivePhotoCell: PHLivePhotoViewDelegate { 107 | public func livePhotoView( 108 | _ livePhotoView: PHLivePhotoView, 109 | willBeginPlaybackWith playbackStyle: PHLivePhotoViewPlaybackStyle) { 110 | } 111 | 112 | public func livePhotoView( 113 | _ livePhotoView: PHLivePhotoView, 114 | didEndPlaybackWith playbackStyle: PHLivePhotoViewPlaybackStyle) { 115 | livePhotoView.startPlayback(with: .hint) 116 | } 117 | } 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/FacebookNumberLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FacebookNumberLabel.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 6.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Photos 11 | import RxSwift 12 | 13 | class FacebookNumberLabel: UILabel { 14 | 15 | struct Constant { 16 | static let cornerRadius = CGFloat(5) 17 | } 18 | 19 | struct Color { 20 | static let labelTextColor = UIColor.white 21 | static let backgroundColor = UIColor( 22 | red: 104/255, 23 | green: 156/255, 24 | blue: 255/255, 25 | alpha: 1.0) 26 | } 27 | 28 | struct Font { 29 | static let labelFont = UIFont.systemFont(ofSize: 14) 30 | } 31 | 32 | override init(frame: CGRect) { 33 | super.init(frame: frame) 34 | commonInit() 35 | } 36 | 37 | required init?(coder aDecoder: NSCoder) { 38 | super.init(coder: aDecoder) 39 | commonInit() 40 | } 41 | 42 | func commonInit() { 43 | font = Font.labelFont 44 | textColor = Color.labelTextColor 45 | backgroundColor = Color.backgroundColor 46 | textAlignment = .center 47 | clipsToBounds = true 48 | } 49 | 50 | override func layoutSubviews() { 51 | super.layoutSubviews() 52 | layer.cornerRadius = Constant.cornerRadius 53 | layer.masksToBounds = true 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/FacebookPermissionVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FacebookPermissionVC.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 7.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | import RxCocoa 12 | 13 | class FacebookPermissionVC: BaseVC { 14 | struct Constant { 15 | static let titleLabelText = "Please Allow Photo Access" 16 | static let contentLabelText = "This allows you to share photos from your library and save photos to your camera roll." 17 | static let enumerationOneLabelText = "1. Tap Privacy" 18 | static let enumerationSecondLabelText = "2. Switch Photos On" 19 | static let allowAccessButtonText = "Allow Access" 20 | } 21 | 22 | struct Color { 23 | static let containerViewBGColor = UIColor.white 24 | static let titleLabelColor = UIColor( 25 | red: 114/255, 26 | green: 116/255, 27 | blue: 130/255, 28 | alpha: 1.0) 29 | static let contentLabelColor = UIColor( 30 | red: 114/255, 31 | green: 116/255, 32 | blue: 130/255, 33 | alpha: 1.0) 34 | static let enumerationOneLabelColor = UIColor( 35 | red: 114/255, 36 | green: 116/255, 37 | blue: 130/255, 38 | alpha: 1.0) 39 | static let enumerationSecondLabelColor = UIColor( 40 | red: 114/255, 41 | green: 116/255, 42 | blue: 130/255, 43 | alpha: 1.0) 44 | } 45 | 46 | struct Font { 47 | static let titleLabelFont = UIFont.boldSystemFont(ofSize: 18) 48 | static let contentLabelFont = UIFont.systemFont(ofSize: 14) 49 | static let enumerationOneLabelFont = UIFont.boldSystemFont(ofSize: 16) 50 | static let enumerationSecondLabelFont = UIFont.boldSystemFont(ofSize: 16) 51 | static let allowAccessButtonFont = UIFont.boldSystemFont(ofSize: 16) 52 | } 53 | 54 | struct Metric { 55 | static let containerViewLeft = CGFloat(25) 56 | static let containerViewTop = CGFloat(100) 57 | static let containerViewRight = CGFloat(-25) 58 | static let containerViewBottom = CGFloat(50) 59 | 60 | static let titleLabelTop = CGFloat(5) 61 | 62 | static let contentLabelTop = CGFloat(10) 63 | 64 | static let enumerationOneLabelLabelTop = CGFloat(20) 65 | 66 | static let enumerationSecondLabelTop = CGFloat(10) 67 | 68 | static let allowAccessButtonTop = CGFloat(35) 69 | } 70 | 71 | var containerView = UIView().then { 72 | $0.backgroundColor = Color.containerViewBGColor 73 | } 74 | 75 | var titleLabel = UILabel().then { 76 | $0.textColor = Color.titleLabelColor 77 | $0.font = Font.titleLabelFont 78 | $0.textAlignment = .center 79 | $0.text = Constant.titleLabelText 80 | } 81 | 82 | var contentLabel = UILabel().then { 83 | $0.textColor = Color.contentLabelColor 84 | $0.font = Font.contentLabelFont 85 | $0.text = Constant.contentLabelText 86 | $0.textAlignment = .center 87 | $0.numberOfLines = 0 88 | } 89 | 90 | var enumerationOneLabel = UILabel().then { 91 | $0.textColor = Color.enumerationOneLabelColor 92 | $0.font = Font.enumerationOneLabelFont 93 | $0.text = Constant.enumerationOneLabelText 94 | } 95 | 96 | var enumerationSecondLabel = UILabel().then { 97 | $0.textColor = Color.enumerationSecondLabelColor 98 | $0.font = Font.enumerationSecondLabelFont 99 | $0.text = Constant.enumerationSecondLabelText 100 | } 101 | 102 | var allowAccessButton = UIButton(type: .system).then { 103 | $0.setTitle(Constant.allowAccessButtonText, for: .normal) 104 | $0.titleLabel?.font = Font.allowAccessButtonFont 105 | } 106 | 107 | var doneButton = UIBarButtonItem( 108 | barButtonSystemItem: .done, 109 | target: nil, 110 | action: nil) 111 | 112 | var disposeBag = DisposeBag() 113 | 114 | override func setupViews() { 115 | navigationItem.rightBarButtonItem = doneButton 116 | view.backgroundColor = .white 117 | view.addSubview(containerView) 118 | containerView.addSubview(titleLabel) 119 | containerView.addSubview(contentLabel) 120 | containerView.addSubview(enumerationOneLabel) 121 | containerView.addSubview(enumerationSecondLabel) 122 | containerView.addSubview(allowAccessButton) 123 | bind() 124 | } 125 | 126 | override func setupConstraints() { 127 | containerView 128 | .fs_leftAnchor( 129 | equalTo: view.leftAnchor, 130 | constant: Metric.containerViewLeft) 131 | .fs_topAnchor( 132 | equalTo: topLayoutGuide.bottomAnchor, 133 | constant: Metric.containerViewTop) 134 | .fs_rightAnchor( 135 | equalTo: view.rightAnchor, 136 | constant: Metric.containerViewRight) 137 | .fs_bottomAnchor( 138 | equalTo: view.bottomAnchor, 139 | constant: Metric.containerViewBottom) 140 | .fs_endSetup() 141 | 142 | titleLabel 143 | .fs_topAnchor( 144 | equalTo: containerView.topAnchor, 145 | constant: Metric.titleLabelTop) 146 | .fs_centerXAnchor(equalTo: containerView.centerXAnchor) 147 | .fs_endSetup() 148 | 149 | contentLabel 150 | .fs_topAnchor( 151 | equalTo: titleLabel.bottomAnchor, 152 | constant: Metric.contentLabelTop) 153 | .fs_leftAnchor(equalTo: containerView.leftAnchor) 154 | .fs_rightAnchor(equalTo: containerView.rightAnchor) 155 | .fs_endSetup() 156 | 157 | enumerationOneLabel 158 | .fs_topAnchor( 159 | equalTo: contentLabel.bottomAnchor, 160 | constant: Metric.enumerationOneLabelLabelTop) 161 | .fs_centerXAnchor(equalTo: containerView.centerXAnchor) 162 | .fs_endSetup() 163 | 164 | enumerationSecondLabel 165 | .fs_topAnchor( 166 | equalTo: enumerationOneLabel.bottomAnchor, 167 | constant: Metric.enumerationSecondLabelTop) 168 | .fs_centerXAnchor(equalTo: containerView.centerXAnchor) 169 | .fs_endSetup() 170 | 171 | allowAccessButton 172 | .fs_topAnchor( 173 | equalTo: enumerationSecondLabel.bottomAnchor, 174 | constant: Metric.allowAccessButtonTop) 175 | .fs_centerXAnchor(equalTo: containerView.centerXAnchor) 176 | .fs_endSetup() 177 | } 178 | 179 | func bind() { 180 | doneButton.rx.tap 181 | .observeOn(MainScheduler.instance) 182 | .subscribe(onNext: { [weak self] _ in 183 | guard let `self` = self else { return } 184 | self.dismiss(animated: true, completion: nil) 185 | }) 186 | .disposed(by: disposeBag) 187 | 188 | allowAccessButton.rx.controlEvent(.touchUpInside) 189 | .subscribe(onNext: { 190 | UIApplication.shared 191 | .openURL(URL(string: UIApplication.openSettingsURLString)!) 192 | }) 193 | .disposed(by: disposeBag) 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/FacebookPhotoCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FacebookPhotoCell.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 6.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Photos 11 | import RxSwift 12 | import EasyMakePhotoPicker 13 | 14 | class FacebookPhotoCell: BaseCollectionViewCell, PhotoCellable { 15 | 16 | // MARK: - Constant 17 | 18 | struct Constant { 19 | static let selectedViewBorderWidth = CGFloat(5) 20 | } 21 | 22 | struct Color { 23 | static let selectedViewBorderColor = UIColor( 24 | red: 104/255, 25 | green: 156/255, 26 | blue: 255/255, 27 | alpha: 1.0) 28 | } 29 | 30 | struct Metric { 31 | static let orderLabelWidth = CGFloat(30) 32 | static let orderLabelHeight = CGFloat(30) 33 | } 34 | 35 | // MARK: - Properties 36 | 37 | var selectedView = UIView().then { 38 | $0.layer.borderWidth = Constant.selectedViewBorderWidth 39 | $0.layer.borderColor = Color.selectedViewBorderColor.cgColor 40 | $0.isHidden = true 41 | } 42 | 43 | var orderLabel = FacebookNumberLabel().then { 44 | $0.clipsToBounds = false 45 | $0.isHidden = true 46 | } 47 | 48 | var imageView = UIImageView().then { 49 | $0.contentMode = .scaleAspectFill 50 | $0.clipsToBounds = true 51 | } 52 | 53 | var disposeBag: DisposeBag = DisposeBag() 54 | 55 | var viewModel: PhotoCellViewModel? { 56 | didSet { 57 | guard let viewModel = viewModel else { return } 58 | bind(viewModel: viewModel) 59 | } 60 | } 61 | 62 | // MARK: - Life Cycle 63 | 64 | override func prepareForReuse() { 65 | super.prepareForReuse() 66 | disposeBag = DisposeBag() 67 | viewModel = nil 68 | imageView.image = nil 69 | orderLabel.text = nil 70 | orderLabel.isHidden = true 71 | selectedView.isHidden = true 72 | } 73 | 74 | override func addSubviews() { 75 | addSubview(imageView) 76 | addSubview(selectedView) 77 | addSubview(orderLabel) 78 | } 79 | 80 | override func setupConstraints() { 81 | imageView 82 | .fs_leftAnchor(equalTo: leftAnchor) 83 | .fs_topAnchor(equalTo: topAnchor) 84 | .fs_rightAnchor(equalTo: rightAnchor) 85 | .fs_bottomAnchor(equalTo: bottomAnchor) 86 | .fs_endSetup() 87 | 88 | selectedView 89 | .fs_leftAnchor(equalTo: leftAnchor) 90 | .fs_topAnchor(equalTo: topAnchor) 91 | .fs_rightAnchor(equalTo: rightAnchor) 92 | .fs_bottomAnchor(equalTo: bottomAnchor) 93 | .fs_endSetup() 94 | 95 | orderLabel 96 | .fs_widthAnchor( 97 | equalToConstant: Metric.orderLabelWidth) 98 | .fs_heightAnchor( 99 | equalToConstant: Metric.orderLabelHeight) 100 | .fs_rightAnchor( 101 | equalTo: rightAnchor) 102 | .fs_topAnchor( 103 | equalTo: topAnchor) 104 | .fs_endSetup() 105 | } 106 | 107 | // MARK: - Bind 108 | 109 | func bind(viewModel: PhotoCellViewModel) { 110 | viewModel.isSelect 111 | .observeOn(MainScheduler.instance) 112 | .subscribe(onNext: { [weak self, weak viewModel] isSelect in 113 | guard let `self` = self, 114 | let `viewModel` = viewModel else { return } 115 | self.selectedView.isHidden = !isSelect 116 | 117 | if viewModel.configure.allowsMultipleSelection { 118 | self.orderLabel.isHidden = !isSelect 119 | } 120 | else { 121 | self.orderLabel.isHidden = true 122 | } 123 | }) 124 | .disposed(by: disposeBag) 125 | 126 | viewModel.isSelect 127 | .skip(1) 128 | .observeOn(MainScheduler.instance) 129 | .subscribe(onNext: { [weak self] isSelect in 130 | guard let `self` = self else { return } 131 | if isSelect { 132 | self.cellAnimationWhenSelectedCell() 133 | } 134 | else { 135 | self.cellAnimationWhenDeselectedCell() 136 | } 137 | }) 138 | .disposed(by: disposeBag) 139 | 140 | viewModel.selectedOrder 141 | .subscribe(onNext: { [weak self] selectedOrder in 142 | guard let `self` = self else { return } 143 | self.orderLabel.text = "\(selectedOrder)" 144 | }) 145 | .disposed(by: disposeBag) 146 | 147 | viewModel.image.asObservable() 148 | .observeOn(MainScheduler.instance) 149 | .subscribe(onNext: { [weak self] image in 150 | guard let `self` = self else { return } 151 | self.imageView.image = image 152 | }) 153 | .disposed(by: disposeBag) 154 | } 155 | } 156 | 157 | // MARK: - Animation 158 | 159 | extension FacebookPhotoCell { 160 | var cellAnimationWhenSelectedCell: () -> () { 161 | return { 162 | UIView.SpringAnimator(duration: 0.3) 163 | .options(.curveEaseOut) 164 | .velocity(0.0) 165 | .damping(0.5) 166 | .beforeAnimations { [weak self] in 167 | guard let `self` = self else { return } 168 | self.transform = CGAffineTransform(scaleX: 0.95, y: 0.95) 169 | } 170 | .animations { [weak self] in 171 | guard let `self` = self else { return } 172 | self.transform = CGAffineTransform(scaleX: 1, y: 1) 173 | } 174 | .animate() 175 | } 176 | } 177 | 178 | var cellAnimationWhenDeselectedCell: () -> () { 179 | return { 180 | UIView.SpringAnimator(duration: 0.3) 181 | .options(.curveEaseOut) 182 | .velocity(0.0) 183 | .damping(0.5) 184 | .beforeAnimations { [weak self] in 185 | guard let `self` = self else { return } 186 | self.transform = CGAffineTransform(scaleX: 1.05, y: 1.05) 187 | } 188 | .animations { [weak self] in 189 | guard let `self` = self else { return } 190 | self.transform = CGAffineTransform(scaleX: 1, y: 1) 191 | } 192 | .animate() 193 | } 194 | } 195 | } 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/FacebookPhotoCollectionCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FacebookPhotoCollectionCell.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 7.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Photos 11 | import EasyMakePhotoPicker 12 | import RxSwift 13 | 14 | class FacebookPhotoCollectionCell: BaseCollectionViewCell, PhotoCollectionCellable { 15 | 16 | // MARK: - Constant 17 | 18 | struct Constant { 19 | static let checkViewLineWidth = CGFloat(2.5) 20 | } 21 | 22 | struct Color { 23 | static let checkViewBGColor = UIColor.white 24 | static let checkViewCheckColor = UIColor( 25 | red: 104/255, 26 | green: 156/255, 27 | blue: 255/255, 28 | alpha: 1.0) 29 | 30 | static var countLabelTextColor = UIColor( 31 | red: 155/255, 32 | green: 155/255, 33 | blue: 155/255, 34 | alpha: 1.0) 35 | 36 | static var titleLabelTextColor = UIColor.black 37 | 38 | static var lineViewBGColor = UIColor( 39 | red: 216/255, 40 | green: 217/255, 41 | blue: 216/255, 42 | alpha: 1.0) 43 | } 44 | 45 | struct Font { 46 | static var titleLabelFont = UIFont.systemFont(ofSize: 16) 47 | static var countLabelFont = UIFont.systemFont(ofSize: 12) 48 | } 49 | 50 | struct Metric { 51 | 52 | static let checkViewWidth = CGFloat(30) 53 | static let checkViewHeight = CGFloat(30) 54 | static let checkViewRight = CGFloat(-15) 55 | 56 | // Note: PhotoCollectionsViewConfigure` photoThumbnailSize and 57 | // the size of the thumbnailImageView must be the same. !!!!! 58 | static var thumbnailImageViewHeight = CGFloat(54) 59 | static var thumbnailImageViewWidth = CGFloat(54) 60 | static var thumbnailImageViewLeft = CGFloat(10) 61 | 62 | static var titleLabelLeft = CGFloat(10) 63 | static var titleLabelTop = CGFloat(5) 64 | 65 | static var countLabelTop = CGFloat(5) 66 | 67 | static var lineViewHeight = CGFloat(1) 68 | } 69 | 70 | var checkView: UIView = CheckImageView().then { 71 | $0.backgroundColor = Color.checkViewBGColor 72 | $0.lineWidth = Constant.checkViewLineWidth 73 | $0.checkColor = Color.checkViewCheckColor 74 | $0.isHidden = true 75 | } 76 | 77 | var thumbnailImageView = UIImageView().then { 78 | $0.contentMode = .scaleAspectFill 79 | $0.clipsToBounds = true 80 | } 81 | 82 | var titleLabel = UILabel().then() { 83 | $0.font = Font.titleLabelFont 84 | $0.textColor = Color.titleLabelTextColor 85 | $0.font = Font.titleLabelFont 86 | } 87 | 88 | var countLabel = UILabel().then() { 89 | $0.textColor = Color.countLabelTextColor 90 | $0.font = Font.countLabelFont 91 | } 92 | 93 | var lineView = UIView().then { 94 | $0.backgroundColor = Color.lineViewBGColor 95 | } 96 | 97 | var disposeBag = DisposeBag() 98 | 99 | var viewModel: PhotoCollectionCellViewModel? { 100 | didSet { 101 | guard let viewModel = viewModel else { return } 102 | bind(viewModel: viewModel) 103 | } 104 | } 105 | 106 | // MARK: - Life Cycle 107 | 108 | override func prepareForReuse() { 109 | super.prepareForReuse() 110 | disposeBag = DisposeBag() 111 | viewModel = nil 112 | thumbnailImageView.image = nil 113 | checkView.isHidden = true 114 | } 115 | 116 | override func setupViews() { 117 | addSubview(checkView) 118 | addSubview(thumbnailImageView) 119 | addSubview(titleLabel) 120 | addSubview(countLabel) 121 | addSubview(lineView) 122 | } 123 | 124 | override func setupConstraints() { 125 | checkView 126 | .fs_widthAnchor(equalToConstant: Metric.checkViewWidth) 127 | .fs_heightAnchor(equalToConstant: Metric.checkViewHeight) 128 | .fs_rightAnchor( 129 | equalTo: rightAnchor, 130 | constant: Metric.checkViewRight) 131 | .fs_centerYAnchor(equalTo: centerYAnchor) 132 | .fs_endSetup() 133 | 134 | // PhotoCollectionsViewConfigure` photoThumbnailSize and 135 | // the size of the thumbnailImageView must be the same. 136 | thumbnailImageView 137 | .fs_leftAnchor( 138 | equalTo: leftAnchor, 139 | constant: Metric.thumbnailImageViewLeft) 140 | .fs_centerYAnchor(equalTo: centerYAnchor) 141 | .fs_widthAnchor(equalToConstant: Metric.thumbnailImageViewWidth) 142 | .fs_heightAnchor(equalToConstant: Metric.thumbnailImageViewHeight) 143 | .fs_endSetup() 144 | 145 | titleLabel 146 | .fs_leftAnchor( 147 | equalTo: thumbnailImageView.rightAnchor, 148 | constant: Metric.titleLabelLeft) 149 | .fs_topAnchor( 150 | equalTo: thumbnailImageView.topAnchor, 151 | constant: Metric.titleLabelTop) 152 | .fs_endSetup() 153 | 154 | countLabel 155 | .fs_leftAnchor( 156 | equalTo: titleLabel.leftAnchor) 157 | .fs_topAnchor( 158 | equalTo: titleLabel.bottomAnchor, 159 | constant: Metric.countLabelTop) 160 | .fs_endSetup() 161 | 162 | lineView 163 | .fs_leftAnchor(equalTo: thumbnailImageView.leftAnchor) 164 | .fs_rightAnchor(equalTo: rightAnchor) 165 | .fs_bottomAnchor(equalTo: bottomAnchor) 166 | .fs_heightAnchor(equalToConstant: Metric.lineViewHeight) 167 | .fs_endSetup() 168 | } 169 | 170 | func bind(viewModel: PhotoCollectionCellViewModel) { 171 | viewModel.isSelect.asObservable() 172 | .subscribe(onNext: { [weak self] isSelect in 173 | guard let`self` = self else { return } 174 | if isSelect { 175 | self.checkView.isHidden = false 176 | } 177 | else { 178 | self.checkView.isHidden = true 179 | } 180 | }) 181 | .disposed(by: disposeBag) 182 | viewModel.count 183 | .subscribe(onNext: { [weak self] count in 184 | guard let `self` = self else { return } 185 | self.countLabel.text = "\(count)" 186 | }) 187 | .disposed(by: disposeBag) 188 | 189 | viewModel.thumbnail 190 | .subscribe(onNext: { [weak self] thumbnail in 191 | guard let `self` = self else { return } 192 | self.thumbnailImageView.image = thumbnail 193 | }) 194 | .disposed(by: disposeBag) 195 | 196 | viewModel.title 197 | .subscribe(onNext: { [weak self] title in 198 | guard let `self` = self else { return } 199 | self.titleLabel.text = title 200 | }) 201 | .disposed(by: disposeBag) 202 | } 203 | } 204 | 205 | 206 | 207 | 208 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/FacebookPhotoCollectionLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FacebookPhotoCollectionLayout.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 7.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FacebookPhotoCollectionsLayout: UICollectionViewFlowLayout { 12 | override var itemSize: CGSize { 13 | set { } 14 | 15 | get { 16 | guard let collectionView = collectionView else { return .zero } 17 | return CGSize(width: collectionView.frame.width, height: 80) 18 | } 19 | } 20 | 21 | override init() { 22 | super.init() 23 | setupLayout() 24 | } 25 | 26 | required init?(coder aDecoder: NSCoder) { 27 | super.init() 28 | setupLayout() 29 | } 30 | 31 | func setupLayout() { 32 | minimumInteritemSpacing = 0 33 | minimumLineSpacing = 0 34 | scrollDirection = .vertical 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/FacebookPhotoCollectionsViewConfigure.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FacebookPhotoCollectionsViewConfigure.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 7.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Photos 10 | import EasyMakePhotoPicker 11 | 12 | struct FacebookPhotoCollectionsViewConfigure: PhotoCollectionsViewConfigure { 13 | var fetchOptions = PHFetchOptions() 14 | 15 | // to show collection types. 16 | var showsCollectionTypes: [PHAssetCollectionSubtype] = [ 17 | .smartAlbumUserLibrary, 18 | .smartAlbumGeneric, 19 | .smartAlbumFavorites, 20 | .smartAlbumRecentlyAdded, 21 | .smartAlbumVideos, 22 | .smartAlbumPanoramas, 23 | .smartAlbumBursts, 24 | .smartAlbumScreenshots 25 | ] 26 | 27 | var photoCollectionThumbnailSize = CGSize(width: 54, height: 54) 28 | 29 | var layout: UICollectionViewFlowLayout = FacebookPhotoCollectionsLayout() 30 | 31 | var photoCollectionCellTypeConverter = 32 | PhotoCollectionCellTypeConverter(type: FacebookPhotoCollectionCell.self) 33 | } 34 | 35 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/FacebookPhotoPicker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FacebookPhotoPicker.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 7.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | import RxCocoa 12 | import EasyMakePhotoPicker 13 | 14 | class FacebookPhotoPicker: UINavigationController, FacebookPhotoPickerOutput { 15 | 16 | var output: FacebookPhotoPickerOutput { return self } 17 | 18 | var cancel: Observable { 19 | return pickerVC.output.cancel 20 | } 21 | 22 | var selectionDidComplete: Observable<[PhotoAsset]> { 23 | return pickerVC.output.selectionDidComplete 24 | } 25 | 26 | var photoDidTake: Observable { 27 | return pickerVC.output.photoDidTake 28 | } 29 | 30 | fileprivate var pickerVC = FacebookPhotoPickerVC() 31 | 32 | fileprivate var permissionVC = FacebookPermissionVC() 33 | 34 | fileprivate var disposeBag = DisposeBag() 35 | 36 | override func loadView() { 37 | super.loadView() 38 | 39 | let authorized = PhotoManager.shared.checkPhotoLibraryPermission().publish() 40 | // if access has been previously granted 41 | // on a first run of the app, the user taps OK in the alert box: 42 | // ---- true ----> .completed 43 | // on any subsequent run of the app if access has been previously granted: 44 | // ---- true ----> .completed 45 | authorized 46 | .skipWhile { $0 == false } 47 | .take(1) 48 | .observeOn(MainScheduler.instance) 49 | .subscribe(onNext: { [weak self] _ in 50 | guard let `self` = self else { return } 51 | self.setViewControllers([self.pickerVC], animated: false) 52 | }) 53 | .disposed(by: disposeBag) 54 | 55 | // if the user doesn`t grant access 56 | // on the first run of the app, the user taps on Don`t Grant in the access alert box 57 | // ---- false ----> false ----> .completed 58 | // on any subsequent run if the user has previously denied access 59 | // ---- false ----> false ----> .completed 60 | authorized 61 | .skip(1) 62 | .filter { $0 == false } 63 | .observeOn(MainScheduler.instance) 64 | .subscribe(onNext: { [weak self] _ in 65 | guard let `self` = self else { return } 66 | self.setViewControllers([self.permissionVC], animated: false) 67 | }) 68 | .disposed(by: disposeBag) 69 | 70 | authorized 71 | .connect() 72 | .disposed(by: disposeBag) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/FacebookPhotosLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FacebookPhotosLayout.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 7.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FacebookPhotosLayout: UICollectionViewFlowLayout { 12 | 13 | // MARK: - Constant 14 | 15 | fileprivate struct Constant { 16 | static let padding = CGFloat(5) 17 | static let numberOfColumns = CGFloat(3) 18 | } 19 | 20 | override var itemSize: CGSize { 21 | set { } 22 | 23 | get { 24 | guard let collectionView = collectionView else { return .zero } 25 | let collectionViewWidth = (collectionView.bounds.width) 26 | 27 | let columnWidth = (collectionViewWidth - 28 | Constant.padding * (Constant.numberOfColumns - 1)) / Constant.numberOfColumns 29 | return CGSize(width: columnWidth, height: columnWidth) 30 | } 31 | } 32 | 33 | override init() { 34 | super.init() 35 | setupLayout() 36 | } 37 | 38 | required init?(coder aDecoder: NSCoder) { 39 | super.init() 40 | setupLayout() 41 | } 42 | 43 | func setupLayout() { 44 | minimumLineSpacing = Constant.padding 45 | minimumInteritemSpacing = Constant.padding 46 | } 47 | } 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/FacebookPhotosViewConfigure.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FacebookPhotosViewConfigure.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 7.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import EasyMakePhotoPicker 10 | import Photos 11 | 12 | class FacebookPhotosViewConfigure: PhotosViewConfigure { 13 | var fetchOptions: PHFetchOptions = PHFetchOptions() 14 | 15 | var allowsMultipleSelection: Bool = true 16 | 17 | var allowsCameraSelection: Bool = true 18 | 19 | // .video, .livePhoto 20 | var allowsPlayTypes: [AssetType] = [.video, .livePhoto] 21 | 22 | var messageWhenMaxCountSelectedPhotosIsExceeded: String = "over!!!" 23 | 24 | var maxCountSelectedPhotos: Int = 15 25 | 26 | var layout: UICollectionViewFlowLayout = FacebookPhotosLayout() 27 | 28 | var cameraCellTypeConverter = CameraCellTypeConverter(type: FacebookCameraCell.self) 29 | 30 | var photoCellTypeConverter = PhotoCellTypeConverter(type: FacebookPhotoCell.self) 31 | 32 | var livePhotoCellTypeConverter = LivePhotoCellTypeConverter(type: FacebookLivePhotoCell.self) 33 | 34 | var videoCellTypeConverter = VideoCellTypeConverter(type: FacebookVideoCell.self) 35 | } 36 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/FacebookVideoCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FacebookVideoCell.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 6.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Photos 11 | import RxSwift 12 | import EasyMakePhotoPicker 13 | 14 | class FacebookVideoCell: FacebookPhotoCell, VideoCellable { 15 | 16 | // MARK: - Constant 17 | 18 | struct Color { 19 | static let selectedDurationBackgroundViewBGColor = UIColor( 20 | red: 104/255, 21 | green: 156/255, 22 | blue: 255/255, 23 | alpha: 1.0) 24 | static let deselectedDurationBackgroundViewBGColor = UIColor( 25 | red: 0, 26 | green: 0, 27 | blue: 0, 28 | alpha: 0.6) 29 | } 30 | 31 | struct Metric { 32 | static let durationBackgroundViewHeight = CGFloat(26) 33 | 34 | static let videoIconImageViewWidth = CGFloat(16) 35 | static let videoIconImageViewHeight = CGFloat(16) 36 | static let videoIconImageViewLeft = CGFloat(5) 37 | 38 | static let durationLabelRight = CGFloat(-5) 39 | } 40 | 41 | var durationLabel: UILabel = DurationLabel().then { 42 | $0.textAlignment = .right 43 | $0.backgroundColor = .clear 44 | } 45 | 46 | var playerView = PlayerView().then { 47 | $0.playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill 48 | $0.isHidden = true 49 | } 50 | 51 | fileprivate var player: AVPlayer? { 52 | didSet { 53 | if let player = player { 54 | playerView.playerLayer.player = player 55 | 56 | NotificationCenter.default.addObserver( 57 | forName: .AVPlayerItemDidPlayToEndTime, 58 | object: player.currentItem, 59 | queue: nil) { _ in 60 | DispatchQueue.main.async { 61 | player.seek(to: CMTime.zero) 62 | player.play() 63 | } 64 | } 65 | } 66 | else { 67 | playerView.playerLayer.player = nil 68 | NotificationCenter.default.removeObserver(self) 69 | } 70 | } 71 | } 72 | 73 | var durationBackgroundView = UIView().then { 74 | $0.backgroundColor = Color.deselectedDurationBackgroundViewBGColor 75 | } 76 | 77 | var videoIconImageView = UIImageView(image: #imageLiteral(resourceName: "video")) 78 | 79 | var duration: TimeInterval = 0.0 { 80 | didSet { 81 | durationLabel.text = timeFormatted(timeInterval: duration) 82 | } 83 | } 84 | 85 | // MARK: - Life Cycle 86 | 87 | override func prepareForReuse() { 88 | super.prepareForReuse() 89 | player = nil 90 | playerView.isHidden = true 91 | } 92 | 93 | override func addSubviews() { 94 | super.addSubviews() 95 | insertSubview(playerView, aboveSubview: imageView) 96 | addSubview(durationLabel) 97 | durationBackgroundView.addSubview(videoIconImageView) 98 | insertSubview(durationBackgroundView, belowSubview: durationLabel) 99 | } 100 | 101 | override func setupConstraints() { 102 | super.setupConstraints() 103 | durationBackgroundView 104 | .fs_heightAnchor(equalToConstant: Metric.durationBackgroundViewHeight) 105 | .fs_leftAnchor(equalTo: leftAnchor) 106 | .fs_rightAnchor(equalTo: rightAnchor) 107 | .fs_bottomAnchor(equalTo: bottomAnchor) 108 | .fs_endSetup() 109 | 110 | videoIconImageView 111 | .fs_leftAnchor( 112 | equalTo: durationBackgroundView.leftAnchor, 113 | constant: Metric.videoIconImageViewLeft) 114 | .fs_centerYAnchor(equalTo: durationBackgroundView.centerYAnchor) 115 | .fs_widthAnchor(equalToConstant: Metric.videoIconImageViewWidth) 116 | .fs_heightAnchor(equalToConstant: Metric.videoIconImageViewHeight) 117 | .fs_endSetup() 118 | 119 | durationLabel 120 | .fs_rightAnchor( 121 | equalTo: durationBackgroundView.rightAnchor, 122 | constant: Metric.durationLabelRight) 123 | .fs_centerYAnchor(equalTo: durationBackgroundView.centerYAnchor) 124 | .fs_endSetup() 125 | 126 | playerView 127 | .fs_leftAnchor(equalTo: leftAnchor) 128 | .fs_topAnchor(equalTo: topAnchor) 129 | .fs_rightAnchor(equalTo: rightAnchor) 130 | .fs_bottomAnchor(equalTo: bottomAnchor) 131 | .fs_endSetup() 132 | } 133 | // MARK: - Bind 134 | 135 | override func bind(viewModel: PhotoCellViewModel) { 136 | super.bind(viewModel: viewModel) 137 | if let viewModel = viewModel as? VideoCellViewModel { 138 | duration = viewModel.duration 139 | 140 | viewModel.playEvent.asObserver() 141 | .subscribe(onNext: { [weak self] playEvent in 142 | guard let `self` = self else { return } 143 | switch playEvent { 144 | case .play: self.play() 145 | case .stop: self.stop() 146 | } 147 | }) 148 | .disposed(by: disposeBag) 149 | 150 | viewModel.isSelect 151 | .subscribe(onNext: { [weak self] isSelect in 152 | guard let `self` = self else { return } 153 | if isSelect { 154 | self.durationBackgroundView.backgroundColor = 155 | Color.selectedDurationBackgroundViewBGColor 156 | } 157 | else { 158 | self.durationBackgroundView.backgroundColor = 159 | Color.deselectedDurationBackgroundViewBGColor 160 | } 161 | }) 162 | .disposed(by: disposeBag) 163 | } 164 | } 165 | 166 | fileprivate func play() { 167 | guard let viewModel = viewModel as? VideoCellViewModel, 168 | let playerItem = viewModel.playerItem else { return } 169 | 170 | self.player = AVPlayer(playerItem: playerItem) 171 | 172 | if let player = player { 173 | playerView.isHidden = false 174 | player.play() 175 | } 176 | } 177 | 178 | fileprivate func stop() { 179 | if let player = player { 180 | player.pause(); 181 | self.player = nil 182 | playerView.isHidden = true 183 | } 184 | } 185 | 186 | fileprivate func timeFormatted(timeInterval: TimeInterval) -> String { 187 | let seconds = lround(timeInterval) 188 | var hour = 0 189 | var minute = (seconds / 60) 190 | let second = seconds % 60 191 | if minute > 59 { 192 | hour = (minute / 60) 193 | minute = (minute % 60) 194 | return String(format: "%d:%d:%02d", hour, minute, second) 195 | } 196 | else { 197 | return String(format: "%d:%02d", minute, second) 198 | } 199 | } 200 | } 201 | 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "back@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "back@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/back.imageset/back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audrl1010/EasyMakePhotoPicker/1cab1640d998c988ac37994a044d3485e745c8a7/Example/EasyMakePhotoPicker/Images.xcassets/back.imageset/back@2x.png -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/back.imageset/back@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audrl1010/EasyMakePhotoPicker/1cab1640d998c988ac37994a044d3485e745c8a7/Example/EasyMakePhotoPicker/Images.xcassets/back.imageset/back@3x.png -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/camera.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "camera.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "camera@x2.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "camera@x3.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/camera.imageset/camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audrl1010/EasyMakePhotoPicker/1cab1640d998c988ac37994a044d3485e745c8a7/Example/EasyMakePhotoPicker/Images.xcassets/camera.imageset/camera.png -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/camera.imageset/camera@x2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audrl1010/EasyMakePhotoPicker/1cab1640d998c988ac37994a044d3485e745c8a7/Example/EasyMakePhotoPicker/Images.xcassets/camera.imageset/camera@x2.png -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/camera.imageset/camera@x3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audrl1010/EasyMakePhotoPicker/1cab1640d998c988ac37994a044d3485e745c8a7/Example/EasyMakePhotoPicker/Images.xcassets/camera.imageset/camera@x3.png -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/cameraroll-picker-grip.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "cameraroll-picker-grip@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "cameraroll-picker-grip@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/cameraroll-picker-grip.imageset/cameraroll-picker-grip@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audrl1010/EasyMakePhotoPicker/1cab1640d998c988ac37994a044d3485e745c8a7/Example/EasyMakePhotoPicker/Images.xcassets/cameraroll-picker-grip.imageset/cameraroll-picker-grip@2x.png -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/cameraroll-picker-grip.imageset/cameraroll-picker-grip@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audrl1010/EasyMakePhotoPicker/1cab1640d998c988ac37994a044d3485e745c8a7/Example/EasyMakePhotoPicker/Images.xcassets/cameraroll-picker-grip.imageset/cameraroll-picker-grip@3x.png -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/straighten-grid.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "straighten-grid@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "straighten-grid@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/straighten-grid.imageset/straighten-grid@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audrl1010/EasyMakePhotoPicker/1cab1640d998c988ac37994a044d3485e745c8a7/Example/EasyMakePhotoPicker/Images.xcassets/straighten-grid.imageset/straighten-grid@2x.png -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/straighten-grid.imageset/straighten-grid@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audrl1010/EasyMakePhotoPicker/1cab1640d998c988ac37994a044d3485e745c8a7/Example/EasyMakePhotoPicker/Images.xcassets/straighten-grid.imageset/straighten-grid@3x.png -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/video.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "icons8-Video Call_25.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "icons8-Video Call_50.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/video.imageset/icons8-Video Call_25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audrl1010/EasyMakePhotoPicker/1cab1640d998c988ac37994a044d3485e745c8a7/Example/EasyMakePhotoPicker/Images.xcassets/video.imageset/icons8-Video Call_25.png -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Images.xcassets/video.imageset/icons8-Video Call_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/audrl1010/EasyMakePhotoPicker/1cab1640d998c988ac37994a044d3485e745c8a7/Example/EasyMakePhotoPicker/Images.xcassets/video.imageset/icons8-Video Call_50.png -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSCameraUsageDescription 26 | access camera 27 | NSPhotoLibraryUsageDescription 28 | access photo library 29 | UILaunchStoryboardName 30 | LaunchScreen 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/KaKaoCameraCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KaKaoCameraCell.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 5.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import RxSwift 10 | import Photos 11 | import PhotosUI 12 | import EasyMakePhotoPicker 13 | 14 | class KaKaoCameraCell: BaseCollectionViewCell, CameraCellable { 15 | 16 | var cameraIcon: UIImage { 17 | return #imageLiteral(resourceName: "camera") 18 | } 19 | 20 | var bgColor: UIColor { 21 | return .white 22 | } 23 | 24 | var imageView = UIImageView().then { 25 | $0.contentMode = .scaleAspectFill 26 | $0.clipsToBounds = true 27 | } 28 | 29 | override func layoutSubviews() { 30 | super.layoutSubviews() 31 | imageView.image = centerAtRect( 32 | image: cameraIcon, 33 | rect: frame, 34 | bgColor: bgColor) 35 | } 36 | 37 | fileprivate func centerAtRect( 38 | image: UIImage?, 39 | rect: CGRect, 40 | bgColor: UIColor) -> UIImage? { 41 | guard let image = image else { return nil } 42 | 43 | UIGraphicsBeginImageContextWithOptions(rect.size, false, image.scale) 44 | bgColor.setFill() 45 | UIRectFill( CGRect(x: 0, y: 0, width: rect.size.width, height: rect.size.height)) 46 | 47 | image.draw(in: 48 | CGRect( 49 | x:rect.size.width/2 - image.size.width/2, 50 | y:rect.size.height/2 - image.size.height/2, 51 | width: image.size.width, 52 | height: image.size.height)) 53 | 54 | let result = UIGraphicsGetImageFromCurrentImageContext() 55 | UIGraphicsEndImageContext() 56 | return result 57 | } 58 | 59 | override func addSubviews() { 60 | addSubview(imageView) 61 | } 62 | 63 | override func setupConstraints() { 64 | imageView 65 | .fs_leftAnchor(equalTo: leftAnchor) 66 | .fs_topAnchor(equalTo: topAnchor) 67 | .fs_rightAnchor(equalTo: rightAnchor) 68 | .fs_bottomAnchor(equalTo: bottomAnchor) 69 | .fs_endSetup() 70 | } 71 | } 72 | 73 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/KaKaoChatCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KaKaoChatCell.swift 3 | // KaKaoChatInputView 4 | // 5 | // Created by smg on 2017. 5. 13.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class KaKaoChatCell: BaseTableViewCell { 12 | 13 | // MARK: - Constants 14 | 15 | fileprivate struct Constant { 16 | static let labelNumberOfLines = 1 17 | } 18 | 19 | fileprivate struct Font { 20 | static let labelFont = UIFont.systemFont(ofSize: 16) 21 | } 22 | 23 | fileprivate struct Metric { 24 | static let labelLeft = CGFloat(10) 25 | static let labelRight = CGFloat(-10) 26 | static let labelTop = CGFloat(10) 27 | static let labelBottom = CGFloat(-10) 28 | } 29 | 30 | // MARK: - Properties 31 | 32 | var label = UILabel().then { 33 | $0.font = Font.labelFont 34 | $0.numberOfLines = Constant.labelNumberOfLines 35 | } 36 | 37 | // MARK: - Life Cycle 38 | 39 | override func prepareForReuse() { 40 | super.prepareForReuse() 41 | } 42 | 43 | override func setupViews() { 44 | addSubview(label) 45 | } 46 | 47 | override func setupConstraints() { 48 | label 49 | .fs_leftAnchor( 50 | equalTo: leftAnchor, 51 | constant: Metric.labelLeft) 52 | .fs_rightAnchor( 53 | equalTo: rightAnchor, 54 | constant: Metric.labelRight) 55 | .fs_topAnchor( 56 | equalTo: topAnchor, 57 | constant: Metric.labelTop) 58 | .fs_bottomAnchor( 59 | equalTo: bottomAnchor, 60 | constant: Metric.labelBottom) 61 | .fs_endSetup() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/KaKaoChatPhotoViewsConfigure.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KaKaoChatPhotoViewsConfigure.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 7.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Photos 10 | import EasyMakePhotoPicker 11 | 12 | struct KaKaoChatPhotosViewConfigure: PhotosViewConfigure { 13 | var fetchOptions: PHFetchOptions = PHFetchOptions() 14 | 15 | var allowsMultipleSelection: Bool = true 16 | 17 | var allowsCameraSelection: Bool = false 18 | 19 | // .video, .livePhoto 20 | var allowsPlayTypes: [AssetType] = [] 21 | 22 | var messageWhenMaxCountSelectedPhotosIsExceeded: String = "over!!!" 23 | 24 | var maxCountSelectedPhotos: Int = 10 25 | 26 | var layout: UICollectionViewFlowLayout = KaKaoChatPhotosLayout() 27 | 28 | var cameraCellTypeConverter = CameraCellTypeConverter(type: KaKaoCameraCell.self) 29 | 30 | var photoCellTypeConverter = PhotoCellTypeConverter(type: KaKaoPhotoCell.self) 31 | 32 | var livePhotoCellTypeConverter = LivePhotoCellTypeConverter(type: KaKaoLivePhotoCell.self) 33 | 34 | var videoCellTypeConverter = VideoCellTypeConverter(type: KaKaoVideoCell.self) 35 | } 36 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/KaKaoChatPhotosLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KaKaoChatPhotosLayout.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 3.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class KaKaoChatPhotosLayout: UICollectionViewFlowLayout { 12 | 13 | struct Constant { 14 | static let itemWidth = CGFloat(160) 15 | static let minimumLineSpacing = CGFloat(2) 16 | } 17 | 18 | override public init() { 19 | super.init() 20 | setupLayout() 21 | } 22 | 23 | open override var itemSize: CGSize { 24 | set { } 25 | 26 | get { 27 | guard let collectionView = collectionView else { return .zero } 28 | return CGSize(width: Constant.itemWidth, height: collectionView.bounds.height) 29 | } 30 | } 31 | 32 | required public init?(coder aDecoder: NSCoder) { 33 | super.init() 34 | setupLayout() 35 | } 36 | 37 | func setupLayout() { 38 | scrollDirection = .horizontal 39 | minimumLineSpacing = Constant.minimumLineSpacing 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/KaKaoCollectionCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KaKaoPhotoCollectionCell.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 5.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | import RxCocoa 12 | import EasyMakePhotoPicker 13 | 14 | class KaKaoPhotoCollectionCell: BaseCollectionViewCell, PhotoCollectionCellable { 15 | // MARK: - Constant 16 | 17 | public struct Color { 18 | public static var countLabelTextColor = UIColor( 19 | red: 155/255, 20 | green: 155/255, 21 | blue: 155/255, 22 | alpha: 1.0) 23 | 24 | public static var titleLabelTextColor = UIColor.black 25 | 26 | public static var lineViewBGColor = UIColor( 27 | red: 216/255, 28 | green: 217/255, 29 | blue: 216/255, 30 | alpha: 1.0) 31 | } 32 | 33 | public struct Font { 34 | public static var titleLabelFont = UIFont.systemFont(ofSize: 16) 35 | public static var countLabelFont = UIFont.systemFont(ofSize: 12) 36 | } 37 | 38 | public struct Metric { 39 | public static var thumbnailImageViewLeft = CGFloat(10) 40 | public static var thumbnailImageViewHeight = CGFloat(54) 41 | public static var thumbnailImageViewWidth = CGFloat(54) 42 | 43 | public static var titleLabelLeft = CGFloat(10) 44 | public static var titleLabelTop = CGFloat(5) 45 | 46 | public static var countLabelTop = CGFloat(5) 47 | 48 | public static var lineViewHeight = CGFloat(1) 49 | } 50 | 51 | // MARK: - Properties 52 | var thumbnailImageView = UIImageView().then { 53 | $0.contentMode = .scaleAspectFill 54 | $0.clipsToBounds = true 55 | } 56 | 57 | var titleLabel = UILabel().then() { 58 | $0.textColor = Color.titleLabelTextColor 59 | $0.font = Font.titleLabelFont 60 | } 61 | 62 | var countLabel = UILabel().then() { 63 | $0.textColor = Color.countLabelTextColor 64 | $0.font = Font.countLabelFont 65 | } 66 | 67 | var lineView = UIView().then { 68 | $0.backgroundColor = Color.lineViewBGColor 69 | } 70 | 71 | var disposeBag = DisposeBag() 72 | 73 | var viewModel: PhotoCollectionCellViewModel? { 74 | didSet { 75 | guard let viewModel = viewModel else { return } 76 | bind(viewModel: viewModel) 77 | } 78 | } 79 | 80 | // MARK: - Life Cycle 81 | 82 | override open func prepareForReuse() { 83 | super.prepareForReuse() 84 | viewModel = nil 85 | thumbnailImageView.image = nil 86 | disposeBag = DisposeBag() 87 | } 88 | 89 | override func addSubviews() { 90 | addSubview(thumbnailImageView) 91 | addSubview(titleLabel) 92 | addSubview(countLabel) 93 | addSubview(lineView) 94 | } 95 | 96 | override open func setupConstraints() { 97 | thumbnailImageView 98 | .fs_leftAnchor( 99 | equalTo: leftAnchor, 100 | constant: Metric.thumbnailImageViewLeft) 101 | .fs_centerYAnchor(equalTo: centerYAnchor) 102 | .fs_heightAnchor(equalToConstant: Metric.thumbnailImageViewHeight) 103 | .fs_widthAnchor(equalToConstant: Metric.thumbnailImageViewWidth) 104 | .fs_endSetup() 105 | 106 | titleLabel 107 | .fs_leftAnchor( 108 | equalTo: thumbnailImageView.rightAnchor, 109 | constant: Metric.titleLabelLeft) 110 | .fs_topAnchor( 111 | equalTo: thumbnailImageView.topAnchor, 112 | constant: Metric.titleLabelTop) 113 | .fs_endSetup() 114 | 115 | countLabel 116 | .fs_leftAnchor( 117 | equalTo: titleLabel.leftAnchor) 118 | .fs_topAnchor( 119 | equalTo: titleLabel.bottomAnchor, 120 | constant: Metric.countLabelTop) 121 | .fs_endSetup() 122 | 123 | lineView 124 | .fs_leftAnchor(equalTo: thumbnailImageView.leftAnchor) 125 | .fs_rightAnchor(equalTo: rightAnchor) 126 | .fs_bottomAnchor(equalTo: bottomAnchor) 127 | .fs_heightAnchor(equalToConstant: Metric.lineViewHeight) 128 | .fs_endSetup() 129 | } 130 | 131 | open func bind(viewModel: PhotoCollectionCellViewModel) { 132 | viewModel.count 133 | .subscribe(onNext: { [weak self] count in 134 | guard let `self` = self else { return } 135 | self.countLabel.text = "\(count)" 136 | }) 137 | .disposed(by: disposeBag) 138 | 139 | viewModel.thumbnail 140 | .subscribe(onNext: { [weak self] thumbnail in 141 | guard let `self` = self else { return } 142 | self.thumbnailImageView.image = thumbnail 143 | }) 144 | .disposed(by: disposeBag) 145 | 146 | viewModel.title 147 | .subscribe(onNext: { [weak self] title in 148 | guard let `self` = self else { return } 149 | self.titleLabel.text = title 150 | }) 151 | .disposed(by: disposeBag) 152 | } 153 | } 154 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/KaKaoLivePhotoCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KaKaoLivePhotoCell.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 5.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import PhotosUI 11 | import RxSwift 12 | import EasyMakePhotoPicker 13 | 14 | class KaKaoLivePhotoCell: KaKaoPhotoCell, LivePhotoCellable { 15 | 16 | // MARK: - Constant 17 | 18 | struct Constant { 19 | static let selectedViewBorderWidth = CGFloat(2) 20 | } 21 | 22 | struct Metric { 23 | static let livePhotoBadgeImageViewWidth = CGFloat(20) 24 | static let livePhotoBadgeImageViewHeight = CGFloat(20) 25 | static let livePhotoBadgeImageViewRight = CGFloat(-10) 26 | static let livePhotoBadgeImageViewBottom = CGFloat(-10) 27 | } 28 | 29 | struct Color { 30 | static var selectedViewBGC = UIColor(white: 1.0, alpha: 0.0) 31 | } 32 | 33 | lazy var livePhotoView: PHLivePhotoView = { [unowned self] in 34 | let lpv = PHLivePhotoView() 35 | lpv.delegate = self 36 | lpv.isHidden = true 37 | return lpv 38 | }() 39 | 40 | var livePhotoBadgeImageView = UIImageView().then { 41 | $0.contentMode = .scaleAspectFit 42 | } 43 | 44 | // MARK: - Life Cycle 45 | 46 | override func prepareForReuse() { 47 | super.prepareForReuse() 48 | livePhotoBadgeImageView.image = nil 49 | livePhotoView.isHidden = true 50 | } 51 | 52 | override func setupViews() { 53 | super.setupViews() 54 | selectedView.backgroundColor = Color.selectedViewBGC 55 | } 56 | 57 | override func addSubviews() { 58 | super.addSubviews() 59 | insertSubview(livePhotoView, aboveSubview: imageView) 60 | addSubview(livePhotoBadgeImageView) 61 | } 62 | 63 | override func setupConstraints() { 64 | super.setupConstraints() 65 | livePhotoBadgeImageView 66 | .fs_widthAnchor( 67 | equalToConstant: Metric.livePhotoBadgeImageViewWidth) 68 | .fs_heightAnchor( 69 | equalToConstant: Metric.livePhotoBadgeImageViewHeight) 70 | .fs_rightAnchor( 71 | equalTo: rightAnchor, 72 | constant: Metric.livePhotoBadgeImageViewRight) 73 | .fs_bottomAnchor( 74 | equalTo: bottomAnchor, 75 | constant: Metric.livePhotoBadgeImageViewBottom) 76 | .fs_endSetup() 77 | 78 | livePhotoView 79 | .fs_leftAnchor(equalTo: leftAnchor) 80 | .fs_topAnchor(equalTo: topAnchor) 81 | .fs_rightAnchor(equalTo: rightAnchor) 82 | .fs_bottomAnchor(equalTo: bottomAnchor) 83 | .fs_endSetup() 84 | } 85 | 86 | // MARK: - Bind 87 | 88 | override func bind(viewModel: PhotoCellViewModel) { 89 | super.bind(viewModel: viewModel) 90 | if let viewModel = viewModel as? LivePhotoCellViewModel { 91 | livePhotoBadgeImageView.image = viewModel.badgeImage 92 | viewModel.playEvent.asObserver() 93 | .subscribe(onNext: { [weak self] playEvent in 94 | guard let `self` = self else { return } 95 | switch playEvent { 96 | case .play: self.play() 97 | case .stop: self.stop() 98 | } 99 | }) 100 | .disposed(by: disposeBag) 101 | } 102 | } 103 | 104 | fileprivate func play() { 105 | guard let viewModel = viewModel as? LivePhotoCellViewModel, 106 | let livePhoto = viewModel.livePhoto else { return } 107 | 108 | livePhotoView.startPlayback(with: .hint) 109 | livePhotoView.isHidden = false 110 | livePhotoView.livePhoto = livePhoto 111 | } 112 | 113 | fileprivate func stop() { 114 | self.livePhotoView.stopPlayback() 115 | self.livePhotoView.isHidden = true 116 | self.livePhotoView.livePhoto = nil 117 | } 118 | } 119 | 120 | // MARK: - PHLivePhotoViewDelegate 121 | extension KaKaoLivePhotoCell: PHLivePhotoViewDelegate { 122 | public func livePhotoView( 123 | _ livePhotoView: PHLivePhotoView, 124 | willBeginPlaybackWith playbackStyle: PHLivePhotoViewPlaybackStyle) { 125 | } 126 | 127 | public func livePhotoView( 128 | _ livePhotoView: PHLivePhotoView, 129 | didEndPlaybackWith playbackStyle: PHLivePhotoViewPlaybackStyle) { 130 | livePhotoView.startPlayback(with: .hint) 131 | } 132 | } 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/KaKaoPhotoCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KaKaoPhotoCell.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 5.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import EasyMakePhotoPicker 11 | import PhotosUI 12 | import RxSwift 13 | 14 | class KaKaoPhotoCell: BaseCollectionViewCell, PhotoCellable { 15 | 16 | // MARK: - Constant 17 | 18 | struct Constant { 19 | static let selectedViewBorderWidth = CGFloat(2) 20 | } 21 | 22 | struct Color { 23 | static let selectedViewBorderColor = UIColor( 24 | red: 255/255, 25 | green: 255/255, 26 | blue: 0/255, 27 | alpha: 1.0) 28 | static let selectedViewBGColor = UIColor( 29 | red: 0, 30 | green: 0, 31 | blue: 0, 32 | alpha: 0.6) 33 | } 34 | 35 | struct Metric { 36 | static let orderLabelWidth = CGFloat(30) 37 | static let orderLabelHeight = CGFloat(30) 38 | static let orderLabelRight = CGFloat(-10) 39 | static let orderLabelTop = CGFloat(10) 40 | 41 | static let checkImageViewWidth = CGFloat(30) 42 | static let checkImageViewHeight = CGFloat(30) 43 | static let checkImageViewRight = CGFloat(-10) 44 | static let checkImageViewTop = CGFloat(10) 45 | } 46 | 47 | // MARK: - Properties 48 | 49 | var checkView = CheckImageView().then { 50 | $0.isHidden = true 51 | } 52 | 53 | var selectedView = UIView().then { 54 | $0.layer.borderWidth = Constant.selectedViewBorderWidth 55 | $0.layer.borderColor = Color.selectedViewBorderColor.cgColor 56 | $0.backgroundColor = Color.selectedViewBGColor 57 | $0.isHidden = true 58 | } 59 | 60 | var orderLabel = NumberLabel().then { 61 | $0.isHidden = true 62 | } 63 | 64 | var imageView = UIImageView().then { 65 | $0.contentMode = .scaleAspectFill 66 | $0.clipsToBounds = true 67 | } 68 | 69 | var disposeBag: DisposeBag = DisposeBag() 70 | 71 | var viewModel: PhotoCellViewModel? { 72 | didSet { 73 | guard let viewModel = viewModel else { return } 74 | bind(viewModel: viewModel) 75 | } 76 | } 77 | 78 | // MARK: - Life Cycle 79 | 80 | override func prepareForReuse() { 81 | super.prepareForReuse() 82 | disposeBag = DisposeBag() 83 | viewModel = nil 84 | imageView.image = nil 85 | orderLabel.label.text = nil 86 | orderLabel.isHidden = true 87 | selectedView.isHidden = true 88 | } 89 | 90 | override func addSubviews() { 91 | addSubview(imageView) 92 | addSubview(selectedView) 93 | addSubview(orderLabel) 94 | addSubview(checkView) 95 | } 96 | 97 | override func setupConstraints() { 98 | imageView 99 | .fs_leftAnchor(equalTo: leftAnchor) 100 | .fs_topAnchor(equalTo: topAnchor) 101 | .fs_rightAnchor(equalTo: rightAnchor) 102 | .fs_bottomAnchor(equalTo: bottomAnchor) 103 | .fs_endSetup() 104 | 105 | selectedView 106 | .fs_leftAnchor(equalTo: leftAnchor) 107 | .fs_topAnchor(equalTo: topAnchor) 108 | .fs_rightAnchor(equalTo: rightAnchor) 109 | .fs_bottomAnchor(equalTo: bottomAnchor) 110 | .fs_endSetup() 111 | 112 | orderLabel 113 | .fs_widthAnchor( 114 | equalToConstant: Metric.orderLabelWidth) 115 | .fs_heightAnchor( 116 | equalToConstant: Metric.orderLabelHeight) 117 | .fs_rightAnchor( 118 | equalTo: rightAnchor, 119 | constant: Metric.orderLabelRight) 120 | .fs_topAnchor( 121 | equalTo: topAnchor, 122 | constant: Metric.orderLabelTop) 123 | .fs_endSetup() 124 | 125 | checkView 126 | .fs_widthAnchor( 127 | equalToConstant: Metric.checkImageViewWidth) 128 | .fs_heightAnchor( 129 | equalToConstant: Metric.checkImageViewHeight) 130 | .fs_rightAnchor( 131 | equalTo: rightAnchor, 132 | constant: Metric.checkImageViewRight) 133 | .fs_topAnchor( 134 | equalTo: topAnchor, 135 | constant: Metric.checkImageViewTop) 136 | .fs_endSetup() 137 | } 138 | 139 | // MARK: - Bind 140 | 141 | func bind(viewModel: PhotoCellViewModel) { 142 | viewModel.isSelect 143 | .observeOn(MainScheduler.instance) 144 | .subscribe(onNext: { [weak self, weak viewModel] isSelect in 145 | guard let `self` = self, 146 | let `viewModel` = viewModel else { return } 147 | self.selectedView.isHidden = !isSelect 148 | 149 | if viewModel.configure.allowsMultipleSelection { 150 | self.orderLabel.isHidden = !isSelect 151 | self.checkView.isHidden = isSelect 152 | } 153 | else { 154 | self.orderLabel.isHidden = true 155 | self.checkView.isHidden = true 156 | } 157 | }) 158 | .disposed(by: disposeBag) 159 | 160 | viewModel.selectedOrder 161 | .subscribe(onNext: { [weak self] selectedOrder in 162 | guard let `self` = self else { return } 163 | self.orderLabel.number = selectedOrder 164 | }) 165 | .disposed(by: disposeBag) 166 | 167 | viewModel.image.asObservable() 168 | .observeOn(MainScheduler.instance) 169 | .subscribe(onNext: { [weak self] image in 170 | guard let `self` = self else { return } 171 | self.imageView.image = image 172 | }) 173 | .disposed(by: disposeBag) 174 | } 175 | } 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/KaKaoPhotoCollectionCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KaKaoPhotoCollectionCell.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 6.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | import RxCocoa 12 | import EasyMakePhotoPicker 13 | 14 | class KaKaoPhotoCollectionCell: BaseCollectionViewCell, PhotoCollectionCellable { 15 | // MARK: - Constant 16 | 17 | struct Color { 18 | static var countLabelTextColor = UIColor( 19 | red: 155/255, 20 | green: 155/255, 21 | blue: 155/255, 22 | alpha: 1.0) 23 | 24 | static var titleLabelTextColor = UIColor.black 25 | 26 | static var lineViewBGColor = UIColor( 27 | red: 216/255, 28 | green: 217/255, 29 | blue: 216/255, 30 | alpha: 1.0) 31 | } 32 | 33 | struct Font { 34 | static var titleLabelFont = UIFont.systemFont(ofSize: 16) 35 | static var countLabelFont = UIFont.systemFont(ofSize: 12) 36 | } 37 | 38 | struct Metric { 39 | static var thumbnailImageViewLeft = CGFloat(10) 40 | static var thumbnailImageViewHeight = CGFloat(54) 41 | static var thumbnailImageViewWidth = CGFloat(54) 42 | 43 | static var titleLabelLeft = CGFloat(10) 44 | static var titleLabelTop = CGFloat(5) 45 | 46 | static var countLabelTop = CGFloat(5) 47 | 48 | static var lineViewHeight = CGFloat(1) 49 | } 50 | 51 | // MARK: - Properties 52 | var thumbnailImageView = UIImageView().then { 53 | $0.contentMode = .scaleAspectFill 54 | $0.clipsToBounds = true 55 | } 56 | 57 | var titleLabel = UILabel().then() { 58 | $0.textColor = Color.titleLabelTextColor 59 | $0.font = Font.titleLabelFont 60 | } 61 | 62 | var countLabel = UILabel().then() { 63 | $0.textColor = Color.countLabelTextColor 64 | $0.font = Font.countLabelFont 65 | } 66 | 67 | var lineView = UIView().then { 68 | $0.backgroundColor = Color.lineViewBGColor 69 | } 70 | 71 | var disposeBag = DisposeBag() 72 | 73 | var viewModel: PhotoCollectionCellViewModel? { 74 | didSet { 75 | guard let viewModel = viewModel else { return } 76 | bind(viewModel: viewModel) 77 | } 78 | } 79 | 80 | // MARK: - Life Cycle 81 | 82 | override func prepareForReuse() { 83 | super.prepareForReuse() 84 | viewModel = nil 85 | thumbnailImageView.image = nil 86 | disposeBag = DisposeBag() 87 | } 88 | 89 | override func addSubviews() { 90 | addSubview(thumbnailImageView) 91 | addSubview(titleLabel) 92 | addSubview(countLabel) 93 | addSubview(lineView) 94 | } 95 | 96 | override func setupConstraints() { 97 | thumbnailImageView 98 | .fs_leftAnchor( 99 | equalTo: leftAnchor, 100 | constant: Metric.thumbnailImageViewLeft) 101 | .fs_centerYAnchor(equalTo: centerYAnchor) 102 | .fs_heightAnchor(equalToConstant: Metric.thumbnailImageViewHeight) 103 | .fs_widthAnchor(equalToConstant: Metric.thumbnailImageViewWidth) 104 | .fs_endSetup() 105 | 106 | titleLabel 107 | .fs_leftAnchor( 108 | equalTo: thumbnailImageView.rightAnchor, 109 | constant: Metric.titleLabelLeft) 110 | .fs_topAnchor( 111 | equalTo: thumbnailImageView.topAnchor, 112 | constant: Metric.titleLabelTop) 113 | .fs_endSetup() 114 | 115 | countLabel 116 | .fs_leftAnchor( 117 | equalTo: titleLabel.leftAnchor) 118 | .fs_topAnchor( 119 | equalTo: titleLabel.bottomAnchor, 120 | constant: Metric.countLabelTop) 121 | .fs_endSetup() 122 | 123 | lineView 124 | .fs_leftAnchor(equalTo: thumbnailImageView.leftAnchor) 125 | .fs_rightAnchor(equalTo: rightAnchor) 126 | .fs_bottomAnchor(equalTo: bottomAnchor) 127 | .fs_heightAnchor(equalToConstant: Metric.lineViewHeight) 128 | .fs_endSetup() 129 | } 130 | 131 | func bind(viewModel: PhotoCollectionCellViewModel) { 132 | viewModel.count 133 | .subscribe(onNext: { [weak self] count in 134 | guard let `self` = self else { return } 135 | self.countLabel.text = "\(count)" 136 | }) 137 | .disposed(by: disposeBag) 138 | 139 | viewModel.thumbnail 140 | .subscribe(onNext: { [weak self] thumbnail in 141 | guard let `self` = self else { return } 142 | self.thumbnailImageView.image = thumbnail 143 | }) 144 | .disposed(by: disposeBag) 145 | 146 | viewModel.title 147 | .subscribe(onNext: { [weak self] title in 148 | guard let `self` = self else { return } 149 | self.titleLabel.text = title 150 | }) 151 | .disposed(by: disposeBag) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/KaKaoPhotoCollectionLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KaKaoPhotoCollectionLayout.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 6.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class KaKaoPhotoCollectionsLayout: UICollectionViewFlowLayout { 12 | 13 | override var itemSize: CGSize { 14 | set { } 15 | 16 | get { 17 | guard let collectionView = collectionView else { return .zero } 18 | return CGSize(width: collectionView.frame.width, height: 120) 19 | } 20 | } 21 | 22 | override init() { 23 | super.init() 24 | setupLayout() 25 | } 26 | 27 | required init?(coder aDecoder: NSCoder) { 28 | super.init() 29 | setupLayout() 30 | } 31 | 32 | func setupLayout() { 33 | minimumInteritemSpacing = 0 34 | minimumLineSpacing = 0 35 | scrollDirection = .vertical 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/KaKaoPhotoCollectionsViewConfigure.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KaKaoPhotoCollectionsViewConfigure.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 5.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Photos 11 | import EasyMakePhotoPicker 12 | 13 | struct KaKaoPhotoCollectionsViewConfigure: PhotoCollectionsViewConfigure { 14 | var fetchOptions = PHFetchOptions() 15 | 16 | // to show collection types. 17 | var showsCollectionTypes: [PHAssetCollectionSubtype] = [ 18 | .smartAlbumUserLibrary, 19 | .smartAlbumGeneric, 20 | .smartAlbumFavorites, 21 | .smartAlbumRecentlyAdded, 22 | .smartAlbumVideos, 23 | .smartAlbumPanoramas, 24 | .smartAlbumBursts, 25 | .smartAlbumScreenshots 26 | ] 27 | 28 | var photoCollectionThumbnailSize = CGSize(width: 54, height: 54) 29 | 30 | var layout: UICollectionViewFlowLayout = KaKaoPhotoCollectionsLayout() 31 | 32 | var photoCollectionCellTypeConverter = 33 | PhotoCollectionCellTypeConverter(type: KaKaoPhotoCollectionCell.self) 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/KaKaoPhotoPicker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KaKaoPhotoPicker.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 3.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | import RxCocoa 12 | import EasyMakePhotoPicker 13 | 14 | class KaKaoPhotoPicker: UINavigationController, KaKaoPhotoPickerOutput { 15 | 16 | var output: KaKaoPhotoPickerOutput { return self } 17 | 18 | var cancel: Observable { 19 | return pickerVC.output.cancel 20 | } 21 | 22 | var selectionDidComplete: Observable<[PhotoAsset]> { 23 | return pickerVC.output.selectionDidComplete 24 | } 25 | 26 | var photoDidTake: Observable { 27 | return pickerVC.output.photoDidTake 28 | } 29 | 30 | fileprivate var pickerVC = KaKaoPhotoPickerVC() 31 | 32 | fileprivate var permissionVC = kaKaoPermissionVC() 33 | 34 | var disposeBag = DisposeBag() 35 | 36 | override func loadView() { 37 | super.loadView() 38 | 39 | let authorized = PhotoManager.shared.checkPhotoLibraryPermission().publish() 40 | // if access has been previously granted 41 | // on a first run of the app, the user taps OK in the alert box: 42 | // ---- true ----> .completed 43 | // on any subsequent run of the app if access has been previously granted: 44 | // ---- true ----> .completed 45 | authorized 46 | .skipWhile { $0 == false } 47 | .take(1) 48 | .observeOn(MainScheduler.instance) 49 | .subscribe(onNext: { [weak self] _ in 50 | guard let `self` = self else { return } 51 | self.setViewControllers([self.pickerVC], animated: false) 52 | }) 53 | .disposed(by: disposeBag) 54 | 55 | // if the user doesn`t grant access 56 | // on the first run of the app, the user taps on Don`t Grant in the access alert box 57 | // ---- false ----> false ----> .completed 58 | // on any subsequent run if the user has previously denied access 59 | // ---- false ----> false ----> .completed 60 | authorized 61 | .skip(1) 62 | .filter { $0 == false } 63 | .observeOn(MainScheduler.instance) 64 | .subscribe(onNext: { [weak self] _ in 65 | guard let `self` = self else { return } 66 | self.setViewControllers([self.permissionVC], animated: false) 67 | }) 68 | .disposed(by: disposeBag) 69 | 70 | authorized 71 | .connect() 72 | .disposed(by: disposeBag) 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/KaKaoPhotosLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KaKaoPhotosLayout.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 5.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class KaKaoPhotosLayout: UICollectionViewFlowLayout { 12 | 13 | fileprivate struct Constant { 14 | static let padding = CGFloat(1) 15 | static let numberOfColumns = CGFloat(3) 16 | } 17 | 18 | override var itemSize: CGSize { 19 | set { } 20 | 21 | get { 22 | guard let collectionView = collectionView else { return .zero } 23 | let collectionViewWidth = (collectionView.bounds.width) 24 | 25 | let columnWidth = (collectionViewWidth - 26 | Constant.padding * (Constant.numberOfColumns - 1)) / Constant.numberOfColumns 27 | return CGSize(width: columnWidth, height: columnWidth) 28 | } 29 | } 30 | 31 | override init() { 32 | super.init() 33 | setupLayout() 34 | } 35 | 36 | required init?(coder aDecoder: NSCoder) { 37 | super.init() 38 | setupLayout() 39 | } 40 | 41 | func setupLayout() { 42 | minimumLineSpacing = Constant.padding 43 | minimumInteritemSpacing = Constant.padding 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/KaKaoPhotosViewConfigure.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KaKaoPhotosViewConfigure.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 5.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | 10 | import Photos 11 | import EasyMakePhotoPicker 12 | 13 | struct KaKaoPhotosViewConfigure: PhotosViewConfigure { 14 | var fetchOptions: PHFetchOptions = PHFetchOptions() 15 | 16 | var allowsMultipleSelection: Bool = true 17 | 18 | var allowsCameraSelection: Bool = true 19 | 20 | // .video, .livePhoto 21 | var allowsPlayTypes: [AssetType] = [.video, .livePhoto] 22 | 23 | var messageWhenMaxCountSelectedPhotosIsExceeded: String = "over!!!" 24 | 25 | var maxCountSelectedPhotos: Int = 15 26 | 27 | var layout: UICollectionViewFlowLayout = KaKaoPhotosLayout() 28 | 29 | var cameraCellTypeConverter = CameraCellTypeConverter(type: KaKaoCameraCell.self) 30 | 31 | var photoCellTypeConverter = PhotoCellTypeConverter(type: KaKaoPhotoCell.self) 32 | 33 | var livePhotoCellTypeConverter = LivePhotoCellTypeConverter(type: KaKaoLivePhotoCell.self) 34 | 35 | var videoCellTypeConverter = VideoCellTypeConverter(type: KaKaoVideoCell.self) 36 | } 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/KaKaoVideoCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KaKaoVideoCell.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 5.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import PhotosUI 11 | import RxSwift 12 | import EasyMakePhotoPicker 13 | 14 | class KaKaoVideoCell: KaKaoPhotoCell, VideoCellable { 15 | // MARK: - Constant 16 | 17 | struct Color { 18 | static let selectedViewBGC = UIColor(white: 1.0, alpha: 0.0) 19 | } 20 | 21 | struct Metric { 22 | static let durationLabelLeft = CGFloat(5) 23 | static let durationLabelTop = CGFloat(5) 24 | } 25 | 26 | var durationLabel: UILabel = DurationLabel() 27 | 28 | var playerView = PlayerView().then { 29 | $0.playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill 30 | $0.isHidden = true 31 | } 32 | 33 | var duration: TimeInterval = 0.0 { 34 | didSet { 35 | durationLabel.text = timeFormatted(timeInterval: duration) 36 | } 37 | } 38 | 39 | fileprivate var player: AVPlayer? { 40 | didSet { 41 | if let player = player { 42 | playerView.playerLayer.player = player 43 | 44 | NotificationCenter.default.addObserver( 45 | forName: .AVPlayerItemDidPlayToEndTime, 46 | object: player.currentItem, 47 | queue: nil) { _ in 48 | DispatchQueue.main.async { 49 | player.seek(to: CMTime.zero) 50 | player.play() 51 | } 52 | } 53 | } 54 | else { 55 | playerView.playerLayer.player = nil 56 | NotificationCenter.default.removeObserver(self) 57 | } 58 | } 59 | } 60 | 61 | // MARK: - Life Cycle 62 | 63 | override func prepareForReuse() { 64 | super.prepareForReuse() 65 | player = nil 66 | playerView.isHidden = true 67 | } 68 | 69 | override func setupViews() { 70 | super.setupViews() 71 | selectedView.backgroundColor = Color.selectedViewBGC 72 | } 73 | 74 | override func addSubviews() { 75 | super.addSubviews() 76 | insertSubview(playerView, aboveSubview: imageView) 77 | addSubview(durationLabel) 78 | } 79 | 80 | override func setupConstraints() { 81 | super.setupConstraints() 82 | durationLabel 83 | .fs_leftAnchor( 84 | equalTo: leftAnchor, 85 | constant: Metric.durationLabelLeft) 86 | .fs_topAnchor( 87 | equalTo: topAnchor, 88 | constant: Metric.durationLabelTop) 89 | .fs_endSetup() 90 | 91 | playerView 92 | .fs_leftAnchor(equalTo: leftAnchor) 93 | .fs_topAnchor(equalTo: topAnchor) 94 | .fs_rightAnchor(equalTo: rightAnchor) 95 | .fs_bottomAnchor(equalTo: bottomAnchor) 96 | .fs_endSetup() 97 | } 98 | 99 | // MARK: - Bind 100 | 101 | override func bind(viewModel: PhotoCellViewModel) { 102 | super.bind(viewModel: viewModel) 103 | if let viewModel = viewModel as? VideoCellViewModel { 104 | duration = viewModel.duration 105 | 106 | viewModel.playEvent.asObserver() 107 | .subscribe(onNext: { [weak self] playEvent in 108 | guard let `self` = self else { return } 109 | switch playEvent { 110 | case .play: self.play() 111 | case .stop: self.stop() 112 | } 113 | }) 114 | .disposed(by: disposeBag) 115 | } 116 | } 117 | 118 | fileprivate func play() { 119 | guard let viewModel = viewModel as? VideoCellViewModel, 120 | let playerItem = viewModel.playerItem else { return } 121 | 122 | self.player = AVPlayer(playerItem: playerItem) 123 | 124 | if let player = player { 125 | playerView.isHidden = false 126 | player.play() 127 | } 128 | } 129 | 130 | fileprivate func stop() { 131 | if let player = player { 132 | player.pause(); 133 | self.player = nil 134 | playerView.isHidden = true 135 | } 136 | } 137 | 138 | fileprivate func timeFormatted(timeInterval: TimeInterval) -> String { 139 | let seconds = lround(timeInterval) 140 | var hour = 0 141 | var minute = (seconds / 60) 142 | let second = seconds % 60 143 | if minute > 59 { 144 | hour = (minute / 60) 145 | minute = (minute % 60) 146 | return String(format: "%d:%d:%02d", hour, minute, second) 147 | } 148 | else { 149 | return String(format: "%d:%02d", minute, second) 150 | } 151 | } 152 | } 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/MainVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainVC.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 3.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | import RxCocoa 12 | import EasyMakePhotoPicker 13 | 14 | class MainVC: UIViewController { 15 | 16 | var photosViewInChatInputBarButton = UIButton(type: .system).then() { 17 | $0.setTitle("Show PhotosView in ChatInputBar", for: .normal) 18 | } 19 | 20 | var kakaoPhotoPickerButton = UIButton(type: .system).then { 21 | $0.setTitle("Show KaKaoPhotoPicker", for: .normal) 22 | } 23 | 24 | var facebookPhotoPickerButton = UIButton(type: .system).then { 25 | $0.setTitle("Show FacebookPhotoPicker", for: .normal) 26 | } 27 | 28 | var disposeBag = DisposeBag() 29 | 30 | override func viewDidLoad() { 31 | super.viewDidLoad() 32 | 33 | view.backgroundColor = .white 34 | 35 | photosViewInChatInputBarButton.rx.controlEvent(.touchUpInside) 36 | .subscribe(onNext: { [weak self] in 37 | guard let `self` = self else { return } 38 | let chatNavController = UINavigationController( 39 | rootViewController: KaKaoChatVC()) 40 | self.present( 41 | chatNavController, 42 | animated: true, 43 | completion: nil) 44 | }) 45 | .disposed(by: disposeBag) 46 | 47 | kakaoPhotoPickerButton.rx.controlEvent(.touchUpInside) 48 | .subscribe(onNext: { [weak self] in 49 | guard let `self` = self else { return } 50 | let photoPicker = KaKaoPhotoPicker() 51 | 52 | photoPicker.output.cancel 53 | .subscribe(onNext: { print("cancel") }) 54 | .disposed(by: self.disposeBag) 55 | 56 | photoPicker.output.photoDidTake 57 | .subscribe(onNext: { photoAsset in 58 | print("photoAsset: \(photoAsset)") 59 | }) 60 | .disposed(by: self.disposeBag) 61 | 62 | photoPicker.output.selectionDidComplete 63 | .subscribe(onNext: { photoAssets in 64 | print("photoAssetsCount: \(photoAssets.count)") 65 | photoAssets.forEach { 66 | print($0) 67 | } 68 | }) 69 | .disposed(by: self.disposeBag) 70 | 71 | self.present( 72 | photoPicker, 73 | animated: true, 74 | completion: nil) 75 | }) 76 | .disposed(by: disposeBag) 77 | 78 | facebookPhotoPickerButton.rx.controlEvent(.touchUpInside) 79 | .subscribe(onNext: { [weak self] in 80 | guard let `self` = self else { return } 81 | let facebookPhotoPicker = FacebookPhotoPicker() 82 | 83 | facebookPhotoPicker.output.cancel 84 | .subscribe(onNext: { print("cancel") }) 85 | .disposed(by: self.disposeBag) 86 | 87 | facebookPhotoPicker.output.photoDidTake 88 | .subscribe(onNext: { photoAsset in 89 | print("photoAsset: \(photoAsset)") 90 | }) 91 | .disposed(by: self.disposeBag) 92 | 93 | facebookPhotoPicker.output.selectionDidComplete 94 | .subscribe(onNext: { photoAssets in 95 | print("photoAssetsCount: \(photoAssets.count)") 96 | photoAssets.forEach { 97 | print($0) 98 | } 99 | }) 100 | .disposed(by: self.disposeBag) 101 | 102 | self.present( 103 | facebookPhotoPicker, 104 | animated: true, 105 | completion: nil) 106 | }) 107 | .disposed(by: disposeBag) 108 | 109 | view.addSubview(photosViewInChatInputBarButton) 110 | view.addSubview(kakaoPhotoPickerButton) 111 | view.addSubview(facebookPhotoPickerButton) 112 | 113 | photosViewInChatInputBarButton 114 | .fs_leftAnchor( 115 | equalTo: view.leftAnchor, 116 | constant: 30) 117 | .fs_rightAnchor( 118 | equalTo: view.rightAnchor, 119 | constant: -30) 120 | .fs_heightAnchor(equalToConstant: 50) 121 | .fs_centerXAnchor(equalTo: view.centerXAnchor) 122 | .fs_centerYAnchor(equalTo: view.centerYAnchor) 123 | .fs_endSetup() 124 | 125 | 126 | kakaoPhotoPickerButton 127 | .fs_topAnchor( 128 | equalTo: photosViewInChatInputBarButton.bottomAnchor, 129 | constant: 20) 130 | .fs_leftAnchor( 131 | equalTo: view.leftAnchor, 132 | constant: 30) 133 | .fs_rightAnchor( 134 | equalTo: view.rightAnchor, 135 | constant: -30) 136 | .fs_heightAnchor(equalToConstant: 50) 137 | .fs_endSetup() 138 | 139 | facebookPhotoPickerButton 140 | .fs_topAnchor( 141 | equalTo: kakaoPhotoPickerButton.bottomAnchor, 142 | constant: 20) 143 | .fs_leftAnchor( 144 | equalTo: view.leftAnchor, 145 | constant: 30) 146 | .fs_rightAnchor( 147 | equalTo: view.rightAnchor, 148 | constant: -30) 149 | .fs_heightAnchor(equalToConstant: 50) 150 | .fs_endSetup() 151 | 152 | } 153 | } 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/NumberLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NumberLabel.swift 3 | // KaKaoChatInputView 4 | // 5 | // Created by myung gi son on 2017. 5. 25.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class NumberLabel: BaseView { 12 | 13 | struct Color { 14 | static let labelTextColor = UIColor.black 15 | static let backgroundColor = UIColor( 16 | red: 255/255, 17 | green: 255/255, 18 | blue: 0/255, 19 | alpha: 1.0) 20 | } 21 | 22 | struct Font { 23 | static let labelFont = UIFont.systemFont(ofSize: 12) 24 | } 25 | 26 | var number: Int = 0 { 27 | didSet { 28 | label.text = "\(number)" 29 | } 30 | } 31 | 32 | var font: UIFont = Font.labelFont { 33 | didSet { 34 | label.font = font 35 | } 36 | } 37 | 38 | var label = UILabel().then { 39 | $0.font = Font.labelFont 40 | $0.textColor = Color.labelTextColor 41 | } 42 | 43 | override func layoutSubviews() { 44 | super.layoutSubviews() 45 | layer.cornerRadius = frame.height / 2 46 | } 47 | 48 | override func setupViews() { 49 | super.setupViews() 50 | addSubview(label) 51 | backgroundColor = Color.backgroundColor 52 | } 53 | 54 | override func setupConstraints() { 55 | super.setupConstraints() 56 | label 57 | .fs_centerXAnchor(equalTo: centerXAnchor) 58 | .fs_centerYAnchor(equalTo: centerYAnchor) 59 | .fs_endSetup() 60 | } 61 | } 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/PlayerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlayerView.swift 3 | // Pods 4 | // 5 | // Created by myung gi son on 2017. 7. 5.. 6 | // 7 | // 8 | 9 | import UIKit 10 | import AVFoundation 11 | 12 | class PlayerView: UIView { 13 | 14 | var player: AVPlayer? { 15 | get { return playerLayer.player } 16 | set { playerLayer.player = newValue } 17 | } 18 | 19 | var playerLayer: AVPlayerLayer { 20 | return layer as! AVPlayerLayer 21 | } 22 | 23 | override static var layerClass: AnyClass { 24 | return AVPlayerLayer.self 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/PlusButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlusButton.swift 3 | // KaKaoChatInputView 4 | // 5 | // Created by smg on 2017. 5. 13.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class PlusButton: UIButton { 12 | 13 | var lineColor: UIColor = .gray { 14 | didSet { 15 | setNeedsLayout() 16 | } 17 | } 18 | 19 | var lineWidth: CGFloat = 3 { 20 | didSet { 21 | setNeedsLayout() 22 | } 23 | } 24 | 25 | override func draw(_ rect: CGRect) { 26 | super.draw(rect) 27 | 28 | let padding = CGFloat(8) 29 | let drawingRect = rect.insetBy(dx: padding, dy: padding) 30 | 31 | let path = UIBezierPath() 32 | path.lineJoinStyle = .round 33 | path.lineWidth = lineWidth 34 | lineColor.setStroke() 35 | 36 | path.move( 37 | to: CGPoint(x: padding, 38 | y: padding + drawingRect.height/2 + 0.5)) 39 | path.addLine( 40 | to: CGPoint(x: padding + drawingRect.width, 41 | y: padding + drawingRect.height/2 + 0.5)) 42 | path.close() 43 | 44 | path.move( 45 | to: CGPoint(x: padding + drawingRect.width/2 + 0.5, 46 | y: padding)) 47 | path.addLine( 48 | to: CGPoint(x: padding + drawingRect.width/2 + 0.5, 49 | y: padding + drawingRect.height + 0.5)) 50 | path.close() 51 | 52 | path.stroke() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/TextView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TextView.swift 3 | // KaKaoChatInputView 4 | // 5 | // Created by smg on 2017. 5. 13.. 6 | // Copyright © 2017년 grutech. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol HasPlaceholder { 12 | var placeholderLabel: UILabel { get set } 13 | } 14 | 15 | class TextView: UITextView, HasPlaceholder { 16 | 17 | // MARK - Properties 18 | 19 | var heightDidChange: ((_ newHeight: CGFloat) -> Void)? 20 | 21 | lazy var placeholderLabel = UILabel().then { 22 | $0.clipsToBounds = false 23 | $0.numberOfLines = 1 24 | $0.font = self.font 25 | $0.backgroundColor = .clear 26 | $0.textColor = self.placeholderColor 27 | $0.isHidden = true 28 | $0.autoresizesSubviews = false 29 | } 30 | 31 | var expectedHeight: CGFloat = 0 32 | 33 | var maxNumberOfLines: CGFloat = 0 34 | 35 | var minimumHeight: CGFloat { 36 | return ceil(font!.lineHeight) + 37 | textContainerInset.top + textContainerInset.bottom 38 | } 39 | 40 | var placeholder: String = "" { 41 | didSet { 42 | placeholderLabel.text = placeholder 43 | } 44 | } 45 | 46 | var placeholderColor: UIColor = .gray { 47 | didSet { 48 | placeholderLabel.textColor = placeholderColor 49 | } 50 | } 51 | 52 | override var font: UIFont? { 53 | didSet { placeholderLabel.font = font } 54 | } 55 | 56 | override var contentSize: CGSize { 57 | didSet { updateSize() } 58 | } 59 | 60 | // MARK: - View Life Cycle 61 | 62 | override init(frame: CGRect, textContainer: NSTextContainer?) { 63 | super.init(frame: frame, textContainer: textContainer) 64 | setupViews() 65 | } 66 | 67 | required init?(coder aDecoder: NSCoder) { 68 | super.init(coder: aDecoder) 69 | setupViews() 70 | } 71 | 72 | func setupViews() { 73 | addSubview(placeholderLabel) 74 | isScrollEnabled = false 75 | } 76 | 77 | override func layoutSubviews() { 78 | super.layoutSubviews() 79 | placeholderLabel.isHidden = isHiddenPlaceholder 80 | 81 | if !isHiddenPlaceholder { 82 | placeholderLabel.frame = placeholderRectThatFits(bounds) 83 | sendSubviewToBack(placeholderLabel) 84 | } 85 | } 86 | 87 | // MARK - Public Methods 88 | func textViewDidChange() { 89 | placeholderLabel.isHidden = isHiddenPlaceholder 90 | updateSize() 91 | } 92 | 93 | // MARK: - Helper Methods 94 | 95 | fileprivate var isHiddenPlaceholder:Bool { 96 | return placeholder.lengthOfBytes(using: .utf8) == 0 97 | || text.lengthOfBytes(using: .utf8) > 0 98 | } 99 | 100 | fileprivate func placeholderRectThatFits(_ rect: CGRect) -> CGRect { 101 | var placeholderRect = CGRect.zero 102 | placeholderRect.size = placeholderLabel.sizeThatFits(rect.size) 103 | 104 | placeholderRect.origin = 105 | rect.inset(by: textContainerInset).origin 106 | 107 | let padding = textContainer.lineFragmentPadding 108 | placeholderRect.origin.x += padding 109 | return placeholderRect 110 | } 111 | 112 | fileprivate func ensureCaretDisplaysCorrectly() { 113 | if let s = selectedTextRange { 114 | let rect = caretRect(for: s.end) 115 | UIView.performWithoutAnimation { [weak self] in 116 | guard let `self` = self else { return } 117 | self.scrollRectToVisible(rect, animated: false) 118 | } 119 | } 120 | } 121 | 122 | fileprivate func updateSize() { 123 | var maxHeight = CGFloat.infinity 124 | 125 | if maxNumberOfLines > 0 { 126 | maxHeight = (ceil(font!.lineHeight) * maxNumberOfLines) + 127 | textContainerInset.top + textContainerInset.bottom 128 | } 129 | 130 | let roundedHeight = roundHeight 131 | if roundedHeight >= maxHeight { 132 | expectedHeight = maxHeight 133 | isScrollEnabled = true 134 | } 135 | else { 136 | expectedHeight = roundedHeight 137 | isScrollEnabled = false 138 | } 139 | 140 | if let heightDidChange = heightDidChange { 141 | heightDidChange(expectedHeight) 142 | } 143 | 144 | ensureCaretDisplaysCorrectly() 145 | } 146 | 147 | fileprivate var roundHeight: CGFloat { 148 | var newHeight = CGFloat(0) 149 | if let font = font { 150 | let attributes = [NSAttributedString.Key.font: font] 151 | let boundingSize = 152 | CGSize(width: frame.size.width, height: CGFloat.infinity) 153 | 154 | let size = 155 | (text as NSString).boundingRect( 156 | with: boundingSize, 157 | options: NSStringDrawingOptions.usesLineFragmentOrigin, 158 | attributes: attributes, context: nil) 159 | newHeight = ceil(size.height) 160 | } 161 | return newHeight + textContainerInset.top + textContainerInset.bottom 162 | } 163 | } 164 | 165 | 166 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/Then.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Suyeol Jeon (xoul.kr) 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 | 23 | import Foundation 24 | import CoreGraphics 25 | 26 | public protocol Then {} 27 | 28 | extension Then where Self: Any { 29 | 30 | /// Makes it available to set properties with closures just after initializing and copying the value types. 31 | /// 32 | /// let frame = CGRect().with { 33 | /// $0.origin.x = 10 34 | /// $0.size.width = 100 35 | /// } 36 | public func with(_ block: (inout Self) -> Void) -> Self { 37 | var copy = self 38 | block(©) 39 | return copy 40 | } 41 | 42 | /// Makes it available to execute something with closures. 43 | /// 44 | /// UserDefaults.standard.do { 45 | /// $0.set("devxoul", forKey: "username") 46 | /// $0.set("devxoul@gmail.com", forKey: "email") 47 | /// $0.synchronize() 48 | /// } 49 | public func `do`(_ block: (Self) -> Void) { 50 | block(self) 51 | } 52 | 53 | } 54 | 55 | extension Then where Self: AnyObject { 56 | 57 | /// Makes it available to set properties with closures just after initializing. 58 | /// 59 | /// let label = UILabel().then { 60 | /// $0.textAlignment = .Center 61 | /// $0.textColor = UIColor.blackColor() 62 | /// $0.text = "Hello, World!" 63 | /// } 64 | public func then(_ block: (Self) -> Void) -> Self { 65 | block(self) 66 | return self 67 | } 68 | } 69 | 70 | extension NSObject: Then {} 71 | extension CGPoint: Then {} 72 | extension CGRect: Then {} 73 | extension CGSize: Then {} 74 | extension CGVector: Then {} 75 | -------------------------------------------------------------------------------- /Example/EasyMakePhotoPicker/kaKaoPermissionVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // kaKaoPermissionVC.swift 3 | // EasyMakePhotoPicker 4 | // 5 | // Created by myung gi son on 2017. 7. 3.. 6 | // Copyright © 2017년 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | import RxCocoa 12 | 13 | class kaKaoPermissionVC: BaseVC { 14 | 15 | struct Constant { 16 | static let titleLabelText = "Please Allow Photo Access" 17 | static let contentLabelText = "This allows you to share photos from your library and save photos to your camera roll." 18 | static let enumerationOneLabelText = "1. Tap Privacy" 19 | static let enumerationSecondLabelText = "2. Switch Photos On" 20 | static let allowAccessButtonText = "Allow Access" 21 | } 22 | 23 | struct Color { 24 | static let containerViewBGColor = UIColor.white 25 | static let titleLabelColor = UIColor( 26 | red: 114/255, 27 | green: 116/255, 28 | blue: 130/255, 29 | alpha: 1.0) 30 | static let contentLabelColor = UIColor( 31 | red: 114/255, 32 | green: 116/255, 33 | blue: 130/255, 34 | alpha: 1.0) 35 | static let enumerationOneLabelColor = UIColor( 36 | red: 114/255, 37 | green: 116/255, 38 | blue: 130/255, 39 | alpha: 1.0) 40 | static let enumerationSecondLabelColor = UIColor( 41 | red: 114/255, 42 | green: 116/255, 43 | blue: 130/255, 44 | alpha: 1.0) 45 | } 46 | 47 | struct Font { 48 | static let titleLabelFont = UIFont.boldSystemFont(ofSize: 18) 49 | static let contentLabelFont = UIFont.systemFont(ofSize: 14) 50 | static let enumerationOneLabelFont = UIFont.boldSystemFont(ofSize: 16) 51 | static let enumerationSecondLabelFont = UIFont.boldSystemFont(ofSize: 16) 52 | static let allowAccessButtonFont = UIFont.boldSystemFont(ofSize: 16) 53 | } 54 | 55 | struct Metric { 56 | static let containerViewLeft = CGFloat(25) 57 | static let containerViewTop = CGFloat(100) 58 | static let containerViewRight = CGFloat(-25) 59 | static let containerViewBottom = CGFloat(50) 60 | 61 | static let titleLabelTop = CGFloat(5) 62 | 63 | static let contentLabelTop = CGFloat(10) 64 | 65 | static let enumerationOneLabelLabelTop = CGFloat(20) 66 | 67 | static let enumerationSecondLabelTop = CGFloat(10) 68 | 69 | static let allowAccessButtonTop = CGFloat(35) 70 | } 71 | 72 | var containerView = UIView().then { 73 | $0.backgroundColor = Color.containerViewBGColor 74 | } 75 | 76 | var titleLabel = UILabel().then { 77 | $0.textColor = Color.titleLabelColor 78 | $0.font = Font.titleLabelFont 79 | $0.textAlignment = .center 80 | $0.text = Constant.titleLabelText 81 | } 82 | 83 | var contentLabel = UILabel().then { 84 | $0.textColor = Color.contentLabelColor 85 | $0.font = Font.contentLabelFont 86 | $0.text = Constant.contentLabelText 87 | $0.textAlignment = .center 88 | $0.numberOfLines = 0 89 | } 90 | 91 | var enumerationOneLabel = UILabel().then { 92 | $0.textColor = Color.enumerationOneLabelColor 93 | $0.font = Font.enumerationOneLabelFont 94 | $0.text = Constant.enumerationOneLabelText 95 | } 96 | 97 | var enumerationSecondLabel = UILabel().then { 98 | $0.textColor = Color.enumerationSecondLabelColor 99 | $0.font = Font.enumerationSecondLabelFont 100 | $0.text = Constant.enumerationSecondLabelText 101 | } 102 | 103 | var allowAccessButton = UIButton(type: .system).then { 104 | $0.setTitle(Constant.allowAccessButtonText, for: .normal) 105 | $0.titleLabel?.font = Font.allowAccessButtonFont 106 | } 107 | 108 | var doneButton = UIBarButtonItem( 109 | barButtonSystemItem: .done, 110 | target: nil, 111 | action: nil) 112 | 113 | var disposeBag = DisposeBag() 114 | 115 | override func setupViews() { 116 | navigationItem.rightBarButtonItem = doneButton 117 | view.backgroundColor = .white 118 | view.addSubview(containerView) 119 | containerView.addSubview(titleLabel) 120 | containerView.addSubview(contentLabel) 121 | containerView.addSubview(enumerationOneLabel) 122 | containerView.addSubview(enumerationSecondLabel) 123 | containerView.addSubview(allowAccessButton) 124 | bind() 125 | } 126 | 127 | override func setupConstraints() { 128 | containerView 129 | .fs_leftAnchor( 130 | equalTo: view.leftAnchor, 131 | constant: Metric.containerViewLeft) 132 | .fs_topAnchor( 133 | equalTo: topLayoutGuide.bottomAnchor, 134 | constant: Metric.containerViewTop) 135 | .fs_rightAnchor( 136 | equalTo: view.rightAnchor, 137 | constant: Metric.containerViewRight) 138 | .fs_bottomAnchor( 139 | equalTo: view.bottomAnchor, 140 | constant: Metric.containerViewBottom) 141 | .fs_endSetup() 142 | 143 | titleLabel 144 | .fs_topAnchor( 145 | equalTo: containerView.topAnchor, 146 | constant: Metric.titleLabelTop) 147 | .fs_centerXAnchor(equalTo: containerView.centerXAnchor) 148 | .fs_endSetup() 149 | 150 | contentLabel 151 | .fs_topAnchor( 152 | equalTo: titleLabel.bottomAnchor, 153 | constant: Metric.contentLabelTop) 154 | .fs_leftAnchor(equalTo: containerView.leftAnchor) 155 | .fs_rightAnchor(equalTo: containerView.rightAnchor) 156 | .fs_endSetup() 157 | 158 | enumerationOneLabel 159 | .fs_topAnchor( 160 | equalTo: contentLabel.bottomAnchor, 161 | constant: Metric.enumerationOneLabelLabelTop) 162 | .fs_centerXAnchor(equalTo: containerView.centerXAnchor) 163 | .fs_endSetup() 164 | 165 | enumerationSecondLabel 166 | .fs_topAnchor( 167 | equalTo: enumerationOneLabel.bottomAnchor, 168 | constant: Metric.enumerationSecondLabelTop) 169 | .fs_centerXAnchor(equalTo: containerView.centerXAnchor) 170 | .fs_endSetup() 171 | 172 | allowAccessButton 173 | .fs_topAnchor( 174 | equalTo: enumerationSecondLabel.bottomAnchor, 175 | constant: Metric.allowAccessButtonTop) 176 | .fs_centerXAnchor(equalTo: containerView.centerXAnchor) 177 | .fs_endSetup() 178 | } 179 | 180 | func bind() { 181 | doneButton.rx.tap 182 | .observeOn(MainScheduler.instance) 183 | .subscribe(onNext: { [weak self] _ in 184 | guard let `self` = self else { return } 185 | self.dismiss(animated: true, completion: nil) 186 | }) 187 | .disposed(by: disposeBag) 188 | 189 | allowAccessButton.rx.controlEvent(.touchUpInside) 190 | .subscribe(onNext: { 191 | UIApplication.shared 192 | .openURL(URL(string: UIApplication.openSettingsURLString)!) 193 | }) 194 | .disposed(by: disposeBag) 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'EasyMakePhotoPicker_Example' do 4 | pod 'EasyMakePhotoPicker', :path => '../' 5 | pod 'RxSwift' 6 | pod 'RxCocoa' 7 | 8 | target 'EasyMakePhotoPicker_Tests' do 9 | inherit! :search_paths 10 | pod 'RxSwift' 11 | pod 'RxCocoa' 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - EasyMakePhotoPicker (0.1.1): 3 | - RxCocoa (>= 3.0) 4 | - RxSwift (>= 3.0) 5 | - RxAtomic (4.4.0) 6 | - RxCocoa (4.4.0): 7 | - RxSwift (~> 4.0) 8 | - RxSwift (4.4.0): 9 | - RxAtomic (~> 4.4) 10 | 11 | DEPENDENCIES: 12 | - EasyMakePhotoPicker (from `../`) 13 | - RxCocoa 14 | - RxSwift 15 | 16 | SPEC REPOS: 17 | https://github.com/cocoapods/specs.git: 18 | - RxAtomic 19 | - RxCocoa 20 | - RxSwift 21 | 22 | EXTERNAL SOURCES: 23 | EasyMakePhotoPicker: 24 | :path: "../" 25 | 26 | SPEC CHECKSUMS: 27 | EasyMakePhotoPicker: d7c6cf3acc9f30900b4d25643a8db68c393484f5 28 | RxAtomic: eacf60db868c96bfd63320e28619fe29c179656f 29 | RxCocoa: df63ebf7b9a70d6b4eeea407ed5dd4efc8979749 30 | RxSwift: 5976ecd04fc2fefd648827c23de5e11157faa973 31 | 32 | PODFILE CHECKSUM: fd1e973444ff2b42299291a5a4eb7db0d5bc78b7 33 | 34 | COCOAPODS: 1.5.3 35 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/EasyMakePhotoPicker.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "EasyMakePhotoPicker", 3 | "version": "0.1.1", 4 | "summary": "EasyPhotoPicker makes it easy to create your own PhotoPicker.", 5 | "description": "If you need to create your own PhotoPicker, it is not easy to create because you need to implement many of the features (UI, business logic) needed to implement PhotoPicker. So EasyMakePhotoPicker provides an abstraction layer of PhotoPicker. EasyMakePhotoPicker implements all the business logic required for PhotoPicker so you can focus on the UI.", 6 | "homepage": "https://github.com/audrl1010/EasyMakePhotoPicker", 7 | "license": { 8 | "type": "MIT", 9 | "file": "LICENSE" 10 | }, 11 | "authors": { 12 | "Myung gi son": "audrl1010@naver.com" 13 | }, 14 | "source": { 15 | "git": "https://github.com/audrl1010/EasyMakePhotoPicker.git", 16 | "tag": "0.1.1" 17 | }, 18 | "platforms": { 19 | "ios": "9.1" 20 | }, 21 | "source_files": "EasyMakePhotoPicker/Classes/**/*", 22 | "dependencies": { 23 | "RxSwift": [ 24 | ">= 3.0" 25 | ], 26 | "RxCocoa": [ 27 | ">= 3.0" 28 | ] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - EasyMakePhotoPicker (0.1.1): 3 | - RxCocoa (>= 3.0) 4 | - RxSwift (>= 3.0) 5 | - RxAtomic (4.4.0) 6 | - RxCocoa (4.4.0): 7 | - RxSwift (~> 4.0) 8 | - RxSwift (4.4.0): 9 | - RxAtomic (~> 4.4) 10 | 11 | DEPENDENCIES: 12 | - EasyMakePhotoPicker (from `../`) 13 | - RxCocoa 14 | - RxSwift 15 | 16 | SPEC REPOS: 17 | https://github.com/cocoapods/specs.git: 18 | - RxAtomic 19 | - RxCocoa 20 | - RxSwift 21 | 22 | EXTERNAL SOURCES: 23 | EasyMakePhotoPicker: 24 | :path: "../" 25 | 26 | SPEC CHECKSUMS: 27 | EasyMakePhotoPicker: d7c6cf3acc9f30900b4d25643a8db68c393484f5 28 | RxAtomic: eacf60db868c96bfd63320e28619fe29c179656f 29 | RxCocoa: df63ebf7b9a70d6b4eeea407ed5dd4efc8979749 30 | RxSwift: 5976ecd04fc2fefd648827c23de5e11157faa973 31 | 32 | PODFILE CHECKSUM: fd1e973444ff2b42299291a5a4eb7db0d5bc78b7 33 | 34 | COCOAPODS: 1.5.3 35 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/EasyMakePhotoPicker/EasyMakePhotoPicker-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_EasyMakePhotoPicker : NSObject 3 | @end 4 | @implementation PodsDummy_EasyMakePhotoPicker 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/EasyMakePhotoPicker/EasyMakePhotoPicker-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/EasyMakePhotoPicker/EasyMakePhotoPicker-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double EasyMakePhotoPickerVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char EasyMakePhotoPickerVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/EasyMakePhotoPicker/EasyMakePhotoPicker.modulemap: -------------------------------------------------------------------------------- 1 | framework module EasyMakePhotoPicker { 2 | umbrella header "EasyMakePhotoPicker-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/EasyMakePhotoPicker/EasyMakePhotoPicker.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/EasyMakePhotoPicker 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RxAtomic" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/EasyMakePhotoPicker/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.1.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Example/Pods-EasyMakePhotoPicker_Example-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## EasyMakePhotoPicker 5 | 6 | Copyright (c) 2017 audrl1010 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | 27 | ## RxAtomic 28 | 29 | **The MIT License** 30 | **Copyright © 2015 Krunoslav Zaher** 31 | **All rights reserved.** 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 34 | 35 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 36 | 37 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 38 | 39 | ## RxCocoa 40 | 41 | **The MIT License** 42 | **Copyright © 2015 Krunoslav Zaher** 43 | **All rights reserved.** 44 | 45 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 46 | 47 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 48 | 49 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 50 | 51 | ## RxSwift 52 | 53 | **The MIT License** 54 | **Copyright © 2015 Krunoslav Zaher** 55 | **All rights reserved.** 56 | 57 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 58 | 59 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 60 | 61 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 62 | Generated by CocoaPods - https://cocoapods.org 63 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Example/Pods-EasyMakePhotoPicker_Example-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2017 audrl1010 <audrl1010@naver.com> 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | EasyMakePhotoPicker 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | **The MIT License** 47 | **Copyright © 2015 Krunoslav Zaher** 48 | **All rights reserved.** 49 | 50 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 51 | 52 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 53 | 54 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 55 | License 56 | MIT 57 | Title 58 | RxAtomic 59 | Type 60 | PSGroupSpecifier 61 | 62 | 63 | FooterText 64 | **The MIT License** 65 | **Copyright © 2015 Krunoslav Zaher** 66 | **All rights reserved.** 67 | 68 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 69 | 70 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 71 | 72 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 73 | License 74 | MIT 75 | Title 76 | RxCocoa 77 | Type 78 | PSGroupSpecifier 79 | 80 | 81 | FooterText 82 | **The MIT License** 83 | **Copyright © 2015 Krunoslav Zaher** 84 | **All rights reserved.** 85 | 86 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 87 | 88 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 89 | 90 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 91 | License 92 | MIT 93 | Title 94 | RxSwift 95 | Type 96 | PSGroupSpecifier 97 | 98 | 99 | FooterText 100 | Generated by CocoaPods - https://cocoapods.org 101 | Title 102 | 103 | Type 104 | PSGroupSpecifier 105 | 106 | 107 | StringsTable 108 | Acknowledgements 109 | Title 110 | Acknowledgements 111 | 112 | 113 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Example/Pods-EasyMakePhotoPicker_Example-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_EasyMakePhotoPicker_Example : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_EasyMakePhotoPicker_Example 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Example/Pods-EasyMakePhotoPicker_Example-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 63 | 64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 65 | code_sign_cmd="$code_sign_cmd &" 66 | fi 67 | echo "$code_sign_cmd" 68 | eval "$code_sign_cmd" 69 | fi 70 | } 71 | 72 | # Strip invalid architectures 73 | strip_invalid_archs() { 74 | binary="$1" 75 | # Get architectures for current file 76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 77 | stripped="" 78 | for arch in $archs; do 79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 80 | # Strip non-valid architectures in-place 81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 82 | stripped="$stripped $arch" 83 | fi 84 | done 85 | if [[ "$stripped" ]]; then 86 | echo "Stripped $binary of architectures:$stripped" 87 | fi 88 | } 89 | 90 | 91 | if [[ "$CONFIGURATION" == "Debug" ]]; then 92 | install_framework "$BUILT_PRODUCTS_DIR/EasyMakePhotoPicker/EasyMakePhotoPicker.framework" 93 | install_framework "$BUILT_PRODUCTS_DIR/RxCocoa/RxCocoa.framework" 94 | install_framework "$BUILT_PRODUCTS_DIR/RxSwift/RxSwift.framework" 95 | fi 96 | if [[ "$CONFIGURATION" == "Release" ]]; then 97 | install_framework "$BUILT_PRODUCTS_DIR/EasyMakePhotoPicker/EasyMakePhotoPicker.framework" 98 | install_framework "$BUILT_PRODUCTS_DIR/RxCocoa/RxCocoa.framework" 99 | install_framework "$BUILT_PRODUCTS_DIR/RxSwift/RxSwift.framework" 100 | fi 101 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 102 | wait 103 | fi 104 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Example/Pods-EasyMakePhotoPicker_Example-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_EasyMakePhotoPicker_ExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_EasyMakePhotoPicker_ExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Example/Pods-EasyMakePhotoPicker_Example.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/EasyMakePhotoPicker" "${PODS_CONFIGURATION_BUILD_DIR}/RxAtomic" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/EasyMakePhotoPicker/EasyMakePhotoPicker.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxAtomic/RxAtomic.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "EasyMakePhotoPicker" -framework "RxAtomic" -framework "RxCocoa" -framework "RxSwift" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Example/Pods-EasyMakePhotoPicker_Example.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_EasyMakePhotoPicker_Example { 2 | umbrella header "Pods-EasyMakePhotoPicker_Example-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Example/Pods-EasyMakePhotoPicker_Example.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/EasyMakePhotoPicker" "${PODS_CONFIGURATION_BUILD_DIR}/RxAtomic" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/EasyMakePhotoPicker/EasyMakePhotoPicker.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxAtomic/RxAtomic.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "EasyMakePhotoPicker" -framework "RxAtomic" -framework "RxCocoa" -framework "RxSwift" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Tests/Pods-EasyMakePhotoPicker_Tests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## RxAtomic 5 | 6 | **The MIT License** 7 | **Copyright © 2015 Krunoslav Zaher** 8 | **All rights reserved.** 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | ## RxCocoa 17 | 18 | **The MIT License** 19 | **Copyright © 2015 Krunoslav Zaher** 20 | **All rights reserved.** 21 | 22 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 23 | 24 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 25 | 26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | 28 | ## RxSwift 29 | 30 | **The MIT License** 31 | **Copyright © 2015 Krunoslav Zaher** 32 | **All rights reserved.** 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 35 | 36 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 37 | 38 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 39 | Generated by CocoaPods - https://cocoapods.org 40 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Tests/Pods-EasyMakePhotoPicker_Tests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | **The MIT License** 18 | **Copyright © 2015 Krunoslav Zaher** 19 | **All rights reserved.** 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 22 | 23 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | License 27 | MIT 28 | Title 29 | RxAtomic 30 | Type 31 | PSGroupSpecifier 32 | 33 | 34 | FooterText 35 | **The MIT License** 36 | **Copyright © 2015 Krunoslav Zaher** 37 | **All rights reserved.** 38 | 39 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 44 | License 45 | MIT 46 | Title 47 | RxCocoa 48 | Type 49 | PSGroupSpecifier 50 | 51 | 52 | FooterText 53 | **The MIT License** 54 | **Copyright © 2015 Krunoslav Zaher** 55 | **All rights reserved.** 56 | 57 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 58 | 59 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 60 | 61 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 62 | License 63 | MIT 64 | Title 65 | RxSwift 66 | Type 67 | PSGroupSpecifier 68 | 69 | 70 | FooterText 71 | Generated by CocoaPods - https://cocoapods.org 72 | Title 73 | 74 | Type 75 | PSGroupSpecifier 76 | 77 | 78 | StringsTable 79 | Acknowledgements 80 | Title 81 | Acknowledgements 82 | 83 | 84 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Tests/Pods-EasyMakePhotoPicker_Tests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_EasyMakePhotoPicker_Tests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_EasyMakePhotoPicker_Tests 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Tests/Pods-EasyMakePhotoPicker_Tests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 63 | 64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 65 | code_sign_cmd="$code_sign_cmd &" 66 | fi 67 | echo "$code_sign_cmd" 68 | eval "$code_sign_cmd" 69 | fi 70 | } 71 | 72 | # Strip invalid architectures 73 | strip_invalid_archs() { 74 | binary="$1" 75 | # Get architectures for current file 76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 77 | stripped="" 78 | for arch in $archs; do 79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 80 | # Strip non-valid architectures in-place 81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 82 | stripped="$stripped $arch" 83 | fi 84 | done 85 | if [[ "$stripped" ]]; then 86 | echo "Stripped $binary of architectures:$stripped" 87 | fi 88 | } 89 | 90 | 91 | if [[ "$CONFIGURATION" == "Debug" ]]; then 92 | install_framework "$BUILT_PRODUCTS_DIR/RxCocoa/RxCocoa.framework" 93 | install_framework "$BUILT_PRODUCTS_DIR/RxSwift/RxSwift.framework" 94 | fi 95 | if [[ "$CONFIGURATION" == "Release" ]]; then 96 | install_framework "$BUILT_PRODUCTS_DIR/RxCocoa/RxCocoa.framework" 97 | install_framework "$BUILT_PRODUCTS_DIR/RxSwift/RxSwift.framework" 98 | fi 99 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 100 | wait 101 | fi 102 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Tests/Pods-EasyMakePhotoPicker_Tests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_EasyMakePhotoPicker_TestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_EasyMakePhotoPicker_TestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Tests/Pods-EasyMakePhotoPicker_Tests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RxAtomic" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/EasyMakePhotoPicker" "${PODS_CONFIGURATION_BUILD_DIR}/RxAtomic" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxAtomic/RxAtomic.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/EasyMakePhotoPicker/EasyMakePhotoPicker.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxAtomic/RxAtomic.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "RxAtomic" -framework "RxCocoa" -framework "RxSwift" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Tests/Pods-EasyMakePhotoPicker_Tests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_EasyMakePhotoPicker_Tests { 2 | umbrella header "Pods-EasyMakePhotoPicker_Tests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-EasyMakePhotoPicker_Tests/Pods-EasyMakePhotoPicker_Tests.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RxAtomic" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/EasyMakePhotoPicker" "${PODS_CONFIGURATION_BUILD_DIR}/RxAtomic" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxAtomic/RxAtomic.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/EasyMakePhotoPicker/EasyMakePhotoPicker.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxAtomic/RxAtomic.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "RxAtomic" -framework "RxCocoa" -framework "RxSwift" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Example/Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import XCTest 3 | import EasyMakePhotoPicker 4 | 5 | class Tests: XCTestCase { 6 | 7 | override func setUp() { 8 | super.setUp() 9 | // Put setup code here. This method is called before the invocation of each test method in the class. 10 | } 11 | 12 | override func tearDown() { 13 | // Put teardown code here. This method is called after the invocation of each test method in the class. 14 | super.tearDown() 15 | } 16 | 17 | func testExample() { 18 | // This is an example of a functional test case. 19 | XCTAssert(true, "Pass") 20 | } 21 | 22 | func testPerformanceExample() { 23 | // This is an example of a performance test case. 24 | self.measure() { 25 | // Put the code you want to measure the time of here. 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 audrl1010 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------