iOS_scrollView title and line view, 滚动的标题和线

废话不多说, 直接上代码

//
//  MOTitleLineView.h
//  MOTitleLineView
//
//  Created by 莫小言 on 15/12/17.
//  Copyright © 2015年 莫小言. All rights reserved.
//

#import <UIKit/UIKit.h>

// title 对齐type
typedef NS_ENUM(NSUInteger, MOTitleLineViewType) {
  MOTitleLineViewTypeLeft,
  MOTitleLineViewTypeCenter
};

//声明协议
@protocol MOTitleLineViewDelegate <NSObject>
// 标题按钮点击事件 从0开始
- (void)selectedIndex:(NSUInteger)index;
@end

@interface MOTitleLineView : UIView

@property(nonatomic, strong) NSArray *titles; //标题数组
@property(nonatomic, assign) NSUInteger currentIndex; //当前选中的按钮 从0开始
@property(nonatomic, assign) NSInteger buttonMargin;  //button左右边距
@property(nonatomic, assign) NSInteger lineBottomMargin;  //line跟底部间距
@property(nonatomic, assign) NSInteger lineMargin;  //line跟 button width 的缩进距离
@property(nonatomic, weak) id<MOTitleLineViewDelegate> delegate;

- (instancetype)initWithType:(MOTitleLineViewType)type normalColor:(UIColor *) normalColor selectedColor:(UIColor *)selectedColor font:(UIFont *)font;

+ (MOTitleLineView *)titleAndLineViewWithType:(MOTitleLineViewType)type normalColor:(UIColor *) normalColor selectedColor:(UIColor *)selectedColor font:(UIFont *)font;

// 外界需要改变当前scrollView的frame
- (void)updateSize:(CGSize)size;

// 外界需要UI改变 不调用代理方法
- (void)changeIndex:(NSInteger)index;

@end
//
//  MOTitleLineView.m
//  MOTitleLineView
//
//  Created by 莫小言 on 15/12/17.
//  Copyright © 2015年 莫小言. All rights reserved.
//

#import "MOTitleLineView.h"
#define kBaseTag (10000)
#define kSelfHeight (self.frame.size.height)
#define kSelfWidth (self.frame.size.width)
#define kCenterTypeBtnWidth (kSelfWidth / self.titles.count)

@interface MOTitleLineView () <UIScrollViewDelegate>
@property(nonatomic, assign) MOTitleLineViewType type;
@property(nonatomic, strong) UIScrollView *scrollView;
@property(nonatomic, strong) UIView *line;
@property(nonatomic, strong) UIColor *normalColor;
@property(nonatomic, strong) UIColor *selectedColor;
@property(nonatomic, strong) UIFont *font;
@end

@implementation MOTitleLineView

+ (MOTitleLineView *)titleAndLineViewWithType:(MOTitleLineViewType)type normalColor:(UIColor *)normalColor selectedColor:(UIColor *)selectedColor font:(UIFont *)font {
  return [[MOTitleLineView alloc] initWithType:type normalColor:normalColor selectedColor:selectedColor font:font];
}

- (instancetype)initWithType:(MOTitleLineViewType)type normalColor:(UIColor *)normalColor selectedColor:(UIColor *)selectedColor font:(UIFont *)font {
  self = [super initWithFrame:CGRectZero];
  if (self) {
    self.type = type;
    self.normalColor = normalColor;
    self.selectedColor = selectedColor;
    self.font = font;
    _currentIndex = 0;
    [self addSubview:self.scrollView];
  }
  return self;
}

- (void)setTitles:(NSArray *)titlesArray {
  _titles = titlesArray;
  [self createTitleButton];
}

