2016-10-19 2 views
1

В любое время по моему приложению я замечаю, что когда я звоню UIView.animateWithDuration, если у меня есть какие-либо другие запросы автоматической компоновки или даже что-то такое же простое, как изменение константа NSLayoutConstraint, эти изменения также будут анимированы, даже если они НЕ находятся внутри блока анимации. Например: у меня есть UIViewController, который сразу после вызова viewDidAppear добавит изображение, которое было выбрано пользователем, на UIScrollView, чтобы его можно было изменить и отредактировать. Я работаю над добавлением небольшого демонстрационного представления с некоторыми GIF, чтобы помочь пользователю при первом использовании этого приложения. Как только появится представление, он должен добавить изображение (я установил его, чтобы оно соответствовало их экрану вначале), в scrollView, а затем анимировать demonstrationView альфа от нуля до 1.0. Проблема, которую я испытываю, хотя моя функция добавления и изменения размера изображения assignImageChosen вызывается до animateWithDuration и находится вне блока анимации, изменения в ограничениях размера также анимируются.UIView animateWithDuration Анимация Auto-Layout Views За пределами блока анимации

override func viewDidAppear(animated: Bool) { 

    if let image = startImage { 
     self.assignImageChosen(image) 
     currentlyEditing = true 
    } 

    if preLoadGame { 

     UIView.animateWithDuration(1.0, delay: 0.5, options: UIViewAnimationOptions.CurveEaseInOut, animations: { 


      self.demonstrationView.alpha = 1.0 
      self.view.layoutIfNeeded() 

      }, completion: nil) 

    } 

} 

Вот функция assignImageChosen:

func assignImageChosen(image: UIImage) { 

    var ratio:CGFloat! 
    let screen:CGFloat = max(self.scrollView.frame.height, self.scrollView.frame.width) 
    var maxImage:CGFloat! 
    if UIDevice.currentDevice().orientation.isPortrait { 
     if image.size.height >= image.size.width { 
      maxImage = max(image.size.height, image.size.width) 
     } 
     else { 
      maxImage = min(image.size.height, image.size.width) 
     } 
    } 
    else { 
     if image.size.width >= image.size.height { 
      maxImage = max(image.size.height, image.size.width) 
     } 
     else { 
      maxImage = min(image.size.height, image.size.width) 
     } 
    } 

    if maxImage > screen { 
     ratio = screen/maxImage 
    } 
    else { 
     ratio = 1.0 
    } 

    self.imageView.image = image 
    self.imageHeight.constant = image.size.height * ratio 
    self.scrollView.contentSize.height = image.size.height * ratio 
    self.imageWidth.constant = image.size.width * ratio 
    self.scrollView.contentSize.width = image.size.width * ratio 

} 

Переменные imageHeight и imageWidth оба NSLayoutConstraint «s, и imageView является UIImageView, к которому они установлены. Поэтому, как только я изменяю изображение, я меняю ограничения, чтобы изображениеView внутри UIScrollView соответствовало экрану. Проблема в том, что когда я делаю это так, хотя изменения вызывают вне блока анимации, эти изменения все еще анимируются.

Вот пример GIF того, что я пытаюсь (плохо), чтобы общаться с помощью слов:

issue

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

override func viewDidAppear(animated: Bool) { 

    if let image = startImage { 
     self.assignImageChosen(image) 
     currentlyEditing = true 
    } 

    if preLoadGame { 

     let delay = 0.5 * Double(NSEC_PER_SEC) 
     let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay)) 
     dispatch_after(popTime, dispatch_get_main_queue(), { 
      UIView.animateWithDuration(1.0, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: { 


       self.demonstrationView.alpha = 1.0 
       self.view.layoutIfNeeded() 

       }, completion: nil) 
     }) 

    } 

} 

Таким образом, вместо того, чтобы использовать переменную delay внутри функции animateWithDuration, я вместо того, чтобы задержать выполнение функции. Это работает, но я думаю, что должен быть лучший способ сделать это! Почему эти изменения макета анимируются, когда они не находятся внутри блока анимации? Как я могу обойти это правильно, эффективно?

Вот как это выглядит с моей хак, и как я предположил бы, что он должен выглядеть без него:

with hack

Я признателен за любую помощь с этим вопросом расстраивает!

ответ

2

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

К счастью, решение кажется таким простым, как можно было надеяться, вызвать layoutIfNeeded перед выполнением анимации, прежде чем называть его снова в блоке. Согласно this answer и другим, это, по сути, официальная рекомендация Apple, хотя ссылка, которую они предоставляют apple.com, нарушена.

(Кстати, о вашем взломе, есть случаи, когда вам нужно пошатнуть изменения после того, как UIKit завершил свой макет, например, чтобы что-то сделать в UITableView после завершения reloadData. «Правильный взлом», это сделать UIViewAnimation, а затем в своем блоке завершения сделать вторую анимацию, сохранив ее последовательность в UIKit land.)

+1

Wow thank you! Я не могу поверить, насколько невероятно просто было решение. Эта проблема периодически меня била месяцами! Это всегда горько-сладкий, когда ответ такой простой: приятно знать, что его так легко исправить, что вам сложно разобраться в том, что вы так потратили столько времени на что-то такое простое! еще раз спасибо – Pierce

Смежные вопросы