移动文件

This commit is contained in:
2025-10-29 16:44:00 +08:00
parent 9101ffaab0
commit c5326a3079
9 changed files with 71 additions and 10 deletions

View File

@@ -0,0 +1,20 @@
//
// KBGuideKFCell.h
// keyBoard
//
// 客服回复气泡 cell左侧头像 + 白色圆角气泡)
//
#import "BaseCell.h"
NS_ASSUME_NONNULL_BEGIN
@interface KBGuideKFCell : BaseCell
/// 设置文案
- (void)configText:(NSString *)text;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,77 @@
//
// KBGuideKFCell.m
// keyBoard
//
#import "KBGuideKFCell.h"
@interface KBGuideKFCell ()
@property (nonatomic, strong) UIView *avatarView; //
@property (nonatomic, strong) UIView *bubbleView; //
@property (nonatomic, strong) UILabel *contentLabel; //
@end
@implementation KBGuideKFCell
- (void)setupUI {
self.contentView.backgroundColor = [UIColor colorWithWhite:0.96 alpha:1.0];
[self.contentView addSubview:self.avatarView];
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.contentView).offset(16);
make.top.equalTo(self.contentView).offset(10);
make.width.height.mas_equalTo(36);
}];
[self.contentView addSubview:self.bubbleView];
[self.bubbleView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.avatarView.mas_right).offset(8);
make.top.equalTo(self.contentView).offset(8);
make.right.lessThanOrEqualTo(self.contentView).offset(-80);
make.bottom.equalTo(self.contentView).offset(-8);
}];
[self.bubbleView addSubview:self.contentLabel];
[self.contentLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.bubbleView).insets(UIEdgeInsetsMake(10, 12, 10, 12));
}];
}
- (void)configText:(NSString *)text {
self.contentLabel.text = text;
}
#pragma mark - Lazy
- (UIView *)avatarView {
if (!_avatarView) {
_avatarView = [UIView new];
_avatarView.backgroundColor = [UIColor colorWithRed:0.23 green:0.47 blue:0.96 alpha:1.0];
_avatarView.layer.cornerRadius = 18;
_avatarView.layer.masksToBounds = YES;
}
return _avatarView;
}
- (UIView *)bubbleView {
if (!_bubbleView) {
_bubbleView = [UIView new];
_bubbleView.backgroundColor = [UIColor whiteColor];
_bubbleView.layer.cornerRadius = 18;
_bubbleView.layer.masksToBounds = YES;
}
return _bubbleView;
}
- (UILabel *)contentLabel {
if (!_contentLabel) {
_contentLabel = [UILabel new];
_contentLabel.numberOfLines = 0;
_contentLabel.font = [UIFont systemFontOfSize:15];
_contentLabel.textColor = [UIColor colorWithWhite:0.15 alpha:1.0];
}
return _contentLabel;
}
@end

View File

