【SwiftUI项目】0020、SwiftUI搭建一个TodoList任务清单的App 1/2部分(UI部分)

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

技术:SwiftUI、SwiftUI4.0、任务管理、TodoList、任务清单
运行环境:
SwiftUI4.0 + Xcode14 + MacOS12.6 + iPhone Simulator iPhone 14 Pro Max

概述

使用SwiftUI搭建一个TodoList任务清单的App 1/2部分(UI部分)

详细

一、运行效果

请添加图片描述

二、项目结构图

在这里插入图片描述

三、程序实现 - 过程

思路:
1.创建头部模块 日期+头像
2.搭建日期选择 - 一周时间
3.任务卡片
4.监听日期选择 更新任务数据

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

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

1.1.引入资源文件和颜色

颜色
Black #2E2E2E
头像1张
随机图片3张

在这里插入图片描述

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

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

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

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

4. 创建一个文件New File 选择SwiftUI View类型 命名为Task 删除预览图 并且继承Identifiable 作为模型

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

在这里插入图片描述

在这里插入图片描述

5. 创建一个虚拟文件New Group 命名为 ViewModel

在这里插入图片描述

在这里插入图片描述

6. 创建一个文件New File 选择SwiftUI View类型 命名为TaskViewModel 类型为class 删除预览图 并且继承ObservableObject 作为模型

主要是:作为视图模型

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

在这里插入图片描述

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

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

Code

ContentView - 主窗口

主要是展示主窗口Home

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

import SwiftUI

struct ContentView: View {
    var body: some View {
        Home()
    }
}

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

思路
1.创建头部模块 日期+头像
2.搭建日期选择 - 一周时间
3.任务卡片
4.监听日期选择 更新任务数据

//
//  Home.swift
//  TaskManagement
//
//  Created by 李宇鸿 on 2022/9/23.
//

import SwiftUI

