Files
keyboard/CustomKeyboard/View/Function/KBStreamOverlayView.m

151 lines
4.9 KiB
Mathematica
Raw Normal View History

2025-11-12 16:03:30 +08:00
//
// KBStreamOverlayView.m
//
#import "KBStreamOverlayView.h"
#import "KBStreamTextView.h"
#import "Masonry.h"
@interface KBStreamOverlayView ()
@property (nonatomic, strong) KBStreamTextView *textViewInternal;
@property (nonatomic, strong) UIButton *closeButton;
2025-12-09 16:12:54 +08:00
// &
@property (nonatomic, strong) NSMutableString *pendingText;
@property (nonatomic, strong) NSTimer *streamTimer;
@property (nonatomic, assign) NSInteger charsPerTick; //
// SSE done
@property (nonatomic, assign) BOOL streamDidReceiveDone;
2025-11-12 16:03:30 +08:00
@end
@implementation KBStreamOverlayView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.92];
self.layer.cornerRadius = 12.0; self.layer.masksToBounds = YES;
[self addSubview:self.textViewInternal];
[self addSubview:self.closeButton];
[self.textViewInternal mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.mas_left).offset(0);
make.right.equalTo(self.mas_right).offset(0);
make.bottom.equalTo(self.mas_bottom).offset(0);
make.top.equalTo(self.mas_top).offset(0);
}];
[self.closeButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.mas_top).offset(8);
make.right.equalTo(self.mas_right).offset(-8);
make.height.mas_equalTo(28);
make.width.mas_greaterThanOrEqualTo(56);
}];
2025-12-09 16:12:54 +08:00
_pendingText = [NSMutableString string];
_charsPerTick = 2; // 1~2
_streamDidReceiveDone = NO;
2025-11-12 16:03:30 +08:00
}
return self;
}
- (KBStreamTextView *)textViewInternal {
if (!_textViewInternal) {
_textViewInternal = [[KBStreamTextView alloc] init];
}
return _textViewInternal;
}
- (UIButton *)closeButton {
if (!_closeButton) {
UIButton *del = [UIButton buttonWithType:UIButtonTypeSystem];
del.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.35];
del.layer.cornerRadius = 14; del.layer.masksToBounds = YES;
del.titleLabel.font = [UIFont systemFontOfSize:13 weight:UIFontWeightSemibold];
2025-12-05 21:54:10 +08:00
[del setTitle:KBLocalized(@"common_back") forState:UIControlStateNormal];
2025-11-12 16:03:30 +08:00
[del setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[del addTarget:self action:@selector(onTapClose) forControlEvents:UIControlEventTouchUpInside];
_closeButton = del;
}
return _closeButton;
}
- (void)onTapClose {
if ([self.delegate respondsToSelector:@selector(streamOverlayDidTapClose:)]) {
[self.delegate streamOverlayDidTapClose:self];
}
}
- (void)appendChunk:(NSString *)text {
if (text.length == 0) return;
2025-12-09 16:12:54 +08:00
if (![NSThread isMainThread]) {
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf appendChunk:text];
});
return;
}
[self.pendingText appendString:text];
[self startStreamTimerIfNeeded];
}
- (void)startStreamTimerIfNeeded {
if (self.streamTimer) return;
self.streamTimer = [NSTimer scheduledTimerWithTimeInterval:0.02
target:self
selector:@selector(handleStreamTick)
userInfo:nil
repeats:YES];
}
- (void)stopStreamTimer {
[self.streamTimer invalidate];
self.streamTimer = nil;
}
- (void)handleStreamTick {
if (self.pendingText.length == 0) {
// done finish
if (self.streamDidReceiveDone) {
[self.textViewInternal finishStreaming];
}
[self stopStreamTimer];
return;
}
NSInteger len = MIN(self.charsPerTick, self.pendingText.length);
NSString *slice = [self.pendingText substringToIndex:len];
[self.pendingText deleteCharactersInRange:NSMakeRange(0, len)];
[self.textViewInternal appendStreamText:slice];
2025-11-12 16:03:30 +08:00
}
2025-12-09 16:12:54 +08:00
2025-11-12 16:03:30 +08:00
- (void)finish {
2025-12-09 16:12:54 +08:00
if (![NSThread isMainThread]) {
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf finish];
});
return;
}
//
self.streamDidReceiveDone = YES;
//
if (self.pendingText.length == 0) {
[self stopStreamTimer];
[self.textViewInternal finishStreaming];
}
// handleStreamTick pendingText
// pendingText == 0 streamDidReceiveDone == YES finishStreaming
2025-11-12 16:03:30 +08:00
}
2025-12-09 16:12:54 +08:00
2025-11-12 16:03:30 +08:00
- (KBStreamTextView *)textView { return self.textViewInternal; }
@end