0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

WKWebView で YouTube モバイル版のショートの棚とタブを CSS/JS で隠す:やり方と壊れやすいところ

0
Last updated at Posted at 2026-10-26

minulo は、アプリの中で m.youtube.com を WKWebView で開き、ショートの棚と下のショートタブだけを見えなくする iPhone アプリです。この記事では、その作り方と、運用してみて分かった壊れやすいところをまとめます。(この記事は minulo の開発者が書いています)

先に結論を書きます。

  • 隠すのは CSS が基本で、WKUserScript を .atDocumentStart で入れる
  • 後から足される要素と、ページを読み直さない画面の切り替えには MutationObserver で追いつく
  • 一番の手間は、YouTube 側が画面の作りを変えたときに黙って効かなくなること
  • 審査では「ただの Web の包み」に見えないこと、YouTube の名前やロゴの扱いに気をつける

セレクタはすべて説明用の例です。HTML は予告なく変わります。

なぜ公式アプリではなく WKWebView なのか

iPhone では、ほかのアプリの表示を変えるアプリは作れません。なので「YouTube のショートだけ消したい」なら、Safari の機能拡張を作るか、自分のアプリの中でウェブ版を開いて表示を変えるか、のどちらかになります。

1. WKUserScript で CSS を最初に入れる

CSS はページの読み込みが終わってから入れると、棚が一瞬見えてから消えます。.atDocumentStart で入れておけば、最初に描かれる時点で効いています。

import WebKit

let css = """
ytm-reel-shelf-renderer,
ytm-pivot-bar-item-renderer:has(a[href^="/shorts"]) {
  display: none !important;
}
"""

let source = """
(function () {
  var style = document.createElement('style');
  style.id = 'my-hide-style';
  style.textContent = \(String(reflecting: css));
  (document.head || document.documentElement).appendChild(style);
})();
"""

let script = WKUserScript(
    source: source,
    injectionTime: .atDocumentStart,
    forMainFrameOnly: true
)

let config = WKWebViewConfiguration()
config.userContentController.addUserScript(script)
let webView = WKWebView(frame: .zero, configuration: config)
webView.load(URLRequest(url: URL(string: "https://m.youtube.com/")!))

気をつける点は3つです。

  • .atDocumentStart の時点では head がまだ無いことがあるので、documentElement にも逃げられるようにしておきます。
  • :has() は iOS 15.4 以降の WebKit で使えます。それより古い iOS も対象なら、親を隠す処理は次の JS 側でやります。
  • CSS の文字列を Swift から埋め込むときは、引用符や改行で壊れないように String(reflecting:) などで JS の文字列にしてから入れます。

iOS 14 以降なら、WKContentWorld を指定してページ側の JS と分けておくと、ページのスクリプトに自分の関数を書き換えられる心配が減ります。

let isolated = WKUserScript(
    source: source,
    injectionTime: .atDocumentStart,
    forMainFrameOnly: true,
    in: .defaultClient
)

ただし style 要素は DOM に入るのでページから見えます。

2. MutationObserver で後から出てくる要素に追いつく

m.youtube.com は、スクロールすると棚を足し、画面を移るときもページを読み直さずに中身を差し替えます。CSS で済むものは CSS が勝手に効きますが、「この見出しの棚だけ」「このリンクを含む親だけ」のように JS で判断したいものは、1回動かしただけでは足りません。

(function () {
  function apply() {
    document.querySelectorAll('a[href^="/shorts/"]').forEach(function (a) {
      var shelf = a.closest('ytm-reel-shelf-renderer, ytm-item-section-renderer');
      if (shelf && !shelf.dataset.myHidden) {
        shelf.dataset.myHidden = '1';
        shelf.style.setProperty('display', 'none', 'important');
      }
    });
  }

  var scheduled = false;
  var observer = new MutationObserver(function () {
    if (scheduled) return;
    scheduled = true;
    requestAnimationFrame(function () {
      scheduled = false;
      apply();
    });
  });

  function start() {
    observer.observe(document.documentElement, { childList: true, subtree: true });
    apply();
  }

  if (document.documentElement) start();
  else document.addEventListener('DOMContentLoaded', start);
})();

