iOS开发之AVPlayer的精彩使用--->网易新闻视频播放界面的另类实现

遇到个需求需要涉及到视频播放,那么没办法,先找资料开始进一步了解下这个不熟悉的东西 . 一个是 MP ,一个

AVMP是封装好的,用起来非常简单,但是自定义样式就基本不可能了。AVPlayer存在于AVFundation中,更接近

于底层,所以灵活性更强大,废话不多说,咱们先简单写个Demo看下他的工作原理,然后模仿网易新闻写个界面出

来,这里用到了一个封装的框架,如果不熟悉内部原理的同学可以先看看我写的第一个Demo,基本所有逻辑都有。


这里容我啰嗦一句:

开发中,单纯的使用AVPlayer类是无法播放视频的,需要将视频层添加到AVPLayerLayer层,这样视频才能显示出

来,Layer的定义方式有两种,一种是下面这种直接使用PlayerLayer,还有一个就是自己做一个View,然后把他自身的Layer改成playerLayer

第一种方式:

  1. self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];  
  2. self.playerLayer.videoGravity     = AVLayerVideoGravityResizeAspect;  
  3. self.playerLayer.frame = self.view.bounds;  
  4. [self.view.layer addSublayer:self.playerLayer];  


第二种方式:

  1. //修改当前view的 layer的 class  
  2. +(Class)layerClass  
  3. {  
  4.     //AVPlayerLayer  
  5.     return [AVPlayerLayer class];  
  6. }  

不BB了,直接看图说话,先看看最终的效果图   

      


只能上传2M的东东,这视频一帧一帧消耗太快了,都不敢多录了,各位大爷将就着看吧。。。。。。

不要来打我,不然我让我表哥打死你     


先简单介绍下AVPlayer的用法

很多朋友应该和我一样,一开始接触视频的时候都不知道用什么东东来写,如果是大神

就直接下载Demo吧。小白来介绍下,我也第一次用

第一:初始化播放器

  1. // 初始化播放器item  
  2.     self.playerItem = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:@"http://flv2.bn.netease.com/videolib3/1608/30/zPuaL7429/SD/zPuaL7429-mobile.mp4"]];  
  3.     self.player = [[AVPlayer alloc] initWithPlayerItem:self.playerItem];  
  4.     // 初始化播放器的Layer  
  5.     self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];  
  6.     // layer的frame  
  7.     self.playerLayer.frame = self.backView.bounds;  
  8.     // layer的填充属性 和UIImageView的填充属性类似  
  9.     // AVLayerVideoGravityResizeAspect 等比例拉伸,会留白  
  10.     // AVLayerVideoGravityResizeAspectFill // 等比例拉伸,会裁剪  
  11.     // AVLayerVideoGravityResize // 保持原有大小拉伸  
  12.     self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;  
  13.     // 把Layer加到底部View上  
  14.     [self.backView.layer insertSublayer:self.playerLayer atIndex:0];  

第二:给播放器加监听以及屏幕旋转的通知

  1. // 监听播放器状态变化  
  2.     [self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];  
  3.     // 监听缓存进去,就是大家所看到的一开始进去底部灰色的View会迅速加载  
  4.     [self.playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];  
  5.       
  6.       
  7.     //旋转屏幕通知  
  8.     [[NSNotificationCenter defaultCenter] addObserver:self  
  9.                                              selector:@selector(onDeviceOrientationChange)  
  10.                                                  name:UIDeviceOrientationDidChangeNotification  
  11.                                                object:nil  
  12.      ];  

