SlideShare a Scribd company logo
1 of 55
Download to read offline
動画プレイヤーアプリの開発を通じて学んだ

機能を実現するための要点解説
iOSDC	Japan	2021
2021/09/17
Fumiya	Sakai
自己紹介
・Fumiya	Sakai
・Freelance	App	Engineer
アカウント:
・Twitter:	https://twitter.com/fumiyasac

・Facebook:	https://www.facebook.com/fumiya.sakai.37

・Github:	https://github.com/fumiyasac	

・Qiita:	https://qiita.com/fumiyasac@github
発表者:
・Born	on	September	21,	1984
これまでの歩み:
Web	Designer
2008	~	2010
Web	Engineer
2012	~	2016
App	Engineer
2017	~	Now
iOS	/	Android	/	sometimes	Flutter
iOSのUI実装本を執筆しています
少しの工夫で実現できるTIPS集 ライブラリ表現の活用集
こちらは同人誌&技術の泉シリーズとして現在販売中のラインナップです。
そして直近1年以内の執筆活動に関する紹介
その他同人誌を執筆や今年のパンフレット原稿の執筆もしました。
現在はBooth	/	BookTech等にて現在も販売中です! iOSDC	Japan	2021のパンフレット原稿も執筆しました。
業務内でUI実装を考える上でのポイントをまとめました。
「円滑なUI&機能実装やデザイナーとの共同作業を進めるために
心がけてきた事」というタイトルで執筆致しました。ほんの少し
でも皆様のご参考になる事ができれば嬉しく思います。
本稿のテキストURLは右のQRコードからアクセスできます!
チームを巻き込みながら、アプリUI実装
と機能ロジックをコラボして仕上げてい
く際に、私自身が心がけている習慣やポ
イントや着眼点等をご紹介しています。

また、本稿では実際に私自身が実践して
いる事例なども掲載しています。
本稿のテキストURLは右のQRコードからアクセスできます!
現在業務で取り組んでいるアプリに関する概要
「GLOBIS学び放題」ビジネスナレッジの定額制動画学習サービス
今回の発表で前提とする形とバックエンドとの連携
バックエンド経由で動画URL等の必要な情報を取得しApp側でコントロールをする
今回想定しているApp側とServer側の関係性
iOS	Application
🎥	Media	files	1	(MP4)
🎥	Media	files	2	(MP4)
🎥	Media	files	N	(MP4)
…
HLS(mp4)形式の動画
DB情報から返却する情報	
📝	Course	Informaition
📊	Score	&	Lesson	Histories
🔑	Authenticated	Token
🏃	Personal	Information
iOSアプリ側の実装概要
UIKit	/	RxSwift	/	Layered	Architecture	/	DDD
Server	Side	Application
GraphQL(Apollo)	/	Restful	API
今回の発表で前提とするiOSアプリ側のアーキテクチャ
MVP(Model-View-Presenter)	+	Layered	Architecture(with	DDD)
今回想定しているApp内部のアーキテクチャ概要
UI UseCase Domain InfraStructure
View	(ViewController)
Presenter
Contract
View-Model-Presenter

※	ViewObject
プレゼンテーション ビジネスロジック ドメインロジック
DomainModel	(Entity)
Repository
DomainService
Api	/	GraphQL	Client
LocalStore
外部技術
ロジック部分はiOS/Android間で実装や機能設計で比較できる様に設計思想を統一している
※	基本的には疎結合な構造&責務の明確化はしている(UnitTestは導入している)形となっています。
※	本発表では主にViewControllerにおける動画再生や関連する部分の処理がメインです。
RestfulAPI	/	GraphQL

UserDefault	/	Realm
今回の発表でお話すること
動画プレイヤーに関連する部分の基本的な機能実装および関連知識の整理&考察
本発表では機能を実現するための実装に関するはじめの一歩を踏み出すための難易度を想定したものになります。
1.	動画プレイヤー機能において必要な基本事項と操作部分の解説:
・動画プレイヤー機能を作成するために必要なものや基本操作の紹介

・動画プレイヤー画面でのレイアウトを考える上でのポイント
2.	動画プレイヤーアプリで特徴的な機能事例と実装ポイントに関するご紹介:
・アプリがバックグラウンドへ移行した際に音声再生へ移行する処理などの紹介

・動画を途中で閉じた場合の再生位置保存機能など内部構造の工夫で実現する機能
3.	その他動画プレイヤーに付随する機能や動画再生に関連するUI実装や表現における考察:
・UI実装の観点から見た動画プレイヤーに付随する部分の実現アイデア紹介

・昨今の動画再生機能を持つアプリでよく見るUI構造や表現に関する考察
1.	動画プレイヤー機能において必要な基本事項と操作部分の解説
動画プレイヤーの基本的な処理を実現するために(1)
動画プレイヤーの機能としてまずは必要なものは何だろうか?
refs:	https://ulog.sugiy.com/swift-avplayer-sample/
①	HLS(mp4)形式の動画再生&停止
②	再生状況取得
③	再生レート変更
④	SeekbBarでの再生位置変更
⑤	任意秒数の巻き戻し&早送り
⑥	ヘッドホン状態検知や音量調整
🤔
等…
①	今回解説する動画プレイヤーで利用するクラス一覧
動画プレイヤーの基本的な処理を実現するために(2)
実現をするために押さえておきたい部分はどこだろうか?
②	機能を実現するに当たって利用する処理や値のポイント
🌟	AVPlayer

🌟	AVPlayerLayer

🌟	AVPlayerItem

🌟	AVAudioSession
今回はあくまでも最低限の動画プレイヤー機能+αの想定
🌟	AVPlayer/AVPlayerLayer等から取得した値の活用

🌟	CMTimeMakeWithSecondsに関する処理

🌟	時間変化をはじめ各種イベントの監視処理	
再生位置とUI表示の整合性
等…
等…
動画プレイヤーの基本的な処理を実現するために(3)
実際の動画プレイヤー画面の構造からポイントを紐解く
再生・停止・10(任意の秒数)秒送り&戻し・再
生レート変更
HLS(mp4)形式の動画を表示する部分は、
AVPlayerLayer	/	AVPlayerを組み込んだUIView継承
クラスを利用して表示する。
1.	動画再生表示部分
2.	動画コントロール部分
再生位置調節用SeekBar
UIButton
UISlider
動画プレイヤーの基本的な処理を実現するために(4)
動画表示用のAVPlayerLayer	/	AVPlayerを組み込んだUIView継承クラス
final	class	AVPlayerView:	UIView	{

				var	player:	AVPlayer?	{

								get	{

												return	playerLayer.player

								}

								set	{

												playerLayer.player	=	newValue

								}

				}

				var	playerLayer:	AVPlayerLayer	{

								return	layer	as!	AVPlayerLayer

				}

				override	static	var	layerClass:	AnyClass	{

								return	AVPlayerLayer.self

				}

				func	setVideoFillMode(mode:	String)	{

								let	layer:	AVPlayerLayer	=	self.layer	as!	AVPlayerLayer

								layer.videoGravity	=	AVLayerVideoGravity(rawValue:	mode)

				}

}
AVPlayer
AVPlayerLayer
表示モード設定
Media	Assetの再生
動画のスクリーン表示
タイミング調整
refs:	https://developer.apple.com/documentation/avfoundation/avplayerlayer
動画プレイヤーの基本的な処理を実現するために(5)
viewDidLoadのタイミングで実施する動画表示部分に関する処理例
🌟	AVPlayerViewクラスに関する設定
private	var	player	=	AVPlayer()
//	MEMO:	AVPlayerを動画表示用のViewにセットする

playerView.player	=	player
//	MEMO:	再生終了時には特に処理はしない

player.actionAtItemEnd	=	AVPlayer.ActionAtItemEnd.none
//	MEMO:	動画ファイルURLを元にAVPlayerItemを作成

player.replaceCurrentItem(with:	AVPlayerItem(url:	videoUrl))
//	MEMO:	再生開始位置を一番最初にセットする

player.seek(to:	CMTimeMakeWithSeconds(0,	preferredTimescale:	
Int32(NSEC_PER_SEC)))
//	MEMO:	動画表示用のViewに関するアスペクト比等を決定する

playerView.setVideoFillMode(mode:	
AVLayerVideoGravity.resizeAspectFill.rawValue)
@IBOutlet	weak	var	playerView:	AVPlayerView!
🌟	AVPlayerクラスに関する設定
VideoLessonStepViewController(動画再生プレイヤー画面)	
バックエンドから取得した動画URLを元に組み立てる
画面に配置して動画を表示させる
1.	AVPlayerの状態監視
動画プレイヤーの基本的な処理を実現するために(6)
動画プレイヤーに必要な時間経過イベントやNotificationの紹介
🌟	一定時間の監視:	addPeriodicTimeObserver(forInterval:queue:using:)
🌟	特定時間の監視:	addBoundaryTimeObserver(forTimes:queue:using:)
🌟	KVO(Key-Value	Observing):	observeValue(forKeyPath:of:change:context:)
2.	AVPlayerItem	/	AVAudioSession	Notification
🌟	ヘッドホンが抜かれた時:	AVAudioSession.routeChangeNotification
🌟	電話の割り込み:	AVAudioSession.interruptionNotification
🌟	動画が最後まで到達:	Notification.Name.AVPlayerItemDidPlayToEndTime
refs:	https://masegi.hatenablog.com/entry/2019/02/18/090034
1.	addPeriodicTimeObserverでの状態監視
動画プレイヤーの基本的な処理を実現するために(7)
AVPlayerの状態監視イベントやNotificationを利用した実装例
//	MEMO:	1秒刻みで再生位置を更新する

