仿网易新闻Navbar

效果图:


网易导航条.gif

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

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

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

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

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

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

@property(nonatomic,weak)id <LXSegmentControlDelegate> 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滚动类型

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 175,791评论 25 709
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 14,258评论 4 61
  • 雨一直下,从昨晚至今天,早晨上班路上,心里一阵惊喜,雨伞终于派上用场了,要是在南方,雨伞是出门的必备品,因为多数时...
    女公子99阅读 1,839评论 1 1
  • 指尖微凉 下雨了 天空灰霾 抬头时总有一丝伤感 而我却喜欢在这种气氛中提笔让思绪乱飞 如果是在以前 涛子现在一定在...
    王十一_1a26阅读 1,774评论 0 0