objective-c - 如何欺骗 OS X 应用程序认为鼠标是手指?

标签 objective-c macos cocoa

我正在编写一个包含 Collection View 的 Mac 应用程序。此应用程序将在大型触摸屏显示器上运行(55" EP series 来自 Planar)。由于硬件限制,触摸屏不发送滚动事件(甚至任何多点触控事件)。 我怎样才能让应用程序认为“鼠标按下+拖动”与“鼠标滚动”相同?

我通过子类化 NSCollectionView 并在其中实现我自己的 NSPanGestureRecognizer 处理程序,让它工作了一半。不幸的是,结果很笨拙,没有正常 OS X 滚动的感觉(即,滚动末尾的速度效果,或内容末尾的滚动弹跳)。

@implementation UCTouchScrollCollectionView
...
- (IBAction)showGestureForScrollGestureRecognizer:(NSPanGestureRecognizer *)recognizer
{
    CGPoint location = [recognizer locationInView:self];

    if (recognizer.state == NSGestureRecognizerStateBegan) {

        touchStartPt = location;
        startOrigin = [(NSClipView*)[self superview] documentVisibleRect].origin;

    } else if (recognizer.state == NSGestureRecognizerStateEnded) {

        /* Some notes here about a future feature: the Scroll Bounce
           I don't want to have to reinvent the wheel here, but it
           appears I already am. Crud.

           1. when the touch ends, get the velocity in view
           2. Using the velocity and a constant "deceleration" factor, you can determine
               a. The time taken to decelerate to 0 velocity
               b. the distance travelled in that time
           3. If the final scroll point is out of bounds, update it.
           4. set up an animation block to scroll the document to that point. Make sure it uses the proper easing to feel "natural".
           5. make sure you retain a pointer or something to that animation so that a touch DURING the animation will cancel it (is this even possible?)
        */

        [self.scrollDelegate.pointSmoother clearPoints];
        refreshDelegateTriggered = NO;

    } else  if (recognizer.state == NSGestureRecognizerStateChanged) {

        CGFloat dx = 0;
        CGFloat dy = (startOrigin.y - self.scrollDelegate.scrollScaling * (location.y - touchStartPt.y));
        NSPoint scrollPt = NSMakePoint(dx, dy);

        [self.scrollDelegate.pointSmoother addPoint:scrollPt];
        NSPoint smoothedPoint = [self.scrollDelegate.pointSmoother getSmoothedPoint];
        [self scrollPoint:smoothedPoint];

        CGFloat end = self.frame.size.height - self.superview.frame.size.height;
        CGFloat threshold = self.superview.frame.size.height * kUCPullToRefreshScreenFactor;
        if (smoothedPoint.y + threshold >= end &&
            !refreshDelegateTriggered) {
            NSLog(@"trigger pull to refresh");
            refreshDelegateTriggered = YES;
            [self.refreshDelegate scrollViewReachedBottom:self];
        }
    }
}

关于此实现的注意事项:我将 scrollScalingpointSmoother 放在一起尝试改进滚动 UX。我使用的触摸屏是基于 IR 的,并且会变得非常抖动(尤其是在太阳出来的时候)。

如果相关:我在 Yosemite beta (14A329r) 上使用 Xcode 6 beta 6 (6A280e),我的构建目标是 10.10。

谢谢!

最佳答案

我成功地使用了 NSPanGestureRecognizer 并模拟了触控板滚轮事件。如果你很好地模拟它们,你将“免费”从 NSScrollView 获得弹跳。

我没有公共(public)代码,但我发现最好的资源是在下面模拟动量滚动的单元测试中解释 NSScrollView 的期望。 (请参阅此处的 mouseScrollByWithWheelAndMomentumPhases)。

https://github.com/WebKit/webkit/blob/master/LayoutTests/fast/scrolling/latching/scroll-iframe-in-overflow.html

mouseScrollByWithWheelAndMomentumPhases 的实现提供了一些关于如何在低级别合成滚动事件的提示。我发现我需要做的一项补充是在事件中实际设置一个递增的时间戳,以使 ScrollView 开始播放。

https://github.com/WebKit/webkit/blob/master/Tools/WebKitTestRunner/mac/EventSenderProxy.mm

最后,为了实际创建衰减速度,我使用了 POPDecayAnimation 并调整了 NSPanGestureRecognizer 的速度,使其感觉相似。它并不完美,但它确实符合 NSScrollView 的弹跳。

关于objective-c - 如何欺骗 OS X 应用程序认为鼠标是手指?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25516280/

相关文章:

Objective-c:要枚举的 NSString

cocoa - CorePlot 中的 CPTScatterPlotDelegate 崩溃

objective-c - OSX Lion libz 解压文件结构发生变化

ios - 在 NSString stringWithFormat 上崩溃

ios - 通过谓词进行整数比较

ios - UINavigationBar 的奇怪行为

linux - 描述 xmm 寄存器中未传递给 rax 的浮点参数数量的整数

macos - Mac 64 位编译器常量

ios - Polidea/ios-class-guard - 如何排除外部类

macos - Snow Leopard 中的旧 Mac 扩展属性