当前位置: 移动技术网 > 移动技术>移动开发>IOS > 导航栏穿透效果原理图

导航栏穿透效果原理图

2020年07月17日  | 移动技术网移动技术  | 我要评论

1. 实现 UITableView可以 穿透状态栏 效果: 

   1.  UITableView设置 占据屏幕,从 状态栏 下开始 布局 
   2.  设置内边距   状态栏   20(状态栏) +44(导航栏)+ nav(35) + 底部(tabbar 49) 
       设置底部内壁那局  底部(tabbar  49)避免内容被遮挡

2. 自定义 UITabBar  重写  layoutSubviews 重新摆放控件,把 +  摆放到中间即可
  懒加载问题:
添加对应的View 到 scrollView 
滚动到对应的View

3. nav 实现: 
  3.1. 添加 title ,添加 指示器, 设置指示器 centerX 等于 选中 按钮  centerX 
  3.2.    XMGAllViewController *all = [[XMGAllViewController alloc] init];
    [self addChildViewController:all]; 
     添加  navController 到 self

  3.3.  [self.scrollView addSubview:childVc.view];
 把 Controller 的 view 添加到 scrollView中

  3.4. 监听滑动,修改 指示器坐标 、scrollView的 view 坐标
// 系统 api 触发  滚动时间,滚动停止触发
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
    [self addChildVcView];
}
// 人为 手动滑动触发 
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{

核心代码 

XMGTabBarController.m



#import "XMGTabBarController.h"
#import "OneController.h"
#import "TwoController.h"
#import "ThreeController.h"
#import "FourController.h"
#import "ModelController.h"
#import "XMGTabBar.h"
#import "XMGNavigationController.h"


@interface XMGTabBarController ()

@end

@implementation XMGTabBarController

- (void)viewDidLoad {
    [super viewDidLoad];
    
   // 第一个子控制器
      OneController *oneVC= [[OneController alloc]init];
      UINavigationController* oneVCN= [[UINavigationController alloc] initWithRootViewController:oneVC];
      oneVCN.tabBarItem.title=@"A控制器";
      
      //1. 分别设置
      UIImage* image1= [UIImage imageNamed:@"navigationbar_friendsearch"];
      UIImage* selelctImage1= [UIImage imageNamed:@"navigationbar_friendsearch_highlighted"];
      // 系统会被默认图片渲染绿色,这里改变系统渲染图片的模式
      selelctImage1= [selelctImage1
                      imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
      oneVCN.tabBarItem.image = image1;
      oneVCN.tabBarItem.selectedImage = selelctImage1;

      // tabbarItem 设置文字 属性
      // 属性到  using the keys found in NSAttributedString.h 中找  UIKit下
      //
      [oneVCN.tabBarItem setBadgeValue:@"100"];
      NSMutableDictionary* normalAttrs= [NSMutableDictionary dictionary];
      normalAttrs[NSFontAttributeName]= [UIFont systemFontOfSize:14];
      normalAttrs[NSForegroundColorAttributeName]=[UIColor blackColor];
  //    [oneVC.tabBarItem setTitleTextAttributes:normalAttrs forState:UIControlStateNormal];
      
      
      NSMutableDictionary* selectAttrs= [NSMutableDictionary dictionary];
         selectAttrs[NSForegroundColorAttributeName]=[UIColor redColor];
         selectAttrs[NSFontAttributeName]= [UIFont systemFontOfSize:14];
  //    [oneVC.tabBarItem setTitleTextAttributes:selectAttrs forState:UIControlStateSelected];
      
      //2. 统一设置
      //setTitleTextAttributes:(nullable NSDictionary<NSAttributedStringKey,id> *)attributes forState:(UIControlState)state API_AVAILABLE(ios(5.0)) UI_APPEARANCE_SELECTOR;
      // 后面 有 UI_APPEARANCE_SELECTOR 可以通过 appear设置
      // 取出appear,统一设置,那么这个app中所有的 UITabBarItem 都有这个属性
      UITabBarItem* item= [UITabBarItem appearance];
      [item setTitleTextAttributes:normalAttrs forState:UIControlStateNormal];
      [item setTitleTextAttributes:selectAttrs forState:UIControlStateSelected];
     // [self addChildViewController:oneVC];
     [self addChildViewController:oneVCN];
      
      
      // 第二个子控制器
        TwoController *twoVC= [[TwoController alloc]init];
     UINavigationController* twoVCN= [[XMGNavigationController alloc] initWithRootViewController:twoVC];
        twoVCN.tabBarItem.title=@"B控制器";
        [self addChildViewController:twoVCN];
      
      // 中间 按钮,如果自定义   XMGTabBar 那么必须设置中间 控制器了
//      UIViewController* center =[[ UIViewController alloc] init];
//      [self addChildViewController: center];
    
      ThreeController *threeVC= [[ThreeController alloc]init];
       UINavigationController* threeVCN= [[XMGNavigationController alloc] initWithRootViewController:threeVC];
      threeVCN.tabBarItem.title=@"C控制器";
      [self addChildViewController:threeVCN];
      
    
      FourController *fourVC= [[FourController alloc]init];
      UINavigationController* fourVCN= [[XMGNavigationController alloc] initWithRootViewController:fourVC];
      fourVCN.tabBarItem.title=@"D控制器";
      [self addChildViewController:fourVCN];
    
    
  //    [tabBarVc addChildViewController:oneVC];
  //    [tabBarVc addChildViewController:twoVC];
  //    [tabBarVc addChildViewController:threeVC];
      
      //使用 数组的方式 同一设置
  //     tabBarVc.viewControllers=@[oneVC,twoVC,threeVC,fourVC];
    
    
    // 设置自定义tabbar
    // tabbar 是  readonly 不能直接设置,通过kvc 设置
   //  self.tabBar= [[XMGTabBar alloc] init];
    // kvc设置
    [self setValue:[[XMGTabBar alloc] init] forKeyPath:@"tabBar"];

    
}

- (void)viewWillAppear10:(BOOL)animated
{
    [super viewWillAppear:animated];
    
    /**** 增加一个发布按钮 ****/
// 这个方法会被调入多次,比如,弹出模态窗口,串口消失的时候,会调用这个方法
    // 避免添加多个button
    //解决方法: 1.dispatch_once
    // 2.  懒加载  定义一个变量 publishButton
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        /**** 增加一个发布按钮 ****/
        UIButton *publishButton = [UIButton buttonWithType:UIButtonTypeCustom];
        publishButton.backgroundColor = XMGRandomColor;
        [publishButton setImage:[UIImage imageNamed:@"tabBar_publish_icon"] forState:UIControlStateNormal];
        [publishButton setImage:[UIImage imageNamed:@"tabBar_publish_click_icon"] forState:UIControlStateHighlighted];
        publishButton.frame = CGRectMake(0, 0, self.tabBar.frame.size.width / 5, self.tabBar.frame.size.height);
        publishButton.center = CGPointMake(self.tabBar.frame.size.width * 0.5, self.tabBar.frame.size.height * 0.5);
        [publishButton addTarget:self action:@selector(publishClick) forControlEvents:UIControlEventTouchUpInside];
        [self.tabBar addSubview:publishButton];
    });
}


