UWP

[UWP]CompactOverlay/ミニモードを使ってみる

CompactOverlay/ミニモードって?

  • Windows 10 Creators Updateで追加された「最前面表示可能なウィンドウ表示モード」です。
  • CompactOverlay、ミニモード、またはPicture in Picture (PinP)とも呼ばれます。(この記事では説明文で「ミニモード」、コード内では「CompactOverlay」と表記)
  • ウィンドウ表示モードは現在は「Default」と「CompactOverlay」があります。
  • CUの機能を利用する場合は、UniversalApiContractのバージョン4がサポートされていることを確認した上でAPIを利用していくように気をつけていきます。*
  • Creators Update現在においてはWindows 10にのみ対応(デスクトップとタブレット)しており、Windows 10 Mobile及びXboxOne(Windows 10 IOTも?)では利用できません
  • アプリのメインウィンドウを含めて、ApplicationViewを通じて表示モード(ViewMode)を切り替えることができます。

ちなみに2017/08/25時点でWindows10のCreatorsUpdate適用率はだいたい60%弱とのこと。対応する価値はありそうですね。

ミニモードをサポートしているかを調べる

CU適用済みであってもViewModeの種類によっては対応していないプラットフォームがあるのでチェックしていきます。
ミニモード利用不可能な環境ではミニモード開始のボタンを非表示にするなど対応が必要となります。(そのためのStateTrigger)

単にミニモードをサポートしているかだけチェックするなら

var view = ApplicationView.GetForCurrentView();
var isSupported = view.IsViewModeSupported(ApplicationViewMode.CompactOverlay);

XamlのVisualStateManager向けのStateTriggerにするなら

using Windows.Foundation.Metadata;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;

namespace NicoPlayerHohoema.Views.StateTrigger
{
    public class IsSupportCompactOverlayTrigger : StateTriggerBase
    {
        public IsSupportCompactOverlayTrigger()
        {
            if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 4))
            {
                var view = ApplicationView.GetForCurrentView();
                var supported = view.IsViewModeSupported(ApplicationViewMode.CompactOverlay);
                SetActive(supported);
            }
            else
            {
                SetActive(false);
            }
        }
    }
}

ミニモードを検出するStateTrigger

ミニモード開始に反応してUIの表示状態を制御するためのStateTriggerです。

using Windows.Foundation.Metadata;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;

namespace NicoPlayerHohoema.Views.StateTrigger
{
    public class CompactOverlayViewModeTrigger : InvertibleStateTrigger
    {
        public CompactOverlayViewModeTrigger()
        {
            if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 4))
            {
                Update();
                Window.Current.SizeChanged += Current_SizeChanged;
            }
            else
            {
                SetActiveInvertible(false);
            }
        }

        ~CompactOverlayViewModeTrigger()
        {
            if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 4))
            {
                Window.Current.SizeChanged -= Current_SizeChanged;
            }
        }

        private void Current_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
        {
            Update();
        }

        private void Update()
        {
            var view = ApplicationView.GetForCurrentView();

            SetActiveInvertible(view.ViewMode == ApplicationViewMode.CompactOverlay);
        }
    }

    abstract public class InvertibleStateTrigger : StateTriggerBase
    {
        public bool Inverted { get; set; } = false;

        protected void SetActiveInvertible(bool isActive)
        {
            SetActive(Inverted ? !isActive : isActive);
        }
    }
}

現在のウィンドウをミニモードに切り替える

public static async Task SwitchViewModeCompactOverayAsync(bool isCompactOverlay)
{
    if (!ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 4))
    {
        return;
    }

    var appView = ApplicationView.GetForCurrentView();
    if (appView.IsViewModeSupported(ApplicationViewMode.CompactOverlay))
    {
        if (isCompactOverlay)
        {
            // PC等1080pであればデフォルトで最大サイズ(横500px 縦280px)で表示されるが
            // タブレット等の小サイズ画面ではより小さい幅で表示されます
            // そのため任意に表示サイズを指定してください
            ViewModePreferences compactOptions = ViewModePreferences.CreateDefault(ApplicationViewMode.CompactOverlay);
            compactOptions.CustomSize = new Windows.Foundation.Size(500, 280);

            var result = await appView.TryEnterViewModeAsync(ApplicationViewMode.CompactOverlay, compactOptions);
            if (result)
            {
                // タイトルバーにもUIを展開表示するための設定(これは必須ではありませんが、見た目的には必要)
                CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = true;
                appView.TitleBar.ButtonBackgroundColor = Colors.Transparent;
                appView.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
            }
        }
        else
        {
            var result = await appView.TryEnterViewModeAsync(ApplicationViewMode.Default);
            if (result)
            {
                // タイトルバーを透過表示するための設定を元に戻す
                CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = false;
                appView.TitleBar.ButtonBackgroundColor = null;
                appView.TitleBar.ButtonInactiveBackgroundColor = null;
            }
        }
    }
}

見栄えのための覚書き

  • ミニモード中にボタンを置く場合には最小150x150まで正常にボタンが表示されるようにAdaptiveTrigger等で対応します
    • 特にミニモードを解除するためのボタンをミニモード開始時のボタンと近しい場所に配置して、いつでもアクセスできるようにします
  • SplitViewを置いている場合、OpenPaneLengthが最小幅150よりも大きい場合に表示が崩れます。この場合ミニモード中にSplitView.OpenPaneLengthが150px以下になるよう対応が必要です。
  • ミニモードのお手本としては MS製の「映画&テレビ」がオススメです。(ローカルのmp4等動画でも利用可能です)

参考

CompactOverlay mode – aka Picture-in-Picture

宣伝

拙作のWindows 10向けのニコニコ動画アプリ Hohoemaはミニモードにも対応してます。是非お試しください。