13

間もなく登場する OSX 10.10 ("Yosemite") は、新しいタイプのビューである NSVisualEffectView を提供します。これは、窓越しまたは窓内の半透明性をサポートします。私は主に窓越しの半透明性に関心があるので、この質問ではそれに焦点を当てますが、窓内の半透明性にも当てはまります。

10.10 で透過的な透過性を使用するのは簡単です。NSVisualEffectViewビュー階層のどこかに を配置し、 に設定するだけblendingModeですNSVisualEffectBlendingModeBehindWindow。それだけです。

10.10 では、IB で を定義NSVisualEffectViewし、それらのブレンド モード プロパティを設定するだけで、すぐに実行できます。

ただし、以前の OSX バージョンとの下位互換性を維持したい場合は、それができません。XIB に を含めようNSVisualEffectViewとすると、XIB をロードしようとするとすぐにクラッシュします。

10.10 で実行すると透過性を提供し、以前の OS バージョンで実行すると不透明なビューに単純に劣化する「設定して忘れる」ソリューションが必要です。

これまでに行ったことは、問題のビューを XIB の通常の NSView にしてから、(awakeFromNib によって呼び出される) をチェックするコードを追加することです[NSVisualEffectView class] != nil。クラスが定義されたら、NSVisualEffectView のインスタンスを作成します。現在のビューのすべてのサブビューを新しいビューに移動し、所定の位置にインストールします。これは機能しますが、半透明のビューが必要になるたびに記述しなければならないカスタム コードです。

NSProxy オブジェクトを使用すると、これが可能になる可能性があると考えています。これが私が考えていることです:

NSView のカスタム サブクラスを定義します (MyTranslucentView と呼びましょう)。すべての init メソッド (initWithFrame および initWithCoder) で、新しく作成されたオブジェクトを破棄し、代わりにプライベート インスタンス変数 (myActualView) を持つ NSProxy のサブクラスを作成します。初期化時に、OS>=10.10 の場合は myActualView オブジェクトを NSVisualEffectView として作成し、OS<10.10 では通常の NSView を作成することを決定します。

プロキシは、すべてのメッセージを myActualView に転送します。

これはかなりの面倒で低レベルのコードですが、うまくいくはずです。

誰かがこのようなことをしましたか?もしそうなら、あなたは私を正しい方向に向けるか、私に何か指針を与えることができますか?

Apple はYosemiteとの Beta 契約で、以前の Beta 契約よりもはるかにオープンです。これについて一般的な用語で話すことで、ベータNDAに違反しているとは思いませんが、実際に使用するコードNSVisualEffectViewはおそらくNDAの下で共有する必要があります...

4

3 に答える 3

11

非常に単純ですが、ややハックな解決策がありNSVisualEffectViewます。アプリの起動時に名前が付けられたクラスを動的に作成するだけです。次に、OS X 10.9 以前で適切なフォールバックを使用して、クラスを含む nib をロードできます。

アイデアを説明するために、私のアプリデリゲートの抜粋を次に示します。

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 になります。その場合、次の 2 行は、NSViewメソッドも ivar も持たない のサブクラスを という名前で作成しNSVisualEffectViewます。

したがって、AppKitNSVisualEffectViewが nib ファイルからアーカイブを解除すると、新しく作成されたクラスが使用されます。そのサブクラスは、NSView と同じように動作します。

しかし、なぜすべてが炎上しないのでしょうか?

ビューが nib ファイルからアーカイブ解除されると、NSKeyedArchiver. これの良いところは、 のプロパティ / ivar に対応する追加のキーを単純に無視することですNSVisualEffectView

他に気をつけることはありますか?

  1. NSVisualEffectViewコード内 ( など)のプロパティにアクセスする前に、クラスがセレクター ( )materialに応答することを確認してください。[view respondsToSelector:@selector(setMaterial:)]
  2. [[NSVisualEffectView alloc] initWithFrame:]クラス名はコンパイル時に解決されるため、まだ機能しません。を使用するか、 if is NULL[[NSClassFromString(@"NSVisualEffectView") alloc] initWithFrame:]を割り当てるだけです。NSView[NSVisualEffectView class]
于 2014-10-16T14:26:11.570 に答える
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
于 2014-10-03T16:42:40.490 に答える
0

@Confused Vorlon のバリエーションを作成しましたが、次のように子ビューを視覚効果ビューに移動しました。

@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;
}

@end
于 2015-08-11T04:33:17.743 に答える