即将推出的OSX10.10(“约塞米蒂”)提供了一种新的视图,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子类。在初始阶段,它将决定将myActualView对象创建为NSVisualEffectView if OS>=10.10,以及OS<10.10下的普通NSView。
代理将把所有的消息转发给它的myActualView。
这将是相当数量的繁琐,低级别的代码,但我认为它应该能工作。
有人做过这样的事吗?如果是的话,你能给我指出正确的方向或者给我指点吗?
苹果是,与约塞米蒂的协议比以前的贝塔更开放。我不认为我是违反了我的Beta谈论这一一般术语,但实际使用NSVisualEffectView的代码可能需要共享在NDA .
发布于 2014-10-16 14:26:11
有一个非常简单但有点麻烦的解决方案:在应用程序启动时动态创建一个名为NSVisualEffectView的类。然后,您可以加载包含该类的nib,在OSX10.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您必须在OSX10.10SDK上编译它。
它是如何工作的?
当您的应用程序运行在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:],要么只分配一个NSView (如果[NSVisualEffectView class]为NULL )。发布于 2014-10-03 16:42:40
我只是在我的顶层视图中使用这个类别。
如果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发布于 2015-08-11 04:33:17
最后,我得到了@困惑的Vorlon's的变体,但是将子视图移到了视觉效果视图,如下所示:
@implementation NSView (Vibrancy)
- (instancetype) insertVibrancyView
{
Class vibrantClass = NSClassFromString( @"NSVisualEffectView" );
if( vibrantClass ) {
NSVisualEffectView* vibrant = [[vibrantClass alloc] initWithFrame:self.bounds];
[vibrant setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
NSArray* mySubviews = [self.subviews copy];
for( NSView* aView in mySubviews ) {
[aView removeFromSuperview];
[vibrant addSubview:aView];
}
[self addSubview:vibrant];
return vibrant;
}
return nil;
}
@endhttps://stackoverflow.com/questions/25914918
复制相似问题