欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

导航栏穿透效果原理图

程序员文章站 2022-05-20 17:59:33
首先看一下bounds和frame区别:每一个控件都有矩形框、内容区bounds: 以自己内容区为左上角为原点,矩形框左上角到该点的距离frame: 是以父控件内容区左上角为原点坐标contentOffset.x、contentOffset.y:和bounds x、y值一致,内容区和矩形区左上角距离contentInset:内边距......

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

相关标签: IOS开发