Edward Brey
Edward Brey

Reputation: 41638

Swipe gesture for animated TabbedPage transition in Xamarin.Forms on iOS

In Xamarin.Forms, on iOS, how can I extend TabbedPage so that in addition to changing pages using the tabs at the bottom, the user can swipe left or right to change pages? Ideally, the page transition should be animated when swiping.

None of the pages use left or right gestures within the pages themselves, so swiping left or right is available to unambiguously indicate a desire to change the current page.

Upvotes: 2

Views: 2437

Answers (1)

Edward Brey
Edward Brey

Reputation: 41638

The following code responds to a user swipe gesture with a tab change animated with a cross dissolve. It creates a custom renderer for TabbedPage that adds an iOS UISwipeGestureRecognizer for each direction.

The gesture recognizer works simultaneously with other gestures on the page (otherwise, page gestures such as scrolling will prevent the swipe gesture from working unless the gesture is perfectly horizontal), but not itself (otherwise, one swipe will move across multiple pages).

[assembly: ExportRenderer(typeof(TabbedPage), typeof(SwipeTabbedRenderer))]

class SwipeTabbedRenderer : TabbedRenderer
{
    public override void ViewWillAppear(bool animated)
    {
        base.ViewWillAppear(animated);

        NativeView.AddGestureRecognizer(new UISwipeGestureRecognizer(() => SelectNextTab(1)) { Direction = UISwipeGestureRecognizerDirection.Left, ShouldRecognizeSimultaneously = ShouldRecognizeSimultaneously });
        NativeView.AddGestureRecognizer(new UISwipeGestureRecognizer(() => SelectNextTab(-1)) { Direction = UISwipeGestureRecognizerDirection.Right, ShouldRecognizeSimultaneously = ShouldRecognizeSimultaneously });
    }

    void SelectNextTab(int direction)
    {
        int nextIndex = TabbedPage.GetIndex(Tabbed.CurrentPage) + direction;
        if (nextIndex < 0 || nextIndex >= Tabbed.Children.Count) return;
        var nextPage = Tabbed.Children[nextIndex];
        UIView.Transition(Platform.GetRenderer(Tabbed.CurrentPage).NativeView, Platform.GetRenderer(nextPage).NativeView, 0.15, UIViewAnimationOptions.TransitionCrossDissolve, null);
        Tabbed.CurrentPage = nextPage;
    }

    static bool ShouldRecognizeSimultaneously(UIGestureRecognizer gestureRecognizer, UIGestureRecognizer otherGestureRecognizer) => gestureRecognizer != otherGestureRecognizer;
}

Opportunity for improvement: A slide gesture would be better than the cross dissolve, but when I tried to use CATransition to create one, the initial state of the animation was a blank page, rather than the page for the starting tab. This caused the animation to feel slightly jarring.

Upvotes: 6

Related Questions