最近公司的OA项目,产品要求按照钉钉实现此功能,具体请查看IOS端的钉钉app,上下滑动tabbar,可以拉出更多的操作按钮。
这个比较高级。实现思路无非两个:
1、自己写一个基于UIViewController的tabbar控制器
2、对UITabBarController视图组织进行重构
实现效果:
在这里,我使用的是第二种方式。请按照以下步骤添加代码即可:
1、先自定义一个UITabBar,注意此处必须自定义,因为我们滑动调整tabbar坐标的时候,会出现抖动,为避免抖动,我们需要处理其setFrame方法。
@interface UISlideTabBar : UITabBar
@end
@interface UISlideTabBar ()
@end
@implementation UISlideTabBar
- (void)setFrame:(CGRect)frame
{
CGFloat bottomHeight = [[UIApplication sharedApplication] delegate].window.safeAreaInsets.bottom;
CGFloat maxY = [UIScreen mainScreen].bounds.size.height - 49 - bottomHeight;
if (frame.size.height == 49 && frame.origin.y>= maxY && bottomHeight>0)
{
NSLog(@"return");
}
else
{
[super setFrame:frame];
}
}
@end
2、 定义一个UITabViewController 基于UITabBarController并添加此block方法后面要用到,这个就是一个插槽VIEW,用于对外暴露一个视图容器,添加更多的操作。
@interface UITabViewController : UITabBarController
- (void)addSlotterViewWithConfigBlock:(UIView* _Nullable (^)(void))setupBlock;
@end
3、在UITabViewController.m文件中,添加以下代码:
typedef UIView* _Nullable (^UITabbarSlotterViewBlock)(void);
@interface UITabViewController ()<UITabBarControllerDelegate>
{
NSInteger tabbarIndex;
BOOL fromTop;
CGFloat lineY;
}
@property (nonatomic, strong) UIView *slotterView;
@property (nonatomic, strong) UIButton *maskView;
@property (nonatomic,copy) UITabbarSlotterViewBlock slotterViewBlock;
@end
- (void)viewDidLayoutSubviews {
[self.tabBar layoutSubviews];//Fix issue #93 #392
CYLTabBar *tabBar = (CYLTabBar *)self.tabBar;
CGFloat bottomHeight = [[UIApplication sharedApplication] delegate].window.safeAreaInsets.bottom;
for (UIView *view in self.tabBar.subviews)
{
if ([view isKindOfClass:NSClassFromString(@"UITabBarButton")])
{
[view removeFromSuperview];
}
}
CGFloat tabBarHeight = self.tabBarHeight;
if (self.slotterViewBlock)
{
if (!self.slotterView)
{
UIView *cntView = self.slotterViewBlock();
CGRect fm = cntView.frame;
fm.origin.y += bottomHeight;
[cntView setFrame:fm];
CGRect rt = CGRectMake(0, 0, self.view.bounds.size.width, fm.size.height+fm.origin.y);
UIView *view = [[UIView alloc] initWithFrame:rt];
[view setBackgroundColor:cntView.backgroundColor];
[view addSubview:cntView];
self.slotterView = view;
[self.view addSubview:self.slotterView];
//阴影层
rt = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height-tabBarHeight);
self.maskView = [UIButton buttonWithType:UIButtonTypeCustom];
[self.maskView setFrame:rt];
[self.maskView addTarget:self action:@selector(mmiSetViewToActive) forControlEvents:UIControlEventTouchUpInside];
[self.maskView setBackgroundColor:[UIColor colorWithRed:0.0/255.0 green:0.0/255.0 blue:0.0/255.0 alpha:0.35]];
[self.maskView setAlpha:0.0];
[self.view insertSubview:self.maskView belowSubview:self.tabBar];
[self.view sendSubviewToBack:self.maskView];
NSInteger idx = 0;
CGFloat x = 0;
CGFloat w = self.view.bounds.size.width/self.tabBar.items.count;
CGFloat spacing = 3;
UIFont *font = [UIFont systemFontOfSize:10];
while (idx<self.tabBar.items.count)
{
CGRect rt = CGRectMake(x, 0, w, self.tabBarHeight);
UIView *view = [[UIView alloc] initWithFrame:rt];
[view setBackgroundColor:[UITabBar appearance].backgroundColor];
[self.tabBar addSubview:view];
UITabBarItem *i = [[self.tabBar items] objectAtIndex:idx];
[i setDataObject:view];
CGSize imageSize = i.image.size;
CGSize titleSize = [self stringSizeWithFont:font strText:i.title];
rt = CGRectMake(0, 0, rt.size.width, rt.size.height);
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setFrame:rt];
button.titleEdgeInsets = UIEdgeInsetsMake(0, - imageSize.width, - (imageSize.height + spacing), 0);
button.imageEdgeInsets = UIEdgeInsetsMake(- (titleSize.height + spacing), 0, 0, - titleSize.width);
[button setTitle:i.title forState:UIControlStateNormal];
[button setImage:i.image forState:UIControlStateNormal];
button.imageView.contentMode = UIViewContentModeScaleAspectFit;
[button setImage:i.selectedImage forState:UIControlStateSelected];
[button.titleLabel setFont:font];
[button setSelected:idx==0];
[button setTag:-900-idx];
[button setTitleColor:[UIColor colorWithRed:102.0/255.0 green:102.0/255.0 blue:102.0/255.0 alpha:1] forState:UIControlStateNormal];
[button setTitleColor:[UIColor colorWithRed:2.0/255.0 green:134.0/255.0 blue:224.0/255.0 alpha:1] forState:UIControlStateSelected];
[button addTarget:self action:@selector(tabbarItemSelectedAction:) forControlEvents:UIControlEventTouchUpInside];
[view addSubview:button];
idx ++;
x += w;
}
w = self.tabBar.bounds.size.width/20;
rt = CGRectMake((self.view.bounds.size.width-w)/2, 7, w, 3);
if (![self.tabBar viewWithTag:2022])
{
UIView *iview = [[UIView alloc] initWithFrame:rt];
[iview setBackgroundColor:[UIColor colorWithRed:153.0/255.0 green:153.0/255.0 blue:153.0/255.0 alpha:0.5]];
[iview setTag:2022];
[iview.layer setMasksToBounds:YES];
[iview.layer setCornerRadius:1.5];
[self.tabBar addSubview:iview];
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureAct:)];
[self.tabBar setUserInteractionEnabled:YES];
[self.tabBar addGestureRecognizer:panGesture];
}
[[self.tabBar viewWithTag:2022] setFrame:rt];
rt = self.slotterView.frame;
rt.origin.y = self.tabBar.frame.origin.y + self.tabBar.frame.size.height+bottomHeight;
[self.slotterView setFrame:rt];
}
}
}
- (void)mmiSetViewToActive
{
CGFloat maxY = [UIScreen mainScreen].bounds.size.height - self.tabBarHeight;
CGFloat alpha = 1.0;
[UIView animateWithDuration:0.15 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^
{
[self.maskView setAlpha:1-alpha];
} completion:^(BOOL finished) {
[self.view sendSubviewToBack:self.maskView];
}];
[UIView transitionWithView:self.tabBar duration:0.15 options:UIViewAnimationOptionCurveLinear animations:^{
CGRect fm = self.tabBar.frame;
fm.origin.y = maxY;
fm.size.height = self.tabBarHeight;
[self.tabBar setFrame:fm];
} completion:^(BOOL finished) {
}];
[UIView transitionWithView:self.tabBar duration:0.2 options:UIViewAnimationOptionCurveLinear animations:^{
CGRect rt = self.slotterView.frame;
rt.origin.y = self.view.bounds.size.height;
[self.slotterView setFrame:rt];
} completion:^(BOOL finished) {
}];
}
- (void)panGestureAct: (UIPanGestureRecognizer *)recognizer
{
CGPoint touchPoint = [recognizer translationInView:self.view];
CGFloat bottomHeight = [[UIApplication sharedApplication] delegate].window.safeAreaInsets.bottom;
CGFloat maxY = [UIScreen mainScreen].bounds.size.height - self.tabBarHeight;
CGFloat minY = maxY-self.slotterView.frame.size.height;
CGFloat hm = (maxY-minY)/2;
CGRect frame = self.tabBar.frame;
NSTimeInterval interval = 0.0;
if ([self.view.subviews indexOfObject:self.maskView] != 1)
{
[self.view exchangeSubviewAtIndex:1 withSubviewAtIndex:0];
}
if (touchPoint.y>=0 && frame.origin.y>=maxY && !self->fromTop)
{
return;
}
if (recognizer.state == UIGestureRecognizerStateBegan)
{
if (frame.origin.y == minY)
{
self->fromTop = YES;
}
else
{
self->fromTop = NO;
}
}
//-1向上没去 0未动 1向下滑动
NSInteger ifUp = touchPoint.y==self->lineY?0:touchPoint.y<self->lineY?-1:1;
self->lineY = touchPoint.y;
if (self->fromTop)
{
if (frame.origin.y >= maxY)
{
self->fromTop = NO;
}
frame.origin.y = MAX(minY+touchPoint.y, minY);
frame.origin.y = MIN(frame.origin.y, maxY);
}
else
{
if (ifUp == -1)
{
CGFloat ofy = 0;
frame.origin.y = MAX(maxY+touchPoint.y, minY);
frame.origin.y -= ofy;
frame.origin.y = MIN(frame.origin.y, maxY);
}
else if (ifUp == 1)
{
frame.origin.y = MIN(maxY+touchPoint.y, maxY);
frame.origin.y = MAX(frame.origin.y, minY);
}
}
self.tabBar.frame = frame;
CGRect rt = self.slotterView.frame;
rt.origin.y = frame.origin.y + frame.size.height;
[self.slotterView setFrame:rt];
CGFloat alpha = (frame.origin.y-minY)/(maxY-minY);
[UIView animateWithDuration:0.05 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^
{
[self.maskView setAlpha:1-alpha];
} completion:^(BOOL finished) {
}];
if (recognizer.state == UIGestureRecognizerStateEnded)
{
self->lineY = 0;
self->fromTop = NO;
CGRect frame = self.tabBar.frame;
if (frame.origin.y <= minY+hm)
{
frame.origin.y = minY;
}
else
{
frame.origin.y = maxY;
}
interval = 0.25;
[UIView animateWithDuration:interval delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
[self.tabBar setFrame:frame];
CGRect rt = self.slotterView.frame;
rt.origin.y = self.tabBar.frame.origin.y+ self.tabBar.frame.size.height;
[self.slotterView setFrame:rt];
} completion:^(BOOL finished) {
}];
CGFloat alpha = (frame.origin.y-minY)/(maxY-minY);
[UIView animateWithDuration:0.05 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^
{
[self.maskView setAlpha:1-alpha];
} completion:^(BOOL finished) {
if (alpha == 1.0)
{
[self.view sendSubviewToBack:self.maskView];
}
}];
}
}
- (void)tabbarItemSelectedAction:(UIButton *)btn
{
NSInteger tag = labs(btn.tag+900);
UIViewController *controller = [self selectedViewController];
NSArray *array = [controller childViewControllers];
UIViewController *rootController = [array firstObject];
[self setSelectedIndex:tag];
[self mmiSetViewToActive];
}
- (void)setSelectedIndex:(NSUInteger)selectedIndex {
[super setSelectedIndex:selectedIndex];
if (self.slotterViewBlock)
{
for (NSInteger idx=0; idx<self.tabBar.items.count; idx++)
{
UIButton *button = (UIButton *)[self.tabBar viewWithTag:-900-idx];
[button setSelected:idx==selectedIndex?YES:NO];
}
}
}
4、如何使用呢?
在tabbarcontroller初始化的地方:通过对外暴露的插槽block返回你要下拉显示的更多的操作视图。
self.tabBarController = [[UITabViewController alloc] init];
[self.tabBarController addSlotterViewWithConfigBlock:^UIView * _Nullable
{
CGRect rt = CGRectMake(0, 0, KScreenSize.width, 100);
UIView *view = [[UIView alloc] initWithFrame:rt];
[view setBackgroundColor:[UIColor whiteColor]];
return view;
}];
self.window.rootViewController = self.tabBarController;
到此,此功能已经实现,同学们可以进行参考。