9 悬浮窗

9.1 概述

悬浮窗功能指的是支持播放器在一个可拖动位置、可改变大小、视图层级位于最顶层的窗口进行播放。我们在 demo 中提供了两种悬浮窗出现的交互,一种是当页面滚动到(固定在页面顶部的)播放器底部消失时,出现悬浮小窗并继续播放视频,大播放器出现时,悬浮窗消失;另一种是点击播放器皮肤上的悬浮窗按钮,导航回上个页面,视频在悬浮窗上继续播放。

9.2 快速集成

9.2.1 SDK 版本

悬浮窗功能不涉及 SDK 的改动,使用该功能无需升级到特定的 SDK 版本,不过开发该功能时使用的 SDK 版本是 2.8.0,建议不要使用版本落后太大的版本,以免产生不必要的问题,该功能的测试均基于 v2.8.0 的 SDK。

9.2.2 开源代码

悬浮窗功能代码全部都是开源的,位于 demo 项目的文件夹 PolyvOpenSourceModule/Floating 中,请把 demo 的代码更新到版本 2.8.0 以上,然后把这个文件夹下的代码拖到你的项目中。该文件夹下的文件目录如下:

├── Floating

​ ├── PLVVFloatingPlayerViewController.h/m

​ ├── Resource ​ └── FloatingWindow ​ ├── PLVVFloatingWindow.h/m ​ ├── PLVVFloatingWindowSkin.h/m ​ └── PLVVFloatingWindowViewController.h/m

文件夹 Resource 下面是悬浮窗功能所需要的资源文件,FloatingWindow 是悬浮窗的所有代码,PLVVFloatingPlayerViewController 类为悬浮窗功能示例代码,建议在我们的示例代码基础上进行集成。

同时,需将 PolyvOpenSourceModule/Skin 文件夹下的代码也更新到 2.8.0 版本以上,以支持悬浮窗功能。

9.2.3 demo 示例

在视频列表页面添加以下头文件:

#import "PLVVFloatingPlayerViewController.h"
#import "PLVVFloatingWindow.h"

PLVVFloatingPlayerViewController 类有两个初始化方法:

  • -initWithPlayer: 当前已出现悬浮窗且播放视频 vid 相同时使用。

  • -initWithVid: 当前未出现悬浮窗,或悬浮窗播放视频与即将播放视频 vid 不同。

使用这两个方法进行页面跳转的代码示例如下,详细可参考 PLVAccountVideoListController 类。

PLVVFloatingWindow *window = [PLVVFloatingWindow sharedInstance];
PLVVodSkinPlayerController *player = window.contentVctrl.player;
NSString *playingVid = window.contentVctrl.vid;
NSString *vid; // 从视频列表中选中并即将播放的视频 vid
if (player && playingVid && [playingVid isEqualToString:vid]) { 
    PLVVFloatingPlayerViewController *vctrl = [[PLVVFloatingPlayerViewController alloc] initWithPlayer:player];
    [self.navigationController pushViewController:vctrl animated:YES];
} else {
    PLVVFloatingPlayerViewController *vctrl = [[PLVVFloatingPlayerViewController alloc] initWithVid:vid];
    [self.navigationController pushViewController:vctrl animated:YES];
}

9.3 悬浮窗窗口

悬浮窗窗口 PLVVFloatingWindowUIWindow 类单例,windowLevelUIWindowLevelNormal + 1,默认 hidden 属性为 YES。悬浮窗宽高比为 16:9,窗口宽度默认为屏幕宽度的 1/2。

按住悬浮窗左上角可进行缩放,窗口宽度的缩放范围为大于 160 pt,小于屏幕宽。使用以下方法可使窗口恢复初始尺寸、初始位置。

// 悬浮窗回到初始尺寸、初始位置
- (void)reset;

9.4 悬浮窗窗口控制器

PLVVFloatingWindowViewController 为悬浮窗窗口的根控制器,可通过悬浮窗口 PLVVFloatingWindow 的属性 contentVctrl 获取:

PLVVFloatingWindowViewController *vctrl = [PLVVFloatingWindow sharedInstance].contentVctrl;

控制器 PLVVFloatingWindowViewController 的只读属性 vidplayer 除了能用于获取当前悬浮窗持有的播放器(如果有的话)以及播放视频的 vid,还可用于判断当前悬浮窗是否正在播放视频,没有播放视频时这两个属性值均为 $nil$。

// 悬浮窗正在播放视频 vid,悬浮窗没有播放视频时为 nil,默认值为 nil
@property (nonatomic, strong, readonly) NSString * _Nullable vid;

// 悬浮窗持有的视频播放器,悬浮窗没有播放视频时为 nil,默认值为 nil
@property (nonatomic, strong, readonly) PLVVodSkinPlayerController * _Nullable player;

以下方法用于把页面上的播放器置于悬浮窗上,使用示例详见类 PLVVFloatingPlayerViewController

// vctrl 为持有大播放器的页面,当退出当前播放页面进行悬浮窗播放时 vctrl 为 nil
- (void)addPlayer:(PLVVodSkinPlayerController *)player partnerViewController:(id<PLVVFloatingWindowProtocol> _Nullable)vctrl;

其中,PLVVFloatingWindowProtocol 规定了使用悬浮窗的页面需要遵循的协议。

@protocol PLVVFloatingWindowProtocol <NSObject>

// 悬浮窗【exchange】按钮响应回调
- (void)exchangePlayer;

@end

以下方法用于移除或销毁悬浮窗口播放器:

// 销毁并移除悬浮窗口持有的播放器
- (void)destroyPlayer;

// 将悬浮窗上的播放器移走,但是不销毁
- (void)removePlayer;

控制器 PLVVFloatingWindowViewController 还定义了以下广播事件,在进入/退出悬浮窗模式时会发送广播到所有 observer

// 进入悬浮窗模式广播事件
extern NSString *PLVVFloatingWindowEnterNotification;
// 退出悬浮窗模式广播事件
extern NSString *PLVVFloatingWindowLeaveNotification;

9.5 悬浮窗窗口皮肤

悬浮窗播放器的皮肤 PLVVFloatingWindowSkin 由悬浮窗口的根控制器 PLVVFloatingWindowViewController 持有,包含了【关闭悬浮窗】按钮、【Exchange】 按钮、【播放/暂停】按钮。悬浮窗播放器皮肤的持有者需遵循协议 PLVVFloatingWindowSkinProtocol 作为 delegate 以响应皮肤上的按钮:

/// 悬浮窗播放器控制按钮层响应事件回调
@protocol PLVVFloatingWindowSkinProtocol <NSObject>

- (void)tapCloseButton;

- (void)tapExchangeButton;

- (void)tapPlayButton:(BOOL)play;

@end
  
@interface PLVVFloatingWindowSkin : UIView

@property (nonatomic, weak) id<PLVVFloatingWindowSkinProtocol> delegate;

@end

此外,通过 SDK 播放器类 PLVVodPlayerViewController 的播放状态回调 playbackStateHandler,调用方法 -statusIsPlaying: 实时更新皮肤上【播放/暂停】按钮的状态:

__weak typeof(self) weakSelf = self;
self.player.playbackStateHandler = ^(PLVVodPlayerViewController *player) {
        [weakSelf.windowSkin statusIsPlaying:(player.playbackState == PLVVodPlaybackStatePlaying)];
};

self.windowSkin 为悬浮窗播放器皮肤的实例对象,self.playerPLVVodPlayerViewController 的实例对象。

Last updated