Swift 自定义图片选择器(三) -- 图片展示界面

多选图片

首先来分析一下界面的交互:

  • 导航栏设置了返回按钮和右边的确定按钮。点击确定按钮则将选中的照片返回。
  • 可以设置最多可勾选张数maxCount, 如果勾选超过maxCount则弹出提示框。
  • 勾选时,选中的照片显示勾选图标,并且添加一层蒙版。取消勾选时,显示可勾选的空心圈圈,并去掉蒙版。
  • 点击照片可以显示该图片的大图

实现

使用UICollectionView, 每行4列,根据屏幕的大小计算出照片的宽度,高度与宽度一致,得出UICollectionViewCell的itemSize.
class PickImageViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
private var collectionView : UICollectionView?
private let CellIdentifier = "ImageCollectionCellIdentifier"
/// 获取相册相关的class
private let imageHelper = PickerHelper.helperDefault
private var pictures = [PictureItem]()  // 保存当前相册所有的图片资源
var albumItem: AlbumItem?   // 相册资源
var selectedPictures = [PictureItem]()  // 选中的图片资源
}

在viewDidLoad中创建collectionView,并获取根据albumItem获取图片资源,保存到pictures,pictures就是collectionView的数据源

/// 创建collectionView
private func initCollectionView() {
    let layout = UICollectionViewFlowLayout()
    layout.itemSize = CGSize(width: width, height: width)
    layout.minimumLineSpacing = lineSpace
    layout.minimumInteritemSpacing = lineSpace
    layout.sectionInset = UIEdgeInsets.init(top: insetSpace, left: insetSpace, bottom: insetSpace, right: insetSpace)
    
    collectionView = UICollectionView(frame: UIScreen.main.bounds, collectionViewLayout: layout)
    collectionView!.backgroundColor = UIColor.white
    collectionView?.delegate = self
    collectionView?.dataSource = self
    //注册cell
    collectionView!.register(ImageCollectionCell.self, forCellWithReuseIdentifier: CellIdentifier)
    self.view.addSubview(collectionView!)
}

/// 获取图片资源
private func getPictures() {
    // MARK: 获取全部图片
    if self.albumItem == nil {
        return
    }
    
    let array = imageHelper.getPicturesInAlbumItem(self.albumItem!)
    pictures += array
    
    //将之前选中的图片在全部图片中勾选上
    self.setPictureSelected()
    
    collectionView!.reloadData()
}
ImageCollectionCell UICollectionViewCell的子类,根据一个PictureItem显示一张图片。
class ImageCollectionCell: UICollectionViewCell {
private let showImageView =  UIImageView()
private let selectButton = UIButton()
private let imageMaskView = UIView()
private var isSelectedImage = false
private var picturnItem: PictureItem?  // 显示的图片资源
var block : (_ isSelected: Bool) -> () = {_ in }  // 勾选或者取消勾选时,通知controller

// 没有使用xib或者storyboard所以需要重载init方法
override init(frame: CGRect) {
    super.init(frame: frame)
    self.initView()
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}
}

设置cell 的内容, 显示的是小图,并且获取大图保存起来(点击缩略图时显示大图,所以就一并获取)。

/// 设置cell的内容
///
/// - parameter item:         图片资源
/// - parameter selectAction: 勾选、取消勾选的操作
func setCellPictureItem(_ item: PictureItem, selectAction: @escaping (_ isSelected: Bool) -> ()) {
    self.picturnItem = item
    block = selectAction
    
    if item.lowImage != nil {
        showImageView.image = item.lowImage
    }
    else {
        // 没有小图,则根据asset资源获取图片的缩略图
        PickerHelper.helperDefault.getImageWithAsset(item.asset, size: self.bounds.size, finishedCallack: { (image) in
            item.lowImage = image
            self.showImageView.image = image
        })
    }
    
    if item.hightImage == nil {
        // 获取大图
        PickerHelper.helperDefault.getImageWithAsset(item.asset, size: PickerHelper.helperDefault.targetSize, finishedCallack: { (image) in
            item.hightImage = image
        })
    }
    
    setSelectButton(item.isSelected)
}

