How To Paginate when using PHAsset to Fetch User Photo library - ios

I am asking the same question as here
I do not understand how to implement to solution.
I have tried the following
fileprivate func fetchPhotos(indexSet: IndexSet) {
let allPhotos = PHAsset.fetchAssets(with: .image, options: assetsFetchOptions())
DispatchQueue.global(qos: .background).async {
allPhotos.enumerateObjects(at: indexSet, options: NSEnumerationOptions.concurrent, using: { (asset, count, stop) in
let imageManager = PHImageManager.default()
let targetSize = CGSize(width: 200, height: 200)
let options = PHImageRequestOptions()
options.isSynchronous = true
imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFit, options: options, resultHandler: { (image, info) in
if let image = image {
self.images.append(image)
self.assets.append(asset)
if self.selectedImage == nil {
self.selectedImage = image
}
}
DispatchQueue.main.async {
self.collectionView.reloadData()
self.hud.dismiss()
}
})
})
}
}
In cellForItemAt I tried doubling the indexes so the next 10 would load. The result i got was a never ending repetition of the first 10 posts.
Can someone please show the proper way to use this.

I tried to do what you want. I couldn't encapsulate it inside one function, coz it requires a few public variables, so here is the code for a UIViewController that has a UICollectionView which loads images by a page of 10 and when scrolled to the last cell it loads next 10 images and so on.
import UIKit
import Photos
import PhotosUI
class ImagesViewController: UIViewController ,UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout{
var assets = [PHAsset]()
var images = [UIImage]()
let page = 10
var beginIndex = 0
var endIndex = 9
var allPhotos : PHFetchResult<PHAsset>?
var loading = false
var hasNextPage = false
#IBOutlet weak var collectionView : UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
let options = PHFetchOptions()
options.includeHiddenAssets = true
allPhotos = PHAsset.fetchAssets(with: .image, options: options)
getImages()
}
func getImages(){
endIndex = beginIndex + (page - 1)
if endIndex > allPhotos!.count {
endIndex = allPhotos!.count - 1
}
let arr = Array(beginIndex...endIndex)
let indexSet = IndexSet(arr)
fetchPhotos(indexSet: indexSet)
}
fileprivate func fetchPhotos(indexSet: IndexSet) {
if allPhotos!.count == self.images.count {
self.hasNextPage = false
self.loading = false
return
}
self.loading = true
DispatchQueue.global(qos: .background).async { [weak self] in
self?.allPhotos?.enumerateObjects(at: indexSet, options: NSEnumerationOptions.concurrent, using: { (asset, count, stop) in
guard let weakSelf = self else {
return
}
let imageManager = PHImageManager.default()
let targetSize = CGSize(width: weakSelf.view.frame.size.height - 20, height: 250)
let options = PHImageRequestOptions()
options.isSynchronous = true
imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFit, options: options, resultHandler: { (image, info) in
if let image = image {
weakSelf.images.append(image)
weakSelf.assets.append(asset)
}
})
if weakSelf.images.count - 1 == indexSet.last! {
print("last element")
weakSelf.loading = false
weakSelf.hasNextPage = weakSelf.images.count != weakSelf.allPhotos!.count
weakSelf.beginIndex = weakSelf.images.count
DispatchQueue.main.async {
weakSelf.collectionView.reloadData()
}
}
})
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let imgView = cell.viewWithTag(1) as! UIImageView
imgView.image = self.images[indexPath.row]
if self.hasNextPage && !loading && indexPath.row == self.images.count - 1 {
getImages()
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width:collectionView.frame.size.width - 20,height: 250)
}
}

