但当您的花色提升的必然的局面公海赌船网站,天下文枢

作为一个iOS程序员,MVC一定是我们熟习的一种架构形式,而且当你的花色规模不大的时候,MVC也着实有它的优势,它的付出功能真的是十足高。但当您的系列发展的一定的局面,你会发现传统的MVC情势会招致C层代码量剧增,维护困难等一密密麻麻题材,这多少个时候我们就需要考虑部分任何情势了。

尊重知识,转发请注解出处:iOS流媒体开发之二:滑动手势控制音量、亮度和进度

公海赌船网站 1

MV(X)的基本要素

常用的架构格局

  • MVC
  • MVVM
  • MVP
  • VIPER

前方两种格局都由五个模块组合:

  • Models —— 数据层,负责数据的拍卖。
  • Views —— 展示层,即具有的UI
  • Controller/Presenter/ViewModele(控制器/展示器/视图模型)——它们承受View与Mode之间的调配

普天之下文枢

MVC

概要

01

刚到夫子庙,就看见牌坊上书写“天下文枢”六个大字,天下文化的主旨呐,告诉众人这就是龙门,破茧成蝶要从此处起初。

圣彼得(Peter)堡是先天古城,这牌坊给人一种雄踞天下的自信。

未曾手机的烦扰,我晃晃悠悠走了进入,下午的街道如马那瓜的南梁御街一律,夜幕降临得越快,游人的嘻笑声和商贩的叫卖声越厉害。

隆重的气氛很能带动游人的心怀,前面的女孩已经摘掉口罩手套给男朋友拍摄了,因为她俩同我一样,不知不觉,就来到了秦滦河畔。

公海赌船网站 2

秦格尔木河畔

02

那就是俞平伯和朱自清同写《桨声灯影里的秦长江》的地方吧!这就是稍稍学子墨客吟诗作赋的地点啊?就是以此地点,许多烟花女孩子翘首期盼多情才子归来,从未想过,一最先就已然是惨不忍睹的情爱结局。也不敢想,不忍恨忘了罢,不想世人竟记了下来。

有些可歌可泣的故事,不过是止于船上,淡于心上

自身从不乘船,夫子庙也没有进来,就呆呆地倚栏凝望,任凭旁边情侣的自拍杆挡住我的视线,任凭男人和太太外孙女在视频聊天,我都尚未专注,只看着河中华丽的船和对面墙上金色的双龙戏珠。

水中一片闪亮,船顶上的彩灯和墙上的灯光倒映下来。乘客络绎不绝地登上船最先游览,又有成百上千乘客刚下了船。不知怎的,突然想起了古秦淮的那一个客人们随着而来,听了多少个小曲,喝了几两小酒,便心满意足得意归去。

现在船中缺乏了半边天,人们依然快乐。可见,我们殷切喜欢的是这条千年的河,女子总会“朱颜辞镜花辞树”的,而秦大黑河从古至今,涓涓流淌成永恒。

公海赌船网站 3

江南贡院

传统的MVC

俺们所熟识的MVC其实Apple给大家提供的Cocoa
MVC,但实际上MVC初阶发生于Web,它原本的规范应该是这么的

公海赌船网站 4

传统MVC

在这种架构下,View是无状态的,在Model变化的时候它只是简单的被Controller重绘,比如网页中你点击了一个新的链接,整个页面就再度加载。即便这种MVC在iOS应该里面可以实现,但是出于MVC的五个模块都紧紧耦合了,每一个模块都和此外两种模块有关系,所以尽管是促成了也没有怎么意思。这种耦合还降低了它们的可重用性,所以,传统的MVC在iOS中得以遗弃了。

看来著作的题目,小伙伴们大概会有二种反应:①这和流媒体技术没关系吧②网上有很多那么些功用的落实方案。
1、对此第一种反应,从开发者的角度看那一个实在不属于流媒体技术层面,可是对于用户来讲这一个早已是看视频时理所当然应该有些效益,密不可分,鉴于用户就是上帝,所以这几个相对属于流媒体范畴.
2、第二种反应确实是事实,不过能真的周到兑现的不多。有6s
Plus的同伙可以打开爱奇艺或者天涯论坛和讯,随便打开一个录像,手势增大音量出现音量无法调节到最大的bug,会剩余4格音量,只可以先调节到很小的高低再一次增大才可以到最大音量,当然这多少个是系统在6s上的一个bug,但不用不能解决,即便是爱奇艺和果壳网也并未系数兑现(您看看此作品的时候有可能已经修复此bug)。还有一点一般的实现方案是丰裕UIPanGestureRecognizer手势,那个方案一经只是简单的录像播放器或者写一个Demo完全没问题,但是放在一个略带复杂点的视频播放器会唤起许多题目,前面再说,因而我这边提供另一种方案。

