Tag: Coreanimation

现代可可观点中的一项习题

最初发布在这里: https : //avaidyam.github.io/2018/03/22/Exercise-Modern-Cocoa-Views.html 就像我在上一个“片段”中所说的那样,最近,我想开始为Parrot添加“热键”支持,并且意识到我不喜欢任何现有解决方案: 使用MASShortcut尝试并证明可在ObjC-land工作的MASShortcut或ShortcutRecorder 。 使用新的但未经验证的Swift热键记录器(那里有一些)。 自己动手(可能以某种方式使用危险的专用SPI)而不进行任何测试! 显然,我选择了#3。 在这里,我们进入了激动人心的传奇的第二部分,您在其中见证了我打破AppKit和WindowServer并引起从事上述组件工作的工程师的愤怒和愤怒。 我希望你带了爆米花。 我想澄清一下,我们将只构建一个管理单个快捷方式的独立控件。 我希望Parrot与大多数应用程序一样,将不需要像Final Cut Pro这样的命令编辑器,该命令编辑器是专门为视频编辑器而专门设计的,该视频编辑器具有两个以上的键盘和两个以上的两只手可以匹配。 我们的最终结果将是看起来,听起来和感觉像这样,而不是: 在进入精妙的手工对编程之前,如果您想直接跳转到带注释的最终源代码,我将在此处提供一个快照。 这是整个最终产品,精确到1500 LOC,包括之前“ episode”中的代码-相应的键盘快捷键功能! 如果您认为这应该是正式的存储库而已,请通过Twitter或Github @avaidyam与我联系! 现代观点原理 子类化View或ViewController 我们应该回答的第一个问题是,我们要构建什么样的组件? 这个问题的答案是什么决定了我们是什么样的子类,以及我们与应用程序中其他组件的交互模型。 当您构建一个采用程序输入并向用户显示内容的分立组件时,对于控件而言,当您接收用户输入并显示程序输出时,您应该将View子类化。 除此之外的任何东西都应该是ViewController的子类,介导其他组件之间的交互的组件,或处理模型/数据库对象的组件,或者实际上只是其他任何东西。 View应该可以在您要立即构建的环境之外的其他环境中轻松重用,但是ViewController不一定能够履行此合同。 例如,联系人头像最好是View子类,但是联系人头像选择器应该是ViewController ,因为它不仅显示头像图像,而且还允许用户选择图像,并且可以处理与头像的同步。联系人存储(可能是JSON文件,可能是远程API,或者可能是CNContact )。 在macOS上,如果要构建的组件既包含其他组件(例如ViewController )又包含在Window ,则还应考虑将WindowController子类化-联系人头像选择器可能不符合此条件,但需要联系人编辑器面板联系人头像选择器将是一个ViewController嵌套在姓名,电话,电子邮件和其他可编辑字段旁边。 层数 虽然与年轻的兄弟姐妹UIKit相比, AppKit通常被视为恐龙,但重要的是要知道, UIKit所使用的几乎所有设计模式几乎都来自AppKit ,如果不是,它们最终会回到AppKit ,除了极少数。 例如,由底层CALayer类型支持的UIView的概念与由NSCell支持(而是过去?)的NSControl非常相似。 不同之处在于CALayer实际上是驱动显示内容的呈现而不是UIView的呈现,并且NSCell充当特定事件处理和绘制的“橡皮图章”。 当涉及到层支持的视图时, AppKit实际上有几种模式可以实现此目的: 层托管: self.layer = CALayer(); self.wantsLayer = true NSView拥有CALayer ,并负责创建和管理它。 […]

CAPluginLayer和CABackdropLayer

最初发表在我的(新的)杂物房中。 私有标头中隐藏着很多很酷的CoreAnimation好吃的东西,但是确实吸引了我的三个私有CALayer类: CAProxyLayer , CALayerHost , CABackdropLayer和CAPluginLayer 。 CALayerHost设计与CARemoteLayerClient/Server相似,因此我将在以后的文章中介绍这两个类。 我对CAProxyLayer了一些实验,因为它用于视觉效果(想想NS/UIVisualEffectView ),或者我不知道正确的常量或没有正确使用它,但是我无法使它可靠地工作而没有麻烦windowserver 。 我将使用代码示例来讨论其余两个。 这个名字很怪异,但是看起来它是为支持windowserver插件而设计的-甚至还有一个vtable,但是到目前为止,仅支持com.apple.WindowServer.CGSWindow 。 这是一个代码示例: let layer = CAPluginLayer() layer.frame = CGRect(x: 50, y: 50, width: 200, height: 200) layer.pluginType = “com.apple.WindowServer.CGSWindow” layer.pluginId = UInt64(self.window.windowNumber) layer.pluginGravity = kCAGravityResizeAspect //layer.pluginFlags = 0x4 // display without a shadow 因此,最大的CAPluginLayer是,现在您只能使用CAPluginLayer实时镜像窗口(其所有UI更改和交互都反映在图层内容中)。 您可以像设置pluginGravity一样设置pluginGravity ,并且唯一已知的pluginFlags值为0x4 ,Dock使用pluginFlags值来显示没有阴影的窗口。 pluginId实际上是指windowNumber (或_realWindowNumber具体取决于您的窗口类型,但这通常不必担心),并且实际上可以是*屏幕上可用的任何窗口! 没错–您可以镜像任何窗口,而不仅仅是自己应用程序的窗口。 为了娱乐,我使用CGWindowListCopyWindowInfo来获取最前面的窗口(不是我自己的应用程序),并使用该ID将其镜像到我的应用程序层中。 […]