Refinement and issues Resolved , Now below code Working fine
import Photos
import PhotosUI
class PhotoGalleryPaginationViewController: UIViewController,UICollectionViewDataSource,UICollectionViewDelegate {
var assets = [PHAsset]()
var images = [UIImage]()
let page = 10
var beginIndex = 0
var endIndex = 9
var allPhotos : PHFetchResult<PHAsset>?
var loading = false
var hasNextPage = false
#IBOutlet weak var collectionView : UICollectionView!
// MARK :- LIFE CYCLE
override func viewDidLoad() {
super.viewDidLoad()
// Cell Register
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
// Fetch Gallery
let options = PHFetchOptions()
options.includeHiddenAssets = true
allPhotos = PHAsset.fetchAssets(with: .image, options: options)
getImages()
// Do any additional setup after loading the view.
}
// MARK :- Methods
func getImages(){
endIndex = beginIndex + (page - 1)
if endIndex > allPhotos!.count {
endIndex = allPhotos!.count - 1
}
if endIndex > beginIndex {
let arr = Array(beginIndex...endIndex)
let indexSet = IndexSet(arr)
fetchPhotos(indexSet: indexSet)
}
}
fileprivate func fetchPhotos(indexSet: IndexSet) {
if allPhotos!.count == self.images.count {
self.hasNextPage = false
self.loading = false
return
}
self.loading = true
let targetSize = CGSize(width: 200, height: 200)
DispatchQueue.global(qos: .background).async { [weak self] in
self?.allPhotos?.enumerateObjects(at: indexSet, options: NSEnumerationOptions.concurrent, using: { (asset, count, stop) in
guard let weakSelf = self else {
return
}
let imageManager = PHImageManager.default()
let options = PHImageRequestOptions()
options.isSynchronous = true
imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFit, options: options, resultHandler: { (image, info) in
if let image = image {
weakSelf.images.append(image)
weakSelf.assets.append(asset)
}
})
if weakSelf.images.count - 1 == indexSet.last! {
print("last element")
weakSelf.loading = false
weakSelf.hasNextPage = weakSelf.images.count != weakSelf.allPhotos!.count
weakSelf.beginIndex = weakSelf.images.count
DispatchQueue.main.async {
weakSelf.collectionView.reloadData()
}
}
})
}
}
// MARK :- CollectionView DataSource and Delegate
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("image Count = \(images.count)")
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let imgView = UIImageView()
imgView.frame = CGRect(x: 0, y: 0, width: 150, height: 150)
imgView.image = self.images[indexPath.row]
cell.contentView.addSubview(imgView)
cell.clipsToBounds = true
// Fetch new Images
if self.hasNextPage && !loading && indexPath.row == self.images.count - 1 {
getImages()
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width:collectionView.frame.width/2,height: 150)
}
}

Related

How to cache and retrieve Feed data from Firebase Storage in swift?

So I'm making an app where one of the view has a canvas view where you can draw images and upload to Firebase Storage image database (under a folder named Feed Images), and in the other view you get all the images that was shared by others as well as you. So first thing I gotta do is ask Firestore to get me the list of all items from the child Folder and then use those URL's from the result array it provided me to download the images.
My problem is most of the solutions here tell how to cache on the assumption that you have the urls to images but how do you cache the array of images when you do not have their urls such as in this case?
Also I would like to know a better way to download all files from my 'FeedImages' folder and then listen to my folder for changes and Update the feed accordingly and Cache!!! those Images.
Here is my code for my Feed View Controller:
import UIKit
import Firebase
let imageCache = NSCache<AnyObject, AnyObject>()
class FeedVC: UICollectionViewController, UICollectionViewDelegateFlowLayout {
var folderList: [StorageReference]?
var storage = Storage.storage()
var downloadedImages : [UIImage] = []
let cellId = "FeedCell"
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.collectionView.reloadData()
self.storage.reference().child("FeedImages").listAll(completion: {
(result,error) in
print("result is \(result.items)")
self.folderList = result.items
DispatchQueue.main.async {
self.folderList?.forEach({ (refer) in
self.load(storageRef: refer)
})
self.collectionView.reloadData()
}
})
}
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView.backgroundColor = .offWhite
collectionView.delegate = self
// collectionView.datasource = self
collectionView.register(FeedCell.self, forCellWithReuseIdentifier: cellId)
}
//
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return downloadedImages.count ?? 5
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! FeedCell
if(downloadedImages.count > 0){
if(downloadedImages[indexPath.row] != nil){
print("Loading downloaded Image....")
cell.img.image = downloadedImages[indexPath.row]
}
}
else{
cell.img.image = UIImage(named: "Placeholder")
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (view.frame.width / 3) - 20, height: 100)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10 )
}
func load(storageRef: StorageReference) {
storageRef.downloadURL(completion: {(downloadURL,error) in
// retrieves image if already available in cache
if let imageFromCache = imageCache.object(forKey: downloadURL as AnyObject) as? UIImage {
if(self.downloadedImages.count == 0){
self.downloadedImages.append(imageFromCache)
}
return
}
if(error != nil){
print(error.debugDescription)
return
}
// print("url is \(downloadURL!)")
print("Download Started")
self.getData(from: downloadURL!) { data, response, error in
guard let data = data, error == nil else { return }
// print(response?.suggestedFilename ?? downloadURL!.lastPathComponent)
print("Download Finished")
DispatchQueue.main.async() { [weak self] in
let imageToCache = UIImage(data: data)
self!.downloadedImages.append(imageToCache!)
imageCache.setObject(imageToCache as AnyObject, forKey: downloadURL as AnyObject)
self?.collectionView.reloadData()
}
}
})
}
func getData(from url: URL, completion: #escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}
}
class FeedCell : UICollectionViewCell{
var img = UIImageView()
override init(frame: CGRect) {
super.init(frame : frame)
Setup()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func Setup(){
self.layer.cornerRadius = 25
self.contentView.addSubview(img)
img.image = UIImage(named: "Placeholder")
img.translatesAutoresizingMaskIntoConstraints = false
img.topAnchor.constraint(equalTo: self.contentView.topAnchor).isActive = true
img.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor).isActive = true
img.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor).isActive = true
img.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor).isActive = true
}
}

