override var isHighlighted: Bool {
didSet {
highlightIndicator.isHidden = !isHighlighted
}
}
override var isSelected: Bool {
didSet {
highlightIndicator.isHidden = !isSelected
selectIndicator.isHidden = !isSelected
}
}
// MARK: - Selected Button
enum Mode {
case view
case select
}
// UICollecionView에서 선택한 셀의 IndexPath를 Key로,
// 선택 여부를 value로 가지며, 선택한 셀들의 정보를 저장한다.
var dictionarySelectedIndexPath: [IndexPath : Bool] = [:]
var eMode: Mode = .view {
didSet {
switch eMode {
// 선택 모드에서 뷰 모드로 변경할 때, 선택한 셀들을 모두 선택 해제하고 선택된 셀의 indexPath를 저장하는 딕셔너리를 비우고, 다시 선택할 수 없는 단일 선택 모드로 변경해주는 역할을 한다.
case .view:
for (key, value) in dictionarySelectedIndexPath {
if value {
collectionView.deselectItem(at: key, animated: true)
}
}
dictionarySelectedIndexPath.removeAll()
collectionView.allowsMultipleSelection = false
case .select:
let selectButton = UIButton(type: .custom)
selectButton.setImage(UIImage(named: "canceledButton")?.withRenderingMode(.alwaysOriginal), for: .normal)
selectButton.addTarget(self, action: #selector(didCanceledButtonClicked), for: .touchUpInside)
let selectBarButtonItem = UIBarButtonItem(customView: selectButton)
let addButton = UIButton(type: .custom)
addButton.setImage(UIImage(named: "deletedButtonDisabled")?.withRenderingMode(.alwaysOriginal), for: .normal)
addButton.addTarget(self, action: #selector(didDeleteButtonClicked), for: .touchUpInside)
let addBarButtonItem = UIBarButtonItem(customView: addButton)
navigationItem.rightBarButtonItems = [addBarButtonItem, selectBarButtonItem]
// collectionView에서 다중 선택이 가능하도록 설정
collectionView.allowsMultipleSelection = true
}
}
}
선택버튼
let selectButton = UIButton(type: .custom)
selectButton.setImage(UIImage(named: "canceledButton")?.withRenderingMode(.alwaysOriginal), for: .normal)
selectButton.addTarget(self, action: #selector(didCanceledButtonClicked), for: .touchUpInside)
let selectBarButtonItem = UIBarButtonItem(customView: selectButton)
추가버튼
let addButton = UIButton(type: .custom)
addButton.setImage(UIImage(named: "deletedButtonDisabled")?.withRenderingMode(.alwaysOriginal), for: .normal)
addButton.addTarget(self, action: #selector(didDeleteButtonClicked), for: .touchUpInside)
let addBarButtonItem = UIBarButtonItem(customView: addButton)
취소버튼 (선택버튼이 클릭되면, 취소버튼이 나오도록 설정)
let selectButton = UIButton(type: .custom)
selectButton.setImage(UIImage(named: "canceledButton")?.withRenderingMode(.alwaysOriginal), for: .normal)
selectButton.addTarget(self, action: #selector(didCanceledButtonClicked), for: .touchUpInside)
let selectBarButtonItem = UIBarButtonItem(customView: selectButton)
삭제버튼
lazy var deleteBarButton : UIBarButtonItem = {
let button = UIButton(type: .system)
button.setImage(UIImage(named: "deletedButtonGrey")?.withRenderingMode(.alwaysOriginal), for: .normal)
button.addTarget(self, action: #selector(didDeleteButtonClicked), for: .touchUpInside)
return UIBarButtonItem(customView: button)
}()
📝 UIBarButton으로 구현하지 않고, UIButton으로 구현한 이유
삭제버튼
@objc func didDeleteButtonClicked(_ sender: UIBarButtonItem) {
// 선택된 사진이 없으면 실행하지 않음
guard !dictionarySelectedIndexPath.isEmpty else {
return
}
let alert = UIAlertController(title: nil, message: "삭제하시겠습니까?", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "취소", style: .cancel, handler: nil)
alert.addAction(cancelAction)
let deleteAction = UIAlertAction(title: "삭제", style: .destructive) { [self] _ in
var deleteNeededIndexPaths: [IndexPath] = []
print(deleteNeededIndexPaths.isEmpty)
for (key, value) in self.dictionarySelectedIndexPath {
if value {
deleteNeededIndexPaths.append(key)
}
}
// realm 데이터 삭제
**for indexPath in deleteNeededIndexPaths.sorted(by: { $0.item > $1.item }) {
guard let photoData = self.viewModel.photoData(at: indexPath) else { return }
self.realmManager.delete(photoData: photoData)
}
// colletionViewCell 삭제
self.collectionView.deleteItems(at: deleteNeededIndexPaths)
self.dictionarySelectedIndexPath.removeAll()
self.collectionView.reloadData()**
// 선택 모드 해제
updateViewWithButtons()
}
alert.addAction(deleteAction)
present(alert, animated: true, completion: nil)
}
// 선택된 셀의 IndexPath를 담은 배열인 deleteNeededIndexPaths를 정렬하여, 삭제해야 할 셀의 인덱스를 큰 값부터 작은값으로 정렬한다.
// 그런 다음 반복문을 사용하여, 삭제해야 할 IndexPath에 대한 작업을 수행한다.
for indexPath in deleteNeededIndexPaths.sorted(by: { $0.item > $1.item }) {
guard let photoData = self.viewModel.photoData(at: indexPath) else { return }
self.realmManager.delete(photoData: photoData)
}
// colletionViewCell 삭제
self.collectionView.deleteItems(at: deleteNeededIndexPaths)
self.dictionarySelectedIndexPath.removeAll()
self.collectionView.reloadData()
// 선택 모드 해제
updateViewWithButtons()
}
추가버튼
@objc func addButtonTapped(_ sender: Any) {
let photoDetailVC = PhotoDetailViewController()
navigationController?.pushViewController(photoDetailVC, animated: true)
}
선택버튼
@objc func didSelectButtonClicked(_ sender: UIBarButtonItem) {
if eMode == .select {
eMode = .view
collectionView.alpha = 1.0
if dictionarySelectedIndexPath.isEmpty {
deleteBarButton.isEnabled = false
}
} else {
eMode = .select
collectionView.alpha = 0.7
deleteBarButton.isEnabled = true
}
}
취소버튼
@objc func didCanceledButtonClicked(_ sender: UIBarButtonItem) {
updateViewWithButtons()
}
updateViewWithButtons()
함수로 묶었다.private func updateViewWithButtons() {
eMode = .view
let addButton = UIButton(type: .custom)
addButton.setImage(UIImage(named: "addButton")?.withRenderingMode(.alwaysOriginal), for: .normal)
addButton.addTarget(self, action: #selector(addButtonTapped), for: .touchUpInside)
let addBarButtonItem = UIBarButtonItem(customView: addButton)
let selectButton = UIButton(type: .custom)
selectButton.setImage(UIImage(named: "selectedButton")?.withRenderingMode(.alwaysOriginal), for: .normal)
selectButton.addTarget(self, action: #selector(didSelectButtonClicked), for: .touchUpInside)
let selectBarButtonItem = UIBarButtonItem(customView: selectButton)
let space = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
space.width = 10
navigationItem.rightBarButtonItems = [addBarButtonItem, space, selectBarButtonItem]
collectionView.alpha = 1.0
collectionView.reloadData()
}
삭제버튼
@objc func didDeleteButtonClicked(_ sender: UIBarButtonItem) {
// 선택된 사진이 없으면 실행하지 않음
guard !dictionarySelectedIndexPath.isEmpty else {
return
}
let alert = UIAlertController(title: nil, message: "삭제하시겠습니까?", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "취소", style: .cancel, handler: nil)
alert.addAction(cancelAction)
let deleteAction = UIAlertAction(title: "삭제", style: .destructive) { [self] _ in
var deleteNeededIndexPaths: [IndexPath] = []
print(deleteNeededIndexPaths.isEmpty)
for (key, value) in self.dictionarySelectedIndexPath {
if value {
deleteNeededIndexPaths.append(key)
}
}
// realm 데이터 삭제
for indexPath in deleteNeededIndexPaths.sorted(by: { $0.item > $1.item }) {
guard let photoData = self.viewModel.photoData(at: indexPath) else { return }
self.realmManager.delete(photoData: photoData)
}
// colletionViewCell 삭제
self.collectionView.deleteItems(at: deleteNeededIndexPaths)
self.dictionarySelectedIndexPath.removeAll()
self.collectionView.reloadData()
// 선택 모드 해제
updateViewWithButtons()
}
alert.addAction(deleteAction)
present(alert, animated: true, completion: nil)
}
dictionarySelectedIndexPath
딕셔너리에서 삭제가 필요한 인덱스들을 deleteNeededIndexPaths
배열에 저장한다.deleteNeededIndexPaths
배열에 있는 각 인덱스에 해당하는 셀들을 삭제한다.updateViewWithButtons()
메서드를 호출하여 선택 모드를 해제한다.func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
self.dictionarySelectedIndexPath[indexPath] = false // 선택을 해제한 셀의 IndexPath를 딕셔너리에서 제거
}
UICollectionViewDelegate
프로토콜의 collectionView(_:didDeselectItemAt:)
메서드를 구현하는 것이다.. 이 메서드는 Collection View에서 셀을 선택 해제할 때마다 호출collectionView(_:didDeselectItemAt:)
메서드를 활용하여 선택을 해제한 IndexPath를 추적하고, 딕셔너리에서 제거하는 코드를 추가