본문 바로가기

iOS/JourneyOfFaith App

[iOS] 컬랙션 뷰 동적으로 Cell 사이즈 변경 후 jumping scroll 오류 원인 분석 및 해결 방법

 

안녕하세요!

오늘은 앱 개발을 하며 마주한 오류의 원인과 해결 방법을 소개하려구 포스트를 작성했습니다.

#UICollectionView Scroll Jumping issue

#UITableView Scroll Jumping error

#동적 사이즈 변화시에 스크롤 버벅임

 

마주한 스크롤 contentOffset 이슈

상황은 다음과 같습니다.

 

1. 사용자의 터치 이벤트가 발생하면 컬랙션뷰 내부 셀들의 크기가 동적으로 변화해야 합니다.

2. 동적으로 변화된 셀 사이즈를 반영하기 위해 reloadData한 이후 스크롤 할 경우 스크롤 함에도, 스크롤 위치가 순간이동되는 현상이 발생됬습니다.

 

Case 1. [정상적인 경우 및 로직 소개]

정상적인 화면 위 아래 스크롤 ( 동적 변화 x )

 

 

위 동영상은 Cell 사이즈가 변경되지 않았을 경우 정상적인 화면 동작 결과입니다.

 

 


 

이 로직은 사용자가 폰트크기를 변경하는 이벤트가 호출될 때마다 호출되는 함수입니다. 사용자가 폰트크기를 변경하면, 위 함수 호출 이전에 각각의 셀의 데이터를 담당하는 데이터 구조체 프로퍼티들의 폰트 값은 업데이트 된 새로운 값으로 반영해 저장합니다. 그리고 위 함수가 호출되어 새로운 폰트가 반영된 셀들이 보여지게 됩니다.

 

( 이때 visibleCells만 cell.폰트 업데이트 함수를 호출하던가, 보이는 item들만 reloadItems(at:)를 통해 업데이트 하던, reloadData()를 하던 로직이 같기에 상관이 없습니다. )

 

 

위 함수의 로직은 폰트가 변화될 때마다, 편의상 기존에 사용자가 보고 있었던 scrollableView의 contentSize에서 offset을 저장한 후에, 새롭게 폰트에 의한 cell들의 높이와, 그에 따른 contentSize가 변화 되어도 저장된 offset을 보여주기는 로직입니다.

 

폰트 사이즈 동적 확대 후 스크롤

 

이렇게 스크롤 위치를 맨 위에 두고 폰트 크기를 변경 할 경우 폰트 사이즈를 동적으로 적용후 스크롤 해도 경우 문제가 없습니다.

 

case 2. [문제 발생 시점]: 화면의 맨 하단, 또는 contentSize의 절반 이하에서 contentSize가 변화되는 경우

폰트사이즈를 동적으로 감소한 후 아래 -> 위로만 스크롤 할 때

 

위 동영상은 폰트 크기를 감소해 cell의 사이즈를 동적으로 낮춘  후에 아래 -> 위로만 스크롤 하는 경우이고, 위로 스크롤 함에도 불구하고 지속적으로 맨 아래로 이동해서 위로 스크롤되는 문제가 발생되는 영상입니다.

 

 

화면의 하단 부분에서 cell들의 높이를 변경할 때(폰트 크기를 변경할 때) 그리고 reloadData를 활용해 새로 반영된 폰트 크기를 cellForRowAt시점에 적용 후 업데이트 할 때입니다.

 

1. 폰트 변경 이벤트

2. reloadData호출

3. cellForRowAt시점에 새로 반영된 폰트에 의한 cell 크기 동적으로 collectionViewLayout에서 높이 동적으로 측정

4. 새롭게 업데이트 된 셀들이 화면에 display됨.

 

이때 문제가 있습니다. 

 

 

scrollableView의 스크롤 가능한 ContentSize는 한정되어 있습니다.

이 상태에서 사용자가 현재 스크롤했던 위치 contentOffset.y를 저장 후, 동적으로 cell의 높이가 변회되고, contentSize는 증가 또는 감소하게되어 기존에 측정했던 contentOffset.y는 무용지물의 값이 되버리는 것입니다. 그래서 기존 컨텐츠 크기가 작아지지만 이전값 위치로 스크롤하게되어( setContentOffset(_:animated:) ) 몇번이고 스크롤이 되면서야 스크롤 뷰의 contentOffset은 변경된 ContentSize에 대해 정상적인 값을 갖게 됩니다.

 

 

그냥 컬랙션 뷰의 리로드만 하게 된다면 어떻게 될까요?!..

(performBatchUpdates 내부에서 특정 섹션만 업데이트하던, 아래처럼 동일한 결과가 발생됩니다)

 

collectionView.offset.y가 정상적인 값을 찾기까지 아래 -> 위로 스크롤

 

좀 더 심해집니다. 

 

기존 ContentSize의 높이가 8000이었는데, 갑자기 1000으로 바뀐다면 collectionView.contentOffset.y는 기존에 갖고 있던 값이 사용자가 스크롤 할 때에서야 본연의 위치로 맞춰지고 그러기위해 스크롤을 아래서 위로 해도 계속해서 아래에서 위로 순간이동해서 올라가는 것처럼 보이게 됩니다.

 

이를 해결하기 위해서는 셀이 1개라도 있는한 offset.y == 0의 값을 주거나, 스크롤의 contentSize가 변경될 때 변경 전 후의 비율까지 고려해서 스크롤 위치를 변경해주어야 합니다. (offset위치를 기억해야 한다면 ratio)필수! 그럼에도 ratio가 완벽하지 않다면 결국 ContentSize보다 offset.y가 아래나 scrollable offset.y위치보다 아래에 위치하게 되어 스크롤 점핑이 나타나게 됩니다.

 

결론?!

 

음.. 저는 그래도 사용자가 고정된 위치에서 폰트 크기가 변화되는걸 감지하는게 좋을것 같다고 생각해서 offset.y == 0 (화면 맨 위) 로 설정했습니다.