Files
keyboard/CustomKeyboard/View/KBKeyboardView/KBKeyboardInteractionHandler.m
2026-03-04 12:54:57 +08:00

157 lines
5.4 KiB
Objective-C

//
// KBKeyboardInteractionHandler.m
// CustomKeyboard
//
#import "KBKeyboardInteractionHandler.h"
#import "KBKeyButton.h"
#import "KBKey.h"
#import "KBKeyPreviewView.h"
static const NSTimeInterval kKBPreviewShowDuration = 0.08;
static const NSTimeInterval kKBPreviewHideDuration = 0.06;
@interface KBKeyboardInteractionHandler ()
@property (nonatomic, strong) KBKeyPreviewView *previewView;
@end
@implementation KBKeyboardInteractionHandler
- (UIView *)resolveHitView:(UIView *)hitView
point:(CGPoint)point
container:(UIView *)container
rowViews:(NSArray<UIView *> *)rowViews {
if ([hitView isKindOfClass:[KBKeyButton class]]) {
return hitView;
}
if ([self isHitInsideKeyRows:hitView rowViews:rowViews]) {
KBKeyButton *btn = [self nearestKeyButtonForPoint:point
container:container
rowViews:rowViews];
if (btn) { return btn; }
}
return hitView;
}
- (NSArray<KBKeyButton *> *)collectKeyButtonsInView:(UIView *)view {
if (!view) { return @[]; }
NSMutableArray<KBKeyButton *> *buttons = [NSMutableArray array];
[self collectKeyButtonsInView:view into:buttons];
return buttons.copy;
}
- (void)showPreviewForButton:(KBKeyButton *)button inContainer:(UIView *)container {
if (!button || !container) { return; }
KBKey *key = button.key;
if (key.type != KBKeyTypeCharacter) return;
if (!self.previewView) {
self.previewView = [[KBKeyPreviewView alloc] initWithFrame:CGRectZero];
self.previewView.hidden = YES;
[container addSubview:self.previewView];
} else if (self.previewView.superview != container) {
[container addSubview:self.previewView];
}
[self.previewView configureWithKey:key icon:button.iconView.image];
// 计算预览视图位置:在按钮上方稍微偏上
CGRect btnFrameInSelf = [button convertRect:button.bounds toView:container];
CGFloat previewWidth = 42;
CGFloat previewHeight = CGRectGetHeight(btnFrameInSelf) * 1.2;
CGFloat centerX = CGRectGetMidX(btnFrameInSelf);
CGFloat centerY = CGRectGetMinY(btnFrameInSelf) - previewHeight * 0.6;
self.previewView.frame = CGRectMake(0, 0, previewWidth, previewHeight);
self.previewView.center = CGPointMake(centerX, centerY);
self.previewView.alpha = 0.0;
self.previewView.hidden = NO;
[UIView animateWithDuration:kKBPreviewShowDuration
delay:0
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseOut
animations:^{
self.previewView.alpha = 1.0;
}
completion:nil];
}
- (void)hidePreview {
if (!self.previewView || self.previewView.isHidden) return;
[UIView animateWithDuration:kKBPreviewHideDuration
delay:0
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn
animations:^{
self.previewView.alpha = 0.0;
}
completion:^(BOOL finished) {
self.previewView.hidden = YES;
}];
}
- (void)bringPreviewToFrontIfNeededInContainer:(UIView *)container {
if (!container) { return; }
if (self.previewView && self.previewView.superview == container) {
[container bringSubviewToFront:self.previewView];
}
}
#pragma mark - Private
- (BOOL)isHitInsideKeyRows:(UIView *)hitView rowViews:(NSArray<UIView *> *)rowViews {
if (!hitView) { return NO; }
if ([rowViews containsObject:hitView]) { return YES; }
for (UIView *row in rowViews) {
if ([hitView isDescendantOfView:row]) { return YES; }
}
return NO;
}
- (KBKeyButton *)nearestKeyButtonForPoint:(CGPoint)point
container:(UIView *)container
rowViews:(NSArray<UIView *> *)rowViews {
if (!container) { return nil; }
KBKeyButton *best = nil;
CGFloat bestDistance = CGFLOAT_MAX;
UIView *targetRow = nil;
for (UIView *row in rowViews) {
CGRect rowFrame = [container convertRect:row.bounds fromView:row];
if (CGRectContainsPoint(rowFrame, point)) {
targetRow = row;
break;
}
}
NSArray<UIView *> *candidateRows = targetRow ? @[targetRow] : rowViews;
for (UIView *row in candidateRows) {
NSArray<KBKeyButton *> *buttons = [self collectKeyButtonsInView:row];
for (KBKeyButton *btn in buttons) {
CGRect frame = [container convertRect:btn.frame fromView:btn.superview];
CGFloat dx = point.x - CGRectGetMidX(frame);
CGFloat dy = point.y - CGRectGetMidY(frame);
CGFloat dist = (dx * dx) + (dy * dy);
if (dist < bestDistance) {
bestDistance = dist;
best = btn;
}
}
}
return best;
}
- (void)collectKeyButtonsInView:(UIView *)view
into:(NSMutableArray<KBKeyButton *> *)buttons {
for (UIView *sub in view.subviews) {
if ([sub isKindOfClass:[KBKeyButton class]]) {
[buttons addObject:(KBKeyButton *)sub];
continue;
}
if (sub.subviews.count > 0) {
[self collectKeyButtonsInView:sub into:buttons];
}
}
}
@end