03

貌似出行,我随便不会打开背包。一个外来乘客在景区开拓自己的背包,无疑不少东西都会有涌出来,甚至会让你决定不住局面,这种两难不只在影片场地中冒出,生活中处处存在。

更重要的是,打开背包,有时会被窃贼盯上,你的无心之举,不料成了外人的“得意之笔”,一定要小心啊。

手机没电,住处都找不到就务须拿出充电宝来,开机找到住处短短几秒钟,身边已经有为数不少个人一晃而过,我循着导航重临了江南贡院(刚出发的地方)。

江南贡院是南陈会试的地点中间有个大水池,两旁的墙上书写“天地玄黄宇宙洪荒……”字与字里面(墙与墙之隔墙上有字)有个小房子,这就是号舍,号舍当然就是考试的地点了,在秦赣江畔试验,不知有些许人才心猿意马,也不知有多少人文思泉涌,全看个人幸福罢!

公海赌船网站 5

波尔图国际青年公寓

Apple的MVC

公海赌船网站 6

Cocoa MVC

Apple提供的MVC中,View和Model之间是相互独立的,它们只通过Controller来彼此关联。可惜的是Controller得重用性太差,因为大家一般都把冗杂的事体逻辑放在了Controller中。

切实中,我们的MVC一般是这般的

公海赌船网站 7

现实MVC

何以会那样啊?首要依旧因为我们的UIViewController它自身就拥有一个VIew,这一个View是怀有视图的根视图,而且View的生命周期也都由Controoler负责管理,所以View和Controller是很难完成相互独立的。尽管您可以把控制器里的一对政工逻辑和数据转换工作交给Model,然而你却未曾主意将有些做事让View来平摊,因为View的紧要职责只是将用户的操作行为付出Controller去处理而已。于是Controller最后就改为了拥有东西的代办和数据源,甚至还有网络请求…..还有……所以我们写的Controller代码量一般都是老大大的,随着当工作需求的充实,Controller的代码量会一直增长,而绝对来说View和Model的代码量就相比较稳定,所以也有人把MVC叫做Massive
View Controller,因为Controller确实显得略微臃肿。

在此间关于Model的分割,其实有一个胖Model和瘦Model之分,它们的差距首要就是把Controller的一些数据处理职责交给了胖Model。

胖Model(Fat Model):

胖Model包含了部分弱业务逻辑。胖Model要达到的目标是,Controller从胖Model这里得到数码将来,不用做额外的操作如故只做非常少的操作就能将数据采用在View上。
FatModel做了那几个弱业务之后,Controller可以变得绝对skinny一点,它只需要关爱强业务代码。而强业务转移的可能要比弱业务大得多,弱业务相对平稳,所以弱业务塞给Model不会有太大问题。另一方面,弱业务重新出现的频率要领先强业务,对复用性要求更高,假诺这有些作业写在Controller,会促成代码冗余,类似的代码会洒得到处都是,而且如若弱业务有修改,你就会需要修改所有地点。假设塞到了Model中,就只需要改Model就够了。
可是胖Mpdel也不是不怕从未缺陷的,它的瑕疵就在于胖Model相对相比较难移植,即便只是富含弱业务,可是它说到底也是业务,迁移的时候很容易拔出Rob带出泥,也就是说它耦合了它的事务。而且软件是会成长的,FatModel也很有可能随着软件的成才尤为Fat,最终难以保障。

瘦Model(Slim Model):

瘦Model只承担作业数据的抒发,所有事务无论强弱一律人给Controller。瘦Model要高达的目标是,尽一切可能去编写细粒度Model,然后配套各种helper类或者措施来对弱业务做抽象,强业务仍旧交给Controller。
出于Slim
Model跟工作完全无关,它的数额足以提交其他一个能处理它多少的Helper或任何的靶子,来形成业务。在代码迁移的时候独立性很强,很少会油但是生拔出萝卜带出泥的情形。其它,由于SlimModel只是数量表明,对它举办珍贵基本上是0成本,软件膨胀得再决定,SlimModel也不会大到哪里去。缺点就在于,Helper这种做法也丢失得很好,由于Model的操作会现身在各类地方,SlimModel很容易出现代码重复,在大势所趋程度上违反了DRY(Don’t
Repeat
Yourself)的思路,Controller依旧不可制止在自然水准下边世代码膨胀。