第三步:实现KVO的监听方法

  1. // 监听播放器的变化属性  
  2. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(voidvoid *)context  
  3. {  
  4.     if ([keyPath isEqualToString:@"status"])  
  5.     {  
  6.          AVPlayerItemStatus statues = [change[NSKeyValueChangeNewKey] integerValue];  
  7.         switch (statues) {  
  8.                 // 监听到这个属性的时候,理论上视频就可以进行播放了  
  9.             case AVPlayerItemStatusReadyToPlay:  
  10.                   
  11.                 // 最大值直接用sec,以前都是  
  12.                 // CMTimeMake(帧数(slider.value * timeScale), 帧/sec)  
  13.                 self.slider.maximumValue = CMTimeGetSeconds(self.playerItem.duration);  
  14.                 [self initTimer];  
  15.                 // 启动定时器 5秒自动隐藏  
  16.                 if (!self.autoDismissTimer)  
  17.                 {  
  18.                     self.autoDismissTimer = [NSTimer timerWithTimeInterval:8.0 target:self selector:@selector(autoDismissView:) userInfo:nil repeats:YES];  
  19.                     [[NSRunLoop currentRunLoop] addTimer:self.autoDismissTimer forMode:NSDefaultRunLoopMode];  
  20.                 }  
  21.                 break;  
  22.                   
  23.             case AVPlayerItemStatusUnknown:  
  24.                   
  25.                   
  26.                   
  27.                 break;  
  28.                 // 这个就是不能播放喽,加载失败了  
  29.             case AVPlayerItemStatusFailed:  
  30.                   
  31.                 // 这时可以通过`self.player.error.description`属性来找出具体的原因  
  32.                   
  33.                 break;  
  34.                   
  35.             default:  
  36.                 break;  
  37.         }  
  38.     }  
  39.     else if ([keyPath isEqualToString:@"loadedTimeRanges"]) // 监听缓存进度的属性  
  40.     {  
  41.         // 计算缓存进度  
  42.         NSTimeInterval timeInterval = [self availableDuration];  
  43.         // 获取总长度  
  44.         CMTime duration = self.playerItem.duration;  
  45.           
  46.         CGFloat durationTime = CMTimeGetSeconds(duration);  
  47.         // 监听到了给进度条赋值  
  48.         [self.progressView setProgress:timeInterval / durationTime animated:NO];  
  49.     }  
  50. }  


AVPlayerItemStatusReadyToPlay

AVPlayerItemStatusFailed

这两个属性还比较好理解,是个人都知道,但是这个是什么鬼

AVPlayerItemStatusUnknown

内部是这么解释的

Indicates that the status of the player item is not yet known because it has not tried to load new media resources

for playback.

fk u 我英语不好,看不懂啊,估计是playerItem这个视频资源对象挂了,识别不了,暂时不知道怎么处理

      



第四步:调用Player的方法观察时间变化更新播放进度

来个官方的说话,显得我比较牛B



  1. // 调用plaer的对象进行UI更新  
  2. - (void)initTimer  
  3. {  
  4.     // player的定时器  
  5.     __weak typeof(self)weakSelf = self;  
  6.     // 每秒更新一次UI Slider  
  7.     [self.player addPeriodicTimeObserverForInterval:CMTimeMake(11) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {  
  8.           
  9.         // 当前时间  
  10.         CGFloat nowTime = CMTimeGetSeconds(weakSelf.playerItem.currentTime);  
  11.         // 总时间  
  12.         CGFloat duration = CMTimeGetSeconds(weakSelf.playerItem.duration);  
  13.         // sec 转换成时间点  
  14.         weakSelf.nowLabel.text = [weakSelf convertToTime:nowTime];  
  15.         weakSelf.remainLabel.text = [weakSelf convertToTime:(duration - nowTime)];  
  16.           
  17.         // 不是拖拽中的话更新UI  
  18.         if (!weakSelf.isDragSlider)  
  19.         {  
  20.             weakSelf.slider.value = CMTimeGetSeconds(weakSelf.playerItem.currentTime);  
  21.         }  
  22.           
  23.     }];  
  24. }  
  1. // sec 转换成指定的格式  
  2. - (NSString *)convertToTime:(CGFloat)time  
  3. {  
  4.     // 初始化格式对象  
  5.     NSDateFormatter *fotmmatter = [[NSDateFormatter alloc] init];  
  6.     // 根据是否大于1H,进行格式赋值  
  7.     if (time >= 3600)  
  8.     {  
  9.         [fotmmatter setDateFormat:@"HH:mm:ss"];  
  10.     }  
  11.     else  
  12.     {  
  13.         [fotmmatter setDateFormat:@"mm:ss"];  
  14.     }  
  15.     // 秒数转换成NSDate类型  
  16.     NSDate *date = [NSDate dateWithTimeIntervalSince1970:time];  
  17.     // date转字符串  
  18.     return [fotmmatter stringFromDate:date];  
  19. }  

