From 9101ffaab0647a0e71ee471d9629ca9a05f675de Mon Sep 17 00:00:00 2001 From: CodeST <694468528@qq.com> Date: Wed, 29 Oct 2025 16:26:57 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8C=87=E5=BC=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- keyBoard.xcodeproj/project.pbxproj | 24 +++ keyBoard/Class/Home/VC/HomeVC.m | 6 + keyBoard/KBGuideKFCell.h | 20 +++ keyBoard/KBGuideKFCell.m | 77 ++++++++++ keyBoard/KBGuideTopCell.h | 17 +++ keyBoard/KBGuideTopCell.m | 156 ++++++++++++++++++++ keyBoard/KBGuideUserCell.h | 19 +++ keyBoard/KBGuideUserCell.m | 60 ++++++++ keyBoard/KBGuideVC.h | 16 ++ keyBoard/KBGuideVC.m | 227 +++++++++++++++++++++++++++++ keyBoard/KeyBoardPrefixHeader.pch | 63 ++++++++ 11 files changed, 685 insertions(+) create mode 100644 keyBoard/KBGuideKFCell.h create mode 100644 keyBoard/KBGuideKFCell.m create mode 100644 keyBoard/KBGuideTopCell.h create mode 100644 keyBoard/KBGuideTopCell.m create mode 100644 keyBoard/KBGuideUserCell.h create mode 100644 keyBoard/KBGuideUserCell.m create mode 100644 keyBoard/KBGuideVC.h create mode 100644 keyBoard/KBGuideVC.m diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index 7faef9b..4eb772f 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -31,6 +31,10 @@ 04FC95D22EB1E7AE007BD342 /* MyVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95D12EB1E7AE007BD342 /* MyVC.m */; }; 04FC95D72EB1EA16007BD342 /* BaseTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95D62EB1EA16007BD342 /* BaseTableView.m */; }; 04FC95D82EB1EA16007BD342 /* BaseCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95D42EB1EA16007BD342 /* BaseCell.m */; }; + 04FC95DD2EB202A3007BD342 /* KBGuideVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95DC2EB202A3007BD342 /* KBGuideVC.m */; }; + 04FC97002EB30A00007BD342 /* KBGuideTopCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC96FF2EB30A00007BD342 /* KBGuideTopCell.m */; }; + 04FC97032EB30A00007BD342 /* KBGuideKFCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC97022EB30A00007BD342 /* KBGuideKFCell.m */; }; + 04FC97062EB30A00007BD342 /* KBGuideUserCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC97052EB30A00007BD342 /* KBGuideUserCell.m */; }; 7A36414DFDA5BEC9B7D2E318 /* Pods_CustomKeyboard.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C1092FB2B452F95B15D4263 /* Pods_CustomKeyboard.framework */; }; A1B2C3D42EB0A0A100000001 /* KBFunctionTagCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D32EB0A0A100000001 /* KBFunctionTagCell.m */; }; A1B2C3E22EB0C0A100000001 /* KBNetworkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3E12EB0C0A100000001 /* KBNetworkManager.m */; }; @@ -109,6 +113,14 @@ 04FC95D42EB1EA16007BD342 /* BaseCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BaseCell.m; sourceTree = ""; }; 04FC95D52EB1EA16007BD342 /* BaseTableView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BaseTableView.h; sourceTree = ""; }; 04FC95D62EB1EA16007BD342 /* BaseTableView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BaseTableView.m; sourceTree = ""; }; + 04FC95DB2EB202A3007BD342 /* KBGuideVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBGuideVC.h; sourceTree = ""; }; + 04FC95DC2EB202A3007BD342 /* KBGuideVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBGuideVC.m; sourceTree = ""; }; + 04FC96FE2EB30A00007BD342 /* KBGuideTopCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBGuideTopCell.h; sourceTree = ""; }; + 04FC96FF2EB30A00007BD342 /* KBGuideTopCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBGuideTopCell.m; sourceTree = ""; }; + 04FC97012EB30A00007BD342 /* KBGuideKFCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBGuideKFCell.h; sourceTree = ""; }; + 04FC97022EB30A00007BD342 /* KBGuideKFCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBGuideKFCell.m; sourceTree = ""; }; + 04FC97042EB30A00007BD342 /* KBGuideUserCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBGuideUserCell.h; sourceTree = ""; }; + 04FC97052EB30A00007BD342 /* KBGuideUserCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBGuideUserCell.m; sourceTree = ""; }; 2C1092FB2B452F95B15D4263 /* Pods_CustomKeyboard.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CustomKeyboard.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51FE7C4C42C2255B3C1C4128 /* Pods-keyBoard.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-keyBoard.release.xcconfig"; path = "Target Support Files/Pods-keyBoard/Pods-keyBoard.release.xcconfig"; sourceTree = ""; }; 727EC7532EAF848B00B36487 /* keyBoard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = keyBoard.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -149,6 +161,14 @@ 04C6EAE32EAF942E0089C901 /* VC */, 04C6EAAC2EAF86530089C901 /* AppDelegate.h */, 04C6EAAD2EAF86530089C901 /* AppDelegate.m */, + 04FC95DB2EB202A3007BD342 /* KBGuideVC.h */, + 04FC95DC2EB202A3007BD342 /* KBGuideVC.m */, + 04FC96FE2EB30A00007BD342 /* KBGuideTopCell.h */, + 04FC96FF2EB30A00007BD342 /* KBGuideTopCell.m */, + 04FC97012EB30A00007BD342 /* KBGuideKFCell.h */, + 04FC97022EB30A00007BD342 /* KBGuideKFCell.m */, + 04FC97042EB30A00007BD342 /* KBGuideUserCell.h */, + 04FC97052EB30A00007BD342 /* KBGuideUserCell.m */, 04C6EAAE2EAF86530089C901 /* Assets.xcassets */, 04C6EAAF2EAF86530089C901 /* Info.plist */, 04C6EAB12EAF86530089C901 /* LaunchScreen.storyboard */, @@ -612,6 +632,10 @@ 04FC95D72EB1EA16007BD342 /* BaseTableView.m in Sources */, 04FC95D82EB1EA16007BD342 /* BaseCell.m in Sources */, 04FC95C92EB1E4C9007BD342 /* BaseNavigationController.m in Sources */, + 04FC95DD2EB202A3007BD342 /* KBGuideVC.m in Sources */, + 04FC97002EB30A00007BD342 /* KBGuideTopCell.m in Sources */, + 04FC97032EB30A00007BD342 /* KBGuideKFCell.m in Sources */, + 04FC97062EB30A00007BD342 /* KBGuideUserCell.m in Sources */, 04FC95CF2EB1E7A1007BD342 /* HomeVC.m in Sources */, 04C6EABF2EAF86530089C901 /* main.m in Sources */, 04FC95CC2EB1E780007BD342 /* BaseTabBarController.m in Sources */, diff --git a/keyBoard/Class/Home/VC/HomeVC.m b/keyBoard/Class/Home/VC/HomeVC.m index c9319e5..3e06425 100644 --- a/keyBoard/Class/Home/VC/HomeVC.m +++ b/keyBoard/Class/Home/VC/HomeVC.m @@ -6,6 +6,7 @@ // #import "HomeVC.h" +#import "KBGuideVC.h" @interface HomeVC () @@ -27,6 +28,11 @@ [self.view addSubview:label]; } +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ + KBGuideVC *vc = [[KBGuideVC alloc] init]; + [self.navigationController pushViewController:vc animated:true]; +} + /* #pragma mark - Navigation diff --git a/keyBoard/KBGuideKFCell.h b/keyBoard/KBGuideKFCell.h new file mode 100644 index 0000000..5871658 --- /dev/null +++ b/keyBoard/KBGuideKFCell.h @@ -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 + diff --git a/keyBoard/KBGuideKFCell.m b/keyBoard/KBGuideKFCell.m new file mode 100644 index 0000000..14a9ac2 --- /dev/null +++ b/keyBoard/KBGuideKFCell.m @@ -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 + diff --git a/keyBoard/KBGuideTopCell.h b/keyBoard/KBGuideTopCell.h new file mode 100644 index 0000000..2cccbbb --- /dev/null +++ b/keyBoard/KBGuideTopCell.h @@ -0,0 +1,17 @@ +// +// KBGuideTopCell.h +// keyBoard +// +// 顶部固定引导 cell(左侧头像 + 卡片文案) +// + +#import "BaseCell.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface KBGuideTopCell : BaseCell + +@end + +NS_ASSUME_NONNULL_END + diff --git a/keyBoard/KBGuideTopCell.m b/keyBoard/KBGuideTopCell.m new file mode 100644 index 0000000..e839946 --- /dev/null +++ b/keyBoard/KBGuideTopCell.m @@ -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 + diff --git a/keyBoard/KBGuideUserCell.h b/keyBoard/KBGuideUserCell.h new file mode 100644 index 0000000..e4837c4 --- /dev/null +++ b/keyBoard/KBGuideUserCell.h @@ -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 + diff --git a/keyBoard/KBGuideUserCell.m b/keyBoard/KBGuideUserCell.m new file mode 100644 index 0000000..88dd15c --- /dev/null +++ b/keyBoard/KBGuideUserCell.m @@ -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 + diff --git a/keyBoard/KBGuideVC.h b/keyBoard/KBGuideVC.h new file mode 100644 index 0000000..38950da --- /dev/null +++ b/keyBoard/KBGuideVC.h @@ -0,0 +1,16 @@ +// +// KBGuideVC.h +// keyBoard +// +// Created by Mac on 2025/10/29. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface KBGuideVC : UIViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/keyBoard/KBGuideVC.m b/keyBoard/KBGuideVC.m new file mode 100644 index 0000000..db0a815 --- /dev/null +++ b/keyBoard/KBGuideVC.m @@ -0,0 +1,227 @@ +// +// 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 () + +@property (nonatomic, strong) BaseTableView *tableView; // 列表(继承 BaseTableView) +@property (nonatomic, strong) UIView *inputBar; // 底部输入容器 +@property (nonatomic, strong) UITextField *textField; // 输入框 +@property (nonatomic, strong) MASConstraint *inputBarBottom;// 输入栏底部约束 + +@property (nonatomic, strong) NSMutableArray *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.view); + }]; + + [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]; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + + +#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 *)items { + if (!_items) { _items = @[].mutableCopy; } + return _items; +} + +@end diff --git a/keyBoard/KeyBoardPrefixHeader.pch b/keyBoard/KeyBoardPrefixHeader.pch index 387dde9..ab9c206 100644 --- a/keyBoard/KeyBoardPrefixHeader.pch +++ b/keyBoard/KeyBoardPrefixHeader.pch @@ -16,11 +16,74 @@ /// 三方 #import +/// 系统 +#import + /// 项目 #import "BaseNavigationController.h" #import "BaseTableView.h" #import "BaseCell.h" +//-----------------------------------------------宏定义全局----------------------------------------------------------/ +/// UI 尺寸/设备宏 +// 屏幕尺寸 +#define KB_SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width) +#define KB_SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height) +#define KB_ONE_PIXEL (1.0 / [UIScreen mainScreen].scale) + +// 基础高度 +#define KB_NAVBAR_BASE_HEIGHT 44.0 +#define KB_TABBAR_BASE_HEIGHT 49.0 + +// 当前 KeyWindow(iOS 13 场景化兼容) +static inline UIWindow *KB_KeyWindow(void) { + UIWindow *window = nil; + if (@available(iOS 13.0, *)) { + for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) { + if (scene.activationState == UISceneActivationStateForegroundActive && [scene isKindOfClass:[UIWindowScene class]]) { + UIWindowScene *ws = (UIWindowScene *)scene; + for (UIWindow *w in ws.windows) { + if (w.isKeyWindow) { window = w; break; } + } + if (window) { break; } + } + } + if (!window) { window = UIApplication.sharedApplication.windows.firstObject; } + } else { + window = UIApplication.sharedApplication.keyWindow; + } + return window; +} + +// Safe Area +static inline UIEdgeInsets KB_SafeAreaInsets(void) { + if (@available(iOS 11.0, *)) { + UIWindow *w = KB_KeyWindow(); + return w ? w.safeAreaInsets : UIEdgeInsetsZero; + } + return UIEdgeInsetsZero; +} + +static inline CGFloat KB_SafeAreaTop(void) { return KB_SafeAreaInsets().top; } +static inline CGFloat KB_SafeAreaBottom(void) { return KB_SafeAreaInsets().bottom; } + +// 状态栏高度(iOS13+ 兼容) +static inline CGFloat KB_StatusBarHeight(void) { + if (@available(iOS 13.0, *)) { + UIWindow *w = KB_KeyWindow(); + UIStatusBarManager *m = w.windowScene.statusBarManager; + return m ? m.statusBarFrame.size.height : 0.0; + } else { + return UIApplication.sharedApplication.statusBarFrame.size.height; + } +} + +// 导航栏+状态栏总高度、TabBar 高度、是否异形屏 +#define KB_STATUSBAR_HEIGHT (KB_StatusBarHeight()) +#define KB_NAV_TOTAL_HEIGHT (KB_NAVBAR_BASE_HEIGHT + KB_StatusBarHeight()) +#define KB_TABBAR_HEIGHT (KB_TABBAR_BASE_HEIGHT + KB_SafeAreaBottom()) +#define KB_IS_IPHONEX_SERIES (KB_SafeAreaBottom() > 0.0) + #endif /* KeyBoardPrefixHeader_pch */