Hủy bỏ một hình ảnh động UIView?


244

Có thể hủy một UIViewhình ảnh động trong khi nó đang được tiến hành? Hay tôi sẽ phải giảm xuống cấp CA?

tức là tôi đã làm một cái gì đó như thế này (có thể thiết lập một hành động kết thúc hoạt hình quá):

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:duration];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
// other animation properties

// set view properties

[UIView commitAnimations];

Nhưng trước khi hoạt hình hoàn thành và tôi nhận được sự kiện kết thúc hoạt hình, tôi muốn hủy nó (cắt ngắn). Điều này có thể không? Googling xung quanh tìm thấy một vài người hỏi cùng một câu hỏi mà không có câu trả lời - và một hoặc hai người suy đoán rằng điều đó không thể được thực hiện.


Bạn có nghĩa là tạm dừng hoạt hình ở giữa và để nó ở đó? Hoặc quay trở lại nơi nó đã bắt đầu?
Chris Lundie

2
Dù để nó chính xác ở đâu, giữa hoạt hình (trong thực tế tôi sẽ bắt đầu một hoạt hình khác ngay sau đó), hoặc nhảy thẳng đến điểm cuối. Tôi tưởng tượng đầu tiên là tự nhiên hơn, nhưng hoặc là làm việc cho tôi trong trường hợp này.
philsquared

3
@Chris Hanson: Tôi đánh giá cao các chỉnh sửa của bạn nhưng tự hỏi lý do của bạn là gì khi xóa thẻ iPhone. Nó có thể được ngụ ý bởi ca cao cảm ứng, nhưng tôi cho một người có bộ lọc thẻ trên iPhone và sẽ bỏ lỡ điều này trong trường hợp đó.
philsquared

@Boot To The Head (một lần nữa): câu hỏi của bạn và câu trả lời của riêng tôi cho câu hỏi đó đã nhắc tôi kiểm tra riêng lẻ thêm một chút và tôi phát hiện ra nếu bạn bắt đầu một hình ảnh động mới trước khi câu đầu tiên kết thúc, nó sẽ nhảy đến điểm cuối trước khi bắt đầu cái mới
philsquared

- điều này đáp ứng nhu cầu tức thời của tôi (và gợi ý tôi có một số lỗi khác trong mã thực của mình), nhưng tôi sẽ để câu hỏi này mở vì tôi biết những người khác đã yêu cầu như vậy.
philsquared

Câu trả lời:


114

Cách tôi làm là tạo ra một hình ảnh động mới đến điểm cuối của bạn. Đặt thời lượng rất ngắn và đảm bảo bạn sử dụng +setAnimationBeginsFromCurrentState:phương pháp để bắt đầu từ trạng thái hiện tại. Khi bạn đặt thành CÓ, hình động hiện tại sẽ bị cắt ngắn. Trông giống như thế này:

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.1];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
// other animation properties

// set view properties

[UIView commitAnimations];

Cảm ơn Stephen. Trên thực tế nếu bạn đọc các bình luận dưới câu hỏi của tôi thì đây chính xác là những gì tôi đã làm cuối cùng - nhưng vì bạn đã đưa ra một tài khoản tốt, rõ ràng, ở đây tôi đánh dấu nó là được chấp nhận
philsquared

1
@Vojto Làm sao vậy? Các tài liệu nói rằng việc sử dụng setAnimationBeginsFromCurrentState:không được khuyến khích từ iOS 4 nhưng nó vẫn ở đó và vẫn hoạt động.
Stephen Darlington

55
trong iOS4 +, sử dụng tùy chọn UIViewAnimationOptionBeginFromCienState trên animateWithDuration: delay: Tùy chọn: hình ảnh động: hoàn thành:
Adam Eberbach

UIViewAnimationOptionBeginFromCienState sẽ hủy bất kỳ hoạt hình nào đang diễn ra hay chỉ là một hoạt hình nhắm vào cùng một thuộc tính? Nếu đó là bất kỳ hoạt hình nào, làm thế nào bạn có thể làm cho nó tiếp tục một hoạt hình mới tại cùng một điểm mà KHÔNG dừng hoạt động tương tự trong tiến trình?
zakdances

