【SwiftUI模块】0027、SwiftUI创建时尚的可折叠动画标题

SwiftUI模块系列 - 已更新27篇
SwiftUI项目 - 已更新2个项目
往期Demo源码下载

技术:SwiftUI、SwiftUI3.0、可折叠、动画标题、滚动视图
运行环境:
SwiftUI3.0 + Xcode13.4.1 + MacOS12.5 + iPhone Simulator iPhone 13 Pro Max

概述

使用SwiftUI创建时尚的可折叠动画标题

详细

一、运行效果

请添加图片描述

二、项目结构图

在这里插入图片描述

三、程序实现 - 过程

思路:

  1. 搭建导航整体拉伸模块
  2. 消息模块
  3. 进行滚动 大于或者小于导航高度 处理 展示拉伸或者隐藏导航头像标题模块

1.创建一个项目命名为 CollapsibleHeader

在这里插入图片描述
在这里插入图片描述

1.1.引入资源文件和颜色

颜色
TopBar #451AF5
头像图片1张

在这里插入图片描述

2. 创建一个虚拟文件New Group 命名为 View

在这里插入图片描述
在这里插入图片描述

3. 创建一个虚拟文件New Group 命名为 Model

在这里插入图片描述
在这里插入图片描述

4. 创建一个文件New File 选择SwiftUI View类型 命名为Home

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

5. 创建一个文件New File 选择SwiftUI View类型 命名为CustomCorner

主要是: 用于设置自定义圆角区域

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

6. 创建一个虚拟文件New Group 命名为 Modifiers

在这里插入图片描述
在这里插入图片描述

7. 创建一个文件New File 选择SwiftUI View类型 命名为OffsetModifier

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

8. 创建一个文件New File 选择SwiftUI View类型 命名为Message 并且继承于Identifiable 作为模型

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Code

ContentView - 主窗口

主要是展示主窗口Home 和获取安全区域

//
//  ContentView.swift
//  Shared
//
//  Created by 李宇鸿 on 2022/9/9.
//

import SwiftUI

struct ContentView: View {
    
    
    var body: some View {
    
    
        
        // 忽略并得到安全区域的大小…
        GeometryReader{
    
    proxy in
            let topEdge = proxy.safeAreaInsets.top
            
            Home(topEdge:topEdge)
                .ignoresSafeArea(.all,edges: .top)

        }
    }
}

struct ContentView_Previews: PreviewProvider {
    
    
    static var previews: some View {
    
    
        ContentView()
    }
}

Home - 主页

思路

  1. 搭建导航整体拉伸模块
  2. 消息模块
  3. 进行滚动 大于或者小于导航高度 处理 展示拉伸或者隐藏导航头像标题模块
//
//  Home.swift
//  CollapsibleHeader (iOS)
//
//  Created by 李宇鸿 on 2022/9/9.
//

import SwiftUI

struct Home: View {
    
    
    var topEdge : CGFloat
    // 最大高度
    let maxHeight = UIScreen.main.bounds.height  /  2.3
    
    // 偏移量
    @State var offset : CGFloat = 0
    
    var body: some View {
    
    
        
        ScrollView(.vertical,showsIndicators: false){
    
    
            VStack(spacing:15){
    
    
                // 顶部导航视图…
                GeometryReader{
    
    proxy in
                    TopBar(topEdge: topEdge,offset: $offset,maxHeight:maxHeight)
                    .foregroundColor(.white)
                    .frame(maxWidth: .infinity)
                    // 粘性的影响
                    .frame(height:getHeaderHeight(), alignment: .bottom)
                    .background(Color("TopBar"),in: CustomCorner(corners: [.bottomRight], radius: getCornerRadius())
                    )
                    .overlay(
                        // 顶部导航视图…
                        HStack(spacing:15){
    
    
                            Button  {
    
    
                                
                            } label: {
    
    
                                Image(systemName: "xmark")
                                    .font(.body.bold())
                            }
                            
                            
                            Image("Pic")
                                .resizable()
                                .aspectRatio(contentMode: .fill)
                                .frame(width: 35, height: 35)
                                .clipShape(Circle())
                                .opacity(topBarTitleOpacity())
                            Text("iJustine")
                                .fontWeight(.bold)
                                .foregroundColor(.white)
                                .opacity(topBarTitleOpacity())

                            
                            Spacer()

                            
                            Button  {
    
    
                                
                            } label: {
    
    
                                Image(systemName: "line.3.horizontal.decrease")
                                    .font(.body.bold())
                            }
                        }
                            .padding(.horizontal)
                        // 最大高度
                            .frame(height:80)
                            .foregroundColor(.white)
                            .padding(.top,topEdge)
                        ,alignment: .top
                    )
                    
                }
                .frame(height:maxHeight)
                // 固定在顶部……
                .offset(y:-offset)
                .zIndex(1)
                
                
                VStack(spacing:15){
    
    
                    
                    ForEach(allMessages){
    
    message in
                        
                        // 视图View
                        MessageCardView(message: message)
                    }
                }
                .padding()
                .zIndex(0)
                
            }
            .modifier(OffsetModifier(offset: $offset))
        }
        // setting coordinate space...
        // 设置坐标空间……
        .coordinateSpace(name: "SCROLL")
        
        
    }
    
    func getHeaderHeight() -> CGFloat{
    
    
        let topHeight = maxHeight + offset
        
        // 80是固定的顶部导航栏高度…
        //因为我们包括了顶部安全区域,所以我们也需要包括它…
        return topHeight > (80 + topEdge) ? topHeight : (80 + topEdge)
    }
    
