Implement a simple breadcrumb navigation

Past articles

Find similar pictures in IOS albums
Multipeer Connectivity Near field multipoint communication
implements a simple sandbox file browser

achieve effect

The main functions are as follows

  • push the view, the breadcrumb navigation bar automatically increases the corresponding view's Item

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

  • When the content width of the breadcrumbs navigation bar item is greater than the current screen width, the breadcrumbs navigation bar enables the sliding mode and automatically moves to the last item to ensure the last item of the breadcrumbs navigation bar corresponding to the current view.

  • When the content width of the breadcrumb navigation bar item is less than or equal to the current screen width, the breadcrumb navigation bar closes the sliding mode

The demonstration effect is as follows

Simulator Screen Recording - iPhone 8 - 2021-11-06 at 15.08.45.gif

Implementation process

The implementation is mainly divided into two sections, one is the breadcrumb navigation controller and the other is the breadcrumb navigation bar.

Breadcrumb Navigation Controller

The current version does not deal with UINavigationController's side slide return function, so the side slide return function of UINavigationController is temporarily turned off

Added breadcrumb bar

In order to facilitate the management of the stacking and pushing of the controller, we need to inherit the UINavigationController provided by the system.

@interface AMBreadCrumbNavController : UINavigationController

@end
复制代码

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

Simulator Screen Shot - iPhone 8 - 2021-11-06 at 15.29.32.png

After adding to the navigation bar, we need to tell the custom breadcrumb navigation bar, the controller stack in the current navigation controller, and put the incoming root view controller into the controller stack. Then it is managed by a custom breadcrumb navigation bar.

And pass the protocol method of the breadcrumb navigation bar to the current navigation controller AMBreadCrumbNavController for implementation.

The implementation code is as follows:

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

    //super中已经初始化好导航条了

    if (self = [super initWithRootViewController:rootViewController]) {
        //将导航栏设置为不透明
        self.navigationBar.translucent = NO;
        //设置根视图
        self.rootViewController = rootViewController;
        //分类添加一个面包屑导航条,用来覆盖掉系统自带的导航条
        [self breadCrumbNav];
        //初始化根视图控制器导航栏
        self.breadCrumbBar.controllers = @[self.rootViewController];
        //设置面包屑导航代理
        self.breadCrumbBar.delegate = self;
        //隐藏自带导航条
        [self setNavigationBarHidden:YES];
    }

    return self;
}
复制代码

Here, the method of adding the breadcrumb navigation bar is changed to be implemented by classifying UIViewController+AMBreadCrumbNav, in order to add breadcrumb navigation to other types of controllers in the future, not just for UINavigationController design.

@interface UIViewController (AMBreadCrumbNav)

/// 面包屑导航栏整个导航栏视图
@property(strong,nonatomic) UIView* breadCrumbView;

/// 面包屑功能条
@property(strong,nonatomic) AMBreadCrumbNavBar* breadCrumbBar;

/// 右侧拓展视图
@property(strong,nonatomic) UIView* rightView;

/// 给右侧拓展视图添加自定义控件
/// **@param** item 右侧控件
- (void) addRightItem:(UIView*) item;

///// 右侧拓展按钮
//@property(strong,nonatomic) UIButton* selectButton;

- (void) breadCrumbNav;
@end
复制代码

Controller pop and push

接下来我们要来实现这个导航控制器的入栈控制器和出栈控制器的方法。

控制器入栈

入栈方法很简单,我们只需要重写UINavigationController的pushViewController,并且将最新的控制器数组传递给面包屑导航条,使其更新导航条内容。

实现代码如下


- (void) pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    //关闭侧滑返回
    if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
        self.interactivePopGestureRecognizer.enabled = NO;
    //先入栈
    [super pushViewController:viewController animated:animated];
    //更新面包屑导航条
    self.breadCrumbBar.controllers = self.viewControllers;
}
复制代码

控制器出栈