-(void)publishClick{
    NSLog(@"helll");
    
     ModelController* modelVc= [[ModelController alloc] init];
     UINavigationController* nav= [[UINavigationController alloc] initWithRootViewController:modelVc];
    
     [self presentViewController:nav animated:YES completion:^{
         
     }];

}


@end

XMGTabBar.m

#import "XMGTabBar.h"

@interface XMGTabBar()
/** 中间的发布按钮 */
@property (nonatomic, weak) UIButton *publishButton;
@end

@implementation XMGTabBar

#pragma mark - 懒加载
/** 发布按钮 */
- (UIButton *)publishButton
{
    if (!_publishButton) {
        UIButton *publishButton = [UIButton buttonWithType:UIButtonTypeCustom];
        publishButton.backgroundColor = XMGRandomColor;
        [publishButton setImage:[UIImage imageNamed:@"tabBar_publish_icon"] forState:UIControlStateNormal];
        [publishButton setImage:[UIImage imageNamed:@"tabBar_publish_click_icon"] forState:UIControlStateHighlighted];
        [publishButton addTarget:self action:@selector(publishClick) forControlEvents:UIControlEventTouchUpInside];
        [self addSubview:publishButton];
        _publishButton = publishButton;
    }
    return _publishButton;
}

#pragma mark - 初始化
/**
 *  布局子控件 ,重写 layoutSubviews  重新摆放
 */
