我試圖創建一個類似於NSScrollView但基於CoreAnimation CAScrollLayer/CATiledLayer的自定義視圖。基本上,我的應用程序需要大量近實時的CGPath繪圖,並使用形狀圖層爲這些路徑加上動畫(它與GarageBand在錄製時動畫的方式類似)。我創建的第一個原型使用了NSScrollView,但是我無法獲得每秒20幀以上的數據(原因是NSRulerView每次發生scrollEvent都會進行更新,並且整個調用流從 - [NSClipView scrollToPoint]到[ NSScrollView reflectScrolledClipView:]是非常昂貴和低效的)。如何在OSX上實現scrollWheel彈性滾動
我創建了一個使用CAScrollLayer作爲滾動機制和CATiledLayer作爲傳統documentView(用於無限滾動選項)的自定義視圖,現在我可以接近60fps。但是,我很難實現scrollWheel彈性滾動,我不知道如何去做。這是我迄今爲止的代碼,如果有人能告訴我如何實現彈性滾動,我將非常感激。
-(void) scrollWheel:(NSEvent *)theEvent{
NSCAssert(mDocumentScrollLayer, @"The Scroll Layer Cannot be nil");
NSCAssert(mDocumentLayer, @"The tiled layer cannot be nil");
NSCAssert(self.layer, @"The base layer of view cannot be nil");
NSCAssert(mRulerLayer, @"The ScrollLayer for ruler cannot be nil");
NSPoint locationInWindow = [theEvent locationInWindow];
NSPoint locationInBaseLayer = [self convertPoint:locationInWindow
fromView:nil];
NSPoint locationInRuler = [mRulerLayer convertPoint:locationInBaseLayer
fromLayer:self.layer];
if ([mRulerLayer containsPoint:locationInRuler]) {
return;
}
CGRect docRect = [mDocumentScrollLayer convertRect:[mDocumentLayer bounds]
fromLayer:mDocumentLayer];
CGRect scrollRect = [mDocumentScrollLayer visibleRect];
CGPoint newOrigin = scrollRect.origin;
CGFloat deltaX = [theEvent scrollingDeltaX];
CGFloat deltaY = [theEvent scrollingDeltaY];
if ([self isFlipped]) {
deltaY *= -1;
}
scrollRect.origin.x -= deltaX;
scrollRect.origin.y += deltaY;
if ((NSMinX(scrollRect) < NSMinX(docRect)) ||
(NSMaxX(scrollRect) > NSMaxX(docRect)) ||
(NSMinY(scrollRect) < NSMinY(docRect)) ||
(NSMaxY(scrollRect) > NSMaxX(docRect))) {
mIsScrollingPastEdge = YES;
CGFloat heightPhase = 0.0;
CGFloat widthPhase = 0.0;
CGSize size = [self frame].size;
if (NSMinX(scrollRect) < NSMinX(docRect)) {
widthPhase = ABS(NSMinX(scrollRect) - NSMinX(docRect));
}
if (NSMaxX(scrollRect) > NSMaxX(docRect)) {
widthPhase = ABS(NSMaxX(scrollRect) - NSMaxX(docRect));
}
if (NSMinY(scrollRect) < NSMinY(docRect)) {
heightPhase = ABS(NSMinY(scrollRect) - NSMinY(docRect));
}
if (NSMaxY(scrollRect) > NSMaxY(docRect)) {
heightPhase = ABS(NSMaxY(scrollRect) - NSMaxY(docRect));
}
if (widthPhase > size.width/2.0) {
widthPhase = size.width/2.0;
}
if (heightPhase > size.width/2.0) {
heightPhase = size.width/2.0;
}
deltaX = deltaX*(1-(2*widthPhase/size.width));
deltaY = deltaY*(1-(2*heightPhase/size.height));
}
newOrigin.x -= deltaX;
newOrigin.y += deltaY;
if (mIsScrollingPastEdge &&
(([theEvent phase] == NSEventPhaseEnded) ||
([theEvent momentumPhase] == NSEventPhaseEnded)
)
){
CGPoint confinedScrollPoint = [mDocumentScrollLayer bounds].origin;
mIsScrollingPastEdge = NO;
CGRect visibleRect = [mDocumentScrollLayer visibleRect];
if (NSMinX(scrollRect) < NSMinX(docRect)){
confinedScrollPoint.x = docRect.origin.x;
}
if(NSMinY(scrollRect) < NSMinY(docRect)) {
confinedScrollPoint.y = docRect.origin.y;
}
if (NSMaxX(scrollRect) > NSMaxX(docRect)) {
confinedScrollPoint.x = NSMaxX(docRect) - visibleRect.size.width;
}
if (NSMaxY(scrollRect) > NSMaxY(docRect)){
confinedScrollPoint.y = NSMaxY(docRect) - visibleRect.size.height;
}
[mDocumentScrollLayer scrollToPoint:confinedScrollPoint];
CGPoint rulerPoint = [mRulerLayer bounds].origin;
rulerPoint.x = [mDocumentLayer bounds].origin.x;
[mRulerLayer scrollToPoint:rulerPoint];
return;
}
CGPoint rulerPoint = [mDocumentScrollLayer convertPoint:newOrigin
toLayer:mRulerLayer];
rulerPoint.y = [mRulerLayer bounds].origin.y;
if (!mIsScrollingPastEdge) {
[CATransaction setDisableActions:YES];
[mDocumentScrollLayer scrollToPoint:newOrigin];
[CATransaction commit];
}else{
[mDocumentScrollLayer scrollToPoint:newOrigin];
[mRulerLayer scrollToPoint:rulerPoint];
}
}