10

我想实现一个功能,当用户将鼠标悬停在特定区域上时,新视图会出现类似抽屉的动画。而且,当用户离开特定区域时,抽屉应该以动画形式消失。这正是您在 OS X 中将鼠标悬停在屏幕底部时所看到的,其中 Dock 会随着动画的出现和消失。

但是,如果我用动画实现该功能,则在动画完成之前重新进入特定区域时,它无法正常工作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,这种情况很少发生。但我想用动画移动视图。

我想要做的是让drawerViewstart 再次出现,即使视图还没有完全消失。有什么练习可以做到吗?

4

2 回答 2

12

我有一个与您的代码非常相似的解决方案。我所做的不同之处在于我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

让我知道这是否有用。乐于做出改变。

于 2015-03-09T19:14:02.460 回答
10

启动 Swift 3。你需要这样做:

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

上面的学分@marc!

于 2016-07-01T03:36:56.430 回答