The articles

Multipeer Connectivity is a simple sandbox file browser

Implementation effect

The main functions are as follows

  • Push view, the breadcrumb navigation bar automatically increases the corresponding view Item

  • Click the corresponding breadcrumb navigation bar Item to jump to the corresponding text of Item

  • When the breadcrumb navigation bar item’s content width is larger than the current screen width, the breadcrumb navigation bar starts sliding mode and automatically moves to the last item, ensuring that the current view corresponds to the last item of the breadcrumb navigation bar

  • Breadcrumb navigation bar Turns off slide mode when the content width of the item is less than or equal to the current screen width

The demo looks like this

The implementation process

The implementation is mainly divided into two parts, one is breadcrumb navigation controller, one is breadcrumb navigation bar.

Breadcrumb navigation controller

The current version of UINavigationController does not handle the sideslip return function, so the UINavigationController sideslip return function is temporarily disabled

Breadcrumb navigation bar added

In order to facilitate the management of the controller on and off the stack, we need to inherit the UINavigationController provided by the system

@interface AMBreadCrumbNavController : UINavigationController

@end
Copy the code

As shown in the figure below, if you use the system-provided UINavigationController, there is a built-in NavigationBar. So we need to hide the built-in NavigationBa and add our own breadcrumb navigation bar.

After adding it to the navigation bar, we need to tell the custom breadcrumb navigation bar, the controller stack in the current navigation controller, and add the passed root view controller to the controller stack. It is then managed by the custom breadcrumb navigation bar.

And the deal method of breadcrumb navigation bar to the current navigation controller AMBreadCrumbNavController implementation.

The implementation code is as follows:

- (instancetype)initWithRootViewController:(UIViewController *)rootViewController {

    // The navigation bar is already initialized in super

    if (self = [super initWithRootViewController:rootViewController]) {
        // Make the navigation bar opaque
        self.navigationBar.translucent = NO;
        // Set the root view
        self.rootViewController = rootViewController;
        // Add a breadcrumb navigation bar to cover the system's own navigation bar
        [self breadCrumbNav];
        // Initialize the root view controller navigation bar
        self.breadCrumbBar.controllers = @[self.rootViewController];
        // Set the breadcrumb navigation agent
        self.breadCrumbBar.delegate = self;
        // Hide the built-in navigation bar
        [self setNavigationBarHidden:YES];
    }

    return self;
}
Copy the code

I’ve changed the breadcrumb navigation method to UIViewController+AMBreadCrumbNav. The goal is to add breadcrumb navigation to other types of controllers in the future, not just UINavigationController design.

@interface UIViewController (AMBreadCrumbNav)

/// Breadcrumb navigation bar the entire navigation bar view
@property(strong.nonatomic) UIView* breadCrumbView;

/// Breadcrumb function bar
@property(strong.nonatomic) AMBreadCrumbNavBar* breadCrumbBar;

/// Expand the right view
@property(strong.nonatomic) UIView* rightView;

/// Add a custom control to the expanded view on the right
// **@param** item
- (void) addRightItem:(UIView*) item;

///// Expansion button on the right
//@property(strong,nonatomic) UIButton* selectButton;

- (void) breadCrumbNav;
@end
Copy the code

Controller out and onto the stack

Next we are going to implement the methods of the navigation controller’s stack controller and the stack controller.

Controller loading

The pushViewController of UINavigationController is simply overwritten and passed the latest controller array to the breadcrumb navigation bar to update the contents of the navigation bar.

The implementation code is as follows


- (void) pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    // Close sideslip and return
    if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
        self.interactivePopGestureRecognizer.enabled = NO;
    / / first into the stack
    [super pushViewController:viewController animated:animated];
    // Update the breadcrumb navigation bar
    self.breadCrumbBar.controllers = self.viewControllers;
}
Copy the code

Controller out of stack

Because the slide back function of the navigation controller is disabled, and there is no corresponding push button on the navigation bar. So I want to make the controller go off the stack, just click the controller’s button on the breadcrumb navigation bar, and switch to the specified controller.

