ios抖音demo
这个 Demo 不会是 1:1 复刻抖音的所有复杂功能,而是会包含其最核心的 “全屏沉浸式短视频播放” 和 “上下滑动切换” 体验,并辅以一些常见的交互元素,如点赞、评论、分享、用户信息等。
(图片来源网络,侵删)
我们将使用苹果官方推荐的 AVKit 框架来播放视频,并利用 SwiftUI 来构建界面,因为它能让我们用更简洁的代码实现复杂的布局和动画。
Demo 核心功能预览
- 全屏视频播放: 视频自动播放,并占据整个屏幕。
- 上下滑动切换: 向上滑动,无缝切换到下一个视频;向下滑动,返回上一个视频。
- 交互式控件: 底部有播放/暂停按钮,右侧有点赞、评论、分享按钮。
- 用户信息: 顶部显示视频作者头像和昵称。
- 音乐信息: 显示当前视频使用的背景音乐。
- 动画反馈: 点赞按钮有点赞动画和计数变化。
技术选型与架构
- 语言: Swift
- UI 框架: SwiftUI (现代、声明式,易于实现动画和复杂布局)
- 视频播放框架: AVKit (AVPlayerViewController)
- 数据源: 本地视频文件 + 模拟的 JSON 数据
- 布局: SwiftUI 的
GeometryReader和overlay来实现全屏和控件叠加。
第一步:项目准备
- 创建新项目: 在 Xcode 中创建一个新的 "iOS App" 项目,语言选择 Swift,界面选择 SwiftUI。
- 准备素材:
- 视频文件: 准备 3-5 个不同主题的短视频(
.mp4格式),拖拽到 Xcode 项目中,确保它们在 "Copy Bundle Resources" 中。 - 图片资源: 准备一些用于点赞、评论、分享、用户头像的图标,以及一个“喜欢”的填充图标,同样拖拽到项目中。
- 数据模拟: 创建一个
Video模型来存储每个视频的元数据。
- 视频文件: 准备 3-5 个不同主题的短视频(
第二步:数据模型
创建一个 Swift 文件 Video.swift,定义我们的视频数据结构。
// Video.swift
import Foundation
import AVKit
struct Video: Identifiable, Hashable {
let id = UUID()
let url: URL // 视频文件路径
let user: User
let music: Music
var isLiked: Bool = false
}
struct User: Hashable {
let id = UUID()
let name: String
let avatar: String // 图片名
}
struct Music: Hashable {
let id = UUID()
let title: String
let author: String
}
// 模拟数据
let mockVideos: [Video] = [
Video(
url: Bundle.main.url(forResource: "video1", withExtension: "mp4")!,
user: User(name: "旅行家小李", avatar: "avatar1"),
music: Music(title: "夏日微风", author: "未知艺术家")
),
Video(
url: Bundle.main.url(forResource: "video2", withExtension: "mp4")!,
user: User(name: "美食家小王", avatar: "avatar2"),
music: Music(title: "美味人生", author: "大厨")
),
Video(
url: Bundle.main.url(forResource: "video3", withExtension: "mp4")!,
user: User(name: "健身达人Alex", avatar: "avatar3"),
music: Music(title: "燃爆全场", author: "健身音乐")
)
]
第三步:主视图 - 全屏视频播放器
这是整个 Demo 的核心,我们将创建一个 VideoPlayerView,它负责管理单个视频的播放逻辑和 UI。
// VideoPlayerView.swift
import SwiftUI
import AVKit
struct VideoPlayerView: View {
let video: Video
@Binding var isPlaying: Bool
@Binding var isLiked: Bool
@State private var player: AVPlayer?
@State private var playerItem: AVPlayerItem?
var body: some View {
GeometryReader { geometry in
ZStack {
// 1. 视频播放器背景
if let player = player {
VideoPlayer(player: player)
.onAppear(perform: setupPlayer)
.onDisappear(perform: cleanupPlayer)
.gesture(
// 2. 单击控制播放/暂停
TapGesture()
.onEnded {
isPlaying.toggle()
isPlaying ? player.play() : player.pause()
}
)
.overlay(
// 3. 右侧交互按钮
VStack {
Spacer()
HStack {
Spacer()
// 点赞按钮
LikeButtonView(isLiked: $isLiked)
.padding(.trailing, 30)
// 评论按钮
ActionButtonView(imageName: "comment", text: "2.3w")
.padding(.trailing, 20)
// 分享按钮
ActionButtonView(imageName: "share", text: "分享")
.padding(.trailing, 20)
}
.padding(.bottom, 100)
}
)
} else {
// 加载状态
Color.black
.overlay(ProgressView())
}
// 4. 顶部用户信息
VStack {
HStack {
Image(video.user.avatar)
.resizable()
.scaledToFill()
.frame(width: 40, height: 40)
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 1))
Text(video.user.name)
.foregroundColor(.white)
.font(.headline)
Spacer()
// 关注按钮
Button(action: {
// 关注逻辑
}) {
Text("关注")
.font(.subheadline)
.fontWeight(.semibold)
.padding(.horizontal, 16)
.padding(.vertical, 6)
.background(Color.red)
.foregroundColor(.white)
.cornerRadius(20)
}
}
.padding(.horizontal, 16)
.padding(.top, 54) // 状态栏高度
Spacer()
}
}
}
}
private func setupPlayer() {
playerItem = AVPlayerItem(url: video.url)
player = AVPlayer(playerItem: playerItem)
// 自动播放
player?.play()
isPlaying = true
// 循环播放
NotificationCenter.default.addObserver(
forName: .AVPlayerItemDidPlayToEndTime,
object: playerItem,
queue: .main
) { _ in
player?.seek(to: .zero)
player?.play()
}
}
private func cleanupPlayer() {
player?.pause()
player = nil
playerItem = nil
NotificationCenter.default.removeObserver(self)
}
}
// 点赞按钮组件
struct LikeButtonView: View {
@Binding var isLiked: Bool
@State private var animationAmount: Double = 1
var body: some View {
Button(action: {
withAnimation(.spring(response: 0.3, dampingFraction: 0.6)) {
animationAmount = 1.5
}
withAnimation(.spring(response: 0.3, dampingFraction: 0.6).delay(0.1)) {
animationAmount = 1.0
}
isLiked.toggle()
}) {
VStack {
Image(systemName: isLiked ? "heart.fill" : "heart")
.font(.system(size: 40))
.foregroundColor(isLiked ? .red : .white)
.scaleEffect(animationAmount)
Text(isLiked ? "已喜欢" : "喜欢")
.foregroundColor(.white)
.font(.caption)
}
}
}
}
// 通用交互按钮组件
struct ActionButtonView: View {
let imageName: String
let text: String
var body: some View {
VStack {
Image(systemName: imageName)
.font(.system(size: 30))
.foregroundColor(.white)
Text(text)
.foregroundColor(.white)
.font(.caption)
}
}
}
第四步:主容器 - 滑动切换
我们需要一个容器来管理 VideoPlayerView,并实现滑动切换的逻辑,我们将使用 TabView 并自定义其 .page 样式。
(图片来源网络,侵删)
// ContentView.swift
import SwiftUI
struct ContentView: View {
// 使用模拟数据
@State private var videos = mockVideos
@State private var currentIndex: Int = 0
var body: some View {
// TabView 的 page 样式是实现抖音式滑动切换的关键
TabView(selection: $currentIndex) {
ForEach(videos.indices, id: \.self) { index in
VideoPlayerView(
video: videos[index],
isPlaying: Binding(
get: { currentIndex == index && videos[index].isLiked == false }, // 只有当前视频且未点赞时才播放
set: { _ in }
),
isLiked: $videos[index].isLiked
)
.tag(index)
// 向上滑动手势,用于切换到下一个视频
.gesture(
DragGesture()
.onEnded { value in
if value.translation.y < -50 { // 向上滑动超过50个点
withAnimation {
if currentIndex < videos.count - 1 {
currentIndex += 1
}
}
}
}
)
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) // 隐藏默认的页码指示器
.background(Color.black) // 设置背景色
}
}
第五步:运行与测试
- 确保素材正确: 在 Xcode 中检查你的视频文件和图片文件是否已正确添加到项目中。
- 设置模拟器: 在模拟器上运行 App,最好使用一个屏幕尺寸较大的模拟器(如 iPhone 14 Pro Max)来获得更好的体验。
- 测试功能:
- 自动播放: App 启动后,第一个视频应该自动开始播放。
- 暂停/播放: 点击屏幕任意位置,视频应该暂停,再次点击,视频应该继续播放。
- 滑动切换: 在屏幕上从下往上快速滑动,应该能切换到下一个视频,从上往下滑动,则返回上一个视频。
- 点赞: 点击右侧的爱心图标,它应该变成红色填充,并有放大动画,再次点击,变回空心。
- UI 元素: 检查顶部的用户头像、昵称和关注按钮是否显示正常。
进阶与扩展思路
这个 Demo 已经有了抖音的核心骨架,你可以基于此进行扩展:
- 网络请求: 使用
URLSession从服务器获取视频列表和元数据,而不是使用本地模拟数据。 - 缓存机制: 使用
AVAssetCache或第三方库(如 Kingfisher)来缓存视频,避免重复下载,提升用户体验。 - 无限滚动: 当滑动到最后一个视频时,自动加载更多视频。
- 更复杂的 UI:
- 评论区: 点击评论按钮,可以弹出一个半透明的评论列表视图。
- 搜索页面: 添加一个搜索页面,可以搜索用户或音乐。
- 个人主页: 点击用户头像,可以跳转到该用户的个人主页。
- 视频拍摄: 使用
AVFoundation的AVCaptureSession来实现拍摄视频的功能。 - 音效和滤镜: 在拍摄或播放时添加背景音效和滤镜效果。
- 数据持久化: 使用
CoreData或UserDefaults来保存用户的点赞记录、关注列表等。
这个 Demo 为你提供了一个坚实的起点,它清晰地展示了如何用 SwiftUI 和 AVKit 构建一个类似抖音的沉浸式视频体验。
文章版权及转载声明
作者:99ANYc3cd6本文地址:https://chumoping.net/post/7989.html发布于 01-07
文章转载或复制请以超链接形式并注明出处初梦运营网


