仿网易新闻Navbar

效果图:


仿网易新闻Navbar_第1张图片
网易导航条.gif

在项目中有类似的需求,仿网易新闻Navbar主要借助UIScrollview与 addChildViewController 实现这个效果。
在滚动时候有可能会有两个需求

  • 点击item 滚动到中间
  • 在超出屏幕时点击才发生滚动

所以在自定义SegmentControl的时候 定义一个枚举:

typedef enum : NSUInteger {
    LXSegmentedControlTypeCenterScroll, // 中心滚动风格
    LXSegmentedControlTypeEndScroll, // 超出屏幕滚动风格
} LXSegmentedControlScrollType;  // 默认为滚动风格

第一步 定义一个LXSegmentControl 继承于UIScrollview。接口中暴露以下属性,有类方法构造以及对象方法构造,滚动类型选择,接口方法中已经传入代理UIViewController。

@protocol LXSegmentControlDelegate
-(void)LXSegmentControl:(LXSegmentControl *)segmentControl didSelectBtnAtIndex:(NSInteger)index;
@end;
@interface LXSegmentControl : UIScrollView
//对象方法创建 LXSegmentControl
-(instancetype)initWithFrame:(CGRect)frame delegate:(id )delegate titleArr:(NSArray *)titleArr;
//类方法创建 LXSegmentControl
+(instancetype)segmentControlWithFrame:(CGRect)frame delegate:(id )delegate titleArr:(NSArray *)titleArr;

@property(nonatomic,weak)id  SeDelegate;

@property(nonatomic,assign)LXSegmentedControlScrollType scrollType;

///** 滚动Conrolller的时候 SegmentControl需要做的处理 */
- (void)titleBtnSelectedWithScrollView:(UIScrollView *)scrollView;

@end

第二部 根据传入的title 数组创建button 然后设置UIScrollview的内容大小。button的大小通过API计算出字符串的长度,然后设置margin。

