swift - 如何与鼠标事件的 NSView 帧同时更新 NSView 图层位置和锚点以进行变换
问题描述
我目前正在尝试实现一些自定义 NSView,这些 NSView 将通过 macOS 应用程序中的某些事件进行动画处理。这些控件与您在 AudioUnit 插件中找到的滑块线一致。
这个问题已经回答了动画的问题。
我不确定的是如何更改视图的 CALayer 位置和锚点以进行旋转并更新其框架以进行鼠标事件。
作为一个基本示例,我希望通过创建一个 NSView 并设置它的背景颜色来绘制一个正方形。然后,我想在 mouseDown 事件上为视图的旋转设置动画,就像上一个链接一样。
通过以下设置,视图被移动到窗口的中心,它的锚点被改变,以便视图围绕它而不是围绕原点旋转window.contentView!
。但是,移动view.layer.position
和设置view.layer!.anchorPoint = CGPoint(x: 0.5,y: 0.5)
并不会移动将检测事件的框架。
@IBOutlet weak var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification)
{
let view = customView(frame: NSRect(x: 0, y: 0, width: 50, height: 50))
window.contentView!.wantsLayer = true
view.wantsLayer = true
view.layer?.backgroundColor = NSColor.blue.cgColor
window.contentView?.addSubview(view)
let containerFrame = window.contentView!.frame
let center = CGPoint(x: containerFrame.midX, y: containerFrame.midY)
view.layer?.position = center
view.layer?.anchorPoint = CGPoint(x: 0.5,y: 0.5)
}
在这种情况下,customView 只是一个 NSView,其中包含在 mouseDown 上执行的上一个链接的旋转动画
override func mouseDown(with event: NSEvent)
{
let timeToRotate = 1
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.fromValue = 0.0
rotateAnimation.toValue = angle
rotateAnimation.duration = timeToRotate
rotateAnimation.repeatCount = .infinity
self.layer?.add(rotateAnimation, forKey: nil)
}
通过设置移动框架view.frame = view.layer.frame
会导致视图围绕其中一个角旋转。所以,相反,我改变了view.setFrameOrigin()
@IBOutlet weak var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification)
{
let view = customView(frame: NSRect(x: 0, y: 0, width: 50, height: 50))
window.contentView!.wantsLayer = true
view.wantsLayer = true
view.layer?.backgroundColor = NSColor.blue.cgColor
window.contentView?.addSubview(view)
let containerFrame = window.contentView!.frame
let center = CGPoint(x: containerFrame.midX, y: containerFrame.midY)
let offsetFrameOrigin = CGPoint(x: center.x - view.bounds.width/2,
y: center.y - view.bounds.height/2)
view.setFrameOrigin(offsetFrameOrigin)
view.layer?.position = center
view.layer?.anchorPoint = CGPoint(x: 0.5,y: 0.5)
}
将 view.frame 原点设置为中心的偏移量可以达到我想要的效果,但我不禁觉得这有点“hacky”,而且我可能以错误的方式处理这个问题。view.layer
特别是因为对或view.frame
将导致动画不正确或在绘制之外检测到事件的任何进一步更改。
如何更改 NSView.layer 使其在设置 NSView.frame 的同时围绕其中心旋转,以便在正确的区域检测到鼠标事件?
此外,是否正在改变NSView.layer.anchorPoint
一种正确的方法来设置它围绕其中心旋转?
解决方案
我想我的处境和你差不多。幸运的是,我偶然发现了一个要点,它扩展NSView
了一个为我完成这项工作的setAnchorPoint
函数(使图层的锚点与主框架保持同步)。
Swift 4 的要点有一个分支。这是代码本身:
extension NSView
{
func setAnchorPoint (anchorPoint:CGPoint)
{
if let layer = self.layer
{
var newPoint = CGPoint(x: self.bounds.size.width * anchorPoint.x, y: self.bounds.size.height * anchorPoint.y)
var oldPoint = CGPoint(x: self.bounds.size.width * layer.anchorPoint.x, y: self.bounds.size.height * layer.anchorPoint.y)
newPoint = newPoint.applying(layer.affineTransform())
oldPoint = oldPoint.applying(layer.affineTransform())
var position = layer.position
position.x -= oldPoint.x
position.x += newPoint.x
position.y -= oldPoint.y
position.y += newPoint.y
layer.position = position
layer.anchorPoint = anchorPoint
}
}
}
然后,您可以直接像这样使用它(NSView
即不是它的层):
func applicationDidFinishLaunching(_ aNotification: Notification)
{
// ... //
let view = customView(frame: NSRect(x: 0, y: 0, width: 50, height: 50))
view.wantsLayer = true
view.setAnchorPoint(anchorPoint: CGPoint(x: 0.5, y: 0.5))
// ... //
}
推荐阅读
- android - 使用 react-native-firebase 启用 Proguard
- git - 在嵌套的依赖项目中使用 git 的方案
- amazon-dynamodb - 如何设置 DynamoDB 范围键、字符串或映射
- php - 从批准松弛到 php 中获取价值
- python - 从python中的pdf文件对象中提取文本
- pandas - 我们使用 Spark 是因为它更快还是因为它可以处理大量数据?
- c# - 无法加载文件或程序集 Bond.IO
- java - 我需要帮助如何在 java 中访问 VM 中的文件
- android - Android和roundedimageview:如何改变边框颜色
- c# - 如果在关闭时使用,是否保证方法的其余部分将在 WhenAll 之后执行?