Scrollview not scrolling in right

I have a scroll vie and i want to display the images selected by user in horizontal scroll manner. The following is my code. but, I am unable to achieve that. please guide me.
var xPosition: CGFloat = 0
var scrollViewContentWidth: CGFloat = 398
func handlePickedImage(image: UIImage){
let imageView = UIImageView(image: image)
imageView.frame = CGRect(x: 0, y: 0, width: 398, height: 188)
imageView.contentMode = UIView.ContentMode.scaleAspectFit
imageView.frame.origin.x = xPosition
imageView.frame.origin.y = 10
let spacer: CGFloat = 10
xPosition = 398 + spacer
scrollViewContentWidth = scrollViewContentWidth + spacer
imageScrollView.contentSize = CGSize(width: scrollViewContentWidth, height: 188)
imageScrollView.addSubview(imageView)
}
I have created just that with this code, it also has pagination implemented in it:
import UIKit
class ImagePagerViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
var data: [FoodPostImageObject] = []
var userId: Int?
var indexPath: IndexPath?
var page: Int = 1
var alreadyFetchingData = false
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: self.view.frame.width, height: self.view.frame.height)
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
layout.scrollDirection = .horizontal
collectionView.collectionViewLayout = layout
if userId != nil {
getUserImages()
}
}
override func viewDidLayoutSubviews() {
guard self.indexPath != nil else {return}
self.collectionView.scrollToItem(at: self.indexPath!, at: .right, animated: false)
}
}
extension ImagePagerViewController: UICollectionViewDataSource, UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImagePagerCell", for: indexPath) as! ImagePagerCollectionViewCell
cell.image.loadImageUsingUrlString(urlString: data[indexPath.row].food_photo)
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
}
extension ImagePagerViewController {
func fetchMoreData(){
if !alreadyFetchingData {
if userId != nil {
getUserImages()
}
}
}
func getUserImages(){
guard let userId = self.userId else {return}
alreadyFetchingData = true
Server.get("/user_images/\(userId)/\(page)/"){ data, response, error in
self.alreadyFetchingData = false
guard let data = data else {return}
do {
self.data.append(contentsOf: try JSONDecoder().decode([FoodPostImageObject].self, from: data))
DispatchQueue.main.async {
self.collectionView.reloadData()
self.collectionView.scrollToItem(at: self.indexPath!, at: .right, animated: false)
self.page += 1
}
} catch {}
}
}
}
and this UICollectionViewCell:
import UIKit
class ImagePagerCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var image: CellImageView!
}
In the storyboard I just have a collectionView and ImagePagerCollectionViewCell inside.
Hope this helps

