iOS开发-实现热门话题标签tag显示控件

iOS开发-实现热门话题标签tag显示控件

话题标签tag显示非常常见,如选择你的兴趣,选择关注的群,超话,话题等等。

一、效果图

二、实现代码

由于显示的是在列表中,这里整体控件是放在UITableViewCell中的。

2.1 标签tag按钮实现

自定义标签tag按钮INRmdTopicButton

INRmdTopicButton.h

objectivec 复制代码
@interface INRmdTopicButton : UIControl

@property (nonatomic, strong) NSString *topicName;
@property (nonatomic, assign) CGFloat showTopicWidth;

+ (CGFloat)topicWidth:(NSString *)name;

@end

INRmdTopicButton.m

objectivec 复制代码
@interface INRmdTopicButton ()

@property (nonatomic, strong) UIImageView *backImageView;       //图片控件
@property (nonatomic, strong) UIImageView *tbkImageView;       //图片控件
@property (nonatomic, strong) UILabel *titleLabel;

@end

@implementation INRmdTopicButton

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self addSubview:self.backImageView];
        [self.backImageView addSubview:self.tbkImageView];
        [self.backImageView addSubview:self.titleLabel];
    }
    return self;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    self.backImageView.frame = self.bounds;
    self.tbkImageView.frame = CGRectMake(0.0, 0.0, CGRectGetWidth(self.backImageView.frame), CGRectGetHeight(self.backImageView.frame) - kSmallPadding);
    self.titleLabel.frame = CGRectMake(0.0, 0.0, CGRectGetWidth(self.backImageView.frame), kTopicNameHeight);
}

- (void)setTopicName:(NSString *)topicName {
    _topicName = (topicName?topicName:@"");
    self.titleLabel.text = _topicName;
    [self setNeedsLayout];
}

+ (CGFloat)topicWidth:(NSString *)name {
    CGSize topicSize = [name sizeWithFont:[UIFont systemFontOfSize:12] forMaxSize:CGSizeMake(MAXFLOAT, kTopicHeight)];
    return topicSize.width + 2*kSmallPadding;
}

#pragma mark - SETTER/GETTER
- (UIImageView *)backImageView {
    if (!_backImageView) {
        _backImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
        _backImageView.userInteractionEnabled = YES;
        _backImageView.backgroundColor = [UIColor clearColor];
    }
    return _backImageView;
}

- (UIImageView *)tbkImageView {
    if (!_tbkImageView) {
        _tbkImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
        _tbkImageView.userInteractionEnabled = YES;
        _tbkImageView.backgroundColor = [UIColor clearColor];
        UIImage *image = [UIImage imageNamed:@"bk_topic_r"];
        image = [image stretchableImageWithLeftCapWidth:floorf(image.size.width * 0.5) topCapHeight:floorf(image.size.height * 0.5)];
        _tbkImageView.image = image;
    }
    return _tbkImageView;
}

- (UILabel *)titleLabel {
    if (!_titleLabel) {
        _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
        _titleLabel.font = [UIFont systemFontOfSize:12];
        _titleLabel.textAlignment = NSTextAlignmentCenter;
        _titleLabel.textColor = [UIColor colorWithHexString:@"555555"];
        _titleLabel.backgroundColor = [UIColor clearColor];
    }
    return _titleLabel;
}

@end

2.2 显示排列标签tag

显示标题tag时候,需要排列按钮

objectivec 复制代码
INRmdTopicButton *lastButton = nil;
    for (UIView *subView in self.topicBKImageView.subviews) {
        if ([subView isKindOfClass:[INRmdTopicButton class]]) {
            INRmdTopicButton *button = (INRmdTopicButton *)subView;
            button.hidden = NO;
            if (lastButton) {
                if (CGRectGetMaxX(lastButton.frame) + button.showTopicWidth + kSmallPadding > maxWidth) {
                    button.frame = CGRectMake(0.0, CGRectGetMaxY(lastButton.frame), button.showTopicWidth, kTopicHeight);
                } else {
                    button.frame = CGRectMake(CGRectGetMaxX(lastButton.frame) + kSmallPadding, CGRectGetMinY(lastButton.frame), button.showTopicWidth, kTopicHeight);
                }
            } else {
                button.frame = CGRectMake(originX, originY, button.showTopicWidth, kTopicHeight);
            }
            
            if (CGRectGetMaxY(button.frame) > maxHeight) {
                button.hidden = YES;
            } else {
                button.hidden = NO;
            }
            
            lastButton = button;
        }
    }

