-
Notifications
You must be signed in to change notification settings - Fork 24.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
UIView
properties are not reset to default values when recycled
#42732
Comments
|
I think you can override |
The problem here is that it's not a custom component inheriting after it but |
Fabric already supports custom view recycled optional in #35378, but it's only in main branch and not released, after that released, seems we can define a category of I can make a PR to add an interface for Fabric built-in components to make recycling optional. |
Summary: Autoresizing mask is a native property of iOS views. When a veiw is created anew, the default value is `UIViewAutoresizingNone`. Libraries might create code that update this property and we need to make sure that it is reset when the view is recycled. This should close: facebook#42732 ## Changelog: [iOS][Changed] - Reset the autoresizing mask to `UIViewAutoresizingNone` when the view is recycled. Differential Revision: D54263847
I looked into the example a bit more, but your So, in - (void)prepareForRecycle
{
[super prepareForRecycle];
_autoSize = NO;
self.autoresizingMask = UIViewAutoresizingNone;
} Resetting the Looking at this comment can you provide a reproducer with |
@cipolleschi Here it is: https://github.com/j-piasecki/autoresizingmask-repro. Just add a breakpoint inside the |
@j-piasecki I'm making some tests with the code you provided. 1. Creating a simple UIPageViewControllerNotice that this is something defined in an extension in the library UIPageViewController* tst = [[UIPageViewController alloc] initWithView:[[UIView alloc] init]];` In this case, 2. Creating a Scroll, Horizontal UIPageViewControllerm with no optionsUIPageViewController* tst = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options:@{}]; In this case, 3. Creating a PageCurl, Horizontal UIPageViewControllerm with no optionsUIPageViewController* tst = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options:@{}]; In this case, 4. Creating a PageCurl, Vertical UIPageViewControllerm with no optionsUIPageViewController* tst = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationVertical
options:@{}]; In this case, 5. Creating a Scroll, Horizontal UIPageViewControllerm with no optionsUIPageViewController* tst = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
navigationOrientation:UIPageViewControllerNavigationOrientationVertical
options:@{}]; In this case, 6. Tested outside react nativeI created a new iOS native project, no react native involved and added: UIPageViewController* tst2 = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options:@{}];
UIPageViewController* tst3 = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
options:@{}];
UIPageViewController* tst4 = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
navigationOrientation:UIPageViewControllerNavigationOrientationVertical
options:@{}];
UIPageViewController* tst5 = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
navigationOrientation:UIPageViewControllerNavigationOrientationVertical
options:@{}];
NSLog(@"autoresizeMask for tst2 is %u", [tst2.view autoresizingMask]);
NSLog(@"autoresizeMask for tst3 is %u", [tst3.view autoresizingMask]);
NSLog(@"autoresizeMask for tst4 is %u", [tst4.view autoresizingMask]);
NSLog(@"autoresizeMask for tst5 is %u", [tst5.view autoresizingMask]); So, I think that the behavior is correct. Fabric is not involved in setting/resetting the Can you share more in which scenario you are encountering the issue and what is the actual behavior and what would be the expected behavior? |
@cipolleschi Sorry for the late reply, I've missed the notification.
I'm not sure what you mean by
It's a bit hard to prepare the exact reproduction, since we only observed it in the Expensify app while working on adapting it to the new architecture and it didn't seem to be deterministic. If this helps, this is what we were seeing: Those problems all were fixed by https://github.com/WoLewicki/App/blob/d6287ba33feb664d4c2aa131cb9d09b177f4bb18/patches/react-native%2B0.73.2%2B010%2BresetAutoresizingOnView.patch We also had to disable recycling for
We tried to get to the bottom of why exactly it's happening, but in the end disabling recycling was much quicker and effective approach, so I don't have any more details to share here 😞. |
I had a similar problem when React Native recycled ScrollViews. I fixed it by changing the frame size to zero and back again in prepareForRecycle. Can you try this patch instead? CGRect oldFrame = self.frame;
self.frame = CGRectZero;
self.frame = oldFrame; |
No, what I was trying to say is that even a Native app (no React Native) which uses the UIPageViewController has the autoresizeMask set like that (see example 6, here). The root cause it is not the Fabric view recycling: it is how UIKit works! |
I get your point but on the other hand, UIKit doesn’t recycle views so it’s not a problem there, Fabric does recycle them. I’m not sure what would be the correct solution, but resetting |
But I don't understand the issue:
So, with vanilla iOS, every time the The only problem might occur when Fabric returns a view that has been created by |
I believe that's exactly the case. Sorry if I didn't make it clear. |
I'll talk with @sammy-SC about that. What I'm confused about is that Fabric should not recycle that view, because, technically, that view is not a |
I updated the repro linked above: j-piasecki/autoresizingmask-repro@9a554eb. Now it should be relatively easy to reproduce the issue: Screen.Recording.2024-04-12.at.10.44.19.mov |
Thanks for updating the reproducer! I was talking with @sammy-SC about this a couple of days ago. This is probably a breaking change between the old and new architecture and might require a change in the pager-view. I'll investigate it further next week, to see if that's the case. |
Closing this to clean up the issues. Let's refer to callstack/react-native-pager-view#819 as it is a library's change |
Description
Some properties of
UIView
are not reset when the views are recycled. The field that caused a problem in our case isautoresizingMask
. By default its set toUIViewAutoresizingNone
but if it gets changed to other values in any way, it stays this way on a view that gets recycled and put in different places of the application causing weird layout issues and it diverges from Yoga.The linked repro works by implementing a custom view that sets the mask to
UIViewAutoresizingNone
orUIViewAutoresizingWidth | UIViewAutoresizingHeight
depending on a prop value (Toggle auto W+H
button) on its children. Then, when the views are recycled (Toggle ReproView
button, which changes the hierarchy that gets rendered), theautoresizingMask
stays on the recycled view it was set on.I guess the big question here is: should the library be responsible for resetting the changed values since the problem doesn't occur when it's disabled?
If the answer is yes, how would the approach be when the OS changes the value? It's happening in case of
react-native-pager-view
here: https://github.com/callstack/react-native-pager-view/blob/7c30957352687906921795c8dd48ed18db9fdad0/ios/Fabric/RNCPagerViewComponentView.mm#L224.Steps to reproduce
Toggle auto W+H
, set it totrue
Toggle ReproView
, set it tofalse
Toggle wide
and/orToggle tall
React Native Version
0.73.3
Affected Platforms
Runtime - iOS
Areas
Fabric - The New Renderer
Output of
npx react-native info
Reproducer
https://github.com/j-piasecki/rn-view-recycling-repro
Screenshots and Videos
Screen.Recording.2024-01-30.at.11.49.10.mov
The text was updated successfully, but these errors were encountered: