Skip to content

Instantly share code, notes, and snippets.

@leoneparise
Created December 6, 2016 14:07
Show Gist options
  • Save leoneparise/eec094d0ed9558eaf801c9e5f573cab2 to your computer and use it in GitHub Desktop.
Save leoneparise/eec094d0ed9558eaf801c9e5f573cab2 to your computer and use it in GitHub Desktop.
Saved Images View Controller
//
// 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