导航栏穿透效果原理图
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
上一篇: 创业之初你真的需要建立自己的网站吗?