player.addPeriodicTimeObserver(

				forInterval:	CMTimeMakeWithSeconds(1.0,	preferredTimescale:	Int32(NSEC_PER_SEC)),	queue:	nil,

				using:	{	[weak	self]	(_:	CMTime)	in

				guard	let	weakSelf	=	self	else	{	return	}

								//	TODO:	動画再生位置表示やSeekBarの更新処理

				}

)
NotificationCenter.default.addObserver(forName:	Notification.Name.AVPlayerItemDidPlayToEndTime,

object:	nil,	queue:	nil)	{	[weak	self]	_	in

				guard	let	weakSelf	=	self	else	{	return	}

				//	TODO:	動画再生が完了した際に実行する処理

}
2.	AVPlayerItemDidPlayToEndTimeでの状態監視
1秒毎に再生位置表示やSeekBar位置を更新する
再生停止処理	&	AVPlayerのreplaceCurrentItemをnilにする
再生中
再生終了
動画プレイヤーの基本的な処理を実現するために(8)
動画再生位置の変更が生じた場合の処理例
player.seek(

				to:	CMTimeMakeWithSeconds(

								Float64(value),

								preferredTimescale:	Int32(NSEC_PER_SEC)

				),

				toleranceBefore:	CMTime.zero,

				toleranceAfter:	CMTime.zero

)
※	加えて再生時間を表示するUILabel等を更新する
slider.maximumValue	=	Float(duration)

let	value	=	(slider.maximumValue	-	slider.minimumValue)	*	
currentTime	/	duration	+	slider.minimumValue

slider.setValue(value,	animated:	false)
1.	各種UI表示や再生位置を計算する
//	MEMO:	currentItemを取得する

guard	let	currentItem	=	player.currentItem	else	{

				return

}

//	MEMNO:	動画全体の長さを取得する

let	duration	=	
Float(CMTimeGetSeconds(currentItem.asset.duration))

//	MEMNO:	現在の動画再生時間を取得する

let	currentTime	=	
Float(CMTimeGetSeconds(player.currentTime()))

//	MEMNO:	残りの動画再生時間を取得する

let	remainTime:	Float	=	duration	-	currentTime
2.	SeekBarの位置調整を実施する
3.	AVPlayerの再生位置を更新する
※	デフォルトでは誤差が発生するので第2,3引数にCMTime.zero
動画プレイヤーの基本的な処理を実現するために(9)
再生時間変化や動画再生完了タイミングを利用した応用例
//	MEMO:	10秒巻き戻しをする

let	currentTime	=	Float(CMTimeGetSeconds(player.currentTime()))

let	adjustedDuration	=	max(currentTime	-	skipInterval,	0.0)
//	MEMO:	10秒早送りをする

guard	let	currentItem	=	player.currentItem	else	{

				return

}

let	duration	=	Float(CMTimeGetSeconds(currentItem.asset.duration))

let	currentTime	=	Float(CMTimeGetSeconds(currentItem.currentTime()))

let	adjustedDuration	=	min(currentTime	+	skipInterval,	duration)
private	let	skipInterval:	Float	=	10.0
1.	各種UI表示や再生位置を計算する 2.	SeekBarの位置調整を実施する
3.	AVPlayerの再生位置を更新する
動画プレイヤーに関連する画面レイアウト構築(1)
縦表示時と横表示時それぞれの状態における細かなUIまわりの違い
🌟	横表示時にのみコントロールエリアを消去&再表示が可能な状態にする
Portrait Landscape
🌟	UIDevice.current	(key名:	orientation)	の値を更新して向きを変更する
ボタン操作で向きを変える処理
動画プレイヤーに関連する画面レイアウト構築(2)
端末の回転を考慮し
た画面構造
View全体&動画表示部分

UITapGestureRecognizerを付与
🌟	回転処理後何もせずに5秒経過
動画コントロールエリアを非表示
動画コントロールエリアが表示された状
態では非表示に切り替え、非表示の場合
は再表示する。
動画視聴を妨げない配慮
デバイスが横向きの場合のみ
動画プレイヤーに関連する画面レイアウト構築(3)
特定の画面のみに関して回転を許可するための実装に関するヒント
final	class	PortraitNavigationController:	UINavigationController	{

				override	var	shouldAutorotate:	Bool	{

								true

				}

				override	var	supportedInterfaceOrientations:	UIInterfaceOrientationMask	{

								return	.portrait

				}

}
※回転を許可する画面ではUINavigationControllerを利用
1.	Project:	✅	Landscape	Left	/	Landscape	Right
2.	動画プレイヤー及び関連する部分以外ではPortraitを固定
⭕	自由記述Quiz
❌	カリキュラム
動画プレイヤーに関連する画面レイアウト構築(4)
デバイスを回転したタイミングで何らかの処理を実施したい場合
refs:	https://www.slideshare.net/yuujihato/adaptive-ui-114253107
override	func	willTransition(to	newCollection:	UITraitCollection,	with	coordinator:	UIViewControllerTransitionCoordinator)	{

				super.willTransition(to:	newCollection,	with:	coordinator)

}
override	func	viewWillTransition(to	size:	CGSize,	with	coordinator:	UIViewControllerTransitionCoordinator)	{

				super.viewWillTransition(to:	size,	with:	coordinator)

  //	MEMO:	UITraitCollectionが、「Regular(Horizontal)	-	Regular(Vertital)」のものがあるのでこのライブサイクルを利用する

				//	※	iPadがこれに該当するサイズ

}
refs:	https://dev.classmethod.jp/references/ios8-trait-collection/
Viewのサイズが変更されようとしていることをコンテナへ伝える
TraitCollectionが変更されたことをコンテナへ伝える
1.	UIInterfaceOrientationの値を利用したデバイス向きの判定
2.	UITraitCollection	/	SizeClass等を利用とデバイスの回転検知との組み合わせ
デバイスの回転を検知するメソッド例
2.	動画プレイヤーアプリで特徴的な機能に関する事例と実装に関するご紹介
バックグラウンド再生機能に関する概要
動画再生中にアプリをバックグラウンド状態にしても音声のみを継続する
現在再生位置から継続できる
🌟	画面	⇄	ControlCenter
🌟	Foreground	⇄	Background
バックグラウンド再生機能のまずは重要なポイント
アプリのライフサイクルやUIApplicationStateに注目する
func	applicationWillEnterForeground(_	application:	UIApplication)
func	sceneWillEnterForeground(_	scene:	UIScene)
func	applicationDidEnterBackground(_	application:	UIApplication)
func	sceneDidEnterBackground(_	scene:	UIScene)
NotificationCenter.default.addObserver(

				forName:	.applicationWillEnterForeground,

				object:	nil,

				queue:	.main

)	{	[weak	self]	_	in

				//	TODO:	動画表示と再生位置を合わせる処理

}
NotificationCenter.default.addObserver(

				forName:	.applicationDidEnterBackground,

				object:	nil,

				queue:	.main

)	{	[weak	self]	_	in

				//	TODO:	動画部分を非表示にする処理

}
NotificationCenter.default.post(

				name:	.applicationDidEnterBackground,	

				object:	nil

)
NotificationCenter.default.post(

				name:	.applicationWillEnterForeground,	

				object:	nil

)
1.	アプリがバックグラウンドへ移行する
2.	アプリがフォアグラウンドに復帰する
3.	(UIApplicationState	==	.background	||	.inactive)	の判定
NotificationCenter.default.addObserver(

				forName:	.applicationWillEnterForeground,

				object:	nil,

				queue:	.main

)	{	[weak	self]	_	in

				guard	let	weakSelf	=	self	else	{

								return

				}

				if	weakSelf.player.isPlaying	{

								weakSelf.playPauseButton.setImage(videoPause.image,	for:	.normal)

				}	else	{

								weakSelf.playPauseButton.setImage(videoPlay.image,	for:	.normal)

				}

				weakSelf.playerView.player	=	weakSelf.player

}
NotificationCenter.default.addObserver(

				forName:	.applicationDidEnterBackground,

				object:	nil,

				queue:	.main

)	{	[weak	self]	_	in

				guard	let	weakSelf	=	self	else	{

								return

				}

				weakSelf.playerView.player	=	nil

}
バックグラウンド再生に関する公式ドキュメント
「Playing	Audio	from	a	Video	Asset	in	the	Background」を参考にする
動画表示部分のplayerプロパティをBackground状態	/	Foreground状態に合わせて変更する
再生状態を元にした判定
バックグラウンド再生の事前準備とコントロール処理(1)
バックグラウンド再生処理を有効にする準備
//	サイレントモードであっても音声を流すための設定

