[iOS]CollectionViewCell의 selected, deselected 효과 구현하기

신용철·2020년 9월 21일
2

iOS_CollectionView

목록 보기
2/2

이 번 포스트에서는 collectionViewCell의 선택 및 선택 해제에 따라 cell의 layout을 변경하는 방법에 대해서 알아보겠습니다. collectionView에서 cell이 선택되거나 해제될 때 cell의 상태변화를 주고 싶은 경우가 있습니다. 이 때 일반적으로 collectionView(didSeleted:)와 collectionView(didDeseleted:) 메서드를 떠올리기 쉽지만 좋은 접근 방법은 아닙니다. 위 메서드들은 상태변화보다는 동작에 사용하는 편이 좋습니다. 그 이유는 너무 한 메서드에 참조가 많이 될 경우 기능과 UI가 조금만 복잡해져도 controll하기가 힘들어지기 때문입니다. 여기서는 두 가지 방법을 소개합니다.

1. .isSected(Bool) 속성을 사용하는 방법

  • collectionView가 있는 ViewController가 아닌 Cell 자체에서 속성을 정의 하는 방법입니다.
 class CollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var speakerLabel: UILabel!
    @IBOutlet weak var seperateLine: UILabel!
    
    
    override var isSelected: Bool {
        didSet{
            if isSelected {
                speakerLabel.textColor = .white
                seperateLine.backgroundColor = .white
            }
            else {
                seperateLine.backgroundColor = .clear
            }
        }
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }
}
  • UICollectionViewCell에 isSeleted 속성이 선언되어 있습니다. 이를 override하고 property observer를 사용하여 isSelected의 값이 변경될 때마다 cell내부의 속성값들을 바꿔줄 수 있습니다.

  • 하지만 이 방법은 초기세팅이 있는 경우에는 사용할 수 없습니다. collectionView가 처음 보여지는 시점에 이미 선택되어져 있어야 하는 값들이 있을 수 있습니다. 그럴 경우 아래와 같이 isSelected를 미리 설정해놓아야 하겠죠? 그런데 여기서 문제가 있습니다. 아래 예시 처럼 collectionView가 initaiate될 때 첫 번째 cell이 선택되도록 하고 앱을 실행한 후에 다른 cell을 클릭하면 .allowsMultipleSelection = false 임에도 불구하고 첫번째 cell이 선택해제가 되지 않습니다. 이런 문제가 발생하는 이유는 userInteraction으로 선택된 것이 아니라 code로 선택된 경우 실행을 타지 않기때문인데요. apple문서를 보면 다음과 같은 설명이 있습니다. It does not call this method when you programmatically set the selection.

extension ViewController: UICollectionViewDataSource {
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CustomCell 
        cell.testLabel.text = dummyList[indexPath.item]
        //
        if indexPath.item == 0 {
            cell.isSelected = true
        }
        
        return cell
    }
}
  • 정리하자면 isSelected 속성을 사용할 수 있는 경우는 초기 설정(selection 관련)값이 없을 경우입니다.

2. [Bool] 을 이용한 방법

  • 이 방법은 cell의 갯수 만큼 [Bool]을 만들어 true이면 selected, false이면 deselected라고 가정하여 코딩하는 방식입니다. 쉽게 풀어 설명하면 [true, false, false, false] 로 되어있으면 cell 4개 중 첫번째 cell만 선택이 되어있는 상태로 보는 것이죠.

  • 이 작업을 하기 위해서는 3가지 밑작업이 필요합니다. 1) cell이 click될 때마다 [Bool]을 최신화 시켜준다. 2) 최신화 된 [Bool]값을 기준으로 cell의 layout을 변경시켜주는 메서드를 작성한다. 3) 적당한 시점에 collectionView를 reload한다. 하나씩 살펴보도록 하겠습니다.

  • 이 방법의 핵심은 실제로 cell이 선택되던 말던 우리는 상관하지 않겠다는 겁니다. user의 눈에 click이 되는 것처럼 보이기만 하면 된다는 것이죠. 따라서 cell click이벤트가 발생하면 무조건 호출이 되는 delegate 메서드를 찾아서 사용해야 합니다. collectionView(didSelected:)와 같은 메서드는 click했을 때 호출되는 메서드가 아니라 click한 후에 진짜로 selected가 되었을 때 호출되는 메서드이기 때문에 우리의 목적과는 맞지 않습니다.

  • 여기서 우리가 사용해야할 delegate 메서드는 바로 collectionView(shouldSelectItemAt indexPath:)입니다. 이 메서드는 click이 되는 순간 바로 호출이 됩니다. 메서드의 내용은 중요하지 않습니다. 우리가 원하는 시점에 호출된다는 사실만이 중요한 것이죠. 하지만 이 메서드도 아주 유용하게 사용되기 때문에 사용법을 알기 원하신다면 다음 링크를 통해 확인하시기 바랍니다.( https://velog.io/@yongchul/CollectionView%EC%97%90%EC%84%9C-%EB%91%90-%EB%B2%88%EC%A7%B8-touch%EB%A1%9C-%EC%84%A0%ED%83%9D%ED%95%B4%EC%A0%9C-%ED%95%98%EA%B8%B0)

  • collectionView(shouldSelectItemAt indexPath:)에서는 click한 cell의 indexPath를 param으로 전달해주기 때문에 전달된 isSelectedArray[indexPath.item].toggle() 식으로 [Bool]값을 변경 시킬 수 있습니다.

extension BosePresetTableViewCell: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
        //isSelectedArray: [Bool]값으로 상위에서 선언해 놓은 property입니다.
        //cell의 갯수가 얼마인지에 따라 Array의 count가 설정되도록 viewDidLoad 등에서 작업해놓으셔야겠죠?
        self.isSelectedArray[indexPath.item].toggle()
        
}
  • 다음으로는 cell의 layout을 변경해주는 메서드를 아래와 같이 custom cell class에 구현합니다. 아래 예에서 update()를 보면 status라는 param으로 Bool값을 받고 있는 것을 볼 수 있는데, 이 것은 [Bool]값이 변할 때마다 해당 index의 cell 값에 [Bool][index]의 값이 할당되도록 하여 cell의 속성을 변화시켜주기 위한 것입니다.
class CollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var presetName: UILabel!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        presetName.adjustsFontSizeToFitWidth = true
    }
    
    func update(_ name:String?, status:Bool?){
        self.presetName.text = name
        
        if let status = status,
            status {
            self.presetName.textColor = .white
        } else {
            self.presetName.textColor = .black
        }
    }
}
  • 다음에는 collectionView(cellForItemAt:) 메서드에 cell.update()를 구현한 후, 코드 구현 내용에 맞게 원하는 시점에 collectionView.reloadData()를 호출하시면 됩니다. 여기서는 click 할 때마다 cell의 layout이 변경되는 것을 예로 하였으니 collectionView(shouldSelectItemAt:) 에서 구현하면 되겠습니다.
profile
iOS developer

0개의 댓글