勾选、取消勾选的时候添加、移除蒙版,蒙版要添加在勾选按钮下面,这样才不会影响勾选按钮的点击响应。

/// 选中或取消选中时更新界面方法
///
/// - parameter isSelected: 是否选中的标志
private func setSelectButton(_ isSelected: Bool) {
    if isSelected {
        //当前选中, 则添加一层蒙版
        self.insertSubview(imageMaskView, belowSubview: selectButton)
    }
    else {
        imageMaskView.removeFromSuperview()
    }
    // 设置勾选按钮的状态,按钮会自动更改图片
    selectButton.isSelected = isSelected;
}

/// 取消选中,主要是给多选了之后,重置用的
public func setCellUnSelected() {
    self.picturnItem?.isSelected = false
    setSelectButton(false)
 }

需要以下设置,按钮才能根据自身的状态更改图片

selectButton.setImage(UIImage.init(named: "store_picture_unselected"), for: .normal)
selectButton.setImage(UIImage.init(named: "store_picture_selected"), for: .selected)

在cellForItemAtIndexPath 设置cell的内容

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let row = indexPath.row
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellIdentifier, for: indexPath) as! ImageCollectionCell
    let pictureItem = pictures[row]
    
    cell.setCellPictureItem(pictureItem, selectAction: { (isSelectedImage) in
        // 勾选、取消勾选后执行的操作
        var isSelectedCell = true
        
        if isSelectedImage {
            // 勾选,则判断是否超过maxCount
            isSelectedCell = self.checkMaxCount(cell)
        }
        
        //只有没有超出maxCount才会勾选,添加到选择数组里
        if (isSelectedCell) {
            self.setImageSelectedInRow(row, isSelected: isSelectedImage)
        }
    })

    return cell
}

判断已经勾选的数量是否大于maxCount。无论有没有超出maxCount,cell都会默认先勾选了。如果已经超出范围时,需要取消勾选。

/// 判断选中数量是否超出可勾选的数值
///
/// - parameter cell: cell
///
/// - returns: 是否超出范围 true: 没有,false: 超出
private func checkMaxCount(_ cell: ImageCollectionCell) -> Bool {
    if selectedPictures.count >= PickerHelper.helperDefault.maxCount {
        // 取消勾选 (因为无论有没有超出maxCount,cell都会默认先勾选了。所以超出范围时,需要取消)
        cell.setCellUnSelected()
        
        //显示提示
        let alert = UIAlertController(title: "温馨提示", message: "最多只能选\(PickerHelper.helperDefault.maxCount)张", preferredStyle: .alert)
        
        let cancelAction = UIAlertAction(title: "确定", style: .default, handler: nil)
        alert.addAction(cancelAction)
        
        self.present(alert, animated: true, completion: nil)
        
        return false
    }
    
    // 没有超出范围
    return true
}

如果是没有超出maxCount的勾选,则将图片添加到已选数组。如果是取消勾选,则在已选数组中移除。

/// 选中或者取消选中方法
///
/// - parameter row:        图片的下标
/// - parameter isSelected: 是否选中的标志
private func setImageSelectedInRow(_ row: Int, isSelected: Bool) {
    let item = pictures[row]
    
    if isSelected {
        //添加到选中数组
        if self.selectedPictures.contains(item) == false {
            self.selectedPictures.append(item)
        }
    }
    else {
        if self.selectedPictures.contains(item) == true {
            let curIndex = self.selectedPictures.index(of: item)!
            self.selectedPictures.remove(at: curIndex)
        }
    }
}

获取照片库的授权权限

想要访问相册,首先的获得系统的授权。在app的配置文件info.plist中添加一下项:(添加相册项即可)