do	{

				try	AVAudioSession.sharedInstance().setCategory(.playback)

}	catch	{

				//	必要に応じてエラーハンドリング等を実施する

				print("Could	not	set	audio	session	category")

}
1.	Project:	✅	Audio,	Air	Play	and	Picture	in	Picture
2.	動画プレイヤー画面を生成する際に下記のコードを追加
バックグラウンド再生の事前準備とコントロール処理(2)
ControlCenter&ViewController間の連携
3.	MPRemoteControlCenterに関する処理を追加
4.	MPNowPlayingInfoCenterに関する処理を追加
🌟	再生&停止
🌟	10秒送り&戻し
🌟	再生位置の決定
🌟	サムネイル画像表示
🌟	メイン&サブタイトル表示
🌟	全体&現在の再生位置
🌟	再生レートの反映
ControlCenterの操作時処理
ControlCenterの表示内容反映など
MPRemoteCommandCenterを利用した処理概要(1)
「Configure	the	Remote	Command	Handlers」を参考にする
changePlaybackPositionCommand
skipForwardCommand	/	skipBackwardCommand
①	再生&停止コントロール
②	10秒送り&戻しコントロール
③	SeekBarでの再生位置コントロール
※	Article「Controlling	Background	Audio」内のSection
ポイントとなる実装方針
🌟	各種addTargetでの実行処理:

クロージャー内に設定するのは動画プレイヤー画面で利用して
いるコントロール処理。
playCommand	/	pauseCommand
MPRemoteCommandCenterを利用した処理概要(2)
「Configure	the	Remote	Command	Handlers」の内容を踏まえたコード例
let	commandCenter	=	MPRemoteCommandCenter.shared()
commandCenter.playCommand.addTarget	{	[weak	self]	_	in

				guard	let	weakSelf	=	self	else	{	return	.commandFailed	}

				weakSelf.play()

				return	.success

}

commandCenter.pauseCommand.addTarget	{	[weak	self]	_	in

				guard	let	weakSelf	=	self	else	{	return	.commandFailed	}

				weakSelf.pause()

				return	.success

}
1.	再生&停止コントロール
🌟	動画プレイヤー画面の再生または停止処理を実行
MPRemoteCommandCenterを利用した処理概要(3)
「Configure	the	Remote	Command	Handlers」の内容を踏まえたコード例
let	commandCenter	=	MPRemoteCommandCenter.shared()
commandCenter.skipForwardCommand.preferredIntervals	=	[NSNumber(value:	skipInterval)]

commandCenter.skipForwardCommand.addTarget	{	[weak	self]	_	in

				guard	let	weakSelf	=	self	else	{	return	.commandFailed	}

				weakSelf.tapForward(())

				return	.success

}

commandCenter.skipBackwardCommand.preferredIntervals	=	[NSNumber(value:	skipInterval)]

commandCenter.skipBackwardCommand.addTarget	{	[weak	self]	_	in

				guard	let	weakSelf	=	self	else	{	return	.commandFailed	}

				weakSelf.tapRewind(())

				return	.success

}
2.	10秒送り&戻しコントロール
🌟	動画プレイヤー画面の10秒送り&戻し処理を実行
MPRemoteCommandCenterを利用した処理概要(4)
「Configure	the	Remote	Command	Handlers」の内容を踏まえたコード例
commandCenter.changePlaybackPositionCommand.addTarget	{	[weak	self]	event	in

				guard	let	weakSelf	=	self	else	{	return	.commandFailed	}

				if	let	event	=	event	as?	MPChangePlaybackPositionCommandEvent	{

								weakSelf.presenter.didChangeNowPlayingSeekbar(value:	Float(event.positionTime))

								return	.success

				}	else	{

								return	.commandFailed

				}

}
let	commandCenter	=	MPRemoteCommandCenter.shared()
3.	SeekBarでの再生位置コントロール
🌟	動画プレイヤー画面の再生位置の決定処理を実行
MPNowPlayingInfoCenterを利用した処理概要(1)
「Provide	Display	Metadata」を参考にする
※	Article「Controlling	Background	Audio」内のSection
画像キャッシュ対応部分
ポイントとなる実装方針
🌟	タイトル文言や画像を表示する:
🌟	再生時間や再生レートを反映する:
APIレスポンス等で取得した動画のタイトルやサムネイル画
像を利用する。
※画像キャッシュ処理も必要に応じ利用
AVPlayerのcurrentItemプロパティから取得した情報を利用
する。
※動画プレイヤー画面側と状態を合わせる
MPNowPlayingInfoCenterを利用した処理概要(2)
「Provide	Display	Metadata」の内容を踏まえたコード例
1.	APIレスポンスとして取得した情報をControlCenterへ反映する
guard	let	currentItem	=	player.currentItem,	

				let	courseDetailDto	=	videoPlayerDto.courseDetailDto	else	{

				return

}

var	nowPlayingInfo:	[String:	Any]	=	[:]

nowPlayingInfo[MPMediaItemPropertyAlbumTitle]	=	courseDetailDto.name

nowPlayingInfo[MPMediaItemPropertyTitle]	=	courseDetailDto.sectionName

if	let	imageURL	=	courseDetailDto.photoThumbUrl	{

				let	imageCache	=	UIImageView.af_sharedImageDownloader.imageCache

				if	let	image	=	imageCache?.image(for:	URLRequest(url:	imageURL),	withIdentifier:	nil)	{

								nowPlayingInfo[MPMediaItemPropertyArtwork]

												=	MPMediaItemArtwork(boundsSize:	image.size)	{	_	in

												image

								}

				}

}
MPMediaItemPropertyArtwork
MPMediaItemPropertyTitle
①	動画タイトル表示
②	動画セクションタイトル表示
③	サムネイル画像表示
MPMediaItemPropertyAlbumTitle
※サムネイル画像取得時にはキャッシュを利用
MPNowPlayingInfoCenterを利用した処理概要(3)
「Provide	Display	Metadata」の内容を踏まえたコード例
2.	AVPlayerのcurrentItemプロパティから取得した情報を反映する
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration]

				=	currentItem.asset.duration.seconds

nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime]

				=	currentItem.currentTime().seconds

nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate]	

				=	Double(player.rate)
guard	let	currentItem	=	player.currentItem,	

				let	courseDetailDto	=	videoPlayerDto.courseDetailDto	else	{

				return

}

var	nowPlayingInfo:	[String:	Any]	=	[:]
🌟	合計再生時間
🌟	現在再生時間
🌟	選択中再生レート
MPNowPlayingInfoCenterを利用した処理概要(4)
「Provide	Display	Metadata」の内容を踏まえたコード例
3.	動画プレイヤー画面の処理との整合を取るための対応
func	updateNowPlaying(currentTime:	Double,	playbackRate:	Double)	{

				guard	var	info	=	MPNowPlayingInfoCenter.default().nowPlayingInfo	else	{

								return

				}

				info[MPNowPlayingInfoPropertyPlaybackRate]	=	playbackRate

				info[MPNowPlayingInfoPropertyElapsedPlaybackTime]	=	currentTime

				MPNowPlayingInfoCenter.default().nowPlayingInfo	=	info

}
func	resetNowPlaying()	{

				MPNowPlayingInfoCenter.default().nowPlayingInfo	=	nil

}
🌟	動画再生位置が更新されたとき
Foreground	/	Background切替時に途切れないで続く体験
🌟	動画再生が完了したとき
アプリ内部実装の工夫で実現する機能事例(1)
連続で動画再生する場合や動画視聴の間に別の画面をはさむ必要がある場合
動画詳細画面
動画プレイヤー画面
コントロール用画面
1.	動画視聴を開始

2.	動画を最後まで視聴

3.	次の動画視聴が開始
①	Delegateでの橋渡しと画面遷移コントロール
動画の間にQuizがある
※上2つは同時に表示
実現したい流れ
Section2(動画) 動画ではなくQuizを表示
②	Backendでも現在と次の情報を一緒に取得
動画プレイヤー画面表示時
その下にコントロール画面を仕込んでおく
アプリ内部実装の工夫で実現する機能事例(2)
最後に視聴したタイミングでの再生位置を保存しておきその位置から再生する
😱
💦	途中まで見ていた動画を閉じた

(誤作動や一時的な中断など)
💦	何らかの理由でアプリをKillされてしまった
1.	画面Dismiss時

2.	applicationWillTerminate
途中まで見た

状態で閉じた
閉じた時点から開始
再度動画を開いて手動で閉じた時の位置まで戻すのは面倒
UserDefaultやAPI処理等を利用して閉じた時の再生位置を保存する
動画プレイヤーアプリを利用時にこの様な経験をした事はありませんか?
この時に再生位置を記憶して再び動画視聴をする際に反映
②	表示中の動画をAVPictureInPictureControllerへセット

			(表示中のAVPlayerLayer)
