티스토리 뷰

반응형

Xcode 13.2 로 iOS 15를 빌드했을 때, 발생하는 문제이다. (iOS 14 이하에서는 발생안함)

 

# 이슈 

cell을 토글하면 isSelected가 잘 토글되고 아래 코드도 잘 동작한다. 

class SomeCollectionViewCell: UICollectionViewCell {

    override var isSelected: Bool {
        willSet {
            if newValue {
                self.backgroundColor = .red
            } else {
                self.backgroundColor = .gray
            }
        }
    }
    
    ...
}

 

하지만 사용자의 액션이 아니라 코드로 직접 isSelected 값을 바꾸면 

순간적으로는 코드로 세팅해줬던 값으로 바뀌지만,  이전의 isSelected 값으로 다시 원복된다. 

디버깅해보면 순차적인 값 변화를 알 수 있지만, 실제 테스트해보면 뷰의 변화가 전혀 안보이기 때문에 isSelected가 일절 안바뀌는 것처럼 보인다. 

class SomeCollectionViewCell: UICollectionViewCell {

    override var isSelected: Bool {
        willSet {
            if newValue {
                self.backgroundColor = .red
            } else {
                self.backgroundColor = .gray
            }
        }
    }
    
    ... 
    
    func configure(number: Int) {
        ... 
        self.isSelected = true
    }
}

 

 

 

# 원인 분석 (1) 

왜 iOS 15에서 동작이 달라졌을까,,, 하면서 찾아보니 

WWDC 2021 > Make blazing fast lists and collection views 에서 

collectionview cell prefetching이 확장되었다고 했다. 

 

 

이것이 원인인가 싶어 prefetching 옵션을 끄고 돌려봐도 여전히 똑같았다. 

self.collectionView.isPrefetchingEnabled = false

 

이것은 원인이 아닌 것으로 판단! 

 

 

# 원인 분석(2) 

UICollectionViewCell > isSelected 문서를 보자. 

직접 이 value를 설정하지 말라고 되어있다. 코드로 바꾸면 cell appearance가 안바뀔 것이라고 하면서,,

적절하게 cell을 select 또는 higlight 하는 방법은 collection view의 selection methods를 사용하는 것이라고 한다. 

 

 

혹시 다른 isSelected 도 코드로 값을 바꾸지 말라고 되어있나..?  하고 isSelected 문서들을 읽어봤는데,

UICollectionViewCell > isSelected 에만 그렇게 되어어있음! 

 

https://developer.apple.com/search/?q=isSelected 

 

Search - Apple Developer

Apple Developer Search

developer.apple.com

 

 

 

그럼 문서에 나와있는데로 collection view의 selection methods를 사용해보자. 

이번에는 테스트할 때, 구분을 해주기 위해서 전체 selection이 아닌 반만 selection 하도록 해줬다. 

configure에서 isSelected 해주는 코드도 지워줬다. 

class SomeCollectionViewCell: UICollectionViewCell {
    
    override var isSelected: Bool {
        willSet {
            if newValue {
                self.backgroundColor = .red
            } else {
                self.backgroundColor = .gray
            }
        }
    }
    
    func configure(number: Int) {
        ...
    }
}
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(SomeCollectionViewCell.self, for: indexPath)
        cell.configure...
        if indexPath.row % 2 == 0 {
            collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredVertically)
        } else {
            collectionView.deselectItem(at: indexPath, animated: false)
        }
        return cell
    }

 

그럼 iOS 15에서는 잘된다. 

 

iOS 15

 

하지만 iOS 14는 동작안한다. (iOS 13도 마찬가지) 

 

iOS 14

 

그럼 결국 모든 버전을 대응하기 위해서는 이렇게 해야한다는 것인가!!!! 

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(ChapterNumberCollectionViewCell.self, for: indexPath)
        cell.configure...
        if indexPath.row % 2 == 0 {
            cell.isSelected = true
            collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredVertically)
        } else {
            cell.isSelected = false
            collectionView.deselectItem(at: indexPath, animated: false)
        }
        return cell
    }

 

 

# 해결방안 

위와 같이 코드를 작성하면 혼란을 줄 수 있고 문서에서 권장하지 않는 방법 (= isSelected를 코드로 바꾸는 것) 을 여전히 사용하는 셈이다.

그래서 코드로 isSelected를 수동으로 바꿔야하는 경우가 있다면,  own property 나 method 를 두는 것이 좋다. 

 

예를들어 아래와 같은 메소드를 만들어주고 (UITableviewCell 의 setSelected(_:animated:)  와 비슷한 형태로 만들어줌)

코드로 isSelected를 바꾸지 않도록 해주는 것이다.

그럼 버전 상관없이 잘 동작! 

class SomeCollectionViewCell: UICollectionViewCell {
    
    override var isSelected: Bool {
        willSet {
            self.setSelected(newValue)
        }
    }
    
    ... 
    
    func configure(number: Int) {
        ...
        self.setSelected(true)
    }
    
    private func setSelected(_ selected: Bool) {
        if selected {
            self.backgroundColor = .red
        } else {
            self.backgroundColor = .gray
        }
    }
}

 

 

 

# 참고 (비슷한 이슈 제보)

 

https://github.com/Instagram/IGListKit/issues/524

 

Cell preselection · Issue #524 · Instagram/IGListKit

I'm trying to preselect a cell in cellForItem(at index: Int) -> UICollectionViewCell by cell.isSelected = true in section controller. It works fine and cell becomes selected. When I try to d...

github.com

 

 

반응형
댓글