The preface

After ios7, apple introduced the gesture swipe back feature, which swipes from the left side of the screen to the right to return to the previous screen. It greatly improves the operating experience of APP on large-screen mobile phones and ipads, and makes scene switching more smooth. Problems you may encounter when you swipe right to return to gesture configuration:

1. Why does the right swipe back gesture fail?

2. How to open the right swipe back gesture globally and how to avoid page jam?

3. How to open the page again after the right swipe gesture is disabled?

4. How to solve the conflict between right swipe back gesture and scroll view gesture?

5. What do I do when I swipe back in full screen?

Problem analysis

Why does the right swipe back gesture fail?

Slide back right gestures failure is mainly due to the custom page navigationItem leftBarButtonItem or leftBarButtonItems, or self. NavigationItem. HidesBackButton = YES; Hide the back button, or the self. The navigationItem. LeftItemsSupplementBackButton = NO; “, let’s sort this out. UINavigationItem (Apple documentation) is a common class, but many developers know little about it, Pay attention to explain backBarButtonItem, leftBarButtonItem, rightBarButtonItem and leftItemsSupplementBackButton four properties. LeftBarButtonItem and rightBarButtonItem are set on the current page and displayed on the navigationItem of the current page. BackBarButtonItem, if set on the current page, is displayed on the secondary page navigationItem.

Such as in AViewController push BViewController, set up A self. In A navigationItem. BackBarButtonItem title and image, after A study found that, This backBarButtonItem for BViewController self. NavigationController. NavigationBar. BackItem. BackBarButtonItem. Although the self. The navigationController. NavigationBar. BackItem. BackBarButtonItem is read/write properties, But the self navigationController, self. NavigationController. NavigationBar, self. NavigationController. NavigationBar. BackItem, Are readonly properties, so backBarButtonItem can only be defined in AViewController and set before Push: BViewController. LeftBarButtonItem and rightBarButtonItem can be set after ViewDidLoad of BViewController.

Note: backBarButtonItem can only customize image and title. It cannot override target or action. Other related Settings are ignored. If you just need to rewrite the action to do something else, you need to define a leftBarButtonItem. By default, leftBarButtonItem has a higher priority than backBarButtonItem. When leftBarButtonItem exists, backBarButtonItem is automatically ignored. Achieves the purpose of overwriting the backBarButtonItem, but causes the response proxy to swipe right to return the gesture to be overwritten from the current page. At the same time, the system also provides leftItemsSupplementBackButton property to control whether backBarButtonItem leftBarButtonItem “cover”, the default value is NO, if leftBarButtonItem configuration, Also need to have a back button and right slide gestures, need after leftBarButtonItem or leftBarButtonItems leftItemsSupplementBackButton, set to YES.

Disable the right swipe gesture for a particular page?

For example, in certain page scenarios such as left and right paging browsing, watching videos, watching audio and paying, users are “not expected” to leave easily, or there is a need for pop-up prompts, and there is also a consideration to avoid user misoperation. At the same time, there may be a right swipe back gesture conflict, or the audio focus may not be released in time after the right swipe back. So how do we do that? We can set the code to disable the right swipe back gesture, or load the page presentViewController instead.

Solution to restore the right swipe gesture

Scheme 1: Gesture proxy replacement

The system has the return arrow and the return button of the superior page title, we do not need to set, the system automatically generated, the default tintColor is blue. However, this style is not what we want. What we usually do is we go and set the leftBarButtonItem or leftBarButtonItems on that page to customize the style of the return button. From the above analysis, we can know that, LeftBarButtonItem or leftBarButtonItems directly covered with the self. The navigationController. NavigationBar. BackItem. BackBarButtonItem, The response proxy that caused the right swipe back gesture is removed from the current page overlay, invalidating the right swipe back gesture. We can pass on the last page setup self. The navigationItem. BackBarButtonItem, and next page setup self. The navigationItem. LeftItemsSupplementBackButton = YES. A project without base class management might be full of custom LeftBarButtonItems or LeftBarButtonItems, which is a lot of work. Get in the car and let the old driver give you a ride!

