2015-02-28 5 views
6

Я хочу реализовать функцию, которая, когда пользователь наводится на определенную область, появляется новый вид с анимацией, подобной ящику. А также, когда пользователь покидает определенную область, ящик должен уходить с анимацией. Это именно то, что вы видите, когда вы нависаете над нижней частью экрана в OS X, где Dock появляется и исчезает с анимацией.Как реализовать NSTrackingArea's mouseEntered/Exited с анимацией?

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

let trackingArea = NSTrackingArea(rect: CGRectMake(0, 0, 120, 300), options: NSTrackingAreaOptions.ActiveAlways | NSTrackingAreaOptions.MouseEnteredAndExited, owner: self, userInfo: nil) 
underView.addTrackingArea(trackingArea) // underView is the dummy view just to respond to the mouse tracking, since the drawerView's frame is changed during the animation; not sure if this is the clean way... 

override func mouseEntered(theEvent: NSEvent) { 
    let frameAfterVisible = CGRectMake(0, 0, 120, 300) 
    NSAnimationContext.runAnimationGroup({ 
     (context: NSAnimationContext!) in 
      context.duration = 0.6 
      self.drawerView.animator().frame = frameAfterVisible 
     }, completionHandler: {() -> Void in 
    }) 
} 

override func mouseExited(theEvent: NSEvent) { 
    let frameAfterInvisible = CGRectMake(-120, 0, 120, 300) 
    NSAnimationContext.runAnimationGroup({ 
     (context: NSAnimationContext!) in 
      context.duration = 0.6 
      self.drawerView.animator().frame = frameAfterInvisible 
     }, completionHandler: {() -> Void in 
    }) 
} 

// drawerView's frame upon launch is (-120, 0, 120, 300), since it is not visible at first 

В этом коде я анимировать drawerView, изменяя его x положение. Однако, как я уже сказал, когда вы входите в зону отслеживания, а затем покидаете зону отслеживания, ящик работает правильно. Но это не так, если вы снова войдете в зону отслеживания до того, как анимация опускания будет полностью завершена.

Конечно, если я установил продолжительность анимации короче, например 0.1, это редко случалось. Но я хочу переместить представление с помощью анимации.

Что я хочу сделать, так это вернуть drawerView, даже если вид не завершен, исчезает. Есть ли практика для этого?

+0

Вызывается функция 'mouseEntered()' при повторном входе во время анимации? То есть, проблема заключается в том, что он не вызывается или это не означает, что настройка нового кадра неэффективна? Поместите некоторые записи в обе функции, перед группой анимации, в конце группы анимации и в обработчике завершения. –

ответ

8

У меня есть решение, очень похожее на ваш код. То, что я делаю по-другому, это то, что я устанавливаю NSTrackingArea не в представлении, содержащем вид ящика, а в самом представлении ящика.

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

Вот моя реализация:

private enum DrawerPosition { 
    case Up, Down 
} 

private let DrawerHeightWhenDown: CGFloat = 16 
private let DrawerAnimationDuration: NSTimeInterval = 0.75 

class ViewController: NSViewController { 

    @IBOutlet weak var drawerView: NSImageView! 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     // Remove the auto-constraints for the image view otherwise we are not able to change its position 
     view.removeConstraints(view.constraints) 
     drawerView.frame = frameForDrawerAtPosition(.Down) 

     let trackingArea = NSTrackingArea(rect: drawerView.bounds, 
      options: NSTrackingAreaOptions.ActiveInKeyWindow|NSTrackingAreaOptions.MouseEnteredAndExited, 
       owner: self, userInfo: nil) 
     drawerView.addTrackingArea(trackingArea) 
    } 

    private func frameForDrawerAtPosition(position: DrawerPosition) -> NSRect { 
     var frame = drawerView.frame 
     switch position { 
     case .Up: 
      frame.origin.y = 0 
      break 
     case .Down: 
      frame.origin.y = (-frame.size.height) + DrawerHeightWhenDown 
      break 
     } 
     return frame 
    } 

    override func mouseEntered(event: NSEvent) { 
     NSAnimationContext.runAnimationGroup({ (context: NSAnimationContext!) in 
      context.duration = DrawerAnimationDuration 
      self.drawerView.animator().frame = self.frameForDrawerAtPosition(.Up) 
     }, completionHandler: nil) 
    } 

    override func mouseExited(theEvent: NSEvent) { 
     NSAnimationContext.runAnimationGroup({ (context: NSAnimationContext!) in 
      context.duration = DrawerAnimationDuration 
      self.drawerView.animator().frame = self.frameForDrawerAtPosition(.Down) 
     }, completionHandler: nil) 
    } 
} 

Полный проект на https://github.com/st3fan/StackOverflow-28777670-TrackingArea

Позвольте мне знать, если это было полезно. Рад внести изменения.

+9

Помните, что начиная с Swift 2.0 вы должны следовать другому синтаксису параметров области отслеживания. let trackingArea = NSTrackingArea (rect: self.bounds, options: [NSTrackingAreaOptions.MouseEnteredAndExited, NSTrackingAreaOptions.ActiveAlways], владелец: self, userInfo: nil) –

5

Запуск Swift 3. Вы должны сделать это так:

let trackingArea = NSTrackingArea(rect: CGRectMake(0, 0, 120, 300), options: [NSTrackingAreaOptions.ActiveAlways ,NSTrackingAreaOptions.MouseEnteredAndExited], owner: self, userInfo: nil) 
view.addTrackingArea(trackingArea) 

Кредиты @marc выше!

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