- (void)layoutSubviews
{
    [super layoutSubviews];
    
    // NSClassFromString(@"UITabBarButton") == [UITabBarButton class]
    // NSClassFromString(@"UIButton") == [UIButton class]
    
    /**** 设置所有UITabBarButton的frame ****/
    // 按钮的尺寸
    CGFloat buttonW = self.frame.size.width / 5;
    CGFloat buttonH = self.frame.size.height;
    CGFloat buttonY = 0;
    // 按钮索引
    int buttonIndex = 0;
    
    for (UIView *subview in self.subviews) {
        // 过滤掉非UITabBarButton
//        if (![@"UITabBarButton" isEqualToString:NSStringFromClass(subview.class)]) continue;
        // UITabBarButton 把 字符串转化为类
        if (subview.class != NSClassFromString(@"UITabBarButton")) continue;
        
        // 设置frame
        CGFloat buttonX = buttonIndex * buttonW;
        if (buttonIndex >= 2) { // 右边的2个UITabBarButton
            buttonX += buttonW;
        }
        subview.frame = CGRectMake(buttonX, buttonY, buttonW, buttonH);
        
        // 增加索引
        buttonIndex++;
    }
    
    /**** 设置中间的发布按钮的frame ****/
    self.publishButton.frame = CGRectMake(0, 0, buttonW, buttonH);
    self.publishButton.center = CGPointMake(self.frame.size.width * 0.5, self.frame.size.height * 0.5);
}

#pragma mark - 监听
- (void)publishClick
{
    XMGLogFunc
}

@end

FourController.m



#import "FourController.h"
#import "Dog.h"
#import "UIView+XMGExtension.h"
#import "XMGTitleButton.h"
#import "XMGAllViewController.h"
#import "XMGPictureViewController.h"
#import "XMGVideoViewController.h"
#import "XMGVoiceViewController.h"
#import "XMGWordViewController.h"

@interface FourController ()<UIScrollViewDelegate>

// 如果是弱引用,那么viewDidLoad  dog 被释放, dog置空nil , self.dog 获取的时候nil
// @property(nonatomic,weak) Dog* dog1;

// 强引用
//@property(nonatomic,strong) Dog* dog1;

// assign  viewDidLoad执行完毕以后dog被释放,但是没有置nil, 那么使用self.dog1 会出现野指针,报错
@property(nonatomic,assign) Dog* dog1;

/** 标题栏 */
@property (nonatomic, weak) UIView *titlesView;

/** 标题按钮底部的指示器 */
@property (nonatomic, weak) UIView *indicatorView;

/** 当前选中的标题按钮 */
@property (nonatomic, weak) XMGTitleButton *selectedTitleButton;

/** UIScrollView */
@property (nonatomic, weak) UIScrollView *scrollView;

@end

@implementation FourController

- (void)viewDidLoad {
    [super viewDidLoad];
    XMGLogFunc
    
    Dog* dog=[Dog new];
    
    self.dog1=dog;
    [self setupChildViewControllers];
    [self setupScrollView];
    [self setupTitlesView];
    
     // 默认添加子控制器的view
    [self addChildVcView];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"%@",self.dog1);
}