第五步:给背景View加个手势,点击的时候让title和时间进度条消失或者几秒钟自动消失

  1. // 启动定时器 5秒自动隐藏  
  2.                 // 咱们这种初始化定时器的方式需要自己手动加到runloop上  
  3.                 // scheduledTimerWithTimeInterval用这个的时候就不需要手动加到runloop中  
  4.                 if (!self.autoDismissTimer)  
  5.                 {  
  6.                     self.autoDismissTimer = [NSTimer timerWithTimeInterval:8.0 target:self selector:@selector(autoDismissView:) userInfo:nil repeats:YES];  
  7.                     [[NSRunLoop currentRunLoop] addTimer:self.autoDismissTimer forMode:NSDefaultRunLoopMode];  
  8.                 }  
  1. #pragma mark - 自动隐藏bottom和top  
  2. - (void)autoDismissView:(NSTimer *)timer  
  3. {  
  4.     // player的属性rate  
  5.     /* indicates the current rate of playback; 0.0 means "stopped", 1.0 means "play at the natural rate of the current item" */  
  6.     if (self.player.rate == 0)  
  7.     {  
  8.         // 暂停状态就不隐藏  
  9.     }  
  10.     else if (self.player.rate == 1)  
  11.     {  
  12.         if (self.bottomView.alpha == 1)  
  13.         {  
  14.             [UIView animateWithDuration:1.0 animations:^{  
  15.                  
  16.                 self.bottomView.alpha = 0;  
  17.                 self.topView.alpha = 0;  
  18.                   
  19.             }];  
  20.         }  
  21.     }  
  22. }  

第六步:来个全屏小屏幕切换示例

其实切换的时候就是把只之前的Layer移除,然后重新布局,加到KeyWindow中去

  1. // 全屏显示  
  2. -(void)toFullScreenWithInterfaceOrientation:(UIInterfaceOrientation )interfaceOrientation{  
  3.     // 先移除之前的  
  4.     [self.backView removeFromSuperview];  
  5.     // 初始化  
  6.     self.backView.transform = CGAffineTransformIdentity;  
  7.     if (interfaceOrientation==UIInterfaceOrientationLandscapeLeft) {  
  8.         self.backView.transform = CGAffineTransformMakeRotation(-M_PI_2);  
  9.     }else if(interfaceOrientation==UIInterfaceOrientationLandscapeRight){  
  10.         self.backView.transform = CGAffineTransformMakeRotation(M_PI_2);  
  11.     }  
  12.     // BackView的frame能全屏  
  13.     self.backView.frame = CGRectMake(00, kScreenWidth, kScreenHeight);  
  14.     // layer的方向宽和高对调  
  15.     self.playerLayer.frame = CGRectMake(00, kScreenHeight, kScreenWidth);  
  16.       
  17.     // remark 约束  
  18.     [self.bottomView mas_remakeConstraints:^(MASConstraintMaker *make) {  
  19.         make.height.mas_equalTo(50);  
  20.         make.top.mas_equalTo(kScreenWidth-50);  
  21.         make.left.equalTo(self.backView).with.offset(0);  
  22.         make.width.mas_equalTo(kScreenHeight);  
  23.     }];  
  24.       
  25.     [self.topView mas_remakeConstraints:^(MASConstraintMaker *make) {  
  26.         make.height.mas_equalTo(50);  
  27.         make.left.equalTo(self.backView).with.offset(0);  
  28.         make.width.mas_equalTo(kScreenHeight);  
  29.     }];  
  30.       
  31.     [self.closeButton mas_remakeConstraints:^(MASConstraintMaker *make) {  
  32.         make.left.equalTo(self.backView).with.offset(5);  
  33.         make.height.mas_equalTo(30);  
  34.         make.width.mas_equalTo(30);  
  35.         make.top.equalTo(self.backView).with.offset(10);  
  36.           
  37.     }];  
  38.   
  39.     [self.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {  
  40.         make.left.equalTo(self.topView).with.offset(45);  
  41.         make.right.equalTo(self.topView).with.offset(-45);  
  42.         make.center.equalTo(self.topView);  
  43.         make.top.equalTo(self.topView).with.offset(0);  
  44.     }];  
  45.       
  46.     [self.nowLabel mas_remakeConstraints:^(MASConstraintMaker *make) {  
  47.         make.left.equalTo(self.slider.mas_left).with.offset(0);  
  48.         make.top.equalTo(self.slider.mas_bottom).with.offset(0);  
  49.         make.size.mas_equalTo(CGSizeMake(10020));  
  50.     }];  
  51.       
  52.     [self.remainLabel mas_remakeConstraints:^(MASConstraintMaker *make) {  
  53.         make.right.equalTo(self.slider.mas_right).with.offset(0);  
  54.         make.top.equalTo(self.slider.mas_bottom).with.offset(0);  
  55.         make.size.mas_equalTo(CGSizeMake(10020));  
  56.     }];  
  57.     // 加到window上面  
  58.     [[UIApplication sharedApplication].keyWindow addSubview:self.backView];  
  59. }  
  60.   
  61.   
  62. // 缩小到cell  
  63. -(void)toCell{  
  64.     // 先移除  
  65.     [self.backView removeFromSuperview];  
  66.       
  67.     __weak typeof(self)weakSelf = self;  
  68.     [UIView animateWithDuration:0.5f animations:^{  
  69.         weakSelf.backView.transform = CGAffineTransformIdentity;  
  70.         weakSelf.backView.frame = CGRectMake(080, kScreenWidth, kScreenHeight / 2.5);  
  71.         weakSelf.playerLayer.frame =  weakSelf.backView.bounds;  
  72.         // 再添加到View上  
  73.         [weakSelf.view addSubview:weakSelf.backView];  
  74.           
  75.         // remark约束  
  76.         [self.bottomView mas_remakeConstraints:^(MASConstraintMaker *make) {  
  77.             make.left.equalTo(weakSelf.backView).with.offset(0);  
  78.             make.right.equalTo(weakSelf.backView).with.offset(0);  
  79.             make.height.mas_equalTo(50);  
  80.             make.bottom.equalTo(weakSelf.backView).with.offset(0);  
  81.         }];  
  82.         [self.topView mas_remakeConstraints:^(MASConstraintMaker *make) {  
  83.             make.left.equalTo(weakSelf.backView).with.offset(0);  
  84.             make.right.equalTo(weakSelf.backView).with.offset(0);  
  85.             make.height.mas_equalTo(50);  
  86.             make.top.equalTo(weakSelf.backView).with.offset(0);  
  87.         }];  
  88.           
  89.         [self.closeButton mas_makeConstraints:^(MASConstraintMaker *make) {  
  90.             make.left.equalTo(weakSelf.backView).with.offset(5);  
  91.             make.centerY.equalTo(weakSelf.topView);  
  92.             make.size.mas_equalTo(CGSizeMake(3030));  
  93.         }];  
  94.     }completion:^(BOOL finished) {  
  95.           
  96.     }];  
  97. }  

