首先來(lái)分析一下界面的交互:
- 導(dǎo)航欄設(shè)置了返回按鈕和右邊的確定按鈕。點(diǎn)擊確定按鈕則將選中的照片返回酝碳。
- 可以設(shè)置最多可勾選張數(shù)maxCount, 如果勾選超過(guò)maxCount則彈出提示框疏哗。
- 勾選時(shí)拴事,選中的照片顯示勾選圖標(biāo)刃宵,并且添加一層蒙版牲证。取消勾選時(shí)坦袍,顯示可勾選的空心圈圈等太,并去掉蒙版。
- 點(diǎn)擊照片可以顯示該圖片的大圖
實(shí)現(xiàn)
使用UICollectionView, 每行4列奠宜,根據(jù)屏幕的大小計(jì)算出照片的寬度压真,高度與寬度一致蘑险,得出UICollectionViewCell的itemSize.
class PickImageViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
private var collectionView : UICollectionView?
private let CellIdentifier = "ImageCollectionCellIdentifier"
/// 獲取相冊(cè)相關(guān)的class
private let imageHelper = PickerHelper.helperDefault
private var pictures = [PictureItem]() // 保存當(dāng)前相冊(cè)所有的圖片資源
var albumItem: AlbumItem? // 相冊(cè)資源
var selectedPictures = [PictureItem]() // 選中的圖片資源
}
在viewDidLoad中創(chuàng)建collectionView佃迄,并獲取根據(jù)albumItem獲取圖片資源呵俏,保存到pictures,pictures就是collectionView的數(shù)據(jù)源
/// 創(chuàng)建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
//注冊(cè)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的子類(lèi)套啤,根據(jù)一個(gè)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 } // 勾選或者取消勾選時(shí)唆鸡,通知controller
// 沒(méi)有使用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")
}
}
設(shè)置cell 的內(nèi)容争占, 顯示的是小圖,并且獲取大圖保存起來(lái)(點(diǎn)擊縮略圖時(shí)顯示大圖伯襟,所以就一并獲饶饭帧)澡绩。
/// 設(shè)置cell的內(nèi)容
///
/// - 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 {
// 沒(méi)有小圖溪掀,則根據(jù)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)
}
勾選揪胃、取消勾選的時(shí)候添加只嚣、移除蒙版艺沼,蒙版要添加在勾選按鈕下面障般,這樣才不會(huì)影響勾選按鈕的點(diǎn)擊響應(yīng)。
/// 選中或取消選中時(shí)更新界面方法
///
/// - parameter isSelected: 是否選中的標(biāo)志
private func setSelectButton(_ isSelected: Bool) {
if isSelected {
//當(dāng)前選中, 則添加一層蒙版
self.insertSubview(imageMaskView, belowSubview: selectButton)
}
else {
imageMaskView.removeFromSuperview()
}
// 設(shè)置勾選按鈕的狀態(tài)藐石,按鈕會(huì)自動(dòng)更改圖片
selectButton.isSelected = isSelected;
}
/// 取消選中于微,主要是給多選了之后,重置用的
public func setCellUnSelected() {
self.picturnItem?.isSelected = false
setSelectButton(false)
}
需要以下設(shè)置驱证,按鈕才能根據(jù)自身的狀態(tài)更改圖片
selectButton.setImage(UIImage.init(named: "store_picture_unselected"), for: .normal)
selectButton.setImage(UIImage.init(named: "store_picture_selected"), for: .selected)
在cellForItemAtIndexPath 設(shè)置cell的內(nèi)容
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
// 勾選抹锄、取消勾選后執(zhí)行的操作
var isSelectedCell = true
if isSelectedImage {
// 勾選伙单,則判斷是否超過(guò)maxCount
isSelectedCell = self.checkMaxCount(cell)
}
//只有沒(méi)有超出maxCount才會(huì)勾選哈肖,添加到選擇數(shù)組里
if (isSelectedCell) {
self.setImageSelectedInRow(row, isSelected: isSelectedImage)
}
})
return cell
}
判斷已經(jīng)勾選的數(shù)量是否大于maxCount。無(wú)論有沒(méi)有超出maxCount扫沼,cell都會(huì)默認(rèn)先勾選了。如果已經(jīng)超出范圍時(shí)严就,需要取消勾選。
/// 判斷選中數(shù)量是否超出可勾選的數(shù)值
///
/// - parameter cell: cell
///
/// - returns: 是否超出范圍 true: 沒(méi)有渐行,false: 超出
private func checkMaxCount(_ cell: ImageCollectionCell) -> Bool {
if selectedPictures.count >= PickerHelper.helperDefault.maxCount {
// 取消勾選 (因?yàn)闊o(wú)論有沒(méi)有超出maxCount祟印,cell都會(huì)默認(rèn)先勾選了粟害。所以超出范圍時(shí),需要取消)
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
}
// 沒(méi)有超出范圍
return true
}
如果是沒(méi)有超出maxCount的勾選套鹅,則將圖片添加到已選數(shù)組卓鹿。如果是取消勾選留荔,則在已選數(shù)組中移除。
/// 選中或者取消選中方法
///
/// - parameter row: 圖片的下標(biāo)
/// - parameter isSelected: 是否選中的標(biāo)志
private func setImageSelectedInRow(_ row: Int, isSelected: Bool) {
let item = pictures[row]
if isSelected {
//添加到選中數(shù)組
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)
}
}
}
獲取照片庫(kù)的授權(quán)權(quán)限
想要訪(fǎng)問(wèn)相冊(cè)藻治,首先的獲得系統(tǒng)的授權(quán)栋艳。在app的配置文件info.plist中添加一下項(xiàng):(添加相冊(cè)項(xiàng)即可)
然后獲取相冊(cè)授權(quán)狀態(tài)吸占,如果你的app沒(méi)有進(jìn)行過(guò)授權(quán)的話(huà)凿宾,是授權(quán)狀態(tài)一般是.notDetermined,此時(shí)需要請(qǐng)求授權(quán)件蚕。
// MARK: 相冊(cè)授權(quán)相關(guān)
private func getPhotoLibraryAuthorization() -> Bool {
let authorizationStatus = PHPhotoLibrary.authorizationStatus()
switch authorizationStatus {
case .authorized:
print("已經(jīng)授權(quán)")
return true
case .notDetermined:
print("不確定是否授權(quán)")
// 請(qǐng)求授權(quán)
PHPhotoLibrary.requestAuthorization({ (status) in })
case .denied:
print("拒絕授權(quán)")
case .restricted:
print("限制授權(quán)")
break
}
return false
}
顯示Pikcer圖片選擇器
將跳轉(zhuǎn)到Picker的方法封裝在PickerHelper中排作,然后用戶(hù)就可以調(diào)用這個(gè)方法直接使用Pikcer
/// 顯示圖片選擇界面
///
/// - parameter viewController: 上一個(gè)界面
/// - parameter selectePictures: 已選圖片
/// - parameter maxCount: 最多選擇張數(shù)
/// - 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() {
// 主要解決:已經(jīng)選中的圖片妄痪,取消選中了之后楞件,沒(méi)有確定,而是取消選擇時(shí)罪针,isSelected會(huì)在取消選中時(shí)置為false的問(wèn)題
for (_, item) in (selectePictures?.enumerated())! {
item.isSelected = true
}
//授權(quán)可以訪(fǎng)問(wèn)相冊(cè)
let albums = PickerHelper.helperDefault.getSmartAlbums()
if albums == nil {
return
}
// 選擇第一個(gè)相冊(cè)顯示
let albumItem = albums![0]
// 圖片選擇
let picker = PickImageViewController()
picker.albumItem = albumItem
picker.completeBlock = { (pictures, isSureButtonTouch) in
if isSureButtonTouch {
//只有點(diǎn)擊了確定按鈕返回的數(shù)據(jù)才是最后確定選擇的
selectedAction(pictures)
}
}
//設(shè)置已選中圖片
if selectePictures != nil {
picker.selectedPictures = selectePictures!
}
// 跳轉(zhuǎn)到picker
viewController.navigationController?.pushViewController(picker, animated: true)
}
else if PHPhotoLibrary.authorizationStatus() != .notDetermined {
// 用戶(hù)明確拒絕授權(quán)后泪酱,提示用戶(hù)在設(shè)置中修改
let alert = UIAlertController(title: "溫馨提示", message: "沒(méi)有相冊(cè)的訪(fǎng)問(wèn)權(quán)限西篓,請(qǐng)?jiān)趹?yīng)用設(shè)置中開(kāi)啟權(quán)限", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "確定", style: .cancel, handler: nil)
alert.addAction(cancelAction)
viewController.present(alert, animated: true, completion: nil)
}
}
用戶(hù)調(diào)用Picker
PickerHelper.helperDefault.showPhotoPickerInController(self, selectePictures: self.selectePictures, maxCount: 4, targetSize: .zero) { (pictures) in
self.selectePictures = pictures!
}