How to display the photolibray as a UICollectionView or as an Asset Grid on Swift

What's the best way to produce something similar to a Twitter upload page where about 10 photo library pictures are displayed for the user to select one?
Below is the picture of what I'm trying to do.
Here is what I have done so far. However, it does not show when I run the app.
import photos
class UploadPostVC: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var imageArray = [UIImage]()
#IBOutlet weak var showPhotosCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
grabPhotos()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
let imageView = cell.viewWithTag(1) as! UIImageView
print("wassup")
imageView.image = self.imageArray[indexPath.row]
return cell
}
func grabPhotos(){
let imgManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
requestOptions.deliveryMode = .highQualityFormat
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let fetchResult : PHFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
if fetchResult.count > 0 {
for i in 0..<fetchResult.count{
//Used for fetch Image//
imgManager.requestImage(for: fetchResult.object(at: i) as PHAsset , targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFill, options: requestOptions, resultHandler: {
image, error in
let image = image! as UIImage
self.imageArray.append(image)
self.showPhotosCollectionView.reloadData()
print ("appended images")
})
self.showPhotosCollectionView.reloadData()
}
}
else {
print("you got no phots")
self.showPhotosCollectionView.reloadData()
}
}
You should set the collection view's delegate and dataSource. Just change your viewDidLoad function :
override func viewDidLoad() {
super.viewDidLoad()
self.showPhotosCollectionView.delegate = self
self.showPhotosCollectionView.dataSource = self
grabPhotos()
}

Questions about removing ios photo asset in Swift

