{"action":"created","issue":{"url":"https://api.github.com/repos/material-components/material-components-ios/issues/5178","repository_url":"https://api.github.com/repos/material-components/material-components-ios","labels_url":"https://api.github.com/repos/material-components/material-components-ios/issues/5178/labels{/name}","comments_url":"https://api.github.com/repos/material-components/material-components-ios/issues/5178/comments","events_url":"https://api.github.com/repos/material-components/material-components-ios/issues/5178/events","html_url":"https://github.com/material-components/material-components-ios/pull/5178","id":361856949,"node_id":"MDExOlB1bGxSZXF1ZXN0MjE2NzIyOTYz","number":5178,"title":"[FlexibleHeader] Improve tracking scroll view switching logic.","user":{"login":"jverkoey","id":45670,"node_id":"MDQ6VXNlcjQ1Njcw","avatar_url":"https://avatars0.githubusercontent.com/u/45670?v=4","gravatar_id":"","url":"https://api.github.com/users/jverkoey","html_url":"https://github.com/jverkoey","followers_url":"https://api.github.com/users/jverkoey/followers","following_url":"https://api.github.com/users/jverkoey/following{/other_user}","gists_url":"https://api.github.com/users/jverkoey/gists{/gist_id}","starred_url":"https://api.github.com/users/jverkoey/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jverkoey/subscriptions","organizations_url":"https://api.github.com/users/jverkoey/orgs","repos_url":"https://api.github.com/users/jverkoey/repos","events_url":"https://api.github.com/users/jverkoey/events{/privacy}","received_events_url":"https://api.github.com/users/jverkoey/received_events","type":"User","site_admin":false},"labels":[{"id":328243523,"node_id":"MDU6TGFiZWwzMjgyNDM1MjM=","url":"https://api.github.com/repos/material-components/material-components-ios/labels/[FlexibleHeader]","name":"[FlexibleHeader]","color":"eeeeee","default":false},{"id":273191333,"node_id":"MDU6TGFiZWwyNzMxOTEzMzM=","url":"https://api.github.com/repos/material-components/material-components-ios/labels/cla:%20yes","name":"cla: yes","color":"c2e0c6","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-09-19T17:47:42Z","updated_at":"2018-09-19T17:51:31Z","closed_at":null,"author_association":"MEMBER","pull_request":{"url":"https://api.github.com/repos/material-components/material-components-ios/pulls/5178","html_url":"https://github.com/material-components/material-components-ios/pull/5178","diff_url":"https://github.com/material-components/material-components-ios/pull/5178.diff","patch_url":"https://github.com/material-components/material-components-ios/pull/5178.patch"},"body":"## Context\r\n\r\nThe flexible header component supports horizontal-paging interfaces that consist of multiple vertically scrolling scroll views. This type of interface is most commonly paired with a tabs component that makes it possible to quickly jump to a specific column. I will use the term \"switching tabs\" to refer to the act of changing from tracking one vertical scroll view to another.\r\n\r\n## The problem\r\n\r\nWhen switching tabs we can't guarantee that the content we're moving to will have a comparable content offset as the content we're switching from. This can be problematic in cases where the flexible header's height is able to expand or when the flexible header is able to shift off screen because the height or off-screen position might have to change.\r\n\r\n## Repro cases\r\n\r\nAll of these repro cases share the same common configuration:\r\n\r\n```swift\r\nappBarViewController.headerView.minMaxHeightIncludesSafeArea = false\r\nappBarViewController.inferTopSafeAreaInsetFromViewController = true\r\nappBarViewController.headerView.canAlwaysExpandToMaximumHeight = true\r\nappBarViewController.headerView.sharedWithManyScrollViews = true\r\n\r\nappBarViewController.headerView.minimumHeight = 56\r\nappBarViewController.headerView.maximumHeight = 128\r\n```\r\n\r\nYou can test these repro cases in the AppBarManualTabsExample, found under \"App Bar\", \"Manual tabs\".\r\n\r\nAll of these test cases assume that you've viewed each of the tabs at least once already (see \"Open issues\" below for more details).\r\n\r\n### Case 1\r\n\r\n1. Scroll tab 1 down until row 20 is just below the header.\r\n2. Change to tab 2.\r\n\r\nThe header should remain collapsed. Row 0 should be just below the header.\r\n\r\n### Case 2\r\n\r\n1. Scroll tab 1 down until row 20 is just below the header.\r\n2. Change to tab 2.\r\n3. Scroll up until the header is fully expanded.\r\n3. Change to tab 1.\r\n\r\nThe header should be fully expanded. Row 20 should still be just below the header.\r\n\r\n### Case 3\r\n\r\n1. Scroll tab 1 down until row 20 is just below the header.\r\n2. Change to tab 2.\r\n3. Scroll up until the header is partially expanded.\r\n3. Change to tab 1.\r\n\r\nThe header should be partially expanded. Row 20 should still be just below the header.\r\n\r\n## Additional details\r\n\r\nPrior to this change, the header's height would often animate between different heights when changing tabs. We had some rudimentary logic that attempted to adjust the target tab's content offset before moving, but this only handled a few cases and was also too aggressive in some cases (e.g. we'd make the destination tab's content offset match the source tab's content offset, disregarding the user's position in the destination content).\r\n\r\nAfter this change, we stash the header height for a given tab when we move away from it. When we're about to return to this tab we look at the header's current height and the stashed height. If these two values don't match, we offset the destination's tab's content offset with the delta. This keeps the destination's content \"pinned\" just below the header.\r\n\r\nWhen self.canAlwaysExpandToMaximumHeight is enabled, we also need to adjust the shift accumulator once we've changed tabs so that we're not increasing the height of the header too much. This logic follows similar reasoning as the content offset adjustment: use the height delta to adjust the shift accumulator. If the header is not floating in front of the content we also just want to blow away any shift accumulation entirely; this handles the case where tab 1 is fully expanded but floating and we move to tab 2 which is at the top of its content. In this case tab 2 doesn't need any shift accumulation.\r\n\r\n## Testing\r\n\r\nI will be pursuing a comprehensive approach to testing of the flexible header in https://github.com/material-components/material-components-ios/issues/5060, so this particular PR does not introduce any additional unit tests. I expect this added logic to be testable as part of a \"height\" unit of logic that encompasses everything related to height calculations for the flexible header.\r\n\r\n## Open issues\r\n\r\nhttps://github.com/material-components/material-components-ios/issues/5176\r\n\r\n## Closed issues\r\n\r\nCloses https://github.com/material-components/material-components-ios/issues/5156\r\nCloses https://github.com/material-components/material-components-ios/issues/4117\r\nCloses https://github.com/material-components/material-components-ios/issues/3130\r\n\r\n## Videos\r\n\r\n### Case 1\r\n\r\n| Before | After |\r\n|:-------|:------|\r\n| ![before](https://user-images.githubusercontent.com/45670/45771215-32778f00-bc4d-11e8-826e-681c8d1d73b7.gif) | ![after](https://user-images.githubusercontent.com/45670/45771221-399e9d00-bc4d-11e8-8bf0-9254d5123d5d.gif) |"},"comment":{"url":"https://api.github.com/repos/material-components/material-components-ios/issues/comments/422897685","html_url":"https://github.com/material-components/material-components-ios/pull/5178#issuecomment-422897685","issue_url":"https://api.github.com/repos/material-components/material-components-ios/issues/5178","id":422897685,"node_id":"MDEyOklzc3VlQ29tbWVudDQyMjg5NzY4NQ==","user":{"login":"material-automation","id":19738546,"node_id":"MDQ6VXNlcjE5NzM4NTQ2","avatar_url":"https://avatars2.githubusercontent.com/u/19738546?v=4","gravatar_id":"","url":"https://api.github.com/users/material-automation","html_url":"https://github.com/material-automation","followers_url":"https://api.github.com/users/material-automation/followers","following_url":"https://api.github.com/users/material-automation/following{/other_user}","gists_url":"https://api.github.com/users/material-automation/gists{/gist_id}","starred_url":"https://api.github.com/users/material-automation/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/material-automation/subscriptions","organizations_url":"https://api.github.com/users/material-automation/orgs","repos_url":"https://api.github.com/users/material-automation/repos","events_url":"https://api.github.com/users/material-automation/events{/privacy}","received_events_url":"https://api.github.com/users/material-automation/received_events","type":"User","site_admin":false},"created_at":"2018-09-19T17:51:31Z","updated_at":"2018-09-19T17:51:31Z","author_association":"NONE","body":"clang-format recommended changes.\n\nConsider installing [git-clang-format](https://github.com/llvm-mirror/clang/blob/master/tools/clang-format/git-clang-format) and running the following command:\n\n```bash\ngit clang-format $(git merge-base origin/develop HEAD)\n```\n\n<!-- identifier: clang-format -->"}}
{"actor":{"display_login":"material-automation"}}