Picture-in-Pictureを利用する際の基本事項(1)
画面隅に小さなウインドウを表示して動画再生をしながら別操作をする機能
例.	Udemyを視聴しながらWebで調べる
③	AVPictureInPictureControllerDelegateとの連動処理

			(開始・終了時等のタイミングに合わせた処理)
①	Picture-in-Picture開始ボタン有効化
Picture-in-Pictureのセットアップ時に必要な処理の流れ
視聴しながら別動作をする
Picture-in-Picture気をつけると良さそうな部分
②	PIP状態での連続再生と画面構造に関する部分
③	バックグラウンド再生処理との共存方法
①	ウィンドウのカスタマイズに関する部分
Picture-in-Pictureを利用する際の基本事項(2)
現在動画プレイヤーで再生中の動画をPicture-in-Pictureで表示する
//	MEMO:	PIPをサポートしているか否かの判定を実施する(※サポートをしていない場合はPIP実行ボタンを動作不可にする)

if	AVPictureInPictureController.isPictureInPictureSupported()	{

					pictureInPictureButton.isEnabled	=	true

			 //	MEMO:	AVPictureInPictureControllerをセットする(※画面上にあるplayerLayerを適用する)

					pictureInPictureController	=	AVPictureInPictureController(playerLayer:	playerView.playerLayer)

					pictureInPictureController?.delegate	=	self

					//	MEMO:	observeでPIPの実行可否の変化をキャッチする(※必要があればここで何らかの処理を加える)

				_	=	pictureInPictureController?.observe(AVPictureInPictureController.isPictureInPicturePossible,	options:	[.initial,	.new])	{	_,	change	in

								//	TODO:	ステータスが変化時に実行したい処理

				}

}	else	{

				pictureInPictureButton.isEnabled	=	false

}
refs:	https://developer.apple.com/documentation/avkit/avpictureinpicturecontroller
基本的な処理をするためのコード例
extension	VideoLessonStepViewController:	AVPictureInPictureControllerDelegate	{

				func	pictureInPictureControllerWillStartPictureInPicture(_	pictureInPictureController:	AVPictureInPictureController)	{}

				func	pictureInPictureControllerDidStartPictureInPicture(_	pictureInPictureController:	AVPictureInPictureController)	{}

				func	pictureInPictureController(_	pictureInPictureController:	AVPictureInPictureController,	failedToStartPictureInPictureWithError	
error:	Error)	{}

				func	pictureInPictureControllerDidStopPictureInPicture(_	pictureInPictureController:	AVPictureInPictureController)	{}

				func	pictureInPictureController(_	pictureInPictureController:	AVPictureInPictureController,	
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler	completionHandler:	@escaping	(Bool)	->	Void)	{}

}
Picture-in-Pictureを利用する際の基本事項(3)
Picture-in-Pictureのウィンドウに関するイベントを取得する
AVPictureInPictureDelegateを利用して開始や終了等のタイミングを取得可能
🍎	アップデートがあったり、私自身まだ知らない事が多いので引き続き調査したい部分
3.	その他動画プレイヤーに付随する機能や動画再生機能に関連するUI実装や表現における考察
動画プレイヤーアプリや機能に関連するUI事例と特徴
実装・表現から紐解く動画プレイヤーや動画利用をするアプリでの特徴的な部分
縦型を生かした画面構造やデザインとUI操作 特徴的な画面遷移やアニメーション
Vertical

Scroll
Action

Buttons
Swipe	Action
z
Continual

Transition
大きな画面であっても片手で操作ができる形の要素配置。
動画視聴と共に関連操作をする。※コメント・シェア等
Semi	Modal

Screen
画面遷移時に連続性がある様な表現で画面が切替る。
PanやSwipe等の指でのジェスチャー操作とも連動する。
画面構造から読み取れる特徴やその意図、そして表示タイミングでの切替処理時の表現事例と考察:
動画プレイヤーアプリと関係しそうな関連実装事例(1)
1.	動画を見ながら操作するセミモーダル画面を実現するための実装ヒント紹介
セミモーダル画面の開閉状態のコントロールや遷移元画面に対する処理の影響を考える:
難しさを感じるポイント
方針・アイデア例
動作状態管理と遷移元との関係性。
GestureRecognizerとの連動部分。
Tough 余談・補足
iOS15からはセミモーダルビューが純正で搭載(細部のカスタマイズ余地次第)。
https://developer.apple.com/videos/play/wwdc2021/10063/
これらを表示側の画面状態をコントロールをする
が指の動きの連動や他画面との連携も別途必要。
①	UIPanGestureRecognizer
②	UIViewPropertyAnimatior
PanModal:	https://github.com/slackhq/PanModal
FloatingPanel:	https://github.com/scenee/FloatingPanel
表示対象の画面だけでなく関連画面も意識したView構造にしなければならない
動画プレイヤーアプリと関係しそうな関連実装事例(2)
セミモーダルで表示する画面と遷移元画面の実装事例
例.	ライブラリ「PanModal」を利用してセミモーダル表示作成と画面状態の設定をする:
Semi	Modal

Screen
PanModalPresentableで提供されているプロパティ等
で細かな調整が可能
//	MEMO:	ライブラリ「PanModal」を利用したセミモーダルを表示する

presentPanModal(movieCommentViewController)
//	MARK:	-	PanModalPresentable

extension	MovieCommentViewController:	PanModalPresentable	{

				//	MEMO:	スクロールと連動させたいUIScrollViewを継承した要素

				var	panScrollable:	UIScrollView?	{

								return	collectionView

				}

				//	MEMO:	ドラッグして画面を閉じることを許可するかの判定

				var	allowsDragToDismiss:	Bool	{

								return	true

				}

				//	セミモーダルビューにて大きく表示させる場合の高さ設定

				var	longFormHeight:	PanModalHeight	{

								return	.contentHeight(288.0)

				}

				//	セミモーダルビューにて小さく表示させる場合の高さ設定

				var	shortFormHeight:	PanModalHeight	{

								return	.contentHeight(64.0)

				}

}
画面遷移元から任意の画面をセミモーダルで表示する
セミモーダル表示先画面
画面遷移元
その他にもPanGestureRecognizer発火タイミングやセ
ミモーダルをDismissしたタイミングで任意の処理を
実行させる様にすることも可能
基本的な設定から応用的な事までが用途に合わせて可能
動画プレイヤーアプリと関係しそうな関連実装事例(3)
2.	連続性がある感じを演出するTransitionを実現するための実装ヒント紹介
一覧配置画面から動画再生までの演出だけでなくジェスチャーでの画面操作も付与する:
z
方針・アイデア例
難しさを感じるポイント
実装や動作イメージが掴みにくい。
配置調整のコードが複雑になりがち。
Tough 余談・補足
他にUICollectionViewのレイアウト処理とContainerViewを併用する場合もあります。
https://github.com/KelvinJin/AnimatedCollectionViewLayout
Hero:	https://github.com/HeroTransitions/Hero

※あらかじめ見た目にも美しいアニメーションが準備されている。
画面遷移元・遷移先の情報も利用しながらアニメーション処理の振る舞いを決める
①	UIViewControllerAnimatedTransitioning
②	UIViewControllerTransitioningDelegate
③	UIViewControllerInteractiveTransitioning
これらのプロトコルに準拠した
各クラスを画面遷移処理時の然
るべきタイミングで適用。
動画プレイヤーアプリと関係しそうな関連実装事例(4)
カスタムトランジションを活用した画面遷移時のアニメーション実装事例
例.	Present/Dismissの画面遷移に対してカスタムトランジションを適用する:
z
Continual

Transition
final	class	ExampleScreenTransitioningDelegate:	NSObject	{

				//	MEMO:	カスタムトランジションを実行するためのクラス

				private	let	exampleScreenTransition	=	ExampleScreenTransition()

				//	アニメーション対象なるViewControllerの位置やサイズ情報を格納するメンバ変数

				var	screenFrame:	CGRect	=	CGRect.zero

}
extension	ExampleScreenTransitioningDelegate:	UIViewControllerTransitioningDelegate	{

				//	進む場合のアニメーションの設定を行う

				func	animationController(forPresented	presented:	UIViewController,	presenting:	UIViewController,	source:	UIViewController)	->	UIViewControllerAnimatedTransitioning?	{

								//	現在の画面サイズを引き渡して画面が縮むトランジションにする

								exampleScreenTransition.originalFrame	=	screenFrame

								exampleScreenTransition.presenting	=	true

								return	exampleScreenTransition

				}

				//	戻る場合のアニメーションの設定を行う

				func	animationController(forDismissed	dismissed:	UIViewController)	->	UIViewControllerAnimatedTransitioning?	{

								//	縮んだ状態から画面が戻るトランジションにする

								exampleScreenTransition.presenting	=	false

								return	exampleScreenTransition

				}

}
※Push/Popの画面遷移の場合はカスタムトランジション
のクラスをUINavigationControllerと組み合わせる。
//	コンテキストを元にViewのインスタンスを取得する