1
@yourfriendzak, dựa trên một thử nghiệm ngắn gọn, có vẻ như nó chỉ hủy một hình ảnh động nhắm vào cùng một thuộc tính. Tôi đã thử thay đổi alpha của một scrollView có nội dung Offerset là hoạt hình và hoạt hình contentPackset tiếp tục.
arlomedia

360

Sử dụng:

#import <QuartzCore/QuartzCore.h>

.......

[myView.layer removeAllAnimations];

22
Tôi thấy rằng tôi phải thêm #import <QuartzCore / QuartzCore.h> để xóa cảnh báo trình biên dịch;)
deanWombourne

4
Rất đơn giản và khó tìm. Cảm ơn bạn vì điều này, tôi đã dành một giờ cố gắng để tìm ra điều này.
MikeyWard

4
Vấn đề có thể xảy ra với giải pháp này là (ít nhất là trong iOS 5) có độ trễ trước khi nó có hiệu lực - nó không hoạt động đủ sớm nếu điều bạn muốn nói là "dừng hoạt hình ngay bây giờ, trước khi tôi chuyển sang dòng tiếp theo mã ".
matt

14
Tôi thấy rằng việc gọi điều này sẽ kích hoạt việc hoàn thành: ^ (BOOL đã kết thúc) nếu sử dụng + (void) animateWithDuration: (NSTimeInterval) hình ảnh động thời gian: (void (^) (void)) hoàn thành hoạt hình: (void (^) (BOOL đã hoàn thành) ) hoàn thành
JWGS

1
@matt bạn có biết một giải pháp dừng ngay hoạt hình không? tôi cũng đang quan sát một độ trễ nhỏ trước khi hoạt hình thực sự dừng lại.
tiguero

62

Cách đơn giản nhất để dừng tất cả các hình động trên một chế độ xem cụ thể, ngay lập tức , là:

Liên kết dự án với QuartzCore.framework. Khi bắt đầu mã của bạn:

#import <QuartzCore/QuartzCore.h>

Bây giờ, khi bạn muốn dừng tất cả các hình động trên một khung nhìn đã chết trong các bản nhạc của chúng, hãy nói điều này:

[CATransaction begin];
[theView.layer removeAllAnimations];
[CATransaction commit];

Đường giữa sẽ tự hoạt động, nhưng có một độ trễ cho đến khi runloop kết thúc ("khoảnh khắc vẽ lại"). Để ngăn chặn sự chậm trễ đó, hãy bọc lệnh trong một khối giao dịch rõ ràng như được hiển thị. Công việc này cung cấp không có thay đổi nào khác đã được thực hiện trên lớp này trong runloop hiện tại.


2
Tôi đã sử dụng [CATransaction flush] sau khi mã của bạn hoạt động tốt, đôi khi khiến nó không được viết ngay lập tức. Tuy nhiên, có một vấn đề hiệu suất 0,1 giây, ví dụ như nó chậm trễ trong thời gian cụ thể đó. Bất kỳ cách giải quyết?
Sidwyn Koh

5
removeAllAnimationssẽ có tác dụng phụ là đưa hình ảnh động đến khung cuối cùng của nó, thay vì dừng nó ở nơi hiện tại.
Ilya

3
Thông thường, khi một người muốn một hình ảnh động dừng lại, họ hy vọng nó dừng lại ở khung hình hiện tại của nó - không phải khung hình đầu tiên cũng như khung hình cuối cùng. Nhảy đến khung đầu tiên hoặc cuối cùng sẽ trông giống như chuyển động giật.
Ilya

1
Tôi sẽ trình bày chi tiết câu trả lời sau đó để xác định hành vi mặc định là gì và cách thay đổi. Rõ ràng là ai đó đang làm hoạt hình rất tích cực quan tâm đến những gì xảy ra trên màn hình sau các cuộc gọi phương thức của anh ấy :)
Ilya

1
Tôi đã chi tiết nó ở nơi khác. Đó không phải là những gì được hỏi nên nó không phải là những gì tôi đã trả lời ở đây. Nếu bạn có câu trả lời khác, chắc chắn đưa ra như một câu trả lời!
matt

48

Trên iOS 4 trở lên, hãy sử dụng UIViewAnimationOptionBeginFromCurrentStatetùy chọn trên hình động thứ hai để cắt ngắn hình động đầu tiên.

