//
// TFScrollNavigationBar.h
// 2015506-02ScrollNavigation框架
//
// Created by appple on 15-5-6.
// Copyright (c) 2015年 appple. All rights reserved.
//
#import
//自定义导航栏
@class TFScrollNavigationBar;
@protocol TFScrollNavigationBar
@optional
/** 选中一个按钮后告诉代理 */
- (void)scrollNavigationBar:(TFScrollNavigationBar *)scrollNavigationBar DidSelectedButton : (UIButton *)button;
@end
@interface TFScrollNavigationBar : UIView
/** 代理 */
@property (nonatomic , weak) id delegate;
/** 标题scrollView */
@property (nonatomic , weak , readonly) UIScrollView * titleScrollView;
/** 右侧辅助按钮数组 */
@property (nonatomic , strong , readonly) NSArray * accessoryButtons;
/** 标题显示字体的大小 (根据字体计算标题长度 , 该参数为nil时默认跟随系统)*/
@property (nonatomic , strong) UIFont *title_font;
/** 标题显示文字及下划线的颜色 */
@property (nonatomic , strong) UIColor * titleSelectedColor;
/** 标题显示文字常规颜色 */
@property (nonatomic , strong) UIColor * titleNomalColor;
/** 当前选中的按钮 */
@property (nonatomic , weak , readonly) UIButton * selectedButton;
/** 标题按钮数组 */
@property (nonatomic , strong , readonly) NSMutableArray * titleButtons;
/** 根据传进来的控制器数组进行初始化 (本方法只为了拿到控制器的标题,不强引用控制器) */
- (instancetype)initWithControllers : (NSArray *) controllers;
/** 添加右侧辅助按钮 (数组形式 , 建议最多添加2个 ) */
- (void)addAccessoryButtons:(NSArray *) accessoryButtons;
/** 设置导航栏中的控制器标题按钮字体颜色及尺寸 */
- (void)setButtonTitleWithNomalColor : (UIColor *)nomalColor AndSelectedColor : (UIColor *)selectedColor AndTitleFont : (UIFont *)font;
/** 设置按钮点击事件 (外部调用该方法只为了选中传入的按钮) */
- (void)clickButton : (UIButton *)button;
/** 根据数组item的位置及比例滚动下划线 */
- (void)scrollUnderLineToItemAtIndex : (NSInteger)index WithScale : (CGFloat)scale;
@end
//
// TFScrollNavigationBar.m
// 2015506-02ScrollNavigation框架
//
// Created by appple on 15-5-6.
// Copyright (c) 2015年 appple. All rights reserved.
//
#import "TFScrollNavigationBar.h"
#define Default_Selected_Color [UIColor colorWithRed:0/255.0 green:122/255.0 blue:255/255.0 alpha:1]
#define Default_Nomal_Color [UIColor whiteColor]
#define DurationTime 0.3
#define titleFont [UIFont systemFontOfSize:13.0]
#define subTitleFont [UIFont systemFontOfSize:5.0]
#define commentFont [UIFont systemFontOfSize:5.0]
/** 按钮标题文字之间的间距 */
#define Button_Inset 20.0
@interface TFScrollNavigationBar ()
/** 标题scrollView (重写属性的目的 : 外界只读 , 内部可读可写) */
@property (nonatomic , weak , readwrite) UIScrollView * titleScrollView;
/** 右侧辅助按钮数组 (重写属性的目的 : 外界只读 , 内部可读可写) */
@property (nonatomic , strong , readwrite) NSArray * accessoryButtons;
/** 标题按钮数组 */
@property (nonatomic , strong , readwrite) NSMutableArray * titleButtons;
/** 底部滑动下划线 */
@property (nonatomic , weak) CALayer * underLine;
/** 当前选中的按钮 */
@property (nonatomic , weak , readwrite) UIButton * selectedButton;
@end
@implementation TFScrollNavigationBar
#pragma mark - 初始化方法
/** 根据传进来的控制器数组进行初始化 , 实际目的只是为了获取控制器的标题 */
- (instancetype)initWithControllers : (NSArray *) controllers
{
self = [super init];
if (self)
{
/** 添加子控制器的标题 */
[self addTitlesFromChildViewControllers: controllers];
/** 默认初始选中按钮为第一个 */
if (self.titleButtons.count > 0)
self.selectedButton = [self.titleButtons firstObject];
}
return self;
}
#pragma mark - 参数懒加载
/** 标题scrollView */
- (UIScrollView *)titleScrollView
{
if(_titleScrollView == nil)
{
UIScrollView * temp = [[UIScrollView alloc] init];
/** 隐藏contentScrollView的垂直与水平的滚动条 */
// temp.showsVerticalScrollIndicator = NO;
// temp.showsHorizontalScrollIndicator = NO;
[self addSubview:temp];
_titleScrollView = temp ;
}
return _titleScrollView;
}
/** 标题按钮数组 */
- (NSMutableArray *)titleButtons
{
if(_titleButtons == nil)
{
NSMutableArray * temp = [NSMutableArray array];
_titleButtons = temp ;
}
return _titleButtons;
}
/** 底部滑动下划线 */
- (CALayer *)underLine
{
if(_underLine == nil)
{
CALayer * temp = [[CALayer alloc] init];
/** 设置线段端点样式 */
temp.cornerRadius = 3.0;
temp.backgroundColor = Default_Selected_Color.CGColor;
[self.titleScrollView.layer addSublayer:temp];
_underLine = temp ;
}
return _underLine;
}
#pragma mark - 参数重写set,get方法
/** 重写setSelectedButton set方法 */
- (void)setSelectedButton:(UIButton *)selectedButton
{
_selectedButton = selectedButton;
_selectedButton.enabled = NO;
_selectedButton.userInteractionEnabled = NO;
/** 滚动选中的按钮到scrollView中心位置 */
[self scrollSelectedButtonToCenter]; //计算滚动的标题按钮滚动的距离
/*=================================================================
提示:
1. 在这里可以给控件写一个代理 , 然后将控件当前选中的按钮用代理的方式传出去.
2. 也可以用KVO方法监听选中的按钮.
3. 在本框架中我就用KVO来监听按钮的变化了,就不用代理了(可见RootViewController的初始化方法).
4. 如果要完美移植本控件, 建议可以给控件添加一个代理.
如下:
=================================================================*/
/** 选中一个按钮后告诉代理 */
if ([self.delegate respondsToSelector:@selector(scrollNavigationBar:DidSelectedButton:)])
{
[self.delegate scrollNavigationBar:self DidSelectedButton:self.selectedButton];
}
}
/** 重写setTitleNomalColor set方法 */
- (void)setTitleNomalColor:(UIColor *)titleNomalColor
{
_titleNomalColor = titleNomalColor;
for (UIButton * obj in self.titleButtons) {
[obj setTitleColor:titleNomalColor forState:UIControlStateNormal];
}
}
/** 重写setTitleSelectedColor set方法 */
- (void)setTitleSelectedColor:(UIColor *)titleSelectedColor
{
_titleSelectedColor = titleSelectedColor;
for (UIButton * obj in self.titleButtons) {
[obj setTitleColor:titleSelectedColor forState:UIControlStateDisabled];
[obj setTitleColor:titleSelectedColor forState:UIControlStateSelected];
}
self.underLine.backgroundColor = titleSelectedColor.CGColor;
}
/** 重写setTitleFont set方法 */
- (void)setTitleFont:(UIFont *)title_font
{
_title_font = title_font;
for (UIButton * obj in self.titleButtons) {
obj.titleLabel.font = titleFont;
}
}
#pragma mark - 重写系统方法
/** 重写设置背景颜色的方法 , 目的是为了让顶部状态栏的颜色跟导航栏的颜色一样. */
- (void)setBackgroundColor:(UIColor *)backgroundColor
{
[super setBackgroundColor:backgroundColor];
/** 如果这个自定义导航栏在屏幕最顶部时 , 则给系统的状态栏也染同色 */
[self colourStatusBarInSelfColorWhenSelfAtTop];
}
#pragma mark - 按钮点击事件
/** 设置按钮点击事件 */
- (void)clickButton : (UIButton *)button
{
if (self.selectedButton == button)
return;
self.selectedButton.enabled = YES;
self.selectedButton.userInteractionEnabled = YES;
self.selectedButton = button; //根据按钮所在的位置调整按钮所在控制器的view的位置
[self layoutUnderLine];
}
#pragma mark - 内部方法
/** 添加子控制器的标题 */
- (void)addTitlesFromChildViewControllers : (NSArray *)controllers
{
for (UIViewController * obj in controllers) {
if (obj.title.length < 1)
{
NSLog(@"[%s--第%d行]--[错误:没有设置控制器的标题!]",__func__,__LINE__);
return;
}
/** 根据标题添加按钮 */
[self addButtonWithTitle:obj.title];
}
}
/** 添加标题scrollView内的按钮 */
- (void)addButtonWithTitle : (NSString *)title
{
UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
/** 设置按钮标题 */
[button setTitle:title forState:UIControlStateNormal];
/** 设置按钮默认正常状态颜色 */
[button setTitleColor:Default_Nomal_Color forState:UIControlStateNormal];
/** 设置按钮默认选中颜色 */
[button setTitleColor:Default_Selected_Color forState:UIControlStateDisabled];
[button setTitleColor:Default_Selected_Color forState:UIControlStateSelected];
/** 设置按钮点击事件 */
[button addTarget:self action:@selector(clickButton:) forControlEvents:UIControlEventTouchUpInside];
/** 将按钮添加到scrollView里面 */
[self.titleScrollView addSubview:button]; //将每个标题按钮添加到自定义的scrollView里面
[self.titleButtons addObject:button]; //添加到按钮数组中进行管理
}
/** 如果这个自定义导航栏在屏幕最顶部时 , 则给系统的状态栏也染同色 */
- (void)colourStatusBarInSelfColorWhenSelfAtTop
{
/** 如果本控件的frame不为空值 */
if(![[NSValue valueWithCGRect:self.frame] isEqualToValue:[NSValue valueWithCGRect:CGRectZero]])
{
CGRect rect = [self convertRect:self.bounds toView:[UIApplication sharedApplication].keyWindow];
/** 判断本控件是否正好在状态栏下面 */
if (rect.origin.y <= 20.0)
{
/** 设置顶部状态栏的背景颜色跟导航栏一样 */
[[UIApplication sharedApplication] setValue:self.backgroundColor forKeyPath:@"statusBar.backgroundColor"];
}
}
}
#pragma mark - 内部 Frame 计算方法
/** 系统自动调用布局方法 */
- (void)layoutSubviews
{
[super layoutSubviews];
/** 如果这个自定义导航栏在屏幕最顶部时 , 则给系统的状态栏也染同色 */
[self colourStatusBarInSelfColorWhenSelfAtTop];
/** 布局辅助按钮 */
[self layoutAccessoryButtons];
/** 布局标题scrollView */
[self layoutTitleScrollView];
/** 布局下划线layer */
[self layoutUnderLine];
}
/** 布局辅助按钮 */
- (void)layoutAccessoryButtons
{
if (self.accessoryButtons.count < 1)
return;
CGFloat height = self.bounds.size.height;
CGFloat width = height;
CGFloat x = self.bounds.size.width - width;
CGFloat y = 0;
for (UIButton * obj in self.accessoryButtons) {
obj.frame = CGRectMake(x, y, width, height);
x = x - width;
}
}
/** 布局标题scrollView */
- (void)layoutTitleScrollView
{
CGFloat x = 0;
CGFloat y = 0;
CGFloat height = self.bounds.size.height;
/** 标题scrollView的宽度 根据辅助按钮的位置进行判断 */
CGFloat width = 0;
if (self.accessoryButtons.count > 0)
{
UIButton * button = [self.accessoryButtons lastObject];
width = CGRectGetMinX(button.frame);
}
else
{
width = self.bounds.size.width;
}
self.titleScrollView.frame = CGRectMake(x, y, width, height);
/** 设置标题ScrollView的内容Size */
CGFloat sizeHeight = height;
CGFloat sizeWidth = [self layoutTitleButtons];
self.titleScrollView.contentSize = CGSizeMake(sizeWidth, sizeHeight);
}
/** 布局标题ScrollView内部的按钮 , 并返回最后一个按钮的最大X值*/
- (CGFloat)layoutTitleButtons
{
for( NSInteger i = 0 ; i < self.titleButtons.count ; i++)
{
UIButton * tempButton = self.titleButtons[i];
/** 计算第一个按钮的frame */
if (i == 0)
{
CGFloat x = 0;
CGFloat y = 0;
CGSize size = [self sizeWithButton:tempButton];
tempButton.frame = CGRectMake(x, y, size.width, size.height);
continue;
}
/** 根据前一个按钮计算后面按钮的位置 */
UIButton * lastButton = self.titleButtons[i-1];
CGFloat x = CGRectGetMaxX(lastButton.frame);
CGFloat y = lastButton.frame.origin.y;
CGSize size = [self sizeWithButton:tempButton];
tempButton.frame = CGRectMake(x, y, size.width, size.height);
}
/** 获取最后一个按钮 */
UIButton * button = [self.titleButtons lastObject];
return CGRectGetMaxX(button.frame);
}
/** 布局下划线layer */
- (void)layoutUnderLine
{
self.underLine.frame = CGRectMake(self.selectedButton.frame.origin.x, self.bounds.size.height - 2, self.selectedButton.frame.size.width, 2);
}
#pragma mark - 内部 自定义 计算方法
/** 根据按钮标题字体属性计算每个button的大小 */
- (CGSize)sizeWithButton : (UIButton *)button
{
NSMutableDictionary * attributes = [NSMutableDictionary dictionary];
UIFont * font = button.titleLabel.font;
/** 设置按钮标题的字体属性 */
if (self.title_font)
{
font = self.title_font;
[button.titleLabel setFont:font];
}
attributes[NSFontAttributeName] = button.titleLabel.font;
/** 计算按钮标题的size */
CGRect rect = [button.titleLabel.text boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, self.titleScrollView.bounds.size.height ) options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
CGSize size = CGSizeMake(rect.size.width + Button_Inset, self.titleScrollView.bounds.size.height);
/** 设置按钮内部label的大小 */
button.titleLabel.frame = CGRectMake(0, 0, size.width, size.height);
/** 设置文字居中 */
button.titleLabel.textAlignment = NSTextAlignmentCenter;
return size;
}
/** 滚动选中的按钮到scrollView中心位置 */
- (void)scrollSelectedButtonToCenter
{
/** 如果titleView的内容长度小于frame , 就不滚动 */
if (self.titleScrollView.contentSize.width <= self.titleScrollView.bounds.size.width)
return;
CGFloat scrollViewCenterX = self.titleScrollView.center.x;
CGFloat selectedButtonCenterX = self.selectedButton.center.x;
CGFloat scrollOffset = selectedButtonCenterX - scrollViewCenterX ;
/** 如果滚动会超过起始位置 , 则偏移置0 */
if (scrollOffset < 0 )
scrollOffset = 0;
/** 如果滚动会超过最末位置 , 则偏移置为最末 */
else if (scrollOffset > (self.titleScrollView.contentSize.width - self.titleScrollView.bounds.size.width))
scrollOffset = self.titleScrollView.contentSize.width - self.titleScrollView.bounds.size.width;
/** 以动画形式滚动 */
[UIView animateWithDuration:DurationTime animations:^{
self.titleScrollView.contentOffset = CGPointMake(scrollOffset, self.titleScrollView.contentOffset.y);
} completion:^(BOOL finished) {
}];
}
/** 滚动下划线的时候根据滚动位置自动选中对应的button */
- (void)selectedButtonWhenUnderLineScroll
{
for (UIButton * obj in self.titleButtons) {
//if (fabs(self.underLine.frame.origin.x - obj.frame.origin.x) <= obj.frame.size.width * 0.2)
if (self.underLine.frame.origin.x == obj.frame.origin.x)
{
[self clickButton:obj];
}
}
}
#pragma mark - 外部调用方法
/** 添加右侧辅助按钮 (数组形式 , 建议最多添加2个 ) */
- (void)addAccessoryButtons:(NSArray *) accessoryButtons
{
if (accessoryButtons.count<1)
{
NSLog(@"[%s--第%d行]--[警告:按钮数组为空,请确认是否传入空数组!]",__func__,__LINE__);
self.accessoryButtons = nil;
return;
}
if (accessoryButtons.count > 3)
{
NSLog(@"[%s--第%d行]--[警告:您传入的辅助按钮数量超过3个,建议最多只添加2个!]",__func__,__LINE__);
}
self.accessoryButtons = nil;
NSMutableArray * array = [NSMutableArray array];
for (UIButton * obj in accessoryButtons) {
if (![obj isKindOfClass:[UIButton class]])
{
NSLog(@"[%s--第%d行]--[错误:请添加UIButton类型的按钮!如果需要自定义,可修改此处的代码!]",__func__,__LINE__);
return;
}
[array addObject:obj];
[self addSubview:obj];
}
self.accessoryButtons = array;
}
/** 设置导航栏中的控制器标题按钮字体颜色及尺寸 */
- (void)setButtonTitleWithNomalColor : (UIColor *)nomalColor AndSelectedColor : (UIColor *)selectedColor AndTitleFont : (UIFont *)font
{
if (nomalColor)
self.titleNomalColor = nomalColor;
if (selectedColor)
self.titleSelectedColor = selectedColor;
if (font)
self.title_font = font;
}
/** 根据数组item的位置及比例滚动下划线 */
- (void)scrollUnderLineToItemAtIndex : (NSInteger)index WithScale : (CGFloat)scale
{
/** 参数边界值判断 */
if (index >= self.titleButtons.count || index < 0 )
{
NSLog(@"[%s--第%d行]--[错误:输入的index有误!]",__func__,__LINE__);
return;
}
UIButton * tempButton = self.titleButtons[index];
CGRect tempFrame = tempButton.frame;
self.underLine.frame = CGRectMake(tempFrame.origin.x + tempFrame.size.width * scale , self.bounds.size.height - 2, self.underLine.frame.size.width, 2);
}
@end