iOS custom tab page

In iOS, the UISegmentedControl control can be used to represent the Tab page, but its style is difficult to modify, and we usually customize the Tab page.

1. Customize the Tab page

Here we first define UKTabItemViewthe tabs used to display it.

// 标签页代理
@protocol UKTabItemViewDelegate <NSObject>

- (void)onTabItemViewSelected:(UKTabItemView *)tabItemView;

@end

@interface UKTabItemView : UIView

@property(nonatomic, weak) id<UKTabItemViewDelegate> delegate;

// 设置标签页标题
- (void)setText:(NSString *)text;
// 设置标签页状态
- (void)setSelected:(BOOL)selected;

@end

@interface UKTabItemView ()

@property(nonatomic, strong) UIButton *itemButton;
@property(nonatomic, strong) UIView *indicatorView;

@end

@implementation UKTabItemView

- (instancetype)init {
    
    
    self = [super init];
    if (self) {
    
    
        [self setupInitialUI];
    }
    return self;
}

- (void)setupInitialUI {
    
    
    [self addSubview:self.itemButton];
    [self.itemButton mas_makeConstraints:^(MASConstraintMaker *make) {
    
    
        make.left.right.top.bottom.equalTo(self);
    }];
    
    [self addSubview:self.indicatorView];
    [self.indicatorView mas_makeConstraints:^(MASConstraintMaker *make) {
    
    
        make.bottom.equalTo(self);
        make.height.equalTo(@2);
        make.centerX.equalTo(self);
        make.width.equalTo(@60);
    }];
}

- (void)setText:(NSString *)text {
    
    
    [self.itemButton setTitle:text forState:UIControlStateNormal];
}

- (void)setSelected:(BOOL)selected {
    
    
    [self.itemButton setSelected:selected];
    self.indicatorView.hidden = !selected;

    if (selected) {
    
    
        [self.itemButton.titleLabel setFont:[UIFont systemFontOfSize:17]];
    } else {
    
    
        [self.itemButton.titleLabel setFont:[UIFont systemFontOfSize:15]];
    }
}

