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.
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)")
}
})
}
}
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
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.
})
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];
}
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
}
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