Подтвердить что ты не робот

IOS, Перезапуск анимации при выходе из фона

когда мое приложение выходит из фона, анимация остановилась. что является нормальным. но я хочу перезапустить мою анимацию из текущего состояния. как я могу это сделать без моего щелчка по всему месту.

[UIView animateWithDuration:60 delay:0 options:(UIViewAnimationOptionCurveLinear |UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState) animations:^{
    [bg setFrame:CGRectMake(0, 0, 1378, 1005)];
} completion:nil];

Я попытался поместить рамку набора перед анимацией, но это просто заставляет ее сбрасывать.

[bg setFrame:CGRectMake(0, 0, 1378, 1005)];

любые идеи?

4b9b3361

Ответ 1

ну, ответ @dany_23 мог бы работать.

Но я натолкнулся на другой метод, который отлично работает, если вам не нужно возобновлять анимацию, но перезапустите свою анимацию, не зная при просмотре или слое при повторном активации приложения.

в

- (void)applicationWillResignActive:(UIApplication *)application

вы вызываете метод в вашем контроллере view, который реализует следующий код.

[view.layer removeAllAnimations];
 // this following CGRect is the point where your view originally started 
[bg setFrame:CGRectMake(0, 0, 1378, 1005)]; 

и в

- (void)applicationDidBecomeActive:(UIApplication *)application

вы вызываете метод в вашем контролере просмотра, который только запускает анимацию. что-то вроде

[UIView animateWithDuration:60 delay:0 options:(UIViewAnimationOptionCurveLinear |UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState) animations:^{
      [bg setFrame:CGRectMake(0, 0, 1378, 1005)];
} completion:nil];

Надеюсь, это поможет, спасибо всем, кто ответил.

Ответ 2

Вы можете добавить наблюдателя в свой класс для UIApplicationWillEnterForegroundNotification:

- (void)addNotifications { 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil]; 
}

- (void)applicationWillEnterForeground { 
    [self animate]; 
}

- (void)animate { 
    [bg setFrame:CGRectMake(0, 0, 0, 0)]; 
    [UIView animateWithDuration:60 delay:0 options:(UIViewAnimationOptionCurveLinear |UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState) animations:^{
        [bg setFrame:CGRectMake(0, 0, 1378, 1005)];
    } completion:nil];
}

Важно установить начальное состояние анимации (и не забудьте удалить наблюдателя уведомлений)

Ответ 3

Здесь есть лучшее решение, чем перезапускать всю анимацию каждый раз, когда вы выходите из фона.

Для Swift 3 вы можете создать подкласс этого класса:

class ViewWithPersistentAnimations : UIView {
    private var persistentAnimations: [String: CAAnimation] = [:]
    private var persistentSpeed: Float = 0.0

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.commonInit()
    }

    func commonInit() {
        NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    func didBecomeActive() {
        self.restoreAnimations(withKeys: Array(self.persistentAnimations.keys))
        self.persistentAnimations.removeAll()
        if self.persistentSpeed == 1.0 { //if layer was plaiyng before backgorund, resume it
            self.layer.resume()
        }
    }

    func willResignActive() {
        self.persistentSpeed = self.layer.speed

        self.layer.speed = 1.0 //in case layer was paused from outside, set speed to 1.0 to get all animations
        self.persistAnimations(withKeys: self.layer.animationKeys())
        self.layer.speed = self.persistentSpeed //restore original speed

        self.layer.pause()
    }

    func persistAnimations(withKeys: [String]?) {
        withKeys?.forEach({ (key) in
            if let animation = self.layer.animation(forKey: key) {
                self.persistentAnimations[key] = animation
            }
        })
    }

    func restoreAnimations(withKeys: [String]?) {
        withKeys?.forEach { key in
            if let persistentAnimation = self.persistentAnimations[key] {
                self.layer.add(persistentAnimation, forKey: key)
            }
        }
    }
}

extension CALayer {
    func pause() {
        if self.isPaused() == false {
            let pausedTime: CFTimeInterval = self.convertTime(CACurrentMediaTime(), from: nil)
            self.speed = 0.0
            self.timeOffset = pausedTime
        }
    }

    func isPaused() -> Bool {
        return self.speed == 0.0
    }

    func resume() {
        let pausedTime: CFTimeInterval = self.timeOffset
        self.speed = 1.0
        self.timeOffset = 0.0
        self.beginTime = 0.0
        let timeSincePause: CFTimeInterval = self.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
        self.beginTime = timeSincePause
    }
}

Он позаботится о приостановке всех ваших анимаций в текущем состоянии и их повторном добавлении, когда приложение выйдет из фона - без их сброса.

Суть: https://gist.github.com/grzegorzkrukowski/a5ed8b38bec548f9620bb95665c06128

