디바이스에 저장되어있는 이미지를 불러와서 CollectionView 에다가 적용하고
CollectionView에 제스쳐 이벤트를 통해 다중선택 및 단일 선택 할수 있는 기능을 적용한다.
CollectionView를 만드는 편의성을 높히기 위해 RxSwift, RxCocoa를 사용 하였다
1. RxSwift, RxCocoa 라이브러리 설치 (pod)
pod 'RxSwift'
pod 'RxCocoa'
2. 저장공간 권한 획득 (Info.plist)
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires access to the photo library.</string>
3. 디바이스 이미지 가져오기
func grabPhotos(){
imageArray = []
DispatchQueue.global(qos: .background).async {
let imgManager=PHImageManager.default()
let requestOptions=PHImageRequestOptions()
requestOptions.isSynchronous=true
requestOptions.deliveryMode = .highQualityFormat
let fetchOptions=PHFetchOptions()
fetchOptions.sortDescriptors=[NSSortDescriptor(key:"creationDate", ascending: false)]
let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
if fetchResult.count > 0 {
for i in 0..<fetchResult.count{
imgManager.requestImage(for: fetchResult.object(at: i) as PHAsset, targetSize: CGSize(width:50, height: 50),contentMode: .aspectFill, options: requestOptions, resultHandler: { (image, error) in
self.imageArray.append(image!)
})
}
}
self.listObservable.onNext(self.imageArray)
}
}
4. 콜렉션뷰 셋팅
class ViewController: UIViewController {
@IBOutlet var mCollView: UICollectionView!
let listObservable = PublishSubject<[UIImage]>()
var imageArray = Array<UIImage>()
let disposeBag = DisposeBag()
private let sectionInsets = UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0)
override func viewDidLoad() {
super.viewDidLoad()
mCollView.delegate = self
mCollView.register(UINib(nibName: "GalleryItem", bundle: nil), forCellWithReuseIdentifier: "GalleryItem")
mCollView.allowsSelection = true
mCollView.allowsMultipleSelection = false
if let flowLayout = mCollView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.minimumLineSpacing = sectionInsets.left
flowLayout.minimumInteritemSpacing = sectionInsets.left
flowLayout.sectionInset = sectionInsets
}
initCollView()
setEditing(true, animated: true)
}
func initCollView()
{
listObservable.subscribe(on: MainScheduler.instance)
.asObservable()
.bind(to: mCollView.rx.items(cellIdentifier: "GalleryItem", cellType: GalleryItem.self))
{
index, item, cell in
cell.imageview.image = item
}.disposed(by: disposeBag)
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if isEditing == false {
collectionView.deselectItem(at: indexPath, animated: false)
}
}
func collectionView(_ collectionView: UICollectionView, shouldBeginMultipleSelectionInteractionAt indexPath: IndexPath) -> Bool {
return true
}
func collectionView(_ collectionView: UICollectionView, didBeginMultipleSelectionInteractionAt indexPath: IndexPath) {
setEditing(true, animated: true)
}
func collectionViewDidEndMultipleSelectionInteraction(_ collectionView: UICollectionView) {
print("\(#function)")
}
}
class GalleryItem: UICollectionViewCell {
var cellDisposedBag = DisposeBag()
@IBOutlet weak var backview: UIView!
@IBOutlet weak var imageview: UIImageView!
override func prepareForReuse() {
super.prepareForReuse()
cellDisposedBag = DisposeBag()
isSelected = false
showSelectionOverlay()
}
private func showSelectionOverlay() {
if isSelected
{
backview.backgroundColor = UIColor.red
backview.alpha = 0.5
}
else
{
backview.backgroundColor = UIColor.clear
backview.alpha = 1.0
}
}
func configureCell() {
isSelected = true
showSelectionOverlay()
}
override var isSelected: Bool {
didSet {
showSelectionOverlay()
setNeedsLayout()
}
}
}
override func setEditing(_ editing: Bool, animated: Bool) {
guard isEditing != editing else {
return
}
super.setEditing(editing, animated: animated)
mCollView.allowsMultipleSelection = editing
clearSelectedItems(animated: true)
}
func clearSelectedItems(animated: Bool) {
mCollView.indexPathsForSelectedItems?.forEach({ (indexPath) in
mCollView.deselectItem(at: indexPath, animated: animated)
})
mCollView.reloadItems(at: mCollView.indexPathsForVisibleItems)
}
5. 실행 결과
6. 전체 소스
import UIKit
import RxCocoa
import RxSwift
import Photos
class ViewController: UIViewController {
@IBOutlet var mCollView: UICollectionView!
let listObservable = PublishSubject<[UIImage]>()
let disposeBag = DisposeBag()
var imageArray = Array<UIImage>()
private let sectionInsets = UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0)
override func viewDidLoad() {
super.viewDidLoad()
mCollView.delegate = self
mCollView.register(UINib(nibName: "GalleryItem", bundle: nil), forCellWithReuseIdentifier: "GalleryItem")
mCollView.allowsSelection = true
mCollView.allowsMultipleSelection = false
if let flowLayout = mCollView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.minimumLineSpacing = sectionInsets.left
flowLayout.minimumInteritemSpacing = sectionInsets.left
flowLayout.sectionInset = sectionInsets
}
initCollView()
setEditing(true, animated: true)
grabPhotos()
}
func grabPhotos(){
imageArray = []
DispatchQueue.global(qos: .background).async {
let imgManager=PHImageManager.default()
let requestOptions=PHImageRequestOptions()
requestOptions.isSynchronous=true
requestOptions.deliveryMode = .highQualityFormat
let fetchOptions=PHFetchOptions()
fetchOptions.sortDescriptors=[NSSortDescriptor(key:"creationDate", ascending: false)]
let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
if fetchResult.count > 0 {
for i in 0..<fetchResult.count{
imgManager.requestImage(for: fetchResult.object(at: i) as PHAsset, targetSize: CGSize(width:50, height: 50),contentMode: .aspectFill, options: requestOptions, resultHandler: { (image, error) in
self.imageArray.append(image!)
})
}
}
self.listObservable.onNext(self.imageArray)
}
}
func initCollView()
{
listObservable.subscribe(on: MainScheduler.instance)
.asObservable()
.bind(to: mCollView.rx.items(cellIdentifier: "GalleryItem", cellType: GalleryItem.self))
{
index, item, cell in
cell.imageview.image = item
}.disposed(by: disposeBag)
}
override func setEditing(_ editing: Bool, animated: Bool) {
guard isEditing != editing else {
return
}
super.setEditing(editing, animated: animated)
mCollView.allowsMultipleSelection = editing
clearSelectedItems(animated: true)
}
func clearSelectedItems(animated: Bool) {
mCollView.indexPathsForSelectedItems?.forEach({ (indexPath) in
mCollView.deselectItem(at: indexPath, animated: animated)
})
mCollView.reloadItems(at: mCollView.indexPathsForVisibleItems)
}
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if isEditing == false {
collectionView.deselectItem(at: indexPath, animated: false)
}
}
func collectionView(_ collectionView: UICollectionView, shouldBeginMultipleSelectionInteractionAt indexPath: IndexPath) -> Bool {
return true
}
func collectionView(_ collectionView: UICollectionView, didBeginMultipleSelectionInteractionAt indexPath: IndexPath) {
setEditing(true, animated: true)
}
func collectionViewDidEndMultipleSelectionInteraction(_ collectionView: UICollectionView) {
print("\(#function)")
}
}
class GalleryItem: UICollectionViewCell {
var cellDisposedBag = DisposeBag()
@IBOutlet weak var backview: UIView!
@IBOutlet weak var imageview: UIImageView!
override func prepareForReuse() {
super.prepareForReuse()
cellDisposedBag = DisposeBag()
isSelected = false
showSelectionOverlay()
}
private func showSelectionOverlay() {
if isSelected
{
backview.backgroundColor = UIColor.red
backview.alpha = 0.5
}
else
{
backview.backgroundColor = UIColor.clear
backview.alpha = 1.0
}
}
func configureCell() {
isSelected = true
showSelectionOverlay()
}
override var isSelected: Bool {
didSet {
showSelectionOverlay()
setNeedsLayout()
}
}
}
아래 애플 공식문서에 좀더 자세한 예시 프로젝트가 있으니까 꼭 확인 하세요!
참고 :
https://github.com/Akhilendra/photosAppiOS
'IOS' 카테고리의 다른 글
Swift - ReactorKit (0) | 2021.06.09 |
---|---|
Swift - Photos + CollectionView + Drag and Drops (0) | 2021.05.30 |
프로젝트에 SwiftLint를 달아보자 (0) | 2021.03.01 |
SwiftUI - ViewPager 관련 기능을 달아보자 (0) | 2021.02.16 |
Swift - Realm 통한 데이터 저장을 해보자 (1) | 2021.01.29 |