struct Home: View {
    @StateObject var taskModel: TaskViewModel = TaskViewModel()
    @Namespace var animation
    var body: some View {
       
        ScrollView(.vertical,showsIndicators: false){
            
            // LazyVStack 和 固定头部
            LazyVStack(spacing:15,pinnedViews: [.sectionHeaders]) {
                
                Section {
// 2.当前星期视图
                    ScrollView(.horizontal,showsIndicators: false){
// 数据从视图模型获取
                        HStack(spacing:10){
                            
                            ForEach(taskModel.currentWeek,id:\.self){day in
                                
                                VStack{
                                    Text(taskModel.extractDate(date: day, format: "dd"))
                                        .font(.system(size:15))

                                    
                                    // EEE将在周一、周二、....等返回
                                    Text(taskModel.extractDate(date: day, format: "EEE"))
                                        .font(.system(size:14))
                                    
                                    Circle()
                                        .fill(.white)
                                        .frame(width:8,height: 8)
                                        .opacity(taskModel.isToday(date: day) ? 1 : 0)
                                }
                                .foregroundStyle(taskModel.isToday(date: day) ? .primary : .secondary)
                                .foregroundColor(taskModel.isToday(date: day) ? .white: .black)
                                // MARK: Capsule Shape
                                // 胶囊的形状
                                .frame(width: 45,height: 90)
                                .background(
                                    ZStack{
                                        // 几何匹配效果
                                        if taskModel.isToday(date: day){
                                                                                    Capsule()
                                                .fill(.black)
                                                .matchedGeometryEffect(id: "CURRENTDAY", in: animation)

                                        }
                                    }
                                    
                                )
                                .containerShape(Capsule())
                                .onTapGesture {
                                    // 更新当前天
                                    withAnimation {
                                        taskModel.currentDay = day
                                    }
                                }
                            }
                        }
                        .padding(.horizontal)
                        
                    }
// 2. 任务视图
                    TaskView()
                    
                } header: {
// 1.头部视图
                    HeaderView()
                }

            }
            
        }
// 防止任务视图超出安全区域
        .ignoresSafeArea(.container,edges: .top)
        
    }
// func 任务视图
    func TaskView() -> some View{
        LazyVStack(spacing:20) {
            
            if let tasks = taskModel.filteredTasks{
                if tasks.isEmpty{
                    Text("Not tasks found!!!!")
                        .font(.system(size: 16))
                        .fontWeight(.light)
                        .offset(y:100)
                }
                else{
                    ForEach(tasks){task in
                        TaskCardView(task: task)
                    }
                }
            }
            else {
                // 进程视图
                ProgressView()
                    .offset(y:100)
            }
        }
        .padding()
        .padding(.top)
        // 更新任务
        .onChange(of: taskModel.currentDay) { newValue in
            taskModel.filterTodayTasks()
        }
    }
// func 任务卡片视图
    func TaskCardView(task: Task) -> some View{
        HStack(alignment:.top,spacing: 15){
//            Text(task.taskTitle)
            VStack(spacing:10){
                Circle()
                    .fill(taskModel.isCurrentHour(date: task.taskDate) ?  .black : .clear)
                    .frame(width: 15,height: 15)
                    .background(
                        Circle()
                            .stroke(.black,lineWidth: 1)
                            .padding(-3)
                    )
                    .scaleEffect(!taskModel.isCurrentHour(date: task.taskDate) ? 0.8 : 1)
                
                Rectangle()
                    .fill(.black)
                    .frame(width:3)
            }
            
            VStack{
                HStack(alignment: .top,spacing:10) {
                    VStack(alignment: .leading,spacing:12) {
                        Text(task.taskTitle)
                            .font(.title2.bold())
                        Text(task.taskDescription)
                            .font(.callout)
                            .foregroundStyle(.secondary)
                    }
                    .hLeading()
                    
                    Text(task.taskDate.formatted(date: .omitted, time: .shortened))
                }
                
// 检查当前小时 如果是就显示参加会议的人数头像
                if taskModel.isCurrentHour(date: task.taskDate){
                    // 团队成员
                    HStack(spacing:0){
                        HStack(spacing:-10){
                            ForEach(["User1","User2","User3"],id: \.self) { user in
                                Image(user)
                                    .resizable()
                                    .aspectRatio(contentMode: .fill)
                                    .frame(width: 45,height: 45)
                                    .clipShape(Circle())
                                    .background(
                                        Circle()
                                            .stroke(.black, lineWidth: 5)
                                    )
                            }
                        }
                        .hLeading()
                        // 检查按钮
                        Button  {
                            
                        } label: {
                            Image(systemName: "checkmark")
                                .foregroundStyle(.black)
                                .padding(10)
                                .background(Color.white,in:RoundedRectangle(cornerRadius: 10))
                        }

                    }
                    .padding(.top)
                }
            }
            .foregroundColor(taskModel.isCurrentHour(date: task.taskDate) ? .white : .black)
            .padding(taskModel.isCurrentHour(date: task.taskDate) ? 15 : 0)
            .padding(.bottom,taskModel.isCurrentHour(date: task.taskDate) ? 0 : 10)
            .hLeading()
            .background(
                Color("Black")
                    .cornerRadius(25)
                    .opacity(taskModel.isCurrentHour(date: task.taskDate) ? 1 : 0)
            )
            
        }
        .hLeading()
    }
    
// func 头部视图
    func HeaderView() -> some View{
        HStack(spacing:10){
            
            VStack(alignment: .leading,spacing: 10) {
                Text(Date().formatted(date:.abbreviated,time: .omitted))
                    .foregroundColor(.gray)
                Text("Today")
                    .font(.largeTitle.bold())
            }
            .hLeading()
            
            Button  {
                
            } label: {
                Image("Profile")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(width: 45,height: 45)
                    .clipShape(Circle())
            }

        }
        .padding()
        .padding(.top,getSafeArea().top)
        .background(Color.white)
    }
}

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

// UI设计辅助函数
extension View {
    func hLeading()-> some View{
        self
            .frame(maxWidth:.infinity,alignment: .leading)
    }
    
    func hTrailing()-> some View{
        self
            .frame(maxWidth:.infinity,alignment: .trailing)
    }
    
    func hCenter()-> some View{
        self
            .frame(maxWidth:.infinity,alignment: .center)
    }
    
    // 安全区域
    func getSafeArea() -> UIEdgeInsets{
        guard let screen = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
            return .zero
        }
        
        guard let safeArea = screen.windows.first?.safeAreaInsets else {
            return .zero
        }
        return safeArea
    }
}

Task - 模型
//
//  Task.swift
//  TaskManagement
//
//  Created by 李宇鸿 on 2022/9/23.
//

import SwiftUI

// Task Model
struct Task: Identifiable{
    var id = UUID().uuidString
    var taskTitle: String
    var taskDescription: String
    var taskDate: Date
}

TaskManagement - 视图模型
//
//  TaskViewModel.swift
//  TaskManagement
//
//  Created by 李宇鸿 on 2022/9/23.
//

import SwiftUI

