Skip to content

fix(ios): persist position during page list head changes#1078

Open
alpha0010 wants to merge 2 commits into
callstack:masterfrom
alpha0010:patch-5
Open

fix(ios): persist position during page list head changes#1078
alpha0010 wants to merge 2 commits into
callstack:masterfrom
alpha0010:patch-5

Conversation

@alpha0010
Copy link
Copy Markdown
Contributor

Summary

Dynamic changes to the page list prior to the currently visible page now maintain the same visible page on iOS (this already works on Android).

Fixes #965

Test Plan

What's required for testing (prerequisites)?

function DemoPager() {
  const allPages = [0, 1, 2, 3, 4];
  const [pages, setPages] = useState(allPages);

  const togglePage = (page: number) =>
    setPages((prev) => {
      const itemIndex = prev.indexOf(page);
      const next = [...prev];
      if (itemIndex === -1) {
        next.push(page);
        next.sort((a, b) => a - b);
      } else {
        next.splice(itemIndex, 1);
      }
      return next;
    });

  return (
    <View style={styles.container}>
      <PagerView style={styles.container}>
        {pages.map((page) => (
          <View collapsable={false} key={`x:${page}`}>
            <Text>{page}</Text>
          </View>
        ))}
      </PagerView>
      <View style={styles.container}>
        {allPages.map((page) => (
          <Button
            key={`x:${page}`}
            onPress={() => togglePage(page)}
            title={`Toggle ${page}`}
          />
        ))}
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {flex: 1},
});

What are the steps to reproduce (after prerequisites)?

Use the above test screen to add/remove pages before/after the currently visible page. With this PR, iOS should now match the existing Android behavior.

Compatibility

OS Implemented
iOS
Android N/A

Checklist

  • I have tested this on a device and a simulator
  • I added the documentation in README.md
  • I updated the typed files (TS and Flow)

@alpha0010 alpha0010 marked this pull request as draft May 8, 2026 16:48
@alpha0010
Copy link
Copy Markdown
Contributor Author

Discovered a potential issue with this implementation, investigating.

@alpha0010
Copy link
Copy Markdown
Contributor Author

So, I am at a bit of a loss. After the following data transition:

Before:
A B C D E
    ^

After:
B X C Y D
    ^

Page "C" is still correctly the active page, but instead of displaying content, renders blank white. The neighboring pages still render correctly, and if I swipe away and swipe back, "C" will render again. But this error does not occur on initial load, only after using the pager some.

Any insights you might have would be greatly appreciated. I feel like I must be missing some fundamental concept, since all my logging and debugging efforts seem to say everything is working as intended.

@alpha0010
Copy link
Copy Markdown
Contributor Author

Documenting my testing:

  • Manage tabs/current page via id (from RepresentableView) instead of index
    • Seems to work essentially the same as the current implementation (including exhibiting the blank screen bug from the above described data transition)
  • In .onChange(of: props.children), if the above data transition is detected, increment the TabView.id() to force a rebuild
    • Fixes the initial blank problem
    • Navigating away and back programmatically works fine
    • Swiping away and back triggers the blank screen (the first time; the second time swiping away/back, it displays correctly)
  • Most related I could find is: https://stackoverflow.com/questions/59450948/swiftui-why-is-tabview-and-its-contents-sometimes-not-refreshing-when-i-change , which makes me wonder if this logic path is hitting a bug in TabView's internal implementation.

@alpha0010
Copy link
Copy Markdown
Contributor Author

What works:

  • Correct active page is maintained when inserting/deleting at any index, on both iOS and Android
    • Note: this already was the state on Android, but now iOS works as well

What fails:

  • On iOS, inserting a page immediately prior to the active page (while simultaneously dropping the list head page), swiping away, then swiping back, displays a blank screen (on next swipe, the screen correctly renders again)
    • Workaround in my app, is to pass the pager a wrapper array of Views, that manage actual insertions by swapping children, but as far as the pager knows, only insert/delete at the head/tail of the array
  • On Android, consecutive animated programmatic page turns (with state changes to the children pages in between), result in the programmatic page turn navigating to the wrong index
    • Workaround in my app is to use non-animated programmatic page turns (since this does not trigger the bug)

I have been unable solve either of these bugs, but since they have workarounds, I cannot really justify spending more dev time on them currently. Marking this as ready (we already use it locally via patches). I think it is an improvement over this library's current status, despite not solving all the dynamic page list related bugs.

@alpha0010 alpha0010 marked this pull request as ready for review May 18, 2026 20:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

(iOS) Dynamic pages causes page switch when adding pages at the start

1 participant