效果圖:
在項(xiàng)目中有類似的需求齿梁,仿網(wǎng)易新聞Navbar主要借助UIScrollview與 addChildViewController 實(shí)現(xiàn)這個(gè)效果。
在滾動(dòng)時(shí)候有可能會(huì)有兩個(gè)需求
- 點(diǎn)擊item 滾動(dòng)到中間
- 在超出屏幕時(shí)點(diǎn)擊才發(fā)生滾動(dòng)
所以在自定義SegmentControl的時(shí)候 定義一個(gè)枚舉:
typedef enum : NSUInteger {
LXSegmentedControlTypeCenterScroll, // 中心滾動(dòng)風(fēng)格
LXSegmentedControlTypeEndScroll, // 超出屏幕滾動(dòng)風(fēng)格
} LXSegmentedControlScrollType; // 默認(rèn)為滾動(dòng)風(fēng)格
第一步 定義一個(gè)LXSegmentControl 繼承于UIScrollview肮蛹。接口中暴露以下屬性勺择,有類方法構(gòu)造以及對(duì)象方法構(gòu)造,滾動(dòng)類型選擇伦忠,接口方法中已經(jīng)傳入代理UIViewController省核。
@protocol LXSegmentControlDelegate<NSObject>
-(void)LXSegmentControl:(LXSegmentControl *)segmentControl didSelectBtnAtIndex:(NSInteger)index;
@end;
@interface LXSegmentControl : UIScrollView
//對(duì)象方法創(chuàng)建 LXSegmentControl
-(instancetype)initWithFrame:(CGRect)frame delegate:(id <LXSegmentControlDelegate>)delegate titleArr:(NSArray *)titleArr;
//類方法創(chuàng)建 LXSegmentControl
+(instancetype)segmentControlWithFrame:(CGRect)frame delegate:(id <LXSegmentControlDelegate>)delegate titleArr:(NSArray *)titleArr;
@property(nonatomic,weak)id <LXSegmentControlDelegate> SeDelegate;
@property(nonatomic,assign)LXSegmentedControlScrollType scrollType;
///** 滾動(dòng)Conrolller的時(shí)候 SegmentControl需要做的處理 */
- (void)titleBtnSelectedWithScrollView:(UIScrollView *)scrollView;
@end
第二部 根據(jù)傳入的title 數(shù)組創(chuàng)建button 然后設(shè)置UIScrollview的內(nèi)容大小。button的大小通過API計(jì)算出字符串的長度昆码,然后設(shè)置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;
//計(jì)算內(nèi)容的size
CGSize buttonSize =[self sizeWithText:_title_Arr[i] font:LXFont(btn_fondOfSize) maxSize:CGSizeMake(MAXFLOAT, button_H)];
//計(jì)算內(nèi)容的寬度
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];
//計(jì)算每個(gè)button的 X 值
button_X = button_X + button_W;
//點(diǎn)擊事件
[_title_btn addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
//默認(rèn)選中第0 個(gè)button
if (i == 0) {
[self buttonAction:_title_btn];
}
//存入所有的 title_btn
[self.titleBtn_mArr addObject:_title_btn];
[self addSubview:_title_btn];
}
//計(jì)算 scrollview 的寬度
UIButton *lastButton = self.titleBtn_mArr.lastObject;
CGFloat scrollViewWidth = CGRectGetMaxX(lastButton.frame);
self.contentSize = CGSizeMake(scrollViewWidth, self.height);
最關(guān)鍵的處理在于 處理button 的點(diǎn)擊方法 在點(diǎn)擊時(shí)候處理contentoffset,是在點(diǎn)擊屏幕最后一個(gè)按鈕然后滾動(dòng)UIScrollview赋咽,還是選擇居中模式旧噪。
#pragma mark - - - 按鈕的點(diǎn)擊事件
- (void)buttonAction:(UIButton *)sender
{
[self titleBtnSelectededCenter:sender];
// 2、代理方法實(shí)現(xiàn)
NSInteger index = sender.tag;
if ([self.SeDelegate respondsToSelector:@selector(LXSegmentControl:didSelectBtnAtIndex:)]) {
[self.SeDelegate LXSegmentControl:self didSelectBtnAtIndex:index];
}
//3 脓匿、 改變指示器的位置
[self titleBtnSelected:sender];
}
/** 滾動(dòng)標(biāo)題選中居中 */
- (void)titleBtnSelectededCenter:(UIButton *)centerBtn {
switch (self.scrollType) {
case LXSegmentedControlTypeCenterScroll:
[self centerScroll:centerBtn];
break;
case LXSegmentedControlTypeEndScroll:
[self endScroll:centerBtn];
default:
break;
}
}
-(void)centerScroll:(UIButton *)centerBtn
{
//計(jì)算偏移量
CGFloat offsetX = centerBtn.center.x - Device_Width * 0.5;
if (offsetX < 0) offsetX = 0;
// 獲取最大滾動(dòng)范圍
CGFloat maxOffsetX = self.contentSize.width - Device_Width;
if (offsetX > maxOffsetX) offsetX = maxOffsetX;
// 滾動(dòng)標(biāo)題滾動(dòng)條
[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];
}
接口中需要暴露 一個(gè)方法當(dāng)我們滾動(dòng)子控制器的時(shí)候item 需要作出的改變
/** 標(biāo)題選中顏色改變以及指示器位置變化 */
- (void)titleBtnSelectedWithScrollView:(UIScrollView *)scrollView {
// 1淘钟、計(jì)算滾動(dòng)到哪一頁
NSInteger index = scrollView.contentOffset.x / scrollView.frame.size.width;
// 2、把對(duì)應(yīng)的標(biāo)題選中
UIButton *selectedBtn = self.titleBtn_mArr[index];
// 3陪毡、滾動(dòng)時(shí)米母,改變標(biāo)題選中
[self titleBtnSelected:selectedBtn];
}
接下來是ViewControll中的處理
實(shí)現(xiàn)LXSegmentControl的代理
-(void)LXSegmentControl:(LXSegmentControl *)segmentControl didSelectBtnAtIndex:(NSInteger)index
{
// 1 計(jì)算滾動(dòng)的位置
CGFloat offsetX = index * self.view.frame.size.width;
self.mainScrollView.contentOffset = CGPointMake(offsetX, 0);
// 2.給對(duì)應(yīng)位置添加對(duì)應(yīng)子控制器
[self showVc:index];
}
當(dāng)我們?cè)谔砑幼右晥D的view的時(shí)候判斷是不是該控制器的視圖已經(jīng)進(jìn)行了懶加載
vc.isViewLoaded
// 顯示控制器的view
- (void)showVc:(NSInteger)index {
CGFloat offsetX = index * self.view.frame.size.width;
UIViewController *vc = self.childViewControllers[index];
// 判斷控制器的view有沒有加載過,如果已經(jīng)加載過,就不需要加載
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);
}
最后 處理在滑動(dòng)切換子控制器的時(shí)候需要LXSegmentControl需要做的處理
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
// 計(jì)算滾動(dòng)到哪一頁
NSInteger index = scrollView.contentOffset.x / scrollView.frame.size.width;
// 1.添加子控制器view
[self showVc:index];
// 2.把對(duì)應(yīng)的標(biāo)題選中 接口中已經(jīng)暴露
[self.segmentControl titleBtnSelectedWithScrollView:scrollView];
}
demo地址 兩種Navbar滾動(dòng)類型