技术:SwiftUI、SwiftUI3.0、3D、轮播滑块、3D轮播滑轮
运行环境:
SwiftUI3.0 + Xcode13.4.1 + MacOS12.5 + iPhone Simulator iPhone 13 Pro Max
SwiftUI创建一个时尚的3D轮播滑块


思路:
1.创建主页 并且使用tabView加载多张图片进行滚动
2.给每张旋转木马图片设置3D动画处理
3.统一使用滚动视图偏移的处理器进行处理偏移问题
MatchedCarousel

颜色
随机图片8张
New Group 命名为 View

New File 选择SwiftUI View类型 命名为Home



New File 选择SwiftUI View类型 命名为CarouselBodyView主要是: 用来加载TabView的每一张图片的3d转场动画的处理




New File 选择SwiftUI View类型 命名为ScrollViewOffsetModifier

主要是展示主窗口
Home
//
// ContentView.swift
// Shared
//
// Created by 李宇鸿 on 2022/9/5.
//
import SwiftUI
struct ContentView: View {
var body: some View {
Home()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
思路
- 主要是用来展示tabView的多张图片
//
// Home.swift
// MatchedCarousel (iOS)
//
// Created by 李宇鸿 on 2022/9/5.
//
import SwiftUI
struct Home: View {
// 背景将是当前标签图像…
@State var currentTab = "p1"
var body: some View {
ZStack{
// 获取屏幕大小的几何读取器…
GeometryReader{proxy in
let size = proxy.size
Image(currentTab)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: size.width, height: size.height)
.cornerRadius(1)
}
.ignoresSafeArea()
// 材料的效果……
.overlay(.ultraThinMaterial)
// 暗黑模式
.colorScheme(.dark)
// 旋转木马列表
TabView(selection: $currentTab) {
ForEach(1...7,id:\.self){index in
// 旋转木马视图
CarouselBodyView(index:index)
}
}
//页面标签风格……
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
}
}
}
struct Home_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
//
// CarouselBodyView.swift
// MatchedCarousel (iOS)
//
// Created by 李宇鸿 on 2022/9/5.
//
import SwiftUI
struct CarouselBodyView: View {
var index : Int
// Offset
@State var offset : CGFloat = 0
var body: some View {
GeometryReader{proxy in
let size = proxy.size
ZStack{
Image("p\(index)")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: size.width - 8, height: size.height / 1.2)
.cornerRadius(12)
VStack{
VStack(alignment: .leading, spacing: 10) {
Text("Human Integration Supervisor")
.font(.title2.bold())
//letter spacing...
.kerning(1.5)
Text("The world's largest collection of animal facts, pictures and more!")
.kerning(1.2)
.foregroundStyle(.secondary)
}
.foregroundStyle(.white)
.padding(.top)
Spacer(minLength: 0)
VStack(alignment:.leading, spacing:30){
HStack(spacing:15){
Image("justine")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 55, height: 55)
.clipShape(Circle())
VStack(alignment: .leading, spacing: 6) {
Text("iJustine")
.font(.title2.bold())
Text("Apple Sheep")
.foregroundStyle(.secondary)
}
.foregroundStyle(.black)
}
HStack{
VStack{
Text("1303")
.font(.title2.bold())
Text("Posts")
.foregroundStyle(.secondary)
}
.frame(maxWidth:.infinity)
VStack{
Text("3103")
.font(.title2.bold())
Text("Followers")
.foregroundStyle(.secondary)
}
.frame(maxWidth:.infinity)
VStack{
Text("1503")
.font(.title2.bold())
Text("Following")
.foregroundStyle(.secondary)
}
.frame(maxWidth:.infinity)
}
.foregroundStyle(.black)
}
.padding(20)
.padding(.horizontal,10)
.background(.white,in:RoundedRectangle(cornerRadius: 4))
}
.padding(20)
}
.frame(width: size.width - 8, height: size.height / 1.2)
.frame(width: size.width, height: size.height)
}
// 设置tag
.tag("p\(index)")
//旋转……
//基于偏移量的锚
.rotation3DEffect(.init(degrees: getProgress() * 90), axis: (x: 0, y: 1, z: 0),anchor: offset > 0 ? .leading : .trailing,anchorZ: 0,perspective: 0.6)
//自定义3D旋转…
//因为我们需要从0开始,所以我们得到前导偏移量…
.modifier(ScrollViewOffsetModifier(anchorPoint: .leading, offset: $offset))
// .overlay(Text("\(offset)").foregroundColor(.white))
}
// 获取进度
func getProgress()-> CGFloat{
let progress = -offset / UIScreen.main.bounds.width
return progress
}
}
struct CarouselBodyView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
主要是监听ScrollView的滚动用来监听ScrollView的滚动 偏移量的改变
//
// ScrollViewOffsetModifier.swift
// MatchedCarousel (iOS)
//
// Created by 李宇鸿 on 2022/9/5.
//
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或标签视图…
//枚举自定义锚…
enum Anchor{
case top
case bottom
case leading
case trailing
}