- (UIButton *)itemButton {
    
    
    if (!_itemButton) {
    
    
        _itemButton = [[UIButton alloc] init];
        [_itemButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
        [_itemButton setTitleColor:[UIColor blueColor] forState:UIControlStateSelected];
        
        [_itemButton.titleLabel setFont:[UIFont systemFontOfSize:15]];
        
        [_itemButton addTarget:self action:@selector(onItemClick:) forControlEvents:UIControlEventTouchUpInside];
    }
    
    return _itemButton;
}

- (void)onItemClick:(UIButton *)sender {
    
    
    if (self.delegate) {
    
    
        [self.delegate onTabItemViewSelected:self];
    }
}

- (UIView *)indicatorView {
    
    
    if (!_indicatorView) {
    
    
        _indicatorView = [[UIView alloc] init];
        
        _indicatorView.layer.backgroundColor = [UIColor blueColor].CGColor;
        _indicatorView.layer.cornerRadius = 1;
        _indicatorView.layer.masksToBounds = YES;
        _indicatorView.hidden = YES;
    }
    return _indicatorView;
}

@end

Customization UKTabView, including several UKTabItemView, the font and color of the selected tab will change, and the prompt below will also become brighter.

@protocol UKTabViewDelegate <NSObject>

- (void)onTabViewSelected:(UKTabView *)tabView position:(NSInteger)position;

@end

@interface UKTabView : UIView

@property(nonatomic, weak) id<UKTabViewDelegate> delegate;

- (void)setItems:(NSArray<NSString *> *)items selection:(NSInteger)selection;
- (void)setSelection:(NSInteger)selection;

@end

@interface UKTabView() <UKTabItemViewDelegate>

@property(nonatomic, assign) NSInteger selection;
@property(nonatomic, strong) NSMutableArray<UKTabItemView *> *tabItemViews;

@end

@implementation UKTabView

- (instancetype)init {
    
    
    self = [super init];
    if (self) {
    
    
        [self setupInitialUI];
    }
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame {
    
    
    self = [super initWithFrame:frame];
    if (self) {
    
    
        [self setupInitialUI];
    }
    return self;
}

- (void)setupInitialUI {
    
    
   _selection = -1;
    self.tabItemViews = [[NSMutableArray alloc] init];
}

- (void)setItems:(NSArray<NSString *> *)items selection:(NSInteger)selection {
    
    
    [self.tabItemViews removeAllObjects];
    
    UKTabItemView *lastItemView = nil;
    for (NSString *item in items) {
    
    
        UKTabItemView *tabItemView = [[UKTabItemView alloc] init];
        [tabItemView setText:item];
        [self addSubview:tabItemView];
        
        // 所有的选项卡都等分排列
        [tabItemView mas_makeConstraints:^(MASConstraintMaker *make) {
    
    
            if (lastItemView) {
    
    
                make.left.equalTo(lastItemView.mas_right);
            } else {
    
    
                make.left.equalTo(self);
            }
            make.top.bottom.equalTo(self);
            make.width.equalTo(self).multipliedBy(1.0/item.length);
        }];
        
        lastItemView = tabItemView;
        
        [self internalAddTabItemView:tabItemView];
    }
    
    [self setSelection:selection];
}

- (void)internalAddTabItemView:(UKTabItemView *)itemView {
    
    
    // 添加itemView,并用tag记录位置
    itemView.tag = self.tabItemViews.count;
    [self.tabItemViews addObject:itemView];
    
    itemView.delegate = self;
}

- (void)setSelection:(NSInteger)selection {
    
    
    if (selection >= 0) {
    
    
        if (selection != self.selection) {
    
    
            if (self.selection >= 0) {
    
    
                [self.tabItemViews[self.selection] setSelected:NO];
            }
            
            _selection = selection;
            [self.tabItemViews[self.selection] setSelected:YES];
        }
    }
}

#pragma mark - UKTabItemViewDelegate -
- (void)onTabItemViewSelected:(UKTabItemView *)tabItemView {
    
    
    [self setSelection:tabItemView.tag];
    
    [self.delegate onTabViewSelected:self position:tabItemView.tag];
}

@end

Inside UIViewController, we define a UKTabView, and add three tabs

UKTabView *tabView = [[UKTabView alloc] initWithFrame:CGRectMake(10, 100, 320, 50)]
[tabView setItems:@[@"选项1", @"选项2", @"选项3"] selection:0];

[self.view addSubview:self.tabView];

The effect is as follows
insert image description here

2. Interaction with UIScrollView

A Tab page often has an interactive interface below it, such as UIScrollView, UICollectionViewetc. UIScrollViewHere will illustrate it with an example. Generally, there are two kinds of interactions here, one is that the Tab tab is selected and then UIScrollViewchanges, and the other is that UIScrollViewthe Tab tab changes after scrolling.

Let's add a display image first UIScrollView,

UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(10, 170, 320, 150)];
scrollView.contentSize = CGSizeMake(320*3, 150);
scrollView.pagingEnabled = YES;
scrollView.showsHorizontalScrollIndicator = NO;
        
scrollView.delegate = self;
[self.view addSubview: scrollView];

for (int index = 1; index <= 3; index++) {
    
    
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(320 * (index - 1), 0, 320, 150)];
    imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"switcher%d", index]];
    [scrollView addSubview:imageView];
}

The added UKTabViewagent listens to each tab change

#pragma mark - UKTabViewDelegate -
- (void)onTabViewSelected:(UKTabView *)tabView position:(NSInteger)position {
    
    
    [self.scrollView setContentOffset:CGPointMake(320 * position, 0) animated:YES];
}

The added UIScrollViewagent UIScrollViewmodifies the state of the Tab tab when the scrolling ends

#pragma mark - UIScrollViewDelegate -
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    
    
    CGFloat width = scrollView.contentOffset.x;
    NSInteger page = width/320 + 0.5;

    [self.tabView setSelection:page];
}

The effect is as follows
insert image description here

3. Dynamically add tabs

Dynamically added UKTabItemView, we need to modify all the previous UKTabItemViewspacing

- (void)addItemView:(UKTabItemView *)itemView {
    
    
    NSInteger len = self.subviews.count;

    for (UIView *view in self.subviews) {
    
    
        [view mas_updateConstraints:^(MASConstraintMaker *make) {
    
    
            make.width.equalTo(self).multipliedBy(1.0/(len + 1));
        }];
    }

    [self addSubview:itemView];
    [itemView mas_makeConstraints:^(MASConstraintMaker *make) {
    
    
        if (len == 0) {
    
    
            make.left.equalTo(self);
        } else {
    
    
            make.left.equalTo(self.subviews[len - 1].mas_right);
        }
        make.top.bottom.equalTo(self);
        make.width.equalTo(self).multipliedBy(1.0/(len + 1));
    }];

    [self internalAddTabItemView:itemView];
}

