Swift-как получить последние 3 фотографии из библиотеки фотографий?

Мне нужно получить и показать последние сделанные 3 фотографии из библиотеки фотографий на событии viewDidload без каких-либо кликов.

после этого шага я должен получить другие фотографии 3 на 3 при прокрутке scrollview.

знаете ли вы правильный способ сделать это с swift? Спасибо.

Sample

5 ответов


вот решение, использующее Photos фреймворк доступен для устройств 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)")
            }
        })
    }
}

подробности

xCode 8.3, Swift 3.1

код

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
    }
}

использование

 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
              }
      }
 }

полный образец (collectionView с изображениями из PhotoLibrary)

информация.файл plist

добавить к информации.файл plist

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

ViewController.Свифт!--9-->

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.Свифт!--9-->

import UIKit
class CollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var cellImageView: UIImageView!
}

PhotoLibrary.Свифт!--9-->

описано выше

Main.раскадровка

<?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>

результат

enter image description here


вы можете извлечь 3 последние фотографии, используя функции в AssetsLibrary основы. Сначала вы должны добавить фреймворк в проект. Следующая функция извлекает 3 последние фотографии и вызывает блок завершения.

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)
    })
}

вышеуказанная функция может быть вызвана следующим образом

getLatestPhotos(completion: { images in
     println(images)
     //Set Images in this block.
})

вот ответ @Lindsey Scott, но в Objective-C. я помещаю последние 9 фотографий из Camera Roll в вид коллекции:

-(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];

}

вот элегантное решение с эффективностью в Swift 4.

короче говоря, мы запрашиваем последние фото активы один раз, а затем конвертировать их в изображение, когда это необходимо.

первый импорт библиотеки фотографий:

import Photos

затем создайте функцию для извлечения последних сделанных фотографий:

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)

}

в вашем случае вы можете захотеть получить достаточно фотографий сразу (например, 50), а затем сохранить результат где-то в вашем представлении контроллер:

var latestPhotoAssetsFetched: PHFetchResult<PHAsset>? = nil

на viewDidLoad:

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

наконец, запросите изображение в нужном месте (например, ячейку представления коллекции):

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
}

Не забудьте добавить свойство в свою ячейку:

class PhotoCell: UICollectionViewCell {
    var representedAssetIdentifier: String? = nil
}