Keep the system’s swipe right back gesture

Since setting the backBarButtonItem is complicated, we can change our thinking. The gesture has been removed from the overlay, so we need to add the right swipe back gesture to the page. If the project has a global UINavigationController base class, implement the following reference code:

@implementation YGNavigationController - (void)viewDidLoad { [super viewDidLoad]; Weakself = self; weakSelf = self; weakSelf = self;if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.interactivePopGestureRecognizer.delegate = (id)weakself;
    }
}

#pragma mark - UIGestureRecognizerDelegate// This method is called before the gesture is activated: Returns YES allows the activation of right hand gestures, returns NO does not allow the activation of right hand gestures - (BOOL) gestureRecognizerShouldBegin: (gestureRecognizer UIGestureRecognizer *) {if(gestureRecognizer = = self. InteractivePopGestureRecognizer) {/ / block call rootViewController sliding back gestures, avoid sliding back right gestures caused crash problemif (self.viewControllers.count < 2 ||
 self.visibleViewController == [self.viewControllers objectAtIndex:0]) {
            returnNO; }} // this is a non-right swipe gesture to call the method, unified allow activationreturn YES;
}
Copy the code

Replace the UINavigationController in the project with the UINavigationController base class, the custom back button Settings unchanged, restore the right swipe back gesture. Note: The left side of the navigation bar also supports the swipe right return gesture. If you have a UIViewController base class, you can also adjust the Settings by referring to the above Settings code to eliminate the small area on the left side of the navigation bar.

Must realize UIGestureRecognizerDelegate rootViewController judgments, and do otherwise, smooth return to crash right at the rootViewController page will exist problems.

Disable the right swipe gesture for a specific page

If we look at the UINavigationController documentation, we can find it

@ property (nullable, nonatomic.readonly) UIGestureRecognizer *interactivePopGestureRecognizer NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED;
Copy the code

Page can be set by VC. The navigationController. InteractivePopGestureRecognizer. Enabled to control the right of the current page returned signal is available. We can create a class of UIViewController and create two class methods.

+ (void)popGestureClose:(UIViewController *)VC {// disable the swipe return gestureif([VC navigationController respondsToSelector: @ the selector (interactivePopGestureRecognizer)]) {/ / to add to right here all the gestures on the slide view is disabledfor (UIGestureRecognizer *popGesture inVC.navigationController.interactivePopGestureRecognizer.view.gestureRecognizers) { popGesture.enabled = NO; } / / open full-screen slide right, if can't use the following methods, please to deal with array / / VC navigationController. InteractivePopGestureRecognizer. Enabled = NO; }} + (void)popGestureOpen:(UIViewController *)VC {// enable slide return gestureif([VC navigationController respondsToSelector: @ the selector (interactivePopGestureRecognizer)]) {/ / here to add to all the gestures on the right viewfor (UIGestureRecognizer *popGesture inVC.navigationController.interactivePopGestureRecognizer.view.gestureRecognizers) { popGesture.enabled = YES; } / / open full-screen slide right, if can't use the following methods, please to deal with array / / VC navigationController. InteractivePopGestureRecognizer. Enabled = YES; }}Copy the code

How do you use it? We need to implement the following two methods in the page where the right swipe return gesture is disabled. After many debugging verification, the following two methods must be implemented. After the current page is disabled, it does not affect the right slide of the upper-level and lower-level pages.

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [UIViewController popGestureClose:self];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [UIViewController popGestureOpen:self];
}

Copy the code

Scheme 2 original ecology: customize backBarButtonItem

Most ideas on the Internet are based on scheme 1, which is a scheme reached by me in the research of Scheme 1. It makes direct use of backBarButtonItem and gesture feature of swipe right return of the system, which is relatively more stable and efficient. I think the design of swipe right return of iOS system APP should be the “official idea”.