transitionContext.view(forKey:	UITransitionContextViewKey.from)

transitionContext.view(forKey:	UITransitionContextViewKey.to)
//	アニメーションの実体となるContainerViewを作成する

transitionContext.containerView
Animationを作るのに必要な情報を取得
ExampleScreenTransition()
カスタムトランジションを定義するクラスで遷移先または
遷移元の要素に関する情報を引き渡せる形にしておく。
昨今の動画を活用したアプリのUI/UX考察の参考資料
デバイス事情やデザイントレンド等も踏まえて考えるとその利点が見えてくる
動画関連のUI実装やUX設計を考えていく際に役立つ参考資料例
🌟	VODにおけるUIデザインとトレンド(前編・後編)
https://developers.cyberagent.co.jp/blog/archives/25334/

https://developers.cyberagent.co.jp/blog/archives/26759/
🌟	スマートフォンのディスプレイ巨大化に伴う、UIデザインの潮流
https://note.com/goando/n/n9346aea1b0ea
🌟	TikTokやSnapchatが覆す、アプリデザインの「常識」
https://wired.jp/2019/04/03/tiktok-snapchat-app-design/
🌟	動画視聴を伴うUX・UI開発プロセスとは 〜スポーツ動画視聴スマホアプリの事例〜
https://wired.jp/2019/04/03/tiktok-snapchat-app-design/
従来のベストプラクティスからは少し異なる形かもしれないが、使い勝手としては理にかなっている場合もある。
refs:	https://blog.mil.movie/marketing/4739.html
📱	若年層は縦での動画視聴が多い傾向
📱	できるだけ片手の状態で操作をする
📱	移動中または別動作を実行している
縦向きの状態を生かしたUIの決め手となるポイント例
Deviceの大型化 片手での利用
見ながら操作 つなぎ目の遷移
👀
余談.	Androidアプリでの動画プレイヤー実装との比較
Androidで動画プレイヤーを実装する際のヒント紹介(1)
動画プレイヤー画面構築に関連する部分はAndroidアプリではどうするか?
https://medium.com/@suchandrimsarkar/youtube-like-
drag-minimizable-view-using-motionlayout-ca2fb40d8483
動画再生部分と付随するUI実装が分離可能
動画視聴及び基本操作機能に関する部分の事例: 動画プレイヤー部分に関連するUI表現の事例:
https://github.com/google/ExoPlayer
ライブラリExoPlayerを活用する MotionLayoutを利用したカスタム遷移
画面遷移AnimationではMotionLayoutを活用
https://exoplayer.dev/hello-world.html
Motion

Layout
MotionLayoutの構築はAndroidStudioの
機能を利用することが可能。
画面下部に配置した小さなサイズの
Player部分はBottomSheetを利用。
動画表示部分が拡大されて浮き上がる様
な画面遷移はMotionLayoutで実現。
ExoPlayerのメリットとしてUI実装にお
けるカスタマイズの柔軟性がある。
Player画面自体はライブラリExoPlayer
で提供されるものを利用。
動画コントロール用ボタン群は別途用意
したView要素(Fragment)を利用。
Androidで動画プレイヤーを実装する際のヒント紹介(2)
バックグラウンド再生機能やPicture-In-Pictureは実現可能か?
https://tech.uzabase.com/entry/2021/03/12/132737

https://developer.android.com/guide/topics/ui/
picture-in-picture
Android8.0移行でPicture-In-Pictureをサポート
Picture-In-Pictureの実現:
バックグラウンド再生機能の実現:
https://qiita.com/siy1121/items/f01167186a6677c22435

https://proandroiddev.com/deep-dive-into-android-
services-4830b8c9a09
プレイヤー画面とServiceクラスが連動した処理
Enter

Background
enterPictureInPictureModeの利用
AndroidのServiceクラスを利用する
動画再生用のActivityクラスで音声再生
用Serviceを立ち上げて双方向からの操
作を反映可能な形としておく。
AndroidのServiceクラスを継承して音声
再生実行用処理を作成。
android:supportsPictureInPicture="tr
ue"をAndroidManifestに設定。
対象ActivityにてPicture-In-Picture
モードへ切り替える処理を記載。
※	再生・停止等のプレイヤー操作
※	表示上のレイアウト制限には注意
※	バックグラウンド再生機能との共存
本発表のまとめ
動画プレイヤーアプリの開発を通じて自分が学べた要点
1.	基本的な動画の取り扱い方法と回転を考慮したレイアウト構築方法に注目すると理解がしやすい:
基本事項(再生・停止・N秒送り&戻し・位置調節・Headset監視・音量調節等)はそれぞれの実装自体はシンプルではあるものの、内
部処理との連動や回転処理を考慮したレイアウト実装等も一緒に考える必要があるのでその点に気をつける様にすると良さそうです。
2.	動画プレイヤー特有機能や内部実装の工夫で実現する機能は「使われる場面」を捉える様にする:
バックグラウンド再生やPicture-In-Pictureの様に「何かをしながら」がポイントになる機能や、画面&アプリのライフサイクルと連
動する処理を取り入れる事で実現可能な余地がある。公式Documentや実装例がヒントにして利用ケースを想定すると良さそうです。
小さな部分を実装するコードはシンプルだが、一連の動画プレイヤー機能として考えると奥深さや難しさがある
3.	動画プレイヤーに関するUIやその意図、付随する特徴にも目を向けてみる:
他動画プレイヤーアプリ・動画を活用するアプリでよく利用されているUI実装や表現等を見ると使い勝手に関する工夫を垣間見る事が
できるので、使い勝手の良いレイアウトやアニメーション・インタラクション表現に関しても工夫の余地がありそうです。
配信対象の動画コンテンツの長さ・属性等に応じて適する
画面全体の見せ方やUI構造は異なってくるもの。
アイデアと利用されうる画面を想定した機能開発
普段の生活や身近な体験の中にも改善のアイデアやヒントがある
動画プレイヤーはどの様な使い方をされるか: 目に見えないもの&見えるものからの発見:
AudioPlayerに近いUI	or	縦型の特徴や利点を活かしたUI
動画プレイヤーをより便利に使ってもらうための配慮
目に見えるもの 目に見えないもの
📝	フィードバック
🍎	Appleのレビュー
😷	ユーザーインタビュー
📱	普段からの使い方
🏃	ユーザーの習慣や癖
✨	世間の流行やトレンド
例.	バックグラウンド再生機能
これに加えてアプリの使い勝手やデザインという観点も
考慮に入れた機能にする必要がある。
例.	再生画面から別画面移動時の再生位置保存
例.	Picture-in-Picture	/	Semi	Modal	/	Portrait	Screen
実装前の背景や課題・ニーズを探る
機能アイデア事例と考えられるユースケース
〜しながら音だけ聞く(移動中の音楽やラジオ感覚)
Appのライフサイクル連動	/	カットインされた時
動画視聴と同時の別操作	/	片手での操作を意識
ユーザーは常に動画プレイヤーを見続けている状態だとは
限らないので視聴状態を維持できる様な機能を追加する。
謝辞と実務を通じての感想
利用されうる場面を押さえた機能やUIを考え抜く為のコラボレーションが大切
この場をお借りして、同僚の皆様及び関係者に感謝の意を述べたいと思います。本当にありがとうございました!
Thank	you	for	listening	!

More Related Content

What's hot

フロー効率性とリソース効率性について #xpjug
フロー効率性とリソース効率性について #xpjugフロー効率性とリソース効率性について #xpjug
フロー効率性とリソース効率性について #xpjugItsuki Kuroda
 
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptx
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptxネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptx
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptxShota Shinogi
 
SolrとElasticsearchを比べてみよう
SolrとElasticsearchを比べてみようSolrとElasticsearchを比べてみよう
SolrとElasticsearchを比べてみようShinsuke Sugaya
 
ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割
ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割
ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割Recruit Lifestyle Co., Ltd.
 
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話Koichiro Matsuoka
 
Spring Day 2016 - Web API アクセス制御の最適解
Spring Day 2016 - Web API アクセス制御の最適解Spring Day 2016 - Web API アクセス制御の最適解
Spring Day 2016 - Web API アクセス制御の最適解都元ダイスケ Miyamoto
 
社内ドキュメント検索システム構築のノウハウ
社内ドキュメント検索システム構築のノウハウ社内ドキュメント検索システム構築のノウハウ
社内ドキュメント検索システム構築のノウハウShinsuke Sugaya
 
分散トレーシングAWS:X-Rayとの上手い付き合い方
分散トレーシングAWS:X-Rayとの上手い付き合い方分散トレーシングAWS:X-Rayとの上手い付き合い方
分散トレーシングAWS:X-Rayとの上手い付き合い方Recruit Lifestyle Co., Ltd.
 
