Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/macos/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 如何欺骗OSX应用程序,使其认为鼠标是手指?_Objective C_Macos_Cocoa - Fatal编程技术网

Objective c 如何欺骗OSX应用程序,使其认为鼠标是手指?

Objective c 如何欺骗OSX应用程序,使其认为鼠标是手指?,objective-c,macos,cocoa,Objective C,Macos,Cocoa,我正在编写一个包含集合视图的Mac应用程序。此应用程序将在大型触摸屏上运行(从平面)。由于硬件限制,触摸屏不会发送滚动事件(甚至任何多点触摸事件)我如何才能欺骗应用程序,使其认为“鼠标向下+拖动”与“鼠标滚动”相同? 通过子类化NSCollectionView并在其中实现我自己的nsPangestureRecogener处理程序,我让它工作了一半。不幸的是,结果是笨重的,没有正常OSX滚动的感觉(即滚动结束时的速度效果,或内容结束时的滚动反弹) 关于这个实现的一个注意事项:我把scrollSca

我正在编写一个包含集合视图的Mac应用程序。此应用程序将在大型触摸屏上运行(从平面)。由于硬件限制,触摸屏不会发送滚动事件(甚至任何多点触摸事件)我如何才能欺骗应用程序,使其认为“鼠标向下+拖动”与“鼠标滚动”相同?

通过子类化NSCollectionView并在其中实现我自己的nsPangestureRecogener处理程序,我让它工作了一半。不幸的是,结果是笨重的,没有正常OSX滚动的感觉(即滚动结束时的速度效果,或内容结束时的滚动反弹)

关于这个实现的一个注意事项:我把
scrollScaling
pointSmoother
放在一起,尝试改进滚动用户体验。我使用的触摸屏是基于红外的,而且非常不稳定(特别是当太阳出来的时候)

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

谢谢

我有一个可以通过
NSTableView
实现这一点的工具,所以希望它能在
NSCollectionView
中很好地工作

免责声明:我在学习GCD时写了这篇文章,所以请注意保留周期。。。我没有检查我刚刚发布的内容是否有bug。请随意指出:)我刚刚在MacOS10.9上测试了它,它仍然可以工作(最初是为10.7IIRC编写的),而不是在10.10上测试

可以肯定,这整个过程是一个黑客行为,它看起来需要(似乎无论如何)异步UI操作(我认为是为了防止无限递归)。可能有一个更干净/更好的方法,请在发现时与我们分享

我已经好几个月没碰过这个了,所以我想不起所有的细节,但它的实质肯定是在
NBBTableView
代码中,它将粘贴的代码片段

首先,有一个
NSAnimation
子类
nbbscrollmanimation
处理“橡皮筋”效果:

您应该能够在具有
NSClipView
的任何控件上使用动画,方法是将其设置为
\u scrollAnimation=[[NBBScrollAnimation scrollAnimationWithClipView:(NSClipView*)[self superview]]retain]

这里的技巧是
NSTableView
的超级视图是
NSClipView
;我不知道
NSCollectionView
,但我怀疑任何可滚动控件都使用
NSClipView

下面是
NBBTableView
子类如何通过鼠标事件使用该动画:

- (void)mouseDown:(NSEvent *)theEvent
{
    _scrollDelta = 0.0;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        if (_scrollAnimation && _scrollAnimation.isAnimating) {
            [_scrollAnimation stopAnimation];
        }
    });
}

- (void)mouseUp:(NSEvent *)theEvent
{
    if (_scrollDelta) {
        [super mouseUp:theEvent];
        // reset the scroll animation
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            NSClipView* cv = (NSClipView*)[self superview];
            NSPoint newPoint = NSMakePoint(0.0, ([cv documentVisibleRect].origin.y - _scrollDelta));

            NBBScrollAnimation* anim = (NBBScrollAnimation*)_scrollAnimation;
            [anim setCurrentProgress:0.0];
            anim.targetPoint = newPoint;

            [anim startAnimation];
        });
    } else {
        [super mouseDown:theEvent];
    }
}