Ví dụ: giả sử bạn có chế độ xem với chỉ báo hoạt động. Bạn muốn làm mờ dần trong chỉ báo hoạt động trong khi một số hoạt động có khả năng tiêu tốn thời gian bắt đầu và làm mờ dần khi hoạt động kết thúc. Trong mã dưới đây, khung nhìn với chỉ báo hoạt động trên đó được gọi activityView.

- (void)showActivityIndicator {
    activityView.alpha = 0.0;
    activityView.hidden = NO;
    [UIView animateWithDuration:0.5
                 animations:^(void) {
                     activityView.alpha = 1.0;
                 }];

- (void)hideActivityIndicator {
    [UIView animateWithDuration:0.5
                 delay:0 options:UIViewAnimationOptionBeginFromCurrentState
                 animations:^(void) {
                     activityView.alpha = 0.0;
                 }
                 completion:^(BOOL completed) {
                     if (completed) {
                         activityView.hidden = YES;
                     }
                 }];
}

41

Để hủy hoạt hình, bạn chỉ cần đặt thuộc tính hiện đang hoạt hình, bên ngoài hoạt hình UIView. Điều đó sẽ dừng hoạt hình mọi lúc mọi nơi và UIView sẽ chuyển đến cài đặt bạn vừa xác định.


9
Điều này chỉ đúng một phần. Nó không dừng hoạt hình, nhưng nó không ngăn nó bắn các phương thức ủy nhiệm, đáng chú ý nhất là trình xử lý animComplete, vì vậy nếu bạn có logic ở đó, bạn sẽ thấy các vấn đề.
M. Ryan

33
Đó là những gì đối số 'đã hoàn thành' -animationDidStop:finished:context:dành cho. Nếu [finished boolValue] == NO, sau đó bạn biết rằng hoạt hình của bạn đã bị hủy bỏ bằng cách nào đó.
Nick Forge

Đây là một mẹo tuyệt vời từ 7 năm trước! lưu ý rằng có một hành vi kỳ lạ: giả sử bạn đang hoạt hình alpha trở lại và trước, và bạn làm cho nó "về 0". để DỪNG hoạt hình, bạn không thể đặt alpha thành 0; bạn đặt nó thành 1.
Fattie

26

Xin lỗi để phục hồi câu trả lời này, nhưng trong iOS 10 mọi thứ đã thay đổi, và bây giờ việc hủy bỏ là có thể và bạn thậm chí có thể hủy bỏ một cách duyên dáng!

Sau iOS 10, bạn có thể hủy hoạt hình bằng UIViewPropertyAnimator !

UIViewPropertyAnimator(duration: 2, dampingRatio: 0.4, animations: {
    view.backgroundColor = .blue
})
animator.stopAnimation(true)

Nếu bạn vượt qua đúng, nó sẽ hủy hoạt hình và nó dừng ngay tại nơi bạn đã hủy nó. Phương thức hoàn thành sẽ không được gọi. Tuy nhiên, nếu bạn vượt qua sai, bạn có trách nhiệm hoàn thành hoạt hình:

animator.finishAnimation(.start)

Bạn có thể hoàn thành hoạt hình của mình và ở trạng thái hiện tại (.c Hiện tại) hoặc chuyển sang trạng thái ban đầu (.start) hoặc đến trạng thái kết thúc (.end)

Nhân tiện, bạn thậm chí có thể tạm dừng và khởi động lại sau ...

animator.pauseAnimation()
animator.startAnimation()

Lưu ý: Nếu bạn không muốn hủy đột ngột, bạn có thể đảo ngược hoạt hình của mình hoặc thậm chí thay đổi hoạt hình sau khi tạm dừng!


2
Điều này chắc chắn là phù hợp nhất cho hình ảnh động hiện đại.
Doug

10

Nếu bạn đang tạo hiệu ứng ràng buộc bằng cách thay đổi hằng số thay vì thuộc tính xem, không có phương pháp nào khác hoạt động trên iOS 8.

Ví dụ hoạt hình:

self.constraint.constant = 0;
[self.view updateConstraintsIfNeeded];
[self.view layoutIfNeeded];
[UIView animateWithDuration:1.0f
                      delay:0.0f
                    options:UIViewAnimationOptionCurveLinear
                 animations:^{
                     self.constraint.constant = 1.0f;
                     [self.view layoutIfNeeded];
                 } completion:^(BOOL finished) {

                 }];

Giải pháp:

Bạn cần xóa hình ảnh động khỏi các lớp của bất kỳ chế độ xem nào bị ảnh hưởng bởi thay đổi ràng buộc và lớp con của chúng.

[self.constraintView.layer removeAllAnimations];
for (CALayer *l in self.constraintView.layer.sublayers)
{
    [l removeAllAnimations];
}

1
Cũng chỉ self.constraintView.layer.removeAllAnimations()hoạt động (Swift)
Lachtan

Không có giải pháp nào khác hiệu quả. Cảm ơn, nó đã làm việc cho tôi.
hoán đổi


5

Không có điều nào ở trên giải quyết được nó cho tôi, nhưng điều này đã giúp: UIViewHoạt hình đặt thuộc tính ngay lập tức, sau đó tạo hiệu ứng cho nó. Nó dừng hoạt hình khi lớp trình bày khớp với mô hình (thuộc tính set).

Tôi đã giải quyết vấn đề của mình, đó là "Tôi muốn làm động từ nơi bạn trông giống như bạn xuất hiện" ("bạn" nghĩa là quan điểm). Nếu bạn muốn THAT, thì:

  1. Thêm QuartzCore.
  2. CALayer * pLayer = theView.layer.presentationLayer;

đặt vị trí cho lớp trình bày

Tôi sử dụng một vài lựa chọn bao gồm UIViewAnimationOptionOverrideInheritedDuration

Nhưng vì tài liệu của Apple rất mơ hồ, tôi không biết liệu nó có thực sự ghi đè lên các hình ảnh động khác khi sử dụng hay chỉ đặt lại bộ hẹn giờ.

[UIView animateWithDuration:blah... 
                    options: UIViewAnimationOptionBeginFromCurrentState ... 
                    animations: ^ {
                                   theView.center = CGPointMake( pLayer.position.x + YOUR_ANIMATION_OFFSET, pLayer.position.y + ANOTHER_ANIMATION_OFFSET);
                   //this only works for translating, but you get the idea if you wanna flip and scale it. 
                   } completion: ^(BOOL complete) {}];

Và đó nên là một giải pháp tốt cho bây giờ.


5
[UIView setAnimationsEnabled:NO];
// your code here
[UIView setAnimationsEnabled:YES];

2

Phiên bản Swift của giải pháp của Stephen Darlington

UIView.beginAnimations(nil, context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(0.1)
// other animation properties

// set view properties
UIView.commitAnimations()

1

Tôi có cùng một vấn đề; API không có gì để hủy một số hình ảnh động cụ thể. Các

+ (void)setAnimationsEnabled:(BOOL)enabled

vô hiệu hóa TẤT CẢ hình ảnh động, và do đó không làm việc cho tôi. Có hai giải pháp:

1) làm cho đối tượng hoạt hình của bạn là một subview. Sau đó, khi bạn muốn hủy hình động cho chế độ xem đó, hãy xóa chế độ xem hoặc ẩn nó. Rất đơn giản, nhưng bạn cần tạo lại khung nhìn phụ mà không có hình động nếu bạn cần giữ nó trong tầm nhìn.

2) chỉ lặp lại một hình động và tạo bộ chọn đại biểu để khởi động lại hoạt hình nếu cần, như thế này:

