software-mansion/react-freeze

Children in a frozen `ScrollView` calls onLayout, but only on Android and not iOS

Opened this issue · 1 comments

When freezing a ScrollView in Android, the children of the ScrollView triggers onLayout with width, height, x, and y` all being 0. The main issue here is that it happens on only Android and not iOS. Ideally it would not happen on either platform.

Screen.Recording.2023-12-12.at.6.34.22.PM.mov

Sample code can be found here: https://github.com/tpcstld/react-native-test/tree/tpcstld/freeze-android

Steps to Repro:

  1. Open the app
  2. Click on Toggle Freeze

Observe that the last recorded height and width change on Android, but not iOS.

+1. as a result of this, if you have a RecyclerView (and perhaps other virtualized list components) inside the frozen component, un-freezing can result in the list losing its scroll position because child views can change height if they are set to width: MATCH_PARENT and height: WRAP_CONTENT.

as a workaround, it is possible to override onMeasure in the child views of your list and do something like this, but it's a bit hacky and requires subclassing View on the native side:

// subclass View
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
  val parent = parent
  val shouldUseCachedMeasurements =
          parent != null && parent.measuredWidth == 0
          && View.MeasureSpec.getMode(widthMeasureSpec) == View.MeasureSpec.EXACTLY
   }

    if (shouldUseCachedMeasurements) {
        val previouslyMeasuredWidth = measuredWidth
        val previouslyMeasuredHeight = measuredHeight
        super.onMeasure(
            View.MeasureSpec.makeMeasureSpec(previouslyMeasuredWidth, View.MeasureSpec.EXACTLY),
            View.MeasureSpec.makeMeasureSpec(previouslyMeasuredHeight, View.MeasureSpec.EXACTLY),
        )
    } else {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }
}