Edited at

UIViewの背景に動画を流す簡単な方法

More than 1 year has passed since last update.


iOSで背景に動画を流す簡単な方法

初回起動画面などで背景に動画が流れているアプリってよくありますよね。

そのようにボタンなどのUIの後ろで動画を流す簡単な方法を紹介します。


最小実装

import UIKit

import AVFoundation

class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()

// Bundle Resourcesからsample.mp4を読み込んで再生
let path = Bundle.main.path(forResource: "sample", ofType: "mp4")!
let player = AVPlayer(url: URL(fileURLWithPath: path))
player.play()

// AVPlayer用のLayerを生成
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = view.bounds
playerLayer.videoGravity = .resizeAspectFill
playerLayer.zPosition = -1 // ボタン等よりも後ろに表示
view.layer.insertSublayer(playerLayer, at: 0) // 動画をレイヤーとして追加
}
}

demo

短い行で実現できましたね。

試しにラベルとボタンを置いてみます。

let label = UILabel(frame: CGRect(x: 0, y: 100, width: 200, height: 40))

label.text = "Hello!"
label.textColor = .white
label.font = UIFont.boldSystemFont(ofSize: 50)
label.textAlignment = .center
label.center.x = view.center.x
label.autoresizingMask = [.flexibleWidth, .flexibleBottomMargin]
view.addSubview(label)

let loginButton = UIButton(frame: .init(x: 30, y: view.frame.height - 150, width: view.frame.width - 60, height: 50))
loginButton.setTitle("LOG IN", for: .normal)
loginButton.setTitleColor(.white, for: .normal)
loginButton.layer.borderWidth = 1
loginButton.layer.borderColor = UIColor.white.cgColor
loginButton.layer.cornerRadius = 4
loginButton.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
view.addSubview(loginButton)

let signupButton = UIButton(frame: loginButton.frame)
signupButton.frame.origin.y = loginButton.frame.minY - 60
signupButton.setTitle("SIGN UP", for: .normal)
signupButton.setTitleColor(.white, for: .normal)
signupButton.backgroundColor = UIColor(red: 0, green: 168.0/255, blue: 107.0/255, alpha: 1)
signupButton.layer.cornerRadius = 4
signupButton.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
view.addSubview(signupButton)

demo

はい、よく見る画面になりましたね。

これだけでも十分ですが、さらによくある以下の実装を追加してみます。


  • 動画を少し薄暗くする

  • リピート再生する

  • アプリをバックグラウンドにした後、フォアグラウンドにしても自動再生される (デフォルトでは停止してしまいます)

  • 端末を回転してもサイズが調整される


動画を薄暗くする & リピート再生 & アプリ復帰後自動再生 & 端末回転対応

class ViewController: UIViewController {

private var observers: (player: NSObjectProtocol,
willEnterForeground: NSObjectProtocol,
bounds: NSKeyValueObservation)?

override func viewDidLoad() {
super.viewDidLoad()

let path = Bundle.main.path(forResource: "sample", ofType: "mp4")!
let player = AVPlayer(url: URL(fileURLWithPath: path))
player.actionAtItemEnd = .none
player.play()

let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = view.bounds
playerLayer.videoGravity = .resizeAspectFill
playerLayer.zPosition = -2 // 次に追加するoverlayより後ろにする
view.layer.insertSublayer(playerLayer, at: 0)

// 動画の上に重ねる半透明の黒いレイヤー
let dimOverlay = CALayer()
dimOverlay.frame = view.bounds
dimOverlay.backgroundColor = UIColor.black.cgColor
dimOverlay.zPosition = -1
dimOverlay.opacity = 0.4 // 不透明度
view.layer.insertSublayer(dimOverlay, at: 0)

// 最後まで再生したら最初から再生する
let playerObserver = NotificationCenter.default.addObserver(
forName: .AVPlayerItemDidPlayToEndTime,
object: player.currentItem,
queue: .main) { [weak playerLayer] _ in
playerLayer?.player?.seek(to: kCMTimeZero)
playerLayer?.player?.play()
}

// アプリがバックグラウンドから戻ってきた時に再生する
let willEnterForegroundObserver = NotificationCenter.default.addObserver(
forName: .UIApplicationWillEnterForeground,
object: nil,
queue: .main) { [weak playerLayer] _ in
playerLayer?.player?.play()
}

// 端末が回転した時に動画レイヤーのサイズを調整する
let boundsObserver = view.layer.observe(\.bounds) { [weak playerLayer, weak dimOverlay] view, _ in
DispatchQueue.main.async {
playerLayer?.frame = view.bounds
dimOverlay?.frame = view.bounds
}
}

observers = (playerObserver, willEnterForegroundObserver, boundsObserver)
}

deinit {
// 画面が破棄された時に監視をやめる
if let observers = observers {
NotificationCenter.default.removeObserver(observers.player)
NotificationCenter.default.removeObserver(observers.willEnterForeground)
observers.bounds.invalidate()
}
}
}

demo

※デモ動画ではボタンとラベルのコードも追加しています。


まとめ

意外と簡単に実現することが出来ました。

細かい制御を入れると少しコード量が増えるので、実際にはUIViewController内に記述せず、別のクラスに実装するのが良いと思います。

サンプルコードはこちらに置いてあります。

https://github.com/tattn/BackgroundVideoSample


関連URL

デモに使用した動画

http://mazwai.com/#/videos/218