【发布时间】:2014-10-20 09:30:36
【问题描述】:
我正在编写一个包含集合视图的 Mac 应用程序。此应用程序将在大型触摸屏显示器上运行(来自 Planar 的55" EP series)。由于硬件限制,触摸屏不发送滚动事件(甚至任何多点触控事件)。 我怎样才能欺骗应用程序认为“mousedown+drag”与“mousescroll”相同?
通过子类化 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];
}
}
}
关于此实现的说明:我将scrollScaling 和pointSmoother 放在一起尝试改进滚动用户体验。我使用的触摸屏是基于 IR 的,并且会变得非常紧张(尤其是当太阳出来时)。
如果相关:我在 Yosemite beta (14A329r) 上使用 Xcode 6 beta 6 (6A280e),我的构建目标是 10.10。
谢谢!
【问题讨论】:
-
OS X 多点触控 API 不支持事件注入,除非你做了一些 非常 肮脏的事情(手动构建内部事件结构并将它们放入 HID 事件流中...... .甚至这并不总是有效)。我已经被这个问题困扰过很多次了——我很想看到这个问题的答案。
-
几年前,我通过使用 Cocoa 生成多点触控事件,将它们转换为 CGEvent,然后转换为 Carbon 事件(这需要逆向工程如何在 Carbon 中表示触摸事件,因为通常它们不会根本不会出现在 Carbon 流中……),然后将它们推送到 Carbon 事件流中。不幸的是,即使在旧版本的 OS X 上尝试编译 64 位代码也会给我带来一大堆错误,所以我怀疑它不再有效。
标签: objective-c macos cocoa