Power BI をアプリに埋め込みたい? ならば Power BI Embedded だ!
Power BI をアプリに埋め込みたい? ならば Power BI Embedded だ!Power BI をアプリに埋め込みたい? ならば Power BI Embedded だ!
Power BI をアプリに埋め込みたい? ならば Power BI Embedded だ!Teruchika Yamada
 
Apache Spark on Kubernetes入門(Open Source Conference 2021 Online Hiroshima 発表資料)
Apache Spark on Kubernetes入門(Open Source Conference 2021 Online Hiroshima 発表資料)Apache Spark on Kubernetes入門(Open Source Conference 2021 Online Hiroshima 発表資料)
Apache Spark on Kubernetes入門(Open Source Conference 2021 Online Hiroshima 発表資料)NTT DATA Technology & Innovation
 
Node-REDをIoTビジネスに適用するために苦労した3つの話
Node-REDをIoTビジネスに適用するために苦労した3つの話Node-REDをIoTビジネスに適用するために苦労した3つの話
Node-REDをIoTビジネスに適用するために苦労した3つの話Tomohiro Nakajima
 
iOSDC 2018 動画をなめらかに動かす技術
iOSDC 2018 動画をなめらかに動かす技術iOSDC 2018 動画をなめらかに動かす技術
iOSDC 2018 動画をなめらかに動かす技術Yuji Hato
 
「関心の分離」と「疎結合」 ソフトウェアアーキテクチャのひとかけら
「関心の分離」と「疎結合」   ソフトウェアアーキテクチャのひとかけら「関心の分離」と「疎結合」   ソフトウェアアーキテクチャのひとかけら
「関心の分離」と「疎結合」 ソフトウェアアーキテクチャのひとかけらAtsushi Nakamura
 
知っておきたいFirebase の色んな上限について
知っておきたいFirebase の色んな上限について知っておきたいFirebase の色んな上限について
知っておきたいFirebase の色んな上限について健一 辰濱
 
情報共有は、なぜGoogle Docsじゃなく、 Confluenceなのか。
情報共有は、なぜGoogle Docsじゃなく、 Confluenceなのか。情報共有は、なぜGoogle Docsじゃなく、 Confluenceなのか。
情報共有は、なぜGoogle Docsじゃなく、 Confluenceなのか。Narichika Kajihara
 
さくっと理解するSpring bootの仕組み
さくっと理解するSpring bootの仕組みさくっと理解するSpring bootの仕組み
さくっと理解するSpring bootの仕組みTakeshi Ogawa
 
マイクロサービス時代の認証と認可 - AWS Dev Day Tokyo 2018 #AWSDevDay
マイクロサービス時代の認証と認可 - AWS Dev Day Tokyo 2018 #AWSDevDayマイクロサービス時代の認証と認可 - AWS Dev Day Tokyo 2018 #AWSDevDay
マイクロサービス時代の認証と認可 - AWS Dev Day Tokyo 2018 #AWSDevDay都元ダイスケ Miyamoto
 
[DI05] Azure Event Hubs と Azure Stream Analytics で、”今を処理”する
[DI05] Azure Event Hubs と Azure Stream Analytics で、”今を処理”する[DI05] Azure Event Hubs と Azure Stream Analytics で、”今を処理”する
[DI05] Azure Event Hubs と Azure Stream Analytics で、”今を処理”するde:code 2017
 
テストコードの DRY と DAMP
テストコードの DRY と DAMPテストコードの DRY と DAMP
テストコードの DRY と DAMPYusuke Kagata
 

What's hot (20)

フロー効率性とリソース効率性について #xpjug
フロー効率性とリソース効率性について #xpjugフロー効率性とリソース効率性について #xpjug
フロー効率性とリソース効率性について #xpjug
 
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptx
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptxネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptx
ネットストーカー御用達OSINTツールBlackBirdを触ってみた.pptx
 
SolrとElasticsearchを比べてみよう
SolrとElasticsearchを比べてみようSolrとElasticsearchを比べてみよう
SolrとElasticsearchを比べてみよう
 
ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割
ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割
ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割
 
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話DDD x CQRS   更新系と参照系で異なるORMを併用して上手くいった話
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
 
Spring Day 2016 - Web API アクセス制御の最適解
Spring Day 2016 - Web API アクセス制御の最適解Spring Day 2016 - Web API アクセス制御の最適解
Spring Day 2016 - Web API アクセス制御の最適解
 
実践 NestJS
実践 NestJS実践 NestJS
実践 NestJS
 
社内ドキュメント検索システム構築のノウハウ
社内ドキュメント検索システム構築のノウハウ社内ドキュメント検索システム構築のノウハウ
社内ドキュメント検索システム構築のノウハウ
 
分散トレーシングAWS:X-Rayとの上手い付き合い方
分散トレーシングAWS:X-Rayとの上手い付き合い方分散トレーシングAWS:X-Rayとの上手い付き合い方
分散トレーシングAWS:X-Rayとの上手い付き合い方
 
Power BI をアプリに埋め込みたい? ならば Power BI Embedded だ!
Power BI をアプリに埋め込みたい? ならば Power BI Embedded だ!Power BI をアプリに埋め込みたい? ならば Power BI Embedded だ!
Power BI をアプリに埋め込みたい? ならば Power BI Embedded だ!
 
Apache Spark on Kubernetes入門(Open Source Conference 2021 Online Hiroshima 発表資料)
Apache Spark on Kubernetes入門(Open Source Conference 2021 Online Hiroshima 発表資料)Apache Spark on Kubernetes入門(Open Source Conference 2021 Online Hiroshima 発表資料)
Apache Spark on Kubernetes入門(Open Source Conference 2021 Online Hiroshima 発表資料)
 
Node-REDをIoTビジネスに適用するために苦労した3つの話
Node-REDをIoTビジネスに適用するために苦労した3つの話Node-REDをIoTビジネスに適用するために苦労した3つの話
Node-REDをIoTビジネスに適用するために苦労した3つの話
 
iOSDC 2018 動画をなめらかに動かす技術
iOSDC 2018 動画をなめらかに動かす技術iOSDC 2018 動画をなめらかに動かす技術
iOSDC 2018 動画をなめらかに動かす技術
 
「関心の分離」と「疎結合」 ソフトウェアアーキテクチャのひとかけら
「関心の分離」と「疎結合」   ソフトウェアアーキテクチャのひとかけら「関心の分離」と「疎結合」   ソフトウェアアーキテクチャのひとかけら
「関心の分離」と「疎結合」 ソフトウェアアーキテクチャのひとかけら
 
知っておきたいFirebase の色んな上限について
知っておきたいFirebase の色んな上限について知っておきたいFirebase の色んな上限について
知っておきたいFirebase の色んな上限について
 
情報共有は、なぜGoogle Docsじゃなく、 Confluenceなのか。
情報共有は、なぜGoogle Docsじゃなく、 Confluenceなのか。情報共有は、なぜGoogle Docsじゃなく、 Confluenceなのか。
情報共有は、なぜGoogle Docsじゃなく、 Confluenceなのか。
 
さくっと理解するSpring bootの仕組み
さくっと理解するSpring bootの仕組みさくっと理解するSpring bootの仕組み
さくっと理解するSpring bootの仕組み
 
マイクロサービス時代の認証と認可 - AWS Dev Day Tokyo 2018 #AWSDevDay
マイクロサービス時代の認証と認可 - AWS Dev Day Tokyo 2018 #AWSDevDayマイクロサービス時代の認証と認可 - AWS Dev Day Tokyo 2018 #AWSDevDay
マイクロサービス時代の認証と認可 - AWS Dev Day Tokyo 2018 #AWSDevDay
 
[DI05] Azure Event Hubs と Azure Stream Analytics で、”今を処理”する
[DI05] Azure Event Hubs と Azure Stream Analytics で、”今を処理”する[DI05] Azure Event Hubs と Azure Stream Analytics で、”今を処理”する
[DI05] Azure Event Hubs と Azure Stream Analytics で、”今を処理”する
 
テストコードの DRY と DAMP
テストコードの DRY と DAMPテストコードの DRY と DAMP
テストコードの DRY と DAMP
 

Similar to 動画プレイヤーアプリの開発を通じて学んだ機能を実現するための要点解説

iOSアプリ開発で意識すると役立ちそうな「つなぎ目」の部分について
iOSアプリ開発で意識すると役立ちそうな「つなぎ目」の部分についてiOSアプリ開発で意識すると役立ちそうな「つなぎ目」の部分について
iOSアプリ開発で意識すると役立ちそうな「つなぎ目」の部分についてFumiya Sakai
 
2022年の抱負とここ数年続けてきたインプット
2022年の抱負とここ数年続けてきたインプット2022年の抱負とここ数年続けてきたインプット
2022年の抱負とここ数年続けてきたインプットFumiya Sakai
 
少しずつキャッチアップしていくAndroidアプリ開発
少しずつキャッチアップしていくAndroidアプリ開発少しずつキャッチアップしていくAndroidアプリ開発
少しずつキャッチアップしていくAndroidアプリ開発Fumiya Sakai
 
