Files
keyboard/CustomKeyboard/KeyboardViewController.m
2025-10-30 20:23:34 +08:00

209 lines
7.1 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.

//
// KeyboardViewController.m
// CustomKeyboard
//
// Created by Mac on 2025/10/27.
//
#import "KeyboardViewController.h"
#import "KBKeyBoardMainView.h"
#import "KBKey.h"
#import "KBFunctionView.h"
#import "KBSettingView.h"
#import "Masonry.h"
static CGFloat KEYBOARDHEIGHT = 256 + 20;
@interface KeyboardViewController () <KBKeyBoardMainViewDelegate, KBFunctionViewDelegate>
@property (nonatomic, strong) UIButton *nextKeyboardButton; // 系统“下一个键盘”按钮(可选)
@property (nonatomic, strong) KBKeyBoardMainView *keyBoardMainView; // 功能面板视图点击工具栏第0个时显示
@property (nonatomic, strong) KBFunctionView *functionView; // 功能面板视图点击工具栏第0个时显示
@property (nonatomic, strong) KBSettingView *settingView; // 设置页
@end
@implementation KeyboardViewController
{
BOOL _kb_didTriggerLoginDeepLinkOnce;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self setupUI];
}
- (void)setupUI {
// 固定键盘整体高度
[self.view.heightAnchor constraintEqualToConstant:KEYBOARDHEIGHT].active = YES;
// 预置功能面板(默认隐藏),与键盘区域共享相同布局
self.functionView.hidden = YES;
[self.view addSubview:self.functionView];
[self.functionView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.view);
make.top.equalTo(self.view).offset(4);
make.bottom.equalTo(self.view.mas_bottom).offset(-4);
}];
[self.view addSubview:self.keyBoardMainView];
[self.keyBoardMainView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.view);
make.top.equalTo(self.view).offset(4);
make.bottom.equalTo(self.view.mas_bottom).offset(-4);
}];
}
#pragma mark - Private
/// 切换显示功能面板/键盘主视图
- (void)showFunctionPanel:(BOOL)show {
// 简单显隐切换,复用相同的布局区域
self.functionView.hidden = !show;
self.keyBoardMainView.hidden = show;
// 可选:把当前显示的视图置顶,避免层级遮挡
if (show) {
[self.view bringSubviewToFront:self.functionView];
} else {
[self.view bringSubviewToFront:self.keyBoardMainView];
}
}
/// 显示/隐藏设置页(高度与 keyBoardMainView 一致),右侧滑入/滑出
- (void)showSettingView:(BOOL)show {
if (show) {
// if (!self.settingView) {
self.settingView = [[KBSettingView alloc] init];
self.settingView.hidden = YES;
[self.view addSubview:self.settingView];
[self.settingView mas_makeConstraints:^(MASConstraintMaker *make) {
// 与键盘主视图完全等同的区域,保证高度、宽度一致
make.edges.equalTo(self.keyBoardMainView);
}];
[self.settingView.backButton addTarget:self action:@selector(onTapSettingsBack) forControlEvents:UIControlEventTouchUpInside];
// }
[self.view bringSubviewToFront:self.settingView];
// 以 keyBoardMainView 的实际宽度为准,避免首次添加时 self.view 宽度尚未计算
[self.view layoutIfNeeded];
CGFloat w = CGRectGetWidth(self.keyBoardMainView.bounds);
if (w <= 0) { w = CGRectGetWidth(self.view.bounds); }
if (w <= 0) { w = [UIScreen mainScreen].bounds.size.width; }
self.settingView.transform = CGAffineTransformMakeTranslation(w, 0);
self.settingView.hidden = NO;
[UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
self.settingView.transform = CGAffineTransformIdentity;
} completion:nil];
} else {
if (!self.settingView || self.settingView.hidden) return;
CGFloat w = CGRectGetWidth(self.keyBoardMainView.bounds);
if (w <= 0) { w = CGRectGetWidth(self.view.bounds); }
if (w <= 0) { w = [UIScreen mainScreen].bounds.size.width; }
[UIView animateWithDuration:0.22 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
self.settingView.transform = CGAffineTransformMakeTranslation(w, 0);
} completion:^(BOOL finished) {
self.settingView.hidden = YES;
}];
}
}
// MARK: - KBKeyBoardMainViewDelegate
- (void)keyBoardMainView:(KBKeyBoardMainView *)keyBoardMainView didTapKey:(KBKey *)key {
switch (key.type) {
case KBKeyTypeCharacter:
[self.textDocumentProxy insertText:key.output ?: key.title ?: @""]; break;
case KBKeyTypeBackspace:
[self.textDocumentProxy deleteBackward]; break;
case KBKeyTypeSpace:
[self.textDocumentProxy insertText:@" "]; break;
case KBKeyTypeReturn:
[self.textDocumentProxy insertText:@"\n"]; break;
case KBKeyTypeGlobe:
[self advanceToNextInputMode]; break;
case KBKeyTypeCustom:
// 点击自定义键如“AI”切换到功能面板
[self showFunctionPanel:YES];
break;
case KBKeyTypeModeChange:
case KBKeyTypeShift:
// 这些已在 KBKeyBoardMainView/KBKeyboardView 内部处理
break;
}
}
- (void)keyBoardMainView:(KBKeyBoardMainView *)keyBoardMainView didTapToolActionAtIndex:(NSInteger)index {
if (index == 0) {
[self showFunctionPanel:YES];
} else {
[self showFunctionPanel:NO];
}
}
- (void)keyBoardMainViewDidTapSettings:(KBKeyBoardMainView *)keyBoardMainView {
[self showSettingView:YES];
}
// MARK: - KBFunctionViewDelegate
- (void)functionView:(KBFunctionView *)functionView didTapToolActionAtIndex:(NSInteger)index {
// 需求:当 index == 0 时,切回键盘主视图
if (index == 0) {
[self showFunctionPanel:NO];
}
}
#pragma mark - lazy
- (KBKeyBoardMainView *)keyBoardMainView{
if (!_keyBoardMainView) {
_keyBoardMainView = [[KBKeyBoardMainView alloc] init];
_keyBoardMainView.delegate = self;
}
return _keyBoardMainView;
}
- (KBFunctionView *)functionView{
if (!_functionView) {
_functionView = [[KBFunctionView alloc] init];
_functionView.delegate = self; // 监听功能面板顶部Bar点击
}
return _functionView;
}
- (KBSettingView *)settingView {
if (!_settingView) {
_settingView = [[KBSettingView alloc] init];
}
return _settingView;
}
#pragma mark - Actions
- (void)onTapSettingsBack {
[self showSettingView:NO];
}
// 当键盘第一次显示时,尝试唤起主 App 以提示登录(由主 App 决定是否真的弹登录)。
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (!_kb_didTriggerLoginDeepLinkOnce) {
_kb_didTriggerLoginDeepLinkOnce = YES;
[self kb_tryOpenContainerForLoginIfNeeded];
}
}
- (void)kb_tryOpenContainerForLoginIfNeeded {
NSURL *url = [NSURL URLWithString:@"kbkeyboard://login?src=keyboard"];
if (!url) return;
__weak typeof(self) weakSelf = self;
[self.extensionContext openURL:url completionHandler:^(__unused BOOL success) {
// 即使失败也不重复尝试;避免打扰。
__unused typeof(weakSelf) selfStrong = weakSelf;
}];
}
@end