Swift-comment obtenir la dernière prise de 3 photos de la photothèque?
j'ai besoin d'obtenir et de montrer enfin pris 3 photos de la bibliothèque de photos sur viewDidload de l'événement sans aucun clics.
après cette étape, je devrais obtenir d'autres photos 3 par 3 Quand je fais défiler le scrollview.
connaissez-vous la bonne façon de le faire avec swift? Grâce.
5 réponses
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)")
}
})
}
}
Détails
xCode 8.3, Swift 3.1
Code
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
}
}
Utilisation
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
}
}
}
totalité de l'Échantillon (collectionView avec des images de PhotoLibrary)
Info.plist
ajouter à L'Info.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
Décrit ci-dessus
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>
Résultat
vous pouvez extraire les 3 dernières photos en utilisant des fonctions dans le AssetsLibrary
cadre. Vous devez d'abord ajouter le cadre du projet. La fonction suivante récupère les 3 dernières photos et appelle le bloc d'achèvement.
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 fonction ci-dessus peut être appelé comme ceci
getLatestPhotos(completion: { images in
println(images)
//Set Images in this block.
})
Voici la réponse de @Lindsey Scott mais dans Objective-C. je mets les 9 dernières photos de Camera Roll dans une vue de collection:
-(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];
}
Voici une solution élégante et efficace pour Swift 4.
en bref, nous demandons les dernières ressources photo Une fois, puis les convertir en image si nécessaire.
tout d'Abord importer les Photos de la Bibliothèque:
import Photos
Ensuite créer une fonction pour récupérer les dernières photos prises:
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)
}
Dans votre cas vous pouvez chercher assez de photos à la fois (par exemple 50), puis stocker le résultat quelque part dans votre vue contrôleur:
var latestPhotoAssetsFetched: PHFetchResult<PHAsset>? = nil
viewDidLoad
:
self.latestPhotoAssetsFetched = self.fetchLatestPhotos(forCount: 50)
enfin, demandez l'image au bon endroit (par exemple, une cellule de vue collection):
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
}
n'oubliez pas d'ajouter une propriété à votre cellule:
class PhotoCell: UICollectionViewCell {
var representedAssetIdentifier: String? = nil
}