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

news2024/11/18 1:25:59

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

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

SwiftUI基于Firebase搭建一个类似InstagramApp 3/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 3/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/1125066.html

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

相关文章

数据集的特征提取

1、 特征提取 1.1、 将任意数据&#xff08;如文本或图像&#xff09;转换为可用于机器学习的数字特征 注&#xff1a;特征值化是为了计算机更好的去理解数据 字典特征提取(特征离散化)文本特征提取图像特征提取&#xff08;深度学习将介绍&#xff09; 2 特征提取API sklear…

Python OpenCV通过灰度平均值进行二值化处理以减少像素误差

Python OpenCV通过灰度平均值进行二值化处理以减少像素误差 前言前提条件相关介绍实验环境通过灰度平均值进行二值化处理以减少像素误差固定阈值二值化代码实现 灰度平均值二值化代码实现 前言 由于本人水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。更多精彩内容…

数据安全与PostgreSQL:最佳保护策略

在当今数字化时代&#xff0c;数据安全成为了企业不可或缺的一环。特别是对于使用数据库管理系统&#xff08;DBMS&#xff09;的组织来说&#xff0c;确保数据的完整性、保密性和可用性至关重要。在众多DBMS中&#xff0c;PostgreSQL作为一个强大而灵活的开源数据库系统&#…

酒类商城小程序怎么做

随着互联网的快速发展&#xff0c;线上购物越来越普及。酒类商品也慢慢转向线上销售&#xff0c;如何搭建一个属于自己的酒类小程序商城呢&#xff1f;下面就让我们一起来看看吧&#xff01; 一、登录乔拓云平台 首先&#xff0c;我们需要进入乔拓云平台的后台&#xff0c;点击…

Pytorch公共数据集、tensorboard、DataLoader使用

本文将主要介绍torchvision.datasets的使用&#xff0c;并以CIFAR-10为例进行介绍&#xff0c;对可视化工具tensorboard进行介绍&#xff0c;包括安装&#xff0c;使用&#xff0c;可视化过程等&#xff0c;最后介绍DataLoader的使用。希望对你有帮助 Pytorch公共数据集 torc…

【第三天】C++类和对象进阶指南:从堆区空间操作到友元的深度掌握

一、new和delete 堆区空间操作 1、new和delete操作基本类型的空间 new与C语言中malloc、delete和C语言中free 作用基本相同 区别&#xff1a; new 不用强制类型转换 new在申请空间的时候可以 初始化空间内容 2、 new申请基本类型的数组 3、new和delete操作类的空间 4、new申请…

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

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部分-搭建Tab…

构建离线应用: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 的重要性和优势 实践和注…