Forum Xamarin.iOS

Announcement:

The Xamarin Forums have officially moved to the new Microsoft Q&A experience. Microsoft Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

To create new threads and ask questions head over to Microsoft Q&A for .NET and get involved today.

PageRenderer which adds SwipeBack Functionality on iOS not working properly.

Hey everyone!
I got a problem with my custom page renderer which adds the SwipeBack Functionality on all Pages. I need this because the NavigationBar on the respective Pages is disabled but I want to have the SwipeBack Functionality. I found this solution in multiple sources but it is not working as expected.

[assembly: Xamarin.Forms.ExportRenderer(typeof(ContentPage), typeof(ContentPageRenderer))]
namespace MyApp
{
    public class ContentPageRenderer : PageRenderer
    {
        public ContentPage ContentPageElement => Element as ContentPage;

        public override void ViewWillAppear(bool animated)
        {
            base.ViewDidAppear(animated);

            if (ViewController.NavigationController != null)
            {
                ViewController.NavigationController.InteractivePopGestureRecognizer.Enabled = true;
                ViewController.NavigationController.InteractivePopGestureRecognizer.Delegate = new UIGestureRecognizerDelegate();
                ViewController.NavigationController.InteractivePopGestureRecognizer.AddTarget(this, new ObjCRuntime.Selector(nameof(HandleBackSwipe)));
            }
        }

        [Export(nameof(HandleBackSwipe))]
        private void HandleBackSwipe()
        {
            if (ViewController.NavigationController != null)
            {
                ViewController.NavigationController.InteractivePopGestureRecognizer.RemoveTarget(this, new ObjCRuntime.Selector(nameof(HandleBackSwipe)));
                ContentPageElement.SendBackButtonPressed();
            }
        }
    }

When swiping back from the page which is my second page in the navigation stack, I get to my root page as expected. But when I carry on swiping back on the root page (it makes no sense to swipe back at the root page, i know, however a user could do it for any reason), the app freezes, meaning i am not able to click any button on the root page.

Has anyone experienced the same issue with a renderer like mine? Thanks in advance!

Best Answer

  • nickisinthehousenickisinthehouse Member ✭✭
    Accepted Answer

    I just made it work by moving everything in the ViedDidAppear instead of ViewWillAppear.
    I also made disabling or enabling of the InteractivePopGestureRecognizer accessible via a field on a ContentPage wich tells me if SiwpeBack is allowed or not. I gave the root page IsSwipeBackAllowed = false. Here is the solution:

    [assembly: Xamarin.Forms.ExportRenderer(typeof(ContentPage), typeof(ContentPageRenderer))]
    namespace MyApp
    {
        public class ContentPageRenderer : PageRenderer
        {
            public ContentPage ContentPageElement => Element as ContentPage;
    
            public override void ViewDidAppear(bool animated)
            {
                base.ViewDidAppear(animated);
    
                if (ViewController.NavigationController != null && ContentPageElement.IsSwipeBackAllowed == false)
                {
                    ViewController.NavigationController.InteractivePopGestureRecognizer.Enabled = false;
                }
    
                if (ViewController.NavigationController != null && ContentPageElement.IsSwipeBackAllowed == true)
                {
                    ViewController.NavigationController.InteractivePopGestureRecognizer.Enabled = true;
                    ViewController.NavigationController.InteractivePopGestureRecognizer.Delegate = new UIGestureRecognizerDelegate();
                    ViewController.NavigationController.InteractivePopGestureRecognizer.AddTarget(this, new ObjCRuntime.Selector(nameof(HandleBackSwipe)));
                }
            }
    
            [Export(nameof(HandleBackSwipe))]
            private void HandleBackSwipe()
            {
                if (ViewController.NavigationController != null && ContentPageElement.IsSwipeBackAllowed == true)
                {
                    ViewController.NavigationController.InteractivePopGestureRecognizer.RemoveTarget(this, new ObjCRuntime.Selector(nameof(HandleBackSwipe)));
                    ContentPageElement.SendBackButtonPressed();
                }
            }
        }
    }
    

    Thanks for any help!

Answers

  • ColeXColeX Member, Xamarin Team Xamurai

    Add a condition to detect if current page is the root viewController in the navigation stack .

    Try the following code

     private void HandleBackSwipe()
            {
                if (ViewController.NavigationController != null )
                {
                    ViewController.NavigationController.InteractivePopGestureRecognizer.RemoveTarget(this, new ObjCRuntime.Selector(nameof(HandleBackSwipe)));
    
                    if(ViewController.NavigationController.ViewControllers.Length > 1)  //it's not root viewController 
                        ContentPageElement.SendBackButtonPressed();
                }
            }
    

    Xamarin forums are migrating to a new home on Microsoft Q&A!
    We invite you to post new questions in the Xamarin forums’ new home on Microsoft Q&A!
    For more information, please refer to this sticky post.

  • Thank you, I tried your solution but unfortunately this does not work...

