Created
December 6, 2016 14:07
-
-
Save leoneparise/eec094d0ed9558eaf801c9e5f573cab2 to your computer and use it in GitHub Desktop.
Saved Images View Controller
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SavedImagesViewController.swift | |
// Kuoter | |
// | |
// Created by Leone Parise Vieira da Silva on 01/10/16. | |
// Copyright © 2016 Kuoter. All rights reserved. | |
// | |
import UIKit | |
import RxDataSources | |
import RxCocoa | |
import RxSwift | |
import RealmSwift | |
import MisterFusion | |
import Alamofire | |
import RxAlamofire | |
import Photos | |
import SVProgressHUD | |
import Async | |
private let cellId = "ImageCellId" | |
class SavedImagesViewController:RxViewController<SavedImagesViewModel>, NavigationAwareViewController, AlertViewControllerType { | |
@IBOutlet weak var collectionView:GridCollectionView! | |
@IBOutlet weak var deleteButton:UIBarButtonItem! | |
@IBOutlet weak var saveButton:UIBarButtonItem! | |
@IBOutlet weak var toolBar:UIToolbar! | |
@IBOutlet weak var toolBarBottom: NSLayoutConstraint! | |
private var dataSource:RxCollectionViewSectionedAnimatedDataSource<ImageSection>! | |
private var emptyView:UIView! | |
override func viewDidLoad() { | |
emptyView = R.nib.savedImagesEmptyView.firstView(owner: self)! | |
self.view.addLayoutSubview(emptyView, andConstraints: | |
self.emptyView.Top |-| self.top, self.emptyView.Left, self.emptyView.Right, self.emptyView.Bottom | |
) | |
title = "Images I saved" | |
track(AppEvents.savedImagesOpen) | |
collectionView.columns = 3 | |
collectionView.registerClass(ImageCollectionViewCell.self, forCellWithReuseIdentifier: cellId) | |
collectionView.allowsMultipleSelection = true | |
dataSource = RxCollectionViewSectionedAnimatedDataSource<ImageSection>() | |
dataSource.animationConfiguration = AnimationConfiguration(insertAnimation: .Fade, | |
reloadAnimation: .Fade, | |
deleteAnimation: .Fade) | |
dataSource.configureCell = { ds, collectionView, indexPath, item in | |
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellId, forIndexPath: indexPath) as! ImageCollectionViewCell | |
cell.imageURL = item.url | |
cell.selected = item.selected | |
if item.selected { | |
collectionView.selectItemAtIndexPath(indexPath, animated: false, scrollPosition: .None) | |
} else { | |
collectionView.deselectItemAtIndexPath(indexPath, animated: false) | |
} | |
return cell | |
} | |
setupSideMenu() | |
super.viewDidLoad() | |
} | |
override func bindToViewModel() { | |
viewModel.images.bindTo(collectionView.rx_itemsWithDataSource(dataSource)) | |
.addDisposableTo(rx_disposeBag) | |
collectionView.rx_modelSelected(ImageItem.self).bindTo(viewModel.inputs.addItem) | |
.addDisposableTo(rx_disposeBag) | |
collectionView.rx_modelDeselected(ImageItem.self).bindTo(viewModel.inputs.deleteItem) | |
.addDisposableTo(rx_disposeBag) | |
viewModel.empty.bindTo(emptyView.rx_visible) | |
.addDisposableTo(rx_disposeBag) | |
deleteButton.rx_tap | |
.track(AppEvents.imageDelete) | |
.bindTo(viewModel.inputs.delete) | |
.addDisposableTo(rx_disposeBag) | |
saveButton.rx_tap | |
.track(AppEvents.imageGallerySave) | |
.bindTo(viewModel.inputs.save) | |
.addDisposableTo(rx_disposeBag) | |
viewModel.toolbarVisible.subscribeNext { [unowned self] (visible) in | |
self.toggleToolbar(visible, animated: true) | |
}.addDisposableTo(rx_disposeBag) | |
viewModel.saveResult.subscribeNext {[weak self] (result) in | |
switch result { | |
case .Success: | |
Async.main(after: 0.4){ | |
SVProgressHUD.showSuccessWithStatus("Images saved!") | |
} | |
case .Failure: | |
self?.showAlert(title: "Oh oh!", message: "An error happened when we tried to save images to your gallery. Check your settings and try again") | |
} | |
}.addDisposableTo(rx_disposeBag) | |
viewModel.executing | |
.drive(SVProgressHUD.rx_visible) | |
.addDisposableTo(rx_disposeBag) | |
super.bindToViewModel() | |
} | |
private func toggleToolbar(visible:Bool, animated:Bool = true) { | |
toolBarBottom.constant = visible ? 0 : -50 | |
if animated { | |
UIView.animateWithDuration(0.3) { [unowned self] in | |
self.view.layoutIfNeeded() | |
} | |
} else { | |
self.view.layoutIfNeeded() | |
} | |
} | |
} | |
class SavedImagesViewModel:RxViewModel { | |
let inputs = ( | |
addItem:PublishSubject<ImageItem>(), | |
deleteItem:PublishSubject<ImageItem>(), | |
delete:PublishSubject<Void>(), | |
save:PublishSubject<Void>() | |
) | |
var images:Observable<[ImageSection]>! | |
var toolbarVisible:Observable<Bool>! | |
var empty:Observable<Bool>! | |
var saveResult:Observable<RxResult<Void>>! | |
private var selected = Variable<Set<ImageItem>>(Set<ImageItem>()) | |
init(imageManager:ImageManager) { | |
super.init() | |
images = imageManager.rx_savedImages | |
.map{ images in images.map{ $0.asImageItem } } | |
.map{ [ImageSection(items:$0)] } | |
toolbarVisible = selected.asObservable().map{ $0.count > 0 } | |
inputs.addItem.subscribeNext {[unowned self] (item) in | |
self.selected.value.insert(item) | |
item.selected = true | |
}.addDisposableTo(rx_disposeBag) | |
inputs.deleteItem.subscribeNext { [unowned self] (item) in | |
self.selected.value.remove(item) | |
item.selected = false | |
}.addDisposableTo(rx_disposeBag) | |
inputs.delete.withLatestFrom(selected.asObservable()) | |
.doOnNext{ (selected) in | |
for item in selected { | |
try imageManager.deleteImage(item.url) | |
} | |
} | |
.subscribeNext {[unowned self] (selected) in | |
self.selected.value = Set() | |
} | |
.addDisposableTo(rx_disposeBag) | |
empty = images.map{ $0[0].items.count == 0 } | |
// MARK: Save images to gallery | |
func downloadImages(images:Set<ImageItem>) -> Observable<[UIImage]> { | |
return images.map{ item -> Observable<UIImage?> in | |
Alamofire.request(.GET, item.url) | |
.rx_data() | |
.map{ UIImage(data:$0) } | |
}.zip { (images) -> [UIImage] in | |
images.flatMap{ $0 } | |
.filter{ $0 != nil } | |
.map{ $0! } | |
} | |
.trackActivity(self.activityIndicator) | |
.observeOn(MainScheduler.instance) | |
} | |
func saveImages(images:[UIImage]) -> Observable<Void> { | |
let library = PHPhotoLibrary.sharedPhotoLibrary() | |
return images.map { image -> Observable<Bool> in | |
library.rx_saveImage(image) | |
}.zip { _ in | |
return true | |
} | |
.mapToVoid() | |
.trackActivity(self.activityIndicator) | |
.observeOn(MainScheduler.instance) | |
} | |
saveResult = inputs.save.withLatestFrom(selected.asObservable()) | |
.flatMapLatest(downloadImages) | |
.flatMapLatest(saveImages) | |
.shareReplayLatestWhileConnected() | |
.mapToResult() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment