如何用动画实现NSTrackingArea的mouseEntered/Exited?
How to implement NSTrackingArea's mouseEntered/Exited with animation?
我想实现一个功能,当用户将鼠标悬停在特定区域时,新视图会出现类似抽屉的动画。而且,当用户离开特定区域时,抽屉应该随着动画消失。这正是您在 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
在这段代码中,我通过改变 x
的位置来设置 drawerView
的动画。但是,正如我所说,当您进入跟踪区域然后离开跟踪区域时,抽屉可以正常工作。但是如果你在离开动画完全完成之前重新进入跟踪区域,情况就不是这样了。
当然如果我把动画时长设置得短一些,比如0.1
,这种情况就很少发生了。但是我想用动画移动视图。
我想做的是让drawerView
再次开始出现即使视图还没有完全消失。有什么实践可以做到吗?
我有一个与您的代码非常相似的解决方案。我所做的不同之处在于,我没有将 NSTrackingArea
安装在包含抽屉视图的视图上,而是安装在抽屉视图本身上。
这显然意味着抽屉需要'stick out'一点。在我的例子中,抽屉在放下时有点可见,因为我在其中放置了一个图像视图。如果你不想要那个,那么我建议你把抽屉的可见区域留空和半透明。
这是我的实现:
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/Whosebug-28777670-TrackingArea
让我知道这是否有用。乐于做出改变。
开始Swift 3.你需要这样做:
let trackingArea = NSTrackingArea(rect: CGRectMake(0, 0, 120, 300), options: [NSTrackingAreaOptions.ActiveAlways ,NSTrackingAreaOptions.MouseEnteredAndExited], owner: self, userInfo: nil)
view.addTrackingArea(trackingArea)
感谢@marc 以上!
我想实现一个功能,当用户将鼠标悬停在特定区域时,新视图会出现类似抽屉的动画。而且,当用户离开特定区域时,抽屉应该随着动画消失。这正是您在 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
在这段代码中,我通过改变 x
的位置来设置 drawerView
的动画。但是,正如我所说,当您进入跟踪区域然后离开跟踪区域时,抽屉可以正常工作。但是如果你在离开动画完全完成之前重新进入跟踪区域,情况就不是这样了。
当然如果我把动画时长设置得短一些,比如0.1
,这种情况就很少发生了。但是我想用动画移动视图。
我想做的是让drawerView
再次开始出现即使视图还没有完全消失。有什么实践可以做到吗?
我有一个与您的代码非常相似的解决方案。我所做的不同之处在于,我没有将 NSTrackingArea
安装在包含抽屉视图的视图上,而是安装在抽屉视图本身上。
这显然意味着抽屉需要'stick out'一点。在我的例子中,抽屉在放下时有点可见,因为我在其中放置了一个图像视图。如果你不想要那个,那么我建议你把抽屉的可见区域留空和半透明。
这是我的实现:
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/Whosebug-28777670-TrackingArea
让我知道这是否有用。乐于做出改变。
开始Swift 3.你需要这样做:
let trackingArea = NSTrackingArea(rect: CGRectMake(0, 0, 120, 300), options: [NSTrackingAreaOptions.ActiveAlways ,NSTrackingAreaOptions.MouseEnteredAndExited], owner: self, userInfo: nil)
view.addTrackingArea(trackingArea)
感谢@marc 以上!