    In more detail: after I swiped back on the root page, I am able to click on a button which should navigate me to another page, but nothing happens. I did debug my code and it seems like, I get stuck on the await Navigation.PushAsync(nextPage) call. The program never returns from the call. When I swipe back again, meaning swiping from the left edge of the screen to the center of the screen, I see the page (second page, not root page) coming from the right side moving inwards. I think this indicates that the page was there but not shown or something like that. Afterwards, I can click on any button and the application behaves as expected. The only thing is that my navigation stack is messed up meaning the "second page" is there twice. Another guy uploaded a GIF where you can see exactely the same problem. I would be very thankful if someone finds a solution!

  • ColeXColeX Member, Xamarin Team Xamurai
    edited February 18

    Replace the renderer as below

    [assembly: Xamarin.Forms.ExportRenderer(typeof(ContentPage), typeof(ContentPageRenderer))]
    namespace FormsA.iOS
    {
        public class ContentPageRenderer : PageRenderer
        {
            public override void ViewWillAppear(bool animated)
            {
                base.ViewWillAppear(animated);
    
                if(ViewController.NavigationController != null && ViewController.NavigationController.ViewControllers.Length > 1)
                {
                    var swipe = new UISwipeGestureRecognizer(() => {
                        this.NavigationController.PopViewController(true);
                    });
                    swipe.Direction = UISwipeGestureRecognizerDirection.Right;
                    this.View.AddGestureRecognizer(swipe);
                }
            }
        }
    }
    
  • Yes this one is working!!! However the Swipe Gesture isn't like the one before. Do you also have a clue how to combine your solution with the InteractivePopGesureRecognizer in order to have a smooth swipe back functionality? Thank you very much in advance!!!

  • ColeXColeX Member, Xamarin Team Xamurai

    Try to disable the gesture when reaching root page .

    [assembly: Xamarin.Forms.ExportRenderer(typeof(ContentPage), typeof(ContentPageRenderer))]
    namespace MyApp
    {
        public class ContentPageRenderer : PageRenderer
        {
            public ContentPage ContentPageElement => Element as ContentPage;
    
            public override void ViewWillAppear(bool animated)
            {
                base.ViewDidAppear(animated);
    
                ViewController.NavigationController.InteractivePopGestureRecognizer.Enabled = false;
                if (ViewController.NavigationController != null && ViewController.NavigationController.ViewControllers.Length > 1) 
                {
                    ViewController.NavigationController.InteractivePopGestureRecognizer.Enabled = true;
                    ViewController.NavigationController.InteractivePopGestureRecognizer.Delegate = new UIGestureRecognizerDelegate();
                    ViewController.NavigationController.InteractivePopGestureRecognizer.AddTarget(this, new ObjCRuntime.Selector(nameof(HandleBackSwipe)));
                }
            }
    
    
            [Export(nameof(HandleBackSwipe))]
            private void HandleBackSwipe()
            {
                if (ViewController.NavigationController != null)
                {
                    ViewController.NavigationController.InteractivePopGestureRecognizer.RemoveTarget(this, new ObjCRuntime.Selector(nameof(HandleBackSwipe)));
                    ContentPageElement.SendBackButtonPressed();
                }
            }
        }
    }
    
  • It doesn't work... even if I check for != null. The gesture seems to be disabled before I navigated back to the root page what causes a total freez on the second page.

  • nickisinthehousenickisinthehouse Member ✭✭
    Accepted Answer

    I just made it work by moving everything in the ViedDidAppear instead of ViewWillAppear.
    I also made disabling or enabling of the InteractivePopGestureRecognizer accessible via a field on a ContentPage wich tells me if SiwpeBack is allowed or not. I gave the root page IsSwipeBackAllowed = false. Here is the solution:

    [assembly: Xamarin.Forms.ExportRenderer(typeof(ContentPage), typeof(ContentPageRenderer))]
    namespace MyApp
    {
        public class ContentPageRenderer : PageRenderer
        {
            public ContentPage ContentPageElement => Element as ContentPage;
    
            public override void ViewDidAppear(bool animated)
            {
                base.ViewDidAppear(animated);
    
                if (ViewController.NavigationController != null && ContentPageElement.IsSwipeBackAllowed == false)
                {
                    ViewController.NavigationController.InteractivePopGestureRecognizer.Enabled = false;
                }
    
                if (ViewController.NavigationController != null && ContentPageElement.IsSwipeBackAllowed == true)
                {
                    ViewController.NavigationController.InteractivePopGestureRecognizer.Enabled = true;
                    ViewController.NavigationController.InteractivePopGestureRecognizer.Delegate = new UIGestureRecognizerDelegate();
                    ViewController.NavigationController.InteractivePopGestureRecognizer.AddTarget(this, new ObjCRuntime.Selector(nameof(HandleBackSwipe)));
                }
            }
    
            [Export(nameof(HandleBackSwipe))]
            private void HandleBackSwipe()
            {
                if (ViewController.NavigationController != null && ContentPageElement.IsSwipeBackAllowed == true)
                {
                    ViewController.NavigationController.InteractivePopGestureRecognizer.RemoveTarget(this, new ObjCRuntime.Selector(nameof(HandleBackSwipe)));
                    ContentPageElement.SendBackButtonPressed();
                }
            }
        }
    }
    

    Thanks for any help!

Sign In or Register to comment.