[✨] 대시보드 공통, 일정, 관리 박스 생성
|
@ -324,7 +324,7 @@
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||||
SUPPORTS_MACCATALYST = NO;
|
SUPPORTS_MACCATALYST = NO;
|
||||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited) DEV";
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited) DEV LOCAL";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = 1;
|
TARGETED_DEVICE_FAMILY = 1;
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
// MARK: - ACAMATE
|
// MARK: - ACAMATE
|
||||||
// APPSTORE_URL : https://apps.apple.com/us/app/%EC%95%84%EC%B9%B4%EB%8D%B0%EB%AF%B8%EB%A9%94%EC%9D%B4%ED%8A%B8/id6739448113
|
// APPSTORE_URL : https://apps.apple.com/us/app/%EC%95%84%EC%B9%B4%EB%8D%B0%EB%AF%B8%EB%A9%94%EC%9D%B4%ED%8A%B8/id6739448113
|
||||||
#if DEV && LOCAL
|
//#if DEV && LOCAL
|
||||||
public let API_URL: String = "http://localhost:5144"
|
//public let API_URL: String = "http://localhost:5144"
|
||||||
#elseif DEV
|
//#else
|
||||||
|
#if DEV
|
||||||
public let API_URL: String = "https://devacamate.ipstein.myds.me"
|
public let API_URL: String = "https://devacamate.ipstein.myds.me"
|
||||||
#else
|
#else
|
||||||
public let API_URL: String = "https://acamate.ipstein.myds.me"
|
public let API_URL: String = "https://acamate.ipstein.myds.me"
|
||||||
|
|
74
AcaMate/1. View/10. Common/OffsetObservableScollView.swift
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
//
|
||||||
|
// OffsetObservableScollView.swift
|
||||||
|
// AcaMate
|
||||||
|
//
|
||||||
|
// Created by TAnine on 2/10/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
struct OffsetObservableScrollView<Content: View>: View {
|
||||||
|
var axes: Axis.Set = .vertical
|
||||||
|
var showsIndicators: Bool = true
|
||||||
|
|
||||||
|
@Binding var scrollOffset: CGPoint
|
||||||
|
@ViewBuilder var content: (ScrollViewProxy) -> Content
|
||||||
|
|
||||||
|
@Namespace var coordinateSpaceName: Namespace.ID
|
||||||
|
|
||||||
|
init(
|
||||||
|
_ axes: Axis.Set = .vertical,
|
||||||
|
showsIndicators: Bool = true,
|
||||||
|
scrollOffset: Binding<CGPoint>,
|
||||||
|
@ViewBuilder content: @escaping (ScrollViewProxy) -> Content
|
||||||
|
) {
|
||||||
|
self.axes = axes
|
||||||
|
self.showsIndicators = showsIndicators
|
||||||
|
self._scrollOffset = scrollOffset
|
||||||
|
self.content = content
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ScrollView(axes, showsIndicators: showsIndicators) {
|
||||||
|
ScrollViewReader { scrollViewProxy in
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
// 맨 위로 스크롤하기 위한 anchor 뷰 (id "TOP")
|
||||||
|
Color.clear
|
||||||
|
.frame(height: 1)
|
||||||
|
.id("TOP")
|
||||||
|
|
||||||
|
// 실제 콘텐츠. content 클로저에서 scrollViewProxy를 전달받으므로,
|
||||||
|
// 버튼 등을 통해 프로그래밍적으로 스크롤할 수 있음.
|
||||||
|
content(scrollViewProxy)
|
||||||
|
.overlay {
|
||||||
|
GeometryReader { geometryProxy in
|
||||||
|
Color.clear
|
||||||
|
.preference(
|
||||||
|
key: ScrollOffsetPreferenceKey.self,
|
||||||
|
value: CGPoint (
|
||||||
|
x: -geometryProxy.frame(in: .named(coordinateSpaceName)).minX,
|
||||||
|
y: -geometryProxy.frame(in: .named(coordinateSpaceName)).minY
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.coordinateSpace(name: coordinateSpaceName)
|
||||||
|
.onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in
|
||||||
|
printLog("\(scrollOffset) -> \(value)")
|
||||||
|
scrollOffset = value
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ScrollOffsetPreferenceKey: PreferenceKey {
|
||||||
|
static var defaultValue: CGPoint = .zero
|
||||||
|
|
||||||
|
static func reduce(value: inout CGPoint, nextValue: () -> CGPoint) {
|
||||||
|
value = nextValue()
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,6 +39,9 @@ struct IntroView: View {
|
||||||
|
|
||||||
.onAppear {
|
.onAppear {
|
||||||
printLog("IntroView_onAppear")
|
printLog("IntroView_onAppear")
|
||||||
|
#if LOCAL
|
||||||
|
naviState.set(act: .RESET, path: .Login)
|
||||||
|
#else
|
||||||
subscribeAlertAction()
|
subscribeAlertAction()
|
||||||
loadVersion()
|
loadVersion()
|
||||||
.sink { completion in
|
.sink { completion in
|
||||||
|
@ -66,7 +69,7 @@ struct IntroView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,49 +7,24 @@
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct AttendanceView: View {
|
struct AttendanceBoxView: View {
|
||||||
@StateObject var btnVM = ButtonViewModel()
|
|
||||||
@State private var attMoreBtnID = UUID()
|
|
||||||
|
|
||||||
@State var monthlyGroup: (Int,Int) = (5,10)//(0,0)
|
@State var monthlyGroup: (Int,Int) = (5,10)//(0,0)
|
||||||
@State var dailyGroup: (Int,Int) = (3,4)//(0,0)
|
@State var dailyGroup: (Int,Int) = (3,4)//(0,0)
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
DashBoardView(image: Image(.Icon.attendance),
|
||||||
HStack(spacing: 0){
|
title: "출석")
|
||||||
Image(.Icon.attendance)
|
{
|
||||||
.resizable()
|
|
||||||
.frame(width: 24, height: 24, alignment: .center)
|
|
||||||
.padding([.trailing],4)
|
|
||||||
Text("출석")
|
|
||||||
.font(.nps(font: .bold, size: 20))
|
|
||||||
.foregroundStyle(Color(.Text.detail))
|
|
||||||
Spacer()
|
|
||||||
SimpleBtnView(vm: btnVM, id: attMoreBtnID)
|
|
||||||
}
|
|
||||||
.padding([.bottom],2)
|
|
||||||
|
|
||||||
Rectangle()
|
|
||||||
.frame(maxWidth: .infinity, maxHeight: 2)
|
|
||||||
.foregroundStyle(Color(.Second.normal))
|
|
||||||
.padding([.bottom],12)
|
|
||||||
|
|
||||||
HStack(spacing: 4) {
|
HStack(spacing: 4) {
|
||||||
AttCellView(isDaily: false, valueGroup: $monthlyGroup)
|
AttCellView(isDaily: false, valueGroup: $monthlyGroup)
|
||||||
Spacer()
|
Spacer()
|
||||||
// Spacer(minLength: 1)
|
|
||||||
AttCellView(isDaily: true, valueGroup: $dailyGroup)
|
AttCellView(isDaily: true, valueGroup: $dailyGroup)
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
}
|
} moreAction: {
|
||||||
.padding(24)
|
|
||||||
|
|
||||||
.onAppear {
|
|
||||||
btnVM.setSize(for: attMoreBtnID, newWidth: 40, newHeight: 24)
|
|
||||||
btnVM.setText(for: attMoreBtnID, newText: "더보기", newFont: .nps(size: 12))
|
|
||||||
btnVM.setTextColor(for: attMoreBtnID, newColor: .Text.disabled)
|
|
||||||
// MARK: TO-DO
|
// MARK: TO-DO
|
||||||
// 더보기 동작 로직 추가
|
// 출석 쪽 더보기 동작 만들기
|
||||||
|
print("123")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,7 +46,6 @@ struct AttCellView: View {
|
||||||
.foregroundStyle(Color(.Text.detail))
|
.foregroundStyle(Color(.Text.detail))
|
||||||
Text("출석").font(.nps(size: 12))
|
Text("출석").font(.nps(size: 12))
|
||||||
.foregroundStyle(Color(.Text.detail))
|
.foregroundStyle(Color(.Text.detail))
|
||||||
// Spacer()
|
|
||||||
}
|
}
|
||||||
.padding(.top,4)
|
.padding(.top,4)
|
||||||
HStack(alignment: .center, spacing: 2) {
|
HStack(alignment: .center, spacing: 2) {
|
||||||
|
@ -110,7 +84,3 @@ struct AttCellView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
|
||||||
AttendanceView()
|
|
||||||
}
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
//
|
||||||
|
// CalendarBoxView.swift
|
||||||
|
// AcaMate
|
||||||
|
//
|
||||||
|
// Created by TAnine on 2/10/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct CalendarBoxView: View {
|
||||||
|
@State var summaryCalDataList: [SummaryCalendar]
|
||||||
|
var body: some View {
|
||||||
|
|
||||||
|
DashBoardView(image: Image(.Icon.calendar), title: "최근 일정") {
|
||||||
|
VStack(spacing: 12) {
|
||||||
|
ForEach(Array(summaryCalDataList.enumerated()), id: \.offset) { index, data in
|
||||||
|
CalCellView(summaryCalData: data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} moreAction: {
|
||||||
|
printLog("최근일정의 더보기")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CalCellView: View {
|
||||||
|
var summaryCalData: SummaryCalendar
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 8) {
|
||||||
|
HStack (spacing: 12) {
|
||||||
|
Image(.Icon.clock)
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 24, height: 24, alignment: .center)
|
||||||
|
Text("\(summaryCalData.date)")
|
||||||
|
.font(.nps(size: 20))
|
||||||
|
.foregroundStyle(Color(.Text.detail))
|
||||||
|
Spacer(minLength: 1)
|
||||||
|
}
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
Spacer(minLength: 1)
|
||||||
|
Text("\(summaryCalData.summary)")
|
||||||
|
.font(.nps(size: 20))
|
||||||
|
.foregroundStyle(Color(.Text.black))
|
||||||
|
.lineLimit(1)
|
||||||
|
.minimumScaleFactor(0.5)
|
||||||
|
.truncationMode(.tail)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.padding(12)
|
||||||
|
.background {
|
||||||
|
RoundedRectangle(cornerRadius: 4)
|
||||||
|
.stroke(Color(.Second.normal), lineWidth: 2)
|
||||||
|
.fill(Color(.Second.light))
|
||||||
|
}
|
||||||
|
.onTapGesture {
|
||||||
|
printLog("캘린더 내부 셀 클릭")
|
||||||
|
// MARK: TO-DO
|
||||||
|
// Summary의 날짜 위치로 이동하게 하는걸로
|
||||||
|
// SummaryCalendar' id 를 이용하는걸로
|
||||||
|
// 해당 날짜의 해당 이벤트가 아니라 그냥 해당 날짜로 이동하는게 좋을 것으로 보임
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
53
AcaMate/1. View/12. Main/122. DashBoard/DashBoardView.swift
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
//
|
||||||
|
// DashBoardView.swift
|
||||||
|
// AcaMate
|
||||||
|
//
|
||||||
|
// Created by TAnine on 2/10/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct DashBoardView<Content: View>: View {
|
||||||
|
@StateObject var btnVM = ButtonViewModel()
|
||||||
|
@State private var attMoreBtnID = UUID()
|
||||||
|
let image: Image
|
||||||
|
let title: String
|
||||||
|
@ViewBuilder let content: Content
|
||||||
|
var moreAction: VOID_TO_VOID?
|
||||||
|
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
HStack(spacing: 0){
|
||||||
|
image
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 24, height: 24, alignment: .center)
|
||||||
|
.padding([.trailing],4)
|
||||||
|
Text("\(title)")
|
||||||
|
.font(.nps(font: .bold, size: 20))
|
||||||
|
.foregroundStyle(Color(.Text.detail))
|
||||||
|
Spacer()
|
||||||
|
SimpleBtnView(vm: btnVM, id: attMoreBtnID)
|
||||||
|
}
|
||||||
|
.padding([.bottom],2)
|
||||||
|
|
||||||
|
Rectangle()
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: 2)
|
||||||
|
.foregroundStyle(Color(.Second.normal))
|
||||||
|
.padding([.bottom],12)
|
||||||
|
|
||||||
|
content
|
||||||
|
}
|
||||||
|
.padding(24)
|
||||||
|
.onAppear {
|
||||||
|
btnVM.setSize(for: attMoreBtnID, newWidth: 40, newHeight: 24)
|
||||||
|
btnVM.setText(for: attMoreBtnID, newText: "더보기", newFont: .nps(size: 12))
|
||||||
|
btnVM.setTextColor(for: attMoreBtnID, newColor: .Text.disabled)
|
||||||
|
|
||||||
|
if let action = moreAction {
|
||||||
|
btnVM.setAction(for: attMoreBtnID, newAction: action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
//
|
||||||
|
// ManagementBoxView.swift
|
||||||
|
// AcaMate
|
||||||
|
//
|
||||||
|
// Created by TAnine on 2/10/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ManagementBoxView: View {
|
||||||
|
@StateObject var btnVM = ButtonViewModel()
|
||||||
|
@State private var leftBtnID = UUID()
|
||||||
|
@State private var rightBtnID = UUID()
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
DashBoardView(image: Image(.Icon.edu), title: "학습 관리") {
|
||||||
|
|
||||||
|
} moreAction: {
|
||||||
|
printLog("학습 관리의 더보기")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MangCellView: View {
|
||||||
|
@StateObject var btnVM = ButtonViewModel()
|
||||||
|
let summaryMang: SummaryManagement
|
||||||
|
@State var ratioBtn = UUID()
|
||||||
|
@State var flagBtn = UUID()
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 8) {
|
||||||
|
HStack(spacing: 12) {
|
||||||
|
Image(.Icon.management)
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 24, height: 24, alignment: .center)
|
||||||
|
Text("\(summaryMang.title)")
|
||||||
|
.font(.nps(size: 20))
|
||||||
|
.foregroundStyle(Color(.Text.title))
|
||||||
|
Spacer(minLength: 1)
|
||||||
|
}
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
Spacer(minLength: 1)
|
||||||
|
Text("\(summaryMang.teacher)")
|
||||||
|
.font(.nps(size: 12))
|
||||||
|
.foregroundStyle(Color(.Text.detail))
|
||||||
|
Text("이름")
|
||||||
|
.font(.nps(size: 12))
|
||||||
|
.foregroundStyle(Color(.Text.detail))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
// 상단 내용은 여까지
|
||||||
|
HStack(spacing: 6) {
|
||||||
|
HStack(spacing: 10) {
|
||||||
|
CircleBtnView(vm: btnVM, id: ratioBtn)
|
||||||
|
VStack(spacing: 10) {
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
Text("학습")
|
||||||
|
.font(.nps(size: 16))
|
||||||
|
.foregroundStyle(Color(.Text.detail))
|
||||||
|
Text("진도")
|
||||||
|
.font(.nps(font: .bold, size: 16))
|
||||||
|
.foregroundStyle(Color(.Text.detail))
|
||||||
|
Spacer(minLength: 1)
|
||||||
|
}
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
Spacer(minLength: 1)
|
||||||
|
Text("\(summaryMang.ratio)")
|
||||||
|
.font(.nps(font: .bold, size: 24))
|
||||||
|
.foregroundStyle(summaryMang.ratio > 70 ? Color(.Other.blue):Color(.Other.red))
|
||||||
|
Text("%")
|
||||||
|
.font(.nps(font: .bold, size: 20))
|
||||||
|
.foregroundStyle(Color(.Text.detail))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,43 +13,69 @@ struct MainView: View {
|
||||||
@State var cancellables: Set<AnyCancellable> = []
|
@State var cancellables: Set<AnyCancellable> = []
|
||||||
@Binding var naviState : NaviState
|
@Binding var naviState : NaviState
|
||||||
|
|
||||||
|
@State private var scrollOffset: CGPoint = .zero
|
||||||
|
@State private var topViewState: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
TopView(titleName: "Name")
|
ZStack {
|
||||||
// MARK: TO-DO
|
OffsetObservableScrollView(scrollOffset: $scrollOffset) { proxy in
|
||||||
// 스크롤뷰 해서 탑뷰 가리는거 이제 만들어 보기
|
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
TopProfileView()
|
TopProfileView()
|
||||||
.padding(EdgeInsets(top: 0, leading: 0, bottom: 12, trailing: 0))
|
.padding(EdgeInsets(top: 0, leading: 0, bottom: 12, trailing: 0))
|
||||||
AttendanceView()
|
AttendanceBoxView()
|
||||||
.background {
|
.background {
|
||||||
RoundedRectangle(cornerRadius: 8)
|
RoundedRectangle(cornerRadius: 8)
|
||||||
.foregroundStyle(Color(.Other.cell))
|
.foregroundStyle(Color(.Other.cell))
|
||||||
}
|
}
|
||||||
.padding(EdgeInsets(top: 12, leading: 24, bottom: 12, trailing: 24))
|
.padding(EdgeInsets(top: 12, leading: 24, bottom: 12, trailing: 24))
|
||||||
AttendanceView()
|
AttendanceBoxView()
|
||||||
.background {
|
.background {
|
||||||
RoundedRectangle(cornerRadius: 8)
|
RoundedRectangle(cornerRadius: 8)
|
||||||
.foregroundStyle(Color(.Other.cell))
|
.foregroundStyle(Color(.Other.cell))
|
||||||
}
|
}
|
||||||
.padding(EdgeInsets(top: 12, leading: 24, bottom: 12, trailing: 24))
|
.padding(EdgeInsets(top: 12, leading: 24, bottom: 12, trailing: 24))
|
||||||
AttendanceView()
|
AttendanceBoxView()
|
||||||
.background {
|
.background {
|
||||||
RoundedRectangle(cornerRadius: 8)
|
RoundedRectangle(cornerRadius: 8)
|
||||||
.foregroundStyle(Color(.Other.cell))
|
.foregroundStyle(Color(.Other.cell))
|
||||||
}
|
}
|
||||||
.padding(EdgeInsets(top: 12, leading: 24, bottom: 12, trailing: 24))
|
.padding(EdgeInsets(top: 12, leading: 24, bottom: 12, trailing: 24))
|
||||||
AttendanceView()
|
CalendarBoxView(summaryCalDataList: [
|
||||||
|
SummaryCalendar(id: "123", date: "2025-02-28", summary: "요약내용입니다."),
|
||||||
|
SummaryCalendar(id: "123", date: "2025-02-28", summary: "요약내용입니다.")])
|
||||||
|
|
||||||
.background {
|
.background {
|
||||||
RoundedRectangle(cornerRadius: 8)
|
RoundedRectangle(cornerRadius: 8)
|
||||||
.foregroundStyle(Color(.Other.cell))
|
.foregroundStyle(Color(.Other.cell))
|
||||||
}
|
}
|
||||||
.padding(EdgeInsets(top: 12, leading: 24, bottom: 12, trailing: 24))
|
.padding(EdgeInsets(top: 12, leading: 24, bottom: 12, trailing: 24))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if topViewState {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
TopView(titleName: "Name")
|
||||||
|
.transition(.move(edge: .top))
|
||||||
|
.animation(.easeInOut, value: scrollOffset)
|
||||||
|
Spacer(minLength: 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
BottomView()
|
BottomView()
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
.onChange(of: scrollOffset.y) { oldValue, newValue in
|
||||||
|
if newValue > 200 && topViewState == false {
|
||||||
|
topViewState = true
|
||||||
|
} else if newValue < 20 && topViewState == true{
|
||||||
|
topViewState = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
AcaMate/2. Model/Calendar Data.swift
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
//
|
||||||
|
// SimpleCalendar.swift
|
||||||
|
// AcaMate
|
||||||
|
//
|
||||||
|
// Created by TAnine on 2/10/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct SummaryCalendar {
|
||||||
|
var id: String
|
||||||
|
var date: String
|
||||||
|
var summary: String
|
||||||
|
}
|
16
AcaMate/2. Model/Management Data.swift
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
//
|
||||||
|
// ManagementData.swift
|
||||||
|
// AcaMate
|
||||||
|
//
|
||||||
|
// Created by TAnine on 2/10/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct SummaryManagement {
|
||||||
|
var id: String
|
||||||
|
var title: String
|
||||||
|
var teacher: String
|
||||||
|
var ratio: Int
|
||||||
|
var homework: Int
|
||||||
|
}
|
|
@ -72,6 +72,42 @@ struct LoadingModifier: ViewModifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PressEffect: ViewModifier {
|
||||||
|
@State private var isPressed = false
|
||||||
|
var scale: CGFloat
|
||||||
|
var opacity: CGFloat
|
||||||
|
var duration: Double
|
||||||
|
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
content
|
||||||
|
.scaleEffect(isPressed ? scale : 1.0)
|
||||||
|
.opacity(isPressed ? opacity : 1.0)
|
||||||
|
.animation(.easeOut(duration: duration), value: isPressed)
|
||||||
|
.simultaneousGesture(
|
||||||
|
LongPressGesture(minimumDuration: 0.01)
|
||||||
|
.onChanged { _ in isPressed = true }
|
||||||
|
.onEnded { _ in isPressed = false }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct PressBackgroundEffect: ViewModifier {
|
||||||
|
@State private var isPressed = false
|
||||||
|
var backgroundColor: Color
|
||||||
|
var duration: Double
|
||||||
|
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
content
|
||||||
|
.background(isPressed ? backgroundColor : Color.clear)
|
||||||
|
.animation(.easeOut(duration: duration), value: isPressed)
|
||||||
|
.simultaneousGesture(
|
||||||
|
LongPressGesture(minimumDuration: 0.01)
|
||||||
|
.onChanged { _ in isPressed = true }
|
||||||
|
.onEnded { _ in isPressed = false }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
extension View {
|
extension View {
|
||||||
/// View에 전체적으로 색 입히기
|
/// View에 전체적으로 색 입히기
|
||||||
func fullDrawView(_ backColor: Color) -> some View {
|
func fullDrawView(_ backColor: Color) -> some View {
|
||||||
|
@ -105,15 +141,13 @@ extension View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
func pressAnimation(scale: CGFloat = 0.95, opacity: CGFloat = 0.85, duration: Double = 0.1) -> some View {
|
||||||
func switchButtonStyle(_ animate: Bool) -> some View {
|
self.modifier(PressEffect(scale: scale, opacity: opacity, duration: duration))
|
||||||
if animate {
|
|
||||||
self.buttonStyle(DefaultButtonStyle())
|
|
||||||
} else {
|
|
||||||
self.buttonStyle(PlainButtonStyle())
|
|
||||||
// .allowsHitTesting(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//extension BUtton
|
}
|
||||||
|
}
|
||||||
|
extension View {
|
||||||
|
func pressColorAnimation(backgroundColor: Color = Color.black.opacity(0.1), duration: Double = 0.1) -> some View {
|
||||||
|
self.modifier(PressBackgroundEffect(backgroundColor: backgroundColor, duration: duration))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Clock.imageset/Clock.png
vendored
Normal file
After Width: | Height: | Size: 1.8 KiB |
15
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Clock.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Clock.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"preserves-vector-representation" : true
|
||||||
|
}
|
||||||
|
}
|
15
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Edu.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Edu.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"preserves-vector-representation" : true
|
||||||
|
}
|
||||||
|
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Edu.imageset/Edu.png
vendored
Normal file
After Width: | Height: | Size: 1.2 KiB |
15
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Left.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Left.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"preserves-vector-representation" : true
|
||||||
|
}
|
||||||
|
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Left.imageset/Left.png
vendored
Normal file
After Width: | Height: | Size: 505 B |
15
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Right.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Right.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"preserves-vector-representation" : true
|
||||||
|
}
|
||||||
|
}
|
BIN
AcaMate/6. Resources/Assets.xcassets/Image Folder/Icon/Right.imageset/Right.png
vendored
Normal file
After Width: | Height: | Size: 470 B |
BIN
AcaMate/6. Resources/Images/Icon/Clock.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
AcaMate/6. Resources/Images/Icon/Edu.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
AcaMate/6. Resources/Images/Icon/Left.png
Normal file
After Width: | Height: | Size: 505 B |
BIN
AcaMate/6. Resources/Images/Icon/Right.png
Normal file
After Width: | Height: | Size: 470 B |