基本逻辑差不多介绍完了,效果就这样的




  



下面咱们试着写个网易播放视频的Demo,在tableView中使用下,效果图已经在最上面了

这里无非多了几个属性

@property (nonatomic,strong)NSIndexPath *currentIndexPath; // 当前播放的cell

@property (nonatomic,assign)BOOL isSmallScreen; //是否放置在window

@property(nonatomic,strong)ViedoTableViewCell *currentCell; // 当前cell


分析1:全屏小屏切换的时候回到指定的cell,那么先点击播放记录位置

1.第一种cell播放:Layer是加载到cell上的背景图片区域的 滚动的时候要记录当前cell

2.第二种全屏播放:Layer是加载到Window上的 frame全屏

3.第三种小窗播放:它其实就是全屏播放的一个特例,也是加载到Window上的,frame自定义

其实不同状态的切换无非就是Layer所在View的位置不停切换

下面这个方法就是记录当前播放的cell下标

  1. #pragma mark - 播放器播放  
  2.   
  3. - (void)startPlayVideo:(UIButton *)sender  
  4. {  
  5.     // 获取当前的indexpath  
  6.     self.currentIndexPath = [NSIndexPath indexPathForRow:sender.tag inSection:0];  
  7.       
  8.     // iOS 7 和 8 以上获取cell的方式不同  
  9.     if ([UIDevice currentDevice].systemVersion.floatValue>=8||[UIDevice currentDevice].systemVersion.floatValue<7) {  
  10.         self.currentCell = (ViedoTableViewCell *)sender.superview.superview;  
  11.     }else{//ios7系统 UITableViewCell上多了一个层级UITableViewCellScrollView  
  12.         self.currentCell = (ViedoTableViewCell *)sender.superview.superview.subviews;  
  13.     }  
  14.     ViedoModel *model = [self.viedoLists objectAtIndex:sender.tag];  
  15.       
  16.     // 小窗口的时候点击播放另一个 先移除掉  
  17.     if (self.isSmallScreen) {  
  18.         [self releaseWMPlayer];  
  19.         self.isSmallScreen = NO;  
  20.           
  21.     }  
  22.     // 当有上一个在播放的时候 点击 就先release  
  23.     if (self.wmPlayer) {  
  24.         [self releaseWMPlayer];  
  25.         self.wmPlayer = [[WMPlayer alloc]initWithFrame:self.currentCell.mainImageView.bounds];  
  26.         self.wmPlayer.delegate = self;  
  27.         self.wmPlayer.closeBtnStyle = CloseBtnStyleClose;  
  28.         self.wmPlayer.URLString = model.mp4URL;  
  29.         self.wmPlayer.titleLabel.text = model.title;  
  30.         //        [wmPlayer play];  
  31.     }else{  
  32.         // 当没有一个在播放的时候  
  33.         self.wmPlayer = [[WMPlayer alloc]initWithFrame:self.currentCell.mainImageView.bounds];  
  34.         self.wmPlayer.delegate = self;  
  35.         self.wmPlayer.closeBtnStyle = CloseBtnStyleClose;  
  36.         self.wmPlayer.titleLabel.text = model.title;  
  37.         self.wmPlayer.URLString = model.mp4URL;  
  38.     }  
  39.     // 把播放器加到当前cell的imageView上面  
  40.     [self.currentCell.mainImageView addSubview:self.wmPlayer];  
  41.     [self.currentCell.mainImageView bringSubviewToFront:self.wmPlayer];  
  42.     [self.currentCell.playButton.superview sendSubviewToBack:self.currentCell.playButton];  
  43.     [self.tableView reloadData];  
  44. }  

