Files
keyboard/keyBoard/Class/Guard/VC/KBGuideVC.m
2025-10-29 16:44:00 +08:00

257 lines
9.6 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// KBGuideVC.m
// keyBoard
//
// Created by Mac on 2025/10/29.
//
#import "KBGuideVC.h"
#import "KBGuideTopCell.h"
#import "KBGuideKFCell.h"
#import "KBGuideUserCell.h"
typedef NS_ENUM(NSInteger, KBGuideItemType) {
KBGuideItemTypeTop = 0, // 顶部固定卡片
KBGuideItemTypeUser, // 我方消息
KBGuideItemTypeKF // 客服回复
};
@interface KBGuideVC () <UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate, UIGestureRecognizerDelegate>
@property (nonatomic, strong) BaseTableView *tableView; // 列表(继承 BaseTableView
@property (nonatomic, strong) UIView *inputBar; // 底部输入容器
@property (nonatomic, strong) UITextField *textField; // 输入框
@property (nonatomic, strong) MASConstraint *inputBarBottom;// 输入栏底部约束
@property (nonatomic, strong) UITapGestureRecognizer *bgTap;// 点击空白收起键盘
@property (nonatomic, strong) NSMutableArray<NSDictionary *> *items; // 数据源 [{type, text}]
@end
@implementation KBGuideVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorWithWhite:0.96 alpha:1.0];
self.title = @"使用引导";
[self.view addSubview:self.tableView];
[self.view addSubview:self.inputBar];
[self.inputBar addSubview:self.textField];
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.right.equalTo(self.view);
make.bottom.equalTo(self.inputBar.mas_top);
}];
[self.inputBar mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.view);
make.height.mas_equalTo(52);
// 底部跟随键盘变化
if (@available(iOS 11.0, *)) {
self.inputBarBottom = make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom);
} else {
self.inputBarBottom = make.bottom.equalTo(self.view);
}
}];
[self.textField mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.inputBar).offset(12);
make.right.equalTo(self.inputBar).offset(-12);
make.centerY.equalTo(self.inputBar);
make.height.mas_equalTo(36);
}];
// 初始只有固定 Top
[self.items addObject:@{ @"type": @(KBGuideItemTypeTop), @"text": @"" }];
[self.tableView reloadData];
// 键盘监听
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(kb_keyboardWillChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
// 点击空白收起键盘(不干扰 cell 的点击/滚动)
self.bgTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(kb_didTapBackground)];
self.bgTap.cancelsTouchesInView = NO;
self.bgTap.delegate = self;
[self.tableView addGestureRecognizer:self.bgTap];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)kb_didTapBackground {
// 结束编辑,隐藏键盘
[self.view endEditing:YES];
}
#pragma mark - Actions
// 发送:回车发送一条消息,随后插入固定的客服回复
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
NSString *text = [textField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (text.length == 0) { return NO; }
// 1. 插入我方消息
[self.items addObject:@{ @"type": @(KBGuideItemTypeUser), @"text": text ?: @"" }];
// 2. 紧跟一条固定客服消息
NSString *reply = @"🎉 如您遇到其他问题,可点击在线客服帮您解决~";
[self.items addObject:@{ @"type": @(KBGuideItemTypeKF), @"text": reply }];
// 刷新并滚动到底部
[self.tableView reloadData];
[self scrollToBottomAnimated:YES];
textField.text = @"";
return YES;
}
- (void)kb_keyboardWillChange:(NSNotification *)note {
NSDictionary *info = note.userInfo;
NSTimeInterval duration = [info[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationOptions curve = ([info[UIKeyboardAnimationCurveUserInfoKey] integerValue] << 16);
CGRect endFrame = [info[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat screenH = UIScreen.mainScreen.bounds.size.height;
CGFloat kbHeight = MAX(0, screenH - endFrame.origin.y);
CGFloat safeBtm = 0;
if (@available(iOS 11.0, *)) { safeBtm = self.view.safeAreaInsets.bottom; }
// 输入栏距离底部 = -max(kbHeight - 安全区, 0)
CGFloat offset = -MAX(kbHeight - safeBtm, 0);
self.inputBarBottom.offset = offset;
[UIView animateWithDuration:duration delay:0 options:curve animations:^{
[self.view layoutIfNeeded];
// 留出输入栏高度的 contentInset避免最后一行被遮挡
UIEdgeInsets inset = self.tableView.contentInset;
inset.bottom = 52 + MAX(kbHeight - safeBtm, 0);
self.tableView.contentInset = inset;
self.tableView.scrollIndicatorInsets = inset;
} completion:^(BOOL finished) {
[self scrollToBottomAnimated:YES];
}];
}
- (void)scrollToBottomAnimated:(BOOL)animated {
if (self.items.count == 0) return;
NSInteger last = self.items.count - 1;
NSIndexPath *ip = [NSIndexPath indexPathForRow:last inSection:0];
if (last >= 0) {
[self.tableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionBottom animated:animated];
}
}
#pragma mark - UITableView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *it = self.items[indexPath.row];
KBGuideItemType type = [it[@"type"] integerValue];
NSString *text = it[@"text"] ?: @"";
if (type == KBGuideItemTypeTop) {
KBGuideTopCell *cell = [tableView dequeueReusableCellWithIdentifier:[KBGuideTopCell reuseId]];
if (!cell) cell = [[KBGuideTopCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:[KBGuideTopCell reuseId]];
return cell;
} else if (type == KBGuideItemTypeUser) {
KBGuideUserCell *cell = [tableView dequeueReusableCellWithIdentifier:[KBGuideUserCell reuseId]];
if (!cell) cell = [[KBGuideUserCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:[KBGuideUserCell reuseId]];
[cell configText:text];
return cell;
} else {
KBGuideKFCell *cell = [tableView dequeueReusableCellWithIdentifier:[KBGuideKFCell reuseId]];
if (!cell) cell = [[KBGuideKFCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:[KBGuideKFCell reuseId]];
[cell configText:text];
return cell;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 100;
}
#pragma mark - Lazy
- (BaseTableView *)tableView {
if (!_tableView) {
_tableView = [[BaseTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone; // 无分割线
_tableView.backgroundColor = [UIColor colorWithWhite:0.96 alpha:1.0];
_tableView.rowHeight = UITableViewAutomaticDimension;
_tableView.estimatedRowHeight = 120; // 开启自适应高度
_tableView.contentInset = UIEdgeInsetsMake(0, 0, 52 + KB_SafeAreaBottom(), 0);
_tableView.scrollIndicatorInsets = _tableView.contentInset;
}
return _tableView;
}
- (UIView *)inputBar {
if (!_inputBar) {
_inputBar = [UIView new];
_inputBar.backgroundColor = [UIColor colorWithWhite:0.96 alpha:1.0];
UIView *bg = [UIView new];
bg.backgroundColor = [UIColor whiteColor];
bg.layer.cornerRadius = 10; bg.layer.masksToBounds = YES;
[_inputBar addSubview:bg];
[bg mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(_inputBar).offset(12);
make.right.equalTo(_inputBar).offset(-12);
make.top.equalTo(_inputBar).offset(8);
make.bottom.equalTo(_inputBar).offset(-8);
}];
}
return _inputBar;
}
- (UITextField *)textField {
if (!_textField) {
_textField = [UITextField new];
_textField.delegate = self;
_textField.returnKeyType = UIReturnKeySend; // 回车发送
_textField.font = [UIFont systemFontOfSize:15];
_textField.placeholder = @"在键盘粘贴对话后,选择回复方式";
_textField.backgroundColor = [UIColor whiteColor];
_textField.layer.cornerRadius = 10; _textField.layer.masksToBounds = YES;
UIView *pad = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 36)];
_textField.leftView = pad; _textField.leftViewMode = UITextFieldViewModeAlways;
}
return _textField;
}
- (NSMutableArray<NSDictionary *> *)items {
if (!_items) { _items = @[].mutableCopy; }
return _items;
}
#pragma mark - UIGestureRecognizerDelegate
// 避免点到输入栏触发收起
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (gestureRecognizer == self.bgTap) {
if ([touch.view isDescendantOfView:self.inputBar]) {
return NO;
}
}
return YES;
}
// 与其它手势同时识别,避免影响表格滚动/选择
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
@end