@@ -0,0 +1,17 @@
//
// KBGuideTopCell.h
// keyBoard
//
// 顶部固定引导 cell左侧头像 + 卡片文案)
//
#import "BaseCell.h"
NS_ASSUME_NONNULL_BEGIN
@interface KBGuideTopCell : BaseCell
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,156 @@
//
// KBGuideTopCell.m
// keyBoard
//
#import "KBGuideTopCell.h"
@interface KBGuideTopCell ()
//
@property (nonatomic, strong) UIView *avatarView;
//
@property (nonatomic, strong) UIView *cardView;
// /
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UILabel *descLabel;
@property (nonatomic, strong) UIView *line1;
@property (nonatomic, strong) UILabel *q1Label;
@property (nonatomic, strong) UIView *line2;
@property (nonatomic, strong) UILabel *q2Label;
@end
@implementation KBGuideTopCell
- (void)setupUI {
self.contentView.backgroundColor = [UIColor colorWithWhite:0.96 alpha:1.0];
//
[self.contentView addSubview:self.avatarView];
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.contentView).offset(16);
make.top.equalTo(self.contentView).offset(12);
make.width.height.mas_equalTo(36);
}];
//
[self.contentView addSubview:self.cardView];
[self.cardView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.avatarView.mas_right).offset(8);
make.right.lessThanOrEqualTo(self.contentView).offset(-32);
make.top.equalTo(self.contentView).offset(8);
make.bottom.equalTo(self.contentView).offset(-8);
}];
[self.cardView addSubview:self.titleLabel];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.equalTo(self.cardView).offset(12);
make.right.equalTo(self.cardView).offset(-12);
}];
[self.cardView addSubview:self.descLabel];
[self.descLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.titleLabel.mas_bottom).offset(6);
make.left.right.equalTo(self.titleLabel);
}];
//
[self.cardView addSubview:self.line1];
[self.cardView addSubview:self.q1Label];
[self.cardView addSubview:self.line2];
[self.cardView addSubview:self.q2Label];
[self.line1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.descLabel.mas_bottom).offset(10);
make.left.right.equalTo(self.cardView);
make.height.mas_equalTo(KB_ONE_PIXEL);
}];
[self.q1Label mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.line1.mas_bottom).offset(10);
make.left.right.equalTo(self.titleLabel);
}];
[self.line2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.q1Label.mas_bottom).offset(10);
make.left.right.equalTo(self.cardView);
make.height.mas_equalTo(KB_ONE_PIXEL);
}];
[self.q2Label mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.line2.mas_bottom).offset(10);
make.left.right.equalTo(self.titleLabel);
make.bottom.equalTo(self.cardView).offset(-14);
}];
}
#pragma mark - Lazy
- (UIView *)avatarView {
if (!_avatarView) {
_avatarView = [UIView new];
_avatarView.backgroundColor = [UIColor colorWithRed:0.23 green:0.47 blue:0.96 alpha:1.0];
_avatarView.layer.cornerRadius = 18;
_avatarView.layer.masksToBounds = YES;
}
return _avatarView;
}
- (UIView *)cardView {
if (!_cardView) {
_cardView = [UIView new];
_cardView.backgroundColor = [UIColor whiteColor];
_cardView.layer.cornerRadius = 18;
_cardView.layer.masksToBounds = YES;
}
return _cardView;
}
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [UILabel new];
_titleLabel.numberOfLines = 0;
_titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
_titleLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1.0];
_titleLabel.text = @"👋 欢迎使用『Lovekey 键盘』";
}
return _titleLabel;
}
- (UILabel *)descLabel {
if (!_descLabel) {
_descLabel = [UILabel new];
_descLabel.numberOfLines = 0;
_descLabel.font = [UIFont systemFontOfSize:14];
_descLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1.0];
_descLabel.text = @"点击任一对话去粘贴,选择任意回复方式去试用吧~";
}
return _descLabel;
}
- (UIView *)line1 { if (!_line1) { _line1 = [UIView new]; _line1.backgroundColor = [UIColor colorWithWhite:0.92 alpha:1.0]; } return _line1; }
- (UIView *)line2 { if (!_line2) { _line2 = [UIView new]; _line2.backgroundColor = [UIColor colorWithWhite:0.92 alpha:1.0]; } return _line2; }
- (UILabel *)q1Label {
if (!_q1Label) {
_q1Label = [UILabel new];
_q1Label.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
_q1Label.textColor = [UIColor blackColor];
_q1Label.text = @"在干嘛?";
}
return _q1Label;
}
- (UILabel *)q2Label {
if (!_q2Label) {
_q2Label = [UILabel new];
_q2Label.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
_q2Label.textColor = [UIColor blackColor];
_q2Label.text = @"我去洗澡了";
}
return _q2Label;
}
@end

View File

@@ -0,0 +1,19 @@
//
// KBGuideUserCell.h
// keyBoard
//
// 我方发送气泡 cell右侧紫色气泡
//
#import "BaseCell.h"
NS_ASSUME_NONNULL_BEGIN
@interface KBGuideUserCell : BaseCell
- (void)configText:(NSString *)text;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,60 @@
//
// KBGuideUserCell.m
// keyBoard
//
#import "KBGuideUserCell.h"
@interface KBGuideUserCell ()
@property (nonatomic, strong) UIView *bubbleView; //
@property (nonatomic, strong) UILabel *contentLabel;
@end
@implementation KBGuideUserCell
- (void)setupUI {
self.contentView.backgroundColor = [UIColor colorWithWhite:0.96 alpha:1.0];
[self.contentView addSubview:self.bubbleView];
[self.bubbleView mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.contentView).offset(-16);
make.top.equalTo(self.contentView).offset(8);
make.left.greaterThanOrEqualTo(self.contentView).offset(80);
make.bottom.equalTo(self.contentView).offset(-8);
}];
[self.bubbleView addSubview:self.contentLabel];
[self.contentLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.bubbleView).insets(UIEdgeInsetsMake(8, 10, 8, 10));
}];
}
- (void)configText:(NSString *)text {
self.contentLabel.text = text;
}
#pragma mark - Lazy
- (UIView *)bubbleView {
if (!_bubbleView) {
_bubbleView = [UIView new];
_bubbleView.backgroundColor = [UIColor colorWithRed:0.42 green:0.45 blue:0.98 alpha:1.0];
_bubbleView.layer.cornerRadius = 12;
_bubbleView.layer.masksToBounds = YES;
}
return _bubbleView;
}
- (UILabel *)contentLabel {
if (!_contentLabel) {
_contentLabel = [UILabel new];
_contentLabel.numberOfLines = 0;
_contentLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
_contentLabel.textColor = [UIColor whiteColor];
_contentLabel.textAlignment = NSTextAlignmentCenter;
}
return _contentLabel;
}
@end

View File

@@ -0,0 +1,16 @@
//
// KBGuideVC.h
// keyBoard
//
// Created by Mac on 2025/10/29.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface KBGuideVC : UIViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,256 @@
//
// 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