Отменить анимацию UIView?
Можно ли отменить UIView
анимация пока идет? Или мне придется опуститься до уровня CA?
то есть я сделал что-то вроде этого (может быть, установил действие окончания анимации тоже):
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:duration];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
// other animation properties
// set view properties
[UIView commitAnimations];
Но прежде чем анимация завершится, и я получу событие завершения анимации, я хочу отменить его (сократить его). Это возможно? В поисках гугла находят несколько человек, задающих один и тот же вопрос без ответов - и один или два человека гадают, что это невозможно.
18 ответов
Я делаю это, чтобы создать новую анимацию для вашей конечной точки. Установите очень короткую продолжительность и убедитесь, что вы используете +setAnimationBeginsFromCurrentState:
способ начать с текущего состояния. Когда вы устанавливаете его в YES, текущая анимация обрывается. Выглядит примерно так:
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.1];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
// other animation properties
// set view properties
[UIView commitAnimations];
Использование:
#import <QuartzCore/QuartzCore.h>
.......
[myView.layer removeAllAnimations];
Простейший способ немедленно остановить все анимации в определенном виде:
Свяжите проект с QuartzCore.framework. В начале вашего кода:
#import <QuartzCore/QuartzCore.h>
Теперь, когда вы хотите остановить все анимации на представлении, мертвом в своих треках, скажите это:
[CATransaction begin];
[theView.layer removeAllAnimations];
[CATransaction commit];
Средняя линия будет работать сама по себе, но есть задержка, пока не закончится runloop ("момент перерисовки"). Чтобы предотвратить эту задержку, оберните команду в явный блок транзакции, как показано. Это работает при условии, что в текущем цикле выполнения не было никаких других изменений на этом слое.
На iOS 4 и выше используйте UIViewAnimationOptionBeginFromCurrentState
опция для второй анимации, чтобы сократить первую анимацию коротко.
В качестве примера предположим, что у вас есть представление с индикатором активности. Вы хотите добавить индикатор активности в то время, когда начинается какое-то потенциально трудоемкое действие, и затемнить его, когда действие закончится. В приведенном ниже коде вид с индикатором активности называется 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;
}
}];
}
Чтобы отменить анимацию, вам просто нужно установить свойство, которое в данный момент анимируется, вне анимации UIView. Это остановит анимацию, где бы она ни находилась, и UIView перейдет к настройке, которую вы только что определили.
Извините, что воскресил этот ответ, но в iOS 10 все изменилось, и теперь отмена возможна, и вы даже можете отменить изящно!
После iOS 10 вы можете отменить анимацию с UIViewPropertyAnimator!
UIViewPropertyAnimator(duration: 2, dampingRatio: 0.4, animations: {
view.backgroundColor = .blue
})
animator.stopAnimation(true)
Если вы передадите true, анимация будет отменена и остановится там, где вы ее отменили. Метод завершения не будет вызван. Однако, если вы передадите false, вы несете ответственность за завершение анимации:
animator.finishAnimation(.start)
Вы можете закончить анимацию и оставаться в текущем состоянии (.current) или перейти в исходное состояние (.start) или в конечное состояние (.end)
Кстати, вы можете даже приостановить и перезагрузить позже...
animator.pauseAnimation()
animator.startAnimation()
Примечание. Если вы не хотите внезапного отмены, вы можете полностью изменить анимацию или даже изменить ее после приостановки!
Если вы анимируете ограничение, изменяя константу вместо свойства представления, ни один из других методов не работает на iOS 8.
Пример анимации:
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) {
}];
Решение:
Вам необходимо удалить анимацию из слоев любых видов, на которые влияет изменение ограничения, и их подслоев.
[self.constraintView.layer removeAllAnimations];
for (CALayer *l in self.constraintView.layer.sublayers)
{
[l removeAllAnimations];
}
Если вы просто хотите плавно приостановить / остановить анимацию
self.yourView.layer.speed = 0;
Источник: Как приостановить анимацию дерева слоев
Ничто из вышеперечисленного не решило это для меня, но это помогло: UIView
Анимация сразу устанавливает свойство, а затем анимирует его. Останавливает анимацию, когда уровень представления соответствует модели (свойство set).
Я решил свою проблему, которая была "Я хочу анимировать, откуда вы выглядите, как вы выглядите" ("вы" означает представление). Если вы хотите ЭТО, то:
- Добавьте QuartzCore.
CALayer * pLayer = theView.layer.presentationLayer;
установить положение на уровне представления
Я использую несколько вариантов, включая UIViewAnimationOptionOverrideInheritedDuration
Но поскольку документация Apple нечеткая, я не знаю, действительно ли она перекрывает другие анимации при использовании или просто сбрасывает таймеры.
[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) {}];
И это должно быть достойным решением на данный момент.
[UIView setAnimationsEnabled:NO];
// your code here
[UIView setAnimationsEnabled:YES];
Свифт версия решения Стивена Дарлингтона
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(0.1)
// other animation properties
// set view properties
UIView.commitAnimations()
Даже если вы отмените анимацию способами, описанными выше didStopSelector
все еще работает. Так что если в вашем приложении есть логические состояния, управляемые анимацией, у вас будут проблемы. По этой причине описанными выше способами я использую контекстную переменную UIView
анимации. Если вы передаете текущее состояние вашей программы через контекстный параметр анимации, когда анимация останавливает didStopSelector
Функция может решить, должна ли она что-то делать или просто вернуть, основываясь на текущем состоянии и значении состояния, переданном в качестве контекста.
У меня та же проблема; API не имеют ничего, чтобы отменить какую-то конкретную анимацию.
+ (void)setAnimationsEnabled:(BOOL)enabled
отключает ВСЕ анимации, и, следовательно, не работает для меня. Есть два решения:
1) сделать ваш анимированный объект подпредставлением. Затем, когда вы хотите отменить анимацию для этого вида, удалите вид или скройте его. Очень просто, но вам нужно воссоздать подпредставление без анимации, если вам нужно держать его в поле зрения.
2) Повторите аним только один, и сделайте селектор делегата, чтобы перезапустить аним, если это необходимо, например:
-(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];
}
}
Проблема с 2) заключается в том, что при остановке анимации всегда задерживается остановка анимации.
Надеюсь это поможет.
None of the answered solutions worked for me. I solved my issues this way (I do not know if it is a correct way?), because I had problems when calling this too-fast (when previous animation was not yet finished). I pass my wanted animation with customAnim block.
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()
}
})
}
}
Чтобы приостановить анимацию, не возвращая состояние в исходное или окончательное состояние:
CFTimeInterval paused_time = [myView.layer convertTime:CACurrentMediaTime() fromLayer:nil];
myView.layer.speed = 0.0;
myView.layer.timeOffset = paused_time;
Если вы ищете гибкую анимацию, которая может запускаться и останавливаться в соответствии с вашими потребностями, используйте UIViewPropertyAnimator.
образец кода
let sampleView = UIView()
lazy var animator: UIViewPropertyAnimator = {
let animator = UIViewPropertyAnimator(duration: 1.2, curve: .easeOut)
animator.isInterruptible = true
return animator
}()
// add and your animatable property
animator.addAnimations {
// what you need to animate add here
sampleView.alpha = 0
}
// start animation
animator.startAnimation(afterDelay: 0.5)
Если вам нужно остановиться в середине анимации. это остановит анимацию в ее текущем состоянии.
// stop animation
if animator.isRunning {
animator.stopAnimation(true)
}
CALayer * pLayer = self.layer.presentationLayer;
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView animateWithDuration:0.001 animations:^{
self.frame = pLayer.frame;
}];
Когда я работаю с UIStackView
анимация, кроме removeAllAnimations()
Мне нужно установить некоторые значения в исходное, потому что removeAllAnimations()
можно установить их в непредсказуемое состояние. я имею stackView
с view1
а также view2
внутри, и один вид должен быть виден, а другой скрыт:
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)
}