This source code is a photo app like the iPhone's photo app.
When you launch the app, each Asset that is a CollectionViewCell is shown.
What I would like to ask is the ability to select and delete an image asset. If you look at the iPhone photo app, you can press the select button to select photos and delete and share selected photos. You can choose as many photos as you want rather than just one. I have implemented #IBAction selectButtonPressed.
class PhotoCollectionViewController: UICollectionViewController {
#IBOutlet weak var sortButton: UIBarButtonItem!
#IBOutlet weak var selectButton: UIBarButtonItem!
#IBOutlet weak var actionButton: UIBarButtonItem!
#IBOutlet weak var trashButton: UIBarButtonItem!
// MARK:- Properties
var fetchResult: PHFetchResult<PHAsset>? {
didSet {
OperationQueue.main.addOperation {
self.collectionView?.reloadSections(IndexSet(0...0))
}
}
}
var assetCollection: PHAssetCollection?
// MARK:- Privates
private let cellReuseIdentifier: String = "photoCell"
private lazy var cachingImageManager: PHCachingImageManager = {
return PHCachingImageManager()
}()
// MARK:- Life Cycle
deinit {
PHPhotoLibrary.shared().unregisterChangeObserver(self)
}
#IBAction func sortButtonPressed(_ sender: UIBarButtonItem) {
let fetchOptions: PHFetchOptions = PHFetchOptions()
if (self.sortButton.title == "In the past") {
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "modificationDate",
ascending: false)]
self.fetchResult = PHAsset.fetchAssets(in: assetCollection!, options: fetchOptions )
self.sortButton.title = "The latest"
} else if (self.sortButton.title == "The latest") {
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate",
ascending: true)]
self.fetchResult = PHAsset.fetchAssets(in: assetCollection!, options: fetchOptions )
self.sortButton.title = "In the past"
}
}
#IBAction func seletButtonPressed(_ sender: Any) {
if (self.sortButton.isEnabled == true) {
self.sortButton.isEnabled = false
self.actionButton.isEnabled = true
self.trashButton.isEnabled = true
} else if (self.sortButton.isEnabled == false) {
self.sortButton.isEnabled = true
self.actionButton.isEnabled = false
self.trashButton.isEnabled = false
}
PHPhotoLibrary.shared().performChanges({
//Delete Photo
PHAssetChangeRequest.deleteAssets(self.fetchResult!)
},
completionHandler: {(success, error)in
NSLog("\nDeleted Image -> %#", (success ? "Success":"Error!"))
if(success){
}else{
print("Error: \(error)")
}
})
}
}
extension PhotoCollectionViewController {
private func configureCell(_ cell: PhotoCollectionViewCell,
collectionView: UICollectionView,
indexPath: IndexPath) {
guard let asset: PHAsset = self.fetchResult?.object(at: indexPath.item) else { return }
let manager: PHCachingImageManager = self.cachingImageManager
let handler: (UIImage?, [AnyHashable:Any]?) -> Void = { image, _ in
let cellAtIndex: UICollectionViewCell? = collectionView.cellForItem(at: indexPath)
guard let cell: PhotoCollectionViewCell = cellAtIndex as? PhotoCollectionViewCell
else { return }
cell.imageView.image = image
}
manager.requestImage(for: asset,
targetSize: CGSize(width: 100, height: 100),
contentMode: PHImageContentMode.aspectFill,
options: nil,
resultHandler: handler)
}
}
// MARK:- UICollectionViewDataSource
extension PhotoCollectionViewController {
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return self.fetchResult?.count ?? 0
}
}
extension PhotoCollectionViewController {
override func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: PhotoCollectionViewCell
cell = collectionView.dequeueReusableCell(withReuseIdentifier: self.cellReuseIdentifier,
for: indexPath) as! PhotoCollectionViewCell
return cell
}
override func collectionView(_ collectionView: UICollectionView,
willDisplay cell: UICollectionViewCell,
forItemAt indexPath: IndexPath) {
guard let cell: PhotoCollectionViewCell = cell as? PhotoCollectionViewCell else {
return
}
self.configureCell(cell, collectionView: collectionView, indexPath: indexPath)
}
}
// MARK:- UICollectionViewDelegateFlowLayout
extension PhotoCollectionViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
guard let flowLayout: UICollectionViewFlowLayout =
self.collectionViewLayout as? UICollectionViewFlowLayout else { return CGSize.zero}
let numberOfCellsInRow: CGFloat = 4
let viewSize: CGSize = self.view.frame.size
let sectionInset: UIEdgeInsets = flowLayout.sectionInset
let interitemSpace: CGFloat = flowLayout.minimumInteritemSpacing * (numberOfCellsInRow - 1)
var itemWidth: CGFloat
itemWidth = viewSize.width - sectionInset.left - sectionInset.right - interitemSpace
itemWidth /= numberOfCellsInRow
let itemSize = CGSize(width: itemWidth, height: itemWidth)
return itemSize
}
}
extension PhotoCollectionViewController {
private func updateCollectionView(with changes: PHFetchResultChangeDetails<PHAsset>) {
guard let collectionView = self.collectionView else { return }
// 업데이트는 삭제, 삽입, 다시 불러오기, 이동 순으로 진행합니다
if let removed: IndexSet = changes.removedIndexes, removed.count > 0 {
collectionView.deleteItems(at: removed.map({
IndexPath(item: $0, section: 0)
}))
}
if let inserted: IndexSet = changes.insertedIndexes, inserted.count > 0 {
collectionView.insertItems(at: inserted.map({
IndexPath(item: $0, section: 0)
}))
}
if let changed: IndexSet = changes.changedIndexes, changed.count > 0 {
collectionView.reloadItems(at: changed.map({
IndexPath(item: $0, section: 0)
}))
}
changes.enumerateMoves { fromIndex, toIndex in
collectionView.moveItem(at: IndexPath(item: fromIndex, section: 0),
to: IndexPath(item: toIndex, section: 0))
}
}
}
// MARK:- PHPhotoLibraryChangeObserver
extension PhotoCollectionViewController: PHPhotoLibraryChangeObserver {
private func resetCachedAssets() {
self.cachingImageManager.stopCachingImagesForAllAssets()
}
func photoLibraryDidChange(_ changeInstance: PHChange) {
guard let fetchResult: PHFetchResult<PHAsset> = self.fetchResult
else { return }
guard let changes: PHFetchResultChangeDetails<PHAsset> =
changeInstance.changeDetails(for: fetchResult)
else { return }
DispatchQueue.main.sync {
self.resetCachedAssets()
self.fetchResult = changes.fetchResultAfterChanges
if changes.hasIncrementalChanges {
self.updateCollectionView(with: changes)
} else {
self.collectionView?.reloadSections(IndexSet(0...0))
}
}
}
}
extension PhotoCollectionViewController {
// MARK:- Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
PHPhotoLibrary.shared().register(self)
self.sortButton.title = "In the past"
self.actionButton.isEnabled = false
self.trashButton.isEnabled = false
}
}
AssetCollection can be selected by clicking the select barButtonItem in the upper right corner of the screen, and I want to delete or share selected pictures.
You can perfore delete action on phAsset as below:
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
//Delete Photo
PHAssetChangeRequest.deleteAssets(delShotsAsset)
},
completionHandler: {(success, error)in
NSLog("\nDeleted Image -> %#", (success ? "Success":"Error!"))
if(success){
}else{
println("Error: \(error)")
}
})

