美文网首页
iOS Swift图片选择SDK开发设计

iOS Swift图片选择SDK开发设计

作者: wCodeQ | 来源:发表于2017-08-11 14:22 被阅读0次

    该SDK设计参考微信选择,支持预览(支持网络图预览及删除)、多选、单张裁剪(一般头像上传用)。基本相关主要页面有相册选择列表、图片选择列表以及预览。仅支持iOS8以上系统。
    GitHub源码

    一、系统资源的获取

    通过Photos.framework库获取系统相册,创建一个管理类集成PHCachingImageManager进行对图片各种处理,主要通过系统API请求图片。

    // 获取缩略图
        public func requestThumbnailImage(for asset: PHAsset, resultHandler: @escaping (UIImage?, [AnyHashable : Any]?) -> Void) -> PHImageRequestID {
            let option = PHImageRequestOptions()
    //        option.resizeMode = .fast
            let targetSize = self.getThumbnailSize(originSize: CGSize(width: asset.pixelWidth, height: asset.pixelHeight))
            return self.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFit, options: option) { (image: UIImage?, dictionry: Dictionary?) in
                resultHandler(image, dictionry)
            }
        }
        
        // 获取预览图
        public func requestPreviewImage(for asset: PHAsset, progressHandler: Photos.PHAssetImageProgressHandler?, resultHandler: @escaping (UIImage?, [AnyHashable : Any]?) -> Void) -> PHImageRequestID {
            let option = PHImageRequestOptions()
    //        option.version = .current
            option.resizeMode = .exact
    //        option.deliveryMode = .fastFormat
            option.isNetworkAccessAllowed = true
            option.progressHandler = progressHandler
            
            let targetSize = self.getPriviewSize(originSize: CGSize(width: asset.pixelWidth, height: asset.pixelHeight))
    
            return self.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFit, options: option) { (image: UIImage?, dictionry: Dictionary?) in
                resultHandler(image, dictionry)
            }
        }
    
        private func getPriviewSize(originSize: CGSize) -> CGSize {
            let width = originSize.width
            let height = originSize.height
            let pixelScale = CGFloat(width)/CGFloat(height)
            var targetSize = CGSize()
            if width <= 1280 && height <= 1280 {
                //a,图片宽或者高均小于或等于1280时图片尺寸保持不变,不改变图片大小
                targetSize.width = CGFloat(width)
                targetSize.height = CGFloat(height)
            } else if width > 1280 && height > 1280 {
                //宽以及高均大于1280,但是图片宽高比例大于(小于)2时,则宽或者高取小(大)的等比压缩至1280
                if pixelScale > 2 {
                    targetSize.width = 1280*pixelScale
                    targetSize.height = 1280
                } else if pixelScale < 0.5 {
                    targetSize.width = 1280
                    targetSize.height = 1280/pixelScale
                } else if pixelScale > 1 {
                    targetSize.width = 1280
                    targetSize.height = 1280/pixelScale
                } else {
                    targetSize.width = 1280*pixelScale
                    targetSize.height = 1280
                }
            } else {
                //b,宽或者高大于1280,但是图片宽度高度比例小于或等于2,则将图片宽或者高取大的等比压缩至1280
                if pixelScale <= 2 && pixelScale > 1 {
                    targetSize.width = 1280
                    targetSize.height = 1280/pixelScale
                } else if pixelScale > 0.5 && pixelScale <= 1 {
                    targetSize.width = 1280*pixelScale
                    targetSize.height = 1280
                } else {
                    targetSize.width = CGFloat(width)
                    targetSize.height = CGFloat(height)
                }
            }
            return targetSize
        }
    

    这里需要注意requestImage的参数设置的坑。具体请参考参数详解
    此处获取预览图替代原图,是因为预览图的清晰度足够满足清晰度需求,反而加载原图会大量消耗内存。预览图的获取规则以1280(8的倍数)为临界,具体看代码实现。
    该管理类中还涉及简单本地缓存方法。

    二、UI布局

    整体设计为一个UINavigationController,目的是为了通过模态弹出后,可以内部进行自己的导航操作。界面流程如图所示:

    界面流程.png

    1、WQPhotoNavigationViewController
    该NavigationController为进入SDK照片选择导航控制器,通过初始化设置代理等各种参数设置。该类中初始化需要注意一点,因为进入SDK默认展示所有图片选择viewController,点击返回到相册列表选择viewController,所以该navigationController的rootViewController要为相册列表VC,然后将所有图片VCpush进来。实现如下:

        private let photoAlbumVC = WQPhotoAlbumViewController()
        
        private convenience init() {
            self.init(photoAlbumDelegate: nil, photoAlbumType: .selectPhoto)
        }
        
        public init(photoAlbumDelegate: WQPhotoAlbumProtocol?, photoAlbumType: WQPhotoAlbumType) {
            let photoAlbumListVC = WQPhotoAlbumListViewController()
            photoAlbumListVC.photoAlbumDelegate = photoAlbumDelegate
            photoAlbumListVC.type = photoAlbumType
            super.init(rootViewController: photoAlbumListVC)
            self.isNavigationBarHidden = true
            photoAlbumVC.photoAlbumDelegate = photoAlbumDelegate
            photoAlbumVC.type = photoAlbumType
            self.pushViewController(photoAlbumVC, animated: false)
        }
    

    该处要把默认init()方法private处理,防止接入使用默认init()。
    2、WQPhotoAlbumListViewController
    该VC没什么可说的,就是通过Photos库API获取相册列表信息资源,然后通过UITableView实现展示内容,点击cell时将获取的相册照片传递个选择VC去展示。
    3、WQPhotoAlbumViewController
    这个VC是默认进入SDK后展示所有照片,模态弹出时,会默认push展示,此时就如上图所示。
    这里只有默认进来时会请求所以照片,当点击返回相册列表后在点进来则会加载选择相册的照片,而无需请求。
    还需要注意的一点就是,UICollectionView中cell重用的的问题,会导致选中状态UI重用,我这里的思路是在获取的照片数据在PhotoData类中进行存储和标记,在collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)代理方法中取相应的标记即可。

    class WQPhotoData: NSObject {
        // 判断数据是否发生变化
        var dataChanged = false
        // 存储每个cell选择标记(false:未选中,true:选中)
        var divideArray = [Bool]() {
            didSet {
                self.dataChanged = true
            }
        }
        //  相册所有图片数据源
        var assetArray = [PHAsset]()
        //  已选图片数组,数据类型是 PHAsset
        var seletedAssetArray = [PHAsset]()
    }
    
    // 所有图片处理后都会是该model
    public class WQPhotoModel: NSObject {
        // 缩略图
        public var thumbnailImage: UIImage?
        // 预览图
        public var originImage: UIImage?
        // 网络图URL
        public var imageURL: String?
        
        public convenience override init() {
            self.init(thumbnailImage: nil, originImage: nil, imageURL: nil)
        }
        
        public init(thumbnailImage: UIImage?, originImage: UIImage?, imageURL: String?) {
            self.thumbnailImage = thumbnailImage
            self.originImage = originImage
            self.imageURL = imageURL
        }
    }
    

    这里还有个PhotoModel的类是贯通所有处理的model。当选择完成和需要预览时,都需通过该model进行返回和赋值处理。
    4、预览和裁剪viewController
    这里WQPhotoPreviewViewController、WQPhotoPreviewDeleteViewController、WQPhotoClipViewController三个界面设计思想基本一样。只不过previewVC是用来内部展示预览使用,previewDeleteVC和clipVC是公开外部使用。之中previewDeleteVC可以通过设置是否支持删除。
    这里为了优化预览体验,使用的是预览图,而不是原图,原因上面已说明,且刚开始进入时优先展示的是缩略图,然后再请求预览图去展示(目的是为了适配iCloud图片展示问题和切换图片空白问题)。

    三、导入SDK方式

    支持Carthage动态库,在Cartfile中添加
    github "wCodeQ/WQPhotoAlbum"

    Carthage compatibleCarthage compatible

    功能说明

    • 模态弹出所有相册列表,选择图片和预览。
    • 返回列表为相册列表,点击取消dismiss
    • 单独公开预览界面,支持删除
    • 支持裁剪功能

    接入说明

    • 修改皮肤色
    WQPhotoAlbumSkinColor = UIColor.red
    
    • 直接跳转所有照片,默认是选择图片
    let photoAlbumVC = WQPhotoNavigationViewController(photoAlbumDelegate: self, photoAlbumType: .selectPhoto)   //初始化需要设置代理对象
    photoAlbumVC.maxSelectCount = 10    //最大可选择张数
    self.navigationController?.present(photoAlbumVC, animated: true, completion: nil)
    
    //跳转裁剪图片选择列表
    let photoAlbumVC = WQPhotoNavigationViewController(photoAlbumDelegate: self, photoAlbumType: .clipPhoto)
    photoAlbumVC.clipBounds = CGSize(width: self.view.frame.width, height: 400)     //裁剪框大小,不设置默认为屏幕宽度正方形
    self.navigationController?.present(photoAlbumVC, animated: true, completion: nil)
    
    //跳转图片列表类型
    public enum WQPhotoAlbumType {
        case selectPhoto, clipPhoto
    }
    
    //实现WQPhotoAlbumProtocol协议获取选择图片资源
    @objc public protocol WQPhotoAlbumProtocol: NSObjectProtocol {
        //返回图片原资源,需要用PHCachingImageManager或者我封装的WQCachingImageManager进行解析处理
        @available(iOS 8.0, *)
        @objc optional func photoAlbum(selectPhotoAssets: [PHAsset]) -> Void
    
        //返回WQPhotoModel数组,其中包含选择的缩略图和预览图
        @available(iOS 8.0, *)
        @objc optional func photoAlbum(selectPhotos: [WQPhotoModel]) -> Void
    
        //返回裁剪后图片
        @available(iOS 8.0, *)
        @objc optional func photoAlbum(clipPhoto: UIImage?) -> Void
    }
    
    • 直接预览跳转,支持删除
    //基于WQPhotoModel中的资源,如果有原图直接展示,否者先展示缩略图然后加载网络图,完成后再展示原图,已做掉缓存
    let wqPhotoPreviewVC = WQPhotoPreviewDeleteViewController()
    wqPhotoPreviewVC.previewPhotoArray = self.selectIamgeArr        //传入预览源,为WQPhotoModel数组,支持缩略图,原图和网络图
    wqPhotoPreviewVC.currentIndex = currentIndex                    //当前展示第几张   
    wqPhotoPreviewVC.isAllowDelete = true                           //设置是否支持删除,默认不支持,当设置了deleteClicked闭包时默认支持删除
    wqPhotoPreviewVC.deleteClicked = { [unowned self] (photos: [WQPhotoModel], deleteModel: WQPhotoModel) in
        self.selectIamgeArr = photos
    }
    self.navigationController?.pushViewController(wqPhotoPreviewVC, animated: true)
    
    • 可以根据PHAsset或者UIImage获取相应缩略图和预览图
    // WQCachingImageManager实例方法
    // 获取缩略图
    public func requestThumbnailImage(for asset: PHAsset, resultHandler: @escaping (UIImage?, [AnyHashable : Any]?) -> Void) -> PHImageRequestID
    // 获取预览图
    public func requestPreviewImage(for asset: PHAsset, progressHandler: Photos.PHAssetImageProgressHandler?, resultHandler: @escaping (UIImage?, [AnyHashable : Any]?) -> Void) -> PHImageRequestID
    // 根据原图获取缩略图和预览图
    public func getThumbnailAndPreviewImage(originImage: UIImage) -> (thumbnailImage: UIImage?, previewImage: UIImage?)
    

    总结:

    github地址:https://github.com/wCodeQ/WQPhotoAlbum
    欢迎大家点星😜
    本人第一次发布源码和文章,可能存在好多不足的地方,望大家多多指教,共同学习。吼吼····

    相关文章

      网友评论

          本文标题:iOS Swift图片选择SDK开发设计

          本文链接:https://www.haomeiwen.com/subject/ewwhrxtx.html