SwiftUI案例:3D旋转图片播放器
效果
目标
- 实现多张图片的3D切换查看功能
外观配置
任选 7 张任意尺寸的图片按 p1
至 p7
进行命名并拖拽进 Assets.xcassets
文件中(如图所示)
创建View视图
在工作区的项目文件夹下创建名为 View
的 Group
并在其中依次创建 Home.swift
CarouseBodyView.swift
ScrollViewOffsetModifier.swift
视图文件,其功能如下:
- Home.swift: 主视图,用来控制文字布局与图片布局;
- CarouseBodyView.swift: 控件视图,用来具体实现文字部分与图片部分;
- ScrollViewOffsetModifier.swift: 滚动偏量视图,用来设置3D滚动效果;
视图的实现
主视图 Home.swift
大致定义整个屏幕视图的布局与容器接口。
代码语言:javascript复制import SwiftUI
struct Home: View {
//定义当前的图片
@State var currentTab = "p1"
var body: some View {
//ZStack视图容器
ZStack {
GeometryReader {
//背景阅读器
proxy in
let size = proxy.size
//图片样式
Image(currentTab)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: size.width, height: size.height)
.cornerRadius(1)
}
//约束ZStack容器样式
.ignoresSafeArea()
.overlay(.ultraThinMaterial)
.colorScheme(.dark)
//调用CarouseBodyView控件将图片信息返回给currentTab变量
TabView(selection: $currentTab) {
ForEach(1...7, id: .self) { index in
CarouseBodyView(index: index)
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
}
}
}
struct Home_Previews: PreviewProvider {
static var previews: some View {
Home()
}
}
控件视图 CarouseBodyView.swift
通过视图容器的嵌套布局,实现 Home.swift
中组件的文本与图片的具体内容。
import SwiftUI
struct CarouseBodyView: View {
//整型index标注图片序号
var index: Int
//定义浮点型offset偏量大小
@State var offset:CGFloat = 0
var body: some View {
GeometryReader {proxy in
let size = proxy.size
ZStack {
//通过index来显示指定图片
Image("p(index)")
//设置图片样式
.resizable()
.aspectRatio( contentMode: .fill)
.frame(width: size.width - 8, height: size.height / 1.2, alignment: .center)
.cornerRadius(12) //圆角
//嵌套VStack(垂直排列子元素的视图)容器
VStack(spacing: 25) {
//嵌套VStack容器
VStack(alignment: .leading, spacing: 10) {
Text("标题")
.font(.title2.bold())
.kerning(1.5)
Text("副标题")
.foregroundStyle(.primary)
}
.foregroundStyle(.white)
.padding(.top)
Spacer()
VStack (alignment:.leading){
//嵌套HStack容器
HStack( spacing: 25) {
Image("justine")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 55, height: 55)
.clipShape(Circle())
VStack(alignment: .leading, spacing: 6) {
Text("Dioxide.CN")
.font(.title2.bold())
Text("个人开发者")
.foregroundStyle(.secondary)
}
.foregroundStyle(.black)
}
//嵌套HStack容器
HStack {
VStack {
Text("2022")
.font(.title2.bold())
Text("文章")
.foregroundStyle(.secondary)
}
.frame(maxWidth: .infinity)
VStack {
Text("1000")
.font(.title2.bold())
Text("粉丝")
.foregroundStyle(.secondary)
}
.frame(maxWidth: .infinity)
//嵌套VStack容器
VStack {
Text("50")
.font(.title2.bold())
Text("关注者")
.foregroundStyle(.secondary)
}
.frame(maxWidth: .infinity)
}
.padding(.top)
}
.padding(20)
.padding(.horizontal)
.background(.white)
.cornerRadius(12)
}
.padding(20)
}
.frame(width: size.width - 8, height: size.height / 1.2)
.frame(width: size.width, height: size.height)
}
.tag("p(index)")
//调用getProgress()函数设置整体容器偏量
.rotation3DEffect(
.init(degrees: getProgress() * 90), //旋转度数
axis:(x:0, y: 1, z: 0),
anchor: offset > 0 ?.leading : .trailing, //锚点
anchorZ: 0,
perspective: 0.5
)
//调用ScrollViewOffsetModifier控件定义3D旋转动画
.modifier(
ScrollViewOffsetModifier(
anchorPoint: .leading,
offset: $offset
)
)
}
func getProgress()->CGFloat {
//progress = (-)偏移 ÷ 弹性screen宽度
let progress = -offset / UIScreen.main.bounds.width
return progress
}
}
struct CarouseBodyView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
滚动偏量视图 ScrollViewOffsetModifier.swift
这类似于 css 中的 transform: rotateX()
transform: rotateY()
属性,通过屏幕反馈的滑动位置来控制每个图片组件的 X Y 轴偏量。
import SwiftUI
struct ScrollViewOffsetModifier: ViewModifier {
//定义顶部锚点
var anchorPoint: Anchor = .top
@Binding var offset: CGFloat
func body(content: Content) -> some View {
content
.overlay(
GeometryReader { proxy -> Color in
let frame = proxy.frame(in: .global)
DispatchQueue.main.async {
//基于锚点设置偏移量
switch anchorPoint {
case .top:
offset = frame.minY
case .bottom:
offset = frame.maxY
case .leading:
offset = frame.minX
case .trailing:
offset = frame.maxX
}
}
return Color.clear
}
)
}
}
//为ScrollView和tab视图定义修饰器
//枚举锚点位置
enum Anchor {
case top
case bottom
case leading
case trailing
}
源码
3d-carousel-slider.zip
来源:百度网盘 | 提取码:up0f
3d-carousel-slider.zip
来源:蓝奏云网盘 | 提取码:1z5b