Swift - ¿cómo obtener la última 3 fotos tomadas de la biblioteca de fotos?


Necesito obtener y mostrar las últimas 3 fotos tomadas de la biblioteca de fotos en el evento viewDidLoad sin ningún clic.

Después de este paso debo obtener otras fotos 3 por 3 cuando me desplazo en la vista de desplazamiento.

¿Conoces la forma correcta de hacer esto con swift? Gracias.

Muestra

Author: fatihyildizhan, 2015-02-01

5 answers

Aquí hay una solución que utiliza el marco Photos disponible para dispositivos iOS 8+:

import Photos

class ViewController: UIViewController {

    var images:[UIImage] = []

    func fetchPhotos () {
        // Sort the images by descending creation date and fetch the first 3
        let fetchOptions = PHFetchOptions()
        fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: false)]
        fetchOptions.fetchLimit = 3

        // Fetch the image assets
        let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions)

        // If the fetch result isn't empty,
        // proceed with the image request
        if fetchResult.count > 0 {
            let totalImageCountNeeded = 3 // <-- The number of images to fetch
            fetchPhotoAtIndex(0, totalImageCountNeeded, fetchResult)
        }
    }

    // Repeatedly call the following method while incrementing
    // the index until all the photos are fetched
    func fetchPhotoAtIndex(_ index:Int, _ totalImageCountNeeded: Int, _ fetchResult: PHFetchResult<PHAsset>) {

        // Note that if the request is not set to synchronous
        // the requestImageForAsset will return both the image
        // and thumbnail; by setting synchronous to true it
        // will return just the thumbnail
        let requestOptions = PHImageRequestOptions()
        requestOptions.isSynchronous = true

        // Perform the image request
        PHImageManager.default().requestImage(for: fetchResult.object(at: index) as PHAsset, targetSize: view.frame.size, contentMode: PHImageContentMode.aspectFill, options: requestOptions, resultHandler: { (image, _) in
            if let image = image {
                // Add the returned image to your array
                self.images += [image]
            }
            // If you haven't already reached the first
            // index of the fetch result and if you haven't
            // already stored all of the images you need,
            // perform the fetch request again with an
            // incremented index
            if index + 1 < fetchResult.count && self.images.count < totalImageCountNeeded {
                self.fetchPhotoAtIndex(index + 1, totalImageCountNeeded, fetchResult)
            } else {
                // Else you have completed creating your array
                print("Completed array: \(self.images)")
            }
        })
    }
}
 41
Author: Lyndsey Scott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-13 18:02:10

Detalles

XCode 8.3, Swift 3.1

Código

import UIKit
import Photos

class PhotoLibrary {

    fileprivate var imgManager: PHImageManager
    fileprivate var requestOptions: PHImageRequestOptions
    fileprivate var fetchOptions: PHFetchOptions
    fileprivate var fetchResult: PHFetchResult<PHAsset>

    init () {
        imgManager = PHImageManager.default()
        requestOptions = PHImageRequestOptions()
        requestOptions.isSynchronous = true
        fetchOptions = PHFetchOptions()
        fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: true)]
        fetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions)
    }

    var count: Int {
        return fetchResult.count
    }

    func setPhoto(at index: Int, completion block: @escaping (UIImage?)->()) {

        if index < fetchResult.count  {
            imgManager.requestImage(for: fetchResult.object(at: index) as PHAsset, targetSize: UIScreen.main.bounds.size, contentMode: PHImageContentMode.aspectFill, options: requestOptions) { (image, _) in
                block(image)
            }
        } else {
            block(nil)
        }
    }

    func getAllPhotos() -> [UIImage] {

        var resultArray = [UIImage]()
        for index in 0..<fetchResult.count {
            imgManager.requestImage(for: fetchResult.object(at: index) as PHAsset, targetSize: UIScreen.main.bounds.size, contentMode: PHImageContentMode.aspectFill, options: requestOptions) { (image, _) in

                if let image = image {
                    resultArray.append(image)
                }
            }
        }
        return resultArray
    }
}

Uso

 var photoLibrary = PhotoLibrary()
 // Number of all photos
 photoLibrary.count
 // Get photo
 self.photoLibrary.setPhoto(at: indexPath.row) { image in
      if let image = image {
              DispatchQueue.main.async {
                    imageView.image = image
              }
      }
 }

Muestra completa (collectionView con imágenes de PhotoLibrary)

Info.plist

Añadir a la Información.plist

<key>NSPhotoLibraryUsageDescription</key>
<string>{bla-bla-bla}</string>

ViewController.swift

import UIKit
import Photos

class ViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!
    @IBOutlet weak var collectionViewFlowLayout: UICollectionViewFlowLayout!

    fileprivate var photoLibrary: PhotoLibrary!
    fileprivate var numberOfSections = 0

    override func viewDidLoad() {
        initCollectionView()
        PHPhotoLibrary.requestAuthorization { [weak self] result in
            if let _self = self {
                if result == .authorized {
                    _self.photoLibrary = PhotoLibrary()
                    _self.numberOfSections = 1
                     DispatchQueue.main.async {
                        _self.collectionView.reloadData()
                    }
                }
            }
        }
    }
}