分析2:上下滚动的时候根据坐标切换cell显示还是小窗显示

  1. #pragma mark scrollView delegate  
  2. - (void)scrollViewDidScroll:(UIScrollView *)scrollView  
  3. {  
  4.     if(scrollView ==self.tableView){  
  5.         if (self.wmPlayer==nil) {  
  6.             return;  
  7.         }  
  8.           
  9.         if (self.wmPlayer.superview) {  
  10.             // 当前cell在tableView中的frame  
  11. //            (lldb) po rectInTableView  
  12. //            (origin = (x = 0, y = 0), size = (width = 375, height = 300))  
  13.             CGRect rectInTableView = [self.tableView rectForRowAtIndexPath:self.currentIndexPath];  
  14.             // 把当前的frame从tableView转换到屏幕View上面去  
  15. //            (lldb) po rectInSuperview  
  16. //            (origin = (x = 0, y = 61), size = (width = 375, height = 300))  
  17.             CGRect rectInSuperview = [self.tableView convertRect:rectInTableView toView:[self.tableView superview]];  
  18.             NSLog(@"Y轴变化:%lf,currentCell:%lf",rectInSuperview.origin.y,self.currentCell.mainImageView.frame.size.height);  
  19.             // 当网上移出屏幕的时候或者往下移出屏幕的时候,根据逻辑是否加载到小窗上来  
  20.             if (rectInSuperview.origin.y<-self.currentCell.mainImageView.frame.size.height ||rectInSuperview.origin.y>kScreenHeight-kNavbarHeight-kTabBarHeight) {//往上拖动  
  21.                 // 如果已经小屏幕显示了,就不做任何操作  
  22.                 if ([[UIApplication sharedApplication].keyWindow.subviews containsObject:self.wmPlayer]&&self.isSmallScreen) {  
  23.                     self.isSmallScreen = YES;  
  24.                 }else{  
  25.                     //放widow上,小屏显示 这里的逻辑和展示到全屏是一样的道理,只是位置和frame自己定义就好了,想放哪就放哪  
  26.                     [self toSmallScreen];  
  27.                 }  
  28.                   
  29.             }else{  
  30.                 // 如果已经在cell里面了,那么就不做任何操作  
  31.                 if ([self.currentCell.mainImageView.subviews containsObject:self.wmPlayer]) {  
  32.                       
  33.                 }else{  
  34.                     // 如果进入屏幕,而且未在cell上,那么动画回currentCell  
  35.                     [self toCell];  
  36.                 }  
  37.             }  
  38.         }  
  39.           
  40.     }  
  41. }  

  1. // 滚动的时候小屏幕,放window上显示  
  2. -(void)toSmallScreen{  
  3.     //放widow上  
  4.     [self.wmPlayer removeFromSuperview];  
  5.     __weak typeof(self)weakSelf = self;  
  6.     [UIView animateWithDuration:0.5f animations:^{  
  7.        weakSelf.wmPlayer.transform = CGAffineTransformIdentity;  
  8.         // 设置window上的位置  
  9.         weakSelf.wmPlayer.frame = CGRectMake(kScreenWidth/2,kScreenHeight-kTabBarHeight + 40 -(kScreenWidth/2)*0.75, kScreenWidth/2, (kScreenWidth/2)*0.75);  
  10.         weakSelf.wmPlayer.playerLayer.frame =  weakSelf.wmPlayer.bounds;  
  11.         // 下面就是更新布局的代码,此处省略了,需要的去下载Demo看看  

分析3:用MJRefresh做个JD的加载动画(随便做的,大家随便感受下)



  1. MKJRefreshHeader * header = [MKJRefreshHeader headerWithRefreshingTarget:self refreshingAction:@selector(refreshData)];  
  2.     header.stateLabel.hidden = YES;  
  3.     header.lastUpdatedTimeLabel.hidden = YES;  
  4.     header.mj_h = 80;  
  5.     self.tableView.mj_header = header;  

    这是JD的加载动画View以及重写的MJHeader文件


这里简单的写个重写的方法示例,具体需要看的大家去下载Demo

  1. - (void)setState:(MJRefreshState)state  
  2. {  
  3.     MJRefreshCheckState  
  4.     // 根据状态做事情  
  5.     // 刷新完毕  
  6.     if (state == MJRefreshStateIdle) {  
  7.         if (oldState == MJRefreshStateRefreshing) {  
  8.             self.arrowView.transform = CGAffineTransformIdentity;  
  9.               
  10.             [UIView animateWithDuration:MJRefreshSlowAnimationDuration animations:^{  
  11.                 self.loadingView1.alpha = 0.0;  
  12.             } completion:^(BOOL finished) {  
  13.                 // 如果执行完动画发现不是idle状态,就直接返回,进入其他状态  
  14.                 if (self.state != MJRefreshStateIdle) return;  
  15.                   
  16.                 self.loadingView1.alpha = 1.0;  
  17.                 [self.loadingView1 endRefresing];  
  18.                 self.arrowView.hidden = NO;  
  19.             }];  
  20.         } else { // 拉倒即将刷新的时候,又往回缩,不进行刷新  
  21.             [self.loadingView1 endRefresingDown];  
  22.             self.arrowView.hidden = NO;  
  23.             [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{  
  24.                 self.arrowView.transform = CGAffineTransformIdentity;  
  25.             }];  
  26.         }  
  27.     } else if (state == MJRefreshStatePulling) { // 继续往下拉的时候  
  28.         [self.loadingView1 refreing];  
  29.         NSLog(@"连接点");  
  30.         self.arrowView.hidden = NO;  
  31.         [UIView animateWithDuration:MJRefreshFastAnimationDuration animations:^{  
  32.             self.arrowView.transform = CGAffineTransformMakeRotation(0.000001 - M_PI);  
  33.         }];  
  34.     } else if (state == MJRefreshStateRefreshing) { // 刷新  
  35.         self.loadingView1.alpha = 1.0// 防止refreshing -> idle的动画完毕动作没有被执行  
  36.         [self.loadingView1 refreing];  
  37.         self.arrowView.hidden = YES;  
  38.     }  
  39. }  

尼玛啊,一口气写了那么多,语文水平还没及格的我真的感觉身体被掏空了



篇幅有点多了,感觉没必要什么都写出来,需要的同学去研究下Demo吧,感谢看到这

里的小伙伴,你们都是好人,好人一生平安啊,要不再点个赞???!!!




简单Demo示例地址:点击打开简单Demo链接


类似网易视频播放最终Demo地址:点击打开网易Demo链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值