这里还加了拖动手势UIPanGestureRecognizer,当往左拖动的时候会显示松开换一换的功能。调用接口实现。

objectivec 复制代码
#pragma mark - panGestureHandle
- (void)panGestureHandle:(UIPanGestureRecognizer *)pan{
    if (pan.state == UIGestureRecognizerStateBegan) {
        NSLog(@"UIGestureRecognizerStateBegan");
        self.startPoint = [pan translationInView:self];
    } if (pan.state == UIGestureRecognizerStateChanged) {
        NSLog(@"UIGestureRecognizerStateChanged");
        
        CGPoint point = [pan translationInView:self];
        CGFloat xDistance = point.x - self.startPoint.x;
        // 左右滑动
        NSLog(@"左右滑动");
        if (xDistance > 0) {
            NSLog(@"向右滑动");
            CGRect backFrame = self.topicBKImageView.frame;
            backFrame.origin.x = kMidPadding;
            self.topicBKImageView.frame = backFrame;
        } else {
            NSLog(@"向左滑动");
            CGRect backFrame = self.topicBKImageView.frame;
            backFrame.origin.x = kMidPadding + xDistance*0.5;
            self.topicBKImageView.frame = backFrame;
        }
        
    } else {
        // if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled || pan.state == UIGestureRecognizerStateFailed)
        NSLog(@"UIGestureRecognizerStateEnded");
        CGRect backFrame = self.topicBKImageView.frame;
        backFrame.origin.x = kMidPadding;
        [UIView animateWithDuration:0.55 delay:0.0 usingSpringWithDamping:0.5 initialSpringVelocity:7.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            self.topicBKImageView.frame = backFrame;
        } completion:^(BOOL finished) {
            
        }];
    }
}

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}

2.3 完整代码如下

INRecommentTopicCell.h

objectivec 复制代码
#import <UIKit/UIKit.h>

@interface INRmdTopicButton : UIControl

@property (nonatomic, strong) NSString *topicName;
@property (nonatomic, assign) CGFloat showTopicWidth;

+ (CGFloat)topicWidth:(NSString *)name;

@end

/**
 推荐的话题
 */
@interface INRecommentTopicCell : UITableViewCell

+ (CGFloat)cellHeight;

@end

INRecommentTopicCell.m

objectivec 复制代码
#import "INRecommentTopicCell.h"
#import "UIColor+Addition.h"
#import "NSString+Size.h"

static CGFloat kCellHeight = 260.0;

static CGFloat kCellHorBGPadding = 10.0f;
static CGFloat kCellVerBGPadding = 5.0f;

static CGFloat kTitleHeight = 44.0f;
static CGFloat kMidPadding = 10.0f;

static CGFloat kSmallPadding = 5.0f;
static CGFloat kTopicHeight = 40.0f;
static CGFloat kTopicNameHeight = 30.0f;

static CGFloat kExchangeBtnSize = 40.0f;
static CGFloat kTisWidth = 20.0f;

@interface INRmdTopicButton ()

@property (nonatomic, strong) UIImageView *backImageView;       //图片控件
@property (nonatomic, strong) UIImageView *tbkImageView;       //图片控件
@property (nonatomic, strong) UILabel *titleLabel;

@end

@implementation INRmdTopicButton

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self addSubview:self.backImageView];
        [self.backImageView addSubview:self.tbkImageView];
        [self.backImageView addSubview:self.titleLabel];
    }
    return self;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    self.backImageView.frame = self.bounds;
    self.tbkImageView.frame = CGRectMake(0.0, 0.0, CGRectGetWidth(self.backImageView.frame), CGRectGetHeight(self.backImageView.frame) - kSmallPadding);
    self.titleLabel.frame = CGRectMake(0.0, 0.0, CGRectGetWidth(self.backImageView.frame), kTopicNameHeight);
}

- (void)setTopicName:(NSString *)topicName {
    _topicName = (topicName?topicName:@"");
    self.titleLabel.text = _topicName;
    [self setNeedsLayout];
}

+ (CGFloat)topicWidth:(NSString *)name {
    CGSize topicSize = [name sizeWithFont:[UIFont systemFontOfSize:12] forMaxSize:CGSizeMake(MAXFLOAT, kTopicHeight)];
    return topicSize.width + 2*kSmallPadding;
}