    func getCornerRadius()->CGFloat{
    
    
        let progress = -offset / (maxHeight - (80 + topEdge) )
        let value = 1 - progress
        let radius = value * 50
        
        return  offset < 0 ? radius : 50
    }
    
    func topBarTitleOpacity()->CGFloat {
    
    
        //开始后主要内容消失....
        //我们需要消除70从偏移....
        //开始…
        let progress = -(offset + 60) / (maxHeight - (80 + topEdge))
            
//        let opacity = 1 - progress
        
        return progress
        
    }
}

struct Home_Previews: PreviewProvider {
    
    
    static var previews: some View {
    
    
        ContentView()
    }
}

struct TopBar : View {
    
    
    var topEdge : CGFloat
    @Binding var offset : CGFloat
    
    var maxHeight : CGFloat
    
    var body: some View {
    
    
        VStack(alignment: .leading, spacing: 15) {
    
    
            
            Image("Pic")
                .resizable()
                .aspectRatio(contentMode: .fill)
                .frame(width: 80, height: 80)
                .cornerRadius(10)
            
            Text("iJustine")
                .font(.largeTitle.bold())
            Text("Justine Ezarik is an American YouTuber, host, author, She is best known as iJustine")
                .fontWeight(.semibold)
                .foregroundColor(Color.white.opacity(0.8))
                .lineLimit(2)

        }
        .padding()
        .padding(.bottom)
        .opacity(getOpacity())

    }
    
    // 计算不透明度
    func getOpacity() -> CGFloat {
    
    
        // 一些随机量的时间,以可见的滚动....
        let progress =  -offset / 70
        
        let opacity = 1 - progress
        
        return offset < 0 ? opacity : 1
    }
}


struct MessageCardView: View {
    
    
    var message : Message
    var body: some View{
    
    
        HStack(spacing: 15){
    
    
            Circle()
                .fill(message.tintColor)
                .frame(width: 50, height: 50)
                .opacity(0.8)
            
            VStack(alignment: .leading, spacing: 8) {
    
    
                Text(message.userName)
                    .fontWeight(.bold)
                Text(message.message)
                    .foregroundColor(.secondary)
            }
            .foregroundColor(.primary)
            .frame(maxWidth:.infinity,alignment: .leading)
            
        }
        
    }
    
}

CustomCorner - 主要是自定义圆角角落

//
//  CustomCorner.swift
//  CollapsibleHeader (iOS)
//
//  Created by 李宇鸿 on 2022/9/9.
//

import SwiftUI

// 自定义圆角区域
struct CustomCorner: Shape {
    
    
    
    var corners : UIRectCorner
    var radius : CGFloat
    
    func path(in rect: CGRect) -> Path {
    
    
        let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        return Path(path.cgPath)
    }
    
}

OffsetModifier - 偏移量修饰

用来监听偏移量的值

//
//  OffsetModifier.swift
//  CollapsibleHeader (iOS)
//
//  Created by 李宇鸿 on 2022/9/9.
//

import SwiftUI

// 得到ScrollView抵消……
struct OffsetModifier : ViewModifier {
    
    
    @Binding var offset : CGFloat
    func body(content: Content) -> some View {
    
    
        content
            .overlay(
                GeometryReader{
    
    proxy -> Color in
                    
                    // 获取坐标空间的值叫做scroll…
                    let minY = proxy.frame(in: .named("SCROLL")).minY
                    // 测试滚动的范围打印
//                    print(minY)
                    
                    DispatchQueue.main.async {
    
    
                        self.offset = minY
                    }
                    return Color.clear
                }
                ,alignment: .top
            )
    }
}

Message - 模型

//
//  Message.swift
//  CollapsibleHeader (iOS)
//
//  Created by 李宇鸿 on 2022/9/9.
//

import SwiftUI

// Message Model and Sample Data...
// 消息模型和样本数据…
struct Message: Identifiable{
    
    
    var id = UUID().uuidString
    var message: String
    var userName: String
    var tintColor: Color
}

var allMessages : [Message] = [

    Message(message: "Can we go to the park.", userName: "iJustine", tintColor: .pink),
    Message(message: "We can go down to the store with the dog. It is not too far away.", userName: "Jenna", tintColor: .red),
    Message(message: "Can we go to the park.", userName: "Aaron", tintColor: .green),
    Message(message: "We can go down to the store with the dog. It is not too far away.", userName: "Kaviya", tintColor: .yellow),
    Message(message: "Can we go to the park.", userName: "Finch", tintColor: .orange),
    Message(message: "Can we go to the park.", userName: "Jeff", tintColor: .pink),
    Message(message: "Can we go to the park.", userName: "Melinda", tintColor: .red),
    Message(message: "Can we go to the park.", userName: "Cook", tintColor: .green),
    Message(message: "Can we go to the park.", userName: "Jimmy", tintColor: .yellow),
    Message(message: "Can we go to the park.", userName: "Tom", tintColor: .orange),
    Message(message: "Can we go to the park.", userName: "Ellen", tintColor: .pink),
    Message(message: "Can we go to the park.", userName: "Julia", tintColor: .red),
    Message(message: "Can we go to the park.", userName: "Ashley", tintColor: .green),
    Message(message: "Can we go to the park.", userName: "Emily", tintColor: .yellow),
]



猜你喜欢

转载自blog.csdn.net/qq_42816425/article/details/126756037