How to implement NSTrackingArea mouseEntered / Exited with animation?

I want to implement a function that, when a user is hovering over a specific area, a new view appears with an animation similar to a box. And also, when the user leaves a certain area, the box should go with the animation. This is exactly what you see when you hover the top of the screen in OS X, where the Dock appears and disappears with animation.

However, if I implement a function with animation, it does not work properly when I re-enter a specific area until the animation in mouseExited: complete. Here is my code:

 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 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 frame upon launch is (-120, 0, 120, 300), since it is not visible at first 

In this code, I drawerView by changing its x position. However, as I said, when you enter the tracking zone and then leave the tracking zone, the box works correctly. But this is not the case if you enter the tracking zone again before the lowering animation is fully completed.

Of course, if I set the animation duration to shorter, for example 0.1 , this rarely happened. But I want to move the view using animation.

I want to make drawerView appear again , even if the view did not end the fade . Is there any practice for this?

+6
source share
2 answers

I have a solution very similar to your code. What I do differently is that I install NSTrackingArea not in the view containing the box view, but in the box view itself.

This, obviously, means that the box should stick out a little. In my case, the box is a little visible when it is not working, because I put an image in it. If you do not want this, I suggest you just leave the visible area of ​​the box empty and translucent.

Here is my implementation:

 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) } } 

Full project https://github.com/st3fan/StackOverflow-28777670-TrackingArea

Let me know if this was helpful. We are happy to make changes.

+8
source

Running Swift 3. You need to do this as follows:

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

Credits @marc above!

+5
source

Source: https://habr.com/ru/post/983139/


All Articles