携帯のカメラロールから画像を選択する機能はUIImagePickerControllerを使用すれば、
簡単に実装できるようですが、カスタマ要素が増える時には困ってしまいます。
※特に困らないか?私が実装方法を知っていないだけか?
UIImagePickerControllerを使わず、以下機能を実装してみました。
・フォトライブラリから画像を選択できる
・選択した画像がハイライトで見せる
・選択順番の番号が振られる
・選択を取り消す際には番号が振りなおす
様々なサイズの画像をバランスよく見せるために、まず全ての画像を統一のサイズへResizeします。
画像の短い辺を幅としるスクエア画像を取得するメソッドを書きます。
extension UIImage{
func cropToSquare() -> UIImage? {
let cropSide = min(self.size.width, self.size.height)
let x = (cropSide - self.size.width) / 2
let y = (cropSide - self.size.height) / 2
UIGraphicsBeginImageContextWithOptions(CGSize(width: cropSide, height: cropSide), false, self.scale)
self.draw(in: CGRect(x: x, y: y, width: self.size.width, height: self.size.height))
let croppedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return croppedImage
}
}
さて実装に入ります。
1.Info.plist
Privacy - Photo Library Usage Description を追加して説明文を記載します。
2.アクセス権限をチェック→画像Asset取得→CollectionViewを更新
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
var phAssets:[PHAsset] = [PHAsset]() ///取得した画像のAsset配列
override func viewDidLoad() {
super.viewDidLoad()
checkAuth()
}
private func checkAuth(){
PHPhotoLibrary.requestAuthorization { (status) in
switch status {
case .authorized:
self.getPHAssets()
default:
return
}
}
}
private func getPHAssets(){
let assets:PHFetchResult = PHAsset.fetchAssets(with: .image, options: nil)
assets.enumerateObjects { (asset, index, stop) in
self.phAssets.append(assets[index])
}
updateCollectionView()
}
private func updateCollectionView(){
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
3.取得したAssetをUIImageに変換→CollectionViewCellでImageを表示
private func getImage(_ phAsset:PHAsset) -> UIImage? {
var targetImage: UIImage? = UIImage()
let options = PHImageRequestOptions()
options.deliveryMode = .opportunistic
options.resizeMode = .fast
let size: CGSize = CGSize(width: 0, height: 0)
///Size指定が効かない?CollectionViewCellのSize指定で効くのでここは適当で指定
let imageManager = PHImageManager()
imageManager.requestImage(for: phAsset, targetSize: size, contentMode: .aspectFit, options: options) { (image, info) in
targetImage = image?.cropToSquare()
}
return targetImage
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return phAssets.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let asset = phAssets[indexPath.row]
var image = getImage(asset)
if image == nil{
image = UIImage(named: "NoImage")
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
guard let customCell = cell as? photoCollectionViewCell else {return cell}
customCell.setImage(image!)
updatePhotoModel(image!)//ハイライト効果や番号を振るためにImageをPhotoModelへ変換
return customCell
}
}
選択した画像がハイライトで見せる、選択番号が振る処理のためにphotoCollectionViewCellのカスタムCellを実装していきます。
4.カスタムCellを実装
class photoCollectionViewCell: UICollectionViewCell{
@IBOutlet weak var imageView: UIImageView! //選択した画像
@IBOutlet weak var cellContentView: UIView! //選択した画像のハイライト用
@IBOutlet weak var checkImageView: UIImageView!{ //選択順番の表示用
didSet{
checkImageView.isHidden = true
}
}
func setImage(_ image:UIImage){
imageView.image = image
}
func setBackColor(_ photoModel:PhotoModel){
if photoModel.isSelected {
cellContentView.backgroundColor = .black
checkImageView.isHidden = false
}else{
cellContentView.backgroundColor = .clear
checkImageView.isHidden = true
}
}
func setNumberImage(_ index: Int) {
let imageNmae = "\(index).circle.fill"
checkImageView.image = UIImage(systemName: imageNmae)
}
}
5.photoModelを実装
①この画像が選択されているかを示す値
②この画像がフォトライブラリの何番目の画像かを示すIDのような値(③計算のために)
③選択番号が振り直すためには今選択中の画像のIndexの再計算のために選択中の画像Modelの配列
以上のものた必要になります。具体的な実装はこちら
class PhotoModel {
var image:UIImage? = nil
var isSelected:Bool = false
var number: Int? = nil
}
extension ViewController: UICollectionViewDelegate{
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! photoCollectionViewCell
let photoModel = photoModels[indexPath.row]
var select = photoModel.isSelected
select.toggle()
photoModel.isSelected = select
if photoModel.number == nil{
orderArray.append(indexPath.row)
photoModel.number = indexPath.row
let index = orderArray.firstIndex(of: photoModel.number!)! + 1
cell.setNumberImage(index)
cell.setBackColor(photoModel)
selectedCells.append(cell)
}else{
orderArray = orderArray.filter{$0 != indexPath.row}
selectedCells = selectedCells.filter{$0 != cell}
photoModel.number = nil
updateCheckNumber()
cell.setBackColor(photoModel)
}
}
func updateCheckNumber(){
guard orderArray.count >= 1 else {return}
for i in 0...orderArray.count - 1{
let index = i + 1
let cell = selectedCells[i]
cell.setNumberImage(index)
}
}
}
6.CollectionViewCellのLayout調整
private func setupCollectionView(){
let width = (UIScreen.main.bounds.width - 25) / 4
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: width, height: width)
layout.minimumLineSpacing = 10
layout.minimumInteritemSpacing = 5
layout.sectionInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
collectionView.collectionViewLayout = layout
}