因为关闭了导航控制器的侧滑返回功能,而且导航条上也没有对应的出栈按钮。所以我想要实现控制器的出栈,只有点面包屑导航条上的控制器对应的按钮,从而切换到指定的控制器。

所以这里我们需要实现面包屑导航条的委托方法。从而获取到当前在面包屑导航条上所点击的控制器,也就是我们想要切换到控制器。

当获取到了想要切换到的控制器之后,我们就需要将这个控制器之后的控制器全部出栈,只保留当前想要切换的控制器以及它之前的控制器

比如 A - B - C - D这个控制器栈,我们当前在D控制器页面,当我们点击B之后,clickViewController代理方法给我们回传需要切换到的控制器B,这时候我们就需要将C 和 D两个控制器出栈,只保留A和B两个控制器在栈内。

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

    if ([self.viewControllers containsObject:viewController]) {
        if ([self.visibleViewController isEqual:viewController]) {
            return;
        }
        [self popToViewController:viewController animated:YES];
       //判断当前ViewControllers栈底元素是否是根视图,因为如果是跨页面跳转,比如A-B-C-D,D-C跳转 self.viewControllers栈底元素是根视图A,但是D-B跳转,self.viewControllers栈底元素却不是根视图A
        if (![self.viewControllers.firstObject isEqual:self.rootViewController]) {
            //删除不是根视图的栈底元素
            [self.viewControllers.firstObject removeFromParentViewController];
        }
        //更新面包屑导航条
        self.breadCrumbBar.controllers = self.viewControllers;
    }
}
复制代码

面包屑导航条

面包屑导航条内部是由一个UICollectionView来实现的,每一个控制器对应CollectionView中的一个item。

item分为文字区和拓展区

  • 文字区主要显示当前控制器要在面包屑导航条中的显示的文字。
  • 拓展区,主要用于当item的数量大于2的时候,给非最后一个item添加面包屑导航条的分割符号。这里我们用的是一个简单 > 符号来替代,后期可以根据自身需求替换成动画或者图片。

image.png

当然每个item的宽度是由所要显示的文字加上拓展区的宽度来计算的。也就是说当不需要显示拓展区的时候(item只有一个或者最后一个item)时,item的宽度就等于文字区的宽度。

item显示数据获取

这里我们通过获取面包屑导航控制器(AMBreadCrumbNavController)中的控制器栈(controllers),将其传递给AMBreadCrumbNavBar

@interface AMBreadCrumbNavBar : UIView
/// 保存系统导航控制器入栈的控制器
@property(strong,nonatomic) NSArray<UIViewController*>* controllers;
@property(weak,nonatomic) id<AMBreadCrumbBarDelegate> delegate;

@end
复制代码

Through controllers, we can get the controllers in the entire controller stack, and then get the title that needs to be displayed according to the navigationItem property in the controller UIViewController. Then it is displayed by AMBreadCrumbNavCell.


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

}
复制代码

Automatically slide to the end

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

The implementation principle is very simple, that is to use the scrollToItemAtIndexPath method of CollectionView

This method will not trigger when the specified item is visible, and automatically scroll to the specified item when the specified item is not visible.

So we only need to get the last controller when the breadcrumb navigation bar (AMBreadCrumbNavBar) loads data, that is, the last item that needs to be displayed, and let the CollectionView scroll to him. It can be achieved that when the end item is not visible, it automatically scrolls to the end item.

The implementation code is as follows

- (void)setControllers:(NSArray<UIViewController *> *)controllers{
    _controllers = controllers;
    [self.collectionView reloadData];
    //如果导航条无法展示所有cell,则将collectionView自动滑动到末尾。
    [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:controllers.count-1 inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
}

复制代码

Note that when scrollToItemAtIndexPath specifies scrolling, be sure to refresh the CollectionView before specifying scrolling.

project address

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

The project will be continuously improved in the future.

AMBreadcrumbs

Guess you like

Origin juejin.im/post/7027381839526379527