Skip to content
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

fix: bad initial frame position #1463

Merged
merged 7 commits into from
Jun 1, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: apply logic from paper on Fabric
  • Loading branch information
WoLewicki authored and kkafar committed Jun 1, 2022
commit d4402c900f9e9b1d803c5f8fb8a639e874796de0
70 changes: 54 additions & 16 deletions ios/RNSScreen.mm
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ @interface RNSScreenView ()
@implementation RNSScreenView {
__weak RCTBridge *_bridge;
#ifdef RN_FABRIC_ENABLED
// we recreate the behavior of `reactSetFrame` on new architecture
facebook::react::LayoutMetrics _oldLayoutMetrics;
facebook::react::LayoutMetrics _newLayoutMetrics;
RCTSurfaceTouchHandler *_touchHandler;
facebook::react::RNSScreenShadowNode::ConcreteState::Shared _state;
#else
Expand Down Expand Up @@ -288,6 +291,7 @@ - (void)notifyWillAppear
std::dynamic_pointer_cast<const facebook::react::RNSScreenEventEmitter>(_eventEmitter)
->onWillAppear(facebook::react::RNSScreenEventEmitter::OnWillAppear{});
}
[self updateLayoutMetrics:_layoutMetrics oldLayoutMetrics:_oldLayoutMetrics];
#else
if (self.onWillAppear) {
self.onWillAppear(nil);
Expand Down Expand Up @@ -444,6 +448,11 @@ - (void)prepareForRecycle
// TODO: Make sure that there is no edge case when this should be uncommented
// _controller=nil;
_dismissed = NO;
// we don't reset the transform of view at the end of custom transitions,
// so in order not to add code to all transitions, we just reset it here.
// Maybe it would be better to do it in each animation since we could change other
// view props there too and they need to be reset due to view recycling.
self.transform = CGAffineTransformIdentity;
_state.reset();
}

Expand Down Expand Up @@ -520,6 +529,24 @@ - (void)updateState:(facebook::react::State::Shared const &)state
_state = std::static_pointer_cast<const facebook::react::RNSScreenShadowNode::ConcreteState>(state);
}

- (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics
oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics
{
_layoutMetrics = layoutMetrics;
_oldLayoutMetrics = oldLayoutMetrics;
UIViewController *parentVC = self.reactViewController.parentViewController;
if (parentVC != nil && ![parentVC isKindOfClass:[RNScreensNavigationController class]]) {
[super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
}
// when screen is mounted under RNScreensNavigationController it's size is controller
// by the navigation controller itself. That is, it is set to fill space of
// the controller. In that case we ignore react layout system from managing
// the screen dimensions and we wait for the screen VC to update and then we
// pass the dimensions to ui view manager to take into account when laying out
// subviews
// Explanation taken from `reactSetFrame`, which is old arch equivalent of this code.
}

#pragma mark - Paper specific
#else

Expand Down Expand Up @@ -614,9 +641,7 @@ - (void)invalidate
@implementation RNSScreen {
__weak id _previousFirstResponder;
CGRect _lastViewFrame;
#ifdef RN_FABRIC_ENABLED
RNSScreenView *_initialView;
#else
UIView *_fakeView;
CADisplayLink *_animationTimer;
CGFloat _currentAlpha;
Expand All @@ -625,7 +650,6 @@ @implementation RNSScreen {
int _dismissCount;
BOOL _isSwiping;
BOOL _shouldNotify;
#endif
}

#pragma mark - Common
Expand All @@ -648,12 +672,12 @@ - (instancetype)initWithView:(UIView *)view
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!_isSwiping) {
#ifdef RN_FABRIC_ENABLED
[RNSScreenWindowTraits updateWindowTraits];
[_initialView notifyWillAppear];
#else
if (!_isSwiping) {
[((RNSScreenView *)self.view) notifyWillAppear];
#endif
if (self.transitionCoordinator.isInteractive) {
// we started dismissing with swipe gesture
_isSwiping = YES;
Expand All @@ -663,26 +687,28 @@ - (void)viewWillAppear:(BOOL)animated
// The _isSwiping is still true, but we don't want to notify then
_shouldNotify = NO;
}

#ifdef RN_FABRIC_ENABLED
#else
[self hideHeaderIfNecessary];

#endif
// as per documentation of these methods
_goingForward = [self isBeingPresented] || [self isMovingToParentViewController];

[RNSScreenWindowTraits updateWindowTraits];
if (_shouldNotify) {
_closing = NO;
#ifdef RN_FABRIC_ENABLED
#else
[self notifyTransitionProgress:0.0 closing:_closing goingForward:_goingForward];
[self setupProgressNotification];
}
#endif
}
}

- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
#ifdef RN_FABRIC_ENABLED
[_initialView notifyWillDisappear];
#else
if (!self.transitionCoordinator.isInteractive) {
// user might have long pressed ios 14 back button item,
Expand All @@ -696,10 +722,15 @@ - (void)viewWillDisappear:(BOOL)animated
} else {
_dismissCount = 1;
}
#endif

// same flow as in viewWillAppear
if (!_isSwiping) {
#ifdef RN_FABRIC_ENABLED
[_initialView notifyWillDisappear];
#else
[((RNSScreenView *)self.view) notifyWillDisappear];
#endif
if (self.transitionCoordinator.isInteractive) {
_isSwiping = YES;
}
Expand All @@ -712,35 +743,37 @@ - (void)viewWillDisappear:(BOOL)animated

if (_shouldNotify) {
_closing = YES;
#ifdef RN_FABRIC_ENABLED
#else
[self notifyTransitionProgress:0.0 closing:_closing goingForward:_goingForward];
[self setupProgressNotification];
}
#endif
}
}

- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (!_isSwiping || _shouldNotify) {

#ifdef RN_FABRIC_ENABLED
[_initialView notifyAppear];
[_initialView notifyAppear];
#else
if (!_isSwiping || _shouldNotify) {
// we are going forward or dismissing without swipe
// or successfully swiped back
[((RNSScreenView *)self.view) notifyAppear];
[self notifyTransitionProgress:1.0 closing:NO goingForward:_goingForward];
#endif
}

_isSwiping = NO;
_shouldNotify = YES;
#endif
}

- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
#ifdef RN_FABRIC_ENABLED
[_initialView notifyDisappear];
if (self.parentViewController == nil && self.presentingViewController == nil) {
// screen dismissed, send event
[_initialView notifyDismissedWithCount:1];
Expand All @@ -757,16 +790,21 @@ - (void)viewDidDisappear:(BOOL)animated
[((RNSScreenView *)self.view) notifyDismissedWithCount:_dismissCount];
}
}

