【SwiftUI模块】0060、SwiftUI基于Firebase搭建一个类似InstagramApp 2/7部分-搭建TabBar

news2024/11/18 2:42:30

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

技术:SwiftUI、SwiftUI4.0、Instagram、Firebase
运行环境:
SwiftUI4.0 + Xcode14 + MacOS12.6 + iPhone Simulator iPhone 14 Pro Max

SwiftUI基于Firebase搭建一个类似InstagramApp 2/7部分-搭建TabBar

    • 概述
    • 详细
      • 一、运行效果
      • 二、项目结构图
      • 三、程序实现 - 过程
        • 1.创建一个项目命名为 `SpotifyResponvieUI`
        • 1.1.引入资源文件和颜色
        • 2. 创建一个虚拟文件`New Group` 命名为 `View`
        • 3. 创建一个虚拟文件`New Group` 命名为 `Model`
        • 4. 创建一个文件`New File` 选择`SwiftUI View`类型 命名为`Album` 并且继承`Identifiable`
        • 5. 创建一个文件`New File` 选择`SwiftUI View`类型 命名为`Home`
        • 6. 创建一个文件`New File` 选择`SwiftUI View`类型 命名为`OffsetModifier`
      • Code
        • ContentView - 主窗口
        • Home - 主页
        • OffsetModifier - `主要是监听ScrollView的滚动`
        • Album - 模型

概述

使用SwiftUI基于Firebase搭建一个类似InstagramApp 2/7部分-搭建TabBar - 效果

详细

一、运行效果

请添加图片描述

二、项目结构图

在这里插入图片描述

三、程序实现 - 过程

思路:
1.创建头部模块 进行测试上下滚动拥有放大缩小效果
2.搭建分类模块 固定在头部下面
3.搭建列表模块
4.监听滚动偏移的操作

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

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

1.1.引入资源文件和颜色

颜色
BG #281A1A
Green #4DD037
随机图片9张
个人大图背景1张
logo1张

在这里插入图片描述

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

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

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

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

4. 创建一个文件New File 选择SwiftUI View类型 命名为Album 并且继承Identifiable

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

在这里插入图片描述

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

主要是:

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

在这里插入图片描述

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

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

Code

ContentView - 主窗口

主要是展示主窗口Home和设置暗黑模式

import SwiftUI

struct ContentView: View {
    var body: some View {
        Home()
        // 永远是黑暗模式
            .preferredColorScheme(.dark)
    }
    
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Home - 主页

思路

  1. 主要就是展示大图背景 + 固定的分类 + 列表模块
//
//  Home.swift
//  SpotifyResponvieUI (iOS)
//
//  Created by lyh on 2022/8/23.
//

import SwiftUI

struct Home: View {
    @State var currentType: String = "Popular"
    // 光滑滑动效果
    @Namespace var animation
    @State var _albums: [Album] = albums
    
    // x,y
    @State var headerOffsets: (CGFloat,CGFloat) = (0,0)
    
    var body: some View {
        ScrollView(.vertical, showsIndicators: false) {
            VStack(spacing: 0){
                HeaderView()
                // 带内容的固定标题
                LazyVStack(pinnedViews: [.sectionHeaders]) {
                    Section {
                        SongList()
                    } header: {
                        PinnedHeaderView()
                            .background(Color.black)
                            .offset(y: headerOffsets.1 > 0 ? 0 : -headerOffsets.1 / 8)
                            .modifier(OffsetModifier(offset: $headerOffsets.0, returnFromStart: false))
                            .modifier(OffsetModifier(offset: $headerOffsets.1))
                    }
                }
            }
        }
        .overlay(content: {
            Rectangle()
                .fill(.black)
                .frame(height: 50)
                .frame(maxHeight: .infinity,alignment: .top)
                .opacity(headerOffsets.0 < 5 ? 1 : 0)
        })
        .coordinateSpace(name: "SCROLL")
        .ignoresSafeArea(.container, edges: .vertical)
    }
    
    // 固定的内容
    @ViewBuilder
    func SongList()->some View{
        VStack(spacing: 25){
            ForEach($_albums){$album in
                
                HStack(spacing: 12){
                    
                    Text("#\(getIndex(album: album) + 1)")
                        .fontWeight(.semibold)
                        .foregroundColor(.gray)
                    
                    Image(album.albumImage)
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                        .frame(width: 55, height: 55)
                        .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
                    
                    VStack(alignment: .leading, spacing: 8) {
                        Text(album.albumName)
                            .fontWeight(.semibold)
                        
                        Label {
                            Text("65,587,909")
                        } icon: {
                            Image(systemName: "beats.headphones")
                                .foregroundColor(.white)
                        }
                        .foregroundColor(.gray)
                        .font(.caption)
                    }
                    .frame(maxWidth: .infinity,alignment: .leading)
                    
                    Button {
                        album.isLiked.toggle()
                    } label: {
                     
                        Image(systemName: album.isLiked ? "suit.heart.fill" : "suit.heart")
                            .font(.title3)
                            .foregroundColor(album.isLiked ? Color("Green") : .white)
                    }
                    
                    Button {
                    } label: {
                     
                        Image(systemName: "ellipsis")
                            .font(.title3)
                            .foregroundColor(.white)
                    }
                }
            }
        }
        .padding()
        .padding(.top,25)
        .padding(.bottom,150)
    }
    