- (void)setupTitlesView
{
    // 标题栏
    UIView *titlesView = [[UIView alloc] init];
    titlesView.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.7];
    titlesView.frame = CGRectMake(0, 64, self.view.xmg_width, 35); //导航控制器的高度默认是64
    [self.view addSubview:titlesView];
    self.titlesView = titlesView;
    
    // 添加标题
    NSArray *titles = @[@"全部", @"视频", @"声音", @"图片", @"段子"];
    NSUInteger count = titles.count;
    CGFloat titleButtonW = titlesView.xmg_width / count;
    CGFloat titleButtonH = titlesView.xmg_height;
    for (NSUInteger i = 0; i < count; i++) {
        // 创建
        XMGTitleButton *titleButton = [XMGTitleButton buttonWithType:UIButtonTypeCustom];
        titleButton.tag = i;
        [titleButton addTarget:self action:@selector(titleClick:) forControlEvents:UIControlEventTouchUpInside];
        [titlesView addSubview:titleButton];
        
        // 设置数据
        [titleButton setTitle:titles[i] forState:UIControlStateNormal];
        
        // 设置frame
        titleButton.frame = CGRectMake(i * titleButtonW, 0, titleButtonW, titleButtonH);
    }
    
    // 按钮的选中颜色
    XMGTitleButton *firstTitleButton = titlesView.subviews.firstObject;
    
    // 底部的指示器
    UIView *indicatorView = [[UIView alloc] init];
    indicatorView.backgroundColor = [firstTitleButton titleColorForState:UIControlStateSelected];
    indicatorView.xmg_height = 1;
    indicatorView.xmg_y = titlesView.xmg_height - indicatorView.xmg_height;
    [titlesView addSubview:indicatorView];
    self.indicatorView = indicatorView;
    
    // firstTitleButton 中的 label 没有计算出来 一开始 
    // 立刻根据文字内容计算label的宽度,这里必须要调用一下,有的时候firstTitleButton.titleLabel.xmg_width没有计算出来
    [firstTitleButton.titleLabel sizeToFit];
    indicatorView.xmg_width = firstTitleButton.titleLabel.xmg_width;
    // 获取选中按钮的 坐标cneterX,赋值给 指示器
    indicatorView.xmg_centerX = firstTitleButton.xmg_centerX;
    
    // 默认情况 : 选中最前面的标题按钮
    firstTitleButton.selected = YES;
    self.selectedTitleButton = firstTitleButton;
}

#pragma mark - 监听点击
- (void)titleClick:(XMGTitleButton *)titleButton
{
    // 控制按钮状态
    self.selectedTitleButton.selected = NO;
    titleButton.selected = YES;
    self.selectedTitleButton = titleButton;

    // 指示器
    [UIView animateWithDuration:0.25 animations:^{
        // 如果需求 是文字边框再加一些 边距 那么
        //   self.indicatorView.xmg_width = titleButton.titleLabel.xmg_width +10 ; 即可
        self.indicatorView.xmg_width = titleButton.titleLabel.xmg_width;
        self.indicatorView.xmg_centerX = titleButton.xmg_centerX;
    }];
    
    // 让UIScrollView滚动到对应位置
    CGPoint offset = self.scrollView.contentOffset;
    offset.x = titleButton.tag * self.scrollView.xmg_width;
    [self.scrollView setContentOffset:offset animated:YES];
}

- (void)setupChildViewControllers
{
    XMGAllViewController *all = [[XMGAllViewController alloc] init];
    [self addChildViewController:all];
    
    XMGVideoViewController *video = [[XMGVideoViewController alloc] init];
    [self addChildViewController:video];
    
    XMGVoiceViewController *voice = [[XMGVoiceViewController alloc] init];
    [self addChildViewController:voice];
    
    XMGPictureViewController *picture = [[XMGPictureViewController alloc] init];
    [self addChildViewController:picture];
    
    XMGWordViewController *word = [[XMGWordViewController alloc] init];
    [self addChildViewController:word];
}

