diff --git a/ios/gamma/split-view/RNSSplitViewScreenComponentView.mm b/ios/gamma/split-view/RNSSplitViewScreenComponentView.mm index 33536a44a7..6f4455fee2 100644 --- a/ios/gamma/split-view/RNSSplitViewScreenComponentView.mm +++ b/ios/gamma/split-view/RNSSplitViewScreenComponentView.mm @@ -167,24 +167,6 @@ - (void)safeAreaInsetsDidChange return react::concreteComponentDescriptorProvider(); } -- (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics - oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics -{ - // We're tracking presentation layer updates in the RNSSplitViewScreen. - // There's a problem with SplitView that it sets the frame to the end value of the animation right after the animation - // begins. Because of that, the size of our component is desynchronizing easily and we're blocking a communication - // between native and shadow layout for a while until the transition ends. For the following case when we want to make - // a transition from width A to B: - // 1. size 'A' is set on ShadowNode - // 2. animation for the transition starts - // 3. `setFrame` is called with the width 'B' - // 4. in the same time, we want to track updates and treat intermediate value A' indicated from the presentation layer - // as our source of truth - if (![_controller isViewSizeTransitionInProgress]) { - [super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics]; - } -} - + (BOOL)shouldBeRecycled { // There won't be tens of instances of this component usually & it's easier for now. diff --git a/ios/gamma/split-view/RNSSplitViewScreenController.swift b/ios/gamma/split-view/RNSSplitViewScreenController.swift index 7e5f8c09eb..904c694d37 100644 --- a/ios/gamma/split-view/RNSSplitViewScreenController.swift +++ b/ios/gamma/split-view/RNSSplitViewScreenController.swift @@ -18,8 +18,6 @@ public class RNSSplitViewScreenController: UIViewController { return splitViewScreenComponentView.reactEventEmitter() } - private var viewSizeTransitionState: ViewSizeTransitionState? = nil - @objc public required init(splitViewScreenComponentView: RNSSplitViewScreenComponentView) { self.splitViewScreenComponentView = splitViewScreenComponentView super.init(nibName: nil, bundle: nil) @@ -62,18 +60,6 @@ public class RNSSplitViewScreenController: UIViewController { return self.splitViewController is RNSSplitViewHostController } - /// - /// @brief Determines whether an SplitView animated transition is currently running - /// - /// Used to differentiate favor frames from the presentation layer over view's frame . - /// - /// @return true if the transition is running, false otherwise. - /// - @objc - public func isViewSizeTransitionInProgress() -> Bool { - return viewSizeTransitionState != nil - } - // MARK: Signals @objc @@ -83,55 +69,6 @@ public class RNSSplitViewScreenController: UIViewController { // MARK: Layout - /// - /// @brief This method is overridden to extract the value to which we're transitioning - /// and attach the DisplayLink to track frame updates on the presentation layer. - /// - public override func viewWillTransition( - to size: CGSize, - with coordinator: any UIViewControllerTransitionCoordinator - ) { - super.viewWillTransition(to: size, with: coordinator) - - viewSizeTransitionState = ViewSizeTransitionState() - - coordinator.animate( - alongsideTransition: { [weak self] context in - guard let self = self else { return } - guard let viewSizeTransitionState = self.viewSizeTransitionState else { return } - - if viewSizeTransitionState.displayLink == nil { - viewSizeTransitionState.setupDisplayLink( - forTarget: self, selector: #selector(trackTransitionProgress)) - } - }, - completion: { [weak self] context in - guard let self = self else { return } - self.cleanupViewSizeTransitionState() - // After the animation completion, ensure that ShadowTree state - // is calculated relatively to the ancestor's frame by requesting - // the state update. - self.updateShadowTreeState() - }) - } - - private func cleanupViewSizeTransitionState() { - viewSizeTransitionState?.invalidate() - viewSizeTransitionState = nil - } - - /// - /// @brief This method is responsible for tracking animation frames and requests layout - /// which will synchronize ShadowNode size with the animation frame size. - /// - @objc - private func trackTransitionProgress() { - if let currentFrame = view.layer.presentation()?.frame { - viewSizeTransitionState?.lastViewPresentationFrame = currentFrame - updateShadowTreeState() - } - } - @objc public override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() @@ -146,8 +83,6 @@ public class RNSSplitViewScreenController: UIViewController { /// Differentiates cases when we're in the Host hierarchy to calculate frame relatively /// to the Host view from the modal case where we're passing absolute layout metrics to the ShadowNode. /// - /// Prefers to apply dynamic updates from the presentation layer if the transition is in progress. - /// private func updateShadowTreeState() { // For modals, which are presented outside the SplitViewHost subtree (and RN hierarchy), // we're attaching our touch handler and we don't need to apply any offset corrections, @@ -163,48 +98,19 @@ public class RNSSplitViewScreenController: UIViewController { "[RNScreens] Expected to find RNSSplitViewHost component for RNSSplitViewScreen component" ) - // If the resize animation is currently running, we prefer to apply dynamic updates, - // based on the results from the presentation layer - // which is read from `trackTransitionProgress` method. - if let lastViewPresentationFrame = viewSizeTransitionState?.lastViewPresentationFrame, - !lastViewPresentationFrame.isNull - { - shadowStateProxy.updateShadowState( - ofComponent: splitViewScreenComponentView, withFrame: lastViewPresentationFrame, - inContextOfAncestorView: ancestorView!) - return - } - - // There might be the case, when transition is about to start and in the meantime, - // sth else is triggering frame update relatively to the parent. As we know - // that dynamic updates from the presentation layer are coming, we're blocking this - // to prevent interrupting with the frames that are less important for us. - // This works fine, because after the animation completion, we're sending the last update - // which is compatible with the frame which would be calculated relatively to the ancestor here. - if !isViewSizeTransitionInProgress() { - shadowStateProxy.updateShadowState( - ofComponent: splitViewScreenComponentView, inContextOfAncestorView: ancestorView) - } + shadowStateProxy.updateShadowState( + ofComponent: splitViewScreenComponentView, inContextOfAncestorView: ancestorView) } /// /// @brief Request ShadowNode state update when the SplitView screen frame origin has changed. /// - /// If there's a transition in progress, this function is ignored as we prefer to apply updates - /// that are dynamically coming from the presentation layer, rather than reading the frame, because - /// view's frame is set to the target value at the begining of the transition. - /// /// @param splitViewController The UISplitViewController whose layout positioning changed, represented by RNSSplitViewHostController. /// func columnPositioningDidChangeIn(splitViewController: UISplitViewController) { - // During the transition, we're listening for the animation - // frame updates on the presentation layer and we're - // treating these updates as the source of truth - if !isViewSizeTransitionInProgress() { - shadowStateProxy.updateShadowState( - ofComponent: splitViewScreenComponentView, inContextOfAncestorView: splitViewController.view - ) - } + shadowStateProxy.updateShadowState( + ofComponent: splitViewScreenComponentView, inContextOfAncestorView: splitViewController.view + ) } // MARK: Events @@ -237,23 +143,3 @@ extension RNSSplitViewScreenController: RNSFrameCorrectionProvider { self.splitViewScreenComponentView.unregister(fromFrameCorrection: view) } } - -private class ViewSizeTransitionState { - public var displayLink: CADisplayLink? - public var lastViewPresentationFrame: CGRect = CGRect.null - - public func setupDisplayLink(forTarget target: Any, selector sel: Selector) { - if displayLink != nil { - displayLink?.invalidate() - } - - displayLink = CADisplayLink(target: target, selector: sel) - displayLink!.add(to: .main, forMode: .common) - } - - public func invalidate() { - displayLink?.invalidate() - displayLink = nil - lastViewPresentationFrame = CGRect.null - } -}