概括,Cocoa MVC在各地点的显现如下:

  • 划分 – View 和 Model 确实是落实了分离,可是 View 和 Controller
    耦合的太 厉害
  • 可测性 – 因为划分的不够明白,所以能测的核心就只有 Model 而已
  • 易用
    相较于其他格局,它的代码量最少。而且基本上每个人都很谙习它,固然是没太多经历的开发者也能维护。

天涯论坛微博手势调节音量bug.png

04

开拓进取数百步,右转到了本人住的地点:德班国际青年旅馆,办好手续之后,上楼径直来到温馨宿舍。

一进门放下背包再也不想动了,其实门外是个清呢,顾客大多是我们住青旅的观光客,歌手一首接一首的歌谣轻轻唱起。

自身听出来的有李志,有陈粒,有宋冬野也有赵雷,就如此听啊,不去看了。南陈犹抱琵琶半遮面的歌女不也是如此吧?身临其境会损坏美感,岂不可惜。

没有标准化听桨声,歌声也不错,错过有错过的美好,人要知足常乐。

公海赌船网站 8

鸭血粉丝汤和蟹黄汤包

MVP

公海赌船网站 9

MVP

看起来和Cocoa
MVC很像,也的确很像。可是,在MVC中View和COntroller是一环扣一环耦合的,而在MVP中,Presenter完全不尊崇ViewController的生命周期,而且View也能被简单mock出来,所以在Presenter里面基本没有什么布局相关的代码,它的任务只是透过数据和景观更新View。
并且在MVP中,UIVIewController的这么些子类其实是属于View的。这样就提供了更好的可测性,只是开发进度会更高,因为你必须手动去创建数量和绑定事件。

下边我写了个简单的Demo

公海赌船网站 10

MVPDemo

是因为此处最重假设上学架构情势思想,所以自己的命名简单粗暴,希望大家了然。

公海赌船网站 11

界面1

界面也很简单,就是经过点击按钮修改五个label彰显的情节

Model很简单,就是一个数据结构,但在实际应用中,你可以将网络请求等部分数码处理放在这里

@interface Model : NSObject

@property (nonatomic, strong) NSString *first;
@property (nonatomic, strong) NSString *second;

@end

要让Presenter和View通信,所以我们定义一个研商,以促成Presenter向View发送命令

@protocol MyProtocol <NSObject>

- (void)setFirst:(NSString *)first;
- (void)setSecond:(NSString *)second;

@end

view/VIewController,实现该协议

.h
 @interface ViewController : UIViewController

@property (nonatomic, strong) UILabel *firstLabel;
@property (nonatomic, strong) UILabel *secondLabel;
@property (nonatomic, strong) UIButton *tapButton;

@end


.m主要代码
- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.firstLabel];
    [self.view addSubview:self.secondLabel];
    [self.view addSubview:self.tapButton];
    self.presenter = [Presenter new];
    [self.presenter attachView:self];
}

- (void)buttonClicked{
    [self.presenter reloadView];
}

- (void)setFirst:(NSString *)first{
    self.firstLabel.text = first;
}

- (void)setSecond:(NSString *)second{
    self.secondLabel.text = second;
}

Presenter

.h
@interface Presenter : NSObject

- (void)attachView:(id <MyProtocol>)attachView;
- (void)reloadView;

@end


.m
@interface Presenter()

@property (nonatomic, weak) id <MyProtocol> view;
@property (nonatomic, strong) Model *model;

@end

@implementation Presenter

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.model = [Model new];
        self.model.first = @"first";
        self.model.second = @"second";
    }
    return self;
}

- (void)attachView:(id<MyProtocol>)attachView{
    self.view = attachView;
}

- (void)reloadView{
    //可以在这里做一些数据处理
    [self.view setFirst:self.model.first];
    [self.view setSecond:self.model.second];
}
@end

这边只是一个简短的Demo,其实想想很简短,就是讲业务逻辑交给Presenter,而Presenter以命令的款式来支配View。
完整Demo可以看这里