- (void)createTitleButton {
  // 移除scrollView上的buttons
  while (self.scrollView.subviews.count) {
    [self.scrollView.subviews.lastObject removeFromSuperview];
  }
  CGFloat scrollWidth = 0;
  if (self.type == MOTitleLineViewTypeCenter) {
    self.scrollView.scrollEnabled = NO;
    self.scrollView.contentSize = self.scrollView.frame.size;
  }
  
  for (int i = 0; i < self.titles.count; i++) {
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
    [btn setTitle:self.titles[i] forState:UIControlStateNormal];
    btn.titleLabel.font = self.font;
    [btn setTitleColor:self.normalColor forState:UIControlStateNormal];
    [btn setTitleColor:self.selectedColor forState:UIControlStateSelected];
    btn.tag = i + kBaseTag;
    [btn addTarget:self action:@selector(clickButton:) forControlEvents:UIControlEventTouchUpInside];
    
    if (self.type == MOTitleLineViewTypeCenter) {
      btn.frame = CGRectMake(i * kCenterTypeBtnWidth, 0, kCenterTypeBtnWidth, kSelfHeight - 2);
    } else {
      CGFloat width = [self widthOfLineWithTitle:self.titles[i] font:self.font] + 2*self.buttonMargin;
      btn.frame = CGRectMake(scrollWidth, 0, width, kSelfHeight - 2);
      scrollWidth += width; //所有标题自适应宽度和
    }
    [self.scrollView addSubview:btn];
  }
  self.scrollView.contentSize = CGSizeMake(scrollWidth, kSelfHeight);
  [self.scrollView addSubview:self.line];
  //设置默认选中的按钮
  UIButton *btn = [self.scrollView viewWithTag:_currentIndex + kBaseTag];
  btn.selected = !btn.selected;
  [self changedOffsentWithBtn:btn];
  
  //代理执行协议方法
  if (self.delegate && [self.delegate respondsToSelector:@selector(selectedIndex:)]) {
    [self.delegate selectedIndex:_currentIndex];
  }
}

- (void)changedOffsentWithBtn:(UIButton *)btn {
  CGFloat width;
  if (self.type == MOTitleLineViewTypeCenter) {
    width = kSelfWidth / self.titles.count;
  } else {
    width = [self widthOfLineWithTitle:[btn titleForState:UIControlStateNormal] font:self.font];
  }
  //改变line的位置
  //根据当前btn中心,算出line的x
  CGFloat x = btn.center.x - width / 2;
  [UIView animateWithDuration:0.2 animations:^{
    self.line.frame = CGRectMake(x + self.lineMargin, kSelfHeight - 2 - self.lineBottomMargin, width - 2*self.lineMargin, 2);
  }];
  
  //判断scrollView 是否需要滑动
  CGFloat startX = CGRectGetMinX(btn.frame);
  CGFloat endX = CGRectGetMaxX(btn.frame);
  if (startX < self.scrollView.contentOffset.x) {
    if (btn.tag - kBaseTag != 0) {
      UIButton *formerBtn = [self.scrollView viewWithTag:btn.tag - 1];
      [self.scrollView setContentOffset:CGPointMake(CGRectGetMinX(formerBtn.frame), 0) animated:YES];
    } else {
      [self.scrollView setContentOffset:CGPointMake(0, 0) animated:YES];
    }
  }
  CGFloat maxOffset = self.scrollView.contentSize.width - self.scrollView.frame.size.width;
  if (endX > self.scrollView.contentOffset.x + self.scrollView.frame.size.width) {
    if (startX > maxOffset) {
      [self.scrollView setContentOffset:CGPointMake(maxOffset, 0) animated:YES];
    } else {
      [self.scrollView setContentOffset:CGPointMake(startX - 10, 0) animated:YES];
    }
  }
}

#pragma mark -- left button的点击方法
- (void)clickButton:(UIButton *)sender {
  //如果点击的是当前选中的按钮, 跳出该方法
  if (sender.tag == _currentIndex + kBaseTag) {
    return;
  }
  // 取消上一个选中
  UIButton *formerBtn = [self.scrollView viewWithTag:_currentIndex + kBaseTag];
  formerBtn.selected = !formerBtn.selected;
  
  // 选中当前点击的btn
  _currentIndex = sender.tag - kBaseTag;
  sender.selected = !sender.selected;
  [self changedOffsentWithBtn:sender];
  
  //代理执行协议方法
  if (self.delegate && [self.delegate respondsToSelector:@selector(selectedIndex:)]) {
    [self.delegate selectedIndex:_currentIndex];
  }
}

