[iphone] -[CALayer setNeedsDisplayInRect :]에서 암시 적 애니메이션 비활성화

-drawInContext : 메소드에 복잡한 그리기 코드가있는 레이어가 있습니다. 내가해야 할 그림의 양을 최소화하려고 노력 중이므로 변경된 부분 만 업데이트하기 위해 -setNeedsDisplayInRect :를 사용하고 있습니다. 이것은 훌륭하게 작동합니다. 그러나 그래픽 시스템이 레이어를 업데이트하면 크로스 페이드를 사용하여 이전 이미지에서 새 이미지로 전환됩니다. 즉시 전환하고 싶습니다.

CATransaction을 사용하여 작업을 끄고 기간을 0으로 설정했지만 작동하지 않았습니다. 사용중인 코드는 다음과 같습니다.

[CATransaction begin];
[CATransaction setDisableActions: YES];
[self setNeedsDisplayInRect: rect];
[CATransaction commit];

CATransaction에 다른 방법이 있습니까? 대신 사용해야합니다 (kCATransactionDisableActions와 같은 -setValue : forKey : 시도).



답변

레이어에서 액션 딕셔너리를 설정 [NSNull null]하여 적절한 키에 대한 애니메이션 으로 반환 할 수 있습니다 . 예를 들어

NSDictionary *newActions = @{
    @"onOrderIn": [NSNull null],
    @"onOrderOut": [NSNull null],
    @"sublayers": [NSNull null],
    @"contents": [NSNull null],
    @"bounds": [NSNull null]
};

layer.actions = newActions;

내 레이어 중 하나에서 하위 레이어를 삽입하거나 변경할 때 페이드 인 / 아웃 애니메이션을 비활성화하고 레이어의 크기와 내용을 변경합니다. 나는 믿는다contents업데이트 된 도면에서 크로스 페이드를 방지하기 위해 키가 찾고 열쇠 .


스위프트 버전 :

let newActions = [
        "onOrderIn": NSNull(),
        "onOrderOut": NSNull(),
        "sublayers": NSNull(),
        "contents": NSNull(),
        "bounds": NSNull(),
    ]


답변

또한:

[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];

//foo

[CATransaction commit];


답변

계층의 속성을 변경하면 CA는 일반적으로 변경을 애니메이션하기 위해 암시 적 트랜잭션 개체를 만듭니다. 변경 사항에 애니메이션을 적용하지 않으려면 명시 적 트랜잭션을 만들고 kCATransactionDisableActions 속성을 true로 설정하여 암시 적 애니메이션을 비활성화 할 수 있습니다 .

목표 -C

[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
// change properties here without animation
[CATransaction commit];

빠른

CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
// change properties here without animation
CATransaction.commit()


답변

Brad Larson의 답변 외에도 사용자 정의 레이어 (사용자가 만든 레이어)의 경우 레이어를 수정하는 대신 위임사용할 수 있습니다actions 사전 . 이 방법은보다 역동적이고 성능이 우수 할 수 있습니다. 또한 애니메이션 가능한 모든 키를 나열하지 않고도 모든 암시 적 애니메이션을 비활성화 할 수 있습니다.

불행히도, UIView각각 UIView은 이미 자체 레이어의 대리자 이므로 s를 사용자 정의 레이어 대리자로 사용할 수 없습니다 . 그러나 다음과 같은 간단한 도우미 클래스를 사용할 수 있습니다.

@interface MyLayerDelegate : NSObject
    @property (nonatomic, assign) BOOL disableImplicitAnimations;
@end

@implementation MyLayerDelegate

- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
{
    if (self.disableImplicitAnimations)
         return (id)[NSNull null]; // disable all implicit animations
    else return nil; // allow implicit animations

    // you can also test specific key names; for example, to disable bounds animation:
    // if ([event isEqualToString:@"bounds"]) return (id)[NSNull null];
}

@end

사용법 (보기 내부) :

MyLayerDelegate *delegate = [[MyLayerDelegate alloc] init];

// assign to a strong property, because CALayer's "delegate" property is weak
self.myLayerDelegate = delegate;

self.myLayer = [CALayer layer];
self.myLayer.delegate = delegate;

// ...

self.myLayerDelegate.disableImplicitAnimations = YES;
self.myLayer.position = (CGPoint){.x = 10, .y = 42}; // will not animate

// ...

self.myLayerDelegate.disableImplicitAnimations = NO;
self.myLayer.position = (CGPoint){.x = 0, .y = 0}; // will animate

때때로 뷰의 컨트롤러를 뷰의 커스텀 서브 레이어의 델리게이트로 사용하는 것이 편리합니다. 이 경우 도우미 클래스가 필요하지 않으며 actionForLayer:forKey:컨트롤러 내부에서 메서드 를 직접 구현할 수 있습니다 .

중요 사항 : UIView기본 레이어 의 위임을 수정하지 마십시오 (예 : 암시 적 애니메이션 사용). 나쁜 일이 발생합니다.)

참고 : 레이어 다시 그리기에 애니메이션을 적용하지 않으려면 애니메이션을 비활성화하지 않는 경우가 있습니다. 실제 다시 그리기는 때때로 나중에 발생할 수 있기 때문에 [CALayer setNeedsDisplayInRect:]호출 CATransaction할 수 없습니다. 이 답변에 설명 대로 사용자 지정 속성을 사용하는 것이 좋습니다 .


답변

허용 된 답변과 유사하지만 Swift 용보다 효율적인 솔루션이 있습니다. 어떤 경우에는 값을 수정할 때마다 트랜잭션을 생성하는 것보다 성능이 문제가 될 수 있습니다. 예를 들어 60fps에서 레이어 위치를 드래그하는 일반적인 사용 사례가 언급되어 있습니다.

// Disable implicit position animation.
layer.actions = ["position": NSNull()]      

레이어 동작이 해결 되는 방법 은 애플 문서를 참조하십시오 . 대리자를 구현하면 계단식에서 한 단계 더 건너 뛰지 만 대리자는 관련 UIView로 설정 해야하는 것에 대한 경고 로 인해 너무 지저분했습니다 .

편집 :을 NSNull준수 하는 주석 작성자로 인해 업데이트 되었습니다 CAAction.


답변

Sam의 답변과 Simon의 어려움을 기반으로 CSShapeLayer를 만든 후 델리게이트 참조를 추가하십시오.

CAShapeLayer *myLayer = [CAShapeLayer layer];
myLayer.delegate = self; // <- set delegate here, it's magic.

“m”파일의 다른 곳에 …

사용자 정의 “disableImplicitAnimations”변수 배열을 통해 전환 할 수있는 기능이없는 Sam과 기본적으로 동일합니다. “하드 와이어”접근 방식

- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event {

    // disable all implicit animations
    return (id)[NSNull null];

    // allow implicit animations
    // return nil;

    // you can also test specific key names; for example, to disable bounds animation:
    // if ([event isEqualToString:@"bounds"]) return (id)[NSNull null];

}


답변

사실, 나는 정답을 찾지 못했습니다. 나를 위해 문제를 해결하는 방법은 다음과 같습니다.

- (id<CAAction>)actionForKey:(NSString *)event {
    return nil;
}

그런 다음 논리를 사용하여 특정 애니메이션을 비활성화 할 수 있지만 모든 애니메이션을 제거하고 싶었으므로 nil을 반환했습니다.