    func getIndex(album: Album)->Int{
        return _albums.firstIndex { currentAlbum in
            return album.id == currentAlbum.id
        } ?? 0
    }
    
    // 头部视图
    @ViewBuilder
    func HeaderView()->some View{
        GeometryReader{proxy in
            let minY = proxy.frame(in: .named("SCROLL")).minY
            let size = proxy.size
            let height = (size.height + minY)
            
            Image("Ariana")
                .resizable()
                .aspectRatio(contentMode: .fill)
                .frame(width: size.width,height: height > 0 ? height : 0,alignment: .top)
                .overlay(content: {
                    ZStack(alignment: .bottom) {
                        
                        // 调暗文本内容
                        LinearGradient(colors: [
                            .clear,
                            .black.opacity(0.8)
                        ], startPoint: .top, endPoint: .bottom)
                        
                        VStack(alignment: .leading, spacing: 12) {
                            
                            Text("宇夜iOS")
                                .font(.callout)
                                .foregroundColor(.gray)
                            
                            HStack(alignment: .bottom, spacing: 10) {
                                Text("Ariana Grande")
                                    .font(.title.bold())
                                
                                Image(systemName: "checkmark.seal.fill")
                                    .foregroundColor(.blue)
                                    .background{
                                        Circle()
                                            .fill(.white)
                                            .padding(3)
                                    }
                            }
                            
                            Label {
                             
                                Text("Monthly Listeners")
                                    .fontWeight(.semibold)
                                    .foregroundColor(.white.opacity(0.7))
                            } icon: {
                                Text("62,354,659")
                                    .fontWeight(.semibold)
                            }
                            .font(.caption)
                        }
                        .padding(.horizontal)
                        .padding(.bottom,25)
                        .frame(maxWidth: .infinity,alignment: .leading)
                    }
                })
                .cornerRadius(15)
                .offset(y: -minY)
        }
        .frame(height: 250)
    }
    
    // 固定在头部
    @ViewBuilder
    func PinnedHeaderView()->some View{
        let types: [String] = ["Popular","Albums","Songs","Fans also like","About"]
        ScrollView(.horizontal, showsIndicators: false) {
            HStack(spacing: 25){
                
                ForEach(types,id: \.self){type in
                    VStack(spacing: 12){
                        
                        Text(type)
                            .fontWeight(.semibold)
                            .foregroundColor(currentType == type ? .white : .gray)
                        
                        ZStack{
                            if currentType == type{
                                RoundedRectangle(cornerRadius: 4, style: .continuous)
                                    .fill(.white)
                                    .matchedGeometryEffect(id: "TAB", in: animation)
                            }
                            else{
                                RoundedRectangle(cornerRadius: 4, style: .continuous)
                                    .fill(.clear)
                            }
                        }
                        .padding(.horizontal,8)
                        .frame(height: 4)
                    }
                    .contentShape(Rectangle())
                    .onTapGesture {
                        withAnimation(.easeInOut){
                            currentType = type
                        }
                    }
                }
            }
            .padding(.horizontal)
            .padding(.top,25)
            .padding(.bottom,5)
        }
    }
}

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

OffsetModifier - 主要是监听ScrollView的滚动

用来监听ScrollView的滚动 偏移量的改变

//
//  OffsetModifier.swift
//  SpotifyResponvieUI (iOS)
//
//  Created by lyh on 2022/8/23.
//

import SwiftUI

// 继承于 ViewModifier 最主要是能方便扩展一些常见的设置属性
/*
 比如 给Text设置字体\背景颜色\阴影效果
 extension Text {
     func songStyle() -> some View {
         self
             .font(.system(size: 24, weight: .bold))
             .foregroundColor(.white)
             .shadow(radius: 20)
     }
 }
 
 ⭐️如果是继承ViewModifier
 struct SongTextViewModifier: ViewModifier {
     func body(content: Content) -> some View {
         content
           .font(.system(size: 24, weight: .bold))
           .foregroundColor(.white)
           .shadow(radius: 20)
     }
 }

 然后直接通过
 
 Text(song)
       .modifier(SongTextViewModifier())
 设置
 */

struct OffsetModifier: ViewModifier {
    @Binding var offset: CGFloat
    
