文章的目的为了记录使用Objective-C 进行IOS app 开发学习的经历。本职为嵌入式软件开发,公司安排开发app,临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。
相关链接:
开源 Objective-C IOS 应用开发(一)macOS 的使用
开源 Objective-C IOS 应用开发(二)Xcode安装
开源 Objective-C IOS 应用开发(三)第一个iPhone的APP
开源 Objective-C IOS 应用开发(四)Xcode工程文件结构
开源 Objective-C IOS 应用开发(五)iOS操作(action)和输出口(Outlet)
开源 Objective-C IOS 应用开发(六)Objective-C 和 C语言
开源 Objective-C IOS 应用开发(七)Objective-C核心代码示例
开源 Objective-C IOS 应用开发(八)常见控件UI
开源 Objective-C IOS 应用开发(九)复杂控件-tableview
开源 Objective-C IOS 应用开发(十)数据持久化--文件
开源 Objective-C IOS 应用开发(十一)数据持久化--sqlite
开源 Objective-C IOS 应用开发(十二)通讯--ble
开源 Objective-C IOS 应用开发(十三)通讯--Http访问
开源 Objective-C IOS 应用开发(十四)传感器--陀螺仪和gps
开源 Objective-C IOS 应用开发(十五)通讯--蓝牙ble扫描
开源 Objective-C IOS 应用开发(十六)Storyboard模式下的纯代码界面
开源 Objective-C IOS 应用开发(十七)CAF音频的录制
开源 Objective-C IOS 应用开发(十八)音频的播放
开源 Objective-C IOS 应用开发(十九)视频的播放
开源 Objective-C IOS 应用开发(二十)多线程处理
开源 Objective-C IOS 应用开发(二十一)自定义控件--示波器
开源 Objective-C IOS 应用开发(二十二)自定义控件--车速仪表盘
推荐链接:
开源 Arkts 鸿蒙应用 开发(一)工程文件分析-CSDN博客
开源 Arkts 鸿蒙应用 开发(二)封装库.har制作和应用-CSDN博客
开源 Arkts 鸿蒙应用 开发(三)Arkts的介绍-CSDN博客
开源 Arkts 鸿蒙应用 开发(四)布局和常用控件-CSDN博客
开源 Arkts 鸿蒙应用 开发(五)控件组成和复杂控件-CSDN博客
开源 Arkts 鸿蒙应用 开发(六)数据持久--文件和首选项存储-CSDN博客
开源 Arkts 鸿蒙应用 开发(七)数据持久--sqlite关系数据库-CSDN博客
开源 Arkts 鸿蒙应用 开发(八)多媒体--相册和相机-CSDN博客
开源 Arkts 鸿蒙应用 开发(九)通讯--tcp客户端-CSDN博客
开源 Arkts 鸿蒙应用 开发(十)通讯--Http-CSDN博客
开源 Arkts 鸿蒙应用 开发(十一)证书和包名修改-CSDN博客
开源 Arkts 鸿蒙应用 开发(十二)传感器的使用-CSDN博客
开源 Arkts 鸿蒙应用 开发(十三)音频--MP3播放_arkts avplayer播放音频 mp3-CSDN博客
开源 Arkts 鸿蒙应用 开发(十四)线程--任务池(taskpool)-CSDN博客
开源 Arkts 鸿蒙应用 开发(十五)自定义绘图控件--仪表盘-CSDN博客
开源 Arkts 鸿蒙应用 开发(十六)自定义绘图控件--波形图-CSDN博客
开源 Arkts 鸿蒙应用 开发(十七)通讯--http多文件下载-CSDN博客
开源 Arkts 鸿蒙应用 开发(十八)通讯--Ble低功耗蓝牙服务器-CSDN博客
推荐链接:
开源 java android app 开发(一)开发环境的搭建-CSDN博客
开源 java android app 开发(二)工程文件结构-CSDN博客
开源 java android app 开发(三)GUI界面布局和常用组件-CSDN博客
开源 java android app 开发(四)GUI界面重要组件-CSDN博客
开源 java android app 开发(五)文件和数据库存储-CSDN博客
开源 java android app 开发(六)多媒体使用-CSDN博客
开源 java android app 开发(七)通讯之Tcp和Http-CSDN博客
开源 java android app 开发(八)通讯之Mqtt和Ble-CSDN博客
开源 java android app 开发(九)后台之线程和服务-CSDN博客
开源 java android app 开发(十)广播机制-CSDN博客
开源 java android app 开发(十一)调试、发布-CSDN博客
开源 java android app 开发(十二)封库.aar-CSDN博客
本章内容主要是使用iphone进行视频播放。
目录:
1.手机演示
2.所有源码
3.源码分析
一、手机演示