- (void)mouseDragged:(NSEvent *)theEvent
{
    NSClipView* clipView=(NSClipView*)[self superview];
    NSPoint newPoint = NSMakePoint(0.0, ([clipView documentVisibleRect].origin.y - [theEvent deltaY]));
    CGFloat limit = self.frame.size.height;

    if (newPoint.y >= limit) {
        newPoint.y = limit - 1.0;
    } else if (newPoint.y <= limit * -1) {
        newPoint.y = (limit * -1) + 1;
    }
    // do NOT constrain the point here. we want to "rubber band"
    [clipView scrollToPoint:newPoint];
    [[self enclosingScrollView] reflectScrolledClipView:clipView];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        NBBScrollAnimation* anim = (NBBScrollAnimation*)_scrollAnimation;
        anim.originPoint = newPoint;
    });

    // because we have to animate asyncronously, we must save the target value to use later
    // instead of setting it in the animation here
    _scrollDelta = [theEvent deltaY] * 3.5;
}

- (BOOL)autoscroll:(NSEvent *)theEvent
{
    return NO;
}
-(void)鼠标向下移动:(n事件*)事件
{
_滚动增量=0.0;
调度异步(调度获取全局队列(调度队列优先级高,0)^{
if(_scrollAnimation&&u scrollAnimation.isAnimating){
[滚动动画停止动画];
}
});
}
-(无效)鼠标点击:(n事件*)事件
{
如果(_){
[超级鼠标:theEvent];
//重置滚动动画
调度异步(调度获取全局队列(调度队列优先级高,0)^{
NSClipView*cv=(NSClipView*)[self superview];
NSPoint newPoint=NSMakePoint(0.0,([cv documentVisibleRect].origin.y-_scrollDelta));
NBBScrollAnimation*动画=(NBBScrollAnimation*)\u滚动动画;
[anim setCurrentProgress:0.0];
anim.targetPoint=新点;
[动画开始动画];
});
}否则{
[超级鼠标镇:theEvent];
}
}
-(无效)鼠标标记:(n事件*)事件
{
NSClipView*clipView=(NSClipView*)[自超视图];
NSPoint newPoint=NSMakePoint(0.0,([clipView documentVisibleRect].origin.y-[theEvent deltaY]);
CGFloat limit=自身框架尺寸高度;
如果(newPoint.y>=限制){
newPoint.y=限值-1.0;

}否则,如果(newPoint.y我成功地使用了NSPangestureRecognitor并模拟了轨迹板滚轮事件。如果你模拟得好,你将从NSScrollView“免费”获得反弹

我没有公共代码,但我找到的解释NSScrollView所期望的最好的资源是在下面模拟动量滚动的单元测试中(请参见
mousescrollbywithwheels和momentumphases

mousescrollbywithwheel和momentumphases的实现为如何在低级别合成滚动事件提供了一些提示。我发现我需要的一个补充是在事件中实际设置一个递增的时间戳,以便让滚动视图玩球


最后,为了实际创建衰减速度,我使用了一个
POPDecayAnimation
,并调整了
NSPanGestureRecognizer
中的速度,使其感觉类似。这并不完美,但它确实与
NSScrollView
的反弹保持一致。

OSX多点触控API不支持事件注入,除非您做一些修改极其肮脏的东西(手工构建内部事件结构并将其放入HID事件流中…即使这样也不总是有效).我已经被这个问题困扰了好几次-我很想看到这个问题的答案。几年前,我用可可豆制作了多点触控事件,将它们转换为CGEvent,然后转换为碳事件(这需要反向工程如何用碳来表示触摸事件,因为通常它们根本不会出现在碳流中…)不幸的是,即使在较旧版本的OS X上尝试以64位编译该代码也会给我带来大量错误,因此我怀疑它不再有效。谢谢!我刚刚开始讨论这个问题。我注意到你有两个类NBBVirtualKeyboard和NBBKeyboardKeyCell。是吗ose放弃了,转而采用另一种键盘解决方案?它们似乎没有得到实现。@SpencerWi
@implementation NBBScrollAnimation

@synthesize clipView;
@synthesize originPoint;
@synthesize targetPoint;

+ (NBBScrollAnimation*)scrollAnimationWithClipView:(NSClipView *)clipView
{
    NBBScrollAnimation *animation = [[NBBScrollAnimation alloc] initWithDuration:0.6 animationCurve:NSAnimationEaseOut];

    animation.clipView = clipView;
    animation.originPoint = clipView.documentVisibleRect.origin;
    animation.targetPoint = animation.originPoint;

    return [animation autorelease];
}

- (void)setCurrentProgress:(NSAnimationProgress)progress
{
    typedef float (^MyAnimationCurveBlock)(float, float, float);
    MyAnimationCurveBlock cubicEaseOut = ^ float (float t, float start, float end) {
        t--;
        return end*(t * t * t + 1) + start;
    };

    dispatch_sync(dispatch_get_main_queue(), ^{
        NSPoint progressPoint = self.originPoint;
        progressPoint.x += cubicEaseOut(progress, 0, self.targetPoint.x - self.originPoint.x);
        progressPoint.y += cubicEaseOut(progress, 0, self.targetPoint.y - self.originPoint.y);

        NSPoint constraint = [self.clipView constrainScrollPoint:progressPoint];
        if (!NSEqualPoints(constraint, progressPoint)) {
            // constraining the point and reassigning to target gives us the "rubber band" effect
            self.targetPoint = constraint;
        }

        [self.clipView scrollToPoint:progressPoint];
        [self.clipView.enclosingScrollView reflectScrolledClipView:self.clipView];
        [self.clipView.enclosingScrollView displayIfNeeded];
    });
}

@end
- (void)mouseDown:(NSEvent *)theEvent
{
    _scrollDelta = 0.0;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        if (_scrollAnimation && _scrollAnimation.isAnimating) {
            [_scrollAnimation stopAnimation];
        }
    });
}

- (void)mouseUp:(NSEvent *)theEvent
{
    if (_scrollDelta) {
        [super mouseUp:theEvent];
        // reset the scroll animation
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            NSClipView* cv = (NSClipView*)[self superview];
            NSPoint newPoint = NSMakePoint(0.0, ([cv documentVisibleRect].origin.y - _scrollDelta));

            NBBScrollAnimation* anim = (NBBScrollAnimation*)_scrollAnimation;
            [anim setCurrentProgress:0.0];
            anim.targetPoint = newPoint;

            [anim startAnimation];
        });
    } else {
        [super mouseDown:theEvent];
    }
}

- (void)mouseDragged:(NSEvent *)theEvent
{
    NSClipView* clipView=(NSClipView*)[self superview];
    NSPoint newPoint = NSMakePoint(0.0, ([clipView documentVisibleRect].origin.y - [theEvent deltaY]));
    CGFloat limit = self.frame.size.height;

    if (newPoint.y >= limit) {
        newPoint.y = limit - 1.0;
    } else if (newPoint.y <= limit * -1) {
        newPoint.y = (limit * -1) + 1;
    }
    // do NOT constrain the point here. we want to "rubber band"
    [clipView scrollToPoint:newPoint];
    [[self enclosingScrollView] reflectScrolledClipView:clipView];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        NBBScrollAnimation* anim = (NBBScrollAnimation*)_scrollAnimation;
        anim.originPoint = newPoint;
    });

    // because we have to animate asyncronously, we must save the target value to use later
    // instead of setting it in the animation here
    _scrollDelta = [theEvent deltaY] * 3.5;
}

- (BOOL)autoscroll:(NSEvent *)theEvent
{
    return NO;
}