#pragma mark - SETTER/GETTER
- (UIImageView *)backImageView {
    if (!_backImageView) {
        _backImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
        _backImageView.userInteractionEnabled = YES;
        _backImageView.backgroundColor = [UIColor clearColor];
    }
    return _backImageView;
}

- (UIImageView *)tbkImageView {
    if (!_tbkImageView) {
        _tbkImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
        _tbkImageView.userInteractionEnabled = YES;
        _tbkImageView.backgroundColor = [UIColor clearColor];
        UIImage *image = [UIImage imageNamed:@"bk_topic_r"];
        image = [image stretchableImageWithLeftCapWidth:floorf(image.size.width * 0.5) topCapHeight:floorf(image.size.height * 0.5)];
        _tbkImageView.image = image;
    }
    return _tbkImageView;
}

- (UILabel *)titleLabel {
    if (!_titleLabel) {
        _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
        _titleLabel.font = [UIFont systemFontOfSize:12];
        _titleLabel.textAlignment = NSTextAlignmentCenter;
        _titleLabel.textColor = [UIColor colorWithHexString:@"555555"];
        _titleLabel.backgroundColor = [UIColor clearColor];
    }
    return _titleLabel;
}

@end

/**
 推荐的话题
 */
@interface INRecommentTopicCell ()

@property (nonatomic, strong) UIImageView *backImageView;       //图片控件
@property (nonatomic, strong) UIImageView *contentBGImageView;       //图片控件
@property (nonatomic, strong) UIImageView *topicBKImageView;       //图片控件
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIButton *exchangeButton;     // 更多

@property (nonatomic, strong) UILabel *tipsLabel;

@property (nonatomic) CGPoint startPoint;     // 开始点

@end

@implementation INRecommentTopicCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // Initialization code
        
        self.backgroundColor = [UIColor clearColor];
        self.contentView.backgroundColor = [UIColor clearColor];
        [self.contentView addSubview:self.backImageView];
        [self.contentView addSubview:self.contentBGImageView];
        [self.contentBGImageView addSubview:self.titleLabel];
        [self.contentBGImageView addSubview:self.exchangeButton];
        [self.contentBGImageView addSubview:self.tipsLabel];
        [self.contentBGImageView addSubview:self.topicBKImageView];

        [self setupRmdTopicViews];
        
        UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panGestureHandle:)];
        panGesture.minimumNumberOfTouches = 1;
        panGesture.maximumNumberOfTouches = 1;
        panGesture.delegate = self;
        [self.contentBGImageView addGestureRecognizer:panGesture];
    }
    return self;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    self.backImageView.frame = CGRectMake(kCellHorBGPadding, kCellVerBGPadding, CGRectGetWidth(self.bounds) - 2*kCellHorBGPadding, CGRectGetHeight(self.bounds) - 2*kCellVerBGPadding);
    
    self.contentBGImageView.frame = CGRectMake(kCellHorBGPadding, kCellVerBGPadding, CGRectGetWidth(self.bounds) - 2*kCellHorBGPadding, CGRectGetHeight(self.bounds) - 2*kCellVerBGPadding);

    self.titleLabel.frame = CGRectMake(kMidPadding, 0.0, CGRectGetWidth(self.backImageView.frame) - 3*kMidPadding - kExchangeBtnSize, kTitleHeight);
    
    self.exchangeButton.frame = CGRectMake(CGRectGetWidth(self.backImageView.frame) - kMidPadding - kExchangeBtnSize, (kTitleHeight - kExchangeBtnSize)/2, kExchangeBtnSize, kExchangeBtnSize);
    
    CGFloat height = CGRectGetHeight(self.backImageView.frame) - CGRectGetMaxY(self.titleLabel.frame);
    self.tipsLabel.frame = CGRectMake(CGRectGetWidth(self.backImageView.frame) - kMidPadding - kTisWidth, 0.0, kTisWidth, height);
    
    self.topicBKImageView.frame = CGRectMake(kMidPadding, CGRectGetMaxY(self.titleLabel.frame), CGRectGetWidth(self.backImageView.frame) - 2*kMidPadding, height);
    
    CGFloat maxWidth = CGRectGetWidth(self.topicBKImageView.frame);
    CGFloat maxHeight = CGRectGetHeight(self.topicBKImageView.frame);
    
    CGFloat originX = 0.0;
    CGFloat originY = 0.0;

    INRmdTopicButton *lastButton = nil;
    for (UIView *subView in self.topicBKImageView.subviews) {
        if ([subView isKindOfClass:[INRmdTopicButton class]]) {
            INRmdTopicButton *button = (INRmdTopicButton *)subView;
            button.hidden = NO;
            if (lastButton) {
                if (CGRectGetMaxX(lastButton.frame) + button.showTopicWidth + kSmallPadding > maxWidth) {
                    button.frame = CGRectMake(0.0, CGRectGetMaxY(lastButton.frame), button.showTopicWidth, kTopicHeight);
                } else {
                    button.frame = CGRectMake(CGRectGetMaxX(lastButton.frame) + kSmallPadding, CGRectGetMinY(lastButton.frame), button.showTopicWidth, kTopicHeight);
                }
            } else {
                button.frame = CGRectMake(originX, originY, button.showTopicWidth, kTopicHeight);
            }
            
            if (CGRectGetMaxY(button.frame) > maxHeight) {
                button.hidden = YES;
            } else {
                button.hidden = NO;
            }
            
            lastButton = button;
        }
    }
}

