项目中,也写过类似"视频全屏"的功能, 前一阵子读到今日头条 的一篇技术文章,详细介绍三种旋转方法差异优劣最终择取。文章从技术角度看写的非常好,从用户角度看,也用过多家有视频功能的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 <UIKit/UIKit.h> @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 <UIKit/UIKit.h>
#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 <UIKit/UIKit.h>
#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)参考之前 写过视频播放的相关方法 优化架构分工

参考 SectionDemo

参考:

1. https://techblog.toutiao.com/2017/03/28/fullscreen/

2. iOS AVPlayer 学习

iOS 视频全屏功能 学习的更多相关文章

  1. iOS端一次视频全屏需求的实现(转)

    对于一个带有视频播放功能的app产品来说,视频全屏是一个基本且重要的需求.虽然这个需求看起来很简单,但是在实现上,我们前后迭代了三套技术方案.这篇文章将介绍这三种实现方案中的利弊和坑点,以及实现过程中 ...

  2. js 实现操作浏览器或者元素的全屏与退出全屏功能

    <!DOCTYPE html> <html lang="en" id="div1"> <head> <meta cha ...

  3. Chrome71版本使用screenfull.js全屏功能时报参数错误

    在生产环境长期使用的一个“全屏”功能突然失效了,查看Console 如下报错: Failed to execute 'requestFullscreen' on 'Element': paramete ...

  4. 富文本编辑器vue2-editor实现全屏功能

    vue2-editor非常不错,可惜并未带全屏功能,自己实现了一个,供大家参考. 实现思路:自定义模块. 1. 定义全屏模块Fullscreen /** * 编辑器的全屏实现 */ import no ...

  5. iOS的录屏功能

    iOS的录屏功能其实没什么好说的,因为网上的教程很多,但是网上的Demo无一例外几乎都有一个bug,那就是iPad上会出现闪退,这也体现了国内的教程文档的一个特点,就是抄袭,教程几乎千篇一律,bug也 ...

  6. JS实现元素的全屏、退出全屏功能

     在实际开发中,我们很可能需要实现某一元素的全屏和退出全屏功能,如canvas.所幸的是,js提供了相关api用来处理这一问题,只需简单的调用requestFullScreen.exitFullScr ...

  7. H5项目常见问题及注意事项,视频全屏,定位,屏幕旋转和触摸,偏页面重构向 来源joacycode的github

    Meta基础知识: H5页面窗口自动调整到设备宽度,并禁止用户缩放页面 //一.HTML页面结构 <meta name="viewport" content="wi ...

  8. iOS自定义全屏返回与tableView左划删除手势冲突解决

    当自定义一个navigationController实现全屏右划返回时, 使用起来是不是很爽, 代码如下: - (void)viewDidLoad { [super viewDidLoad]; UIG ...

  9. C#窗体全屏功能

    最近有朋友让我给他弄个应用程序全屏的功能,例如银行的取号程序界面.所以我从网上查询了一些实现的方法. C#应用程序中如何实现全屏幕显示功能? 效果就像windows自带的屏幕保护程序和众多的游戏那样, ...

随机推荐

  1. python3 读取csv的常用语法

    import csv #打开文件,用with打开可以不用去特意关闭file了,python3不支持file()打开文件,只能用open() with open("info.csv" ...

  2. keypad代码分析

    keypad作为input设备注册到内核,与platform总线驱动match. 1.描述一个输入设备对象 static struct input_dev *kpd_input_dev; 告知输入子系 ...

  3. ntp集群时间同步

    1. NTP 简介 网络时间协议(英语:Network Time Protocol,简称NTP)是在数据网络潜伏时间可变的计算机系统之间通过分组交换进行时钟同步的一个网络协议.自1985年以来,NTP ...

  4. python第二周数据类型 字符编码 文件处理

    第一数据类型需要学习的几个点: 用途 定义方式 常用操作和内置的方法 该类型总结: 可以存一个值或者多个值 只能存储一个值 可以存储多个值,值都可以是什么类型 有序或者无序 可变或者不可变 二:数字整 ...

  5. 解决easyui tabs中href无法跨域跳转

    <!DOCTYPE HTML> <html> <head> <meta http-equiv="content-type" content ...

  6. 多媒体开发之---h264格式详解

    http://blog.csdn.net/bluebirdssh/article/details/6533501 http://blog.csdn.net/d_l_u_f/article/detail ...

  7. mysql增加自定义函数功能

    mysql默认是不能自定义函数的 当create function时 This function has none of DETERMINISTIC, NO SQL, or READS SQL DAT ...

  8. 水仙花数-python

    题目: 求999以内的水仙花数? 分析: 如果一个3位数等于其各位数字的立方和,则称这个数为水仙花数. 例如:1^3 + 5^3+ 3^3 = 153,因此153就是一个水仙花数 我们需计算出153的 ...

  9. jQuery.getJSON()方法小记

    今天看了下jQquery中的getJSON()方法,做点小结: 原型: jQuery.getJSON( url [, data ] [, success(data, textStatus, jqXHR ...

  10. Python中threading的join和setDaemon的区别及用法[例子]

    Python多线程编程时,经常会用到join()和setDaemon()方法,今天特地研究了一下两者的区别. 1.join ()方法:主线程A中,创建了子线程B,并且在主线程A中调用了B.join() ...