extension ViewController: UICollectionViewDataSource {

    fileprivate var numberOfElementsInRow: Int {
        return 4
    }

    var sizeForCell: CGSize {
        let _numberOfElementsInRow = CGFloat(numberOfElementsInRow)
        let allWidthBetwenCells = _numberOfElementsInRow == 0 ? 0 : collectionViewFlowLayout.minimumInteritemSpacing*(_numberOfElementsInRow-1)
        let width = (collectionView.frame.width - allWidthBetwenCells)/_numberOfElementsInRow
        return CGSize(width: width, height: width)
    }

    func initCollectionView() {
        collectionView.dataSource = self
        collectionView.delegate = self
    }

    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return numberOfSections
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return photoLibrary.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
        return cell
    }

}

extension ViewController: UICollectionViewDelegateFlowLayout {

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return sizeForCell
    }

    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        let cell = cell as! CollectionViewCell
        cell.cellImageView.image = nil
        DispatchQueue.global(qos: .background).async {
            self.photoLibrary.setPhoto(at: indexPath.row) { image in
                if let image = image {
                    DispatchQueue.main.async {
                        cell.cellImageView.image = image
                    }
                }
            }
        }
    }
}

CollectionViewCell.swift

import UIKit
class CollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var cellImageView: UIImageView!
}

PhotoLibrary.swift

Descrito anteriormente

Main.storyboard

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12118" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
    <device id="retina4_7" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12086"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="tne-QT-ifu">
            <objects>
                <viewController id="BYZ-38-t0r" customClass="ViewController" customModule="stackoverflow_28259961" customModuleProvider="target" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="IZe-8T-NdF">
                                <rect key="frame" x="0.0" y="20" width="375" height="647"/>
                                <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
                                <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="2" minimumInteritemSpacing="2" id="DNv-oA-G6j">
                                    <size key="itemSize" width="100" height="100"/>
                                    <size key="headerReferenceSize" width="0.0" height="0.0"/>
                                    <size key="footerReferenceSize" width="0.0" height="0.0"/>
                                    <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
                                </collectionViewFlowLayout>
                                <cells>
                                    <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="CollectionViewCell" id="FND-c4-nYC" customClass="CollectionViewCell" customModule="stackoverflow_28259961" customModuleProvider="target">
                                        <rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
                                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                        <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
                                            <rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
                                            <autoresizingMask key="autoresizingMask"/>
                                            <subviews>
                                                <imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="FA0-5K-Pxh">
                                                    <rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
                                                </imageView>
                                            </subviews>
                                        </view>
                                        <color key="backgroundColor" red="0.82995896879999997" green="0.82995896879999997" blue="0.82995896879999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <constraints>
                                            <constraint firstItem="FA0-5K-Pxh" firstAttribute="top" secondItem="FND-c4-nYC" secondAttribute="top" id="0WE-w2-xTC"/>
                                            <constraint firstAttribute="trailing" secondItem="FA0-5K-Pxh" secondAttribute="trailing" id="7rj-8k-UrU"/>
                                            <constraint firstItem="FA0-5K-Pxh" firstAttribute="leading" secondItem="FND-c4-nYC" secondAttribute="leading" id="ofw-aq-B79"/>
                                            <constraint firstAttribute="bottom" secondItem="FA0-5K-Pxh" secondAttribute="bottom" id="ogx-56-qNt"/>
                                        </constraints>
                                        <connections>
                                            <outlet property="cellImageView" destination="FA0-5K-Pxh" id="DE1-DT-Oik"/>
                                        </connections>
                                    </collectionViewCell>
                                </cells>
                            </collectionView>
                        </subviews>
                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstAttribute="trailing" secondItem="IZe-8T-NdF" secondAttribute="trailing" id="1W1-Fl-ZL8"/>
                            <constraint firstItem="IZe-8T-NdF" firstAttribute="top" secondItem="y3c-jy-aDJ" secondAttribute="bottom" id="9QC-93-vwd"/>
                            <constraint firstItem="IZe-8T-NdF" firstAttribute="bottom" secondItem="wfy-db-euE" secondAttribute="top" id="BEF-2W-Otd"/>
                            <constraint firstItem="IZe-8T-NdF" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" id="isg-PJ-70G"/>
                        </constraints>
                    </view>
                    <connections>
                        <outlet property="collectionView" destination="IZe-8T-NdF" id="z6G-PD-d44"/>
                        <outlet property="collectionViewFlowLayout" destination="DNv-oA-G6j" id="y8U-CI-3CD"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="133.59999999999999" y="129.98500749625188"/>
        </scene>
    </scenes>
</document>

Resultado

introduzca la descripción de la imagen aquí

 7
Author: Vasily Bodnarchuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-01 20:52:42

