添加长按多文字删除

This commit is contained in:
2025-12-19 16:24:47 +08:00
parent c108077178
commit e0379d3717

View File

@@ -17,6 +17,11 @@
static const NSTimeInterval kKBBackspaceLongPressMinDuration = 0.35;
static const NSTimeInterval kKBBackspaceRepeatInterval = 0.06;
static const NSTimeInterval kKBBackspaceChunkStartDelay = 0.7;
static const NSTimeInterval kKBBackspaceChunkRepeatInterval = 0.1;
static const NSTimeInterval kKBBackspaceChunkFastDelay = 1.4;
static const NSInteger kKBBackspaceChunkSize = 6;
static const NSInteger kKBBackspaceChunkSizeFast = 12;
static const NSTimeInterval kKBPreviewShowDuration = 0.08;
static const NSTimeInterval kKBPreviewHideDuration = 0.06;
@@ -28,6 +33,14 @@ static const CGFloat kKBSpaceWidthMultiplier = 3.0;
//
static const CGFloat kKBLettersRow2EdgeSpacerMultiplier = 0.5;
typedef NS_ENUM(NSInteger, KBBackspaceChunkClass) {
KBBackspaceChunkClassUnknown = 0,
KBBackspaceChunkClassWhitespace,
KBBackspaceChunkClassASCIIWord,
KBBackspaceChunkClassPunctuation,
KBBackspaceChunkClassOther
};
@interface KBKeyboardView ()
@property (nonatomic, strong) UIView *row1;
@property (nonatomic, strong) UIView *row2;
@@ -36,6 +49,7 @@ static const CGFloat kKBLettersRow2EdgeSpacerMultiplier = 0.5;
@property (nonatomic, strong) NSArray<NSArray<KBKey *> *> *keysForRows;
// 退使 NSTimer GCD
@property (nonatomic, assign) BOOL backspaceHoldActive;
@property (nonatomic, assign) NSTimeInterval backspaceHoldStartTime;
@property (nonatomic, strong) KBKeyPreviewView *previewView;
@end
@@ -666,11 +680,12 @@ edgeSpacerMultiplier:(CGFloat)edgeSpacerMultiplier {
}];
}
// 退使 NSTimer/DisplayLink
// 退使 NSTimer/DisplayLink
- (void)onBackspaceLongPress:(UILongPressGestureRecognizer *)gr {
switch (gr.state) {
case UIGestureRecognizerStateBegan: {
self.backspaceHoldActive = YES;
self.backspaceHoldStartTime = [NSDate date].timeIntervalSinceReferenceDate;
[self kb_backspaceStep];
} break;
case UIGestureRecognizerStateEnded:
@@ -692,17 +707,83 @@ edgeSpacerMultiplier:(CGFloat)edgeSpacerMultiplier {
id<UITextDocumentProxy> proxy = ivc.textDocumentProxy;
NSString *before = proxy.documentContextBeforeInput ?: @"";
if (before.length <= 0) { self.backspaceHoldActive = NO; return; }
[proxy deleteBackward]; // 1
NSTimeInterval elapsed = [NSDate date].timeIntervalSinceReferenceDate - self.backspaceHoldStartTime;
NSInteger deleteCount = [self kb_backspaceDeleteCountForContext:before elapsed:elapsed];
for (NSInteger i = 0; i < deleteCount; i++) {
[proxy deleteBackward];
}
NSTimeInterval interval = [self kb_backspaceRepeatIntervalForElapsed:elapsed];
__weak typeof(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(kKBBackspaceRepeatInterval * NSEC_PER_SEC)),
(int64_t)(interval * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
__strong typeof(weakSelf) selfStrong = weakSelf;
[selfStrong kb_backspaceStep];
});
}
- (NSTimeInterval)kb_backspaceRepeatIntervalForElapsed:(NSTimeInterval)elapsed {
if (elapsed >= kKBBackspaceChunkStartDelay) {
return kKBBackspaceChunkRepeatInterval;
}
return kKBBackspaceRepeatInterval;
}
- (NSInteger)kb_backspaceDeleteCountForContext:(NSString *)context elapsed:(NSTimeInterval)elapsed {
if (elapsed < kKBBackspaceChunkStartDelay) {
return 1;
}
NSInteger maxCount = (elapsed >= kKBBackspaceChunkFastDelay)
? kKBBackspaceChunkSizeFast : kKBBackspaceChunkSize;
return [self kb_backspaceChunkDeleteCountForContext:context maxCount:maxCount];
}
- (NSInteger)kb_backspaceChunkDeleteCountForContext:(NSString *)context maxCount:(NSInteger)maxCount {
if (context.length == 0) { return 1; }
static NSCharacterSet *whitespaceSet = nil;
static NSCharacterSet *asciiWordSet = nil;
static NSCharacterSet *punctuationSet = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
whitespaceSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
asciiWordSet = [NSCharacterSet characterSetWithCharactersInString:
@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"];
punctuationSet = [NSCharacterSet punctuationCharacterSet];
});
__block NSInteger deleteCount = 0;
__block KBBackspaceChunkClass chunkClass = KBBackspaceChunkClassUnknown;
[context enumerateSubstringsInRange:NSMakeRange(0, context.length)
options:NSStringEnumerationByComposedCharacterSequences | NSStringEnumerationReverse
usingBlock:^(NSString *substring, __unused NSRange substringRange, __unused NSRange enclosingRange, BOOL *stop) {
if (substring.length == 0) { return; }
KBBackspaceChunkClass currentClass = KBBackspaceChunkClassOther;
if ([substring rangeOfCharacterFromSet:whitespaceSet].location != NSNotFound) {
currentClass = KBBackspaceChunkClassWhitespace;
} else if ([substring rangeOfCharacterFromSet:asciiWordSet].location != NSNotFound) {
currentClass = KBBackspaceChunkClassASCIIWord;
} else if ([substring rangeOfCharacterFromSet:punctuationSet].location != NSNotFound) {
currentClass = KBBackspaceChunkClassPunctuation;
}
if (chunkClass == KBBackspaceChunkClassUnknown) {
chunkClass = currentClass;
} else if (chunkClass != currentClass) {
*stop = YES;
return;
}
deleteCount += 1;
if (deleteCount >= maxCount) {
*stop = YES;
}
}];
return MAX(deleteCount, 1);
}
#pragma mark - Lazy
- (UIView *)row1 { if (!_row1) _row1 = [UIView new]; return _row1; }