少しずつキャッチアップしていくAndroidアプリ開発の補足と振り返り
少しずつキャッチアップしていくAndroidアプリ開発の補足と振り返り少しずつキャッチアップしていくAndroidアプリ開発の補足と振り返り
少しずつキャッチアップしていくAndroidアプリ開発の補足と振り返りFumiya Sakai
 
最近の業務やAndroid関連のインプットと振り返り
最近の業務やAndroid関連のインプットと振り返り最近の業務やAndroid関連のインプットと振り返り
最近の業務やAndroid関連のインプットと振り返りFumiya Sakai
 
レイヤー分けをしたアーキテクチャで作るiOSアプリ&バックエンドのサンプル実装をのぞく
レイヤー分けをしたアーキテクチャで作るiOSアプリ&バックエンドのサンプル実装をのぞくレイヤー分けをしたアーキテクチャで作るiOSアプリ&バックエンドのサンプル実装をのぞく
レイヤー分けをしたアーキテクチャで作るiOSアプリ&バックエンドのサンプル実装をのぞくFumiya Sakai
 
iOS側のUIの特徴と見比べるAndroid側でのUI実装のヒント
iOS側のUIの特徴と見比べるAndroid側でのUI実装のヒントiOS側のUIの特徴と見比べるAndroid側でのUI実装のヒント
iOS側のUIの特徴と見比べるAndroid側でのUI実装のヒントFumiya Sakai
 
何故に私達(特に私)はアプリのアニメーションや UI表現に魅了されるのか? そして共存と向き合いを考える
何故に私達(特に私)はアプリのアニメーションや UI表現に魅了されるのか? そして共存と向き合いを考える何故に私達(特に私)はアプリのアニメーションや UI表現に魅了されるのか? そして共存と向き合いを考える
何故に私達(特に私)はアプリのアニメーションや UI表現に魅了されるのか? そして共存と向き合いを考えるFumiya Sakai
 
Approach of Prototyping for making Application User Interface about iOS
Approach of Prototyping for making Application User Interface about iOSApproach of Prototyping for making Application User Interface about iOS
Approach of Prototyping for making Application User Interface about iOSFumiya Sakai
 
デザイナー→Webエンジニア→iOSエンジニアと渡り歩いた僕なりのSwiftとの向き合い方と生かす戦略
デザイナー→Webエンジニア→iOSエンジニアと渡り歩いた僕なりのSwiftとの向き合い方と生かす戦略デザイナー→Webエンジニア→iOSエンジニアと渡り歩いた僕なりのSwiftとの向き合い方と生かす戦略
デザイナー→Webエンジニア→iOSエンジニアと渡り歩いた僕なりのSwiftとの向き合い方と生かす戦略Fumiya Sakai
 
アプリ開発におけるテキスト装飾のアイデア集
アプリ開発におけるテキスト装飾のアイデア集アプリ開発におけるテキスト装飾のアイデア集
アプリ開発におけるテキスト装飾のアイデア集Fumiya Sakai
 
iOSアプリで気になった動きや表現を上手にアレンジして活用してみる
iOSアプリで気になった動きや表現を上手にアレンジして活用してみるiOSアプリで気になった動きや表現を上手にアレンジして活用してみる
iOSアプリで気になった動きや表現を上手にアレンジして活用してみるFumiya Sakai
 
iOSアプリUIとの触れ合いと歩む僕なりのSwiftの楽しみ方
iOSアプリUIとの触れ合いと歩む僕なりのSwiftの楽しみ方iOSアプリUIとの触れ合いと歩む僕なりのSwiftの楽しみ方
iOSアプリUIとの触れ合いと歩む僕なりのSwiftの楽しみ方Fumiya Sakai
 
UI表現ライブラリを有効活用して iOSアプリのUIをオシャレにするワザ紹介
UI表現ライブラリを有効活用して iOSアプリのUIをオシャレにするワザ紹介UI表現ライブラリを有効活用して iOSアプリのUIをオシャレにするワザ紹介
UI表現ライブラリを有効活用して iOSアプリのUIをオシャレにするワザ紹介Fumiya Sakai
 
メディアアプリでよく見る無限スクロールするタブの動きへの考察
メディアアプリでよく見る無限スクロールするタブの動きへの考察メディアアプリでよく見る無限スクロールするタブの動きへの考察
メディアアプリでよく見る無限スクロールするタブの動きへの考察Fumiya Sakai
 
ライブラリやView構造を有効活用して iOSアプリのUIをオシャレにするワザ紹介
ライブラリやView構造を有効活用して iOSアプリのUIをオシャレにするワザ紹介ライブラリやView構造を有効活用して iOSアプリのUIをオシャレにするワザ紹介
ライブラリやView構造を有効活用して iOSアプリのUIをオシャレにするワザ紹介Fumiya Sakai
 
Hint of“Passcode Lock”Screen and Logic (with LocalAuthentication).
Hint of“Passcode Lock”Screen and Logic (with LocalAuthentication).Hint of“Passcode Lock”Screen and Logic (with LocalAuthentication).
Hint of“Passcode Lock”Screen and Logic (with LocalAuthentication).Fumiya Sakai
 
部品に切り分けて考えるView構造とライブラリを上手に活用したUI実装
部品に切り分けて考えるView構造とライブラリを上手に活用したUI実装部品に切り分けて考えるView構造とライブラリを上手に活用したUI実装
部品に切り分けて考えるView構造とライブラリを上手に活用したUI実装Fumiya Sakai
 
RxSwiftとMVVMパターンと仲良くなる次のステップ
RxSwiftとMVVMパターンと仲良くなる次のステップRxSwiftとMVVMパターンと仲良くなる次のステップ
RxSwiftとMVVMパターンと仲良くなる次のステップFumiya Sakai
 
まずはできるところから始める UnitTestとテストができる実装について
まずはできるところから始める UnitTestとテストができる実装についてまずはできるところから始める UnitTestとテストができる実装について
まずはできるところから始める UnitTestとテストができる実装についてFumiya Sakai
 

Similar to 動画プレイヤーアプリの開発を通じて学んだ機能を実現するための要点解説 (20)

iOSアプリ開発で意識すると役立ちそうな「つなぎ目」の部分について
iOSアプリ開発で意識すると役立ちそうな「つなぎ目」の部分についてiOSアプリ開発で意識すると役立ちそうな「つなぎ目」の部分について
iOSアプリ開発で意識すると役立ちそうな「つなぎ目」の部分について
 
2022年の抱負とここ数年続けてきたインプット
2022年の抱負とここ数年続けてきたインプット2022年の抱負とここ数年続けてきたインプット
2022年の抱負とここ数年続けてきたインプット
 
少しずつキャッチアップしていくAndroidアプリ開発
少しずつキャッチアップしていくAndroidアプリ開発少しずつキャッチアップしていくAndroidアプリ開発
少しずつキャッチアップしていくAndroidアプリ開発
 
少しずつキャッチアップしていくAndroidアプリ開発の補足と振り返り
少しずつキャッチアップしていくAndroidアプリ開発の補足と振り返り少しずつキャッチアップしていくAndroidアプリ開発の補足と振り返り
少しずつキャッチアップしていくAndroidアプリ開発の補足と振り返り
 
最近の業務やAndroid関連のインプットと振り返り
最近の業務やAndroid関連のインプットと振り返り最近の業務やAndroid関連のインプットと振り返り
最近の業務やAndroid関連のインプットと振り返り
 
レイヤー分けをしたアーキテクチャで作るiOSアプリ&バックエンドのサンプル実装をのぞく
レイヤー分けをしたアーキテクチャで作るiOSアプリ&バックエンドのサンプル実装をのぞくレイヤー分けをしたアーキテクチャで作るiOSアプリ&バックエンドのサンプル実装をのぞく
レイヤー分けをしたアーキテクチャで作るiOSアプリ&バックエンドのサンプル実装をのぞく
 
iOS側のUIの特徴と見比べるAndroid側でのUI実装のヒント
iOS側のUIの特徴と見比べるAndroid側でのUI実装のヒントiOS側のUIの特徴と見比べるAndroid側でのUI実装のヒント
iOS側のUIの特徴と見比べるAndroid側でのUI実装のヒント
 
何故に私達(特に私)はアプリのアニメーションや UI表現に魅了されるのか? そして共存と向き合いを考える
何故に私達(特に私)はアプリのアニメーションや UI表現に魅了されるのか? そして共存と向き合いを考える何故に私達(特に私)はアプリのアニメーションや UI表現に魅了されるのか? そして共存と向き合いを考える
何故に私達(特に私)はアプリのアニメーションや UI表現に魅了されるのか? そして共存と向き合いを考える
 
Approach of Prototyping for making Application User Interface about iOS
Approach of Prototyping for making Application User Interface about iOSApproach of Prototyping for making Application User Interface about iOS
Approach of Prototyping for making Application User Interface about iOS
 
