首先来分析一下界面的交互:
- 导航栏设置了返回按钮和右边的确定按钮。点击确定按钮则将选中的照片返回。
- 可以设置最多可勾选张数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中添加一下项:(添加相册项即可)
然后获取相册授权状态,如果你的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!
}