2

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.

Edward Brey
  • 40,302
  • 20
  • 199
  • 253

1 Answers1

6

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.

Edward Brey
  • 40,302
  • 20
  • 199
  • 253
  • thanks for this solution Edward. any idea how I might stop the side menu (the Xamarin Master detail Side menu that opens when sliding from the left to the right) from automatically opening when swiping back to Tabbed page at position 0? – Jurgen Cuschieri Sep 20 '18 at 07:17
  • OK sorted this out - perhaps it will be useful for anyone else having the same issue. I disabled gestures in the OnAppearing() ovverride of Tabbed page at position 1: (App.Current.MainPage as RootPage).IsGestureEnabled = false; , and re enable it in the ovverride of Tabbed page at position 0: (App.Current.MainPage as RootPage).IsGestureEnabled = true; – Jurgen Cuschieri Sep 20 '18 at 07:35