デザイナー→Webエンジニア→iOSエンジニアと渡り歩いた僕なりのSwiftとの向き合い方と生かす戦略
デザイナー→Webエンジニア→iOSエンジニアと渡り歩いた僕なりのSwiftとの向き合い方と生かす戦略デザイナー→Webエンジニア→iOSエンジニアと渡り歩いた僕なりのSwiftとの向き合い方と生かす戦略
デザイナー→Webエンジニア→iOSエンジニアと渡り歩いた僕なりのSwiftとの向き合い方と生かす戦略
 
アプリ開発におけるテキスト装飾のアイデア集
アプリ開発におけるテキスト装飾のアイデア集アプリ開発におけるテキスト装飾のアイデア集
アプリ開発におけるテキスト装飾のアイデア集
 
iOSアプリで気になった動きや表現を上手にアレンジして活用してみる
iOSアプリで気になった動きや表現を上手にアレンジして活用してみるiOSアプリで気になった動きや表現を上手にアレンジして活用してみる
iOSアプリで気になった動きや表現を上手にアレンジして活用してみる
 
iOSアプリUIとの触れ合いと歩む僕なりのSwiftの楽しみ方
iOSアプリUIとの触れ合いと歩む僕なりのSwiftの楽しみ方iOSアプリUIとの触れ合いと歩む僕なりのSwiftの楽しみ方
iOSアプリUIとの触れ合いと歩む僕なりのSwiftの楽しみ方
 
UI表現ライブラリを有効活用して iOSアプリのUIをオシャレにするワザ紹介
UI表現ライブラリを有効活用して iOSアプリのUIをオシャレにするワザ紹介UI表現ライブラリを有効活用して iOSアプリのUIをオシャレにするワザ紹介
UI表現ライブラリを有効活用して iOSアプリのUIをオシャレにするワザ紹介
 
メディアアプリでよく見る無限スクロールするタブの動きへの考察
メディアアプリでよく見る無限スクロールするタブの動きへの考察メディアアプリでよく見る無限スクロールするタブの動きへの考察
メディアアプリでよく見る無限スクロールするタブの動きへの考察
 
ライブラリやView構造を有効活用して iOSアプリのUIをオシャレにするワザ紹介
ライブラリやView構造を有効活用して iOSアプリのUIをオシャレにするワザ紹介ライブラリやView構造を有効活用して iOSアプリのUIをオシャレにするワザ紹介
ライブラリやView構造を有効活用して iOSアプリのUIをオシャレにするワザ紹介
 
Hint of“Passcode Lock”Screen and Logic (with LocalAuthentication).
Hint of“Passcode Lock”Screen and Logic (with LocalAuthentication).Hint of“Passcode Lock”Screen and Logic (with LocalAuthentication).
Hint of“Passcode Lock”Screen and Logic (with LocalAuthentication).
 
部品に切り分けて考えるView構造とライブラリを上手に活用したUI実装
部品に切り分けて考えるView構造とライブラリを上手に活用したUI実装部品に切り分けて考えるView構造とライブラリを上手に活用したUI実装
部品に切り分けて考えるView構造とライブラリを上手に活用したUI実装
 
RxSwiftとMVVMパターンと仲良くなる次のステップ
RxSwiftとMVVMパターンと仲良くなる次のステップRxSwiftとMVVMパターンと仲良くなる次のステップ
RxSwiftとMVVMパターンと仲良くなる次のステップ
 
まずはできるところから始める UnitTestとテストができる実装について
まずはできるところから始める UnitTestとテストができる実装についてまずはできるところから始める UnitTestとテストができる実装について
まずはできるところから始める UnitTestとテストができる実装について
 

More from Fumiya Sakai

RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介
RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介
RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介Fumiya Sakai
 
少しずつ手厚くして不具合や仕様漏れを防ぐために
少しずつ手厚くして不具合や仕様漏れを防ぐために少しずつ手厚くして不具合や仕様漏れを防ぐために
少しずつ手厚くして不具合や仕様漏れを防ぐためにFumiya Sakai
 
Measures for Growth with Firebase Remote Config & Unit Testing Using RxSwift
Measures for Growth with Firebase Remote Config & Unit Testing Using RxSwiftMeasures for Growth with Firebase Remote Config & Unit Testing Using RxSwift
Measures for Growth with Firebase Remote Config & Unit Testing Using RxSwiftFumiya Sakai
 
既存プロジェクトで使っていたDIをお引っ越し&DIYすることになった
既存プロジェクトで使っていたDIをお引っ越し&DIYすることになった既存プロジェクトで使っていたDIをお引っ越し&DIYすることになった
既存プロジェクトで使っていたDIをお引っ越し&DIYすることになったFumiya Sakai
 
UI実装に関するセッションを 簡単ながら振り返ってみる(仮)
UI実装に関するセッションを 簡単ながら振り返ってみる(仮)UI実装に関するセッションを 簡単ながら振り返ってみる(仮)
UI実装に関するセッションを 簡単ながら振り返ってみる(仮)Fumiya Sakai
 
UIKitやSwiftUIで表現や動きが特徴的なUI実装事例を考察する
UIKitやSwiftUIで表現や動きが特徴的なUI実装事例を考察するUIKitやSwiftUIで表現や動きが特徴的なUI実装事例を考察する
UIKitやSwiftUIで表現や動きが特徴的なUI実装事例を考察するFumiya Sakai
 
試して感覚を掴んでみるUICollectionViewCompositionalLayout & Combine
試して感覚を掴んでみるUICollectionViewCompositionalLayout & Combine試して感覚を掴んでみるUICollectionViewCompositionalLayout & Combine
試して感覚を掴んでみるUICollectionViewCompositionalLayout & CombineFumiya Sakai
 
Hint of a little ingenuity about UI.
Hint of a little ingenuity about UI.Hint of a little ingenuity about UI.
Hint of a little ingenuity about UI.Fumiya Sakai
 
書籍執筆からの今後に向けてのロードマップ
書籍執筆からの今後に向けてのロードマップ書籍執筆からの今後に向けてのロードマップ
書籍執筆からの今後に向けてのロードマップFumiya Sakai
 
ReduxとSwiftの組み合わせ:改訂版
ReduxとSwiftの組み合わせ:改訂版ReduxとSwiftの組み合わせ:改訂版
ReduxとSwiftの組み合わせ:改訂版Fumiya Sakai
 

More from Fumiya Sakai (10)

RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介
RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介
RxDataSourceをNSDiffableDataSourceへ置き換える際のTips集紹介
 
少しずつ手厚くして不具合や仕様漏れを防ぐために
少しずつ手厚くして不具合や仕様漏れを防ぐために少しずつ手厚くして不具合や仕様漏れを防ぐために
少しずつ手厚くして不具合や仕様漏れを防ぐために
 
Measures for Growth with Firebase Remote Config & Unit Testing Using RxSwift
Measures for Growth with Firebase Remote Config & Unit Testing Using RxSwiftMeasures for Growth with Firebase Remote Config & Unit Testing Using RxSwift
Measures for Growth with Firebase Remote Config & Unit Testing Using RxSwift
 
既存プロジェクトで使っていたDIをお引っ越し&DIYすることになった
既存プロジェクトで使っていたDIをお引っ越し&DIYすることになった既存プロジェクトで使っていたDIをお引っ越し&DIYすることになった
既存プロジェクトで使っていたDIをお引っ越し&DIYすることになった
 
UI実装に関するセッションを 簡単ながら振り返ってみる(仮)
UI実装に関するセッションを 簡単ながら振り返ってみる(仮)UI実装に関するセッションを 簡単ながら振り返ってみる(仮)
UI実装に関するセッションを 簡単ながら振り返ってみる(仮)
 
UIKitやSwiftUIで表現や動きが特徴的なUI実装事例を考察する
UIKitやSwiftUIで表現や動きが特徴的なUI実装事例を考察するUIKitやSwiftUIで表現や動きが特徴的なUI実装事例を考察する
UIKitやSwiftUIで表現や動きが特徴的なUI実装事例を考察する
 
試して感覚を掴んでみるUICollectionViewCompositionalLayout & Combine
試して感覚を掴んでみるUICollectionViewCompositionalLayout & Combine試して感覚を掴んでみるUICollectionViewCompositionalLayout & Combine
試して感覚を掴んでみるUICollectionViewCompositionalLayout & Combine
 
Hint of a little ingenuity about UI.
Hint of a little ingenuity about UI.Hint of a little ingenuity about UI.
Hint of a little ingenuity about UI.
 
書籍執筆からの今後に向けてのロードマップ
書籍執筆からの今後に向けてのロードマップ書籍執筆からの今後に向けてのロードマップ
書籍執筆からの今後に向けてのロードマップ
 
ReduxとSwiftの組み合わせ:改訂版
ReduxとSwiftの組み合わせ:改訂版ReduxとSwiftの組み合わせ:改訂版
ReduxとSwiftの組み合わせ:改訂版
 

動画プレイヤーアプリの開発を通じて学んだ機能を実現するための要点解説