思考

05

宿舍十个床位,我在上铺,一哥们说头两次住满这么几人,更出乎意料的是还有一个黑人兄弟,我们互动打个招呼,我们没有寒暄几句,就忙自己的事情了。

屋子相比较闷,空调温度很高,我喝了某些次水,直至中午才睡去。

第二天离开时,重新来到秦湘江畔,它和前晚离开很大,街上并没有多少人。我站在桥上看山水,这就是一个平和动人的小镇,深更半夜的故事属于早晨,而太阳永远照常升起。

公海赌船网站 12

秦大黑河的桥上

昨夜就传闻海法的佳肴是鸭血粉丝汤和蟹黄汤包,离开夫子庙后就近就有一家,我一贯走了进来,直接点的就是这两样,没多长时间我就大快朵颐吃了四起。

鸭血粉丝汤的汤汁确实好喝,反倒鸭血和粉丝成了佐料。我在迪拜的一家旅馆平日吃羊肉汤加拌面,那么些汤则清淡得多。而以此汤浓稠,味道很足,当然,吃完后自己渴了一中午。

蟹黄汤包是浙江价值观佳肴,我是一口一个,觉得味道和小杨生煎差不多,然而大小相差很大。包子似乎回笼加过热,周围的皮已经发硬,好在不影响口感,我就快快解决了。

吃饱喝足,就活该考虑下一步行程(南昌陵),关于秦雅砻江夫子庙的旅行也停下。早上找的许多景致,正等自身一步步去探索,去感受,去发现它无与伦比的美妙。

连续在中途,出发!

部分证实:

MVP架构拥有五个真正独立的道岔,所以在组建的时候会有一部分题目,而MVP也成了第一个披露这种问题的架构,因为我们不想让View知道Model的音讯,所以在当下的Controller去组装是不得法的,大家应当在此外的地点成功组建。比如大家得以制造一个应用层的Router服务,让它来负责组建和View-to-View的转场。这么些题目下过多格局中都设有。

下边总计一下MVP的各方面展现:

  • 划分——大家把大部分任务都分配到了Presenter和Model里面,而View基本不需要做哪些
  • 可测性——我们可以通过View来测试大部分事务逻辑
  • 易用——代码量差不多是MVC架构的两倍,可是MVP的笔触如故蛮清晰的

另外,MVP还有一个变体,它的不同重要就是添加了数码绑定。那个版本的MVP的View和Model直接绑定,而Presenter如故延续处理View上的用户操作,控制View的来得变化。那种架构和历史观的MVC类似,所以我们着力可以吐弃。

以此效应对于有一部分开发经历的人来说并不难,只要勤快点,别境遇点难题没怎么考虑就去找第三方库,都足以靠自己弄个八九不离十。我这里想通过这些职能向大家介绍自身日常缓解一部分问题的心路历程,希望能帮到我们的不仅是技巧本身,还有一种缓解问题的力量。对于喜好“废话少说,直接上代码”的同伴,这篇作品可能会让您多少不痛快,因为在我看来代码一文不值,值钱的是我们的想想,别急,大家逐步来。

(无戒操练营第54天)

MVVM

MVVM可以说是MV(X)连串颅内肉色素瘤行兴起的也是最非凡的一种架构,而它也广受我们iOS程序员喜爱。

公海赌船网站 13

MVVM

MVVM和MVP很像:

  • 把ViewController看成View
  • View和Model之间从未紧耦合