class TaskViewModel: ObservableObject {
    
    
    @Published var storedTasks: [Task] = [
    
        Task(taskTitle: "Meeting", taskDescription: "Discuss team task for the day", taskDate: .init(timeIntervalSince1970: 1663930000)),
        Task(taskTitle: "Icon set", taskDescription: "Edit icons for team task for next week", taskDate: .init(timeIntervalSince1970: 1663940000)),
        Task(taskTitle: "Prototype", taskDescription: "Make and send prototype", taskDate: .init(timeIntervalSince1970: 1663950000)),
        Task(taskTitle: "Check asset", taskDescription: "Start checking the assets", taskDate: .init(timeIntervalSince1970: 1663960000)),
        Task(taskTitle: "Team party", taskDescription: "Make fun with team mates", taskDate: .init(timeIntervalSince1970: 1663970000)),
        Task(taskTitle: "Client Meeting", taskDescription: "Explain project to clinet", taskDate: .init(timeIntervalSince1970: 1663998000)),
        
        Task(taskTitle: "Next Project", taskDescription: "Discuss next project with team", taskDate: .init(timeIntervalSince1970: 1664030000)),
        Task(taskTitle: "App Proposal", taskDescription: "Meet client for next App Proposal", taskDate: .init(timeIntervalSince1970: 1664100000)),
    ]

    // 当前日期的周
    @Published var currentWeek: [Date] = []

    // 当前日期的今天
    @Published var currentDay: Date = Date()

    // 过滤获取今天的任务
    @Published var filteredTasks : [Task]?
    
    init(){
        fetchCurrentWeek()
        filterTodayTasks()
    }
    
    // 过滤器今天的任务
    func filterTodayTasks(){
        DispatchQueue.global(qos: .userInteractive).async {
            let calendar = Calendar.current
            let filtered = self.storedTasks.filter {
                return calendar.isDate($0.taskDate, inSameDayAs: self.currentDay)
            }
// 倒序排列
                .sorted{ task1 ,task2  in
                    return task2.taskDate < task1.taskDate

                }
            
            
            DispatchQueue.main.async {
                withAnimation {
                    self.filteredTasks = filtered
                }
            }
        }
    }
    
    // 获取当前的星期
    func fetchCurrentWeek(){
        let today = Date()
        let calendar = Calendar.current
        
        let week = calendar.dateInterval(of: .weekOfMonth, for: today)
            
        
        guard let firstWeekDay = week?.start else {
            return
        }
        
        
        (1...7).forEach{ day in
            
            if let weekday = calendar.date(byAdding: .day, value: day, to: firstWeekDay){
                currentWeek.append(weekday)
            }
        }
    }
    
    // 提取日期
    func extractDate(date: Date,format: String) -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = format
        
        return formatter.string(from: date)
    }
    
    // 检查当前日期是否为今天
    func isToday(date:Date) -> Bool{
        let calendar = Calendar.current
        return calendar.isDate(currentDay, inSameDayAs: date)
    }
    
    // 正在检查当前的“小时”是否为任务“小时”
    func isCurrentHour(date: Date) -> Bool{
        let calendar = Calendar.current
        let hour = calendar.component(.hour, from: date)
        let currentHour = calendar.component(.hour, from: Date())
        return hour == currentHour
    }
    
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
【课程特点】1、231节大容量课程:包含了SwiftUI的大部分知识点,详细讲解SwiftUI的方方面面;2、15个超级精彩的实例:包含美食、理财、健身、教育、电子商务等各行业的App实例;3、创新的教学模式:手把手教您SwiftUI用户界面开发技术,一看就懂,一学就会;4、贴心的操作提示:让您的眼睛始终处于操作的焦点位置,不用再满屏找光标;5、语言简洁精练:瞄准问题的核心所在,减少对思维的干扰,并节省您宝贵的时间;6、视频短小精悍:即方便于您的学习和记忆,也方便日后对功能的检索;7、齐全的学习资料:提供所有课程的源码,在Xcode 11 + iOS 13环境下测试通过; 更好的应用,更少的代码!SwiftUI是苹果主推的下一代用户界面搭建技术,具有声明式语法、实时生成界面预览等特性,可以为苹果手机、苹果平板、苹果电脑、苹果电视、苹果手表五个平台搭建统一的用户界面。SwiftUI是一种创新、简单的iOS开发中的界面布局方案,可以通过Swift语言的强大功能,在所有的Apple平台上快速构建用户界面。 仅使用一组工具和API为任何Apple设备构建用户界面。SwiftUI具有易于阅读和自然编写的声明式Swift语法,可与新的Xcode设计工具无缝协作,使您的代码和设计**同步。自动支持动态类型、暗黑模式、本地化和可访问性,意味着您的**行SwiftUI代码已经是您编写过的非常强大的UI代码了。 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇夜iOS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值