最终效果:
模拟器没有相机功能,所以这里就不展示了。
思路
因为ALAssetLibrary获取系统资源的方法在iOS9中不被推荐,所以这里使用PhotoKit
关于PhotoKit跟ALAssetLibrary的差异以及使用详细使用方法可以点击iOS 开发之照片框架详解
图中的图片表格列表是用的UICollectionView,collectionview是类似tableview的列表控件,但是功能更强大,有兴趣的同学可以参考UICollection学习总结以及案例集合
里面有7个关于collectionview的demo,有一些功能比较好玩的实现,这里就不一一介绍了。
回到正题,
因为用的是photokit,photokit中获取的资源并不是直接的是图片资源,而是PHAsset(代表照片库中的一个资源,跟 ALAsset 类似,通过 PHAsset 可以获取和保存资源)所以基本的思路是,从系统相册获取到资源,都以PHAsset保存到模型中,在最后要展示的时候再通过统一的方法将PHAsset转换成图片
模型
class CHImage: NSObject {
var asset:PHAsset!
var isSelected = false
override init() {
super.init()
}
}
用来保存获取的资源信息
图片有选中和未选中两个状态定义一个isSelected属性来记录选中状态
未完待续。。。。
续:
有了可以保存资源的Model以后,就需要去获取资源了,这路贴一张网上的关于photokit组成的图:
PhotoKit获取资源的方式,是通过一系列形如 class func fetchXXX(…, options: PHFetchOptions) -> PHFetchResult 的类方法,可以根据这些方法来封装一些方法:
/** 获取全部相册 */
func getPhotoListDatas() -> NSArray {
var dataArr = [AnyObject]()
// 列出所有相册智能相册
let smartAlbums = PHAssetCollection.fetchAssetCollectionsWithType(.SmartAlbum, subtype:.SmartAlbumUserLibrary, options: nil)
dataArr.append(smartAlbums[0])
// 列出所有用户创建的相册
let topLevelUserCollections:PHFetchResult = PHAssetCollection.fetchTopLevelUserCollectionsWithOptions(nil)
for i in 0 ..< topLevelUserCollections.count {
let sub = topLevelUserCollections[i]
dataArr.append(sub)
}
return dataArr
}
/** 获取一个相册的结果集合 */
func getFetchResult(assetCollection:PHAssetCollection) -> PHFetchResult {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let fetchResult = PHAsset.fetchAssetsInAssetCollection(assetCollection, options: fetchOptions)
return fetchResult
}
/** 只获取相机胶卷结果集 */
func getCameraRollFetchResul() -> PHFetchResult{
let fetchOptions = PHFetchOptions()
let smartAlbumsFetchResult = PHAssetCollection.fetchAssetCollectionsWithType(.SmartAlbum, subtype:.SmartAlbumUserLibrary, options: fetchOptions)
let fetch = PHAsset.fetchAssetsInAssetCollection(smartAlbumsFetchResult[0] as! PHAssetCollection, options: nil)
return fetch
}
/** 获取图片实体并把图片结果存放到数组中,返回数组 */
func getPhotoAssets(fetchResult:PHFetchResult) -> [CHImage]{
var dataArr = [CHImage]()
for i in 0 ..< fetchResult.count {
let asset = fetchResult[i] as! PHAsset
let image = CHImage()
/** 过滤视频 */
if asset.mediaType == .Image{
image.asset = asset
dataArr.append(image)
}
}
return dataArr
}
// 获取某个分组的第一张照片缩略图
func firstPhotoThumbnails(assetCollection:PHAssetCollection,synchronous:Bool, resultHandler: (UIImage?, [NSObject : AnyObject]?) -> Void) {
self.photoThumbnails(self.getFetchResult(assetCollection).firstObject as! PHAsset,synchronous:synchronous, resultHandler: resultHandler)
}
// 获取某一张照片缩略图
func photoThumbnails(asset: PHAsset!,synchronous:Bool,resultHandler: (UIImage?, [NSObject : AnyObject]?) -> Void) {
let superSize = CGSizeMake(186 , 186 )
return self.photoImage(superSize,synchronous:synchronous ,asset: asset, resultHandler: resultHandler)
}
// 获取一张大图
func photoDefault(asset: PHAsset!, synchronous:Bool, targetSize:CGSize,resultHandler: (UIImage?, [NSObject : AnyObject]?) -> Void) {
return self.photoImage(targetSize, synchronous:synchronous, asset: asset, resultHandler: resultHandler)
}
最后,我们前面的这些方法获取到的最后的不管是缩略图还是大图,获取到的都是自定义的Model类型的而不是我们想要的UIImage类型的所以还需要一个方法来获取最后的image
/** 获取图片 */
func photoImage(targetSize: CGSize, synchronous:Bool,asset: PHAsset!, resultHandler: (UIImage?, [NSObject : AnyObject]?) -> Void) {
let options = PHImageRequestOptions()
options.resizeMode = .Exact
options.synchronous = synchronous
PHImageManager.defaultManager().requestImageForAsset(asset, targetSize:targetSize, contentMode: .AspectFill, options: options, resultHandler: resultHandler)
}
这里有一个坑,因为存在相册中的图片的宽高比我们是不知道的,所以获取图片的时候targetSize我们只能穿PhotoKi给我们的默认值就是原图大小PHImageManagerMaximumSize,但是这样获取到的图片大小肯定会超乎你的想象,所以获取到图片之后需要通过:
/** 修改图片尺寸 */
static func scaleToSize(image:UIImage?,width:CGFloat,height:CGFloat) -> UIImage {
// let scale = image!.size.height/image!.size.width
let size = CGSizeMake(width, height)
UIGraphicsBeginImageContext(size)
image!.drawInRect(CGRectMake(0, 0, size.width, size.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
来重新画一张图片,缩小图片更好的展示的同时还可以缩小图片的大小,上传的时候更方便。注意,这里的width跟height应该是你所先要的图片尺寸的2以上倍,不然图片看上去会格外的不清晰(AV画质~)
获取到资源之后,就是用uicollectionview展示,实现delegate跟datasource这里就不赘述了。关键点是实习选中的图片前移,没选中的后移,移动的动画非常简单,用uicollectionview自带的func moveItemAtIndexPath(indexPath: NSIndexPath, toIndexPath newIndexPath: NSIndexPath) 这里要确定要交换的图的初始位置跟要换到的位置,而在点击继续之后需要从全部相册资源中选出isSelected = true的资源,如果用循环,因为大部分人的手机中照片都是500+,用循环会卡死,所以这里用正则来匹配,swift中的正则使用方法:
func getSelectedImageArray() -> [CHImage] {
let predicate1 = NSPredicate(format: "isSelected == true")
return (self.imageArray as NSArray).filteredArrayUsingPredicate(predicate1) as! [CHImage]
}
可以很快的匹配出条件符合的资源,并放入数组,之后只要将这个数组传给下一个页面,或者将资源转换成图片之后再传,这个可以根据需求自己定。下面是demo自定义相册&相机
里面还有一个相机的自定义,基本上就自定义了图标跟重拍之类的。仅供大家一起学习讨论。