- (void)setupScrollView
{
    // 不允许自动调整scrollView的内边距
    /*  3.  如果UIScrollView是控制器View的第一个View, 那么ScrollView 默认上边的内边距是 64  设置
// 不允许自动调整scrollView的内边距,来手动控制内边距
   self.automaticallyAdjustsScrollViewInsets = NO;
    */
    // 
  //  self.automaticallyAdjustsScrollViewInsets = NO;
    
    UIScrollView *scrollView = [[UIScrollView alloc] init];
    scrollView.backgroundColor = XMGRandomColor;
    scrollView.frame = self.view.bounds;
    scrollView.pagingEnabled = YES;
    scrollView.showsHorizontalScrollIndicator = NO;
    scrollView.showsVerticalScrollIndicator = NO;
    scrollView.delegate = self;
    // 添加所有子控制器的view到scrollView中   设置为0,禁止scrollView上下滚动,免得和 UITavleView 上下滚动产生冲突
    scrollView.contentSize = CGSizeMake(self.childViewControllers.count * scrollView.xmg_width, 0);
    [self.view addSubview:scrollView];
    self.scrollView = scrollView;
    NSLog(@"scrollView= %@",NSStringFromUIEdgeInsets(scrollView.contentInset));
    NSLog(@"scrollView--frame==%@",NSStringFromCGRect(scrollView.frame));
}

#pragma mark - 添加子控制器的view
// 如何实现 滑动到当前 View然后在显示: View实现懒加载
- (void)addChildVcView
{
    // 子控制器的索引
    NSUInteger index = self.scrollView.contentOffset.x / self.scrollView.xmg_width;
    
    // 取出子控制器
    UIViewController *childVc = self.childViewControllers[index];
//    if (childVc.view.superview) return;
//    if (childVc.view.window) return;
    //
    if ([childVc isViewLoaded]) return;
    
//    childVc.view.xmg_x = index * self.scrollView.xmg_width;
//    childVc.view.xmg_y = 0;
//    childVc.view.xmg_width = self.scrollView.xmg_width;
//    childVc.view.xmg_height = self.scrollView.xmg_height;
    
//    childVc.view.xmg_x = self.scrollView.contentOffset.x;
//    childVc.view.xmg_y = self.scrollView.contentOffset.y;
//    childVc.view.xmg_width = self.scrollView.xmg_width;
//    childVc.view.xmg_height = self.scrollView.xmg_height;
    
//    childVc.view.xmg_x = self.scrollView.bounds.origin.x;
//    childVc.view.xmg_y = self.scrollView.bounds.origin.y;
//    childVc.view.xmg_width = self.scrollView.bounds.size.width;
//    childVc.view.xmg_height = self.scrollView.bounds.size.height;
    
//    childVc.view.frame = CGRectMake(self.scrollView.bounds.origin.x, self.scrollView.bounds.origin.y, self.scrollView.bounds.size.width, self.scrollView.bounds.size.height);
    /*
      bounds:
 scrollview的bounds的坐标就是偏移量的坐标
             宽度、高度就是 scrollview.frame的宽高
     
     childVc.view: 调用以后, 子View才会 调用 viewDidLoad
    */
    childVc.view.frame = self.scrollView.bounds;
    [self.scrollView addSubview:childVc.view];
}
#pragma mark - <UIScrollViewDelegate>
/**
 * 在scrollView滚动动画结束时, 就会调用这个方法
 * 前提: 使用setContentOffset:animated:或者scrollRectVisible:animated:方法让scrollView产生滚动动画
 *   系统 api 触发
 */
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
    [self addChildVcView];
}

/**
 * 在scrollView滚动动画结束时, 就会调用这个方法
 * 前提: 人为拖拽scrollView产生的滚动动画
 */
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    // 选中\点击对应的按钮
    NSUInteger index = scrollView.contentOffset.x / scrollView.xmg_width;
    XMGTitleButton *titleButton = self.titlesView.subviews[index];
    [self titleClick:titleButton];
    
    // 添加子控制器的view
    [self addChildVcView];
    
    // 当index == 0时, viewWithTag:方法返回的就是self.titlesView
    //    XMGTitleButton *titleButton = (XMGTitleButton *)[self.titlesView viewWithTag:index];
}

@end

首先看一下bounds和frame区别: 

每一个控件都有矩形框、内容区
 bounds: 以自己内容区为左上角为原点,矩形框左上角到该点的距离
 frame: 是以父控件内容区左上角为原点坐标
 contentOffset.x、contentOffset.y: 
 和bounds x、y值一致,内容区和矩形区左上角距离
 contentInset:内边距

效果图:

源码地址:https://download.csdn.net/download/dreams_deng/12618321 

本文地址:https://blog.csdn.net/dreams_deng/article/details/82959623

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网