Photo 相册操作

  • 提要

对于相册的操作网上有很多文章,也有很多讲解,我们今天来实现相册的多选功能。我们利用Photo.framework,这个是iOS8以后的版本。我们先熟悉一个这个框架的基本的几个类。

  • PHAsset:一个资源的相片
  • PHAssetCollection:继承PHCollection的子类,单个资源的集合,如相册、时刻等
  • PHCollectionList:继承PHCollection的子类,集合的集合,如相册文件夹
  • PHPhotoLibrary:相册的管理操作,负责注册通知、检查和请求获取权限
  • PHImageManager:按照要求获取制定的图片
  • PHCachingImageManager:PHImageManager的子类
  • PHAssetChangeRequest:编辑相册,增删改查

这里有一篇文章,写的很好具体的功能不论述了,我们直接来看逻辑。

  • 创建

我们先创建一个相册管理器,主要负责获取相册和照片,我们来看代码。

class JHSAssetManger: NSObject {
    
    weak var changeObserver: JHSAssetMangerDelegate?

    private var allPhoto: PHFetchResult<PHAsset>! // 所有照片
    private var smartALbums: PHFetchResult<PHAssetCollection>! // 智能相册集
    private var userCollecions: PHFetchResult<PHCollection>! // 用户自定相册集和
    
    var model = JHAassetModel();
    
    override init() {
        super.init();
        
        model.resetAssetList();
        
        let allOptions = PHFetchOptions();
        allOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)];
        allPhoto = PHAsset.fetchAssets(with: allOptions);
        let item = JHSAssetItemModel(asset: allPhoto);
        if item.count > 0 {
            model.addItemModel(model: item);
        }
        

        
        smartALbums = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumRegular, options: nil);
        
        smartALbums.enumerateObjects { (collection, idx, stop) in
            let item = JHSAssetItemModel(collection: collection, type: .smart);
            if item.count > 0 {
                self.model.addItemModel(model: item);
            }
        }
        
        userCollecions = PHCollectionList.fetchTopLevelUserCollections(with: nil);
        userCollecions.enumerateObjects { (collection, idx, stop) in
            let item = JHSAssetItemModel(collection: collection, type: .collection);
            if item.count > 0 {
                self.model.addItemModel(model: item);
            }
        }
        
        PHPhotoLibrary.shared().register(self);
    }
    
    deinit {
        PHPhotoLibrary.shared().unregisterChangeObserver(self);
    }
    
}

 

我们需要接收这个管理器所获取到的所有有相册集和照片。我们需要封装成统一的数据模型Model。下面是数据模型。

class JHSAssetItemModel: NSObject {

    var type = JHSAssetModelType.image;
    
    var results: PHFetchResult<PHAsset>!
    var collection: PHAssetCollection!
    
    var chachesImage = [String:UIImage]();
    
    var title = "";
    var count: Int {
        return results?.count ?? 0;
    }
    
    private override init() {
        super.init();
    }
    convenience init(collection: PHCollection,type: JHSAssetModelType = JHSAssetModelType.image) {
        self.init();
        if let asset = collection as? PHAssetCollection {
            title = asset.localizedTitle ?? "";
            results = PHAsset.fetchAssets(in: asset, options: nil);
            self.collection = asset;
        }
        self.type = type;
    }
    
    convenience init(asset: PHFetchResult<PHAsset>) {
        self.init();
        title = "全部照片";
        results = asset;
    }

    
    func fetchImage(index: Int,size: CGSize,finished:@escaping ((_ image: UIImage?) -> Void)) -> Void {
        
        
        let key = NSStringFromRange(NSRange(location: size.width.intValue, length: size.height.intValue)) + "\(index)" + (collection?.localIdentifier ?? "");
        
        if let img = chachesImage[key] {
            finished(img);
            return;
        }
        
        if results == nil || count <= index {
            finished(nil);
            return;
        }
        let asset = results.object(at: index);
        let dispath = DispatchQueue(label: "fetch_image");
        dispath.async {
            let options = PHImageRequestOptions();
            options.isSynchronous = true;
            PHCachingImageManager.default().requestImage(for: asset, targetSize: CGSize(width: size.width, height: size.height), contentMode: PHImageContentMode.default, options: options) { (image, dict) in
                self.chachesImage[key] = image;
                DispatchQueue.main.async {
                    finished(image);
                }
            };
        }
    }
    
}

这个数据模型只是代表一个资源,我们需要一个类来管理不同集合的资源。我们还需要定义一个模型的管理器。

 

enum JHSAssetModelType: Int {
    case image
    case smart
    case collection
}

class JHAassetModel{
    private var assetList: [JHSAssetModelType:[JHSAssetItemModel]]!;
    init() {
        assetList = [JHSAssetModelType:[JHSAssetItemModel]]();
    }
    func addItemModel(model: JHSAssetItemModel) -> Void {
        var list = assetList[model.type] ?? [JHSAssetItemModel]();
        list.append(model);
        assetList[model.type] = list;
    }
    func resetType(type: JHSAssetModelType) -> Void {
        assetList.removeValue(forKey: type);
    }
    subscript(type: JHSAssetModelType) -> [JHSAssetItemModel]? {
        return assetList[type];
    }
    func resetAssetList() -> Void {
        assetList.removeAll();
    }
    
}

上边的JHSAssetModelType是表示资源的集合类型。我们需要知道我们的是对那个集合资源进行操作。

