2016-03-16 1 views
1

Итак, моя цель - сделать своего рода анимацию раздвижных дверей в ответ на жест скольжения. Вы можете увидеть GIF моей текущей анимации here (игнорируйте тот факт, что жест ведет себя противоположно тому, что вы ожидаете).CALayers: A) Могу ли я рисовать непосредственно на них и B) Как сделать настройку ширины на суперслое влиять на подуровень

Вот как я сейчас это делаю: у меня есть подкласс UIView Я звоню в DoorView. DoorView имеет три CALayers: базовый суперслой, который поставляется с каждым UIView; подслой под названием doorLayer, который является белым прямоугольником, который скользит; и еще один подслой, называемый frameLayer, который является «дверной рамкой» (черная рамка вокруг doorLayer). У DoorLayer и frameLayer есть свои отдельные анимации, которые запускаются последовательно.

Вот что мне нужно добавить в DoorView: простой прямоугольник, представляющий ручку двери. На данный момент я не планирую давать ручке свою собственную анимацию. Вместо этого я хочу, чтобы это просто было «привязано» к дверному слою, чтобы оно анимировалось вместе с любыми анимациями, применяемыми к doorLayer.

Вот где мой первый вопрос: я знаю, что я могу добавить еще один слой (назовем его handleLayer) и добавить его в качестве подуровня в doorLayer. Но есть ли способ просто «нарисовать» маленький прямоугольник на дверном слое, не требуя дополнительного слоя? И если да, то это предпочтительнее по любой причине?

Теперь для моего второго вопроса: поэтому на данный момент я фактически использую отдельный слой, называемый handleLayer, который добавляется в качестве подуровня в doorLayer. Вы можете увидеть GIF анимации с помощью handleLayer here.

А вот анимация применяется к doorLayer:

UIView.animateWithDuration(1.0, animations: {() -> Void in 
      self.doorLayer.frame.origin.x = self.doorLayer.frame.maxX 
      self.doorLayer.frame.size.width = 0 
} 

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

Как вы можете видеть в приведенном выше GIF, сдвиг начала дверного слоя применяется к подуровню handleLayer по желанию. Но настройка ширины не переносится на handleLayer. И это хорошо, потому что я не хочу, чтобы ручка становилась более узкой с той же скоростью, что и дверной слой.

Вместо этого желательно, чтобы handleLayer перемещался с помощью слоя DoorLayer, но сохранял свой размер. Но когда дверной слой исчезает в правой части дверной коробки, ручка исчезает вместе с ним (как бы выглядела с нормальной дверью). Есть ли какие-нибудь подсказки, как лучше всего это сделать?

В настоящее время в анимации моего doorLayer, я добавил эту строку:

if self.doorLayer.frame.size.width <= self.handleLayer.frame.size.width { 
       self.handleLayer.frame.size.width = 0 
} 

Но это приводит к this, что не совсем верно.

Спасибо за помощь!

ответ

2

От высокого уровня, вам нужно будет

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

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

func didTapView(gesture:UITapGestureRecognizer) { 

    // Create a couple of closures to perform the animations. Each 
    // closure takes a completion block as a parameter. This will 
    // be used as the completion block for the Core Animation transaction's 
    // completion block. 

    let slideAnimation = { 
     (completion:(() ->())?) in 
     CATransaction.begin() 
     CATransaction.setCompletionBlock(completion) 
     CATransaction.setAnimationDuration(1.0) 
     if CATransform3DIsIdentity(self.slideLayer.transform) { 
      self.slideLayer.transform = CATransform3DMakeTranslation(220.0, 0.0, 0.0) 
     } else { 
      self.slideLayer.transform = CATransform3DIdentity 
     } 
     CATransaction.commit() 
    } 

    let scaleAnimation = { 
     (completion:(() ->())?) in 
     CATransaction.begin() 
     CATransaction.setCompletionBlock(completion) 
     CATransaction.setAnimationDuration(1.0) 
     if CATransform3DIsIdentity(self.baseLayer.transform) { 
      self.baseLayer.transform = CATransform3DMakeScale(2.0, 2.0, 2.0) 
     } else { 
      self.baseLayer.transform = CATransform3DIdentity 
     } 
     CATransaction.commit() 
    } 

    // Check to see if the slide layer's transform is the identity transform 
    // which would mean that the door is currently closed. 
    if CATransform3DIsIdentity(self.slideLayer.transform) { 
     // If the door is closed, perform the slide animation first 
     slideAnimation({ 
      // And when it completes, perform the scale animation 
      scaleAnimation(nil) // Pass nil here since we're done animating 
     }) 
    } else { 
     // Otherwise the door is open, so perform the scale (down) 
     // animation first 
     scaleAnimation({ 
      // And when it completes, perform the slide animation 
      slideAnimation(nil) // Pass nil here since we're done animating 
     }) 
    } 
} 

Вот как слои настроены изначально:

func addLayers() { 
    baseLayer = CALayer() 
    baseLayer.borderWidth = 10.0 
    baseLayer.bounds = CGRect(x: 0.0, y: 0.0, width: 220, height: 500.0) 
    baseLayer.masksToBounds = true 
    baseLayer.position = self.view.center 

    slideLayer = CALayer() 
    slideLayer.bounds = baseLayer.bounds 
    slideLayer.backgroundColor = UIColor.whiteColor().CGColor 
    slideLayer.position = CGPoint(x: baseLayer.bounds.size.width/2.0, y: baseLayer.bounds.size.height/2.0) 

    let knobLayer = CALayer() 
    knobLayer.bounds = CGRect(x: 0.0, y: 0.0, width: 20.0, height: 20.0) 
    knobLayer.cornerRadius = 10.0 // Corner radius with half the size of the width and height make it round 
    knobLayer.backgroundColor = UIColor.blueColor().CGColor 
    knobLayer.position = CGPoint(x: 30.0, y: slideLayer.bounds.size.height/2.0) 

    slideLayer.addSublayer(knobLayer) 

    baseLayer.addSublayer(slideLayer) 

    self.view.layer.addSublayer(baseLayer) 
} 

А вот что анимация выглядит следующим образом:

Door Animation

Вы можете увидеть полный Xcode проект здесь: https://github.com/perlmunger/Door

+0

Отлично, большое вам спасибо за подробный ответ! Мне любопытно, должен ли я/должен использовать CATransactions и тому подобное, а не UIView.animateWithDuration. Я полагаю, что главное отличие состоит в том, что вы используете преобразования, а не простые корректировки фреймов/границ. Просто интересно, какие преимущества/потребности. –

+0

Но, конечно же, я попытался настроить иерархию слоев в соответствии с вашими требованиями и установить masksToBounds на базовом уровне, но оставил весь код animateWithDuration в такт. И я не получаю желаемого результата. [GIF здесь] (http://gfycat.com/SmugEasyIndianpangolin). Как вы можете видеть, ручка исчезает в дверной коробке правильно, но затем становится видимой, когда рама расширяется. Хотя я могу просто пропустить что-то глупое. –

+0

Вы уверены, что ваша ручка двери является дочерью скользящего слоя? –