#endif
// same flow as in viewDidAppear
if (!_isSwiping || _shouldNotify) {
#ifdef RN_FABRIC_ENABLED
[_initialView notifyDisappear];
#else
[((RNSScreenView *)self.view) notifyDisappear];
[self notifyTransitionProgress:1.0 closing:YES goingForward:_goingForward];
#endif
}

_isSwiping = NO;
_shouldNotify = YES;

#ifdef RN_FABRIC_ENABLED
#else
[self traverseForScrollView:self.view];
#endif
}
Expand Down
92 changes: 48 additions & 44 deletions ios/RNSScreenStack.mm
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,32 @@ - (void)handleSwipe:(UIPanGestureRecognizer *)gestureRecognizer
}
}
}

- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:
(id<UIViewControllerAnimatedTransitioning>)animationController
{
return _interactionController;
}

- (id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:
(id<UIViewControllerAnimatedTransitioning>)animator
{
return _interactionController;
}

- (void)navigationController:(UINavigationController *)navigationController
didShowViewController:(UIViewController *)viewController
animated:(BOOL)animated
{
#ifdef RN_FABRIC_ENABLED
#else
if (self.onFinishTransitioning) {
self.onFinishTransitioning(nil);
}
#endif
[RNSScreenWindowTraits updateWindowTraits];
}
#endif

- (void)markChildUpdated
Expand Down Expand Up @@ -806,17 +832,31 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
return [self isScrollViewPanGestureRecognizer:otherGestureRecognizer];
}

- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:
(id<UIViewControllerAnimatedTransitioning>)animationController
- (void)insertReactSubview:(RNSScreenView *)subview atIndex:(NSInteger)atIndex
{
return _interactionController;
if (![subview isKindOfClass:[RNSScreenView class]]) {
RCTLogError(@"ScreenStack only accepts children of type Screen");
return;
}
subview.reactSuperview = self;
[_reactSubviews insertObject:subview atIndex:atIndex];
}

- (id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:
(id<UIViewControllerAnimatedTransitioning>)animator
- (void)removeReactSubview:(RNSScreenView *)subview
{
return _interactionController;
subview.reactSuperview = nil;
[_reactSubviews removeObject:subview];
}

- (void)didUpdateReactSubviews
{
// we need to wait until children have their layout set. At this point they don't have the layout
// set yet, however the layout call is already enqueued on ui thread. Enqueuing update call on the
// ui queue will guarantee that the update will run after layout.
dispatch_async(dispatch_get_main_queue(), ^{
self->_hasLayout = YES;
[self maybeAddToParentAndUpdateContainer];
});
}

#ifdef RN_FABRIC_ENABLED
Expand Down Expand Up @@ -907,43 +947,6 @@ - (void)prepareForRecycle
#else
#pragma mark - Paper specific

- (void)navigationController:(UINavigationController *)navigationController
didShowViewController:(UIViewController *)viewController
animated:(BOOL)animated
{
if (self.onFinishTransitioning) {
self.onFinishTransitioning(nil);
}
[RNSScreenWindowTraits updateWindowTraits];
}

- (void)insertReactSubview:(RNSScreenView *)subview atIndex:(NSInteger)atIndex
{
if (![subview isKindOfClass:[RNSScreenView class]]) {
RCTLogError(@"ScreenStack only accepts children of type Screen");
return;
}
subview.reactSuperview = self;
[_reactSubviews insertObject:subview atIndex:atIndex];
}

- (void)removeReactSubview:(RNSScreenView *)subview
{
subview.reactSuperview = nil;
[_reactSubviews removeObject:subview];
}

- (void)didUpdateReactSubviews
{
// we need to wait until children have their layout set. At this point they don't have the layout
// set yet, however the layout call is already enqueued on ui thread. Enqueuing update call on the
// ui queue will guarantee that the update will run after layout.
dispatch_async(dispatch_get_main_queue(), ^{
self->_hasLayout = YES;
[self maybeAddToParentAndUpdateContainer];
});
}

- (void)invalidate
{
_invalidated = YES;
Expand All @@ -954,6 +957,7 @@ - (void)invalidate
[_controller willMoveToParentViewController:nil];
[_controller removeFromParentViewController];
}

#endif // RN_FABRIC_ENABLED

@end
Expand Down