即将推出的OSX 10.10(“Yosemite”)提供了一种新型视图NSVisualEffectView,它支持窗口内或窗口内的半透明效果。我对窗口的半透明感兴趣,所以我将在这个问题中专注于这一点,但它也适用于窗内半透明。
在10.10中使用通过窗口的半透明是微不足道的。您只需在视图层次结构中的某处放置NSVisualEffectView
,并将其设置为blendingMode
至NSVisualEffectBlendingModeBehindWindow
。这就是全部。
在10.10下你可以在IB中定义NSVisualEffectView
,设置他们的混合模式属性,然后你就可以开始运行了。
但是,如果您希望向后兼容早期的OSX版本,则无法执行此操作。如果您尝试在XIB中包含NSVisualEffectView
,则在尝试加载XIB时会立即崩溃。
我想要一个“设置并忘记它”的解决方案,它将在10.10下运行时提供半透明效果,并且在早期操作系统版本上运行时会降级为不透明视图。
到目前为止,我所做的是使有问题的视图在XIB中成为普通的NSView,然后添加检查[NSVisualEffectView class] != nil
的代码(由awakeFromNib调用),并且在定义类时,我创建了一个NSVisualEffectView的实例,将我当前视图的所有子视图移动到新视图,并将其安装到位。这是有效的,但它是我每次想要半透明视图时必须编写的自定义代码。
我认为使用NSProxy对象可能会出现这种情况。这就是我的想法:
定义NSView的自定义子类(让我们称之为MyTranslucentView)。在所有的init方法(initWithFrame和initWithCoder)中,我会抛弃新创建的对象,而是创建一个具有私有实例变量(myActualView)的NSProxy子类。在初始时,如果OS> = 10.10,则决定将myActualView对象创建为NSVisualEffectView,并在OS< 10.10下创建普通NSView。
代理会将所有消息转发给myActualView。
这将是相当多的繁琐,低级别的代码,但我认为它应该有效。
有人做过这样的事吗?如果是这样,你能指出我正确的方向还是给我任何指示?
与以前的Betas相比,与Yosemite a的Beta协议相比,Apple更加开放很多。我不认为我是通过一般性的讨论来违反我的Beta NDA,但使用NSVisualEffectView
的实际代码可能需要在NDA下共享......
答案 0 :(得分:11)
有一个非常简单但有些笨拙的解决方案:只需在应用启动时动态创建一个名为NSVisualEffectView
的类。然后,您可以加载包含该类的nib,并在OS X 10.9及更早版本上使用优雅的回退。
以下是我的应用代表的摘录,以说明这个想法:
<强> AppDelegate.m 强>
#import "AppDelegate.h"
#import <objc/runtime.h>
@implementation PGEApplicationDelegate
-(void)applicationWillFinishLaunching:(NSNotification *)notification {
if (![NSVisualEffectView class]) {
Class NSVisualEffectViewClass = objc_allocateClassPair([NSView class], "NSVisualEffectView", 0);
objc_registerClassPair(NSVisualEffectViewClass);
}
}
@end
您必须针对OS X 10.10 SDK进行编译。
它是如何运作的?
当您的应用在10.9及更早版本上运行时,[NSVisualEffectView class]
将为NULL。在这种情况下,以下两行创建NSView
的子类,没有方法,没有ivars,名称为NSVisualEffectView
。
因此,当AppKit现在从nib文件中取消归档NSVisualEffectView
时,它将使用您新创建的类。该子类的行为与NSView相同。
但为什么一切都不是火上浇油?
当视图从nib文件中取消归档时,它使用NSKeyedArchiver
。关于它的好处是它只是忽略了与NSVisualEffectView
的属性/ ivars相对应的其他键。
我还需要注意什么?
NSVisualEffectView
的任何属性(例如material
)之前,请确保该类响应选择器([view respondsToSelector:@selector(setMaterial:)]
)[[NSVisualEffectView alloc] initWithFrame:]
仍然无法正常工作,因为类名在编译时已解析。如果[[NSClassFromString(@"NSVisualEffectView") alloc] initWithFrame:]
为NULL,请使用NSView
或仅分配[NSVisualEffectView class]
。答案 1 :(得分:3)
我只是在我的顶级视图中使用此类别。
如果NSVisualEffects视图可用,那么它会在后面插入一个活力视图,一切正常。
唯一需要注意的是你有一个额外的子视图,所以如果你以后改变观点,你必须考虑到这一点。
@implementation NSView (HS)
-(instancetype)insertVibrancyViewBlendingMode:(NSVisualEffectBlendingMode)mode
{
Class vibrantClass=NSClassFromString(@"NSVisualEffectView");
if (vibrantClass)
{
NSVisualEffectView *vibrant=[[vibrantClass alloc] initWithFrame:self.bounds];
[vibrant setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
[vibrant setBlendingMode:mode];
[self addSubview:vibrant positioned:NSWindowBelow relativeTo:nil];
return vibrant;
}
return nil;
}
@end
答案 2 :(得分:0)
我收到了@Confused Vorlon的变体,但将子视图移动到视觉效果视图中,如下所示:
{{1}}