// // KBPersonaChatCell.m // keyBoard // // Created by Kiro on 2026/1/26. // #import "KBPersonaChatCell.h" #import "KBChatTableView.h" #import "KBAiChatMessage.h" #import #import @interface KBPersonaChatCell () /// 背景图 @property (nonatomic, strong) UIImageView *backgroundImageView; /// 头像 @property (nonatomic, strong) UIImageView *avatarImageView; /// 人设名称 @property (nonatomic, strong) UILabel *nameLabel; /// 开场白 @property (nonatomic, strong) UILabel *openingLabel; /// 聊天列表 @property (nonatomic, strong) UITableView *tableView; /// 聊天消息 @property (nonatomic, strong) NSMutableArray *messages; /// 是否已加载数据 @property (nonatomic, assign) BOOL hasLoadedData; @end @implementation KBPersonaChatCell #pragma mark - Lifecycle - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { [self setupUI]; } return self; } #pragma mark - 1:控件初始化 - (void)setupUI { // 背景图 [self.contentView addSubview:self.backgroundImageView]; [self.backgroundImageView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.contentView); }]; // 半透明遮罩 UIView *maskView = [[UIView alloc] init]; maskView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.3]; [self.contentView addSubview:maskView]; [maskView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.contentView); }]; // 头像 [self.contentView addSubview:self.avatarImageView]; [self.avatarImageView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.contentView).offset(80); make.centerX.equalTo(self.contentView); make.size.mas_equalTo(CGSizeMake(80, 80)); }]; // 人设名称 [self.contentView addSubview:self.nameLabel]; [self.nameLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.avatarImageView.mas_bottom).offset(12); make.centerX.equalTo(self.contentView); }]; // 开场白 [self.contentView addSubview:self.openingLabel]; [self.openingLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.nameLabel.mas_bottom).offset(8); make.left.equalTo(self.contentView).offset(40); make.right.equalTo(self.contentView).offset(-40); }]; // 聊天列表 [self.contentView addSubview:self.tableView]; [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.openingLabel.mas_bottom).offset(30); make.left.right.bottom.equalTo(self.contentView); }]; } #pragma mark - Setter - (void)setPersona:(KBPersonaModel *)persona { _persona = persona; // 重置状态 self.hasLoadedData = NO; self.messages = [NSMutableArray array]; // 设置 UI [self.backgroundImageView sd_setImageWithURL:[NSURL URLWithString:persona.coverImageUrl] placeholderImage:[UIImage imageNamed:@"placeholder_bg"]]; [self.avatarImageView sd_setImageWithURL:[NSURL URLWithString:persona.avatarUrl] placeholderImage:[UIImage imageNamed:@"placeholder_avatar"]]; self.nameLabel.text = persona.name; self.openingLabel.text = persona.shortDesc.length > 0 ? persona.shortDesc : persona.introText; [self.tableView reloadData]; } #pragma mark - 2:数据加载 - (void)preloadDataIfNeeded { if (self.hasLoadedData) { return; } // TODO: 这里后续需要用 chatId 去请求聊天记录 // 目前先添加开场白作为第一条消息 self.hasLoadedData = YES; if (self.persona.introText.length > 0) { KBAiChatMessage *openingMsg = [KBAiChatMessage assistantMessageWithText:self.persona.introText]; openingMsg.isComplete = YES; [self.messages addObject:openingMsg]; [self.tableView reloadData]; } } #pragma mark - UITableViewDataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.messages.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"]; cell.backgroundColor = [UIColor clearColor]; cell.textLabel.textColor = [UIColor whiteColor]; cell.textLabel.numberOfLines = 0; } KBAiChatMessage *message = self.messages[indexPath.row]; cell.textLabel.text = message.text; return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return UITableViewAutomaticDimension; } #pragma mark - Lazy Load - (UIImageView *)backgroundImageView { if (!_backgroundImageView) { _backgroundImageView = [[UIImageView alloc] init]; _backgroundImageView.contentMode = UIViewContentModeScaleAspectFill; _backgroundImageView.clipsToBounds = YES; } return _backgroundImageView; } - (UIImageView *)avatarImageView { if (!_avatarImageView) { _avatarImageView = [[UIImageView alloc] init]; _avatarImageView.contentMode = UIViewContentModeScaleAspectFill; _avatarImageView.layer.cornerRadius = 40; _avatarImageView.layer.borderWidth = 3; _avatarImageView.layer.borderColor = [UIColor whiteColor].CGColor; _avatarImageView.clipsToBounds = YES; } return _avatarImageView; } - (UILabel *)nameLabel { if (!_nameLabel) { _nameLabel = [[UILabel alloc] init]; _nameLabel.font = [UIFont boldSystemFontOfSize:20]; _nameLabel.textColor = [UIColor whiteColor]; _nameLabel.textAlignment = NSTextAlignmentCenter; } return _nameLabel; } - (UILabel *)openingLabel { if (!_openingLabel) { _openingLabel = [[UILabel alloc] init]; _openingLabel.font = [UIFont systemFontOfSize:14]; _openingLabel.textColor = [[UIColor whiteColor] colorWithAlphaComponent:0.9]; _openingLabel.textAlignment = NSTextAlignmentCenter; _openingLabel.numberOfLines = 2; } return _openingLabel; } - (UITableView *)tableView { if (!_tableView) { _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; _tableView.delegate = self; _tableView.dataSource = self; _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; _tableView.backgroundColor = [UIColor clearColor]; _tableView.showsVerticalScrollIndicator = NO; _tableView.estimatedRowHeight = 60; _tableView.rowHeight = UITableViewAutomaticDimension; if (@available(iOS 11.0, *)) { _tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; } } return _tableView; } @end