SwiftでPhotosFrameworkを使ってカメラロールから画像を取得する処理を書いてみました。
Swiftはまだあまり書いていないので、文法的に微妙なところがあるかもしれません。
まずは復習
PhotosFrameworkの基本的なクラスです。
クラス | なにを扱うのか |
---|---|
PHCollectionList | PHAssetCollectionの集まり |
PHAssetCollection | カメラロール自体やユーザ作成のフォルダ、モーメントの年など |
PHAsset | 画像やビデオ自体 |
PHFetchResult | fetch結果を格納する |
PHAssetCollectionやPHAssetのfetch結果をPHFetchResultに格納し、enumerateObjectsUsingBlockで中身を取り出すのが基本的なアプローチになります。
画像を年別月別で配列に格納するサンプル
フォルダ別ではなく、年別に画像を取得する場合 Moment という概念でアクセスしていくことになります。
冒頭でPhotosをインポートします
import Photos
MomentList ( =PHCollectionList) を取得します
fetchCollectionListsWithType の type に MomentList、subType に Any を指定すると、
各年のPHAssetCollectionが1つだけ入ったPHCollectionListと、各年のPHAssetCollectionListがすべて入ったPHCollectionListが取得できます。
例)カメラロールに2015年、2014年、2013年に撮影した画像が入っている場合、
PHFetchResultには [ [ 2015 ] , [ 2014 ] , [ 2013 ] , [ 2015,2014,2013 ] ] のような結果が格納されている。
// モーメントリストを取得する
private func getMomentList() -> [PHCollectionList] {
let fetchResult: PHFetchResult = PHCollectionList.fetchCollectionListsWithType(.MomentList, subtype: .Any, options: nil)
var momentLists = [PHCollectionList]()
fetchResult.enumerateObjectsUsingBlock { (moment, idx, stop) -> Void in
momentLists.append(moment as! PHCollectionList)
}
return momentLists
}
MomentList から Moment ( =PHAssetCollection) を取得します
self.getMoment(momentLists[0]) のような形で momentList の1つを渡し、moment を取得します。
例) [ 2015 ] = PHCollectionList を渡し、[ 2015 ] = PHAssetCollection を返す
// モーメントを取得する
private func getMoment(momentList: PHCollectionList) -> [PHAssetCollection] {
let fetchResult: PHFetchResult = PHAssetCollection.fetchMomentsInMomentList(momentList, options: nil)
var moments = [PHAssetCollection]()
if fetchResult.count == 1 {
fetchResult.enumerateObjectsUsingBlock{ (moment, idx, stop) -> Void in
moments.append(moment as! PHAssetCollection)
}
}
return moments
}
Momentから画像を月別に配列に格納します
self.getPhotosByMonth(moments[0]) のような形で moment の1つを渡し、画像を月別に取得します。
これで[PHAsset]の配列を格納したPhotos構造体の配列が取得できます。
struct Photos {
var exist: Bool = false
var photos: [PHAsset] = []
}
// 写真を月別に取得する
private func getPhotosByMonth(moment: PHAssetCollection) -> [Photos] {
// 年の文字列を取得する
let formatter = NSDateFormatter.new()
formatter.dateFormat = "yyyy"
let year = formatter.stringFromDate(moment.startDate)
var photosByMonth = [Photos]()
for var i = 1 ; i <= 12 ; i++ {
// 月初日と月末日を生成します
let month = NSString(format:"%02d", i) as String
let fromDate = self.getDateFromString(year, month: month, day: "01")
let toDate = self.getMonthEndingDate(fromDate!)
// オプションを指定してフェッチします
let fetchOption = PHFetchOptions.new()
fetchOption.predicate = NSPredicate(format: "(creationDate >= %@) and (creationDate) < %@", fromDate!, toDate)
fetchOption.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let fetchResult: PHFetchResult = PHAsset.fetchAssetsInAssetCollection(moment, options: fetchOption)
var photos = Photos()
if fetchResult.count != 0 {
photos.exist = true
fetchResult.enumerateObjectsUsingBlock { (photo, idx, stop) -> Void in
photos.photos.append(photo as! PHAsset)
}
}
else {
photos.exist = false
}
photosByMonth.append(photos)
}
return photosByMonth
}
private func getDateFromString(year: String, month: String, day: String) -> NSDate? {
let formatter = NSDateFormatter.new()
formatter.calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian)! // 24時間表示対策
formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0)
formatter.dateFormat = "yyyyMMdd"
let dateString = year + month + day
return formatter.dateFromString(dateString)
}
private func getMonthEndingDate(beginningDate: NSDate) -> NSDate {
let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian)!
calendar.timeZone = NSTimeZone(forSecondsFromGMT: 0)
var comp = calendar.components(
NSCalendarUnit.CalendarUnitYear
| NSCalendarUnit.CalendarUnitMonth
| NSCalendarUnit.CalendarUnitDay
| NSCalendarUnit.CalendarUnitHour
| NSCalendarUnit.CalendarUnitMinute
| NSCalendarUnit.CalendarUnitSecond
, fromDate:beginningDate)
comp.hour = 23
comp.minute = 59
comp.second = 59
let range = calendar.rangeOfUnit(NSCalendarUnit.CalendarUnitDay, inUnit: NSCalendarUnit.CalendarUnitMonth, forDate: beginningDate)
comp.day = range.length
return calendar.dateFromComponents(comp)!
}
以上になります。