    // 可选从0返回值
    var returnFromStart: Bool = true
    @State var startValue: CGFloat = 0
    
    func body(content: Content) -> some View {
        content
            .overlay {
                GeometryReader{proxy in
                    Color.clear
                        .preference(key: OffsetKey.self, value: proxy.frame(in: .named("SCROLL")).minY)
                        .onPreferenceChange(OffsetKey.self) { value in
                            if startValue == 0{
                                startValue = value
                            }
                            print(value);

                            offset = (value - (returnFromStart ? startValue : 0))
                            
                            print("offset is \(offset)");

                        }
                }
            }
    }
}

// 偏好的关键
struct OffsetKey: PreferenceKey{
    static var defaultValue: CGFloat = 0
    
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = nextValue()
    }
}

Album - 模型
//
//  Album.swift
//  SpotifyResponvieUI (iOS)
//
//  Created by lyh on 2022/8/23.
//

import SwiftUI

// Ablum模型和样本数据
struct Album: Identifiable {
    var id = UUID().uuidString
    var albumName: String
    var albumImage : String
    var isLiked : Bool = false
}


var albums : [Album] = [
    
    Album(albumName: "Positions", albumImage: "Album1"),
    Album(albumName: "The Best", albumImage: "Album2",isLiked: true),
    Album(albumName: "My Everything", albumImage: "Album3"),
    Album(albumName: "Yours Truly", albumImage: "Album4"),
    Album(albumName: "Sweetener", albumImage: "Album5",isLiked: true),
    Album(albumName: "Rain On Me", albumImage: "Album6"),
    Album(albumName: "Stuck With U", albumImage: "Album7"),
    Album(albumName: "7 rings", albumImage: "Album8",isLiked: true),
    Album(albumName: "Bang Bang", albumImage: "Album9"),
    
]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1125055.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

构建离线应用:Apollo与本地状态管理

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…

网络流探索:解决网络最大流问题的算法集锦

1.初识网络流 网络流一直是初学者心中很难过去的一道坎&#xff0c;很多人说它是一个不像DFS/BFS那么直观的算法&#xff0c;同时网上也有各种参差不齐的材料&#xff0c;让人感到一知半解。 如果你也有这样的感觉&#xff0c;那么不要灰心&#xff0c;坚持住&#xff0c;因为…

【仙逆】王林用计灭富二代,有长命锁也没用,藤化元一怒请一人出山

【侵权联系删除】【文/郑尔巴金】 仙逆动漫第七集已经更新了。而这一集看下来&#xff0c;可以说非常精彩&#xff0c;全程在打&#xff0c;期间还能看到主角王林用谋&#xff0c;是如何一步步的把敌人藤厉引入陷阱灭杀的&#xff0c;更可以看到王林是如何筑基的。那么多的不说…

Windows系统下安装CouchDB3.3.2教程

安装 前往CouchDB官网 官网点击download下载msi文件 双击该msi文件&#xff0c;一直下一步 创建个人account 设置cookie value 用于进行身份验证和授权。 愉快下载 点击OK 重启 启动 重启电脑后 打开浏览器并访问以下链接&#xff1a;http://127.0.0.1:5984/ 如果没有问…

SQL UPDATE 语句(更新表中的记录)

SQL UPDATE 语句 UPDATE 语句用于更新表中已存在的记录。 还可以使用AND或OR运算符组合多个条件。 SQL UPDATE 语法 具有WHERE子句的UPDATE查询的基本语法如下所示&#xff1a; UPDATE table_name SET column1 value1, column2 value2, ... WHERE conditi…

编写第一个python程序

实践流程 新建程序 ↓选择“file”\"new feile"命令 向世界问好 ↓print&#xff08;‘hello word’&#xff09; 保存&#xff0c;运行程序 ↓按“F5键”运行程序 修改程序 ↓print(hello word\n *20) 常见错误 中文错误 熟悉python idle编程环境 学习流程…

Go学习第五章——函数与包

Go学习第五章——函数与包 1 函数1.1 基本语法1.2 函数多返回值1.3 函数的可见性和包级函数1.4 函数调用机制底层原理1.5 值类型和引用类型1.6 注意事项和细节1.7 逃逸机制&#xff08;补&#xff0c;可不看&#xff09; 2 包2.1 快速入门2.2 包的使用细节 3 函数详细讲解3.1 递…

3BHE009319R0001 UNS2881b-P,V1 l展示面向边缘人工智能应用

3BHE009319R0001 UNS2881b-P,V1 l展示面向边缘人工智能应用 ASRock Industrial展示了由第12代英特尔酷睿处理器(Alder Lake-P)支持的NUC 1200 BOX/iBOX 1200系列工业计算机&#xff0c;用于跨行业的人工智能加速。工业计算机采用高达12个内核/16个线程的高性能混合架构和高达9…