why collection view in empty when I fetch photos from gallery in swift 3?

I am Using swift 3 and want to see photo library IMAGES in the collection view But these codes doesn't work for me
before seeing my codes Attention to this :
**I will not receive any errors or crash I just can't see any of my image library in the collection view **
here is my codes :
import UIKit
import Photos
class collectionImageViewController: UIViewController , UICollectionViewDataSource , UICollectionViewDelegate {
var imageArray = [UIImage]()
#IBOutlet weak var collectionImageView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewImageCell", for: indexPath) as! CollectionViewImageCell
cell.theImage.image = imageArray[indexPath.row]
return cell
}
func getPhotosFromAlbum() {
let imageManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
requestOptions.deliveryMode = .highQualityFormat
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
if fetchResult.count > 0 {
for i in 0..<fetchResult.count {
imageManager.requestImage(for: fetchResult.object(at: i), targetSize: CGSize(width: 300, height: 300), contentMode: .aspectFill, options: requestOptions, resultHandler: { image, error in
self.imageArray.append(image!)
})
}
} else {
self.collectionImageView?.reloadData()
}
}
This is my whole code to load all images from gallery and load into collectioview. Please see this code
import UIKit
import Photos
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
let arr_img = NSMutableArray()
#IBOutlet var collview: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
let allPhotosOptions : PHFetchOptions = PHFetchOptions.init()
allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
let allPhotosResult = PHAsset.fetchAssets(with: .image, options: allPhotosOptions)
allPhotosResult.enumerateObjects({ (asset, idx, stop) in
self.arr_img.add(asset)
})
}
func getAssetThumbnail(asset: PHAsset, size: CGFloat) -> UIImage {
let retinaScale = UIScreen.main.scale
let retinaSquare = CGSize(width: size * retinaScale, height: size * retinaScale)//CGSizeMake(size * retinaScale, size * retinaScale)
let cropSizeLength = min(asset.pixelWidth, asset.pixelHeight)
let square = CGRect(x: 0, y: 0, width: cropSizeLength, height: cropSizeLength)//CGRectMake(0, 0, CGFloat(cropSizeLength), CGFloat(cropSizeLength))
let cropRect = square.applying(CGAffineTransform(scaleX: 1.0/CGFloat(asset.pixelWidth), y: 1.0/CGFloat(asset.pixelHeight)))
let manager = PHImageManager.default()
let options = PHImageRequestOptions()
var thumbnail = UIImage()
options.isSynchronous = true
options.deliveryMode = .highQualityFormat
options.resizeMode = .exact
options.normalizedCropRect = cropRect
manager.requestImage(for: asset, targetSize: retinaSquare, contentMode: .aspectFit, options: options, resultHandler: {(result, info)->Void in
thumbnail = result!
})
return thumbnail
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK:
//MARK: Collectioview methods
func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return arr_img.count
}
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell",
for: indexPath)
let imgview : UIImageView = cell.viewWithTag(20) as! UIImageView
imgview.image = self.getAssetThumbnail(asset: self.arr_img.object(at: indexPath.row) as! PHAsset, size: 150)
return cell
}
}
You must reload the collectionView after your for loop like so
...
{
for i in 0..<fetchResult.count {
imageManager.requestImage(for: fetchResult.object(at: i), targetSize: CGSize(width: 300, height: 300), contentMode: .aspectFill, options: requestOptions, resultHandler: { image, error in
self.imageArray.append(image!)
})
self.collectionImageView?.reloadData()
}
...

Resources