这就是一些的基本的相册的逻辑操作,下面我们看界面。

我们需要一个类表来显示当前的相册集合。

import UIKit
import Photos

class JHSPhotoTableController: JHSBaseViewController {

    
    var photoManger = JHSAssetManger();
    var maxCount = 10; // 最多选择的张数 默认是10个
    var finished:((_ list: [UIImage]) -> Void)!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        photoManger.changeObserver = self;
        createTable(delegate: self);
        baseTable.register(UITableViewCell.self, forCellReuseIdentifier: "cell");
    }

    

    // MAEK: - table view delegate implement
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 3;
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        let type = JHSAssetModelType(rawValue: section)!
        return photoManger.model[type]?.count ?? 0;

    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath);
        let type = JHSAssetModelType(rawValue: indexPath.section)!
        let list = photoManger.model[type];
        let itemModel = list?[indexPath.row];
        cell.textLabel?.text = itemModel?.title;
      
        return cell;
        
    }
    
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
        let ctrl = JHSPhotoController();
        let type = JHSAssetModelType(rawValue: indexPath.section)!
        let list = photoManger.model[type];
        ctrl.itemModel = list?[indexPath.row];
        ctrl.manger = photoManger;
        ctrl.finishedDone = finished;
        navigationController?.pushViewController(ctrl, animated: true);
        
        
    }
    
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        let titles = ["所有", "智能", "用户"];
        return titles[section];
    }

}

extension JHSPhotoTableController: JHSAssetMangerDelegate {
    func assetManger(manger: JHSAssetManger, operation: JHSOperation) {
        if operation.operation == .smartAlbum {
            baseTable.reloadSections(section: 1);
        }
        if operation.operation == .userCollection {
            baseTable.reloadSections(section: 2);
        }
    }
    
    
}

 

我们需要创建一个另外的显示集合里的所有照片的控制器。

class JHSPhotoController: JHSBaseViewController {

//    var models: JHSPhotoModel?
    
    var itemModel: JHSAssetItemModel!
    
    var manger: JHSAssetManger!
    
    var finishedDone:((_ list: [UIImage]) -> Void)!

    
    var selectedRows = [Int]() {
        didSet{
            navigationItem.rightBarButtonItem?.title = "\(selectedRows.count)个"

        }
    }

    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        createPhoneLevel();
        
        let countItem = UIBarButtonItem(title: "0个", style: .plain, target: self, action: #selector(buttonItemAction(_:)));
        self.navigationItem.rightBarButtonItem = countItem;
        
//        guard let collection = itemModel?.collection else {
//            return;
//        }
//        if collection.canPerform(.addContent) {
//            let addImg = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(buttonItemAction(_:)));
//            navigationItem.rightBarButtonItem = addImg;
//        }else if collection.canPerform(.delete) {
//            let addImg = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(buttonItemAction(_:)));
//            navigationItem.rightBarButtonItem = addImg;
//        }
        
    }

    
    @objc override func buttonItemAction(_ item: UIBarButtonItem) {
        
        var list = [UIImage]();
        var count = 0;
        for idx in selectedRows {
            itemModel?.fetchImage(index: idx, size: ScreenData.bounds.size, finished: { (image) in
                list.append(image!);
                count += 1;
                if count == self.selectedRows.count {
                    self.finishedDone?(list);
                }
            })
        }
        
        self.navigationController?.dismiss(animated: true, completion: {
            
        })

    }
    
    

}
extension JHSPhotoController: UICollectionViewDelegate,UICollectionViewDataSource {
    
    
    func createPhoneLevel() -> Void {
        let layout = UICollectionViewFlowLayout();
        layout.minimumLineSpacing = 4;
        layout.minimumInteritemSpacing = 4;
        layout.sectionInset = UIEdge(size: 4);
        let perWidth = (width() - 20)/4;
        layout.itemSize = CGSize(width: perWidth, height: perWidth);
        createCollection(frame: navigateRect, layout: layout, delegate: self);
        baseCollectionView.register(JHSPhotoImageCell.self, forCellWithReuseIdentifier: "JHSPhotoImageCell");
        baseCollectionView.alwaysBounceVertical = true;

    }
    
    
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

        return itemModel?.count ?? 0;

    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "JHSPhotoImageCell", for: indexPath) as! JHSPhotoImageCell;
        
        cell.isSelected = selectedRows.contains(indexPath.row);
        cell.selectedButn.addTarget(self, action: #selector(touchUpButtonAction(_:)));
        
        itemModel.fetchImage(index: indexPath.row, size: CGSize(width: width()/2, height: width()/2)) { (image) in
            cell.imageView.image = image;
        }
        return cell;
        
        
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let ctrl = JHSImageController();
        ctrl.index = indexPath.row;
        ctrl.modelItems = itemModel;
        navigationController?.pushViewController(ctrl, animated: true);
    }
    
    override func touchUpButtonAction(_ btn: UIButton) {
        guard let cell = btn.superview as? JHSPhotoImageCell else {
            return;
        }
        let indexPath = baseCollectionView.indexPath(for: cell)!;
        if let idx = selectedRows.firstIndex(of: indexPath.row) {
            selectedRows.remove(at: idx);
            cell.isSelected = false;
        }else{
            cell.isSelected = true;
            selectedRows.append(indexPath.row);
        }
    }
}

这样就基本上就可以使用了。展示效果。如下图:

demo总还是有的。demo地址。到此结束。