MySQL -- 库和表的操作

MySQL – 库和表的操作 文章目录 MySQL -- 库和表的操作一、库的操作1.创建数据库2.查看数据库3.删除数据库4.字符集和校验规则5.校验规则对数据库的影响6.修改数据库7.备份和恢复8.查看连接情况 二、表的操作1.创建表2.查看表结构3.修改表4.删除表 一、库的操作 注意&#xf…

针对遗留系统采取的不同演化策略

1. 改造策略 高水平、高价值区&#xff0c;即遗留系统的技术含量较高&#xff0c;本身还有极大的生命力。系统具有较高的业务价值&#xff0c;基本上能够满足企业业务运作和决策支持的需要。这种系统可能建成的时间还很短&#xff0c;对这种遗留系统的演化策略为改造。改造包括…

找不到conda可执行文件:解决方法

1.在新版本的pycharm出现的问题如下&#xff1a; 2.解决方法: 2.1 将anaconda\Scripts\conda.exe选中 2.2选择自己的anconda自己的环境&#xff0c;之后就可以正常创建conda环境

基于Tucker分解的时序知识图谱补全10.23

基于Tucker分解的时序知识图谱补全 摘要引言相关工作静态知识图谱补全时序知识图谱补全 背景提出的模型学习时间复杂度和参数增长表达能力分析 实验 摘要 知识图谱已被证明是众多智能应用的有效工具。然而&#xff0c;大量有价值的知识仍然隐含在知识图谱中。为了丰富现有的知…

异步加载 JavaScript

目录 ​编辑 前言&#xff1a;异步加载 JavaScript 的重要性 详解&#xff1a;异步加载 JavaScript 的方法 使用 使用动态创建标签&#xff1a; 使用模块引入&#xff08;ES6模块&#xff09;&#xff1a; 解析&#xff1a;异步加载 JavaScript 的重要性和优势 实践和注…

后台交互-个人中心->小程序登录微信登录接口演示,小程序授权登录理论,小程序授权登录代码演示,微信表情包存储问题

小程序登录微信登录接口演示小程序授权登录理论小程序授权登录代码演示微信表情包存储问题 1.小程序登录微信登录接口演示 推荐使用 wx.getUserProfile 获取用户信息&#xff0c;开发者每次通过该接口获取用户个人信息均需用户确认 // pages/index/index.js Page({data: {user…

24东北大学计算机计划招生数据

2.结语 24的保研名额很多&#xff0c;统考名额就这些&#xff0c;大家根据自己的情况做出选择 东大计算机不好考&#xff0c;但是不代表考不上&#xff01;加油 3.数据来源于官网 官网链接

基于深度学习实现一张单图,一个视频,一键换脸,Colab脚本使用方法,在线版本,普通人也可以上传一张图片体验机器学习一键换脸

基于深度学习实现一张单图,一个视频,一键换脸,Colab脚本使用方法,在线版本,普通人也可以上传一张图片体验机器学习一键换脸。 AI领域人才辈出,突然就跳出一个大佬“s0md3v”,开源了一个单图就可以进行视频换脸的项目。 项目主页给了一张换脸动图非常有说服力,真是一图…

python网络爬虫(二)基本库的使用urllib/requests

使用urllib 了解一下 urllib 库&#xff0c;它是 Python 内置的 HTTP 请求库&#xff0c;也就是说不需要额外安装即可使用。它包含如下 4 个模块。 request&#xff1a;它是最基本的 HTTP 请求模块&#xff0c;可以用来模拟发送请求。就像在浏览器里输入网址然后回车一样&…

【项目经理】工作流引擎

项目经理之 工作流引擎 一、业务系统管理目的维护信息 二、组织架构管理目的维护信息 三、角色矩阵管理目的维护信息 四、条件变量管理目的维护信息 五、流程模型管理目的维护信息 六、流程版本管理目的维护信息 七、流程监管控制目的维护信息 系列文章版本记录 一、业务系统管…

COS 音视频实践

对象存储 音视频处理概述-媒体处理实践-最佳实践-腾讯云 1、COS https://www.cnblogs.com/cloudstorageangel/p/15977032.html 全程&#xff1a;对象存储&#xff08;Cloud Object Storage&#xff0c;COS&#xff09;&#xff1b;腾讯云提供的对象存储服务。 可以对音视频…

目标检测的方法

目标检测大致分为两个方向:基于传统的目标检测算法和基于深度学习的目标检测算法。 1.基于传统的目标检测算法 在利用深度学习做物体检测之前,传统算法对于目标检测通常分为3个阶段:区域选取、特征提取和体征分类。 2.基于深度学习的目标检测算法 目标检测任务可分为两