SwiftUI模块系列 - 已更新23篇SwiftUI项目 - 已更新2个项目往期Demo源码下载
技术:SwiftUI、SwiftUI3.0、3D转换、视图切换、过渡动画
运行环境:
SwiftUI3.0 + Xcode13.4.1 + MacOS12.5 + iPhone Simulator iPhone 13 Pro Max
SwiftUI搭建一个视图之间的立方体3D自定义转换过渡动画
- 概述
- 详细
- 一、运行效果
- 二、项目结构图
- 三、程序实现 - 过程
- 1.创建一个项目命名为 `CustomTransition`
- 1.1.引入资源文件和颜色
- 2. 创建一个虚拟文件`New Group` 命名为 `View`
- 3. 创建一个文件`New File` 选择`SwiftUI View`类型 命名为`Home`
- 4. 创建一个文件`New File` 选择`SwiftUI View`类型 命名为`CubicTransition`
- Code
- ContentView - 主窗口
- Home - 主页
- CubicTransition - `过渡动画`
概述
使用SwiftUI搭建一个视图之间的立方体3D自定义转换过渡动画
详细
一、运行效果
二、项目结构图
三、程序实现 - 过程
思路:
1.搭建主页部分
2.创建一个过渡动画的类、记录当前详情页和上一个页 、以及是否显示主页的属性
3.过渡动画类 处理当前页 和 下一个页面的偏移的逻辑处理 。比如当前页进行当前页跳转。下一个就进行3d动画过渡偏移整个屏幕
1.创建一个项目命名为 CustomTransition
1.1.引入资源文件和颜色
背景图片2张
2. 创建一个虚拟文件New Group
命名为 View
3. 创建一个文件New File
选择SwiftUI View
类型 命名为Home
4. 创建一个文件New File
选择SwiftUI View
类型 命名为CubicTransition
Code
ContentView - 主窗口
主要是展示主窗口
Home
//
// ContentView.swift
// Shared
//
// Created by 李宇鸿 on 2022/9/4.
//
import SwiftUI
struct ContentView: View {
var body: some View {
Home()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Home - 主页
思路:
1.搭建主页部分
2.创建一个过渡动画的类、记录当前详情页和上一个页 、以及是否显示主页的属性
3.过渡动画类 处理当前页 和 下一个页面的偏移的逻辑处理 。比如当前页进行当前页跳转。下一个就进行3d动画过渡偏移整个屏幕
//
// Home.swift
// CustomTransition (iOS)
//
// Created by 李宇鸿 on 2022/9/4.
//
import SwiftUI
struct Home: View {
@State var show : Bool = false
var body: some View {
GeometryReader{proxy in
let size = proxy.size
CubicTransition(show:$show) {
ZStack{
Image("Pic1")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: size.width, height: size.height)
.clipped()
Button{
// 使用点击屏幕测试来回切换 引发bug的处理
// show.toggle()
}label: {
Text("Navigate")
.font(.title3)
.foregroundColor(.white)
.padding()
.background(.ultraThinMaterial)
.cornerRadius(10)
.environment(\.colorScheme, .dark)
}
.offset(y: 150)
}
} detail: {
Image("Pic2")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: size.width, height: size.height)
.clipped()
}
}
.ignoresSafeArea()
.overlay(alignment: .top) {
HStack(spacing:12){
if show {
Button {
show.toggle()
} label: {
Image(systemName: "arrow.left")
.font(.title2)
.foregroundColor(.white)
}
}
Text(show ? "Back" : "Custom transition")
.font(.title.bold())
.foregroundColor(.white)
}
.padding()
.padding(.top,4)
.frame(maxWidth:.infinity,alignment: .leading)
.background(.ultraThinMaterial)
.environment(\.colorScheme, .dark)
}
.onTapGesture{
show.toggle()
}
}
}
struct Home_Previews: PreviewProvider {
static var previews: some View {
Home()
}
}
CubicTransition - 过渡动画
用来监听ScrollView的滚动 偏移量的改变
// CubicTransition.swift
// CustomTransition (iOS)
//
// Created by 李宇鸿 on 2022/9/4.
//
import SwiftUI
struct CubicTransition<Content: View,Detail:View>: View {
var cotnent: Content
var detail: Detail
// 显示视图
@Binding var show : Bool
init(show:Binding<Bool>,
@ViewBuilder content: @escaping ()-> Content,
@ViewBuilder detail: @escaping()->Detail) {
self.detail = detail()
self.cotnent = content()
self._show = show
}
// 动画属性
@State var animateView : Bool = false
@State var showView : Bool = false
// 避免用户一直来回切换引起的bug - 没有将当前视图移除问题
@State var task : DispatchWorkItem?
var body: some View {
GeometryReader{proxy in
let size = proxy.size
HStack(spacing:0){
cotnent
.frame(width: size.width, height: size.height)
// 当detail View被push时旋转当前视图
.rotation3DEffect(.init(degrees: animateView ? -85 : 0), axis: (x: 0, y: 1, z: 0),anchor: .trailing,anchorZ: 0,perspective: 1)
// 显示详细信息视图
ZStack{
if showView {
detail.frame(width: size.width, height: size.height)
.transition(.move(edge: .trailing))
.onDisappear{
print("Colosed")
}
}
}
.rotation3DEffect(.init(degrees: animateView ? 0 : 85), axis: (x: 0, y: 1, z: 0),anchor: .leading,anchorZ: 0,perspective: 1)
}
// 应用抵消
.offset(x:animateView ? -size.width : 0)
}
.onChange(of: show) { newValue in
task?.cancel()
// 在动画星星前显示视图
if show {
showView = true
}
// 视图未被删除,因此它将产生内存问题
else {
//当动画完成时关闭视图
//在0.35秒之后
// 避免用户一直来回切换引起的bug - 没有将当前视图移除问题
// 为了避免这个问题
task = .init{
showView = false
}
if let task = task {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35,execute: task)
}
}
//为什么使用单独的变量而不是显示
//因为一旦它设置为false,它就会删除
//这样动画将不会完成
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
withAnimation(.easeInOut(duration: 0.35)){
animateView.toggle()
}
}
}
}
}
struct CubicTransition_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}