YouTubeの公式アプリの動画のレイヤー?は横向きにした場合、全画面に広がりますがそのタイミングがデフォルトの遷移のタイミングより少し遅いのが気になって再現出来るか試してみました。
結論としては似たような動きが出来たのでとりあえずこれでいいかなあと。(ちょっと上記のgifでは分かりづらいけど)
新しいプロジェクトでSingleViewApplicationを選択してStoryBoard上のViewControllerにUIViewとTableViewをそれっぽく配置してAutoLayoutで位置とサイズを指定
TableViewの実装は今回パスして、とりあえず動画の部分だけを実装。
UIViewとそれのAutoLayoutをViewController.swiftに紐付けておく
一つ断っておくと今回、LandScapeからPortraitからと、その逆の操作時の動画ビューの大きさを変更するアニメーションの実装方法が違います。
先方はAutoLayoutを利用、もう片方は昔ながらのframeのサイズを変更する方法。
統一した方がいいのはもちろんなのですが、あくまでお試しという事でご容赦。
(AutoLayoutを使ってPortraitからLandScape時のAutoLayoutの設定が面倒そうだったので挫折しただけですが)
@IBOutlet weak var topConstraint: NSLayoutConstraint!
@IBOutlet weak var leftConstraint: NSLayoutConstraint!
@IBOutlet weak var rightConstraint: NSLayoutConstraint!
@IBOutlet weak var subView: UIView!
//サポートするデバイスの向きを指定する
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.Portrait
}
var player = AVPlayer()
var playerLayer: AVPlayerLayer!
var invisibleButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if let url = NSBundle.mainBundle().URLForResource("IMG_0727", withExtension: "mp4") {
do {
player = AVPlayer(URL: url)
playerLayer = AVPlayerLayer(player: player)
subView.layer.insertSublayer(playerLayer, atIndex: 0)
self.playerLayer.anchorPoint = CGPointZero
self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
//再生・停止ボタン追加
subView.addSubview(invisibleButton)
invisibleButton.addTarget(self, action: "invisibleButtonTapped:",
forControlEvents: UIControlEvents.TouchUpInside)
//最前面にしたい
self.view.bringSubviewToFront(subView)
}
} else {
fatalError("error")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// 端末の向きがかわったら呼び出される.
func onOrientationChange(notification: NSNotification){
// 向き毎に制御
if(UIDeviceOrientationIsLandscape(UIDevice.currentDevice().orientation)){
UIView.animateWithDuration(1.0, delay: 0.5, options: UIViewAnimationOptions.CurveEaseIn, animations: { () -> Void in
if(UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeLeft) {
self.subView.transform = CGAffineTransformMakeRotation((CGFloat(90.0 * M_PI) / 180.0))
} else {
self.subView.transform = CGAffineTransformMakeRotation((CGFloat(270.0 * M_PI) / 180.0))
}
self.subView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)
}, completion: { (Bool) -> Void in
})
let oldBounds = self.playerLayer.bounds
var newBounds = oldBounds
newBounds.size = CGSizeMake(self.view.frame.size.height, self.view.frame.size.width)
let animation : CABasicAnimation = CABasicAnimation(keyPath: "bounds")
animation.delegate = self
animation.beginTime = CACurrentMediaTime() + 0.5
animation.fromValue = NSValue(CGRect: oldBounds)
animation.toValue = NSValue(CGRect: newBounds)
animation.autoreverses = false
animation.removedOnCompletion = false
animation.fillMode = kCAFillModeForwards
animation.duration = 1.0
self.playerLayer.addAnimation(animation, forKey: "rotate-landscape")
} else if(UIDeviceOrientationIsPortrait(UIDevice.currentDevice().orientation)) {
self.view.setNeedsUpdateConstraints()
self.topConstraint.constant = 0
self.rightConstraint.constant = 54
self.leftConstraint.constant = 52
UIView.animateWithDuration(1.0, delay: 0.5, options: UIViewAnimationOptions.CurveEaseIn, animations: { () -> Void in
self.subView.transform = CGAffineTransformIdentity
self.view.layoutIfNeeded()
}, completion: { (Bool) -> Void in
})
let oldBounds = self.playerLayer.bounds
var newBounds = oldBounds
newBounds.size = CGSizeMake(self.subView.frame.size.width, self.subView.frame.size.height)
let animation : CABasicAnimation = CABasicAnimation(keyPath: "bounds")
animation.delegate = self
animation.beginTime = CACurrentMediaTime() + 0.5
animation.fromValue = NSValue(CGRect: oldBounds)
animation.toValue = NSValue(CGRect: newBounds)
animation.autoreverses = false
animation.removedOnCompletion = false
animation.fillMode = kCAFillModeForwards
animation.duration = 1.0
self.playerLayer.addAnimation(animation, forKey: "rotate-portant")
}
override func viewDidAppear(animated: Bool) {
UIView.performWithoutAnimation { () -> Void in
self.playerLayer.frame = self.subView.bounds
}
NSNotificationCenter.defaultCenter().addObserver(self, selector: "onOrientationChange:", name: UIDeviceOrientationDidChangeNotification, object: nil)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
invisibleButton.frame = subView.bounds
}
override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
if(anim == self.playerLayer.animationForKey("rotate-landscape")){
self.playerLayer.bounds.size = CGSizeMake(self.view.frame.size.height, self.view.frame.size.width)
self.playerLayer.removeAnimationForKey("rotate-landscape")
}else if(anim == self.playerLayer.animationForKey("rotate-portant")){
self.playerLayer.bounds.size = CGSizeMake(self.subView.frame.size.width, self.subView.frame.size.height)
self.playerLayer.removeAnimationForKey("rotate-portant")
self.playerLayer.removeAllAnimations()
}
invisibleButton.frame = self.playerLayer.bounds
}
func invisibleButtonTapped(sender: UIButton!) {
let playerIsPlaying:Bool = player.rate > 0
if (playerIsPlaying) {
player.pause();
} else {
player.play();
}
}
}
追加したCALayerは、UIViewの大きさの変更に連動しないようなのでUIViewのアニメーションと同じタイミングで動くようにCALayer用のアニメーションも動かしてやる必要があります。
参考
[A Custom Video Player for iOS with AVFoundation]
(http://binarymosaic.com/custom-video-player-for-ios-with-avfoundation/)
AVPlayerLayerの実装と再生、停止ボタンの実装方法はこちらをそのまま参考。英語だけど分かりやすい!
【Objective-C】iOS8になり、動画の縦横回転の実装が難しくなった話
方向変換時のアニメーションで大変参考になりました。