此外它还让VIew和ViewModel做了数据绑定。ViewModel可以调用对Model做更改,也足以再Model更新的时候对自身举行调整,然后经过View和ViewModel之间的绑定,对View举行相应的更新。

  • 浅析问题,抽离本质
    当我们获得这么些要求的时候,首先不要想我用什么样类可以实现,我要添加在哪个view上,我们率先要分析出这么些题目标本质,对于手势调节那些问题的本质很简单,就是用户左右、上下滑动时遵照手指运动的方向和偏移量改变音量等设置。
  • 捋顺逻辑,列出方案
    解析出问题的精神后,依然不要去想和代码有关的业务,接着咱们要思想处理问题的逻辑并交付一个当下总的来说可行的方案。我平时会找一张白纸,画一些草图协理回忆和领会,当然曾多次想用xmind等合计导图,不过发现依然扎实的白纸适合自己,小伙伴们能够遵照我采取,只要能支援捋顺思路就可以了。
    第一步我们要检测到手指在屏幕上的滑动,可以使用UIPanGestureRecognizer手势,前面说了这个会引起很多问题,比如一个视频播放器不仅仅有滑动手势还是单击、双击、左滑退出UINavigationController,各种手势会冲突,其次我们有时会在拖动前或者结束后处理一些事情在再执行拖动,虽然UIPanGestureRecognizer有UIGestureRecognizerStateBegan、UIGestureRecognizerStateEnded这些状态,可是手指点击屏幕没有滑动的时候无法触发,因此我最后弃用这个方案,改为使用- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;系列方法,具体实现看后面代码; 第二步要判断用户向哪个方向移动了,以便调用相应的设置,这可以根据手指在上下左右的偏移量来计算,我的方案是设置一个初始偏移量的数值,比如30,上下左右哪个方向偏移量先到达30,手势就设定为对应的方向; 第三步就是调用对应的方法去改变音量、亮度和进度了,具体实现看后面。
有关绑定

在iOS平台方面有KVO和通告,不过用起来总是认为不太便宜,所以有一部分三方库供大家挑选:

事实上,大家在论及MVVM的时候就很容易想到ReactiveCocoa,它也是大家在iOS中应用MVVM的最好工具。可是相对来说它的就学成本和护卫成本
也是相比较高的,而且假如您使用不当,很可能引致灾难性的题目。

下边我暂时不要RAC来概括呈现一下MVVM:

公海赌船网站 14

MVVM

界面很简短,就是点击一个button修改label里面的数目

公海赌船网站 15

界面

Model

@interface MVVMModel : NSObject

@property (nonatomic, copy) NSString *text;

@end

@implementation MVVMModel

- (NSString *)text{
    _text = [NSString stringWithFormat:@"newText%d",rand()];
    return _text;
}

ViewModel

@interface MVVMViewModel : NSObject

- (void)changeText;

@end

@interface MVVMViewModel()

@property (nonatomic, strong) NSString *text;
@property (nonatomic, strong) MVVMModel *model;

@end

@implementation MVVMViewModel

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.model = [MVVMModel new];
    }
    return self;
}

- (void)changeText{
    self.text = self.model.text;;
}

Controller

@interface MVVMViewController ()

@property (weak, nonatomic) IBOutlet UILabel *textLabel;
@property (nonatomic, strong) MVVMViewModel *viewModel;

@end

@implementation MVVMViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.viewModel = [[MVVMViewModel alloc]init];
    [self.viewModel addObserver:self forKeyPath:@"text" options:NSKeyValueObservingOptionNew context:nil];
}
- (IBAction)buttonClicked:(UIButton *)sender {
    [self.viewModel changeText];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    self.textLabel.text = change[@"new"];
}

MVVM的中坚就是View和ViewModel的一个绑定,这里我只是简短的经过KVO实现,看起来并不是那么优雅,想要深度应用的话我以为如故有必不可少学习一下RAC的,需要完整的Demo请看这里

上面我们再来对MVVM的各地方表现做一个评价:

  • 划分——MVVM 框架之中的 View 比 MVP
    里面负责的事务要更多一些。因为前端是因而 ViewModel
    的多少绑定来更新自己情状的,而后人只是把持有的风波统统付给 Presenter
    去处理就完了,自己我并不担当更新。
  • 可测性—— 因为 ViewModel 对 View
    是大惑不解的,这样我们对它的测试就变得很粗略。View
    应该也是能够被测试的,不过也许因为它对 UIKit的借助,你会一向略过它。
  • 易用——它比MVP会更加从简,因为在 MVP 下你必须要把 View
    的具备事件都交由 Presenter 去处理,而且亟需手动的去立异 View
    的情况;而在 MVVM 下,你只需要用绑定就足以解决。

综上:MVVM
真的很有魅力,因为它不仅结合了上述二种框架的独到之处,还不需要您为视图的更新去写额外的代码(因为在
View 上一度做了数据绑定),另外它在可测性上的显示也仍然很棒。

为了简单易懂,以上的Demo都极度简单,不知晓看了这篇博客能否加深你对MV(X)的片段清楚,这多少个精通也仅看成自身个人的一对参考,有哪些难堪的地点希望大家提出。

行动