Keep the system’s swipe right back gesture

You need to set your backBarButtonItem for each page, just as you set the leftBarButtonItem for each page. BackBarButtonItem, however, is a special button that responds only to the return and destruction of the page, meaning that you can only customize image and title. You can’t override target or action. Let’s customize the following backBarButtonItem. With reference to problem analysis, the following reference code must be implemented in AViewController:

UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil]; // Customize the view of the return button, such as the zoom return icon. [self.navigationController.navigationBar setBackIndicatorImage:[UIImage imageNamed:@"navi_back_icon"]]; [self.navigationController.navigationBar setBackIndicatorTransitionMaskImage:[UIImage imageNamed:@"navi_back_icon"]]; / / set tintColor change set the picture color self. The navigationController. NavigationBar. TintColor = [UIColor whiteColor]; / / set custom back button self. The navigationItem. BackBarButtonItem = backItem;Copy the code

According to the above creation idea, the page has been customized back button, and retains the right swipe back gesture (note: the left side of the navigation bar does not only support the right swipe back gesture, which is a little different from plan 1). In AViewController push BViewController or CViewController you don’t need to redefine leftBarButtonItem anymore to implement the return button. Of course, the above code can be packaged into a UIViewController base class and set uniformly in ViewDidLoad method, or packaged into a tool method to call uniformly. When a new page page requires a different return style, just recreate the backBarButtonItem overlay before pushing the page CViewController. * * note: ** Because the layout style of the left picture and right title used by UIButton encapsulated in backBarButtonItem of the system is different from the layout style of the title in the above picture of UIButton, even if the title is empty, the position of the icon of the return button is still left. We can come through a UIBarButtonItem UIBarButtonSystemItemFixedSpace or set placeholder icon position title gestures response area.

Disable the right swipe gesture or add a new button to the left of a specific page

So how do we do that? Custom leftBarButtonItem or leftBarButtonItems and set leftItemsSupplementBackButton = YES. Reference code:

/ / custom UIButton return button * studySearch = [UIButton buttonWithType: UIButtonTypeCustom]; [studySearchsetImage:[UIImage imageNamed:@"study_search"] forState:UIControlStateNormal];
     [studySearch sizeToFit];
     [studySearch addTarget:self action:@selector(studySearchAction) forControlEvents:UIControlEventTouchUpInside]; UIBarButtonItem *studySearchItem = [[UIBarButtonItem alloc] initWithCustomView:studySearch]; self.navigationItem.leftBarButtonItems = @[studySearchItem]; // Whether to display the left slider back button, NO does not display: leftBarButtonItems overwrites backBarButtonItem, //YES displays: BackBarButtonItem displayed in leftBarButtonItems on the left side of the self. The navigationItem. LeftItemsSupplementBackButton = YES;Copy the code

LeftItemsSupplementBackButton must be effective only after a custom leftBarButtonItem or leftBarButtonItems.

Plan three completely customize the navigation bar

In some projects, the navigation bar or navigation controller is completely customized. The specific implementation can be carried out by referring to scheme 1, which will not be further explored here.

Swiping right back causes gesture conflicts

Plan 2 does not have the jam phenomenon in Plan 1. In iOS, the swipe back gesture is actually a UIPanGestureRecognizer, and the swipe gesture for UIScrollView is also a UIPanGestureRecognizer, UIPanGestureRecognizer receives in the same order as THE UIView hierarchy.

UINavigationController. View - > UIViewController. View - > UIScrollView - > Screen and the User's finger
Copy the code

How it works: panGestureRecognizer of UIScrollView (including subclasses UITextView, UITableView, and UICollectionView) receives the gesture event first and processes it directly without passing it down. In fact, this is the problem of the coexistence of two Pangesturerecognizers. The pan gesture of scrollView will inactivate the pan gesture of the system. When the UIScrollView (UICollectionView) has multiple pages, it will also fail to slide back. We need to enable both gestures at the same time when the position of scrollView is in the initial position. You can create a category category for UIScrollView and then implement the following methods in that category:

#import "UIScrollView+PopGesture.h"@implementation UIScrollView (PopGesture) // When this method returns YES, the gesture event is passed down regardless of whether the current level is responding to the event. - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {if ([self panBack:gestureRecognizer]) {
        return YES;
    }
    returnNO; } //location_X is self-definable, - (BOOL)panBack:(UIGestureRecognizer *)gestureRecognizer {// int location_X = 40;if (gestureRecognizer == self.panGestureRecognizer) {
        UIPanGestureRecognizer *pan = (UIPanGestureRecognizer *)gestureRecognizer;
        CGPoint point = [pan translationInView:self];
        UIGestureRecognizerState state = gestureRecognizer.state;
        if(UIGestureRecognizerStateBegan == state || UIGestureRecognizerStatePossible == state) { CGPoint location = [gestureRecognizer locationInView:self]; // The slide return is only allowed on the first slideif (point.x > 0 && location.x < location_X && self.contentOffset.x <= 0) {
                returnYES; } // int temp1 = location.x; // int temp2 = SCREEN_WIDTH; // NSInteger XX = temp1 % temp2; //if (point.x > 0 && XX < location_X) {
         //      returnYES; / /}}}return NO;
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if ([self panBack:gestureRecognizer]) {
        return NO;
    }
    return YES;
}

@end
Copy the code

Right – swipe back to full screen Settings

As the screen of the mobile phone becomes larger, the original right swipe back slightly less humanized, especially the opponent of small friends, how can happily play the mobile phone with one hand. For the APP to full screen right swipe or keep the original edge trigger, there are different arguments, here is not to discuss whether it is good or bad, according to the needs of the product. We in one way, on the basis of creating a screen gestures, added to the original self. InteractivePopGestureRecognizer. View slide back right gestures on the view of Is about gestures are added to the VC. NavigationController. InteractivePopGestureRecognizer. The gestureRecognizers array, add gestures must be finished before setting agent.

- (void)viewDidLoad { [super viewDidLoad]; // Set the full screen start right swipe back gesture, which can be optimized to support full screen on iPadif ((UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)) {
        id target = self.interactivePopGestureRecognizer.delegate;
        SEL handler = NSSelectorFromString(@"handleNavigationTransition:"); / / get added system View at the edge of the trigger gesture UIView * targetView = self. InteractivePopGestureRecognizer. View; UIPanGestureRecognizer *fullScreenGes = [[UIPanGestureRecognizer alloc]initWithTarget:target action:handler]; fullScreenGes.delegate = self; [targetView addGestureRecognizer:fullScreenGes]; / / close the edge trigger gesture Don't prevent conflict and the original edge gesture (can also be closed) [self. InteractivePopGestureRecognizersetEnabled:NO]; } // Set the right slider to return the gesture proxy as its own __weak Typeof (self) weakSelf = self;if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.interactivePopGestureRecognizer.delegate = (id)weakself;
    }
}
Copy the code

Note: System in the self. InteractivePopGestureRecognizer. The view has been added with VC. The navigationController. InteractivePopGestureRecognizer gestures, Can also be in VC. NavigationController. InteractivePopGestureRecognizer. The gestureRecognizers array, at this point in the array, there are two response gesture. Therefore, the gesture control in scheme 1 should be handled in the form of array.

for (UIGestureRecognizer *popGesture in VC.navigationController.interactivePopGestureRecognizer.view.gestureRecognizers) {
            popGesture.enabled = NO;
        }
Copy the code

conclusion

IOS development is based on the development of the Apple system, when setting system-level global functions, it is best to choose the system or customize on the basis of the system, as little as possible self-righteous complete customization, less weird design, good content is the core of a product, good product experience is also the adhesive of user retention!

The original