Ответ 4

Вам придется приостановить анимацию, когда ваше приложение перейдет в фоновый режим и возобновит его, когда он снова активируется. Вы можете найти пример кода здесь, в котором описывается, как приостановить и возобновить анимацию.

Ответ 5

Хорошо, если я правильно понял вопрос, вы пытаетесь создать анимацию и остановите ее посередине.

В этом случае я предлагаю вам использовать какую-то "государственную" переменную для хранения состояния анимации (например, для свойства frame). Эта переменная, которую вы можете использовать в дополнительном коде, чтобы поместить объект, который анимируется в правильном положении. Это можно сделать в функции animationDidStop (animationDidStop: закончен:). Для реализации этой функции вам придется использовать делегацию. Это означает, что AppNameViewController (или какой-либо другой уникальный объект для всего приложения) является делегатом для анимации и записывает реализацию в свой m файл. Это позволит вам запустить код установки позиции в конце анимации.

Следующая задача - сохранить состояние анимации. При запуске анимации среда Core Animation создает пучок промежуточных слоев (слоев представления). И эти слои отображаются во время анимации, а затем они удаляются. И когда анимация завершает выполнение, объект просто помещается в это конечное состояние. На самом деле вам нужно установить это, когда вы создаете так называемую "явную анимацию" (и если вы этого не сделаете, анимация будет воспроизводиться, но объект вернется в конце). Каждый из этих слоев представления имеет набор или свою собственную копию всех свойств, которые называются "анимируемые свойства". Когда один из анимационных свойств задан как ключ для анимации, вызовы Core Animation (BOOL) требуютDisplayForKey, который возвращает YES/NO. Он сообщает Core Animation, изменились ли изменения к указанному ключу, чтобы слой был повторно отображен. "Повторить" означает вызов метода drawInContext для уровня представления. Здесь вы можете захватить свойства отображаемого слоя, который в настоящее время отображается, и помещать их в переменную состояния.

Анимация воспроизводится в потоке, и для того, чтобы установить переменную "state", я использовал делегирование. Он требовал подкласса CALayer (позволяет использовать AnimLayer) и определять в нем протокол только одним методом, который хранит "состояние". Этот метод имеет один параметр, который является состоянием. Затем я применил метод протокола, который сохраняет состояние в классе AnimLayer и устанавливает уникальный объект в качестве делегата. Таким образом, эти слои представления (копии AnimLayer) не сохраняют состояние. Вместо этого значение "состояние", переданное как параметр функции делегата, сохраняется уникальным объектом в основном потоке.

Я сделал что-то подобное, но моя задача была немного иной. Я не знаю, как проще, извините. Надеюсь, это поможет.

Ответ 6

@Grzegorz Krukowski ответ работал для меня как очарование. Я просто хотел добавить, что если вам не нужно возобновлять анимацию с того места, где она остановилась, а имитировать ее работу в фоновом режиме, вы можете просто пропустить эти строки:

let timeSincePause: CFTimeInterval = self.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
self.beginTime = timeSincePause

из функции resume() в расширении CALayer и оставьте beginTime CALayer 0.0.

Кроме того, вы можете вручную вызывать willResignActive и didBecomeActive для анимации, которую вы хотите возобновить, когда ViewController исчезает, даже если приложение не уходит в фоновый режим.

Ответ 7

Этот способ будет лучше, просто зарегистрируйтесь на ApplicationDelegate станет методами и наблюдателем статус.

- (void)pauseAnimate{
    CFTimeInterval pausedTime = [self.layer timeOffset];
    self.layer.speed = 1.0;
    self.layer.timeOffset = 0.0;
    self.layer.beginTime = 0.0;
    CFTimeInterval timeSincePause = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
    self.layer.beginTime = timeSincePause;
}

- (void)stopAnimate{
    CFTimeInterval pausedTime = [self.layer convertTime:CACurrentMediaTime() fromLayer:nil];
    self.layer.speed = 0.0;
    self.layer.timeOffset = pausedTime;
}

Ответ 8

По состоянию на выпуск swift5.

Это также стоит отметить. Это в некоторых случаях, вам нужно сбросить анимированное свойство, чтобы новая анимация прикрепилась. Я могу подтвердить это с помощью анимированного преобразования.

Просто звоню...

view.layer.removeAllAnimations()

не хватило, и новая анимация не запустилась бы. Я также должен был бежать...

view.transform = .identity

Я подозреваю, что вы можете установить любое новое значение или сохранить предыдущее состояние и установить его.

Ответ 9

Я не помню точных деталей, но думаю, что вы можете получить текущую анимированную позицию кадра, посмотрев на границы слоев и свойство позиции вашего представления. Вы можете сохранить их в приложении suspend и восстановить их, когда приложение снова появится на переднем плане.