- (void)setupRmdTopicViews {
    for (UIView *subView in self.topicBKImageView.subviews) {
        if ([subView isKindOfClass:[INRmdTopicButton class]]) {
            [subView removeFromSuperview];
        }
    }
    
    for (NSInteger index = 0; index < 15; index ++) {
        INRmdTopicButton *button = [[INRmdTopicButton alloc] initWithFrame:CGRectZero];
        button.tag = index;
        [self.topicBKImageView addSubview:button];
        
        if (index % 5 == 0) {
            button.topicName = @"#读书交流";
        } else if (index % 5 == 1) {
            button.topicName = @"#爱手工生活";
        } else if (index % 5 == 2) {
            button.topicName = @"#精致的佛系生活";
        } else if (index % 5 == 3) {
            button.topicName = @"#数码发烧友";
        } else if (index % 5 == 4) {
            button.topicName = @"#晒晒你的心情";
        } else {
            button.topicName = @"#说说身边事";
        }
        button.showTopicWidth = [INRmdTopicButton topicWidth:button.topicName];
    }
    
    [self setNeedsLayout];
}


- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

+ (CGFloat)cellHeight {
    return kCellHeight;
}

#pragma mark - Actions
- (void)exchangeButtonAction {
    
}

#pragma mark - SETTER/GETTER
- (UIImageView *)backImageView {
    if (!_backImageView) {
        _backImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
        _backImageView.userInteractionEnabled = YES;
        _backImageView.backgroundColor = [UIColor whiteColor];
        _backImageView.layer.cornerRadius = 2.0;
        _backImageView.layer.shadowColor = [UIColor colorWithHexString:@"9bb9ef"].CGColor;
        _backImageView.layer.shadowOffset = CGSizeMake(0, 3);
        _backImageView.layer.shadowOpacity = 0.3;
        _backImageView.layer.shadowRadius = 3.0;
    }
    return _backImageView;
}

- (UIImageView *)contentBGImageView {
    if (!_contentBGImageView) {
        _contentBGImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
        _contentBGImageView.userInteractionEnabled = YES;
        _contentBGImageView.backgroundColor = [UIColor whiteColor];
        _contentBGImageView.layer.cornerRadius = 2.0;
        _contentBGImageView.layer.masksToBounds = YES;
        _contentBGImageView.clipsToBounds = YES;
    }
    return _contentBGImageView;
}

- (UIImageView *)topicBKImageView {
    if (!_topicBKImageView) {
        _topicBKImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
        _topicBKImageView.userInteractionEnabled = YES;
        _topicBKImageView.backgroundColor = [UIColor whiteColor];
        _topicBKImageView.clipsToBounds = YES;
    }
    return _topicBKImageView;
}

- (UILabel *)titleLabel {
    if (!_titleLabel) {
        _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
        _titleLabel.font = [UIFont systemFontOfSize:18];
        _titleLabel.textColor = [UIColor colorWithHexString:@"131619"];
        _titleLabel.backgroundColor = [UIColor clearColor];
        _titleLabel.text = @"热门话题";
    }
    return _titleLabel;
}