逻辑通了,方案有了,现在起来行走吧,首先你要有个可以播放视频demo,最好是点播录像,可参考上篇博文:iOS流媒体开发之一:总计系统提供的接口,这里不赘述。

  • 自定义一个UIButton
    我们需要在视频上添加一个晶莹剔透的button来捕捉和响应用户的点击滑动屏幕的事件,同时在.h文件中声称一个代理,向相应的页面传递自定义button的响应事件,代码如下:

.h文件

    #import <UIKit/UIKit.h>

    @protocol ZYLButtonDelegate <NSObject>

    /**
     * 开始触摸
     */
    - (void)touchesBeganWithPoint:(CGPoint)point;

    /**
     * 结束触摸
     */
    - (void)touchesEndWithPoint:(CGPoint)point;

    /**
     * 移动手指
     */
    - (void)touchesMoveWithPoint:(CGPoint)point;

    @end

    @interface ZYLButton : UIButton

    /**
     * 传递点击事件的代理
     */
    @property (weak, nonatomic) id <ZYLButtonDelegate> touchDelegate;

    @end

.m文件
#import “ZYLButton.h”

    @implementation ZYLButton

    //触摸开始
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        [super touchesBegan:touches withEvent:event];
        //获取触摸开始的坐标
        UITouch *touch = [touches anyObject];
        CGPoint currentP = [touch locationInView:self];
        [self.touchDelegate touchesBeganWithPoint:currentP];
    }

    //触摸结束
    - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        [super touchesEnded:touches withEvent:event];
        UITouch *touch = [touches anyObject];
        CGPoint currentP = [touch locationInView:self];
        [self.touchDelegate touchesEndWithPoint:currentP];
    }

    //移动
    - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
        UITouch *touch = [touches anyObject];
        CGPoint currentP = [touch locationInView:self];
        [self.touchDelegate touchesMoveWithPoint:currentP];
    }

    @end

注意:之所以选择UIButton有2点原因: 1、不用手动开启userInteractionEnabled用户交互 2、同时可以很方便的为UIButton添加Target,不需要像UIView那样在再定义一个UITapGestureRecognizer,当然UIButton添加各种状态的背景颜色各背景图也要比UIView方便得多。

  • 将自定义的Button添加到视频上
    //添加自定义的Button到录像镜头上
    self.button = [[ZYLButton alloc]
    initWithFrame:playerLayer.frame];
    self.button.touchDelegate = self;
    [playerView addSubview:self.button];

  • 在代理方法中改变定音量、亮度和速度
    先是定义个一枚举表示上下左右,这里只需要看清手指是上下仍旧左右滑行
    typedef NS_ENUM(NSUInteger, Direction) {
    DirectionLeftOrRight,
    DirectionUpOrDown,
    DirectionNone
    };
    与此同时阐明一个代表方向的变量、一个记录用户触摸视频时的坐标变量、一个笔录用户触摸摄像时的亮度和音量大小的变量和一个记下用户触摸屏幕是录像进度的变量
    @property (assign, nonatomic) Direction direction;
    @property (assign, nonatomic) CGPoint startPoint;
    @property (assign, nonatomic) CGFloat startVB;
    @property (assign, nonatomic) CGFloat startVideoRate;

  • 刚发轫触摸录像的代理
    当用户第一次触摸录像时,记录第一次触摸的坐标、当前高低或者亮度、当前录像的速度,为了拿到当前高低要首先定义MPVolumeView、
    UISlider
    @property (strong, nonatomic) MPVolumeView
    *volumeView;//控制音量的view

      @property (strong, nonatomic) UISlider* volumeViewSlider;//控制音量
    
      //设置self.volumeView的frame
      self.volumeView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.width * 9.0 / 16.0);

在getter方法中起初化:

  • (MPVolumeView *)volumeView {
    if (_volumeView == nil) {
    _volumeView = [[MPVolumeView alloc] init];
    [_volumeView sizeToFit];
    for (UIView view in [_volumeView subviews]){
    if ([view.class.description isEqualToString:@”MPVolumeSlider”]){
    self.volumeViewSlider = (UISlider
    )view;
    break;
    }
    }
    }
    return _volumeView;
    }

