如何在 Mac 上与图层支持的视图交互
我正在设计一个包含多个标签和文本字段的用户界面。我想像这样设置 UI 样式:
- 为我的
NSWindow
的内容视图设置背景图案, - 在左上角的背景中添加自定义图标
我通过制作内容视图解决了第一个问题层支持的视图,如 Apple 的 NSView
文档:
层支持的视图是由核心动画层支持的视图。视图完成的任何绘制都会缓存在支持层中。您只需调用值为
YES
的setWantsLayer:
即可配置图层支持的视图。视图类将自动为您创建一个支持层,并且您可以使用视图类的绘图机制。 当使用图层支持的视图时,您永远不应该直接与图层交互。层托管视图是包含您打算直接操作的核心动画层的视图。您可以通过实例化 Core Animation 图层类的实例并使用视图的
setLayer:
方法设置该图层来创建图层托管视图。执行此操作后,您可以使用值YES
调用setWantsLayer:
。 使用图层托管视图时,不应依赖该视图进行绘图,也不应向图层托管视图添加子视图。
然后生成 CGColorRef
绘制我的 CGImage 的 CGPattern :
NSView *mainView = [[self window]contentView];
[mainView setWantsLayer:YES];
为了将背景图像设置为图案,我使用了来自 如何在此处平铺 CALayer 的内容 以完成第一个任务。
然而,对于第二个任务,添加图标我使用了下面的代码:
CGImageRef iconImage = NULL;
NSString *path = [[NSBundle mainBundle] pathForResource:@"icon_128" ofType:@"png"];
if(path != nil) {
NSURL *imageURL = [NSURL fileURLWithPath:path];
provider = CGDataProviderCreateWithURL((CFURLRef)imageURL);
iconImage = CGImageCreateWithPNGDataProvider(provider,NULL,FALSE,kCGRenderingIntentDefault);
CFRelease(provider);
}
CALayer *iconLayer = [[CALayer alloc] init];
// layer is the mainView's layer
CGRect layerFrame = layer.frame;
CGFloat iconWidth = 128.f;
iconLayer.frame = CGRectMake(0.f, CGRectGetHeight(layerFrame)-iconWidth, 128.f, 128.f);
iconLayer.contents = (id)iconImage;
CGImageRelease(iconImage);
[layer insertSublayer:iconLayer atIndex:0];
[iconLayer release];
问题
- 我不确定我是否违反了Apple关于层支持视图的限制,即您永远不应该直接与层交互。设置图层的背景颜色时我直接与图层交互还是我在这里弄错了?
- 我对直接与图层支持视图的图层层次结构进行交互并插入新图层(就像我在第二个任务中所做的那样)有一种不好的感觉。这是可能的还是也违反了苹果的指导方针?我想指出的是,这个内容视图当然有几个子视图,例如标签、文本视图和按钮。
- 在我看来,仅使用一个层托管
NSView
似乎是最干净的解决方案。然后,所有文本标签都可以添加为 CATextLayers 等。但是,如果我正确理解 Apple 的文档,我就无法再向视图添加任何控件。我是否必须自己在自定义CALayers
中编写所有控件才能使其正常工作?听起来像是重新发明了豪华轮子。我也不知道如何仅在 CoreAnimation 中编写NSTextField
。
任何有关如何使用 CoreAnimation 和标准控件拆分设计用户界面的建议都值得赞赏。
请注意,我在这里谈论的是 Mac。
I am designing a user interface containing several labels and text fields. I would like to style the UI like this:
- setting a background pattern for the content view of my
NSWindow
- adding a custom icon to the background in the upper left corner
I solved the first problem by making the content view a layer-backed view as described in Apple's documentation of NSView
:
A layer-backed view is a view that is backed by a Core Animation layer. Any drawing done by the view is the cached in the backing layer. You configured a layer-backed view by simply invoking
setWantsLayer:
with a value ofYES
. The view class will automatically create the a backing layer for you, and you use the view class’s drawing mechanisms. When using layer-backed views you should never interact directly with the layer.A layer-hosting view is a view that contains a Core Animation layer that you intend to manipulate directly. You create a layer-hosting view by instantiating an instance of a Core Animation layer class and setting that layer using the view’s
setLayer:
method. After doing so, you then invokesetWantsLayer:
with a value ofYES
. When using a layer-hosting view you should not rely on the view for drawing, nor should you add subviews to the layer-hosting view.
and then generating a CGColorRef
out of a CGPattern
which draws my CGImage
:
NSView *mainView = [[self window]contentView];
[mainView setWantsLayer:YES];
To set the background image as a pattern I used the answer from How to tile the contents of a CALayer here on SO to get the first task done.
However for the second task, adding the icon I used the code below:
CGImageRef iconImage = NULL;
NSString *path = [[NSBundle mainBundle] pathForResource:@"icon_128" ofType:@"png"];
if(path != nil) {
NSURL *imageURL = [NSURL fileURLWithPath:path];
provider = CGDataProviderCreateWithURL((CFURLRef)imageURL);
iconImage = CGImageCreateWithPNGDataProvider(provider,NULL,FALSE,kCGRenderingIntentDefault);
CFRelease(provider);
}
CALayer *iconLayer = [[CALayer alloc] init];
// layer is the mainView's layer
CGRect layerFrame = layer.frame;
CGFloat iconWidth = 128.f;
iconLayer.frame = CGRectMake(0.f, CGRectGetHeight(layerFrame)-iconWidth, 128.f, 128.f);
iconLayer.contents = (id)iconImage;
CGImageRelease(iconImage);
[layer insertSublayer:iconLayer atIndex:0];
[iconLayer release];
The Questions
- I am not sure if I am violating Apple's restrictions concerning layer-backed views that you should never interact directly with the layer. When setting the layer's background color I am interacting directly with the layer or am I mistaken here?
- I have a bad feeling about interacting with the layer hierarchy of a layer-backed view directly and inserting a new layer like I did for my second task. Is this possible or also violating Apple's guidelines? I want to point out that this content view of course has several subviews such as labels, a text view and buttons.
- It seems to me that just using one single layer-hosting
NSView
seems to be the cleanest solution. All the text labels could then be added asCATextLayers
etc. However if I understand Apple's documentation correctly I cannot add any controls to the view anymore. Would I have to code all the controls myself in customCALayers
to get it working? Sounds like reinventing the wheel de luxe. I also have no idea how one would code aNSTextField
solely in CoreAnimation.
Any advice on how split designing user interfaces with CoreAnimation and standard controls is appreciated.
Please note that I am talking about the Mac here.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
不需要图层支持恕我直言:
对于 1. 我
为 2. 添加一个 NSImageView 作为内容视图的子视图
在 mac 上,如果有图层支持,某些视图不会很好地响应
:: 例如 pdfview
no layer backing needed IMHO:
for 1. I do a pattern image
for 2. add an NSImageView as a subview of the contentview
on mac some views dont respond nicely IF layer backed
:: e.g. pdfview
创建一个超级视图容器 A。将子视图 B 添加到 A 以满足您的所有 NSView 需求(按钮等)。将子视图 C 添加到 A 以满足您的所有核心动画需求。
编辑:
更好的是:使用超级视图 A 来满足您所有的 NSView 需求,使用一个子视图 C 来满足您的核心动画需求,完全忽略视图 B。
Make a superview container A. Add a subview B to A for all your NSView needs (buttons, etc.). Add a subview C to A for all your Core Animation needs.
Edit:
Even better: use superview A for all your NSView needs and one subview C for your Core Animation needs, ignoring view B altogether.