Puede extraer las 3 últimas fotos usando funciones en el marco AssetsLibrary. Primero tienes que añadir el framework al proyecto. La siguiente función recupera las 3 últimas fotos y llama al bloque de finalización.

import AssetsLibrary

func getLatestPhotos(completion completionBlock : ([UIImage] -> ()))   {
    let library = ALAssetsLibrary()
    var count = 0
    var images : [UIImage] = []
    var stopped = false

    library.enumerateGroupsWithTypes(ALAssetsGroupSavedPhotos, usingBlock: { (group,var stop) -> Void in

        group?.setAssetsFilter(ALAssetsFilter.allPhotos())

        group?.enumerateAssetsWithOptions(NSEnumerationOptions.Reverse, usingBlock: {
            (asset : ALAsset!, index, var stopEnumeration) -> Void in

            if (!stopped)
            {
                if count >= 3
                {

                    stopEnumeration.memory = ObjCBool(true)
                    stop.memory = ObjCBool(true)
                    completionBlock(images)
                    stopped = true
                }
                else
                {
                    // For just the thumbnails use the following line.
                    let cgImage = asset.thumbnail().takeUnretainedValue()

                    // Use the following line for the full image.
                    let cgImage = asset.defaultRepresentation().fullScreenImage().takeUnretainedValue()

                    if let image = UIImage(CGImage: cgImage) {
                        images.append(image)
                        count += 1
                    }
                }
            }

        })

        },failureBlock : { error in
            println(error)
    })
}

La función anterior se puede llamar así

getLatestPhotos(completion: { images in
     println(images)
     //Set Images in this block.
})
 6
Author: rakeshbs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-01 11:44:04

Aquí está la respuesta de @Lindsey Scott, pero en Objective-C. Estoy poniendo las últimas 9 fotos de Camera Roll en una vista de colección:

-(void)fetchPhotoFromEndAtIndex:(int)index{

PHImageRequestOptions *options = [[PHImageRequestOptions alloc]init];
options.synchronous = YES;
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc]init];
fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];

PHFetchResult *photos = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions];
if (photos) {
    [[PHImageManager defaultManager] requestImageForAsset:[photos objectAtIndex:photos.count -1 -index] targetSize:CGSizeMake(self.collectionView.frame.size.width/3, self.collectionView.frame.size.height/3) contentMode:PHImageContentModeAspectFill options:options resultHandler:^(UIImage *result, NSDictionary *info) {
        [self.imagesArray addObject:result];

        if (index + 1 < photos.count && self.imagesArray.count < 9) {
            [self fetchPhotoFromEndAtIndex:index + 1];
        }
    }];
}

[self.collectionView reloadData];

}
 5
Author: Boris Nikolić,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-01 17:01:35

Aquí hay una solución elegante con eficiencia en Swift 4.

En resumen, solicitamos los últimos activos de fotos una vez, luego los convertimos en imagen cuando es necesario.

Primera Biblioteca de fotos de importación:

import Photos

Luego cree una función para obtener las últimas fotos tomadas:

func fetchLatestPhotos(forCount count: Int?) -> PHFetchResult<PHAsset> {

    // Create fetch options.
    let options = PHFetchOptions()

    // If count limit is specified.
    if let count = count { options.fetchLimit = count }

    // Add sortDescriptor so the lastest photos will be returned.
    let sortDescriptor = NSSortDescriptor(key: "creationDate", ascending: false)
    options.sortDescriptors = [sortDescriptor]

    // Fetch the photos.
    return PHAsset.fetchAssets(with: .image, options: options)

}

En su caso, es posible que desee obtener suficientes fotos a la vez (por ejemplo, 50), luego almacene el resultado en algún lugar de su controlador de vista:

var latestPhotoAssetsFetched: PHFetchResult<PHAsset>? = nil

En viewDidLoad:

self.latestPhotoAssetsFetched = self.fetchLatestPhotos(forCount: 50)

Finalmente solicite la imagen en el lugar correcto (por ejemplo, una celda de vista de colección):

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    /*
     ...your code to configure the cell...
     */

    // Get the asset. If nothing, return the cell.
    guard let asset = self.latestPhotoAssetsFetched?[indexPath.item] else {
        return cell
    }
    // Here we bind the asset with the cell.
    cell.representedAssetIdentifier = asset.localIdentifier
    // Request the image.
    PHImageManager.default().requestImage(for: asset,
                                   targetSize: cell.imageView.frame.size,
                                  contentMode: .aspectFill,
                                      options: nil) { (image, _) in
        // By the time the image is returned, the cell may has been recycled.
        // We update the UI only when it is still on the screen.
        if cell.representedAssetIdentifier == asset.localIdentifier {
            cell.imageView.image = image
        }
    }
    return cell
}

Recuerde agregar una propiedad a su celda:

class PhotoCell: UICollectionViewCell {
    var representedAssetIdentifier: String? = nil
}
 5
Author: Tim Chen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-15 10:25:01