Custom navigation controller return button and full screen slide return function

The return button settings for the navigation controller and some details

  • The return button for the navigation controller can be set by the following two properties
1 / / backBarButtonItem source controller attribute to set the sourceVC.navigationItem.backBarButtonItem / / leftBarButtonItem 2 attribute to set the destinationVC.navigatinoItem.leftBarButtonItem target controller
  • According to who things who management principles, we generally use the leftBarButtonItem attribute to set can know that leftBarButtonItem is a UIBarButtonItem class instance. A method instance can create the UIBarButtonItem class Apple official to provide a total of the following methods can quickly create text only (initWithTitle), initWithImage (only display pictures), also can be set for any custom controls (initWithCustomView).
- (instancetype) init; (nullable instancetype) - initWithCoder: (NSCoder * aDecoder); - (instancetype) initWithImage: (nullable * UIImage) image style: (UIBarButtonItemStyle) style target: (nullable ID) target action: (nullable SEL) - (instancetype) action; initWithImage: (nullable * UIImage) image landscapeImagePhone: (nullable * UIImage) landscapeImagePhone style: (UIBarButtonItemStyle) style target: (nullable ID) target action: (nullable SEL) - (instancetype) action; initWithTitle: (nullable * NSString) Title style: (UIBarButtonItemStyle) style target: (nullable ID) target action: (nullable SEL) - (instancetype) action; initWithBarButtonSystemItem: (UIBarButtonSystemItem) systemItem target: (nullable ID) target action: (nullable SEL) - instan (action; Cetype (UIView *) customView (initWithCustomView:);
  • Requirements: the return button has its own color and status in the normal state and in the highlighted state
  • Solution: the UIButton object will generally be packaged into a UIBarButtonItem object to assign to leftBarButtonItem.
  • Question: if you set the UIButton directly to leftBarButtonItem, the system will push the button to return to expand, affecting the user experience
  • Methods: 1 improve the custom UIView, a package of UIButton inside, set the color of the button and the button: the details of the demand state can move a distance to the left, here we assume that 10 (because the clearance default settings for the back button position and navigation controller the leftmost bit) directly adjust the UIView frame is not possible, so here can be adjusted by UIButton frame or contentEdgeInsets to achieve the effect is to shift to the left. Here is the use of a modified UIButton method directly in the layoutSubviews frame values of X (specific code below) here is a small problem: when we set the button’s x value negative time (relative to the parent control button to the left of the mobile part of the parent control), is not beyond. Click on the problem analysis: use event delivery Knowledge, let us first to review the process of the transfer of the 1 event when a first event, UIApplication applications will be the first to receive this event, and the event is usually handed out 2 incident and transmitted to the main window, use the following three step hitTest: method then calls the view to find the view the appropriate window view hierarchy to handle the events of the 2.1 first determine whether the view can receive events (hidden = = NO & & userInteractionEnabled = YES & & alpha > 0.01 to meet the three conditions, that can receive events) after the 2.2 meet the above conditions, in determining the current point is in the the view above 2.3 if the above two conditions are satisfied, then from the forward traversal view.subViews array, in the judgment of the use of the above three conditions, until you find the most suitable V Iew until 3 when to find the most suitable after the view method is called the most appropriate view monitor events, such as touchesBegan (according to the different methods to call different methods.), if the view does not implement the touchesBegan method, or in the method call [super touchesBegan], then the event will follow the response who passed up the chain, in order to call on a response touchesBegan method. From the above analogy transfer events, we can see that when you click the back button (beyond the position when the parent control) the click event will be passed to the button of the parent control, call the hitTest: method to the parent control to determine whether button parent controls are appropriate View. first it will determine whether button parent control can handle event (can) and then determine the point is not in the parent control on button The surface, because the coordinates of this point is beyond the control of the father, so here the judgment is not set up, also will not be passed to the button event. Solution: when the event is passed to the Button of the parent control when we were in hitTest. The judge can solve specific code (in hitTest:) #import “JGBackView.h” @interface (JGBackView) / * * * / @property button (nonatomic, weak) UIButton *button; @end @implementation JGBackView (instancetype) – initWithFrame: (CGRect frame) {if (self = super / initWithFrame:frame]) {UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom] //imageWithOriginalMode:; this method is the classification of my own smoking, is to set the picture rendering mode originalMode [backButton setImage:[UIImage imageWi ThOriginalMode:@ “navigationButtonReturn” forState:UIControlStateNormal] [backButton imageWithOriginalMode:@ “; setImage:[UIImage navigationButtonReturnClick” forState:UIControlStateHighlighted] [backButton setTitle:@ “]; return forState: UIControlStateNormal] [backButton setTitleColor:[UIColor”; blackColor] forState:UIControlStateNormal] [backButton; setTitleColor:[UIColor redColor] forState:UIControlStateHighlighted]; [backButton sizeToFit]; self.button = backButton; [self addSubview:backButton];} return self;} / / will transfer the UIButton event method (addTarget: (void) – out nullable ID) target action: (SEL) action forControlEvents: (UIControlEvents controlEvents) {[self.button addTarget:tar Get action:action forControlEvents:controlEvents];} / / this method is used to processing beyond the parent control part when the button can not click on the problem – (UIView) hitTest: (CGPoint) point withEvent: (UIEvent * event) {/ / coordinates of the point (the point coordinates of the coordinate system is button the parent control, here is converted to button self) coordinate the above point coordinates CGPoint btnP = [self convertPoint:point toView:self.button]; / / the point is not in the button above if (CGRectContainsPoint (self.button.bounds, btnP)) {/ / if the point on the button, the button is the most suitable (view will allow button to handle events) return self.button;} / / restore default approach return [super hitTest:point withEvent:event];} – {(void) layoutSubviews [super lay OutSubviews]; / / set the button’s x value is negative, which is like the left Mobile self.button.jg_x – = 10; / / set size self.jg_width = self.button.jg_width self.jg_height = self.button.jg_height; the; / / if there is x value, then the return button just show time, there will be not in the proper position of the bug, so here we set X using the system, the default value of X can be self.jg_y = (44 – self.jg_height) * 0.5}; @end
  • Global navigation bar on the back button (the back button is the root controller) when the navigation control jump, will call the pushViewController: method of the controller, and in this way we can get to the controller, and we recommend the use of destinationVC.navigatinoItem.leftBarButtonItem controller is to use objective methods to set the solution: the back button. Custom navigation controller, override pushViewController: in this method agree to set the return button (the code in the pushViewController: method)
@interface JGNavigationController (<); UIGestureRecognizerDelegate> @end @implementation JGNavigationController (void) pushViewController: (UIViewController *) viewController animated: (BOOL animated) {if (self.childViewControllers.count! = 0) {/ / current controller for non root controller when we need to set the return button to set the return button JGBackView / / *backView = [[JGBackView alloc] init]; [backView addTarget:self action:@selector forControlEvents:UIControlEventTouchUpInside] (back); / / viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backView] button to return set here; viewController.hidesBottomBar WhenPushed = YES;} pushViewController:viewController animated:animated];} @end [super

Custom return button’s default screen edge sliding return function recovery

  • When we customize the return button of the navigation controller, there will be a small problem: the return of the function of Apple’s own navigation controller failure
  • Firstly, the sliding return of function is achieved. That class should guess is the navigation controller function. The thought of sliding back, so we should think of UIPanGestureRecognizer. sliding gestures in the navigation controller UINavigationController to search for the key word gesture Gesture will find a property interactivePopGestureRecognizer
  • Let’s look at the official explanation for this property:
The gesture recognizer responsible for popping the top view controller off the navigation stack. The navigation controller installs this gesture recognizer on its view and uses it to pop the topmost view controller off the navigation stack. You can use this property to retrieve the gesture recognizer and tie it to the behavior of other gesture recognizers in your user interface. When tying your gesture recognizers together, make sure they recognize their gestures simultaneously to ensure that your gesture recognizers are given a chance to handle the event. this gesture is used to remove the top of the stack. The stack controller navigation navigation controller adds this gesture to its view, it will be removed from the navigation controller stack stack. You can use the this property To obtain this gesture object, can also be other gestures bound this gesture and your user interface together. When you use these gestures mixed use, remember agent method to invoke gestures to allow to identify multiple gestures to ensure your gestures to perform it specified. (English the level is limited, only about the meaning of translation)
  • From the above information we can know that this is indeed a gesture to achieve the sliding return function
  • When the custom slide back button, in the viewDidLoad to print the property to see if the gesture is still there
< UIScreenEdgePanGestureRecognizer: 0x7ff290717eb0; state = Possible; delaysTouchesBegan = YES; view = < UILayoutContainerView; 0x7ff29051d3e0> target= (< action=handleNavigationTransition:, target=< _UINavigationInteractiveTransition 0x7ff290712d00> > >);
  • From the print results can know that this gesture is there, but why the function of the gesture did not achieve it?
  • Or you can think of this, how to control whether gesture recognition? I think so, I think the gestures of the agent. The agent properties of gestures can trigger listening gestures, can also be used to control whether the gesture recognition behavior. (English is limited, only about the meaning of translation)
- (BOOL) gestureRecognizer: (UIGestureRecognizer *) gestureRecognizer shouldReceiveTouch: (UITouch *) touch
  • Think of where we assume that if the use of the return button is not the system’s own type, then the proxy method will return NO.
  • Solution test: the proxy attribute of the gesture can be emptied, it will not call the gesture proxy method to prohibit the identification of the current gesture
  • Question: when the interface is in the controller with time, may also trigger sliding back gesture, also call stack controller popViewController to the controller from the stack, because the controller is a navigation controller root controller, so there will be abnormal, this leads to feign death.
  • Solution: when the sub controller when the analysis of navigation controller for non root controller, can recognize hand gestures. So we need to determine what methods in the agency work. Time for gesture so we need to set up a proxy.
  • Solution: set up a custom navigation controller of the interactivePopGestureRecognizer for this gesture agent, then agent method, determine the current controller for the root controller, or NO if the controller is the root, otherwise it returns YES..
- (BOOL) gestureRecognizer: (UIGestureRecognizer * gestureRecognizer) shouldReceiveTouch: (UITouch * touch) {/ / we here according to the current navigation controller control to determine the number of the current controller is a return controller of self.childViewControllers.count > root; 1;}
  • In this way, we will achieve the same time as Apple’s sliding screen edge, there will be a return function

Custom return button full screen slide return function

  • Demand: to further enhance the demand, we need to achieve full screen slide return function
  • Analysis: why Apple gesture only edge return function. 1 the first sliding gestures with gestures trigger, this class is related to UIScreenEdgePanGestureRecognizer, analysis of information output from the gesture of the object, this gesture estimation is only the edge of the slide will trigger. So is the root cause of the problem. The realization of the 2 types of gestures secondly sliding return function is called the action method. Target function realization conjecture: so here we can try to use a UIPanGestureRecognizer class (full screen range provided by Apple can trigger gestures). Action method to call the target system gestures. But how to get the target and action methods. We can see from the gesture print information system that action called handleNavigationTransition:, type target of this object is _U INavigationInteractiveTransition so we only need to create an instance of the _UINavigationInteractiveTransition object of this class, you can call the handleNavigationTransition: method of the object to achieve sliding function returns. But the problem is we cannot create the _UINavigationInteractiveTransition object types, because it is apple private. An instance of self.interactivePopGestureRecognizer.delegate but we can get this kind of ready-made, i.e. the default proxy attribute navigation controller this gesture is a _UINavigationInteractiveTransition object of this class. Look at the output information of it: &lt _UINavigationInteractiveTransition: 0x7f9fd8d1f9d0>
  • This is all set.
  • Final solution:
- (void) viewDidLoad {[super viewDidLoad]; / / the first static system comes with the edge of the screen slide return gesture self.interactivePopGestureRecognizer.enabled = NO; / / UIPanGestureRecognizer *pan 1 custom full screen slide gestures = [[UIPanGestureRecognizer alloc] initWithTarget:self.interactivePopGestureRecognizer.delegate action:@selector (handleNavigationTransition:) 2]; / / set the agency, to determine when the top of the stack controller and the controller, pan.delegate = prohibition of gesture recognition self; [self.view addGestureRecognizer:pan];} - (BOOL) gestureRecognizer: (UIGestureRecognizer * gestureRecognizer) shouldReceiveTouch: (UITouch * touch) {/ / if the controller is return controller self.childViewControllers.count &g root T; 1;}