1

NSScrollViewに似ていますが、CoreAnimation CAScrollLayer/CATiledLayerに基づくカスタムビューを作成しようとしています。基本的に、私のアプリは多くのほぼリアルタイムのCGPath描画を必要とし、シェイプレイヤーを使用してこれらのパスをアニメーション化します(これは、GarageBandが記録中にアニメーション化する方法に似ています)。私が作成した最初のプロトタイプはNSScrollViewを使用していましたが、1秒あたり20フレームを超えることはできませんでした(理由は、scrollEventが発生するたびに描画によってNSRulerViewが更新され、呼び出し全体が-[NSClipViewscrollToPoint]から-[に流れるためです。 NSScrollView ReflectScrolledClipView:]は非常に高価で非効率的です)。

スクロールメカニズムとしてCAScrollLayerを使用し、従来のdocumentView(無限スクロールオプション用)としてCATiledLayerを使用するカスタムビューを作成しました。これで、60fpsに近づくことができます。ただし、scrollWheelエラスティックスクロールを実装するのに苦労しており、その方法がわかりません。これが私がこれまでに持っているコードです、そして誰かがelasticScrollingを実装する方法を教えてくれたら本当にありがたいです。

-(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];
}

}
4

2 に答える 2

1

https://github.com/twitter/twuiでTUIScrollViewを見てください。

于 2012-09-28T17:56:58.073 に答える
0

ElasticScrollingを追加するためのコアコンセプトは、SpringSolverを使用することです。

これには、再利用できるElasticScrollViewがあります:
https ://github.com/eonist/Element

于 2017-05-07T20:15:31.333 に答える