書いてみて分かったことです。

  • 呼ばれる回数を絞る。 DOM の変化がとても多いので、上の例では requestAnimationFrame で1コマに1回にまとめています。
  • 画面の切り替えは URL で見る。 ページを読み直さない切り替えでは WKUserScript はもう一度動きません。location.pathname を覚えておき、変わったら画面ごとのルールを入れ替えます。
  • /shorts/動画ID に直接来た場合。 ほかのページからショートの動画へ移ると、プレーヤーがそのまま出ます。読み直しを伴う移動なら webView(_:decidePolicyFor:decisionHandler:) で止められますが、pushState による移動はそこを通りません。JS 側で URL の変化を見て、元の画面へ戻すなどの対応が要ります。

3. セレクタが変わったときに壊れる

この方法で一番手間がかかるのはここです。YouTube がタグ名や属性、並び方を変えると、セレクタは何も言わずに当たらなくなります。エラーも例外も出ず、隠していた棚がまた出てくるだけです。気づくのは、たいてい利用者から言われたときです。

やっていることを挙げます。

  • 当たった数を数える。 ルールごとに、そのページで何件当たったかを記録します。ホームなのにショートの棚のルールが0件なら、壊れた疑いがあります。
  • 定期的に自動で確かめる。 主なページを開いて、ルールが当たるかを週に1回くらい確かめます。
  • 変わりにくい手がかりを選ぶ。 自動で付く class 名より、リンク先の href や aria-label、タグ名のほうが長持ちしやすいです。ただ aria-label は表示言語で変わるので、多言語で使うなら頼りすぎないほうが安全です。
  • 隠すより押せなくするほうがよい場所もある。 下のタブのように横に並んでいるものは、1つ消すと残りの幅が変わって崩れることがあります。pointer-events: none と opacity で押せなくするだけのほうが、見た目が壊れにくい場合があります。

それでも、直すまでの間は隠していたものが出ます。防げないので、アプリの説明に先に書いておくほうが、低い評価を付けられるより楽です。

4. App Store の審査で気をつけること

ガイドラインは改定されるので、提出前に原文を確かめてください。そのうえで意識しておくとよい点です。

  • ただの Web の包みに見せない。 最低限の機能(4.2)に当たると、サイトを開くだけのアプリは断られやすいです。何をするアプリなのか、ウェブ版をそのまま開くのと何が違うのかを、画面と説明文ではっきりさせます。
  • YouTube の名前とロゴ。 アプリ名やアイコン、スクリーンショットで YouTube のロゴを大きく使うと、知的財産(5.2)の点で引っかかりやすくなります。「YouTube のショートを隠す」のように説明の中で名前を出すのと、公式に見える使い方は分けて考えます。
  • 動画の保存やバックグラウンド再生をしない。 隠すこととは別に、相手のサービスの利用規約で禁じられている機能を足すと、そこを理由に断られることがあります。
  • ログインとプライバシー。 ログインは YouTube(Google)のページで直接してもらい、アプリでは ID やパスワードを受け取らないようにします。差し込んだスクリプトはページの中身を読めるので、何を送って何を送らないかをプライバシーの申告と説明文に正直に書きます。
  • 開く先を決めておく。 ページとして開く行き先を、対象のサイトとログインの画面に絞っておくと、どこでも開けるブラウザとして見られにくくなります。reCAPTCHA などの確認は iframe で来るので、そこまで止めるとログインできなくなります。

minulo での使い方

ここからは自分のアプリの話です。私は北林 歩といって、minulo という iPhone アプリをひとりで作っています。minulo は Instagram・YouTube・TikTok などの公式のサイトをアプリの中で開き、リールやショート、おすすめを隠します。DM やふつうの動画はそのまま使えます。隠す機能は無料です。

公式アプリの中は変えられないので、公式の Instagram・YouTube・TikTok アプリはスクリーンタイムで止め、アイコンを押すと「minulo で開く」から minulo に移れるようにしています。

上で書いた壊れやすさは minulo でもそのままです。隠すルールは毎週自動で点検していますが、SNS 側が画面の作りを変えると、隠していたものがしばらく出ることがあります。ほかにも、公式アプリを止めている間はそのアプリの通知が届かない、ストーリーズの投稿や通話はウェブ版に無いので使えない、といった限界があります。ログインは各SNSのページで直接行い、ページに入れるスクリプトは画面の文字を見ますが、保存も送信もしません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?