开源 Objective-C IOS 应用开发(十九)视频的播放

文章的目的为了记录使用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 实现文件分析

  1. 初始化函数

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) // 设置播放器并开始播放 }

相关推荐
likeshop 好像科技41 分钟前
新手学习AI智能体Agent逻辑设计的指引
人工智能·学习·开源·github
豆豆1 小时前
企业网站模板 开源企业网站模板 网页模板源码 整站网站源码
开源·cms·源码·建站系统·网站源码·网页源码·模板源码
嗝o゚2 小时前
开源鸿蒙 Flutter 应用包瘦身实战
flutter·华为·开源·harmonyos
中冕—霍格沃兹软件开发测试2 小时前
Git版本控制在测试项目管理中的应用
人工智能·git·科技·开源·appium·bug
岁月向前3 小时前
Jenkins实现iOS自动化打包
ios
武汉唯众智创5 小时前
高职510220开源技术与应用专业产教协同育人解决方案
开源·开源技术·开源技术与应用专业·开源技术与应用·开源开发·开源应用·开源技术与应用实训室
是毛毛吧5 小时前
豆包风波后的破局者:智谱 AutoGLM 让“AI 手机”走向公共基建
人工智能·智能手机·开源·github·开源软件
TTGGGFF6 小时前
开源项目分享:Gitee热榜项目 2025年12月第二周 周榜
gitee·开源
weixin_429690727 小时前
# 数字人系统开发:如何选择可靠的开源方案在人工智能和虚
人工智能·python·开源
中冕—霍格沃兹软件开发测试7 小时前
用户体验测试:功能与界面并重
人工智能·科技·开源·appium·bug·ux