项目中,也写过类似"视频全屏"的功能, 前一阵子读到今日头条 的一篇技术文章,详细介绍三种旋转方法差异优劣最终择取。文章从技术角度看写的非常好,从用户角度看,也用过多家有视频功能的app,今日头条的体验的确很优。特别值得学习特此参考写了一个视频全屏小功能
实现方法:配合重写当前的ViewController的shouldAutorotate方法,返回NO 并且控制 状态栏的展示 然后 通过 animation旋转动画处理UI相对布局
(1)组织类别方法 UINavigationController+Rotation 目的视频旋转 状态栏也要旋转
//// UINavigationController+Rotation.h// SectionDemo//// Created by HF on 17/4/1.// Copyright © 2017年 HF-Liqun. All rights reserved.//#import@interface UINavigationController (Rotation)@end//// UINavigationController+Rotation.m// SectionDemo//// Created by HF on 17/4/1.// Copyright © 2017年 HF-Liqun. All rights reserved.//#import "UINavigationController+Rotation.h"@implementation UINavigationController (Rotation)- (BOOL)shouldAutorotate{ return [[self.viewControllers lastObject] shouldAutorotate];}- (NSUInteger)supportedInterfaceOrientations{ return [[self.viewControllers lastObject] supportedInterfaceOrientations];}- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];}@end
(2)视频UI HFMovieView
//// HFMovieView.h// SectionDemo//// Created by HF on 17/4/1.// Copyright © 2017年 HF-Liqun. All rights reserved.//#import#import "HFPlayerView.h"typedef NS_ENUM(NSUInteger, MovieViewState) { MovieViewStateSmall, MovieViewStateAnimating, MovieViewStateFullscreen,};@interface HFMovieView : UIView/** 视频播放对象 */@property (nonatomic, strong)HFPlayerView *videoView;/** 记录小屏时的parentView */@property (nonatomic, weak) UIView *movieViewParentView;/** 记录小屏时的frame */@property (nonatomic, assign) CGRect movieViewFrame;@property (nonatomic, assign) MovieViewState state;@end
//// HFMovieView.m// SectionDemo//// Created by HF on 17/4/1.// Copyright © 2017年 HF-Liqun. All rights reserved.//#import "HFMovieView.h"@implementation HFMovieView- (instancetype)initWithFrame:(CGRect)frame{ self = [super initWithFrame:frame]; if (self) { //videoView [self addSubview:self.videoView]; //others UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; [self.videoView addGestureRecognizer:tapGestureRecognizer]; } return self;}#pragma mark - event- (void)handleTapGesture:(UITapGestureRecognizer *)sender{ if (sender.state == UIGestureRecognizerStateEnded) { if (self.state == MovieViewStateSmall) { [self enterFullscreen]; } else if (self.state == MovieViewStateFullscreen) { [self exitFullscreen]; } }}#pragma mark - private#pragma mark -- 全屏 animation- (void)enterFullscreen { if (self.state != MovieViewStateSmall) { return; } self.state = MovieViewStateAnimating; /* * 记录进入全屏前的parentView和frame */ self.movieViewParentView = self.videoView.superview; self.movieViewFrame = self.videoView.frame; /* * movieView移到window上 */ CGRect rectInWindow = [self convertRect:self.videoView.bounds toView:[UIApplication sharedApplication].keyWindow]; [self.videoView removeFromSuperview]; self.videoView.frame = rectInWindow; [[UIApplication sharedApplication].keyWindow addSubview:self.videoView]; /* * 执行动画 */ [UIView animateWithDuration:0.5 animations:^{ self.videoView.transform = CGAffineTransformMakeRotation(M_PI_2); self.videoView.bounds = CGRectMake(0, 0, CGRectGetHeight(self.videoView.superview.bounds), CGRectGetWidth(self.videoView.superview.bounds)); self.videoView.center = CGPointMake(CGRectGetMidX(self.videoView.superview.bounds), CGRectGetMidY(self.videoView.superview.bounds)); } completion:^(BOOL finished) { self.state = MovieViewStateFullscreen; }]; [self refreshStatusBarOrientation:UIInterfaceOrientationLandscapeRight];}#pragma mark -- 退出全屏 animation- (void)exitFullscreen{ if (self.state != MovieViewStateFullscreen) { return; } self.state = MovieViewStateAnimating; CGRect frame = [self.movieViewParentView convertRect:self.movieViewFrame toView:[UIApplication sharedApplication].keyWindow]; [UIView animateWithDuration:0.5 animations:^{ self.videoView.transform = CGAffineTransformIdentity; self.videoView.frame = frame; } completion:^(BOOL finished) { /* * movieView回到小屏位置 */ [self.videoView removeFromSuperview]; self.videoView.frame = self.movieViewFrame; [self.movieViewParentView addSubview:self.videoView]; self.state = MovieViewStateSmall; }]; [self refreshStatusBarOrientation:UIInterfaceOrientationPortrait];}#pragma mark -- 更新状态栏方向- (void)refreshStatusBarOrientation:(UIInterfaceOrientation)interfaceOrientation { [[UIApplication sharedApplication] setStatusBarOrientation:interfaceOrientation animated:YES];}#pragma mark - setter and getter- (HFPlayerView *)videoView{ if (!_videoView) { _videoView = [[HFPlayerView alloc]initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)]; _videoView.backgroundColor = [UIColor blackColor]; } return _videoView;}@end
(3)视图控制器
//// MethodDetailViewController.h// SectionDemo//// Created by HF on 17/4/1.// Copyright © 2017年 HF-Liqun. All rights reserved.//#import#import "HFMovieView.h"@interface MethodDetailViewController : UIViewController@property (nonatomic, strong) HFMovieView *moviewView;@end//// MethodDetailViewController.m// SectionDemo//// Created by HF on 17/4/1.// Copyright © 2017年 HF-Liqun. All rights reserved.//#import "MethodDetailViewController.h"@interface MethodDetailViewController ()@property (nonatomic, strong) UITableView *tableView;@property (nonatomic, strong) UIView *headView;@end@implementation MethodDetailViewController- (void)viewDidLoad { [super viewDidLoad]; [self.view addSubview:self.tableView]; [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.view); }]; self.tableView.tableHeaderView = self.headView; [self.headView addSubview:self.moviewView];}- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}#pragma mark - 旋转配置- (BOOL)shouldAutorotate { return NO;}#pragma mark - setter/getter- (UITableView *)tableView{ if (!_tableView) { _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; _tableView.backgroundColor = [UIColor clearColor]; // _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; } return _tableView;}- (UIView *)headView{ if (!_headView) { _headView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 200)];; _headView.backgroundColor = [UIColor lightGrayColor]; } return _headView;}- (HFMovieView *)moviewView{ if (!_moviewView) { _moviewView = [[HFMovieView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 200)]; _moviewView.backgroundColor = [UIColor yellowColor]; } return _moviewView;}@end
效果图:
(4)参考之前 写过视频播放的相关方法 优化架构分工
参考
参考:
1. https://techblog.toutiao.com/2017/03/28/fullscreen/
2.
3.