4. Prompt bar

In the above example, the prompt bar is included in UITabItemViewit. Sometimes we may need the prompt bar to have a dynamic moving effect, so we define the prompt bar in UKTabView.

// 设置提示栏的宽度、高度和颜色等
- (void)setIndicatorWidth:(NSInteger)width height:(NSInteger)height radius:(NSInteger)radius color:(UIColor *)color {
    
    
    self.indicatorWidth = width;
    self.indicatorHeight = height;
    self.indicatorRadius = radius;
    self.indicatorColor = color;

    if (width > 0) {
    
    
        self.indicatorLayer.fillColor = self.indicatorColor.CGColor;
        [self.layer addSublayer:self.indicatorLayer];
    } else {
    
    
        [self.indicatorLayer removeFromSuperlayer];
    }
}

// 修改当前选项卡后,重新绘制提示栏
- (void)setSelection:(NSInteger)selection {
    
    
    if (selection >= 0) {
    
    
        if (selection != self.selection) {
    
    
            if (self.selection >= 0) {
    
    
                [self.tabItemViews[self.selection] setSelected:NO];
            }

            _selection = selection;
            [self.tabItemViews[self.selection] setSelected:YES];
        }
        [self drawIndicatorView];
    }
}

// ratio为偏移度
- (void)setSelection:(NSInteger)selection offsetRatio:(CGFloat)ratio {
    
    
    if (selection >= 0) {
    
    
        self.offsetRatio = ratio;
        
        [self setSelection:selection];
    }
}

// 绘制提示栏,我们利用CALayer的隐式动画来给提示栏添加动态效果
// 每次添加选项卡后,提示栏宽度都会被清空
// 提示栏宽度不能超过选项卡本身宽度
- (void)drawIndicatorView {
    
    
    if (self.indicatorWidth > 0 && self.frame.size.width > 0 && self.tabItemViews.count > 0) {
    
    
        CGFloat itemWidth = self.frame.size.width*1.0/self.tabItemViews.count;
        BOOL initialized = self.indicatorActualWidth != 0;

        CGFloat startX = itemWidth * self.selection + itemWidth * self.offsetRatio;

        if (!initialized) {
    
    
            self.indicatorActualWidth = self.indicatorWidth;

            if (itemWidth <= self.indicatorWidth) {
    
    
                self.indicatorActualWidth = itemWidth;
            }
        }

        if (self.indicatorActualWidth < itemWidth) {
    
    
            startX += (itemWidth - self.indicatorActualWidth) / 2;
        }

        // 绘制选项卡
        if (!initialized) {
    
    
            UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, self.indicatorActualWidth, self.indicatorHeight) cornerRadius:self.indicatorRadius];
            self.indicatorLayer.path = path.CGPath;
        }
        
        // 如果有偏移量,去除CALayer隐式动画
        BOOL anim = self.offsetRatio == 0;        
        if (!anim) {
    
    
            [CATransaction begin];
            [CATransaction setDisableActions:true];
        }

        self.indicatorLayer.frame = CGRectMake(startX, self.frame.size.height - self.indicatorHeight, self.indicatorActualWidth, self.indicatorHeight);

        if (!anim) {
    
    
            [CATransaction commit];
        }
    }
}

The scrolling we scrollViewWillBeginDraggingdistinguish in the method UIScrollViewis triggered by gestures or code. In scrollViewDidScrollthe method, if the movement is triggered by a gesture, the status bar will move in proportion.

#pragma mark - UIScrollViewDelegate -
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    
    
    self.dragging = YES;
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    
    
    if (self.dragging) {
    
    
        CGFloat width = scrollView.contentOffset.x;
        NSInteger page = width/320 + 0.5;

        [self.tabView setSelection:page offsetRatio:(width/320 - page)];
    }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    
    
    CGFloat width = scrollView.contentOffset.x;
    NSInteger page = width/320 + 0.5;

    [self.tabView setSelection:page];
    self.dragging = NO;
}

The effect is as follows

insert image description here

Guess you like

Origin blog.csdn.net/chennai1101/article/details/130082194