#pragma mark - 外界需要改变当前view的frame
- (void)updateSize:(CGSize)size {
  self.scrollView.frame = CGRectMake(0, 0, size.width, size.height);
  [self.scrollView layoutIfNeeded];
  UIButton *btn = [self.scrollView viewWithTag:_currentIndex + kBaseTag];
  if ([btn isKindOfClass:UIButton.class]) {
    [self changedOffsentWithBtn:btn];
  }
}

#pragma mark - 外界需要UI改变 不调用代理方法
- (void)changeIndex:(NSInteger)index {
  UIButton *btn = [self.scrollView viewWithTag:index + kBaseTag];
  if (!btn) {
    return;
  }
  btn.selected = !btn.selected;
  
  //获得上一个按钮
  UIButton *formerBtn = [self.scrollView viewWithTag:_currentIndex + kBaseTag];
  formerBtn.selected = !formerBtn.selected;
  
  //当前按钮 指定为 点击的按钮
  _currentIndex = index;
  
  [self changedOffsentWithBtn:btn];
}

#pragma mark -- 自适应宽度
- (CGFloat)widthOfLineWithTitle:(NSString *)title font:(UIFont *)font {
  //line的显示范围
  CGSize size = CGSizeMake(MAXFLOAT, 2);
  //设置要显示的文本样式
  NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil];
  //根据文本内容,文本样式 计算line的frame
  CGRect frame = [title boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:dic context:nil];
  //返回line的宽度
  return frame.size.width;
}

- (UIScrollView *)scrollView {
  if (!_scrollView) {
    _scrollView = [[UIScrollView alloc] init];
    _scrollView.showsHorizontalScrollIndicator = NO;
    _scrollView.delegate = self;
    [_scrollView addSubview:self.line];
  }
  return _scrollView;
}

- (UIView *)line {
  if (!_line) {
    _line = [[UIView alloc] init];
    _line.backgroundColor = self.selectedColor;
  }
  return _line;
}

@end

使用例子:

//
//  ViewController.m
//  MOTitleAndLineView
//
//  Created by 莫晓卉 on 2018/9/30.
//  Copyright © 2018年 moxiaohui. All rights reserved.
//

#import "ViewController.h"
#import "MOTitleLineView.h"

@interface ViewController () <MOTitleLineViewDelegate>
@end

@implementation ViewController {
  MOTitleLineView *_titleView;
}

- (void)viewDidLoad {
  [super viewDidLoad];

  _titleView = [MOTitleLineView titleAndLineViewWithType:MOTitleLineViewTypeLeft normalColor:[UIColor lightGrayColor] selectedColor:[UIColor blackColor] font:[UIFont boldSystemFontOfSize:14]];
  _titleView.frame = CGRectMake(12, 100, self.view.frame.size.width - 24, 50);
  _titleView.delegate = self;
  _titleView.buttonMargin = 10;
  _titleView.lineBottomMargin = 10;
  _titleView.lineMargin = 4;
  [self.view addSubview:_titleView];
  _titleView.backgroundColor = [UIColor redColor];
  
  _titleView.titles = @[@"swift", @"Objective-C", @"iOS", @"Android", @"Windows", @"macOS"];
  
  _titleView.frame = CGRectMake(12, 100, self.view.frame.size.width - 24, 50);
  // titleView的frame修改后, 需要更新scrollView的frame
  [_titleView updateSize:_titleView.frame.size];
}

#pragma mark - MOTitleLineViewDelegate
- (void)selectedIndex:(NSUInteger)index {
  NSLog(@"selectedIndex:%lu", (unsigned long)index);
}

@end

运行结果:

github Demo 地址  titleAndLineView 方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫小言mo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值