二、所有源码
AppDelegate.h文件
#import <UIKit/UIKit.h>
@class VideoViewController;
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) VideoViewController *videoViewController;
@end
AppDelegate.m文件
#import "AppDelegate.h"
#import "VideoViewController.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 创建主窗口
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
// 创建视频播放视图控制器
self.videoViewController = [[VideoViewController alloc] init];
// 设置根视图控制器
self.window.rootViewController = self.videoViewController;
// 显示窗口
[self.window makeKeyAndVisible];
return YES;
}
@end
VideoPlayer.h文件
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface VideoPlayer : UIView
@property (strong, nonatomic) AVPlayer *player;
@property (strong, nonatomic) AVPlayerLayer *playerLayer;
@property (strong, nonatomic) UIButton *playPauseButton;
@property (strong, nonatomic) UIButton *fullscreenButton;
@property (strong, nonatomic) UIView *controlsView;
@property (strong, nonatomic) UISlider *progressSlider;
@property (strong, nonatomic) UILabel *currentTimeLabel;
@property (strong, nonatomic) UILabel *totalTimeLabel;
- (void)setupPlayerWithURL:(NSURL *)videoURL;
- (void)play;
- (void)pause;
- (void)togglePlayPause;
@end
NS_ASSUME_NONNULL_END
VideoPlayer.m文件
#import "VideoPlayer.h"
@interface VideoPlayer()
@property (strong, nonatomic) id timeObserver;
@property (assign, nonatomic) BOOL controlsHidden;
@end
@implementation VideoPlayer
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setupUI];
[self setupGestures];
}
return self;
}
- (void)setupUI {
self.backgroundColor = [UIColor blackColor];
// 创建播放器图层
self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:nil];
self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
[self.layer addSublayer:self.playerLayer];
// 创建控制界面
self.controlsView = [[UIView alloc] init];
self.controlsView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.7];
[self addSubview:self.controlsView];
// 播放/暂停按钮
self.playPauseButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.playPauseButton setTitle:@"播放" forState:UIControlStateNormal];
[self.playPauseButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[self.playPauseButton addTarget:self action:@selector(togglePlayPause) forControlEvents:UIControlEventTouchUpInside];
[self.controlsView addSubview:self.playPauseButton];
// 进度条
self.progressSlider = [[UISlider alloc] init];
self.progressSlider.minimumValue = 0.0;
[self.progressSlider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged];
[self.progressSlider addTarget:self action:@selector(sliderTouchUpInside:) forControlEvents:UIControlEventTouchUpInside];
[self.controlsView addSubview:self.progressSlider];
// 时间标签
self.currentTimeLabel = [[UILabel alloc] init];
self.currentTimeLabel.text = @"00:00";
self.currentTimeLabel.textColor = [UIColor whiteColor];
self.currentTimeLabel.font = [UIFont systemFontOfSize:12];
[self.controlsView addSubview:self.currentTimeLabel];
self.totalTimeLabel = [[UILabel alloc] init];
self.totalTimeLabel.text = @"00:00";
self.totalTimeLabel.textColor = [UIColor whiteColor];
self.totalTimeLabel.font = [UIFont systemFontOfSize:12];
[self.controlsView addSubview:self.totalTimeLabel];
// 全屏按钮
self.fullscreenButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.fullscreenButton setTitle:@"全屏" forState:UIControlStateNormal];
[self.fullscreenButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[self.controlsView addSubview:self.fullscreenButton];
self.controlsHidden = NO;
[self hideControlsAfterDelay];
}
- (void)setupGestures {
// 添加点击手势来控制界面显示/隐藏
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleControls)];
[self addGestureRecognizer:tapGesture];
}
- (void)layoutSubviews {
[super layoutSubviews];
// 设置播放器图层大小
self.playerLayer.frame = self.bounds;
// 设置控制界面布局
CGFloat controlsHeight = 60.0;
self.controlsView.frame = CGRectMake(0, self.bounds.size.height - controlsHeight,
self.bounds.size.width, controlsHeight);
CGFloat padding = 10.0;
CGFloat buttonWidth = 60.0;
self.playPauseButton.frame = CGRectMake(padding, 10, buttonWidth, 30);
self.currentTimeLabel.frame = CGRectMake(CGRectGetMaxX(self.playPauseButton.frame) + padding, 15, 40, 20);
CGFloat sliderX = CGRectGetMaxX(self.currentTimeLabel.frame) + padding;
CGFloat fullscreenButtonWidth = 50.0;
self.fullscreenButton.frame = CGRectMake(self.bounds.size.width - fullscreenButtonWidth - padding, 10,
fullscreenButtonWidth, 30);
self.totalTimeLabel.frame = CGRectMake(CGRectGetMinX(self.fullscreenButton.frame) - 40 - padding, 15, 40, 20);
CGFloat sliderWidth = CGRectGetMinX(self.totalTimeLabel.frame) - sliderX - padding;
self.progressSlider.frame = CGRectMake(sliderX, 15, sliderWidth, 20);
}
- (void)setupPlayerWithURL:(NSURL *)videoURL {
// 创建AVPlayerItem
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:videoURL];
// 创建AVPlayer
self.player = [AVPlayer playerWithPlayerItem:playerItem];
self.playerLayer.player = self.player;
// 添加时间观察者
__weak typeof(self) weakSelf = self;
self.timeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1)
queue:dispatch_get_main_queue()
usingBlock:^(CMTime time) {
[weakSelf updateProgress];
}];
// 监听播放结束
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:playerItem];
}
- (void)updateProgress {
if (self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
Float64 currentTime = CMTimeGetSeconds(self.player.currentTime);
Float64 duration = CMTimeGetSeconds(self.player.currentItem.duration);
if (isfinite(duration)) {
self.progressSlider.value = currentTime / duration;
self.currentTimeLabel.text = [self timeStringFromSeconds:currentTime];
self.totalTimeLabel.text = [self timeStringFromSeconds:duration];
}
}
}
- (NSString *)timeStringFromSeconds:(Float64)seconds {
NSInteger minutes = (NSInteger)seconds / 60;
NSInteger secs = (NSInteger)seconds % 60;
return [NSString stringWithFormat:@"%02ld:%02ld", (long)minutes, (long)secs];
}
- (void)play {
[self.player play];
[self.playPauseButton setTitle:@"暂停" forState:UIControlStateNormal];
}
- (void)pause {
[self.player pause];
[self.playPauseButton setTitle:@"播放" forState:UIControlStateNormal];
}
- (void)togglePlayPause {
if (self.player.rate == 0.0) {
[self play];
} else {
[self pause];
}
}
- (void)sliderValueChanged:(UISlider *)slider {
[self.player removeTimeObserver:self.timeObserver];
}
- (void)sliderTouchUpInside:(UISlider *)slider {
if (self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
Float64 duration = CMTimeGetSeconds(self.player.currentItem.duration);
Float64 seekTime = duration * slider.value;
[self.player seekToTime:CMTimeMakeWithSeconds(seekTime, NSEC_PER_SEC)];
// 重新添加时间观察者
__weak typeof(self) weakSelf = self;
self.timeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1)
queue:dispatch_get_main_queue()
usingBlock:^(CMTime time) {
[weakSelf updateProgress];
}];
}
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
[self.player seekToTime:kCMTimeZero];
[self pause];
}
- (void)toggleControls {
self.controlsHidden = !self.controlsHidden;
[UIView animateWithDuration:0.3 animations:^{
self.controlsView.alpha = self.controlsHidden ? 0.0 : 1.0;
}];
if (!self.controlsHidden) {
[self hideControlsAfterDelay];
}
}
- (void)hideControlsAfterDelay {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideControls) object:nil];
[self performSelector:@selector(hideControls) withObject:nil afterDelay:3.0];
}
- (void)hideControls {
[UIView animateWithDuration:0.3 animations:^{
self.controlsView.alpha = 0.0;
}];
self.controlsHidden = YES;
}
- (void)dealloc {
if (self.timeObserver) {
[self.player removeTimeObserver:self.timeObserver];
}
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end
VideoViewController.h文件
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface VideoViewController : UIViewController
@end
NS_ASSUME_NONNULL_END
VideoViewController.m文件
#import "VideoViewController.h"
#import "VideoPlayer.h"
@interface VideoViewController ()
@property (strong, nonatomic) VideoPlayer *videoPlayer;
@end
@implementation VideoViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setupUI];
[self setupVideoPlayer];
}
- (void)setupUI {
self.view.backgroundColor = [UIColor whiteColor];
// 创建标题
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.text = @"视频播放器";
titleLabel.font = [UIFont boldSystemFontOfSize:24];
titleLabel.textAlignment = NSTextAlignmentCenter;
titleLabel.frame = CGRectMake(0, 60, self.view.bounds.size.width, 40);
[self.view addSubview:titleLabel];
}
- (void)setupVideoPlayer {
// 创建视频播放器
CGFloat playerWidth = self.view.bounds.size.width - 40;
CGFloat playerHeight = playerWidth * 9.0 / 16.0; // 16:9 比例
self.videoPlayer = [[VideoPlayer alloc] initWithFrame:CGRectMake(20, 120, playerWidth, playerHeight)];
[self.view addSubview:self.videoPlayer];
// 使用本地视频文件或网络视频URL
NSURL *videoURL = nil;
// 方式1: 使用本地视频文件 (需要将视频文件添加到项目中)
NSString *localVideoPath = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"mp4"];
if (localVideoPath) {
videoURL = [NSURL fileURLWithPath:localVideoPath];
} else {
// 方式2: 使用网络视频URL
videoURL = [NSURL URLWithString:@"https://example.com/sample.mp4"];
// 方式3: 使用测试视频URL (这是一个公开的测试视频)
//videoURL = [NSURL URLWithString:@"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"];
}
if (videoURL) {
[self.videoPlayer setupPlayerWithURL:videoURL];
} else {
NSLog(@"无法找到视频文件");
}
// 添加说明标签
UILabel *instructionLabel = [[UILabel alloc] init];
instructionLabel.text = @"点击视频区域显示/隐藏控制界面";
instructionLabel.font = [UIFont systemFontOfSize:14];
instructionLabel.textAlignment = NSTextAlignmentCenter;
instructionLabel.textColor = [UIColor grayColor];
instructionLabel.frame = CGRectMake(20, CGRectGetMaxY(self.videoPlayer.frame) + 20,
self.view.bounds.size.width - 40, 30);
[self.view addSubview:instructionLabel];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// 开始播放视频
[self.videoPlayer play];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// 暂停播放视频
[self.videoPlayer pause];
}
- (BOOL)shouldAutorotate {
return YES;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
@end
info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</plist>
三、源码分析
VideoPlayer.h 头文件分析
属性声明
objectivec
@property (strong, nonatomic) AVPlayer *player; // AVFoundation播放器核心
@property (strong, nonatomic) AVPlayerLayer *playerLayer; // 视频显示图层
@property (strong, nonatomic) UIButton *playPauseButton; // 播放/暂停按钮
@property (strong, nonatomic) UIButton *fullscreenButton; // 全屏按钮(目前无功能)
@property (strong, nonatomic) UIView *controlsView; // 控制界面容器
@property (strong, nonatomic) UISlider *progressSlider; // 进度条
@property (strong, nonatomic) UILabel *currentTimeLabel; // 当前时间显示
@property (strong, nonatomic) UILabel *totalTimeLabel; // 总时间显示
公共方法
objectivec
-
(void)setupPlayerWithURL:(NSURL *)videoURL; // 初始化播放器并设置视频URL
-
(void)play; // 开始播放
-
(void)pause; // 暂停播放
-
(void)togglePlayPause; // 切换播放/暂停状态
VideoPlayer.m 实现文件分析
- 初始化函数
objectivec
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self setupUI\]; // 初始化用户界面 \[self setupGestures\]; // 设置手势识别 } return self; } 2. 界面设置函数 objectivec - (void)setupUI { // 设置黑色背景 self.backgroundColor = \[UIColor blackColor\]; // 创建播放器图层 self.playerLayer = \[AVPlayerLayer playerLayerWithPlayer:nil\]; self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect; // 保持视频比例 \[self.layer addSublayer:self.playerLayer\]; // 创建半透明控制界面 self.controlsView = \[\[UIView alloc\] init\]; self.controlsView.backgroundColor = \[UIColor colorWithWhite:0.0 alpha:0.7\]; // 创建播放/暂停按钮 self.playPauseButton = \[UIButton buttonWithType:UIButtonTypeSystem\]; \[self.playPauseButton setTitle:@"播放" forState:UIControlStateNormal\]; \[self.playPauseButton addTarget:self action:@selector(togglePlayPause) forControlEvents:UIControlEventTouchUpInside\]; // 创建进度条并绑定事件 self.progressSlider = \[\[UISlider alloc\] init\]; \[self.progressSlider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged\]; \[self.progressSlider addTarget:self action:@selector(sliderTouchUpInside:) forControlEvents:UIControlEventTouchUpInside\]; // 创建时间显示标签 self.currentTimeLabel = \[\[UILabel alloc\] init\]; self.currentTimeLabel.text = @"00:00"; self.totalTimeLabel = \[\[UILabel alloc\] init\]; self.totalTimeLabel.text = @"00:00"; // 创建全屏按钮(目前只有UI,无功能) self.fullscreenButton = \[UIButton buttonWithType:UIButtonTypeSystem\]; \[self.fullscreenButton setTitle:@"全屏" forState:UIControlStateNormal\]; // 初始化控制界面状态并设置自动隐藏 self.controlsHidden = NO; \[self hideControlsAfterDelay\]; } 3. 手势设置函数 objectivec - (void)setupGestures { // 添加点击手势用于显示/隐藏控制界面 UITapGestureRecognizer \*tapGesture = \[\[UITapGestureRecognizer alloc
initWithTarget:self
action:@selector(toggleControls)];
self addGestureRecognizer:tapGesture\]; } 4. 布局函数 objectivec - (void)layoutSubviews { \[super layoutSubviews\]; // 设置播放器图层充满整个视图 self.playerLayer.frame = self.bounds; // 设置控制界面在底部 CGFloat controlsHeight = 60.0; self.controlsView.frame = CGRectMake(0, self.bounds.size.height - controlsHeight, self.bounds.size.width, controlsHeight); // 详细布局各个控件的位置和大小 // 从左到右:播放按钮 -\> 当前时间 -\> 进度条 -\> 总时间 -\> 全屏按钮 } 5. 播放器初始化函数 objectivec - (void)setupPlayerWithURL:(NSURL \*)videoURL { // 创建播放项目 AVPlayerItem \*playerItem = \[AVPlayerItem playerItemWithURL:videoURL\]; // 创建播放器实例 self.player = \[AVPlayer playerWithPlayerItem:playerItem\]; self.playerLayer.player = self.player; // 添加时间观察者,每秒更新一次进度 __weak typeof(self) weakSelf = self; self.timeObserver = \[self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:\^(CMTime time) { \[weakSelf updateProgress\]; // 更新进度显示 }\]; // 监听播放结束通知 \[\[NSNotificationCenter defaultCenter\] addObserver:self selector:@selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem\]; } 6. 进度更新函数 objectivec - (void)updateProgress { if (self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) { // 获取当前播放时间和总时长 Float64 currentTime = CMTimeGetSeconds(self.player.currentTime); Float64 duration = CMTimeGetSeconds(self.player.currentItem.duration); if (isfinite(duration)) { // 更新进度条值 self.progressSlider.value = currentTime / duration; // 更新时间显示 self.currentTimeLabel.text = \[self timeStringFromSeconds:currentTime\]; self.totalTimeLabel.text = \[self timeStringFromSeconds:duration\]; } } } 7. 时间格式化函数 objectivec - (NSString \*)timeStringFromSeconds:(Float64)seconds { // 将秒数转换为 "分:秒" 格式 NSInteger minutes = (NSInteger)seconds / 60; NSInteger secs = (NSInteger)seconds % 60; return \[NSString stringWithFormat:@"%02ld:%02ld", (long)minutes, (long)secs\]; } 8. 播放控制函数 objectivec - (void)play { \[self.player play\]; \[self.playPauseButton setTitle:@"暂停" forState:UIControlStateNormal\]; // 更新按钮文字 } - (void)pause { \[self.player pause\]; \[self.playPauseButton setTitle:@"播放" forState:UIControlStateNormal\]; // 更新按钮文字 } - (void)togglePlayPause { // 根据当前播放状态切换 if (self.player.rate == 0.0) { \[self play\]; } else { \[self pause\]; } } 9. 进度条控制函数 objectivec - (void)sliderValueChanged:(UISlider \*)slider { // 拖动进度条时移除时间观察者,避免冲突 \[self.player removeTimeObserver:self.timeObserver\]; } - (void)sliderTouchUpInside:(UISlider \*)slider { if (self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) { // 计算要跳转的时间点 Float64 duration = CMTimeGetSeconds(self.player.currentItem.duration); Float64 seekTime = duration \* slider.value; // 执行跳转 \[self.player seekToTime:CMTimeMakeWithSeconds(seekTime, NSEC_PER_SEC)\]; // 重新添加时间观察者 __weak typeof(self) weakSelf = self; self.timeObserver = \[self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:\^(CMTime time) { \[weakSelf updateProgress\]; }\]; } } 10. 播放结束处理函数 objectivec - (void)playerItemDidReachEnd:(NSNotification \*)notification { // 播放结束时回到开头并暂停 \[self.player seekToTime:kCMTimeZero\]; \[self pause\]; } 11. 控制界面显示/隐藏函数 objectivec - (void)toggleControls { // 切换控制界面显示状态 self.controlsHidden = !self.controlsHidden; \[UIView animateWithDuration:0.3 animations:\^{ self.controlsView.alpha = self.controlsHidden ? 0.0 : 1.0; }\]; // 如果显示控制界面,设置3秒后自动隐藏 if (!self.controlsHidden) { \[self hideControlsAfterDelay\]; } } - (void)hideControlsAfterDelay { // 取消之前的隐藏请求,避免重复 \[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideControls) object:nil\]; // 3秒后执行隐藏 \[self performSelector:@selector(hideControls) withObject:nil afterDelay:3.0\]; } - (void)hideControls { // 隐藏控制界面 \[UIView animateWithDuration:0.3 animations:\^{ self.controlsView.alpha = 0.0; }\]; self.controlsHidden = YES; } 12. 清理函数 objectivec - (void)dealloc { // 移除时间观察者 if (self.timeObserver) { \[self.player removeTimeObserver:self.timeObserver\]; } // 移除通知观察者 \[\[NSNotificationCenter defaultCenter\] removeObserver:self\]; } VideoViewController 分析 主要函数 objectivec - (void)viewDidLoad { \[super viewDidLoad\]; \[self setupUI\]; // 设置界面标题 \[self setupVideoPlayer\]; // 初始化视频播放器 } - (void)setupVideoPlayer { // 创建16:9比例的视频播放器 CGFloat playerWidth = self.view.bounds.size.width - 40; CGFloat playerHeight = playerWidth \* 9.0 / 16.0; self.videoPlayer = \[\[VideoPlayer alloc\] initWithFrame:CGRectMake(20, 120, playerWidth, playerHeight)\]; \[self.view addSubview:self.videoPlayer\]; // 尝试加载视频文件(本地→网络→测试URL) // 设置播放器并开始播放 }