然后在开头触摸的代办里记录
#pragma mark – 起始触摸

    - (void)touchesBeganWithPoint:(CGPoint)point {
        //记录首次触摸坐标
        self.startPoint = point;
        //检测用户是触摸屏幕的左边还是右边,以此判断用户是要调节音量还是亮度,左边是亮度,右边是音量
        if (self.startPoint.x <= self.button.frame.size.width / 2.0) {
            //亮度
            self.startVB = [UIScreen mainScreen].brightness;
        } else {
            //音量
            self.startVB = self.volumeViewSlider.value;
        }
    }
    CMTime ctime = self.avPlayer.currentTime;
    self.startVideoRate = ctime.value / ctime.timescale / self.total;
  • 紧接着在拖动的代理方法里改变音量、亮度和进度
    #pragma mark – 拖动

    • (void)touchesMoveWithPoint:(CGPoint)point {
      //得出手指在Button上运动的离开
      CGPoint panPoint = CGPointMake(point.x – self.startPoint.x,
      point.y – self.startPoint.y);
      //分析出用户滑动的倾向
      if (self.direction == DirectionNone) {
      if (panPoint.x >= 30 || panPoint.x <= -30) {
      //进度
      self.direction = DirectionLeftOrRight;
      } else if (panPoint.y >= 30 || panPoint.y <= -30) {
      //音量和亮度
      self.direction = DirectionUpOrDown;
      }
      }

          if (self.direction == DirectionNone) {
              return;
          } else if (self.direction == DirectionUpOrDown) {
              //音量和亮度
              if (self.startPoint.x <= self.button.frame.size.width / 2.0) {
          //调节亮度
                  if (panPoint.y < 0) {
                      //增加亮度
                      [[UIScreen mainScreen] setBrightness:self.startVB + (-panPoint.y / 30.0 / 10)];
                  } else {
                      //减少亮度
                      [[UIScreen mainScreen] setBrightness:self.startVB - (panPoint.y / 30.0 / 10)];
                  }
      
              } else {
                  //音量
                  if (panPoint.y < 0) {
                       //增大音量
                      [self.volumeViewSlider setValue:self.startVB + (-panPoint.y / 30.0 / 10) animated:YES];
                      if (self.startVB + (-panPoint.y / 30 / 10) - self.volumeViewSlider.value >= 0.1) {
                          [self.volumeViewSlider setValue:0.1 animated:NO];
                          [self.volumeViewSlider setValue:self.startVB + (-panPoint.y / 30.0 / 10) animated:YES];
                      }
      
                  } else {
                      //减少音量
                      [self.volumeViewSlider setValue:self.startVB - (panPoint.y / 30.0 / 10) animated:YES];
                  }
              }
          } else if (self.direction == DirectionLeftOrRight ) {
              //进度
              CGFloat rate = self.startVideoRate + (panPoint.x / 30.0 / 20.0);
              if (rate > 1) {
                  rate = 1;
              } else if (rate < 0) {
                  rate = 0;
              }
          }
      

      }

注意: 1、前面提到一个增大音量的bug,我这里的解决办法是如果检测到用户设置的音量比系统当前的音量大于0.1,表示此时设置的音量已经无效了,然后把音量设置为0.1后再设置为我们设置无效的那个音量就可以了,具体做法参考代码; 2、在修改视频播放进度的时候,最好不在移动的方法中实时修改视频播放进度,一方面会造成卡顿的现象,一方面没有必要这么做,所以这里只是记录了用户想要调整的进度,然后在触摸结束的方法中设置进度; 3、这改变音量会调用系统自己的UI显示音量大小,但是在设置亮度的时候是系统没有提供相应的UI,需要我们自己设置,这个小伙伴们按照项目的需求自行添加一个UI效果就好了。

  • 入手截止的代办
    #pragma mark – 截止触摸

    • (void)touchesEndWithPoint:(CGPoint)point {
      if (self.direction == DirectionLeftOrRight) {
      [self.avPlayer seekToTime:CMTimeMakeWithSeconds(self.total *
      self.currentRate, 1) completionHandler:^(BOOL finished) {
      //在此地处理速度设置成功后的作业
      }];
      }
      }

真机运行效果如图

Demo.png

尾巴

到这里大家就完成了手指滑动视频控制音量、亮度和速度的要求,我们在自己的门类中实际上行使时还亟需基于项目需要充足适当的UI,处理任何一些逻辑和业务流程。希望我们通过这多少个小小的示例可以有谈得来的解决问题的思路和流程,力争将一个固然很简短的效劳做到尽量完美。Demo可以在此间下载:示例<small>Demo</small>

相关文章