测试假数据
This commit is contained in:
@@ -34,6 +34,12 @@ static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId";
|
||||
// 临时:点击标签后展示的“流式文本视图”与其删除按钮
|
||||
@property (nonatomic, strong, nullable) KBStreamTextView *streamTextView;
|
||||
@property (nonatomic, strong, nullable) UIButton *streamDeleteButton;
|
||||
// 流式测试数据 & 定时器
|
||||
@property (nonatomic, strong, nullable) NSTimer *streamMockTimer;
|
||||
@property (nonatomic, strong, nullable) NSArray<NSString *> *streamMockChunks;
|
||||
@property (nonatomic, assign) NSInteger streamMockIndex;
|
||||
@property (nonatomic, copy, nullable) NSString *streamMockSource; // 连续文本源(包含 \t 作为分段)
|
||||
@property (nonatomic, assign) NSInteger streamMockCursor; // 当前光标位置
|
||||
|
||||
// Data
|
||||
@property (nonatomic, strong) NSArray<NSString *> *itemsInternal;
|
||||
@@ -73,6 +79,7 @@ static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId";
|
||||
|
||||
- (void)dealloc {
|
||||
[self stopPasteboardMonitor];
|
||||
[self kb_stopMockStreaming];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
@@ -204,7 +211,7 @@ static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId";
|
||||
[self addSubview:sv];
|
||||
[sv mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(self.mas_left).offset(12);
|
||||
make.right.equalTo(self).offset(-12);
|
||||
make.right.equalTo(self.rightButtonContainer.mas_left).offset(-12);
|
||||
make.top.equalTo(self.pasteViewInternal.mas_bottom).offset(10);
|
||||
make.bottom.equalTo(self.mas_bottom).offset(-10);
|
||||
}];
|
||||
@@ -226,10 +233,14 @@ static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId";
|
||||
make.width.mas_greaterThanOrEqualTo(56);
|
||||
}];
|
||||
self.streamDeleteButton = del;
|
||||
|
||||
// 启动模拟“后端流”数据,用于查看 KBStreamTextView 展示效果
|
||||
[self kb_startMockStreamingWithSeed:title];
|
||||
}
|
||||
|
||||
- (void)kb_onTapStreamDelete {
|
||||
// 关闭并销毁流式视图,恢复标签列表
|
||||
[self kb_stopMockStreaming];
|
||||
[self.streamDeleteButton removeFromSuperview];
|
||||
self.streamDeleteButton = nil;
|
||||
[self.streamTextView removeFromSuperview];
|
||||
@@ -237,6 +248,56 @@ static NSString * const kKBFunctionTagCellId = @"KBFunctionTagCellId";
|
||||
self.collectionViewInternal.hidden = NO;
|
||||
}
|
||||
|
||||
#pragma mark - Mock Streaming
|
||||
|
||||
// 生成并按时间片推送模拟流数据到 KBStreamTextView
|
||||
- (void)kb_startMockStreamingWithSeed:(NSString *)seedTitle {
|
||||
[self kb_stopMockStreaming];
|
||||
|
||||
// 连续文本源(包含 \t 作为“分段”分隔符;\n 为换行),按 1~3 字随机切片推送
|
||||
NSString *t = seedTitle ?: @"高情商";
|
||||
self.streamMockSource = [NSString stringWithFormat:
|
||||
@"选择了标签:%@\n"
|
||||
"下面演示流式输出,每隔一小段时间来一截。第一段结束。\t"
|
||||
"第二段开始:这是一个较长的句子,会被拆成多个小块推送,直到遇到分隔符就换段。\n第二段结束。\t"
|
||||
"最后一段:右上角可随时关闭展示。\n演示完成。\t",
|
||||
t];
|
||||
self.streamMockCursor = 0;
|
||||
self.streamMockChunks = nil; // 废弃数组方式
|
||||
self.streamMockIndex = 0;
|
||||
|
||||
// 定时喂数据(主线程):每次随机 1~3 个字
|
||||
__weak typeof(self) weakSelf = self;
|
||||
self.streamMockTimer = [NSTimer scheduledTimerWithTimeInterval:0.20 repeats:YES block:^(NSTimer * _Nonnull timer) {
|
||||
__strong typeof(weakSelf) self = weakSelf; if (!self) { [timer invalidate]; return; }
|
||||
if (!self.streamTextView) { [timer invalidate]; return; }
|
||||
NSInteger total = (NSInteger)self.streamMockSource.length;
|
||||
if (self.streamMockCursor >= total) {
|
||||
// 结束:收尾裁剪当前段
|
||||
[self.streamTextView finishStreaming];
|
||||
[timer invalidate];
|
||||
self.streamMockTimer = nil;
|
||||
return;
|
||||
}
|
||||
// 随机 1~3 个字(不跨越结尾),逐段追加
|
||||
uint32_t step = arc4random_uniform(3) + 1; // 1..3
|
||||
NSInteger remain = total - self.streamMockCursor;
|
||||
NSInteger take = MIN((NSInteger)step, remain);
|
||||
NSString *piece = [self.streamMockSource substringWithRange:NSMakeRange(self.streamMockCursor, take)];
|
||||
self.streamMockCursor += take;
|
||||
[self.streamTextView appendStreamText:piece ?: @""];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)kb_stopMockStreaming {
|
||||
[self.streamMockTimer invalidate];
|
||||
self.streamMockTimer = nil;
|
||||
self.streamMockChunks = nil;
|
||||
self.streamMockIndex = 0;
|
||||
self.streamMockSource = nil;
|
||||
self.streamMockCursor = 0;
|
||||
}
|
||||
|
||||
// 用户点击功能标签:优先 UL 拉起主App,失败再 Scheme;两次都失败则提示开启完全访问。
|
||||
// 若已开启“完全访问”,则直接在键盘侧创建 KBStreamTextView,并在其右上角提供删除按钮关闭。
|
||||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
//
|
||||
|
||||
#import "KBStreamTextView.h"
|
||||
#import "KBResponderUtils.h" // 通过响应链找到 UIInputViewController,并将文本输出到宿主
|
||||
|
||||
@interface KBStreamTextView ()
|
||||
|
||||
@@ -170,7 +171,7 @@
|
||||
label.textColor = self.labelTextColor;
|
||||
label.userInteractionEnabled = YES;
|
||||
label.text = @"";
|
||||
|
||||
label.backgroundColor = [UIColor redColor];
|
||||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleLabelTap:)];
|
||||
[label addGestureRecognizer:tap];
|
||||
|
||||
@@ -256,8 +257,19 @@
|
||||
UILabel *label = (UILabel *)tap.view;
|
||||
if (![label isKindOfClass:[UILabel class]]) { return; }
|
||||
NSInteger index = [self.labels indexOfObject:label];
|
||||
NSString *text = label.text ?: @"";
|
||||
if (index != NSNotFound && self.onLabelTap) {
|
||||
self.onLabelTap(index, label.text ?: @"");
|
||||
self.onLabelTap(index, text);
|
||||
}
|
||||
|
||||
// 将文本发送到当前宿主应用的输入框(搜索框/TextView 等)
|
||||
// 注:键盘扩展无需“完全访问”也可 insertText:
|
||||
UIInputViewController *ivc = KBFindInputViewController(self);
|
||||
if (ivc) {
|
||||
id<UITextDocumentProxy> proxy = ivc.textDocumentProxy;
|
||||
if (text.length > 0 && [proxy conformsToProtocol:@protocol(UITextDocumentProxy)]) {
|
||||
[proxy insertText:text];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user