-(void) startAnimation {
NSLog(@"startAnim alpha:%f", self.alpha);
[self setAlpha:1.0];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationRepeatCount:1];
[UIView setAnimationRepeatAutoreverses:YES];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(pulseAnimationDidStop:finished:context:)];
[self setAlpha:0.1];
[UIView commitAnimations];
}

- (void)pulseAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
if(hasFocus) {
    [self startAnimation];
} else {
    self.alpha = 1.0;
}
}

-(void) setHasFocus:(BOOL)_hasFocus {
hasFocus = _hasFocus;
if(hasFocus) {
    [self startAnimation];
}
}

Vấn đề với 2) là luôn có sự chậm trễ khi dừng hoạt hình khi nó kết thúc chu kỳ hoạt hình hiện tại.

Hi vọng điêu nay co ich.


1

Ngay cả khi bạn hủy hoạt hình theo những cách trên thì hoạt hình didStopSelectorvẫn chạy. Vì vậy, nếu bạn có trạng thái logic trong ứng dụng của mình được điều khiển bởi hình ảnh động, bạn sẽ gặp vấn đề. Vì lý do này với các cách được mô tả ở trên, tôi sử dụng biến ngữ cảnh của UIViewhình ảnh động. Nếu bạn chuyển trạng thái hiện tại của chương trình theo thông số ngữ cảnh cho hoạt hình, khi hoạt ảnh dừng didStopSelectorchức năng của bạn có thể quyết định xem nó có nên làm gì không hay chỉ trả về dựa trên trạng thái hiện tại và giá trị trạng thái được truyền dưới dạng bối cảnh.