So here we need to implement the breadcrumb navigator delegate method. So we get the controller that we’re currently clicking on in the breadcrumb navigation bar, so we want to switch to the controller.

Once we get the controller we want to switch to, we need to remove all controllers after this controller from the stack, leaving only the controller we want to switch to and the controller before it

For example, when we click B on controller D, the clickViewController agent returns to us the controller B that we want to switch to. In this case, we need to push C and D off the stack, leaving only A and B on the stack.

- (void)clickViewController:(UIViewController *)viewController{

    if ([self.viewControllers containsObject:viewController]) {
        if ([self.visibleViewController isEqual:viewController]) {
            return;
        }
        [self popToViewController:viewController animated:YES];
       // Check whether the current element at the bottom of the ViewControllers stack is the root view, because if it is A cross-page jump, such as a-b-c-d, d-C jump, self. ViewControllers are root view A, but d-B jump, Self. viewControllers the bottom element of the stack is not the root view A
        if(! [self.viewControllers.firstObject isEqual:self.rootViewController]) {
            // Remove the bottom element of the stack that is not the root view
            [self.viewControllers.firstObject removeFromParentViewController];
        }
        // Update the breadcrumb navigation bar
        self.breadCrumbBar.controllers = self.viewControllers; }}Copy the code

Breadcrumb navigation bar

The breadcrumb navigation bar is internally implemented by a UICollectionView, and each controller corresponds to an item in the CollectionView.

Item is divided into text area and expansion area

  • The text area mainly displays the text that the current controller wants to display in the breadcrumb navigation bar.
  • The expansion area is mainly used to add the breadcrumb navigation bar segmentation symbol to the non-last item when the number of items is greater than 2. Here we use a simple > symbol to replace, later can be replaced according to their own needs into animation or pictures.

Of course, the width of each item is calculated by the text to be displayed plus the width of the extension area. That is, the width of the item is equal to the width of the text area when there is no need to display the expanded area (the item has only one or the last item).

Item displays data retrieval

We here by getting bread crumbs navigation controller (AMBreadCrumbNavController) controller in the stack (controllers), pass it to AMBreadCrumbNavBar

@interface AMBreadCrumbNavBar : UIView
// save the controller pushed by the system navigation controller
@property(strong.nonatomic) NSArray<UIViewController*>* controllers;
@property(weak.nonatomic) id<AMBreadCrumbBarDelegate> delegate;

@end
Copy the code

So we can get controllers, all the controllers in the stack, and then we can get the title that we want to display based on the navigationItem property in our controller UIViewController. It is then handed over to the AMBreadCrumbNavCell to display.


/// AMBreadCrumbNavCell
- (void)setController:(UIViewController *)controller{
    _controller = controller;
    NSLog(@" The title is %@",controller.navigationItem.title);
    self.titleLabel.text = controller.navigationItem.title;

}
Copy the code

Automatically slides to the end

Set the breadcrumb navigation bar to automatically slide to the end of the CollectionView when the current screen width cannot display the entire contents of the CollectionView

The implementation principle is very simple, using the CollectionView scrollToItemAtIndexPath method

This method does not fire when the specified item is visible, but scrolls automatically to the specified item when it is not visible.

So we just need to get the first controller, the last one that needs to appear item, and let the CollectionView scroll to it when the AMBreadCrumbNavBar loads the data. When the last item is not visible, it automatically rolls to the last item.

The implementation code is as follows

- (void)setControllers:(NSArray<UIViewController *> *)controllers{
    _controllers = controllers;
    [self.collectionView reloadData];
    // If the navigation bar cannot show all cells, the collectionView slides to the end automatically.
    [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:controllers.count- 1 inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
}

Copy the code

Note that when scrollToItemAtIndexPath specifies the scroll, the CollectionView must be refreshed before the scroll is specified.

The project address

If you have any problems, please send them to me. I will try my best to solve them

The project will continue to be improved.

AMBreadcrumbs