- (UILabel *)tipsLabel {
    if (!_tipsLabel) {
        _tipsLabel = [[UILabel alloc] initWithFrame:CGRectZero];
        _tipsLabel.font = [UIFont systemFontOfSize:12];
        _tipsLabel.textColor = [UIColor colorWithHexString:@"9a9b9c"];
        _tipsLabel.backgroundColor = [UIColor clearColor];
        _tipsLabel.numberOfLines = 0;
        _tipsLabel.textAlignment = NSTextAlignmentCenter;
        _tipsLabel.text = @"松\n开\n换\n一\n换";
    }
    return _tipsLabel;
}

- (UIButton *)exchangeButton {
    if (!_exchangeButton) {
        _exchangeButton = [UIButton buttonWithType:UIButtonTypeCustom];
        [_exchangeButton setImage:[UIImage imageNamed:@"ic_topic_exchange"] forState:UIControlStateNormal];
        [_exchangeButton addTarget:self action:@selector(exchangeButtonAction) forControlEvents:UIControlEventTouchUpInside];
    }
    return _exchangeButton;
}

#pragma mark - panGestureHandle
- (void)panGestureHandle:(UIPanGestureRecognizer *)pan{
    if (pan.state == UIGestureRecognizerStateBegan) {
        NSLog(@"UIGestureRecognizerStateBegan");
        self.startPoint = [pan translationInView:self];
    } if (pan.state == UIGestureRecognizerStateChanged) {
        NSLog(@"UIGestureRecognizerStateChanged");
        
        CGPoint point = [pan translationInView:self];
        CGFloat xDistance = point.x - self.startPoint.x;
        // 左右滑动
        NSLog(@"左右滑动");
        if (xDistance > 0) {
            NSLog(@"向右滑动");
            CGRect backFrame = self.topicBKImageView.frame;
            backFrame.origin.x = kMidPadding;
            self.topicBKImageView.frame = backFrame;
        } else {
            NSLog(@"向左滑动");
            CGRect backFrame = self.topicBKImageView.frame;
            backFrame.origin.x = kMidPadding + xDistance*0.5;
            self.topicBKImageView.frame = backFrame;
        }
        
    } else {
        // if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled || pan.state == UIGestureRecognizerStateFailed)
        NSLog(@"UIGestureRecognizerStateEnded");
        CGRect backFrame = self.topicBKImageView.frame;
        backFrame.origin.x = kMidPadding;
        [UIView animateWithDuration:0.55 delay:0.0 usingSpringWithDamping:0.5 initialSpringVelocity:7.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            self.topicBKImageView.frame = backFrame;
        } completion:^(BOOL finished) {
            
        }];
    }
}

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}

@end

三、小结

iOS开发-实现热门话题标签tag显示控件

话题标签tag显示非常常见,如选择你的兴趣,选择关注的群,超话,话题等等。

学习记录,每天不停进步。

相关推荐
传奇开心果编程1 小时前
【Compose Multiplatform 跨端开发学与练】第9课 测试与调试
android·学习·macos·ios·kotlin·web·composer
传奇开心果编程1 小时前
【Compose Multiplatform 跨端开发学与练】第4课 导航与路由
android·windows·学习·ui·ios·kotlin·composer
黑科技iOS上架1 小时前
iOS隐私合规扫描工具
ios·审核
传奇开心果编程2 小时前
【Compose Multiplatform 跨端开发学与练】第6课 状态管理与架构
android·学习·ui·ios·架构·kotlin·composer
传奇开心果编程2 小时前
【Compose Multiplatform 跨端开发学与练】第2课 Compose 基础语法
android·windows·学习·ui·ios·kotlin·composer
可乐鸡翅yeah_2 小时前
hls.js 缓冲区参数 maxBufferLength、maxBufferSize 通俗讲解与业务调参
开发语言·javascript·ios·音视频·safari·m3u8
supabc1232 小时前
Celium:连接 Windows、Mac、Linux 与 Android,让远程访问和设备管理更简单
android·linux·windows·macos·远程访问·网络管理·celium
牛奔2 小时前
Mac 精准安装指定 macOS 版本完整教程(升级 + 降级通用)
macos
海盗12343 小时前
AI 新闻日报 2026-10-05:三周重构 30 万行 / macOS 给智能体加权限锁 / 具身订单结构被摆上台面
人工智能·macos·重构·机器人·人工智能aigc
茶底世界之下21 小时前
Metal 冷启动并发下,Pipeline Cache 为什么必须合并在途编译
ios·swift