for (NSUInteger i  = 0; i <_title_Arr.count; i++) {
        
        self.title_btn =[UIButton buttonWithType:UIButtonTypeCustom];
        _title_btn.titleLabel.font = LXFont(btn_fondOfSize);
        _title_btn.tag = i;
        
        //计算内容的size
        CGSize buttonSize =[self sizeWithText:_title_Arr[i] font:LXFont(btn_fondOfSize) maxSize:CGSizeMake(MAXFLOAT, button_H)];
        //计算内容的宽度
        CGFloat button_W = 2 *btn_Margin + buttonSize.width;
        _title_btn.frame = CGRectMake(button_X, button_Y, button_W, button_H);
        [_title_btn setTitle:_title_Arr[i] forState:UIControlStateNormal];
        [_title_btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
        [_title_btn setTitleColor:[UIColor redColor] forState:UIControlStateSelected];
        
        //计算每个button的 X 值
        button_X = button_X + button_W;
        //点击事件
        [_title_btn addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
        
        //默认选中第0 个button
        if (i == 0) {
            [self buttonAction:_title_btn];
        }
        //存入所有的 title_btn
        [self.titleBtn_mArr addObject:_title_btn];
        [self addSubview:_title_btn];
        
    }
    
    //计算 scrollview 的宽度
    
    UIButton *lastButton = self.titleBtn_mArr.lastObject;
    CGFloat scrollViewWidth = CGRectGetMaxX(lastButton.frame);
    self.contentSize = CGSizeMake(scrollViewWidth, self.height);

最关键的处理在于 处理button 的点击方法 在点击时候处理contentoffset,是在点击屏幕最后一个按钮然后滚动UIScrollview,还是选择居中模式。

#pragma mark - - - 按钮的点击事件
- (void)buttonAction:(UIButton *)sender
{
    [self titleBtnSelectededCenter:sender];
    
    // 2、代理方法实现
    NSInteger index = sender.tag;
    
    if ([self.SeDelegate respondsToSelector:@selector(LXSegmentControl:didSelectBtnAtIndex:)]) {
        
        [self.SeDelegate LXSegmentControl:self didSelectBtnAtIndex:index];
    }
    
    //3 、 改变指示器的位置
    [self titleBtnSelected:sender];
}

/** 滚动标题选中居中 */
- (void)titleBtnSelectededCenter:(UIButton *)centerBtn {
  
    
    switch (self.scrollType) {
        case LXSegmentedControlTypeCenterScroll:
            
            [self centerScroll:centerBtn];
            
            break;
        case LXSegmentedControlTypeEndScroll:
            
            [self endScroll:centerBtn];
            
        default:
            break;
    }
}
-(void)centerScroll:(UIButton *)centerBtn
{
     //计算偏移量
        CGFloat offsetX = centerBtn.center.x - Device_Width * 0.5;
    
        if (offsetX < 0) offsetX = 0;
    
        // 获取最大滚动范围
        CGFloat maxOffsetX = self.contentSize.width - Device_Width;
    
        if (offsetX > maxOffsetX) offsetX = maxOffsetX;
    
        // 滚动标题滚动条
        [self setContentOffset:CGPointMake(offsetX, 0) animated:YES];
}

-(void)endScroll:(UIButton *)centerBtn
{
    CGFloat offsetX;
    
    if (CGRectGetMaxX(centerBtn.frame) >= Device_Width) {
        
        offsetX = CGRectGetMaxX(centerBtn.frame) - Device_Width;
        
        if (centerBtn.tag <[_title_Arr count]-1) {
            offsetX = offsetX + centerBtn.frame.size.width;
        }
    }else
    {
        offsetX = 0 ;
    }
    [self setContentOffset:CGPointMake(offsetX, 0) animated:YES];

}

接口中需要暴露 一个方法当我们滚动子控制器的时候item 需要作出的改变

/** 标题选中颜色改变以及指示器位置变化 */
- (void)titleBtnSelectedWithScrollView:(UIScrollView *)scrollView {
    // 1、计算滚动到哪一页
    NSInteger index = scrollView.contentOffset.x / scrollView.frame.size.width;
    
    // 2、把对应的标题选中
    UIButton *selectedBtn = self.titleBtn_mArr[index];
    
    // 3、滚动时,改变标题选中
    [self titleBtnSelected:selectedBtn];
}

接下来是ViewControll中的处理

实现LXSegmentControl的代理

-(void)LXSegmentControl:(LXSegmentControl *)segmentControl didSelectBtnAtIndex:(NSInteger)index
{
    // 1 计算滚动的位置
    CGFloat offsetX = index * self.view.frame.size.width;
    self.mainScrollView.contentOffset = CGPointMake(offsetX, 0);
    
    // 2.给对应位置添加对应子控制器
    [self showVc:index];
}

当我们在添加子视图的view的时候判断是不是该控制器的视图已经进行了懒加载

vc.isViewLoaded

// 显示控制器的view
- (void)showVc:(NSInteger)index {
    
    CGFloat offsetX = index * self.view.frame.size.width;
    
    UIViewController *vc = self.childViewControllers[index];
    
    // 判断控制器的view有没有加载过,如果已经加载过,就不需要加载
    if (vc.isViewLoaded) return;
    vc.view.backgroundColor =  LBRandomColor;
    [self.mainScrollView addSubview:vc.view];
    vc.view.frame = CGRectMake(offsetX, 0, self.view.frame.size.width, self.view.frame.size.height);
}

最后 处理在滑动切换子控制器的时候需要LXSegmentControl需要做的处理

#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
    
    // 计算滚动到哪一页
    NSInteger index = scrollView.contentOffset.x / scrollView.frame.size.width;
    
    // 1.添加子控制器view
    [self showVc:index];
    
    // 2.把对应的标题选中 接口中已经暴露
    [self.segmentControl titleBtnSelectedWithScrollView:scrollView];
}

demo地址 两种Navbar滚动类型

你可能感兴趣的:(仿网易新闻Navbar)