NickForge giải thích điều này một cách hoàn hảo trong bình luận bên dưới câu trả lời đúng của TomTaylor ở trên ...
Fattie

Cảm ơn rất nhiều cho lưu ý này! Tôi thực sự có một tình huống như vậy, trong đó một hình ảnh động tiếp theo được kích hoạt khi hoạt hìnhDidStop được gọi. Nếu bạn chỉ cần loại bỏ một hình ảnh động và ngay lập tức thêm lại một cái mới cho cùng một khóa tôi thấy nó sẽ không hoạt động. Bạn cần đợi, ví dụ cho đến khi kích hoạt animationDidStop cho một hình ảnh động khác, hoặc một cái gì đó khác. Hoạt hình vừa được gỡ bỏ sẽ không kích hoạt hoạt hìnhDidStop nữa.
RickJansen

0
CALayer * pLayer = self.layer.presentationLayer;
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView animateWithDuration:0.001 animations:^{
    self.frame = pLayer.frame;
}];

0

Để tạm dừng một hình ảnh động mà không trở lại trạng thái ban đầu hoặc cuối cùng:

CFTimeInterval paused_time = [myView.layer convertTime:CACurrentMediaTime() fromLayer:nil];
myView.layer.speed = 0.0;
myView.layer.timeOffset = paused_time;

0

Khi tôi làm việc với UIStackViewhoạt hình, ngoài ra removeAllAnimations()tôi cần đặt một số giá trị thành giá trị ban đầu vì removeAllAnimations()có thể đặt chúng ở trạng thái không thể đoán trước. Tôi có stackViewbằng view1view2bên trong, và một cái nhìn có thể thấy được và một ẩn:

public func configureStackView(hideView1: Bool, hideView2: Bool) {
    let oldHideView1 = view1.isHidden
    let oldHideView2 = view2.isHidden
    view1.layer.removeAllAnimations()
    view2.layer.removeAllAnimations()
    view.layer.removeAllAnimations()
    stackView.layer.removeAllAnimations()
    // after stopping animation the values are unpredictable, so set values to old
    view1.isHidden = oldHideView1 //    <- Solution is here
    view2.isHidden = oldHideView2 //    <- Solution is here

    UIView.animate(withDuration: 0.3,
                   delay: 0.0,
                   usingSpringWithDamping: 0.9,
                   initialSpringVelocity: 1,
                   options: [],
                   animations: {
                    view1.isHidden = hideView1
                    view2.isHidden = hideView2
                    stackView.layoutIfNeeded()
    },
                   completion: nil)
}

0

Không có giải pháp nào được trả lời làm việc cho tôi. Tôi đã giải quyết vấn đề của mình theo cách này (tôi không biết liệu đó có phải là cách chính xác không?), Vì tôi gặp vấn đề khi gọi quá nhanh (khi hoạt hình trước đó chưa kết thúc). Tôi vượt qua hoạt hình mong muốn của tôi với khối customAnim .

extension UIView
{

    func niceCustomTranstion(
        duration: CGFloat = 0.3,
        options: UIView.AnimationOptions = .transitionCrossDissolve,
        customAnim: @escaping () -> Void
        )
    {
        UIView.transition(
            with: self,
            duration: TimeInterval(duration),
            options: options,
            animations: {
                customAnim()
        },
            completion: { (finished) in
                if !finished
                {
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    self.layer.removeAllAnimations()
                    customAnim()
                }
        })

    }

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.