info配置

然后获取相册授权状态,如果你的app没有进行过授权的话,是授权状态一般是.notDetermined,此时需要请求授权。

// MARK: 相册授权相关
private func getPhotoLibraryAuthorization() -> Bool {
    let authorizationStatus = PHPhotoLibrary.authorizationStatus()

    switch authorizationStatus {
    case .authorized:
        print("已经授权")
        return true
    case .notDetermined:
        print("不确定是否授权")
        // 请求授权
        PHPhotoLibrary.requestAuthorization({ (status) in })
    case .denied:
        print("拒绝授权")
    case .restricted:
        print("限制授权")
        break
    }
    
    return false
}

显示Pikcer图片选择器

将跳转到Picker的方法封装在PickerHelper中,然后用户就可以调用这个方法直接使用Pikcer

/// 显示图片选择界面
///
/// - parameter viewController:  上一个界面
/// - parameter selectePictures: 已选图片
/// - parameter maxCount:        最多选择张数
/// - parameter selectedAction:  选择图片返回block
func showPhotoPickerInController(_ viewController: UIViewController, selectePictures: [PictureItem]?, maxCount count: Int, targetSize size: CGSize, selectedAction: @escaping ([PictureItem]?) -> ()) {
    maxCount = count
    targetSize = size

    if self.getPhotoLibraryAuthorization() {
        // 主要解决:已经选中的图片,取消选中了之后,没有确定,而是取消选择时,isSelected会在取消选中时置为false的问题
        for (_, item) in (selectePictures?.enumerated())! {
            item.isSelected = true
        }
        
        //授权可以访问相册
        let albums = PickerHelper.helperDefault.getSmartAlbums()
        
        if albums == nil {
            return
        }
        // 选择第一个相册显示
        let albumItem = albums![0]
        // 图片选择
        let picker = PickImageViewController()
        picker.albumItem = albumItem
        picker.completeBlock = { (pictures, isSureButtonTouch) in
            if isSureButtonTouch {
                //只有点击了确定按钮返回的数据才是最后确定选择的
                selectedAction(pictures)
            }
        }

        //设置已选中图片
        if selectePictures != nil {
            picker.selectedPictures = selectePictures!
        }
       // 跳转到picker
        viewController.navigationController?.pushViewController(picker, animated: true)
    }
    else if PHPhotoLibrary.authorizationStatus() != .notDetermined {
        // 用户明确拒绝授权后,提示用户在设置中修改
        let alert = UIAlertController(title: "温馨提示", message: "没有相册的访问权限,请在应用设置中开启权限", preferredStyle: .alert)
        
        let cancelAction = UIAlertAction(title: "确定", style: .cancel, handler: nil)
        alert.addAction(cancelAction)
        
        viewController.present(alert, animated: true, completion: nil)
    }
}

用户调用Picker

PickerHelper.helperDefault.showPhotoPickerInController(self, selectePictures: self.selectePictures, maxCount: 4, targetSize: .zero)  { (pictures) in
  self.selectePictures = pictures!
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,943评论 4 60
  • 1)清除参考线--- 视图->清除参考线 2)切图-----------图层->基于图层的切片1,使用切片工具Pa...
    huhu502阅读 315评论 1 0
  • 窗外淅淅沥沥的雨滴声声入耳, 不,那不是雨,是天空的眼泪, 多少个辗转难眠的夜晚, 无法言说的苦楚, 难以抑制的思...
    行走的木木阅读 180评论 0 0
  • 曾有调查显示,人的皮肤与服装之间产生的瞬间静电最高可达上万伏;人在地毯上走动可产生1500~35000伏静电;在室...
    邻居知道阅读 32,872评论 1 1
  • 安妮宝贝曾说:不知道小说里的少年藤井树,会不会是岩井俊二写给自己的一个映照。散漫懒惰,不善于和人打交道。特立独行却...
    阿花呀_阅读 642评论 0 1