diff --git a/BudgetBuddies/BudgetBuddies/BudgetBuddiesTests/CategoryRouterTests.swift b/BudgetBuddies/BudgetBuddies/BudgetBuddiesTests/CategoryRouterTests.swift index abcd1058..324fa5dc 100644 --- a/BudgetBuddies/BudgetBuddies/BudgetBuddiesTests/CategoryRouterTests.swift +++ b/BudgetBuddies/BudgetBuddies/BudgetBuddiesTests/CategoryRouterTests.swift @@ -31,8 +31,7 @@ final class CategoryRouterTests: XCTestCase { /// /categories/add/{userId} 엔드포인트 테스트 메소드 func testAddCategory() { // Request Variable - let categoryRequestDTO = CategoryRequestDTO( - userID: self.userId, name: "프론트엔드 화이팅", isDefault: false) + let categoryRequestDTO = CategoryRequestDTO(name: "프론트엔드 화이팅", isDefault: false) // Network Transmitting Code provider.request(.addCategory(userId: self.userId, categoryRequest: categoryRequestDTO)) { @@ -74,7 +73,7 @@ final class CategoryRouterTests: XCTestCase { // - none // Network Transmitting Code - provider.request(.getCategory(userId: self.userId)) { result in + provider.request(.getCategories(userId: self.userId)) { result in defer { self.expectation.fulfill() } switch result { @@ -106,4 +105,26 @@ final class CategoryRouterTests: XCTestCase { // MARK: - 카테고리 제거 + func testDeleteCategory() { + let categoryId = 13 + provider.request(.deleteCategory(userId: self.userId, categoryId: categoryId)) { result in + defer { self.expectation.fulfill() } + + switch result { + case .success(let response): + debugPrint("/categories/delete/{categoryId} API 연결 성공") + debugPrint(response.statusCode) + XCTAssertEqual(response.statusCode, 200, ErrorMessage.TellsWhatRightStatusCodeIs) + debugPrint(response.request?.url as Any) + case .failure(let error): + XCTFail("/categories/delete/{categoryId} API 연결 실패 : \(error.localizedDescription)") + } + } + + waitForExpectations(timeout: self.timeoutValue) { error in + if let error = error { + XCTFail("카테고리 제거 테스팅 간 에러 발생 : \(error.localizedDescription)") + } + } + } } diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/100.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/100.png index a7de75d5..6a9f8956 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/100.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/100.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/1024.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/1024.png index 5808ef10..e4395ea9 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/1024.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/1024.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/114.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/114.png index ba127d13..8cd89016 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/114.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/114.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/120.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/120.png index 2caf01d3..8a324d18 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/120.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/120.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/144.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/144.png index 7dc21a7e..8cd16865 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/144.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/144.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/152.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/152.png index 2aa9d5ed..84978ddd 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/152.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/152.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/167.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/167.png index fa5760cc..3c4fe2b4 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/167.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/167.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/180.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/180.png index 4d18c57f..1c7f1214 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/180.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/180.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/20.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/20.png index 9a823342..383aa8a7 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/20.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/20.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/29.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/29.png index 98cecf36..542b0547 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/29.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/29.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/40.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/40.png index 1238d833..d24ce6e3 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/40.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/40.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/50.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/50.png index b1800268..329e6c08 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/50.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/50.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/57.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/57.png index 6518fa66..4d79268e 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/57.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/57.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/58.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/58.png index c66e84d5..e1960eda 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/58.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/58.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/60.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/60.png index 66f872e8..ccf5cc6c 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/60.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/60.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/72.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/72.png index f1897f03..bcd3db8f 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/72.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/72.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/76.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/76.png index c026c304..a8803baa 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/76.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/76.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/80.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/80.png index 6da0b6c9..d659d532 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/80.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/80.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/87.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/87.png index ae9168b6..9ed0e5c5 100644 Binary files a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/87.png and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/87.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json index 65b74d7e..4fdf8826 100644 --- a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1 +1,158 @@ -{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"72x72","expected-size":"72","filename":"72.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"50x50","expected-size":"100","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"50x50","expected-size":"50","filename":"50.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"72x72","expected-size":"144","filename":"144.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"}]} \ No newline at end of file +{ + "images" : [ + { + "filename" : "40.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "60.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "29.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "58.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "87.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "80.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "120.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "57.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "57x57" + }, + { + "filename" : "114.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "57x57" + }, + { + "filename" : "120.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "180.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "filename" : "20.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "filename" : "40.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "29.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "58.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "40.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "80.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "50.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "50x50" + }, + { + "filename" : "100.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "50x50" + }, + { + "filename" : "72.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "72x72" + }, + { + "filename" : "144.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "72x72" + }, + { + "filename" : "76.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "152.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "167.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "1024.png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/pieChartBackImage.imageset/Contents.json b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/pieChartBackImage.imageset/Contents.json new file mode 100644 index 00000000..17f6fd94 --- /dev/null +++ b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/pieChartBackImage.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Image.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/pieChartBackImage.imageset/Image.png b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/pieChartBackImage.imageset/Image.png new file mode 100644 index 00000000..eb6943d0 Binary files /dev/null and b/BudgetBuddies/BudgetBuddies/Resources/Assets.xcassets/pieChartBackImage.imageset/Image.png differ diff --git a/BudgetBuddies/BudgetBuddies/Resources/ko.lproj/LaunchScreen.strings b/BudgetBuddies/BudgetBuddies/Resources/ko.lproj/LaunchScreen.strings new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/BudgetBuddies/BudgetBuddies/Resources/ko.lproj/LaunchScreen.strings @@ -0,0 +1 @@ + diff --git a/BudgetBuddies/BudgetBuddies/Sources/Extensions/UITextField+.swift b/BudgetBuddies/BudgetBuddies/Sources/Extensions/UITextField+.swift index e2c00891..7b281d76 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Extensions/UITextField+.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Extensions/UITextField+.swift @@ -20,4 +20,24 @@ extension UITextField { .kern, value: spacing, range: NSRange(location: 0, length: attributedString.length)) self.attributedText = attributedString } + + /// 텍스트필드 왼쪽에 빈 뷰를 추가하는 함수입니다. + /// + /// - Parameters: + /// - width: 빈뷰의 너비값입니다. + /// - height: 빈뷰의 높이값입니다. + func addLeftView(width: Int, height: Int) { + let leftView = UIView(frame: CGRect(x: 0, y: 0, width: width, height: height)) + + self.leftView = leftView + self.leftViewMode = .always + } + + /// TextField에서 사용하기 불편한 세팅을 제거합니다. + func setComfortableTextField() { + self.autocapitalizationType = .none // 첫 글자 자동 대문자 + self.autocorrectionType = .no // 추천 글자를 보여줄지 + self.spellCheckingType = .no // 오류난 글자를 고쳐줄지 + self.clearButtonMode = .always // 텍스트필드에 전체 지우기 버튼 생성 + } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Extensions/UIView+.swift b/BudgetBuddies/BudgetBuddies/Sources/Extensions/UIView+.swift index 8d487f37..d5e81622 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Extensions/UIView+.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Extensions/UIView+.swift @@ -16,4 +16,12 @@ extension UIView { func addSubviews(_ views: UIView...) { views.forEach { addSubview($0) } } + + func setShadow(opacity: Float, Radius: CGFloat, offSet: CGSize) { + self.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.1).cgColor + self.layer.shadowOpacity = opacity + self.layer.shadowRadius = Radius / 2 //반경 (피그마랑 비슷하게 가려면 절반을 나눠야 함..) + self.layer.shadowOffset = offSet + self.layer.masksToBounds = false + } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Extensions/UIViewController+.swift b/BudgetBuddies/BudgetBuddies/Sources/Extensions/UIViewController+.swift new file mode 100644 index 00000000..17a591dd --- /dev/null +++ b/BudgetBuddies/BudgetBuddies/Sources/Extensions/UIViewController+.swift @@ -0,0 +1,54 @@ +// +// UIViewController+.swift +// BudgetBuddies +// +// Created by 김승원 on 8/22/24. +// + +import UIKit + +extension UIViewController { + + /// 기본 네비게이션바 세팅하는 함수입니다. + /// + /// - Parameter backgroundColor: 배경 색입니다. + func setupDefaultNavigationBar(backgroundColor: UIColor) { + let appearance = UINavigationBarAppearance() + appearance.configureWithDefaultBackground() + // 색상 + appearance.backgroundColor = backgroundColor + appearance.shadowColor = nil + + // 네비게이션 바 타이틀 폰트, 자간 설정 + let titleFont = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 18) + let titleAttributes: [NSAttributedString.Key: Any] = [ + .font: titleFont, + .foregroundColor: BudgetBuddiesAsset.AppColor.textBlack.color, + .kern: -0.45, + ] + + appearance.titleTextAttributes = titleAttributes + + navigationController?.navigationBar.standardAppearance = appearance + navigationController?.navigationBar.compactAppearance = appearance + navigationController?.navigationBar.scrollEdgeAppearance = appearance + navigationController?.navigationBar.isHidden = false + } + + /// 네비게이션바 회색 백 버튼을 설정하는 함수입니다. + /// + /// - Parameter selector: selector함수입니다. #selector(<함수이름>)형식입니다. + func addBackButton(selector: Selector) { + lazy var backButton: UIBarButtonItem = { + let btn = UIBarButtonItem( + image: UIImage(systemName: "chevron.left"), + style: .done, + target: self, + action: selector) + btn.tintColor = BudgetBuddiesAsset.AppColor.subGray.color + return btn + }() + + navigationItem.leftBarButtonItem = backButton + } +} diff --git a/BudgetBuddies/BudgetBuddies/Sources/Network/CategoryController/DTO/CategoryRequestDTO.swift b/BudgetBuddies/BudgetBuddies/Sources/Network/CategoryController/DTO/CategoryRequestDTO.swift index 3fcc1b10..7c98a851 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Network/CategoryController/DTO/CategoryRequestDTO.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Network/CategoryController/DTO/CategoryRequestDTO.swift @@ -10,12 +10,10 @@ import Foundation // MARK: - CategoryRequest struct CategoryRequestDTO: Codable { - let userID: Int let name: String let isDefault: Bool enum CodingKeys: String, CodingKey { - case userID = "userId" case name, isDefault } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Network/CategoryController/Router/CategoryRouter.swift b/BudgetBuddies/BudgetBuddies/Sources/Network/CategoryController/Router/CategoryRouter.swift index 9db5f711..71d9a8ec 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Network/CategoryController/Router/CategoryRouter.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Network/CategoryController/Router/CategoryRouter.swift @@ -15,8 +15,13 @@ import Moya */ enum CategoryRouter { + + /// 카테고리 추가 엔드포인트 case addCategory(userId: Int, categoryRequest: CategoryRequestDTO) - case getCategory(userId: Int) + /// 카테고리 조회 엔드포인트 + case getCategories(userId: Int) + /// 카테고리 제거 엔드포인트 + case deleteCategory(userId: Int, categoryId: Int) } extension CategoryRouter: TargetType { @@ -28,8 +33,10 @@ extension CategoryRouter: TargetType { switch self { case .addCategory(let userId, _): return "/categories/add/\(userId)" - case .getCategory(let userId): + case .getCategories(let userId): return "categories/get/\(userId)" + case .deleteCategory(_, let categoryId): + return "categories/delete/\(categoryId)" } } @@ -37,8 +44,10 @@ extension CategoryRouter: TargetType { switch self { case .addCategory: return .post - case .getCategory: + case .getCategories: return .get + case .deleteCategory: + return .delete } } @@ -46,8 +55,10 @@ extension CategoryRouter: TargetType { switch self { case .addCategory(_, let categoryRequest): return .requestJSONEncodable(categoryRequest) - case .getCategory: + case .getCategories: return .requestPlain + case .deleteCategory(let userId, _): + return .requestParameters(parameters: ["userId": userId], encoding: URLEncoding.queryString) } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Network/DiscountInfoController/DTO/DiscountOneResponseDTO.swift b/BudgetBuddies/BudgetBuddies/Sources/Network/DiscountInfoController/DTO/DiscountOneResponseDTO.swift new file mode 100644 index 00000000..b679ea78 --- /dev/null +++ b/BudgetBuddies/BudgetBuddies/Sources/Network/DiscountInfoController/DTO/DiscountOneResponseDTO.swift @@ -0,0 +1,29 @@ +// +// DiscountOneResponseDTO.swift +// BudgetBuddies +// +// Created by 김승원 on 8/22/24. +// + +import Foundation + +// MARK: - DiscountResponseDTO +struct DiscountOneResponseDTO: Codable { + let isSuccess: Bool + let code, message: String + let result: DiscountResult +} + +// MARK: - Result +struct DiscountResult: Codable { + let id: Int + let title, startDate, endDate: String + let anonymousNumber, discountRate, likeCount: Int + let siteURL, thumbnailURL: String + + enum CodingKeys: String, CodingKey { + case id, title, startDate, endDate, anonymousNumber, discountRate, likeCount + case siteURL = "siteUrl" + case thumbnailURL = "thumbnailUrl" + } +} diff --git a/BudgetBuddies/BudgetBuddies/Sources/Network/DiscountInfoController/NetworkManager/DiscountInfoManager.swift b/BudgetBuddies/BudgetBuddies/Sources/Network/DiscountInfoController/NetworkManager/DiscountInfoManager.swift index 0299d3a9..892441e7 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Network/DiscountInfoController/NetworkManager/DiscountInfoManager.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Network/DiscountInfoController/NetworkManager/DiscountInfoManager.swift @@ -15,6 +15,7 @@ final class DiscountInfoManager { let DiscountInfoProvider = MoyaProvider() typealias DiscountInfoNetworkCompletion = (Result) -> Void + typealias DiscountsLikesNetworkCompletion = (Result) -> Void func fetchDiscounts( request: InfoRequestDTO, completion: @escaping (DiscountInfoNetworkCompletion) @@ -26,8 +27,38 @@ final class DiscountInfoManager { print("통신 성공.... 데이터 디코딩 시작") do { let decoder = JSONDecoder() - let DiscountsResponse = try decoder.decode(DiscountsResponseDTO.self, from: response.data) - completion(.success(DiscountsResponse)) + let discountsResponse = try decoder.decode(DiscountsResponseDTO.self, from: response.data) + completion(.success(discountsResponse)) + + } catch { + print("데이터 디코딩 실패") + completion(.failure(error)) + } + + case .failure(let error): + print("통신 에러 발생") + completion(.failure(error)) + } + } + } + + // MARK: - 할인 정보에 좋아요 누르기 + func postDiscountsLikes( + userId: Int, discountInfoId: Int, completion: @escaping (DiscountsLikesNetworkCompletion) + ) { + + DiscountInfoProvider.request( + .postDiscountsLikes(userId: userId, discountInfoId: discountInfoId) + ) { result in + + switch result { + case .success(let response): + print("통신 성공") + do { + let decoder = JSONDecoder() + let discountOneResponse = try decoder.decode( + DiscountOneResponseDTO.self, from: response.data) + completion(.success(discountOneResponse)) } catch { print("데이터 디코딩 실패") diff --git a/BudgetBuddies/BudgetBuddies/Sources/Network/DiscountInfoController/Router/DiscountInfoRouter.swift b/BudgetBuddies/BudgetBuddies/Sources/Network/DiscountInfoController/Router/DiscountInfoRouter.swift index 2b22f7d0..60158f16 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Network/DiscountInfoController/Router/DiscountInfoRouter.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Network/DiscountInfoController/Router/DiscountInfoRouter.swift @@ -10,6 +10,7 @@ import Moya enum DiscountInfoRouter { case getDiscounts(request: InfoRequestDTO) + case postDiscountsLikes(userId: Int, discountInfoId: Int) } extension DiscountInfoRouter: TargetType { @@ -21,6 +22,9 @@ extension DiscountInfoRouter: TargetType { switch self { case .getDiscounts: return "/discounts" + + case .postDiscountsLikes(_, let discountInfoId): + return "/discounts/likes/\(discountInfoId)" } } @@ -28,6 +32,9 @@ extension DiscountInfoRouter: TargetType { switch self { case .getDiscounts: return .get + + case .postDiscountsLikes: + return .post } } @@ -41,14 +48,17 @@ extension DiscountInfoRouter: TargetType { "size": request.size, ] return .requestParameters(parameters: parameters, encoding: URLEncoding.default) + + case .postDiscountsLikes(let userId, _): + let parameters: [String: Any] = [ + "userId": userId + ] + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) // path만 쓰면 .queryString } } var headers: [String: String]? { - switch self { - case .getDiscounts: - return ["Content-type": "application/json"] - } + return ["Content-type": "application/json"] } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Network/SupportInfoController/DTO/SupportOneResponseDTO.swift b/BudgetBuddies/BudgetBuddies/Sources/Network/SupportInfoController/DTO/SupportOneResponseDTO.swift new file mode 100644 index 00000000..e2c014ad --- /dev/null +++ b/BudgetBuddies/BudgetBuddies/Sources/Network/SupportInfoController/DTO/SupportOneResponseDTO.swift @@ -0,0 +1,29 @@ +// +// SupportOneResponseDTO.swift +// BudgetBuddies +// +// Created by 김승원 on 8/22/24. +// + +import Foundation + +// MARK: - SupportResponseDTO +struct SupportOneResponseDTO: Codable { + let isSuccess: Bool + let code, message: String + let result: SupportResult +} + +// MARK: - Result +struct SupportResult: Codable { + let id: Int + let title, startDate, endDate: String + let anonymousNumber, likeCount: Int + let siteURL, thumbnailURL: String + + enum CodingKeys: String, CodingKey { + case id, title, startDate, endDate, anonymousNumber, likeCount + case siteURL = "siteUrl" + case thumbnailURL = "thumbnailUrl" + } +} diff --git a/BudgetBuddies/BudgetBuddies/Sources/Network/SupportInfoController/NetwortManager/SupportInfoManager.swift b/BudgetBuddies/BudgetBuddies/Sources/Network/SupportInfoController/NetwortManager/SupportInfoManager.swift index 937d9147..b567042f 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Network/SupportInfoController/NetwortManager/SupportInfoManager.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Network/SupportInfoController/NetwortManager/SupportInfoManager.swift @@ -15,6 +15,7 @@ final class SupportInfoManager { let SupportInfoProvider = MoyaProvider() typealias SupportInfoNetworkCompletion = (Result) -> Void + typealias SupportsLikesNetworkCompletion = (Result) -> Void func fetchSupports(request: InfoRequestDTO, completion: @escaping (SupportInfoNetworkCompletion)) { @@ -25,8 +26,8 @@ final class SupportInfoManager { print("통신 성공.... 데이터 디코딩 시작") do { let decoder = JSONDecoder() - let SupportsResponse = try decoder.decode(SupportsResponseDTO.self, from: response.data) - completion(.success(SupportsResponse)) + let supportsResponse = try decoder.decode(SupportsResponseDTO.self, from: response.data) + completion(.success(supportsResponse)) } catch { print("데이터 디코딩 실패") @@ -39,4 +40,32 @@ final class SupportInfoManager { } } } + + func postSupportsLikes( + userId: Int, supportInfoId: Int, completion: @escaping (SupportsLikesNetworkCompletion) + ) { + + SupportInfoProvider.request(.postSupportsLikes(userId: userId, supportInfoId: supportInfoId)) { + result in + + switch result { + case .success(let response): + print("통신 성공") + do { + let decoder = JSONDecoder() + let supportOneResponse = try decoder.decode( + SupportOneResponseDTO.self, from: response.data) + completion(.success(supportOneResponse)) + } catch { + print("데이터 디코딩 실패") + completion(.failure(error)) + } + + case .failure(let error): + print("통신 에러 발생") + completion(.failure(error)) + } + } + + } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Network/SupportInfoController/Router/SupportInfoRouter.swift b/BudgetBuddies/BudgetBuddies/Sources/Network/SupportInfoController/Router/SupportInfoRouter.swift index 31f2bc11..bf41b52a 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Network/SupportInfoController/Router/SupportInfoRouter.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Network/SupportInfoController/Router/SupportInfoRouter.swift @@ -12,6 +12,7 @@ import Moya enum SupportInfoRouter { case getSupports(request: InfoRequestDTO) + case postSupportsLikes(userId: Int, supportInfoId: Int) } extension SupportInfoRouter: TargetType { @@ -26,6 +27,9 @@ extension SupportInfoRouter: TargetType { switch self { // 여기서 {id}필요하면 사용 case .getSupports: return "/supports" + + case .postSupportsLikes(_, let supportInfoId): + return "/supports/likes/\(supportInfoId)" } } @@ -34,6 +38,9 @@ extension SupportInfoRouter: TargetType { switch self { case .getSupports: return .get + + case .postSupportsLikes: + return .post } } @@ -48,15 +55,18 @@ extension SupportInfoRouter: TargetType { "size": request.size, ] return .requestParameters(parameters: parameters, encoding: URLEncoding.default) + + case .postSupportsLikes(let userId, let supportInfoId): + let parameters: [String: Any] = [ + "userId": userId + ] + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) } } // 헤더값 (고정) var headers: [String: String]? { - switch self { - case .getSupports: - return ["Content-type": "application/json"] - } + return ["Content-type": "application/json"] } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/SceneDelegate.swift b/BudgetBuddies/BudgetBuddies/Sources/SceneDelegate.swift index ef96577b..ffaefe05 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/SceneDelegate.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/SceneDelegate.swift @@ -18,7 +18,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(frame: UIScreen.main.bounds) window?.windowScene = windowScene - window?.rootViewController = RootTabBarController() + window?.rootViewController = ExtendedLaunchScreenViewController() window?.makeKeyAndVisible() } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategoryPlus/CategoryPlusModel.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategoryPlus/CategoryPlusModel.swift new file mode 100644 index 00000000..a039a862 --- /dev/null +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategoryPlus/CategoryPlusModel.swift @@ -0,0 +1,8 @@ +// +// CategoryPlusModel.swift +// BudgetBuddies +// +// Created by Jiwoong CHOI on 8/22/24. +// + +import Foundation diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategoryPlus/CategoryPlusView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategoryPlus/CategoryPlusView.swift index 584c5e9a..64142576 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategoryPlus/CategoryPlusView.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategoryPlus/CategoryPlusView.swift @@ -24,6 +24,7 @@ class CategoryPlusView: UIView { public var userCategoryTextField: UITextField = { let textField = UITextField() textField.placeholder = "카테고리를 입력하세요." + textField.setComfortableTextField() return textField }() diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategoryPlus/CategoryPlusViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategoryPlus/CategoryPlusViewController.swift index 4298decc..59aa984d 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategoryPlus/CategoryPlusViewController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategoryPlus/CategoryPlusViewController.swift @@ -6,73 +6,112 @@ // import Moya +import PromiseKit import SnapKit import UIKit class CategoryPlusViewController: UIViewController { // MARK: - Properties + + // View Properties private let categoryPlusView = CategoryPlusView() + // Network Properties private let provider = MoyaProvider() + // Variable Properties private let userId = 1 - /* - 주의 - - 서버에서 "임의의 카테고리"가 확인될 시, 시뮬레이터의 키보드로 텍스트필드에 접근하지 않았기 때문입니다. - - PC 키보드로 카테고리를 입력하면 안됩니다. - */ - private var name = "임의의 카테고리" + private var categoryName = String() private let isDefault = false + // Modal Dismissed Handler Closure + public var dismissHandler: (() -> Void)? + // MARK: - View Life Cycle + override func loadView() { + view = categoryPlusView + } + override func viewDidLoad() { super.viewDidLoad() setUITextFieldDelegate() - connectCategoryPlusView() setButtonAction() } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + if isBeingDismissed { + self.dismissHandler?() + } + } + + // 키보드 내림 + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + self.view.endEditing(true) + } + // MARK: - Methods private func setUITextFieldDelegate() { self.categoryPlusView.userCategoryTextField.delegate = self } - private func connectCategoryPlusView() { - view.addSubview(categoryPlusView) - categoryPlusView.snp.makeConstraints { make in - make.edges.equalToSuperview() - } - } - private func setButtonAction() { categoryPlusView.addButton.addTarget( self, action: #selector(addButtonTapped), for: .touchUpInside) } + private func generateAlertController(message: String) { + let alertController = UIAlertController(title: "알림", message: message, preferredStyle: .alert) + let alertAction = UIAlertAction(title: "확인", style: .default) { [weak self] _ in + self?.dismiss(animated: true) + } + alertController.addAction(alertAction) + self.present(alertController, animated: true) + } + @objc private func addButtonTapped() { - let categoryRequestDTO = CategoryRequestDTO( - userID: self.userId, name: self.name, isDefault: self.isDefault) - debugPrint(categoryRequestDTO.name) - provider.request(.addCategory(userId: self.userId, categoryRequest: categoryRequestDTO)) { - result in - switch result { - case .success(let response): - debugPrint("새로 추가한 카테고리를 서버에 전달 성공") - debugPrint(response.statusCode) - case .failure(let error): - debugPrint("새로 추가한 카테고리를 서버에 전달 실패") - debugPrint(error.localizedDescription) - } + let categoryRequestDTO = CategoryRequestDTO(name: self.categoryName, isDefault: self.isDefault) + firstly { + self.addCategory(categoryRequestDTO: categoryRequestDTO) + }.done { [weak self] _ in + self?.dismissHandler?() + self?.generateAlertController(message: "카테고리를 추가했습니다") + }.catch { [weak self] _ in + self?.generateAlertController(message: "카테고리 추가를 실패했습니다") } + } +} + +// MARK: - Network - dismiss(animated: true) +extension CategoryPlusViewController { + private func addCategory(categoryRequestDTO: CategoryRequestDTO) -> Promise { + return Promise { seal in + provider.request(.addCategory(userId: self.userId, categoryRequest: categoryRequestDTO)) { + result in + switch result { + case .success(let response): + do { + let decodedData = try JSONDecoder().decode(CategoryResponseDTO.self, from: response.data) + } catch (let decodingError){ + seal.reject(decodingError) + } + seal.fulfill(()) + case .failure(let connectionError): + seal.reject(connectionError) + } + } + } } } +// MARK: - UITextFieldDelegate + /* 주의! - 시뮬레이터로 텍스트 필드에 입력 시 PC 키보드로 입력하면 전달이 되지 않습니다. @@ -81,7 +120,7 @@ class CategoryPlusViewController: UIViewController { extension CategoryPlusViewController: UITextFieldDelegate { func textFieldDidEndEditing(_ textField: UITextField) { if let text = textField.text { - self.name = text + self.categoryName = text } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategorySelect/CategorySelectTableModel.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategorySelect/CategorySelectTableModel.swift new file mode 100644 index 00000000..c835b0e8 --- /dev/null +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategorySelect/CategorySelectTableModel.swift @@ -0,0 +1,12 @@ +// +// CategorySelectTableModel.swift +// BudgetBuddies +// +// Created by Jiwoong CHOI on 8/22/24. +// + +import Foundation + +class CategorySelectTableModel { + +} diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategorySelect/CategorySelectTableViewCell.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategorySelect/CategorySelectTableViewCell.swift index 51a9a9f3..464ea56f 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategorySelect/CategorySelectTableViewCell.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategorySelect/CategorySelectTableViewCell.swift @@ -22,21 +22,14 @@ class CategorySelectTableViewCell: UITableViewCell { let container = UIView() container.layer.cornerRadius = 15 container.backgroundColor = .white + container.setShadow(opacity: 1, Radius: 5, offSet: CGSize(width: 0, height: 1)) return container }() // 카테고리 아이콘 private var categoryIcon: UIImageView = { let imageView = UIImageView() - /* - 해야 할 일 - 1. 카테고리 아이콘의 그림자 효과 처리 코드를 모듈화할 것 - 2. 그림자 shadowColor는 Asset에 컬러로 등록할 수 있는지 확인 - */ - imageView.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.1).cgColor - imageView.layer.shadowOpacity = 1 - imageView.layer.shadowRadius = 10 - imageView.layer.masksToBounds = false + imageView.setShadow(opacity: 1, Radius: 4, offSet: CGSize(width: 0, height: 0)) return imageView }() @@ -104,7 +97,7 @@ class CategorySelectTableViewCell: UITableViewCell { // 셀 컨테이너 cellContainer.snp.makeConstraints { make in - make.width.equalTo(containerWidth) + make.width.equalToSuperview().inset(16) make.height.equalTo(containerHeight) make.center.equalToSuperview() } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategorySelect/CategorySelectTableViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategorySelect/CategorySelectTableViewController.swift index 617a2542..57f5648e 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategorySelect/CategorySelectTableViewController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/CategorySelect/CategorySelectTableViewController.swift @@ -18,18 +18,23 @@ import UIKit class CategorySelectTableViewController: UITableViewController { // MARK: - Properties + // 네비 애니메이션 변수 + var previousScrollOffset: CGFloat = 0.0 + var scrollThreshold: CGFloat = 1.0 // 네비게이션 바가 나타나거나 + // UITableView Delegate Properties private let heightBetweenCells: CGFloat = 12 private let heightOfCell: CGFloat = 72 + // Network Properties private let provider = MoyaProvider() + // Variable Properties + private let userId = 1 // 서버에서 가져온 카테고리 항목들을 저장하는 모델 배열 private var categories: [CategoryResponseDTO] = [] - // 기본 카테고리를 제거할 수 없도록 설정한 코드 private var defaultCategoryIndex = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - @Published var selectedCategoryName = "카테고리를 선택하세요" @Published var selectedCateogryId = 0 @@ -37,17 +42,19 @@ class CategorySelectTableViewController: UITableViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - - self.fetchDataFromCategoryControllerAPI() + self.setNavigation() + self.getCategoriesFromServer() } override func viewDidLoad() { super.viewDidLoad() // 배경색이 완전 하얀색이 아님 - view.backgroundColor = UIColor(red: 0.978, green: 0.978, blue: 0.978, alpha: 1) + view.backgroundColor = BudgetBuddiesAsset.AppColor.background.color // tableView의 회색 구분선 제거하기 + tableView.showsVerticalScrollIndicator = false + tableView.showsHorizontalScrollIndicator = false tableView.separatorStyle = .none tableView.register( CategorySelectTableViewCell.self, @@ -55,6 +62,12 @@ class CategorySelectTableViewController: UITableViewController { setNavigation() } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + self.setEditing(false, animated: true) + } + override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() self.tableView.contentInset.bottom = 15 @@ -64,6 +77,8 @@ class CategorySelectTableViewController: UITableViewController { private func setNavigation() { navigationItem.title = "카테고리 설정" + // 뒤로가기 제스처 추가 + self.navigationController?.interactivePopGestureRecognizer?.delegate = self // 커스텀 수정 버튼 navigationItem.rightBarButtonItem = UIBarButtonItem( @@ -72,51 +87,88 @@ class CategorySelectTableViewController: UITableViewController { navigationController?.navigationBar.tintColor = UIColor( red: 0.463, green: 0.463, blue: 0.463, alpha: 1) + + self.setupDefaultNavigationBar(backgroundColor: BudgetBuddiesAsset.AppColor.background.color) + self.addBackButton(selector: #selector(didTapBarButton)) } override func setEditing(_ editing: Bool, animated: Bool) { super.setEditing(editing, animated: animated) + + if editing { + navigationItem.title = "카테고리 편집" + navigationItem.rightBarButtonItems = [ + UIBarButtonItem( + image: UIImage(systemName: "checkmark.circle"), style: .plain, target: self, + action: #selector(circleButtonTapped)), + UIBarButtonItem( + image: UIImage(systemName: "plus"), style: .plain, target: self, + action: #selector(plusButtonTapped)), + ] + } else { + navigationItem.title = "카테고리 설정" + navigationItem.rightBarButtonItems = .none + navigationItem.rightBarButtonItem = UIBarButtonItem( + image: UIImage(systemName: "pencil"), style: .plain, target: self, + action: #selector(editButtonTapped)) + } + tableView.reloadData() } + private func generateUIAlertControllerWithPopingViewController(message: String) { + let alertController = UIAlertController( + title: "알림", message: message, preferredStyle: .alert) + let alertAction = UIAlertAction(title: "확인", style: .default) { [weak self] _ in + self?.navigationController?.popViewController(animated: true) + } + alertController.addAction(alertAction) + self.present(alertController, animated: true) + } + + private func generateUIAlertController(message: String) { + let alertController = UIAlertController( + title: "알림", message: message, preferredStyle: .alert) + let alertAction = UIAlertAction(title: "확인", style: .default) { [weak self] _ in + self?.navigationController?.popViewController(animated: true) + } + alertController.addAction(alertAction) + self.present(alertController, animated: true) + } + + // MARK: - Object C Methods @objc - private func editButtonTapped() { - isEditing = true - setEditing(isEditing, animated: true) + private func didTapBarButton() { + self.navigationController?.popViewController( + animated: true + ) + } - navigationItem.title = "카테고리 편집" - navigationItem.rightBarButtonItems = [ - UIBarButtonItem( - image: UIImage(systemName: "checkmark.circle"), style: .plain, target: self, - action: #selector(circleButtonTapped)), - UIBarButtonItem( - image: UIImage(systemName: "plus"), style: .plain, target: self, - action: #selector(plusButtonTapped)), - ] + @objc + private func editButtonTapped() { + self.setEditing(true, animated: true) } @objc private func circleButtonTapped() { - isEditing = false setEditing(false, animated: true) - - navigationItem.title = "카테고리 설정" - navigationItem.rightBarButtonItems = .none - navigationItem.rightBarButtonItem = UIBarButtonItem( - image: UIImage(systemName: "pencil"), style: .plain, target: self, - action: #selector(editButtonTapped)) } @objc private func plusButtonTapped() { - self.present(CategoryPlusViewController(), animated: true) + let categoryPlusViewController = CategoryPlusViewController() + categoryPlusViewController.dismissHandler = { + self.getCategoriesFromServer() + self.tableView.reloadData() + } + self.present(categoryPlusViewController, animated: true) } // MARK: - Network /// 카테고리 컨트롤러 서버에서 데이터를 가져오는 함수입니다. - private func fetchDataFromCategoryControllerAPI() { - provider.request(.getCategory(userId: 1)) { [weak self] result in + private func getCategoriesFromServer() { + provider.request(.getCategories(userId: self.userId)) { [weak self] result in switch result { case .success(let response): do { @@ -124,15 +176,24 @@ class CategorySelectTableViewController: UITableViewController { [CategoryResponseDTO].self, from: response.data) self?.categories = decodedData self?.tableView.reloadData() - } catch (let error) { + } catch { + self?.generateUIAlertController(message: "카테고리를 불러오지 못했습니다") } - case .failure(let error): - let fetchCategoriesDataFailureAlertController = UIAlertController( - title: "에러", message: "카테고리를 가져오지 못했습니다", preferredStyle: .alert) - let confirmedButtonAction = UIAlertAction(title: "확인", style: .default) { [weak self] _ in - self?.navigationController?.popViewController(animated: true) - } - self?.present(fetchCategoriesDataFailureAlertController, animated: true) + case .failure: + self?.generateUIAlertController(message: "서버와 연결을 실패했습니다") + } + } + } + + /// 카테고리를 제거하는 메소드입니다. + private func deleteCategory(categoryId: Int) { + provider.request(.deleteCategory(userId: self.userId, categoryId: categoryId)) { + [weak self] result in + switch result { + case .success: + self?.generateUIAlertController(message: "카테고리 제거에 성공했습니다") + case .failure: + self?.generateUIAlertController(message: "카테고리 제거에 실패했습니다") } } } @@ -159,6 +220,7 @@ class CategorySelectTableViewController: UITableViewController { cell.configure(categoryID: selectedCategory.id, categoryName: selectedCategory.name) + // cell.selectionStyle = .none return cell } @@ -213,10 +275,11 @@ class CategorySelectTableViewController: UITableViewController { forRowAt indexPath: IndexPath ) { if editingStyle == .delete { + let categoryId = self.categories[indexPath.row].id + self.deleteCategory(categoryId: categoryId) + categories.remove(at: indexPath.row) tableView.deleteRows(at: [indexPath], with: .fade) - - // 제거된 카테고리를 서버에도 반영해야 합니다. } } @@ -231,3 +294,30 @@ class CategorySelectTableViewController: UITableViewController { return defaultCategoryIndex.contains(indexPath.row) ? .none : .delete } } + +extension CategorySelectTableViewController { + override func scrollViewDidScroll(_ scrollView: UIScrollView) { + let currentOffset = scrollView.contentOffset.y + let offsetDifference = currentOffset - previousScrollOffset + + if currentOffset <= 0 { // 스크롤을 완전히 위로 올렸을 때 네비게이션 바 나타냄 + navigationController?.setNavigationBarHidden(false, animated: true) + + } else if offsetDifference > scrollThreshold { // 스크롤이 아래로 일정 이상 이동한 경우 네비게이션 바 숨김 + navigationController?.setNavigationBarHidden(true, animated: true) + + } else if offsetDifference < -scrollThreshold { // 스크롤이 위로 일정 이상 이동한 경우 네비게이션 바 나타냄 + navigationController?.setNavigationBarHidden(false, animated: true) + + } + + previousScrollOffset = currentOffset + } +} + +// MARK: - 뒤로 가기 슬라이드 제스처 추가 +extension CategorySelectTableViewController: UIGestureRecognizerDelegate { + func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } +} diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/Consume/ConsumeView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/Consume/ConsumeView.swift index d53aa31e..9918c4af 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/Consume/ConsumeView.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/Consume/ConsumeView.swift @@ -19,8 +19,8 @@ class ConsumeView: UIView { private let stringWidth = 100 private let stringHeight = 24 - private let rectangleWidth = 343 - private let rectangleHeight = 54 + // private let rectangleWidth = 343 + private let rectangleHeight = 60 // MARK: - UI Components @@ -28,6 +28,7 @@ class ConsumeView: UIView { private let consumedPriceText: UILabel = { let label = UILabel() label.text = "소비금액" + label.setCharacterSpacing(-0.4) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 16) return label @@ -39,6 +40,12 @@ class ConsumeView: UIView { textField.layer.cornerRadius = 15 textField.layer.backgroundColor = BudgetBuddiesAsset.AppColor.textBox.color.cgColor textField.keyboardType = .numberPad + textField.addLeftView(width: 16, height: 16) + textField.setCharacterSpacing(-0.4) + textField.setComfortableTextField() + textField.placeholder = "ex) 20,000" + textField.textAlignment = .left + textField.font = BudgetBuddiesFontFamily.Pretendard.regular.font(size: 16) // 폰트 설정 return textField }() @@ -46,6 +53,7 @@ class ConsumeView: UIView { private let consumedContentText: UILabel = { let label = UILabel() label.text = "소비내용" + label.setCharacterSpacing(-0.4) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 16) return label @@ -56,6 +64,12 @@ class ConsumeView: UIView { let textField = UITextField() textField.layer.cornerRadius = 15 textField.layer.backgroundColor = BudgetBuddiesAsset.AppColor.textBox.color.cgColor + textField.addLeftView(width: 16, height: 16) + textField.setCharacterSpacing(-0.4) + textField.setComfortableTextField() + textField.placeholder = "ex) 타코야끼" + textField.textAlignment = .left + textField.font = BudgetBuddiesFontFamily.Pretendard.regular.font(size: 16) // 폰트 설정 return textField }() @@ -63,6 +77,7 @@ class ConsumeView: UIView { private let consumedDateText: UILabel = { let label = UILabel() label.text = "지출일시" + label.setCharacterSpacing(-0.4) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 16) return label @@ -79,6 +94,7 @@ class ConsumeView: UIView { private let categorySetText: UILabel = { let label = UILabel() label.text = "카테고리 설정" + label.setCharacterSpacing(-0.4) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 16) return label @@ -89,7 +105,13 @@ class ConsumeView: UIView { let button = UIButton() // 버튼 타이틀 설정 코드 - button.setTitle("식비", for: .normal) + /* + 설명 + - 버튼의 타이틀은 CategorySelectTableViewController에서 선택한 값이 반영이 되어야 합니다. + - 그런데 해당 클래스에서 버튼의 타이틀을 설정하는 코드를 넣게 된다면, loadView 메소드가 호출될 때 마다, 설정한 초기 UI로 설정됩니다. + */ + // button.setTitle("식비", for: .normal) + button.setCharacterSpacing(-0.4) button.setTitleColor(BudgetBuddiesAsset.AppColor.subGray.color, for: .normal) button.titleLabel?.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 16) @@ -113,6 +135,7 @@ class ConsumeView: UIView { button.backgroundColor = BudgetBuddiesAsset.AppColor.coreYellow.color button.layer.cornerRadius = 15 button.setTitle("추가하기", for: .normal) + button.setCharacterSpacing(-0.45) button.setTitleColor(BudgetBuddiesAsset.AppColor.white.color, for: .normal) button.titleLabel?.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 18) return button @@ -143,15 +166,15 @@ class ConsumeView: UIView { make.width.equalTo(stringWidth) make.height.equalTo(stringHeight) make.leading.equalTo(consumedPriceTextField.snp.leading) - make.top.equalTo(safeAreaLayoutGuide.snp.top) + make.top.equalTo(safeAreaLayoutGuide.snp.top).offset(50) } // 소비금액 텍스트필드 consumedPriceTextField.snp.makeConstraints { make in - make.width.equalTo(rectangleWidth) - make.height.equalTo(rectangleHeight) + make.leading.trailing.equalToSuperview().inset(16) + make.height.equalTo(54) make.centerX.equalToSuperview() - make.top.equalTo(consumedPriceText.snp.bottom) + make.top.equalTo(consumedPriceText.snp.bottom).offset(4) } // 소비내용 텍스트 @@ -164,10 +187,10 @@ class ConsumeView: UIView { // 소비내용 텍스트필드 consumedContentTextField.snp.makeConstraints { make in - make.width.equalTo(rectangleWidth) - make.height.equalTo(rectangleHeight) + make.leading.trailing.equalToSuperview().inset(16) + make.height.equalTo(54) make.centerX.equalToSuperview() - make.top.equalTo(consumedContentText.snp.bottom) + make.top.equalTo(consumedContentText.snp.bottom).offset(4) } // 지출일시 텍스트 @@ -194,13 +217,13 @@ class ConsumeView: UIView { // 카테고리 설정 버튼 categorySettingButton.snp.makeConstraints { make in - make.trailing.equalTo(consumedContentTextField.snp.trailing) + make.trailing.equalTo(consumedContentTextField.snp.trailing).offset(12) make.centerY.equalTo(categorySetText) } // 추가하기 버튼 addButton.snp.makeConstraints { make in - make.width.equalTo(rectangleWidth) + make.leading.trailing.equalToSuperview().inset(16) make.height.equalTo(rectangleHeight) make.centerX.equalToSuperview() make.bottom.equalTo(safeAreaLayoutGuide.snp.bottom).inset(30) // 기존 20에서 10 추가(탭바 가림 해결) diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/Consume/ConsumeViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/Consume/ConsumeViewController.swift index 051d8f2d..240097aa 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/Consume/ConsumeViewController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/Consume/ConsumeViewController.swift @@ -43,6 +43,7 @@ final class ConsumeViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + setNavigation() } override func viewDidLoad() { @@ -55,12 +56,21 @@ final class ConsumeViewController: UIViewController { observeSelectedCategory() } + // 키보드 내림 + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + self.view.endEditing(true) + } + // MARK: - Methods private func setNavigation() { navigationItem.title = "소비 추가하기" + + // 뒤로가기 제스처 추가 + self.navigationController?.interactivePopGestureRecognizer?.delegate = self + navigationItem.rightBarButtonItem = UIBarButtonItem( - title: "소비기록", image: UIImage(systemName: "list.clipboard"), target: self, + title: "소비기록", image: UIImage(systemName: "list.clipboard.fill"), target: self, action: #selector(rightBarButtonItemButtonTapped)) navigationItem.backBarButtonItem = UIBarButtonItem() @@ -71,6 +81,9 @@ final class ConsumeViewController: UIViewController { */ navigationItem.rightBarButtonItem?.tintColor = UIColor( red: 0.463, green: 0.463, blue: 0.463, alpha: 1) + + self.setupDefaultNavigationBar(backgroundColor: BudgetBuddiesAsset.AppColor.white.color) + } private func setUITextFieldDelegate() { @@ -120,7 +133,7 @@ extension ConsumeViewController { /* 해야 할 일 - - 서버로 보낼 날짜 데이터 형식으로 전환해야 함 + - 문자열 데이터 타입으로 보관하는 코드 작성 */ } @@ -142,14 +155,15 @@ extension ConsumeViewController { - ViewController에 있는 비즈니스 로직 코드도 XCTest 프레임워크 기반으로 개발할 수 있는지 연구 */ let categoryId = self.selectedCategoryId - let amount: Int + let amount = Int(self.writtenConsumedPriceText) ?? 0 let description = self.writtenConsumedContentText let expenseDate: String - if let writtenConsumedPrice = Int(self.writtenConsumedPriceText) { - amount = writtenConsumedPrice - } else { - amount = 0 - } + + /* + 해야 할 일 + - 모델 객체에서 날짜를 저장하는 형식을 문자열로 보관 + - 문자열 형식 전환 처리는 날짜를 선택한 object c 메소드에 작성할 것 + */ let dateFormatter = DateFormatter() /* @@ -173,6 +187,11 @@ extension ConsumeViewController { // MARK: - Network +/* + 해야 할 일 + - 모델 객체로 분리 + */ + extension ConsumeViewController { private func postNewExpense(newExpenseRequestDTO: NewExpenseRequestDTO) { provider.request( @@ -183,7 +202,19 @@ extension ConsumeViewController { case .success: let postSuccessAlertController = UIAlertController( title: "알림", message: "새로운 소비 내역을 추가했습니다", preferredStyle: .alert) - let confirmedButtonAction = UIAlertAction(title: "확인", style: .default) + let confirmedButtonAction = UIAlertAction(title: "확인", style: .default) { [weak self] _ in + self?.consumeView.consumedPriceTextField.text = String() + self?.consumeView.consumedContentTextField.text = String() + self?.consumeView.consumedDatePicker.date = Date() + + /* + 설명 + - 카테고리 선택 버튼은 CategorySelectTableViewController의 모델 객체의 데이터를 추종하는 성격이 있습니다. + - 그래서 데이터를 직접 변경하면, ConsumeView의 addButton UI도 변경됩니다. + */ + self?.categorySelectTableViewController.selectedCategoryName = "카테고리를 선택하세요" + self?.categorySelectTableViewController.selectedCateogryId = 0 + } postSuccessAlertController.addAction(confirmedButtonAction) self.present(postSuccessAlertController, animated: true) case .failure: @@ -226,3 +257,10 @@ extension ConsumeViewController: UITextFieldDelegate { return true } } + +// MARK: - 뒤로 가기 슬라이드 제스처 추가 +extension ConsumeViewController: UIGestureRecognizerDelegate { + func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } +} diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/ConsumedHistory/ConsumedHistoryTableViewCell.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/ConsumedHistory/ConsumedHistoryTableViewCell.swift index da4e9b14..8a2d5b6d 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/ConsumedHistory/ConsumedHistoryTableViewCell.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/ConsumedHistory/ConsumedHistoryTableViewCell.swift @@ -18,6 +18,7 @@ class ConsumedHistoryTableViewCell: UITableViewCell { private var categoryIcon: UIImageView = { let imageView = UIImageView() + imageView.setShadow(opacity: 1, Radius: 5, offSet: CGSize(width: 0, height: 0)) return imageView }() @@ -80,18 +81,18 @@ class ConsumedHistoryTableViewCell: UITableViewCell { contentView.addSubviews(categoryIcon, spentPriceLabel, descriptionLabel) categoryIcon.snp.makeConstraints { make in - make.width.height.equalTo(40) + make.width.height.equalTo(47) make.leading.equalToSuperview().inset(25) make.centerY.equalToSuperview() } spentPriceLabel.snp.makeConstraints { make in - make.top.equalTo(categoryIcon.snp.top) + make.top.equalTo(categoryIcon.snp.top).offset(5) make.leading.equalTo(categoryIcon.snp.trailing).offset(20) } descriptionLabel.snp.makeConstraints { make in - make.bottom.equalTo(categoryIcon.snp.bottom) + make.top.equalTo(spentPriceLabel.snp.bottom).offset(2) make.leading.equalTo(categoryIcon.snp.trailing).offset(20) } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/ConsumedHistory/ConsumedHistoryTableViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/ConsumedHistory/ConsumedHistoryTableViewController.swift index 30628404..ec1108e0 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/ConsumedHistory/ConsumedHistoryTableViewController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/ConsumedHistory/ConsumedHistoryTableViewController.swift @@ -48,6 +48,12 @@ final class ConsumedHistoryTableViewController: UIViewController { setViewSetting() } + // 탭바에 가려지는 요소 보이게 하기 + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + self.consumedHistoryTableView.contentInset.bottom = 30 + } + // MARK: - Methods /// 모델 객체이 데이터를 서버에서 가져오고 난 후 UX 제어 함수 @@ -83,8 +89,7 @@ final class ConsumedHistoryTableViewController: UIViewController { } private func setNavigationSetting() { - navigationController?.navigationBar.tintColor = BudgetBuddiesAsset.AppColor.barGray.color - navigationItem.backBarButtonItem = UIBarButtonItem() + self.addBackButton(selector: #selector(didTapBarButton)) } private func setUITableViewSetting() { @@ -126,6 +131,11 @@ final class ConsumedHistoryTableViewController: UIViewController { // MARK: - Object C Methods extension ConsumedHistoryTableViewController { + @objc + private func didTapBarButton() { + self.navigationController?.popViewController(animated: true) + } + @objc private func consumedHistoryHeaderViewPreviousMonthButtonTapped() { let calendar = Calendar.current diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/ConsumedHistoryDetail/ConsumedHistoryDetailView/ConsumedHistoryDetailView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/ConsumedHistoryDetail/ConsumedHistoryDetailView/ConsumedHistoryDetailView.swift index 26af6bff..31b03c65 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/ConsumedHistoryDetail/ConsumedHistoryDetailView/ConsumedHistoryDetailView.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/ConsumedHistoryDetail/ConsumedHistoryDetailView/ConsumedHistoryDetailView.swift @@ -16,8 +16,9 @@ class ConsumedHistoryDetailView: UIView { let imageView = UIImageView() imageView.image = BudgetBuddiesAsset.AppImage.CategoryIcon.foodIcon2.image imageView.snp.makeConstraints { make in - make.width.height.equalTo(48) + make.width.height.equalTo(58) } + imageView.setShadow(opacity: 1, Radius: 5, offSet: CGSize(width: 0, height: 0)) return imageView }() @@ -84,6 +85,7 @@ class ConsumedHistoryDetailView: UIView { public var saveButton: UIButton = { let button = UIButton() button.setTitle("저장하기", for: .normal) + button.titleLabel?.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 18) button.setTitleColor(BudgetBuddiesAsset.AppColor.white.color, for: .normal) button.backgroundColor = BudgetBuddiesAsset.AppColor.coreYellow.color button.layer.cornerRadius = 15 @@ -119,13 +121,13 @@ class ConsumedHistoryDetailView: UIView { // 카테고리 텍스트 expenseDescriptionLabel.snp.makeConstraints { make in - make.top.equalTo(categoryIcon.snp.top) + make.top.equalTo(categoryIcon.snp.top).offset(6) make.leading.equalTo(categoryIcon.snp.trailing).offset(12) } // 금액 텍스트 priceLabel.snp.makeConstraints { make in - make.bottom.equalTo(categoryIcon.snp.bottom) + make.top.equalTo(expenseDescriptionLabel.snp.bottom) make.leading.equalTo(categoryIcon.snp.trailing).offset(12) } @@ -157,8 +159,8 @@ class ConsumedHistoryDetailView: UIView { saveButton.snp.makeConstraints { make in make.leading.equalToSuperview().inset(16) make.trailing.equalToSuperview().inset(16) - make.bottom.equalTo(safeAreaLayoutGuide.snp.bottom).inset(20) - make.height.equalTo(54) + make.bottom.equalTo(safeAreaLayoutGuide.snp.bottom).inset(30) + make.height.equalTo(60) make.centerX.equalToSuperview() } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/ConsumedHistoryDetail/ConsumedHistoryDetailViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/ConsumedHistoryDetail/ConsumedHistoryDetailViewController.swift index cf9fbb5e..1ef66fde 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/ConsumedHistoryDetail/ConsumedHistoryDetailViewController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AccountBook/ConsumedHistoryDetail/ConsumedHistoryDetailViewController.swift @@ -74,6 +74,9 @@ final class ConsumedHistoryDetailViewController: UIViewController { navigationItem.rightBarButtonItem = UIBarButtonItem( image: UIImage(systemName: "trash.fill"), style: .plain, target: self, action: #selector(trashRightBarButtonTapped)) + navigationController?.navigationBar.tintColor = BudgetBuddiesAsset.AppColor.subGray.color + + self.addBackButton(selector: #selector(didTapBarButton)) } private func setButtonAction() { @@ -280,6 +283,11 @@ extension ConsumedHistoryDetailViewController { // MARK: - Object C Methods extension ConsumedHistoryDetailViewController { + @objc + private func didTapBarButton() { + self.navigationController?.popViewController(animated: true) + } + @objc private func categorySettingButtonTapped() { navigationItem.backBarButtonItem = UIBarButtonItem() diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingView/AllLookingView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingView/AllLookingView.swift index 174b535c..9ae3be05 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingView/AllLookingView.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingView/AllLookingView.swift @@ -21,6 +21,7 @@ class AllLookingView: UIView { private let allLookingTitleText: UILabel = { let label = UILabel() label.text = "전체보기" + label.setCharacterSpacing(-0.45) label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 18) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color return label @@ -51,6 +52,11 @@ class AllLookingView: UIView { // MARK: - Methods private func setLayout() { + // 그림자 설정 + profileContainerView.setShadow(opacity: 1, Radius: 10, offSet: CGSize(width: 0, height: 1)) + analysisContainverView.setShadow(opacity: 1, Radius: 10, offSet: CGSize(width: 0, height: 1)) + allServiceContainerView.setShadow(opacity: 1, Radius: 10, offSet: CGSize(width: 0, height: 1)) + addSubviews( allLookingTitleText, profileContainerView, @@ -65,16 +71,22 @@ class AllLookingView: UIView { profileContainerView.snp.makeConstraints { make in make.centerX.equalToSuperview() make.top.equalTo(allLookingTitleText.snp.bottom).offset(19) + make.leading.trailing.equalToSuperview().inset(16) + make.height.equalTo(81) } analysisContainverView.snp.makeConstraints { make in make.centerX.equalToSuperview() make.top.equalTo(profileContainerView.snp.bottom).offset(15) + make.leading.trailing.equalToSuperview().inset(16) + make.height.equalTo(144) } allServiceContainerView.snp.makeConstraints { make in make.centerX.equalToSuperview() make.top.equalTo(analysisContainverView.snp.bottom).offset(15) + make.leading.trailing.equalToSuperview().inset(16) + make.height.equalTo(183) } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingView/AllLookingViewUIComponents/AllServiceContainerView/AllServiceContainerView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingView/AllLookingViewUIComponents/AllServiceContainerView/AllServiceContainerView.swift index 7e91b13d..4d385bd8 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingView/AllLookingViewUIComponents/AllServiceContainerView/AllServiceContainerView.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingView/AllLookingViewUIComponents/AllServiceContainerView/AllServiceContainerView.swift @@ -5,22 +5,24 @@ // Created by Jiwoong CHOI on 7/26/24. // +import SnapKit import UIKit class AllServiceContainerView: UIView { // MARK: - Properties - private static let containerWidth = 320 + // private static let containerWidth = 320 private static let containerHeight = 29 private static let chevronWidth = 10 - private static let chevronHeight = 21 + private static let chevronHeight = 19 // MARK: - UI Components // "전체 서비스" 텍스트 private let allServiceText: UILabel = { let label = UILabel() label.text = "전체 서비스" + label.setCharacterSpacing(-0.35) label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 14) label.textColor = BudgetBuddiesAsset.AppColor.subGray.color return label @@ -33,7 +35,6 @@ class AllServiceContainerView: UIView { let view = UIView() view.backgroundColor = BudgetBuddiesAsset.AppColor.white.color view.snp.makeConstraints { make in - make.width.equalTo(containerWidth) make.height.equalTo(containerHeight) } return view @@ -51,6 +52,7 @@ class AllServiceContainerView: UIView { private let pocketCalendarText: UILabel = { let label = UILabel() label.text = "주머니 캘린더" + label.setCharacterSpacing(-0.35) label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 14) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color return label @@ -75,7 +77,6 @@ class AllServiceContainerView: UIView { let view = UIView() view.backgroundColor = BudgetBuddiesAsset.AppColor.white.color view.snp.makeConstraints { make in - make.width.equalTo(containerWidth) make.height.equalTo(containerHeight) } return view @@ -93,6 +94,7 @@ class AllServiceContainerView: UIView { private let priceEventInfoConfirmText: UILabel = { let label = UILabel() label.text = "이번 달 할인정보 확인하기" + label.setCharacterSpacing(-0.35) label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 14) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color return label @@ -119,7 +121,6 @@ class AllServiceContainerView: UIView { let view = UIView() view.backgroundColor = BudgetBuddiesAsset.AppColor.white.color view.snp.makeConstraints { make in - make.width.equalTo(containerWidth) make.height.equalTo(containerHeight) } return view @@ -137,6 +138,7 @@ class AllServiceContainerView: UIView { private let supportInfoConfirmText: UILabel = { let label = UILabel() label.text = "이번 달 지원정보 확인하기" + label.setCharacterSpacing(-0.35) label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 14) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color return label @@ -174,10 +176,6 @@ class AllServiceContainerView: UIView { // MARK: - Methods private func setLayout() { - self.snp.makeConstraints { make in - make.width.equalTo(343) - make.height.equalTo(183) - } // 컨테이너 레이아웃 addSubviews( @@ -189,17 +187,17 @@ class AllServiceContainerView: UIView { } pocketCalendarContainer.snp.makeConstraints { make in - make.leading.equalToSuperview().inset(12) + make.leading.trailing.equalToSuperview().inset(20) make.top.equalToSuperview().inset(50) } priceEventInfoContainer.snp.makeConstraints { make in - make.leading.equalTo(pocketCalendarContainer) + make.leading.trailing.equalToSuperview().inset(20) make.top.equalTo(pocketCalendarContainer.snp.bottom).offset(12) } supportInfoConfirmContainer.snp.makeConstraints { make in - make.leading.equalTo(priceEventInfoContainer) + make.leading.trailing.equalToSuperview().inset(20) make.top.equalTo(priceEventInfoContainer.snp.bottom).offset(12) } @@ -215,7 +213,7 @@ class AllServiceContainerView: UIView { pocketCalendarIcon.snp.makeConstraints { make in make.centerY.equalToSuperview() - make.leading.equalToSuperview().inset(leadingDistanceFromContainer) + make.leading.equalToSuperview() } pocketCalendarText.snp.makeConstraints { make in @@ -225,7 +223,7 @@ class AllServiceContainerView: UIView { pocketCalendarRightChevron.snp.makeConstraints { make in make.centerY.equalToSuperview() - make.trailing.equalToSuperview().inset(trailingDistanceFromContainer) + make.trailing.equalToSuperview() } // "이번 달 할인정보 확인하기" 항목들 레이아웃 @@ -234,7 +232,7 @@ class AllServiceContainerView: UIView { priceEventInfoConfirmIcon.snp.makeConstraints { make in make.centerY.equalToSuperview() - make.leading.equalToSuperview().inset(leadingDistanceFromContainer) + make.leading.equalToSuperview() } priceEventInfoConfirmText.snp.makeConstraints { make in @@ -244,7 +242,7 @@ class AllServiceContainerView: UIView { priceEventInfoConfirmRightChevron.snp.makeConstraints { make in make.centerY.equalToSuperview() - make.trailing.equalToSuperview().inset(trailingDistanceFromContainer) + make.trailing.equalToSuperview() } // "이번 달 지원정보 확인하기" 항목들 레이아웃 @@ -253,7 +251,7 @@ class AllServiceContainerView: UIView { supportInfoConfirmIcon.snp.makeConstraints { make in make.centerY.equalToSuperview() - make.leading.equalToSuperview().inset(leadingDistanceFromContainer) + make.leading.equalToSuperview() } supportInfoConfirmText.snp.makeConstraints { make in @@ -263,7 +261,7 @@ class AllServiceContainerView: UIView { supportInfoConfirmRightChevron.snp.makeConstraints { make in make.centerY.equalToSuperview() - make.trailing.equalToSuperview().inset(trailingDistanceFromContainer) + make.trailing.equalToSuperview() } } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingView/AllLookingViewUIComponents/AnalysisContainerView/AnalysisContainerView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingView/AllLookingViewUIComponents/AnalysisContainerView/AnalysisContainerView.swift index 851d876d..bddd9a71 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingView/AllLookingViewUIComponents/AnalysisContainerView/AnalysisContainerView.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingView/AllLookingViewUIComponents/AnalysisContainerView/AnalysisContainerView.swift @@ -5,6 +5,7 @@ // Created by Jiwoong CHOI on 7/26/24. // +import SnapKit import UIKit class AnalysisContainerView: UIView { @@ -15,6 +16,7 @@ class AnalysisContainerView: UIView { private let analysisText: UILabel = { let label = UILabel() label.text = "분석" + label.setCharacterSpacing(-0.35) label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 14) label.textColor = BudgetBuddiesAsset.AppColor.subGray.color return label @@ -27,7 +29,6 @@ class AnalysisContainerView: UIView { let view = UIView() view.backgroundColor = BudgetBuddiesAsset.AppColor.white.color view.snp.makeConstraints { make in - make.width.equalTo(320) make.height.equalTo(24) } return view @@ -46,6 +47,7 @@ class AnalysisContainerView: UIView { private let thisMonthReportText: UILabel = { let label = UILabel() label.text = "이번 달 레포트" + label.setCharacterSpacing(-0.35) label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 14) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color return label @@ -58,7 +60,7 @@ class AnalysisContainerView: UIView { imageView.tintColor = BudgetBuddiesAsset.AppColor.subGray.color imageView.snp.makeConstraints { make in make.width.equalTo(10) - make.height.equalTo(21) + make.height.equalTo(19) } return imageView }() @@ -69,7 +71,6 @@ class AnalysisContainerView: UIView { let view = UIView() view.backgroundColor = BudgetBuddiesAsset.AppColor.white.color view.snp.makeConstraints { make in - make.width.equalTo(320) make.height.equalTo(24) } return view @@ -88,6 +89,7 @@ class AnalysisContainerView: UIView { private let peerComsumedAnalysisReportText: UILabel = { let label = UILabel() label.text = "또래 소비분석 리포트" + label.setCharacterSpacing(-0.35) label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 14) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color return label @@ -100,7 +102,7 @@ class AnalysisContainerView: UIView { imageView.tintColor = BudgetBuddiesAsset.AppColor.subGray.color imageView.snp.makeConstraints { make in make.width.equalTo(10) - make.height.equalTo(21) + make.height.equalTo(19) } return imageView }() @@ -122,10 +124,6 @@ class AnalysisContainerView: UIView { // MARK: - Methods private func setLayout() { - self.snp.makeConstraints { make in - make.width.equalTo(343) - make.height.equalTo(144) - } addSubviews(analysisText, thisMonthReportContainer, peerConsumedAnalysisReportContainer) @@ -136,19 +134,19 @@ class AnalysisContainerView: UIView { thisMonthReportContainer.snp.makeConstraints { make in make.top.equalToSuperview().inset(52) - make.leading.equalToSuperview().inset(12) + make.leading.trailing.equalToSuperview().inset(20) } peerConsumedAnalysisReportContainer.snp.makeConstraints { make in make.top.equalToSuperview().inset(96) - make.leading.equalToSuperview().inset(12) + make.leading.trailing.equalToSuperview().inset(20) } thisMonthReportContainer.addSubviews( thisMonthReportIcon, thisMonthReportText, thisMonthReportChevronRight) thisMonthReportIcon.snp.makeConstraints { make in - make.leading.equalToSuperview().inset(4) + make.leading.equalToSuperview() make.centerY.equalToSuperview() } @@ -158,7 +156,7 @@ class AnalysisContainerView: UIView { } thisMonthReportChevronRight.snp.makeConstraints { make in - make.trailing.equalToSuperview().inset(4) + make.trailing.equalToSuperview() make.centerY.equalToSuperview() } @@ -167,7 +165,7 @@ class AnalysisContainerView: UIView { peerConsumedAnalysisReportChevronRight) peerConsumedAnalysisReportIcon.snp.makeConstraints { make in - make.leading.equalToSuperview().inset(4) + make.leading.equalToSuperview() make.centerY.equalToSuperview() } @@ -177,7 +175,7 @@ class AnalysisContainerView: UIView { } peerConsumedAnalysisReportChevronRight.snp.makeConstraints { make in - make.trailing.equalToSuperview().inset(4) + make.trailing.equalToSuperview() make.centerY.equalToSuperview() } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingView/AllLookingViewUIComponents/ProflieContainerView/ProfileContainerView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingView/AllLookingViewUIComponents/ProflieContainerView/ProfileContainerView.swift index 602df182..1aa2f300 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingView/AllLookingViewUIComponents/ProflieContainerView/ProfileContainerView.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingView/AllLookingViewUIComponents/ProflieContainerView/ProfileContainerView.swift @@ -16,6 +16,7 @@ class ProfileContainerView: UIView { public var userNameText: UILabel = { let label = UILabel() label.text = "빈주머니즈" + label.setCharacterSpacing(-0.45) label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 18) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color return label @@ -28,7 +29,7 @@ class ProfileContainerView: UIView { imageView.tintColor = BudgetBuddiesAsset.AppColor.subGray.color imageView.snp.makeConstraints { make in make.width.equalTo(10) - make.height.equalTo(21) + make.height.equalTo(19) } return imageView }() @@ -50,20 +51,16 @@ class ProfileContainerView: UIView { private func setLayout() { self.layer.cornerRadius = 15 - self.snp.makeConstraints { make in - make.width.equalTo(343) - make.height.equalTo(81) - } addSubviews(userNameText, rightChevronIconForProfile) userNameText.snp.makeConstraints { make in - make.leading.equalToSuperview().inset(21.36) + make.leading.equalToSuperview().inset(20) make.centerY.equalToSuperview() } rightChevronIconForProfile.snp.makeConstraints { make in - make.trailing.equalToSuperview().inset(21.36) + make.trailing.equalToSuperview().inset(20) make.centerY.equalToSuperview() } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingViewController.swift index eafc5675..89547d35 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingViewController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/AllLookingViewController.swift @@ -49,6 +49,7 @@ class AllLookingViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + setNavi() hideNavigationBar() self.fetchUserData(userId: self.userId) } @@ -82,6 +83,19 @@ class AllLookingViewController: UIViewController { navigationController?.navigationBar.isHidden = false } + private func setNavi() { + self.navigationController?.setNavigationBarHidden(false, animated: true) + + let appearance = UINavigationBarAppearance() + appearance.configureWithOpaqueBackground() + appearance.backgroundColor = .white + appearance.shadowColor = nil + + navigationController?.navigationBar.standardAppearance = appearance + navigationController?.navigationBar.compactAppearance = appearance + navigationController?.navigationBar.scrollEdgeAppearance = appearance + } + private func observeUserNameProperty() { self.$userName .sink { [weak self] newValue in @@ -170,7 +184,7 @@ extension AllLookingViewController { // MARK: - Network extension AllLookingViewController { - private func fetchUserData(userId: Int) { + public func fetchUserData(userId: Int) { provider.request(.find(userId: userId)) { result in switch result { case .success(let response): diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/ProfileEditView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/ProfileEditView.swift index 8eefd9f4..dc5bb7f7 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/ProfileEditView.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/ProfileEditView.swift @@ -12,12 +12,12 @@ class ProfileEditView: UIView { // MARK: - Properties // 텍스트 빌드 레이아웃 값 - private static let textFieldWidth = 343 + // private static let textFieldWidth = 343 private static let textFieldHeight = 54 // 버튼 레이아웃 값 - private static let buttonWidth = 342 - private static let buttonHeight = 63 + // private static let buttonWidth = 342 + private static let buttonHeight = 60 // 공통 레이아웃 값 private static let cornerRadius: CGFloat = 15 @@ -28,6 +28,7 @@ class ProfileEditView: UIView { private let nameText: UILabel = { let label = UILabel() label.text = "이름" + label.setCharacterSpacing(-0.35) label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 14) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color return label @@ -37,12 +38,17 @@ class ProfileEditView: UIView { public let nameTextField: UITextField = { let textField = UITextField() textField.snp.makeConstraints { make in - make.width.equalTo(ProfileEditView.textFieldWidth) make.height.equalTo(ProfileEditView.textFieldHeight) } textField.layer.cornerRadius = ProfileEditView.cornerRadius textField.backgroundColor = BudgetBuddiesAsset.AppColor.textBox.color + textField.addLeftView(width: 16, height: 16) + textField.setComfortableTextField() + + textField.placeholder = "ex) 홍길동" + textField.textAlignment = .left + textField.font = BudgetBuddiesFontFamily.Pretendard.regular.font(size: 16) // 폰트 설정 return textField }() @@ -50,6 +56,7 @@ class ProfileEditView: UIView { private let emailText: UILabel = { let label = UILabel() label.text = "이메일" + label.setCharacterSpacing(-0.35) label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 14) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color return label @@ -59,7 +66,6 @@ class ProfileEditView: UIView { public let emailTextField: UITextField = { let textField = UITextField() textField.snp.makeConstraints { make in - make.width.equalTo(ProfileEditView.textFieldWidth) make.height.equalTo(ProfileEditView.textFieldHeight) } textField.layer.cornerRadius = ProfileEditView.cornerRadius @@ -67,6 +73,12 @@ class ProfileEditView: UIView { textField.keyboardType = .emailAddress + textField.addLeftView(width: 16, height: 16) + textField.setComfortableTextField() + + textField.placeholder = "ex) example@email.com" + textField.textAlignment = .left + textField.font = BudgetBuddiesFontFamily.Pretendard.regular.font(size: 16) // 폰트 설정 return textField }() @@ -74,12 +86,13 @@ class ProfileEditView: UIView { public let saveButton: UIButton = { let button = UIButton() button.snp.makeConstraints { make in - make.width.equalTo(ProfileEditView.textFieldWidth) - make.height.equalTo(ProfileEditView.textFieldHeight) + make.height.equalTo(ProfileEditView.buttonHeight) } button.backgroundColor = BudgetBuddiesAsset.AppColor.coreYellow.color button.layer.cornerRadius = ProfileEditView.cornerRadius button.setTitle("저장하기", for: .normal) + button.setCharacterSpacing(-0.45) + button.titleLabel?.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 18) button.setTitleColor(BudgetBuddiesAsset.AppColor.white.color, for: .normal) return button }() @@ -103,27 +116,30 @@ class ProfileEditView: UIView { nameText.snp.makeConstraints { make in make.leading.equalToSuperview().inset(24) - make.top.equalTo(safeAreaLayoutGuide.snp.top).inset(73) + make.top.equalTo(safeAreaLayoutGuide.snp.top).inset(32) } nameTextField.snp.makeConstraints { make in make.centerX.equalToSuperview() - make.top.equalTo(safeAreaLayoutGuide.snp.top).inset(98) + make.leading.trailing.equalToSuperview().inset(16) + make.top.equalTo(nameText.snp.bottom).offset(7) } emailText.snp.makeConstraints { make in make.leading.equalToSuperview().inset(24) - make.top.equalTo(safeAreaLayoutGuide.snp.top).inset(179) + make.top.equalTo(nameTextField.snp.bottom).offset(28) } emailTextField.snp.makeConstraints { make in make.centerX.equalToSuperview() - make.top.equalTo(safeAreaLayoutGuide.snp.top).inset(204) + make.leading.trailing.equalToSuperview().inset(16) + make.top.equalTo(emailText.snp.bottom).offset(7) } saveButton.snp.makeConstraints { make in make.centerX.equalToSuperview() make.bottom.equalTo(safeAreaLayoutGuide.snp.bottom).inset(30) + make.leading.trailing.equalToSuperview().inset(16) } } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/ProfileEditViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/ProfileEditViewController.swift index 8cd6db67..b5c492e3 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/ProfileEditViewController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/AllLooking/ProfileEditViewController.swift @@ -46,7 +46,13 @@ class ProfileEditViewController: UIViewController { private func setNavigation() { navigationItem.title = "마이페이지" + + // 뒤로가기 제스처 추가 + self.navigationController?.interactivePopGestureRecognizer?.delegate = self + navigationController?.navigationBar.tintColor = BudgetBuddiesAsset.AppColor.subGray.color + self.setupDefaultNavigationBar(backgroundColor: BudgetBuddiesAsset.AppColor.white.color) + self.addBackButton(selector: #selector(didTapBarButton)) } private func setUITextFieldDelegate() { @@ -58,6 +64,12 @@ class ProfileEditViewController: UIViewController { profileEditView.saveButton.addTarget( self, action: #selector(saveButtonTapped), for: .touchUpInside) } + + // 키보드 내림 + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + self.view.endEditing(true) + } + } // MARK: - Network @@ -94,6 +106,11 @@ extension ProfileEditViewController { let userInfoRequestDTO = UserInfoRequestDTO(email: self.writtenEmail, name: self.writtenName) self.postEditedUserInfo(userId: self.userId, userInfoRequestDTO: userInfoRequestDTO) } + + @objc + private func didTapBarButton() { + self.navigationController?.popViewController(animated: true) + } } // MARK: - UITextField Delegate @@ -124,3 +141,10 @@ extension ProfileEditViewController: UITextFieldDelegate { return true } } + +// MARK: - 뒤로 가기 슬라이드 제스처 추가 +extension ProfileEditViewController: UIGestureRecognizerDelegate { + func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } +} diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/BottomSheet/Cells/CommentCell.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/BottomSheet/Cells/CommentCell.swift index 23c6190e..edd8db5b 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/BottomSheet/Cells/CommentCell.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/BottomSheet/Cells/CommentCell.swift @@ -134,13 +134,24 @@ class CommentCell: UITableViewCell { // MARK: - Configure func configure(userId: Int) { + hideModifyDeleteButtons() guard let commentsUserId = self.userId else { return } + + print("받은 id: \(userId), 현재 셀의 id: \(commentsUserId)") if userId == commentsUserId { // 유저번호가 같은 댓글만 수정,삭제 버튼 보이게 setupModifyDeleteButtons() } } + // MARK: - Hide Modify, Delete Buttons + private func hideModifyDeleteButtons() { + buttonBackView.removeFromSuperview() + verticalSeparator.removeFromSuperview() + editButtonImageView.removeFromSuperview() + deleteButtonImageView.removeFromSuperview() + } + // MARK: - Set up UI private func setupUI() { self.contentView.addSubviews(stackView) diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/Calendar/Cells/InformationCell.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/Calendar/Cells/InformationCell.swift index 4eb7058e..37c1f3e5 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/Calendar/Cells/InformationCell.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/Calendar/Cells/InformationCell.swift @@ -11,6 +11,7 @@ import UIKit protocol InformationCellDelegate: AnyObject { func didTapWebButton(in cell: InformationCell, urlString: String) + func didTapLikesButton(in cell: InformationCell, likesCount: Int, infoType: InfoType, infoId: Int) } class InformationCell: UITableViewCell { @@ -23,11 +24,15 @@ class InformationCell: UITableViewCell { var infoType: InfoType? + var infoId: Int? + + var likesCount: Int? // 전체보기 - 지원 var support: SupportContent? { didSet { guard let support = support else { return } + self.infoId = support.id self.infoTitleLabel.text = support.title self.dateLabel.text = support.dateRangeString self.urlString = support.siteURL @@ -37,6 +42,7 @@ class InformationCell: UITableViewCell { } self.likesLabel.text = String(support.likeCount) + self.likesCount = support.likeCount } } @@ -45,6 +51,7 @@ class InformationCell: UITableViewCell { didSet { guard let discount = discount else { return } + self.infoId = discount.id self.infoTitleLabel.text = discount.title self.dateLabel.text = discount.dateRangeString self.urlString = discount.siteURL @@ -58,6 +65,7 @@ class InformationCell: UITableViewCell { } self.likesLabel.text = String(discount.likeCount) + self.likesCount = discount.likeCount } } @@ -65,6 +73,8 @@ class InformationCell: UITableViewCell { var recommend: InfoDtoList? { didSet { guard let recommend = recommend else { return } + + self.infoId = recommend.id self.infoTitleLabel.text = recommend.title self.dateLabel.text = recommend.dateRangeString self.urlString = recommend.siteURL @@ -78,13 +88,10 @@ class InformationCell: UITableViewCell { } self.likesLabel.text = String(recommend.likeCount) - + self.likesCount = recommend.likeCount } } - var likesToggle: Bool = false - var likes: Int = 0 - // 댓글 var commentCount: Int? { didSet { @@ -99,13 +106,7 @@ class InformationCell: UITableViewCell { let view = UIView() view.backgroundColor = BudgetBuddiesAsset.AppColor.white.color view.layer.cornerRadius = 15 - - view.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.1).cgColor - view.layer.shadowOpacity = 1 - view.layer.shadowRadius = 4 //반경 - view.layer.shadowOffset = CGSize(width: 0, height: 0) - view.layer.masksToBounds = false - + view.setShadow(opacity: 1, Radius: 10, offSet: CGSize(width: 0, height: 0)) return view }() @@ -376,16 +377,11 @@ class InformationCell: UITableViewCell { @objc private func didTapLikesButton() { - print(#function) - self.likesToggle.toggle() - if likesToggle { - self.likesIconImageView.image = UIImage(named: "fillHeartIconImage") - likes += 1 - self.likesLabel.text = String(self.likes) - } else { - self.likesIconImageView.image = UIImage(named: "heartIconImage") - likes -= 1 - self.likesLabel.text = String(self.likes) - } + guard let id = self.infoId, + let infoType = self.infoType, + let likesCount = self.likesCount + else { return } + + delegate?.didTapLikesButton(in: self, likesCount: likesCount, infoType: infoType, infoId: id) } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/Calendar/Controllers/CalendarViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/Calendar/Controllers/CalendarViewController.swift index 6dc40bde..b7bc974c 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/Calendar/Controllers/CalendarViewController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/Calendar/Controllers/CalendarViewController.swift @@ -24,6 +24,9 @@ final class CalendarViewController: UIViewController { var commentManager = CommentManager.shared + var discountInfoManager = DiscountInfoManager.shared + var supportInfoManager = SupportInfoManager.shared + // MARK: - UI Components // 뷰 var calendarView = CalendarView() @@ -83,7 +86,9 @@ final class CalendarViewController: UIViewController { } // MARK: - Set up Data - private func setupData() { + + /// 캘린더의 데이터를 가져오는 메소드입니다. + public func setupData() { print("-----------캘린더 메인 fetch-----------") guard let yearMonth = self.yearMonth else { return } @@ -219,6 +224,13 @@ final class CalendarViewController: UIViewController { @objc private func switchToCalendarHandler() { + if let navigationController = self.navigationController { + // CalendarViewController가 네비게이션 스택의 루트가 아닌 경우 pop + if navigationController.viewControllers.last != self { + navigationController.popToViewController(self, animated: true) + } + } + // 다른 뷰컨에서 넘어온 경우에 스크롤 처음으로 let topInset = self.calendarView.scrollView.adjustedContentInset.top self.calendarView.scrollView.setContentOffset(CGPoint(x: 0, y: -topInset), animated: true) @@ -376,6 +388,65 @@ extension CalendarViewController: MonthPickerViewControllerDelegate { } extension CalendarViewController: InformationCellDelegate { + // 좋아요 버튼 눌리는 시점 + func didTapLikesButton(in cell: InformationCell, likesCount: Int, infoType: InfoType, infoId: Int) + { + print("좋아요 눌린: \(infoId)") + print("CalendarViewController: 좋아요 눌림") + switch infoType { + case .discount: + discountInfoManager.postDiscountsLikes(userId: 1, discountInfoId: infoId) { result in + switch result { + case .success(let response): + print("좋아요 성공") + + if response.result.likeCount > likesCount { + AlertManager.showAlert( + on: self, title: "추천하시겠습니까?", message: nil, needsCancelButton: true + ) { _ in + self.setupData() + } + } else { + AlertManager.showAlert( + on: self, title: "추천을 취소하시겠습니까?", message: nil, needsCancelButton: true + ) { _ in + self.setupData() + } + } + + case .failure(let error): + print(error.localizedDescription) + } + } + + case .support: + supportInfoManager.postSupportsLikes(userId: 1, supportInfoId: infoId) { result in + switch result { + case .success(let response): + print("좋아요 성공") + + if response.result.likeCount > likesCount { + AlertManager.showAlert( + on: self, title: "추천하시겠습니까?", message: nil, needsCancelButton: true + ) { _ in + self.setupData() + } + } else { + AlertManager.showAlert( + on: self, title: "추천을 취소하시겠습니까?", message: nil, needsCancelButton: true + ) { _ in + self.setupData() + } + } + + case .failure(let error): + print(error.localizedDescription) + } + } + } + } + + // 사이트 바로가기 버튼 눌리는 시점 func didTapWebButton(in cell: InformationCell, urlString: String) { guard let url = URL(string: urlString) else { print("Error: 유효하지 않은 url \(urlString)") diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/Calendar/Views/CalendarViewUIComponents/BannerView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/Calendar/Views/CalendarViewUIComponents/BannerView.swift index b4c302de..351e1114 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/Calendar/Views/CalendarViewUIComponents/BannerView.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/Calendar/Views/CalendarViewUIComponents/BannerView.swift @@ -17,13 +17,7 @@ class BannerView: UIView { view.layer.cornerRadius = 15 view.layer.borderWidth = 1.0 view.layer.borderColor = BudgetBuddiesAsset.AppColor.logoLine1.color.cgColor - - view.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.1).cgColor - view.layer.shadowOpacity = 1 - view.layer.shadowRadius = 10 //반경 - view.layer.shadowOffset = CGSize(width: 0, height: 0) - view.layer.masksToBounds = false - + view.setShadow(opacity: 1, Radius: 10, offSet: CGSize(width: 0, height: 0)) return view }() diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/Calendar/Views/CalendarViewUIComponents/MainCalendarView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/Calendar/Views/CalendarViewUIComponents/MainCalendarView.swift index 10a35e4d..992f8a50 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/Calendar/Views/CalendarViewUIComponents/MainCalendarView.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/Calendar/Views/CalendarViewUIComponents/MainCalendarView.swift @@ -43,12 +43,7 @@ class MainCalendarView: UIView { let view = UIView() view.backgroundColor = BudgetBuddiesAsset.AppColor.white.color view.layer.cornerRadius = 15 - - view.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.1).cgColor - view.layer.shadowOpacity = 1 - view.layer.shadowRadius = 5 - view.layer.shadowOffset = CGSize(width: 0, height: 0) - view.layer.masksToBounds = false + view.setShadow(opacity: 1, Radius: 10, offSet: CGSize(width: 0, height: 0)) return view }() @@ -355,7 +350,7 @@ extension MainCalendarView { backViewMargin.addSubview(raisedView) let scale = UIScreen.main.scale - let adjustedInset = 3.0 / scale + let adjustedInset = 8.0 / scale raisedView.snp.makeConstraints { make in make.top.equalTo(headerStackView.snp.bottom).inset(topInsetBase - (80 * row)) diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/InfoList/Controllers/InfoListViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/InfoList/Controllers/InfoListViewController.swift index aa1e2e25..6d867a5f 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/InfoList/Controllers/InfoListViewController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Calendar/InfoList/Controllers/InfoListViewController.swift @@ -137,37 +137,14 @@ final class InfoListViewController: UIViewController { // MARK: - Set up NavigationBar private func setupNavigationBar() { - let appearance = UINavigationBarAppearance() - appearance.configureWithDefaultBackground() - appearance.shadowColor = nil - - // 네비게이션 바 타이틀 폰트, 자간 설정 - let titleFont = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 18) - let titleAttributes: [NSAttributedString.Key: Any] = [ - .font: titleFont, - .foregroundColor: BudgetBuddiesAsset.AppColor.textBlack.color, - .kern: -0.45, - ] - - appearance.titleTextAttributes = titleAttributes - - navigationController?.navigationBar.standardAppearance = appearance - navigationController?.navigationBar.compactAppearance = appearance - navigationController?.navigationBar.scrollEdgeAppearance = appearance + self.setupDefaultNavigationBar(backgroundColor: .clear) + // 뒤로가기 제스처 추가 + self.navigationController?.interactivePopGestureRecognizer?.delegate = self + navigationController?.navigationBar.isHidden = false // 백 버튼 - lazy var backButton: UIBarButtonItem = { - let btn = UIBarButtonItem( - image: UIImage(systemName: "chevron.left"), - style: .done, - target: self, - action: #selector(didTapBarButtonItem)) - btn.tintColor = BudgetBuddiesAsset.AppColor.subGray.color - return btn - }() - - navigationItem.leftBarButtonItem = backButton + addBackButton(selector: #selector(didTapBarButtonItem)) } // MARK: - Set up TableView @@ -342,6 +319,7 @@ extension InfoListViewController: UITableViewDelegate { } } + // MARK: - ScrollView Delegate func scrollViewDidScroll(_ scrollView: UIScrollView) { let currentOffset = scrollView.contentOffset.y let offsetDifference = currentOffset - previousScrollOffset @@ -363,6 +341,64 @@ extension InfoListViewController: UITableViewDelegate { // MARK: - InformationCell Delegate extension InfoListViewController: InformationCellDelegate { + // 좋아요 눌리는 시점 + func didTapLikesButton(in cell: InformationCell, likesCount: Int, infoType: InfoType, infoId: Int) + { + print("좋아요 눌린: \(infoId)") + print("InfoListViewController: 좋아요 눌림") + switch infoType { + case .discount: + discountInfoManager.postDiscountsLikes(userId: 1, discountInfoId: infoId) { result in + switch result { + case .success(let response): + print("좋아요 성공") + + if response.result.likeCount > likesCount { + AlertManager.showAlert( + on: self, title: "추천하시겠습니까?", message: nil, needsCancelButton: true + ) { _ in + self.setupData() + } + + } else { + AlertManager.showAlert( + on: self, title: "추천을 취소하시겠습니까?", message: nil, needsCancelButton: true + ) { _ in + self.setupData() + } + } + + case .failure(let error): + print(error.localizedDescription) + } + } + case .support: + supportInfoManager.postSupportsLikes(userId: 1, supportInfoId: infoId) { result in + switch result { + case .success(let response): + print("좋아요 성공") + + if response.result.likeCount > likesCount { + AlertManager.showAlert( + on: self, title: "추천하시겠습니까?", message: nil, needsCancelButton: true + ) { _ in + self.setupData() + } + + } else { + AlertManager.showAlert( + on: self, title: "추천을 취소하시겠습니까?", message: nil, needsCancelButton: true + ) { _ in + self.setupData() + } + } + + case .failure(let error): + print(error.localizedDescription) + } + } + } + } // informationCell: 사이트 바로가기 버튼이 눌리는 시점 func didTapWebButton(in cell: InformationCell, urlString: String) { @@ -382,3 +418,10 @@ extension InfoListViewController: BottomSheetViewControllerDelegate { setupData() } } + +// MARK: - 뒤로 가기 슬라이드 제스처 추가 +extension InfoListViewController: UIGestureRecognizerDelegate { + func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } +} diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/CustomTabBar/Controllers/RootTabBarController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/CustomTabBar/Controllers/RootTabBarController.swift index e9158c0c..ef2f6375 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/CustomTabBar/Controllers/RootTabBarController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/CustomTabBar/Controllers/RootTabBarController.swift @@ -9,13 +9,40 @@ import UIKit class RootTabBarController: CustomTabBarController { // MARK: - Properties - private let mainViewController = UINavigationController(rootViewController: MainViewController()) - private let consumeViewController = UINavigationController( - rootViewController: ConsumeViewController()) - private let calendarViewController = UINavigationController( - rootViewController: CalendarViewController()) - private let allLookingViewController = UINavigationController( - rootViewController: AllLookingViewController()) + + // Controller + + public let mainViewController: MainViewController! + public let consumeViewController: ConsumeViewController! + public let calendarViewController: CalendarViewController! + public let allLookingViewController: AllLookingViewController! + + private let mainViewNavigationController: UINavigationController! + private let consumeViewNavigationController: UINavigationController! + private let calendarViewNavigationController: UINavigationController! + private let allLookingViewNavigationController: UINavigationController! + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + + mainViewController = MainViewController() + consumeViewController = ConsumeViewController() + calendarViewController = CalendarViewController() + allLookingViewController = AllLookingViewController() + + mainViewNavigationController = UINavigationController(rootViewController: mainViewController) + consumeViewNavigationController = UINavigationController( + rootViewController: consumeViewController) + calendarViewNavigationController = UINavigationController( + rootViewController: calendarViewController) + allLookingViewNavigationController = UINavigationController( + rootViewController: allLookingViewController) + + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } // MARK: - Life Cycle override func viewDidLoad() { @@ -29,10 +56,10 @@ class RootTabBarController: CustomTabBarController { self.view.backgroundColor = BudgetBuddiesAsset.AppColor.background.color self.setupViewControllers([ - mainViewController, - consumeViewController, - calendarViewController, - allLookingViewController, + mainViewNavigationController, + consumeViewNavigationController, + calendarViewNavigationController, + allLookingViewNavigationController, ]) } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/ExtendedLaunchScreen/ExtendedLaunchScreenView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/ExtendedLaunchScreen/ExtendedLaunchScreenView.swift new file mode 100644 index 00000000..0bd64e1b --- /dev/null +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/ExtendedLaunchScreen/ExtendedLaunchScreenView.swift @@ -0,0 +1,45 @@ +// +// ExtendedLaunchScreenView.swift +// BudgetBuddies +// +// Created by Jiwoong CHOI on 8/23/24. +// + +import SnapKit +import UIKit + +class ExtendedLaunchScreenView: UIView { + // MARK: - Properties + + private let budgetBuddiesLogo: UIImageView = { + let imageView = UIImageView() + imageView.image = BudgetBuddiesAsset.AppImage.Logo.logoImage.image + return imageView + }() + + // MARK: - Initializer + + override init(frame: CGRect) { + super.init(frame: frame) + + setLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Methods + + private func setLayout() { + self.backgroundColor = BudgetBuddiesAsset.AppColor.coreYellow.color + + self.addSubview(budgetBuddiesLogo) + budgetBuddiesLogo.snp.makeConstraints { make in + make.width.equalTo(116) + make.height.equalTo(108) + make.top.equalTo(safeAreaLayoutGuide.snp.top).inset(261) + make.centerX.equalToSuperview() + } + } +} diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/ExtendedLaunchScreen/ExtendedLaunchScreenViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/ExtendedLaunchScreen/ExtendedLaunchScreenViewController.swift new file mode 100644 index 00000000..9eabfad2 --- /dev/null +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/ExtendedLaunchScreen/ExtendedLaunchScreenViewController.swift @@ -0,0 +1,56 @@ +// +// ExtendedLaunchScreenViewController.swift +// BudgetBuddies +// +// Created by Jiwoong CHOI on 8/23/24. +// + +import UIKit + +class ExtendedLaunchScreenViewController: UIViewController { + + // MARK: - Properties + + // View Properties + private let extendedLaunchScreenView = ExtendedLaunchScreenView() + + // Controller Properties + private let rootTabBarController = RootTabBarController() + + // Variable Properties + private let userId = 1 + + // MARK: - View Life Cycle + + override func loadView() { + view = extendedLaunchScreenView + } + + override func viewDidLoad() { + super.viewDidLoad() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + self.fetchMainModelData() + Thread.sleep(forTimeInterval: 3) + self.connectRootTabBarController() + } + + // MARK: - Methods + + private func fetchMainModelData() { + self.rootTabBarController.mainViewController.fetchUserDataFromServer(userId: self.userId) + self.rootTabBarController.mainViewController.fetchDataFromMainPageAPI(userId: self.userId) + self.rootTabBarController.calendarViewController.setupData() + self.rootTabBarController.allLookingViewController.fetchUserData(userId: self.userId) + } + + private func connectRootTabBarController() { + if let window = view.window { + UIView.transition(with: window, duration: 0.3, options: .transitionCrossDissolve) { + window.rootViewController = self.rootTabBarController + } + } + } +} diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainView.swift index 16257204..98c64c31 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainView.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainView.swift @@ -24,6 +24,19 @@ final class MainView: UIView { private static let height = 1200 // MARK: - UI Components + // 스크롤뷰 여기서 생성 + var scrollView: UIScrollView = { + let sv = UIScrollView() + sv.showsHorizontalScrollIndicator = false + sv.showsVerticalScrollIndicator = false + // sv.backgroundColor = BudgetBuddiesAsset.AppColor.background.color + return sv + }() + + var contentView: UIView = { + let view = UIView() + return view + }() // Core Yellow 색 배경 private let coreYellowColorBackgroundView = CoreYellowBackground() @@ -85,13 +98,11 @@ final class MainView: UIView { // MARK: - Methods private func setLayout() { - backgroundColor = BudgetBuddiesAsset.AppColor.background.color - self.snp.makeConstraints { make in - make.width.equalTo(MainView.width) - make.height.equalTo(MainView.height) - } - self.addSubviews( + self.addSubviews(scrollView) + scrollView.addSubviews(contentView) + + contentView.addSubviews( coreYellowColorBackgroundView, homeTextLabel, summaryInfoContainerView, @@ -104,16 +115,27 @@ final class MainView: UIView { comsumedAnalysisSecondItem ) + scrollView.snp.makeConstraints { make in + make.edges.equalToSuperview() + make.leading.trailing.equalToSuperview() + } + + contentView.snp.makeConstraints { make in + make.edges.equalToSuperview() + make.width.equalTo(scrollView) + make.bottom.equalTo(comsumedAnalysisSecondItem.snp.bottom) + } + coreYellowColorBackgroundView.snp.makeConstraints { make in - make.top.equalToSuperview() make.centerX.equalToSuperview() - make.width.equalToSuperview() - make.height.equalTo(270) + make.leading.trailing.equalToSuperview() + make.height.equalTo(2000) // 위로 스크롤해도 계속 보이게 + make.bottom.equalTo(homeTextLabel.snp.bottom).offset(184) } homeTextLabel.snp.makeConstraints { make in - make.top.equalTo(safeAreaLayoutGuide.snp.top).inset(14) - make.leading.equalTo(summaryInfoContainerView.snp.leading).inset(13) + make.top.equalToSuperview().inset(14) + make.leading.equalToSuperview().inset(29) } summaryInfoContainerView.snp.makeConstraints { make in @@ -138,14 +160,14 @@ final class MainView: UIView { monthlyBudgetInfoCollectionView.snp.makeConstraints { make in make.width.equalToSuperview() - make.height.equalTo(162) - make.top.equalTo(monthlyBudgetInfoTextLabel.snp.bottom).offset(7) + make.height.equalTo(176) + make.top.equalTo(monthlyBudgetInfoTextLabel.snp.bottom) /*.offset(7)*/ make.centerX.equalToSuperview() } monthlyConsumedAnalysisTextLabel.snp.makeConstraints { make in make.leading.equalTo(monthlyBudgetInfoTextLabel.snp.leading) - make.top.equalTo(monthlyBudgetInfoCollectionView.snp.bottom).offset(32) + make.top.equalTo(monthlyBudgetInfoCollectionView.snp.bottom).offset(25) } monthlyConsumedAnalysisLookEntireButton.snp.makeConstraints { make in diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/FaceGraphView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/FaceGraphView.swift new file mode 100644 index 00000000..e8c32e73 --- /dev/null +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/FaceGraphView.swift @@ -0,0 +1,76 @@ +// +// FaceGraphView.swift +// BudgetBuddies +// +// Created by 이승진 on 8/21/24. +// + +import DGCharts +import SnapKit +import UIKit + +final class FaceGraphView: UIView { + let pieChartView = PieChartView() + + let centerImageView = { + let image = UIImageView() + image.image = BudgetBuddiesAsset.AppImage.Face.basicFace.image + image.contentMode = .scaleAspectFit + return image + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + setup() + setConsts() + } + + private func setup() { + + [pieChartView, centerImageView].forEach { + self.addSubview($0) + } + } + + private func setConsts() { + pieChartView.snp.makeConstraints { + $0.edges.equalToSuperview() + } + + centerImageView.snp.makeConstraints { + $0.center.equalTo(pieChartView) + $0.width.height.equalTo(pieChartView.snp.width).multipliedBy(0.8) + } + } + + func setupChart(entries: [PieChartDataEntry]) { + let dataSet = PieChartDataSet(entries: entries, label: "") + dataSet.colors = [ + BudgetBuddiesAsset.AppColor.coreYellow.color, BudgetBuddiesAsset.AppColor.strokeGray1.color, + ] + dataSet.drawValuesEnabled = false + dataSet.sliceSpace = 2 + dataSet.selectionShift = 5 // 슬라이스의 두께를 줄임 + + let data = PieChartData(dataSet: dataSet) + pieChartView.data = data + + pieChartView.usePercentValuesEnabled = false + pieChartView.drawHoleEnabled = true + pieChartView.holeRadiusPercent = 0.75 // 구멍 크기를 키우기 + pieChartView.transparentCircleRadiusPercent = 0.76 + pieChartView.chartDescription.enabled = false + pieChartView.legend.enabled = false + pieChartView.notifyDataSetChanged() + pieChartView.animate(xAxisDuration: 1.4, easingOption: .easeOutBack) // 애니메이션을 추가 + } + + func updateCenterImage(image: UIImage?) { + centerImageView.image = image + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } +} diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyBudgetInfoCollectionView/MonthlyBudgetInfoCollectionViewUIComponents/MonthlyBudgetInfoCollectionViewCell.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyBudgetInfoCollectionView/MonthlyBudgetInfoCollectionViewUIComponents/MonthlyBudgetInfoCollectionViewCell.swift index 70731d91..ebcee145 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyBudgetInfoCollectionView/MonthlyBudgetInfoCollectionViewUIComponents/MonthlyBudgetInfoCollectionViewCell.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyBudgetInfoCollectionView/MonthlyBudgetInfoCollectionViewUIComponents/MonthlyBudgetInfoCollectionViewCell.swift @@ -34,6 +34,15 @@ class MonthlyBudgetInfoCollectionViewCell: UICollectionViewCell { // MARK: - UI Components + // 최하단 backVIew + public let whiteBackView: UIView = { + let view = UIView() + view.backgroundColor = BudgetBuddiesAsset.AppColor.white.color + view.layer.cornerRadius = 15 + view.setShadow(opacity: 1, Radius: 5, offSet: CGSize(width: 0, height: 0)) + return view + }() + // 정보 구분 배경색 public let colorBackground: UIView = { let view = UIView() @@ -157,7 +166,10 @@ class MonthlyBudgetInfoCollectionViewCell: UICollectionViewCell { backgroundColor = BudgetBuddiesAsset.AppColor.white.color // Add Subviews - addSubviews( + + addSubviews(whiteBackView) + + whiteBackView.addSubviews( colorBackground, infoCategoryBackground, titleTextLabel, @@ -168,6 +180,11 @@ class MonthlyBudgetInfoCollectionViewCell: UICollectionViewCell { infoCategoryBackground.addSubview(infoCategoryTextLabel) // Make UI Components Contraints + // 최하단 백뷰 + whiteBackView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + // 정보 구분 배경색 colorBackground.snp.makeConstraints { make in make.height.equalTo(47) diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyBudgetInfoTextLabel.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyBudgetInfoTextLabel.swift index 90a0fbfb..42062e1f 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyBudgetInfoTextLabel.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyBudgetInfoTextLabel.swift @@ -34,5 +34,6 @@ class MonthlyBudgetInfoTextLabel: UILabel { self.text = "\(month)월 주머니 정보" self.textColor = BudgetBuddiesAsset.AppColor.textBlack.color self.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 18) + self.setCharacterSpacing(-0.45) } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyConsumedAnalysisFirstItem.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyConsumedAnalysisFirstItem.swift index 037036f9..3d28b280 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyConsumedAnalysisFirstItem.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyConsumedAnalysisFirstItem.swift @@ -42,6 +42,7 @@ class MonthlyConsumedAnalysisFirstItem: UIView { // MARK: - Methods private func setLayout() { + self.setShadow(opacity: 1, Radius: 5, offSet: CGSize(width: 0, height: 0)) backgroundColor = BudgetBuddiesAsset.AppColor.white.color layer.cornerRadius = 15 diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyConsumedAnalysisSecondItem.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyConsumedAnalysisSecondItem.swift index 9de871d3..8702a954 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyConsumedAnalysisSecondItem.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyConsumedAnalysisSecondItem.swift @@ -43,6 +43,7 @@ class MonthlyConsumedAnalysisSecondItem: UIView { // MARK: - Methods private func setLayout() { + self.setShadow(opacity: 1, Radius: 5, offSet: CGSize(width: 0, height: 0)) backgroundColor = BudgetBuddiesAsset.AppColor.white.color layer.cornerRadius = 15 diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyConsumedAnalysisTextLabel.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyConsumedAnalysisTextLabel.swift index 2b7b9d45..1ed82103 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyConsumedAnalysisTextLabel.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/MonthlyConsumedAnalysisTextLabel.swift @@ -35,5 +35,6 @@ class MonthlyConsumedAnalysisTextLabel: UILabel { self.text = "\(month)월 주머니 정보" self.textColor = BudgetBuddiesAsset.AppColor.textBlack.color self.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 18) + self.setCharacterSpacing(-0.45) } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/SummaryInfoContainerView/SummaryInfoContainerView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/SummaryInfoContainerView/SummaryInfoContainerView.swift index 94ba4ffa..694cb870 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/SummaryInfoContainerView/SummaryInfoContainerView.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/SummaryInfoContainerView/SummaryInfoContainerView.swift @@ -44,22 +44,17 @@ final class SummaryInfoContainerView: UIView { public let mainTextLabel = MainTextLabel() // 반응형 차트 - /* - - - - - 반응형 차트 UI 컴포넌트 선언 필요!! - - - - - */ + public let faceChartView = { + let view = FaceGraphView() + view.backgroundColor = .white + return view + }() // 코멘트 박스 이미지 private let commentBoxImageView: UIImageView = { let imageView = UIImageView() imageView.image = BudgetBuddiesAsset.AppImage.MainViewImage.commentBox.image + imageView.contentMode = .scaleToFill return imageView }() @@ -81,20 +76,40 @@ final class SummaryInfoContainerView: UIView { // 카테고리 2 잔여금액 표시 컨테이너 public let secondCategoryLeftMoneyContainer = CategoryLeftMoneyContainer() + // 카테고리 1, 2담을 stackView + lazy var firstSecondLeftMoneyStackView: UIStackView = { + let sv = UIStackView(arrangedSubviews: [ + firstCategoryLeftMoneyContainer, secondCategoryLeftMoneyContainer, + ]) + sv.axis = .horizontal + sv.distribution = .fillEqually + sv.alignment = .center + sv.spacing = 12 + return sv + }() + // 카테고리 3 잔여금액 표시 컨테이너 public let thirdCategoryLeftMoneyContainer = CategoryLeftMoneyContainer() // 카테고리 4 잔여금액 표시 컨테이너 public let fourthCategoryLeftMoneyContainer = CategoryLeftMoneyContainer() + // 카테고리 3, 4담을 stackView + lazy var thirdFourthLeftMoneyStackView: UIStackView = { + let sv = UIStackView(arrangedSubviews: [ + thirdCategoryLeftMoneyContainer, fourthCategoryLeftMoneyContainer, + ]) + sv.axis = .horizontal + sv.distribution = .fillEqually + sv.alignment = .center + sv.spacing = 12 + return sv + }() + // 구분선 private let divider: UIView = { let view = UIView() view.backgroundColor = BudgetBuddiesAsset.AppColor.strokeGray1.color - view.snp.makeConstraints { make in - make.width.equalTo(307) - make.height.equalTo(1) - } return view }() @@ -102,6 +117,7 @@ final class SummaryInfoContainerView: UIView { public let editGoalButtonContaier: TextAndRightChevronButton = { let buttonContainer = TextAndRightChevronButton() buttonContainer.textLabel.text = "목표 수정하기" + buttonContainer.setCharacterSpacing(-0.35) buttonContainer.snp.makeConstraints { make in make.width.equalTo(100) make.height.equalTo(21) @@ -122,6 +138,8 @@ final class SummaryInfoContainerView: UIView { // MARK: - Methods private func setLayout() { + self.setShadow(opacity: 1, Radius: 5, offSet: CGSize(width: 0, height: 0)) + self.backgroundColor = BudgetBuddiesAsset.AppColor.white.color self.layer.cornerRadius = 20 @@ -129,21 +147,11 @@ final class SummaryInfoContainerView: UIView { self.addSubviews( monthRoundedRectangle, mainTextLabel, - /* - - - - 반응형 차트 View 추가 코드 필요!! - - - - */ + faceChartView, commentBoxImageView, leftMoneyTextLabel, - firstCategoryLeftMoneyContainer, - secondCategoryLeftMoneyContainer, - thirdCategoryLeftMoneyContainer, - fourthCategoryLeftMoneyContainer, + firstSecondLeftMoneyStackView, + thirdFourthLeftMoneyStackView, divider, editGoalButtonContaier ) @@ -170,32 +178,30 @@ final class SummaryInfoContainerView: UIView { mainTextLabel.snp.makeConstraints { make in make.leading.equalToSuperview().inset(20) make.top.equalToSuperview().inset(62) + make.trailing.equalToSuperview().inset(140) } - /* - - - - - - 반응형 차트 레이아웃 코드 필요!! - - - - - - */ + // 반응형 그래프 + faceChartView.snp.makeConstraints { make in + make.trailing.equalToSuperview().offset(-20) + make.top.equalToSuperview().offset(41) + make.leading.equalTo(mainTextLabel.snp.trailing) + // make.height.width.equalTo(100) + make.height.equalTo(self.faceChartView.snp.width) + } // 코멘트 박스 이미지 commentBoxImageView.snp.makeConstraints { make in make.centerX.equalToSuperview() - make.top.equalToSuperview().inset(172) + make.top.equalTo(self.mainTextLabel.snp.bottom).offset(26) + make.height.equalTo(60) + make.leading.trailing.equalToSuperview().inset(20) } // 코멘트 텍스트 레이블 commentTextLabel.snp.makeConstraints { make in make.height.equalTo(24) - make.bottom.equalToSuperview().inset(11) + make.bottom.equalToSuperview().inset(12) make.leading.equalToSuperview().inset(20) } @@ -207,35 +213,55 @@ final class SummaryInfoContainerView: UIView { // 카테고리 1 잔여금액 표시 컨테이너 firstCategoryLeftMoneyContainer.snp.makeConstraints { make in - make.leading.equalToSuperview().inset(20) - make.trailing.equalToSuperview().inset(184) - make.top.equalToSuperview().inset(263) + // make.leading.equalToSuperview().inset(20) + // make.trailing.equalToSuperview().inset(184) + // make.top.equalTo(leftMoneyTextLabel.snp.bottom).offset(8) + make.height.equalTo(76) } // 카테고리 2 잔여금액 표시 컨테이너 secondCategoryLeftMoneyContainer.snp.makeConstraints { make in - make.trailing.equalToSuperview().inset(20) - make.leading.equalToSuperview().inset(184) - make.centerY.equalTo(firstCategoryLeftMoneyContainer) + // make.trailing.equalToSuperview().inset(20) + // make.leading.equalToSuperview().inset(184) + // make.centerY.equalTo(firstCategoryLeftMoneyContainer) + make.height.equalTo(76) + } + + // 카테고리 1, 2 스택뷰 + firstSecondLeftMoneyStackView.snp.makeConstraints { make in + make.top.equalTo(leftMoneyTextLabel.snp.bottom).offset(8) + make.leading.trailing.equalToSuperview().inset(20) + make.height.equalTo(76) } // 카테고리 3 잔여금액 표시 컨테이너 thirdCategoryLeftMoneyContainer.snp.makeConstraints { make in - make.leading.equalTo(firstCategoryLeftMoneyContainer.snp.leading) - make.trailing.equalTo(firstCategoryLeftMoneyContainer.snp.trailing) - make.top.equalTo(firstCategoryLeftMoneyContainer.snp.bottom).offset(12) + // make.leading.equalTo(firstCategoryLeftMoneyContainer.snp.leading) + // make.trailing.equalTo(firstCategoryLeftMoneyContainer.snp.trailing) + // make.top.equalTo(firstCategoryLeftMoneyContainer.snp.bottom).offset(12) + make.height.equalTo(76) } // 카테고리 4 잔여금액 표시 컨테이너 fourthCategoryLeftMoneyContainer.snp.makeConstraints { make in - make.trailing.equalTo(secondCategoryLeftMoneyContainer.snp.trailing) - make.leading.equalTo(secondCategoryLeftMoneyContainer.snp.leading) - make.centerY.equalTo(thirdCategoryLeftMoneyContainer) + // make.trailing.equalTo(secondCategoryLeftMoneyContainer.snp.trailing) + // make.leading.equalTo(secondCategoryLeftMoneyContainer.snp.leading) + // make.centerY.equalTo(thirdCategoryLeftMoneyContainer) + make.height.equalTo(76) + } + + // 카테고리 3, 4 스택뷰 + thirdFourthLeftMoneyStackView.snp.makeConstraints { make in + make.top.equalTo(firstSecondLeftMoneyStackView.snp.bottom).offset(12) + make.leading.trailing.equalToSuperview().inset(20) + make.height.equalTo(76) } // 구분선 divider.snp.makeConstraints { make in - make.bottom.equalToSuperview().inset(57) + make.height.equalTo(1) + make.leading.trailing.equalToSuperview().inset(18) + make.bottom.equalTo(editGoalButtonContaier.snp.top).inset(-18) make.centerX.equalToSuperview() } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/SummaryInfoContainerView/SummaryInfoContainerViewUIComponents/CategoryLeftMoneyContainer.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/SummaryInfoContainerView/SummaryInfoContainerViewUIComponents/CategoryLeftMoneyContainer.swift index 908836d8..37389b4f 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/SummaryInfoContainerView/SummaryInfoContainerViewUIComponents/CategoryLeftMoneyContainer.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/SummaryInfoContainerView/SummaryInfoContainerViewUIComponents/CategoryLeftMoneyContainer.swift @@ -15,10 +15,12 @@ class CategoryLeftMoneyContainer: UIView { // 동적 UI 컴포넌트 private let iconImageView: UIImageView = { let imageView = UIImageView() - imageView.snp.makeConstraints { make in - make.width.height.equalTo(40) - } imageView.contentMode = .scaleAspectFit + imageView.layer.cornerRadius = 15 + imageView.backgroundColor = .white + imageView.layer.masksToBounds = true + imageView.layer.borderWidth = 1 + imageView.layer.borderColor = BudgetBuddiesAsset.AppColor.mainBoxStroke.color.cgColor return imageView }() @@ -33,6 +35,26 @@ class CategoryLeftMoneyContainer: UIView { // 동적 UI 컴포넌트 private let leftPriceLabel = LeftPriceUILabel() + // 라벨 스택뷰 + lazy var labelStackView: UIStackView = { + let sv = UIStackView(arrangedSubviews: [categoryTextLabel, leftPriceLabel]) + sv.axis = .vertical + sv.spacing = 0 + sv.alignment = .leading + sv.distribution = .fill + return sv + }() + + lazy var iconlabelStackView: UIStackView = { + let sv = UIStackView(arrangedSubviews: [iconImageView, labelStackView]) + sv.axis = .horizontal + sv.distribution = .fill + sv.spacing = 0 + sv.alignment = .center + sv.spacing = 7 + return sv + }() + // MARK: - Initializer override init(frame: CGRect) { @@ -101,24 +123,30 @@ class CategoryLeftMoneyContainer: UIView { } self.addSubviews( - iconImageView, - categoryTextLabel, - leftPriceLabel + iconlabelStackView ) iconImageView.snp.makeConstraints { make in - make.leading.equalToSuperview().inset(12) - make.centerY.equalToSuperview() + make.height.width.equalTo(40) } categoryTextLabel.snp.makeConstraints { make in - make.leading.equalTo(iconImageView.snp.trailing).offset(7) - make.top.equalTo(iconImageView.snp.top).offset(2) + make.height.equalTo(18) } leftPriceLabel.snp.makeConstraints { make in - make.leading.equalTo(iconImageView.snp.trailing).offset(7) - make.bottom.equalTo(iconImageView.snp.bottom).offset(-2) + make.height.equalTo(18) } + + labelStackView.snp.makeConstraints { make in + make.height.equalTo(36) + } + + iconlabelStackView.snp.makeConstraints { make in + make.center.equalToSuperview() + make.leading.trailing.equalToSuperview().inset(12) + make.height.equalTo(40) + } + } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/SummaryInfoContainerView/SummaryInfoContainerViewUIComponents/CommentTextLabel.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/SummaryInfoContainerView/SummaryInfoContainerViewUIComponents/CommentTextLabel.swift index c8b30612..eec9b8b8 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/SummaryInfoContainerView/SummaryInfoContainerViewUIComponents/CommentTextLabel.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/SummaryInfoContainerView/SummaryInfoContainerViewUIComponents/CommentTextLabel.swift @@ -35,7 +35,7 @@ class CommentTextLabel: UILabel { numberFormatter.numberStyle = .decimal if let formattedString = numberFormatter.string(from: NSNumber(value: leftMoney)) { - let mainText = "총\(formattedString)원을 더 쓸 수 있어요" + let mainText = "총 \(formattedString)원을 더 쓸 수 있어요" let attributedString = NSMutableAttributedString(string: mainText) if let range = mainText.range(of: formattedString) { @@ -60,7 +60,7 @@ class CommentTextLabel: UILabel { numberFormatter.numberStyle = .decimal if let formattedString = numberFormatter.string(from: NSNumber(value: leftMoney)) { - let mainText = "총\(formattedString)원을 더 쓸 수 있어요" + let mainText = "총 \(formattedString)원을 더 쓸 수 있어요" let attributedString = NSMutableAttributedString(string: mainText) if let range = mainText.range(of: formattedString) { diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/TextAndRightChevronButton.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/TextAndRightChevronButton.swift index a4d65009..de8e08df 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/TextAndRightChevronButton.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainView/MainViewUIComponents/TextAndRightChevronButton.swift @@ -58,8 +58,8 @@ class TextAndRightChevronButton: UIButton { // 오른쪽 쉐브론 이미지뷰 rightChevronImageView.snp.makeConstraints { make in - make.width.equalTo(10) - make.height.equalTo(13) + make.width.equalTo(8) + make.height.equalTo(15) make.centerY.equalToSuperview() make.trailing.equalToSuperview().inset(5) } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainViewController.swift index 0f82f2d7..be0fa1cf 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainViewController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Main/MainViewController.swift @@ -6,6 +6,7 @@ // import Combine +import DGCharts import Kingfisher import Moya import SnapKit @@ -47,7 +48,24 @@ final class MainViewController: UIViewController { @Published private var userName = String() @Published private var totalConsumptionAmount = 0 + // 총 목표액과 총 소비액 변수 + var totalGoalAmount: Double = 0 + var totalSpentAmount: Double = 0 + + // FaceImage + let images: [UIImage] = [ + BudgetBuddiesAsset.AppImage.Face.failureFace.image, + BudgetBuddiesAsset.AppImage.Face.crisisFace.image, + BudgetBuddiesAsset.AppImage.Face.anxietyFace.image, + BudgetBuddiesAsset.AppImage.Face.basicFace.image, + BudgetBuddiesAsset.AppImage.Face.goodFace.image, + BudgetBuddiesAsset.AppImage.Face.successFace.image, + ] + // MARK: - View Life Cycle + override func loadView() { + self.view = mainView + } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) @@ -60,7 +78,7 @@ final class MainViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() setScrollViewSetting() - setLayout() + // setLayout() setUICollectionViewDelegate() setNavigationSetting() setButtonAction() @@ -71,7 +89,7 @@ final class MainViewController: UIViewController { // 탭바에 가려지는 요소 보이게 하기 override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() - self.mainScrollView.contentInset.bottom = 10 + self.mainView.scrollView.contentInset.bottom = 22 } // MARK: - Methods @@ -93,10 +111,7 @@ final class MainViewController: UIViewController { // Methods in ViewDidLoad method private func setScrollViewSetting() { - mainScrollView.backgroundColor = BudgetBuddiesAsset.AppColor.background.color mainScrollView.contentInsetAdjustmentBehavior = .never - mainScrollView.showsVerticalScrollIndicator = false - mainScrollView.showsHorizontalScrollIndicator = false } private func setLayout() { @@ -149,6 +164,58 @@ final class MainViewController: UIViewController { mainView.comsumedAnalysisFirstItem.isUserInteractionEnabled = true } + + // Chart + private func setChart() { + let spentEntry = PieChartDataEntry(value: totalSpentAmount) + let remainingEntry = PieChartDataEntry(value: totalGoalAmount - totalSpentAmount) + + mainView.summaryInfoContainerView.faceChartView.setupChart(entries: [ + spentEntry, remainingEntry, + ]) + } + + // 금액을 포맷팅하는 헬퍼 함수 + private func formatCurrency(_ amount: Int) -> String { + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + return formatter.string(from: NSNumber(value: amount)) ?? "\(amount)" + } + + func updateGoalAndConsumption(goal: Int, spent: Int, remaining: Int) { + // 금액 포맷팅 + _ = formatCurrency(goal) + _ = formatCurrency(spent) + _ = formatCurrency(remaining) + + // 남은 금액 비율 계산 (남은 금액 / 총 목표 금액) + let remainingPercentage = Double(remaining) / Double(goal) * 100 + + var selectedImage: UIImage? + switch remainingPercentage { + case 81...100: + selectedImage = images[5] // 가장 긍정적인 이미지 + case 61...80: + selectedImage = images[4] + case 41...60: + selectedImage = images[3] + case 21...40: + selectedImage = images[2] + case 1...20: + selectedImage = images[1] + default: + selectedImage = images[0] // 가장 부정적인 이미지 + } + + // + mainView.summaryInfoContainerView.faceChartView.updateCenterImage(image: selectedImage) + + // 차트 데이터 업데이트 + self.totalGoalAmount = Double(goal) + self.totalSpentAmount = Double(spent) + + setChart() + } } // MARK: - Object C Methods @@ -194,7 +261,7 @@ extension MainViewController { extension MainViewController { - private func fetchUserDataFromServer(userId: Int) { + public func fetchUserDataFromServer(userId: Int) { userRouterProvider.request(.find(userId: self.userId)) { result in switch result { case .success(let response): @@ -215,7 +282,7 @@ extension MainViewController { /// /// # 설명 /// - 메인화면에서 보여지는 데이터들을 가져오기 위해서 사용합니다. - private func fetchDataFromMainPageAPI(userId: Int) { + public func fetchDataFromMainPageAPI(userId: Int) { mainRouterProvider.request(.get(userId: userId)) { result in switch result { case let .success(moyaResponse): @@ -240,6 +307,13 @@ extension MainViewController { self.mainView.summaryInfoContainerView.mainTextLabel.updateUsedMoney( usedMoney: totalConsumptionAmount) + self.updateGoalAndConsumption( + goal: consumptionGoalResponseListData.totalGoalAmount, + spent: consumptionGoalResponseListData.totalConsumptionAmount, + remaining: consumptionGoalResponseListData.totalGoalAmount + - consumptionGoalResponseListData.totalConsumptionAmount + ) + // 전체 잔여 금액 let totalLeftMoneyAmount = consumptionGoalResponseListData.totalGoalAmount diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/Cells/ReportTableViewCell.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/Cells/ReportTableViewCell.swift index ccce5043..87d8cbe1 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/Cells/ReportTableViewCell.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/Cells/ReportTableViewCell.swift @@ -17,9 +17,7 @@ final class ReportTableViewCell: UITableViewCell { let view = UIView() view.backgroundColor = .white view.layer.cornerRadius = 15 - view.layer.shadowColor = UIColor.black.withAlphaComponent(0.1).cgColor - view.layer.shadowOpacity = 1 - view.layer.shadowRadius = 10 + view.setShadow(opacity: 1, Radius: 5, offSet: CGSize(width: 0, height: 1)) view.layer.masksToBounds = false return view }() @@ -36,20 +34,25 @@ final class ReportTableViewCell: UITableViewCell { imageView.contentMode = .scaleAspectFit imageView.layer.cornerRadius = 15 imageView.layer.masksToBounds = true + imageView.setShadow(opacity: 1, Radius: 3.6, offSet: CGSize(width: 0, height: 0)) return imageView }() let titleLabel = { let label = UILabel() - label.font = UIFont.systemFont(ofSize: 16, weight: .semibold) - label.textColor = .black + label.text = " " + label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 16) + label.setCharacterSpacing(-0.4) + label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color return label }() let amountLabel = { let label = UILabel() - label.font = UIFont.systemFont(ofSize: 16, weight: .semibold) - label.textColor = .black + label.text = " " + label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 14) + label.setCharacterSpacing(-0.35) + label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color return label }() @@ -89,7 +92,8 @@ final class ReportTableViewCell: UITableViewCell { // MARK: - Setup Constraints private func setConsts() { backView.snp.makeConstraints { - $0.edges.equalToSuperview().inset(8) + $0.top.bottom.equalToSuperview().inset(8) + $0.leading.trailing.equalToSuperview().inset(16) } logoImageView.snp.makeConstraints { @@ -103,7 +107,7 @@ final class ReportTableViewCell: UITableViewCell { } titleLabel.snp.makeConstraints { - $0.top.equalTo(logoImageView.snp.top).offset(2) + $0.top.equalTo(logoImageView.snp.top).offset(5) $0.leading.equalTo(logoImageView.snp.trailing).offset(16) } @@ -113,10 +117,9 @@ final class ReportTableViewCell: UITableViewCell { } descriptionLabel.snp.makeConstraints { - $0.top.equalTo(logoImageView.snp.bottom).offset(20) - $0.leading.equalToSuperview().offset(16) - $0.trailing.equalToSuperview().offset(-16) - $0.bottom.equalToSuperview().offset(-16) + $0.height.equalTo(34) + $0.leading.trailing.equalToSuperview().inset(12) + $0.bottom.equalToSuperview().offset(-12) } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/ViewControllers/AgeEditViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/ViewControllers/AgeEditViewController.swift index f6a7495e..d30742b0 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/ViewControllers/AgeEditViewController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/ViewControllers/AgeEditViewController.swift @@ -23,8 +23,8 @@ final class AgeEditViewController: UIViewController { let titleLabel = { let label = UILabel() label.text = "성별과 연령대를\n선택해주세요" - label.textColor = .black - label.font = .systemFont(ofSize: 22, weight: .bold) + label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color + label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 22) label.numberOfLines = 0 return label }() @@ -32,7 +32,7 @@ final class AgeEditViewController: UIViewController { let genderLabel = { let label = UILabel() label.text = "성별" - label.textColor = .black + label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 14) return label }() @@ -48,13 +48,11 @@ final class AgeEditViewController: UIViewController { lazy var femaleButton: UIButton = { let button = UIButton(type: .system) button.setTitle("여성", for: .normal) - button.setTitleColor(.black, for: .normal) + button.setTitleColor(BudgetBuddiesAsset.AppColor.textBlack.color, for: .normal) + button.titleLabel?.font = BudgetBuddiesFontFamily.Pretendard.regular.font(size: 16) button.layer.cornerRadius = 15 - button.layer.borderColor = UIColor.lightGray.cgColor - button.layer.borderWidth = 1 - button.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.1).cgColor - button.layer.shadowOpacity = 1 - button.layer.shadowRadius = 10 //반경 + + button.setShadow(opacity: 1, Radius: 8, offSet: CGSize(width: 0, height: 1)) button.addTarget(self, action: #selector(selectGender(_:)), for: .touchUpInside) button.tag = 0 return button @@ -63,18 +61,25 @@ final class AgeEditViewController: UIViewController { lazy var maleButton: UIButton = { let button = UIButton(type: .system) button.setTitle("남성", for: .normal) - button.setTitleColor(.black, for: .normal) + button.setTitleColor(BudgetBuddiesAsset.AppColor.textBlack.color, for: .normal) + button.titleLabel?.font = BudgetBuddiesFontFamily.Pretendard.regular.font(size: 16) button.layer.cornerRadius = 15 - button.layer.borderColor = UIColor.lightGray.cgColor - button.layer.borderWidth = 1 - button.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.1).cgColor - button.layer.shadowOpacity = 1 - button.layer.shadowRadius = 10 //반경 + + button.setShadow(opacity: 1, Radius: 8, offSet: CGSize(width: 0, height: 1)) button.addTarget(self, action: #selector(selectGender(_:)), for: .touchUpInside) button.tag = 1 return button }() + lazy var buttonStackView: UIStackView = { + let sv = UIStackView(arrangedSubviews: [femaleButton, maleButton]) + sv.axis = .horizontal + sv.distribution = .fillEqually + sv.alignment = .center + sv.spacing = 11 + return sv + }() + let ageLabel = { let label = UILabel() label.text = "연령" @@ -88,15 +93,13 @@ final class AgeEditViewController: UIViewController { let titles = ["20-22세", "23-25세", "26-28세", "29세 이상"] return titles.enumerated().map { index, title in let button = UIButton(type: .system) + button.backgroundColor = UIColor.white button.setTitle(title, for: .normal) + button.titleLabel?.font = BudgetBuddiesFontFamily.Pretendard.regular.font(size: 16) button.titleLabel?.textAlignment = .left button.setTitleColor(.black, for: .normal) button.layer.cornerRadius = 15 - button.layer.borderColor = UIColor.lightGray.cgColor - button.layer.borderWidth = 1 - button.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.1).cgColor - button.layer.shadowOpacity = 1 - button.layer.shadowRadius = 10 //반경 + button.setShadow(opacity: 1, Radius: 8, offSet: CGSize(width: 0, height: 1)) button.addTarget(self, action: #selector(selectAge(_:)), for: .touchUpInside) button.tag = index return button @@ -106,6 +109,8 @@ final class AgeEditViewController: UIViewController { lazy var saveButton = { let button = UIButton(type: .custom) button.setTitle("저장하기", for: .normal) + button.setCharacterSpacing(-0.45) + button.titleLabel?.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 18) button.setTitleColor(.white, for: .normal) button.backgroundColor = BudgetBuddiesAsset.AppColor.coreYellow.color button.addTarget(self, action: #selector(saveButtonTapped), for: .touchUpInside) @@ -113,18 +118,25 @@ final class AgeEditViewController: UIViewController { return button }() + // MARK: - Life Cycle override func viewDidLoad() { super.viewDidLoad() setup() setConsts() loadPeerInfo() + setupNavigationBar() + } + + // MARK: - Set up NavigationBar + private func setupNavigationBar() { + self.navigationController?.navigationBar.isHidden = true } private func setup() { - view.backgroundColor = .white + view.backgroundColor = BudgetBuddiesAsset.AppColor.background.color - [titleLabel, genderLabel, femaleButton, maleButton, ageLabel, saveButton].forEach { + [titleLabel, genderLabel, buttonStackView, ageLabel, saveButton].forEach { view.addSubviews($0) } ageButtons.forEach { view.addSubview($0) } @@ -143,28 +155,27 @@ final class AgeEditViewController: UIViewController { } femaleButton.snp.makeConstraints { - $0.top.equalTo(genderLabel.snp.bottom).offset(10) - $0.leading.equalToSuperview().offset(16) - $0.width.equalTo(160) $0.height.equalTo(50) } maleButton.snp.makeConstraints { - $0.top.equalTo(genderLabel.snp.bottom).offset(10) - $0.trailing.equalToSuperview().offset(-16) - $0.width.equalTo(160) $0.height.equalTo(50) } + buttonStackView.snp.makeConstraints { make in + make.top.equalTo(genderLabel.snp.bottom).offset(10) + make.leading.trailing.equalToSuperview().inset(16) + } + ageLabel.snp.makeConstraints { - $0.top.equalTo(femaleButton.snp.bottom).offset(20) + $0.top.equalTo(buttonStackView.snp.bottom).offset(20) $0.leading.equalToSuperview().offset(16) } for (index, button) in ageButtons.enumerated() { button.snp.makeConstraints { if index == 0 { - $0.top.equalTo(ageLabel.snp.bottom).offset(20) + $0.top.equalTo(ageLabel.snp.bottom).offset(10) } else { $0.top.equalTo(ageButtons[index - 1].snp.bottom).offset(20) } @@ -174,9 +185,8 @@ final class AgeEditViewController: UIViewController { } saveButton.snp.makeConstraints { - $0.leading.equalToSuperview().offset(20) - $0.trailing.equalToSuperview().offset(-20) - $0.bottom.equalTo(view.safeAreaLayoutGuide).offset(-20) + $0.leading.trailing.equalToSuperview().inset(16) + $0.bottom.equalTo(view.safeAreaLayoutGuide).offset(-30) $0.height.equalTo(60) } } @@ -238,9 +248,9 @@ extension UIButton { self.layer.borderWidth = 2 self.backgroundColor = UIColor.white } else { - self.layer.borderColor = UIColor.lightGray.cgColor - self.layer.borderWidth = 1 - self.backgroundColor = UIColor.clear + self.layer.borderWidth = 0 + self.setShadow(opacity: 1, Radius: 8, offSet: CGSize(width: 0, height: 1)) + self.backgroundColor = UIColor.white } } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/ViewControllers/AnalysisReportViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/ViewControllers/AnalysisReportViewController.swift index d69ceecf..4f5c8246 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/ViewControllers/AnalysisReportViewController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/ViewControllers/AnalysisReportViewController.swift @@ -6,21 +6,23 @@ // import Charts +import Combine import DGCharts +import Moya import SnapKit import UIKit final class AnalysisReportViewController: UIViewController { - // func didUpdateAgeAndGender(ageRange: (Int, Int), gender: String) { - // // 전달받은 나이와 성별로 UI 업데이트 - // ageGenderLabel.text = "\(ageRange.0)~\(ageRange.1)세 \(gender == "male" ? "남성" : "여성")" - // - // // 새로운 나이와 성별로 데이터 로드 (예: 네트워크 호출) - // loadTop4() - // loadTop3() - // } // MARK: - Property + // 네비바 + var previousScrollOffset: CGFloat = 0.0 + var scrollThreshold: CGFloat = 10.0 // 네비게이션 바가 나타나거나 사라질 스크롤 오프셋 차이 + + @Published private var userName = String() + + private let userRouterProvider = MoyaProvider() + var services = Services() var getConsumeGoalResponse: GetConsumeGoalResponse? = nil var getTopGoalResponse: GetTopGoalResponse? = nil @@ -33,13 +35,29 @@ final class AnalysisReportViewController: UIViewController { let scrollView = UIScrollView() let contentView = UIView() + // Combine + private var canecellable = Set() + + private let userId = 1 + + // MARK: - Methods + + private func observeDataModel() { + self.$userName + .sink { [weak self] newUserName in + self?.titleLabel.text = "\(newUserName)님 또래는 \n어떻게 소비했을까요?" + } + .store(in: &canecellable) + } + // MARK: - UI Components let titleLabel = { let label = UILabel() - label.text = "혜인님 또래는 \n어떻게 소비했을까요?" - label.textColor = .black - label.font = .systemFont(ofSize: 22, weight: .bold) + label.text = "김혜인님 또래는 \n어떻게 소비했을까요?" + label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color + label.setCharacterSpacing(-0.6) + label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 24) label.numberOfLines = 0 return label }() @@ -47,20 +65,21 @@ final class AnalysisReportViewController: UIViewController { let ageGenderLabel = { let label = UILabel() label.text = "23~26세 여성" - label.textColor = .black - label.font = .systemFont(ofSize: 14, weight: .medium) + label.setCharacterSpacing(-0.35) + label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color + label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 14) return label }() lazy var rangeEditButton = { let button = UIButton(type: .custom) button.setTitle("범위변경", for: .normal) - button.setTitleColor(.orange, for: .normal) - button.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .medium) - button.backgroundColor = BudgetBuddiesAsset.AppColor.face.color + button.setTitleColor(BudgetBuddiesAsset.AppColor.logoLine2.color, for: .normal) + button.titleLabel?.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 14) + button.backgroundColor = BudgetBuddiesAsset.AppColor.lemon2.color button.layer.cornerRadius = 10 button.layer.borderWidth = 1 - button.layer.borderColor = UIColor.systemOrange.cgColor + button.layer.borderColor = BudgetBuddiesAsset.AppColor.lemon.color.cgColor button.addTarget(self, action: #selector(ageEditButtonTapped), for: .touchUpInside) return button }() @@ -68,13 +87,8 @@ final class AnalysisReportViewController: UIViewController { let ageView = { let view = UIView() view.backgroundColor = .white - view.layer.cornerRadius = 16 - view.layer.borderWidth = 1 - view.layer.borderColor = UIColor.white.cgColor - view.layer.shadowColor = UIColor.black.cgColor - view.layer.shadowOpacity = 0.3 - view.layer.shadowOffset = CGSize(width: 0, height: 2) - view.layer.shadowRadius = 4 + view.layer.cornerRadius = 15 + view.setShadow(opacity: 1, Radius: 10, offSet: CGSize(width: 0, height: 1)) return view }() @@ -89,12 +103,7 @@ final class AnalysisReportViewController: UIViewController { let view = GoalChartView() view.backgroundColor = .white view.layer.cornerRadius = 20 - view.layer.borderWidth = 1 - view.layer.borderColor = UIColor.white.cgColor - view.layer.shadowColor = UIColor.black.cgColor - view.layer.shadowOpacity = 0.3 - view.layer.shadowOffset = CGSize(width: 0, height: 2) - view.layer.shadowRadius = 4 + view.setShadow(opacity: 1, Radius: 10, offSet: CGSize(width: 0, height: 1)) return view }() @@ -110,12 +119,7 @@ final class AnalysisReportViewController: UIViewController { let view = ReportBarChartView() view.backgroundColor = .white view.layer.cornerRadius = 20 - view.layer.borderWidth = 1 - view.layer.borderColor = UIColor.white.cgColor - view.layer.shadowColor = UIColor.black.cgColor - view.layer.shadowOpacity = 0.3 - view.layer.shadowOffset = CGSize(width: 0, height: 2) - view.layer.shadowRadius = 4 + view.setShadow(opacity: 1, Radius: 10, offSet: CGSize(width: 0, height: 1)) return view }() @@ -125,6 +129,9 @@ final class AnalysisReportViewController: UIViewController { super.viewWillAppear(animated) navigationController?.navigationBar.isHidden = false + setNavi() + loadPeerInfo() + setupPieChart() } override func viewDidLoad() { @@ -136,6 +143,10 @@ final class AnalysisReportViewController: UIViewController { setupViews() setupConstraints() setupPieChart() + + self.scrollView.delegate = self + self.scrollView.showsVerticalScrollIndicator = false + self.scrollView.showsHorizontalScrollIndicator = false } // 탭바에 가려지는 요소 보이게 하기 override func viewDidLayoutSubviews() { @@ -146,7 +157,7 @@ final class AnalysisReportViewController: UIViewController { // MARK: - Methods private func setupViews() { - view.backgroundColor = .white + view.backgroundColor = BudgetBuddiesAsset.AppColor.background.color view.addSubview(scrollView) scrollView.addSubview(contentView) @@ -164,10 +175,13 @@ final class AnalysisReportViewController: UIViewController { } private func setNavi() { + // 뒤로가기 제스처 + self.navigationController?.interactivePopGestureRecognizer?.delegate = self + + self.navigationController?.setNavigationBarHidden(false, animated: true) navigationItem.title = "또래 비교 분석 레포트" - let backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: self, action: nil) // title 부분 수정 - backBarButtonItem.tintColor = .black - self.navigationItem.backBarButtonItem = backBarButtonItem + self.setupDefaultNavigationBar(backgroundColor: BudgetBuddiesAsset.AppColor.background.color) + self.addBackButton(selector: #selector(didTapBarButton)) } private func setupConstraints() { @@ -230,7 +244,7 @@ final class AnalysisReportViewController: UIViewController { $0.leading.equalTo(contentView).offset(16) $0.trailing.equalTo(contentView).offset(-16) $0.bottom.equalTo(contentView).offset(-16) - $0.height.equalTo(300) + $0.height.equalTo(260) } } @@ -249,10 +263,10 @@ final class AnalysisReportViewController: UIViewController { private func setupChart(_ topGoals: [GetTopGoalsResponse.GetTopGoalsResult]) { // 차트에 사용할 색상 배열 let colors: [UIColor] = [ - UIColor.systemBlue, - UIColor.systemYellow, - UIColor.systemOrange, - UIColor.systemCyan, + BudgetBuddiesAsset.AppColor.coreBlue.color, + BudgetBuddiesAsset.AppColor.sky3.color, + BudgetBuddiesAsset.AppColor.orange2.color, + BudgetBuddiesAsset.AppColor.coreYellow.color, ] // 최대 4개의 topGoals 데이터만 사용 @@ -305,6 +319,12 @@ final class AnalysisReportViewController: UIViewController { } } + // MARK: - Selector + @objc + private func didTapBarButton() { + self.navigationController?.popViewController(animated: true) + } + @objc func ageEditButtonTapped() { if let naviController = self.navigationController { let AnalysisReportVC = AgeEditViewController() @@ -330,6 +350,23 @@ final class AnalysisReportViewController: UIViewController { // MARK: - 네트워킹 extension AnalysisReportViewController { + private func fetchUserDataFromServer(userId: Int) { + userRouterProvider.request(.find(userId: self.userId)) { result in + switch result { + case .success(let response): + do { + let decodedData = try JSONDecoder().decode( + ApiResponseResponseUserDto.self, from: response.data) + self.userName = decodedData.result.name + } catch (let error) { + self.userName = "익명" + } + case .failure(let error): + self.userName = "익명" + } + } + } + func loadPeerInfo() { services.consumeGoalService.getPeerInfo( userId: 1, peerAgeStart: 23, peerAgeEnd: 25, peerGender: "male" @@ -390,7 +427,7 @@ extension AnalysisReportViewController { // Top 3 데이터 로드하고 차트에 반영하는 메소드 func loadTop3(peerAgeStart: Int, peerAgeEnd: Int, peerGender: String) { services.consumeGoalService.getTopConsumptions( - userId: 1, peerAgeStart: peerAgeStart, peerAgeEnd: peerAgeEnd, peerGender: peerGender + userId: userId, peerAgeStart: peerAgeStart, peerAgeEnd: peerAgeEnd, peerGender: peerGender ) { result in switch result { case .success(let response): @@ -408,3 +445,30 @@ extension AnalysisReportViewController { } } } + +extension AnalysisReportViewController: UIScrollViewDelegate { + func scrollViewDidScroll(_ scrollView: UIScrollView) { + let currentOffset = scrollView.contentOffset.y + let offsetDifference = currentOffset - previousScrollOffset + + if currentOffset <= 0 { // 스크롤을 완전히 위로 올렸을 때 네비게이션 바 나타냄 + navigationController?.setNavigationBarHidden(false, animated: true) + + } else if offsetDifference > scrollThreshold { // 스크롤이 아래로 일정 이상 이동한 경우 네비게이션 바 숨김 + navigationController?.setNavigationBarHidden(true, animated: true) + + } else if offsetDifference < -scrollThreshold { // 스크롤이 위로 일정 이상 이동한 경우 네비게이션 바 나타냄 + navigationController?.setNavigationBarHidden(false, animated: true) + + } + + previousScrollOffset = currentOffset + } +} + +// MARK: - 뒤로 가기 슬라이드 제스처 추가 +extension AnalysisReportViewController: UIGestureRecognizerDelegate { + func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } +} diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/ViewControllers/ConsumeReportViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/ViewControllers/ConsumeReportViewController.swift index 35da25b2..245f23d0 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/ViewControllers/ConsumeReportViewController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/ViewControllers/ConsumeReportViewController.swift @@ -21,6 +21,7 @@ final class ConsumeReportViewController: UIViewController { let mainLabel = { let label = UILabel() label.text = "또래 친구들은\n패션에 가장 많이\n소비했어요" + label.setCharacterSpacing(-0.55) label.textColor = .black label.numberOfLines = 0 label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 22) @@ -54,7 +55,7 @@ final class ConsumeReportViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - + loadConsume() setNavigationSetting() } @@ -69,19 +70,33 @@ final class ConsumeReportViewController: UIViewController { setConsts() } + // 탭바에 가려지는 요소 보이게 하기 + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + self.tableView.contentInset.bottom = 15 + } + // MARK: - Methods private func setNavigationSetting() { - navigationController?.navigationBar.isHidden = false + // 뒤로가기 제스처 + self.navigationController?.interactivePopGestureRecognizer?.delegate = self + + navigationController?.setNavigationBarHidden(false, animated: true) navigationItem.title = "소비 레포트" + + self.setupDefaultNavigationBar(backgroundColor: BudgetBuddiesAsset.AppColor.background.color) + self.addBackButton(selector: #selector(didTapBarButton)) } private func setTableView() { + self.tableView.showsVerticalScrollIndicator = false + self.tableView.showsHorizontalScrollIndicator = false tableView.delegate = self tableView.dataSource = self tableView.register( ReportTableViewCell.self, forCellReuseIdentifier: ReportTableViewCell.identifier) tableView.separatorStyle = .none - tableView.backgroundColor = .white + tableView.backgroundColor = BudgetBuddiesAsset.AppColor.background.color } private func setup() { @@ -150,6 +165,12 @@ final class ConsumeReportViewController: UIViewController { mainLabel.attributedText = attributedText } + + // MARK: - Selectors + @objc + private func didTapBarButton() { + self.navigationController?.popViewController(animated: true) + } } // MARK: - UITableViewDelegate, UITableViewDataSource @@ -198,3 +219,10 @@ extension ConsumeReportViewController { } } } + +// MARK: - 뒤로 가기 슬라이드 제스처 추가 +extension ConsumeReportViewController: UIGestureRecognizerDelegate { + func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } +} diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/ViewControllers/GoalReportViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/ViewControllers/GoalReportViewController.swift index 7c5d8899..fa6b305a 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/ViewControllers/GoalReportViewController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/ViewControllers/GoalReportViewController.swift @@ -21,6 +21,7 @@ final class GoalReportViewController: UIViewController { let mainLabel = { let label = UILabel() label.text = "또래 친구들은\n문화생활에 가장\n큰 목표예산을 세웠어요" + label.setCharacterSpacing(-0.55) label.textColor = .black label.numberOfLines = 0 label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 22) @@ -68,20 +69,34 @@ final class GoalReportViewController: UIViewController { setConsts() } + // 탭바에 가려지는 요소 보이게 하기 + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + self.tableView.contentInset.bottom = 15 + } + // MARK: - Methods private func setNavigationSetting() { - navigationController?.navigationBar.isHidden = false + // 뒤로가기 제스처 + self.navigationController?.interactivePopGestureRecognizer?.delegate = self + + navigationController?.setNavigationBarHidden(false, animated: true) navigationItem.title = "소비목표 레포트" + + self.setupDefaultNavigationBar(backgroundColor: BudgetBuddiesAsset.AppColor.background.color) + self.addBackButton(selector: #selector(didTapBarButton)) } private func setTableView() { + self.tableView.showsVerticalScrollIndicator = false + self.tableView.showsHorizontalScrollIndicator = false tableView.delegate = self tableView.dataSource = self tableView.register( ReportTableViewCell.self, forCellReuseIdentifier: ReportTableViewCell.identifier) tableView.separatorStyle = .none - tableView.backgroundColor = .white + tableView.backgroundColor = BudgetBuddiesAsset.AppColor.background.color } private func setup() { @@ -149,6 +164,12 @@ final class GoalReportViewController: UIViewController { mainLabel.attributedText = attributedText } + + // MARK: - Selectors + @objc + private func didTapBarButton() { + self.navigationController?.popViewController(animated: true) + } } // MARK: - UITableViewDelegate, UITableViewDataSource @@ -212,9 +233,25 @@ extension BudgetBuddiesAsset.AppImage.CategoryIcon { return BudgetBuddiesAsset.AppImage.CategoryIcon.cultureIcon2.image case "교통": return BudgetBuddiesAsset.AppImage.CategoryIcon.trafficIcon2.image - // 필요한 경우 더 많은 카테고리 추가 + case "기타": + return BudgetBuddiesAsset.AppImage.CategoryIcon.etcIcon2.image + case "유흥": + return BudgetBuddiesAsset.AppImage.CategoryIcon.playIcon2.image + case "경조사": + return BudgetBuddiesAsset.AppImage.CategoryIcon.eventIcon2.image + case "정기결제": + return BudgetBuddiesAsset.AppImage.CategoryIcon.regularPaymentIcon2.image + case "카페": + return BudgetBuddiesAsset.AppImage.CategoryIcon.cafeIcon2.image default: - return UIImage() // 기본 이미지 + return BudgetBuddiesAsset.AppImage.CategoryIcon.personal2.image } } } + +// MARK: - 뒤로 가기 슬라이드 제스처 추가 +extension GoalReportViewController: UIGestureRecognizerDelegate { + func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } +} diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/Views/GoalChartView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/Views/GoalChartView.swift index 1847b95f..4aeb7ca2 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/Views/GoalChartView.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/Views/GoalChartView.swift @@ -13,14 +13,17 @@ final class GoalChartView: UIView { let planLabel: UILabel = { let label = UILabel() label.text = "패션에 가장 큰 \n계획을 세웠어요" - label.font = .systemFont(ofSize: 22, weight: .semibold) + label.setCharacterSpacing(-0.55) + label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 22) label.numberOfLines = 0 return label }() let dateLabel: UILabel = { let label = UILabel() - label.textColor = .gray + label.text = " " + label.setCharacterSpacing(-0.3) + label.textColor = BudgetBuddiesAsset.AppColor.subGray.color label.font = .systemFont(ofSize: 12, weight: .regular) // 현재 날짜 및 시간 가져오기 @@ -39,11 +42,19 @@ final class GoalChartView: UIView { return label }() + let pieChartBackImageView: UIImageView = { + let iv = UIImageView() + iv.image = UIImage(named: "pieChartBackImage") + iv.contentMode = .scaleAspectFill + return iv + }() + let pieChartView = PieChartView() let firstLabel = { let label = UILabel() label.text = "패션" + label.setCharacterSpacing(-0.45) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color label.textAlignment = .center label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 18) @@ -53,6 +64,7 @@ final class GoalChartView: UIView { let firstPrice = { let label = UILabel() label.text = "120,000원" + label.setCharacterSpacing(-0.35) label.textColor = BudgetBuddiesAsset.AppColor.subGray.color label.textAlignment = .center label.font = BudgetBuddiesFontFamily.Pretendard.regular.font(size: 14) @@ -63,6 +75,8 @@ final class GoalChartView: UIView { let sv = UIStackView() sv.axis = .vertical sv.spacing = 36 + sv.alignment = .leading + sv.distribution = .fill return sv }() @@ -71,9 +85,11 @@ final class GoalChartView: UIView { sv.axis = .vertical sv.spacing = 36 sv.distribution = .fillEqually + return sv }() + // MARK: - Init override init(frame: CGRect) { super.init(frame: frame) setup() @@ -85,10 +101,14 @@ final class GoalChartView: UIView { } private func setup() { - [planLabel, dateLabel, pieChartView, firstLabel, firstPrice, legendStackView, stackView].forEach - { + [ + planLabel, dateLabel, pieChartBackImageView, firstLabel, firstPrice, legendStackView, + stackView, + ].forEach { self.addSubview($0) } + + pieChartBackImageView.addSubviews(pieChartView) } private func setConst() { @@ -102,11 +122,16 @@ final class GoalChartView: UIView { $0.leading.equalToSuperview().offset(20) } - pieChartView.snp.makeConstraints { - $0.top.equalTo(dateLabel.snp.bottom).offset(30) - $0.leading.equalToSuperview().offset(20) - $0.width.equalTo(200) - $0.height.equalTo(pieChartView.snp.width) + pieChartBackImageView.snp.makeConstraints { make in + make.top.equalTo(dateLabel.snp.bottom).offset(30) + make.leading.equalToSuperview().offset(20) + make.width.equalTo(215) + make.height.equalTo(pieChartView.snp.width) + } + + pieChartView.snp.makeConstraints { make in + make.center.equalToSuperview() + make.height.width.equalTo(200) } firstLabel.snp.makeConstraints { @@ -121,12 +146,12 @@ final class GoalChartView: UIView { legendStackView.snp.makeConstraints { $0.centerY.equalTo(pieChartView) - $0.leading.equalTo(pieChartView.snp.trailing).offset(30) - $0.trailing.equalToSuperview() + $0.trailing.equalToSuperview().inset(10) + $0.width.equalTo(70) } stackView.snp.makeConstraints { - $0.top.equalTo(pieChartView.snp.bottom).offset(40) + $0.top.equalTo(pieChartView.snp.bottom).offset(50) $0.leading.equalToSuperview().offset(20) $0.trailing.equalToSuperview().offset(-20) $0.bottom.equalToSuperview().offset(-30) @@ -136,7 +161,10 @@ final class GoalChartView: UIView { func setupChart(entries: [PieChartDataEntry]) { let dataSet = PieChartDataSet(entries: entries, label: "소비습관") dataSet.colors = [ - UIColor.systemBlue, UIColor.systemYellow, UIColor.systemOrange, UIColor.systemCyan, + BudgetBuddiesAsset.AppColor.coreBlue.color, + BudgetBuddiesAsset.AppColor.sky3.color, + BudgetBuddiesAsset.AppColor.orange2.color, + BudgetBuddiesAsset.AppColor.coreYellow.color, ] dataSet.drawValuesEnabled = false dataSet.sliceSpace = 2 @@ -185,11 +213,11 @@ final class GoalChartView: UIView { label.text = rank label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 14) label.textColor = BudgetBuddiesAsset.AppColor.logoLine2.color - label.backgroundColor = BudgetBuddiesAsset.AppColor.face.color + label.backgroundColor = BudgetBuddiesAsset.AppColor.lemon2.color label.textAlignment = .center - label.layer.cornerRadius = 4 + label.layer.cornerRadius = 8 label.layer.borderWidth = 1 - label.layer.borderColor = BudgetBuddiesAsset.AppColor.calendarYellow.color.cgColor + label.layer.borderColor = BudgetBuddiesAsset.AppColor.lemon.color.cgColor label.clipsToBounds = true return label }() @@ -205,6 +233,7 @@ final class GoalChartView: UIView { let valueLabel: UILabel = { let label = UILabel() label.text = "\(value.formatted())원" + label.setCharacterSpacing(-0.35) label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 14) label.textColor = BudgetBuddiesAsset.AppColor.subGray.color return label @@ -218,10 +247,11 @@ final class GoalChartView: UIView { $0.leading.equalToSuperview() $0.centerY.equalToSuperview() $0.width.equalTo(32) + $0.height.equalTo(20) } categoryLabel.snp.makeConstraints { - $0.leading.equalTo(rankLabel.snp.trailing).offset(8) + $0.leading.equalTo(rankLabel.snp.trailing).offset(17) $0.centerY.equalToSuperview() } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/Views/ReportBarChartView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/Views/ReportBarChartView.swift index 1aa36e13..143c1153 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/Views/ReportBarChartView.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/Views/ReportBarChartView.swift @@ -14,15 +14,17 @@ final class ReportBarChartView: UIView { let titleLabel = { let label = UILabel() label.text = "패션에 가장 많이 \n소비했어요" - label.font = .systemFont(ofSize: 22, weight: .semibold) + label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 22) + label.setCharacterSpacing(-0.55) label.numberOfLines = 0 return label }() let dateLabel: UILabel = { let label = UILabel() - label.textColor = .gray - label.font = .systemFont(ofSize: 12, weight: .regular) + label.text = " " + label.textColor = BudgetBuddiesAsset.AppColor.subGray.color + label.font = BudgetBuddiesFontFamily.Pretendard.regular.font(size: 12) // 현재 날짜 및 시간 가져오기 let currentDate = Date() @@ -78,10 +80,10 @@ final class ReportBarChartView: UIView { } stackView.snp.makeConstraints { - $0.top.equalTo(dateLabel.snp.bottom).offset(10) + $0.top.equalTo(dateLabel.snp.bottom).offset(25) $0.leading.equalToSuperview().offset(20) $0.trailing.equalToSuperview().offset(-20) - $0.bottom.equalToSuperview().offset(-10) + $0.height.equalTo(125) } } @@ -109,11 +111,12 @@ final class ReportBarChartView: UIView { label.text = rank label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 14) label.textColor = BudgetBuddiesAsset.AppColor.logoLine2.color - label.backgroundColor = BudgetBuddiesAsset.AppColor.face.color + label.backgroundColor = BudgetBuddiesAsset.AppColor.lemon2.color label.textAlignment = .center - label.layer.cornerRadius = 4 + label.layer.cornerRadius = 8 label.layer.borderWidth = 1 - label.layer.borderColor = BudgetBuddiesAsset.AppColor.calendarYellow.color.cgColor + label.layer.borderColor = BudgetBuddiesAsset.AppColor.lemon.color.cgColor + label.layer.masksToBounds = true return label }() @@ -137,6 +140,7 @@ final class ReportBarChartView: UIView { let view = UIView() view.backgroundColor = color view.layer.cornerRadius = 4 + view.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner] return view }() @@ -148,6 +152,7 @@ final class ReportBarChartView: UIView { $0.leading.equalToSuperview() $0.centerY.equalToSuperview() $0.width.equalTo(32) + $0.height.equalTo(20) } @@ -162,10 +167,11 @@ final class ReportBarChartView: UIView { } barView.snp.makeConstraints { - $0.leading.equalTo(categoryLabel.snp.trailing).offset(8) + // $0.leading.equalTo(categoryLabel.snp.trailing).offset(8) + $0.leading.equalTo(rankLabel.snp.trailing).offset(65) $0.centerY.equalToSuperview() $0.height.equalTo(24) - $0.width.equalTo(value * 30) + $0.width.equalTo(value * 10) } return containerView diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/Views/TopStackView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/Views/TopStackView.swift index 85c8ecb0..f894f6d4 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/Views/TopStackView.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/AnalysisReport/Views/TopStackView.swift @@ -13,6 +13,8 @@ final class TopStackView: UIView { let titleLabel = { let label = UILabel() label.text = "제목" + label.setCharacterSpacing(-0.45) + label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 18) label.font = .systemFont(ofSize: 18, weight: .semibold) label.textColor = .black return label diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/ViewControllers/GoalEditViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/ViewControllers/GoalEditViewController.swift index f1d8c383..f0011df0 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/ViewControllers/GoalEditViewController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/ViewControllers/GoalEditViewController.swift @@ -23,6 +23,7 @@ final class GoalEditViewController: UIViewController { GoalCategory(name: "패션", placeholder: "ex) 200,000"), ] + // MARK: - UI Components let textFieldStackView = { let sv = UIStackView() sv.axis = .vertical @@ -33,6 +34,7 @@ final class GoalEditViewController: UIViewController { let totalGoalTitleLabel = { let label = UILabel() label.text = "총 목표 금액" + label.setCharacterSpacing(-0.4) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 16) return label @@ -41,6 +43,7 @@ final class GoalEditViewController: UIViewController { let totalGoalLabel = { let label = UILabel() label.text = "500,000원" + label.setCharacterSpacing(-0.55) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 22) return label @@ -49,12 +52,15 @@ final class GoalEditViewController: UIViewController { lazy var finishButton = { let button = UIButton(type: .custom) button.setTitle("작성완료", for: .normal) + button.setCharacterSpacing(-0.45) + button.titleLabel?.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 18) button.setTitleColor(.white, for: .normal) button.backgroundColor = BudgetBuddiesAsset.AppColor.coreYellow.color button.layer.cornerRadius = 15 return button }() + // MARK: - Life Cycle override func viewWillAppear(_ animated: Bool) { setNavi() @@ -68,16 +74,17 @@ final class GoalEditViewController: UIViewController { setupTextFields() } + // MARK: - Set Navi private func setNavi() { + // 뒤로가기 제스처 + self.navigationController?.interactivePopGestureRecognizer?.delegate = self + + navigationController?.navigationBar.isHidden = false + navigationItem.title = "6월 소비목표" - let appearance = UINavigationBarAppearance() - appearance.configureWithOpaqueBackground() - appearance.backgroundColor = .white - appearance.shadowColor = nil - - navigationController?.navigationBar.standardAppearance = appearance - navigationController?.navigationBar.compactAppearance = appearance - navigationController?.navigationBar.scrollEdgeAppearance = appearance + + self.setupDefaultNavigationBar(backgroundColor: BudgetBuddiesAsset.AppColor.white.color) + self.addBackButton(selector: #selector(didTapBarButtonItem)) } private func setup() { @@ -89,9 +96,10 @@ final class GoalEditViewController: UIViewController { } } + // MARK: - Set Const private func setConsts() { textFieldStackView.snp.makeConstraints { - $0.top.equalTo(view.safeAreaLayoutGuide).offset(20) + $0.top.equalTo(view.safeAreaLayoutGuide).offset(10) $0.leading.trailing.equalTo(view).inset(16) } @@ -106,8 +114,7 @@ final class GoalEditViewController: UIViewController { } finishButton.snp.makeConstraints { - $0.leading.equalToSuperview().offset(20) - $0.trailing.equalToSuperview().offset(-20) + $0.leading.trailing.equalToSuperview().inset(16) $0.bottom.equalTo(view.safeAreaLayoutGuide).offset(-30) // 기존 -20에서 -10 추가 (탭바 가려짐 해결) $0.height.equalTo(60) } @@ -118,15 +125,21 @@ final class GoalEditViewController: UIViewController { for category in goalCategories { let label = UILabel() label.text = category.name + label.setCharacterSpacing(-0.35) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 14) let textField = UITextField() textField.layer.cornerRadius = 15 textField.placeholder = category.placeholder + textField.setCharacterSpacing(-0.35) textField.backgroundColor = BudgetBuddiesAsset.AppColor.textBox.color textField.leftView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 16.0, height: 0.0)) textField.leftViewMode = .always + textField.setComfortableTextField() + textField.keyboardType = .numberPad + textField.textAlignment = .left + textField.font = BudgetBuddiesFontFamily.Pretendard.regular.font(size: 14) // 폰트 설정 let stack = UIStackView(arrangedSubviews: [label, textField]) stack.axis = .vertical @@ -143,6 +156,12 @@ final class GoalEditViewController: UIViewController { } } } + + // MARK: - Selectors + @objc + private func didTapBarButtonItem() { + self.navigationController?.popViewController(animated: true) + } } extension GoalEditViewController: UITextFieldDelegate { @@ -151,3 +170,10 @@ extension GoalEditViewController: UITextFieldDelegate { self.view.endEditing(true) } } + +// MARK: - 뒤로 가기 슬라이드 제스처 추가 +extension GoalEditViewController: UIGestureRecognizerDelegate { + func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } +} diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/ViewControllers/GoalTotalViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/ViewControllers/GoalTotalViewController.swift index 8f5fb2ad..0db873b3 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/ViewControllers/GoalTotalViewController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/ViewControllers/GoalTotalViewController.swift @@ -10,9 +10,16 @@ import UIKit final class GoalTotalViewController: UIViewController { + // MARK: - Property + var previousScrollOffset: CGFloat = 0.0 + var scrollThreshold: CGFloat = 10.0 // 네비게이션 바가 나타나거나 사라질 스크롤 오프셋 차이 + + var services = Services() + var getConsumeGoalResponse: GetConsumeGoalResponse? = nil + let tableView = UITableView() - let spendGoals: [SpendGoalModel] = [ + var spendGoals: [SpendGoalModel] = [ SpendGoalModel( categoryImage: BudgetBuddiesAsset.AppImage.CategoryIcon.foodIcon2.image, title: "식비", amount: "123,180", progress: 0.66, @@ -51,39 +58,37 @@ final class GoalTotalViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { setNavi() + loadTotalConsumeGoal() } override func viewDidLoad() { super.viewDidLoad() + + loadTotalConsumeGoal() setup() setTableView() setConsts() } + // MARK: - SetNavi private func setNavi() { - navigationItem.title = "소비 목표" - let appearance = UINavigationBarAppearance() - appearance.configureWithOpaqueBackground() - appearance.backgroundColor = .white - appearance.shadowColor = nil - - navigationController?.navigationBar.standardAppearance = appearance - navigationController?.navigationBar.compactAppearance = appearance - navigationController?.navigationBar.scrollEdgeAppearance = appearance + // 뒤로가기 제스처 + self.navigationController?.interactivePopGestureRecognizer?.delegate = self - let backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: self, action: nil) // title 부분 수정 - backBarButtonItem.tintColor = .black - self.navigationItem.backBarButtonItem = backBarButtonItem + navigationItem.title = "소비 목표" + self.setupDefaultNavigationBar(backgroundColor: BudgetBuddiesAsset.AppColor.background.color) + self.addBackButton(selector: #selector(didTapBarButton)) } private func setup() { - view.backgroundColor = .white + view.backgroundColor = BudgetBuddiesAsset.AppColor.background.color [tableView, editButton].forEach { view.addSubview($0) } } private func setTableView() { + tableView.backgroundColor = BudgetBuddiesAsset.AppColor.background.color tableView.delegate = self tableView.dataSource = self tableView.register(SpendGoalCell.self, forCellReuseIdentifier: SpendGoalCell.identifier) @@ -104,6 +109,46 @@ final class GoalTotalViewController: UIViewController { } } + private func setCategoryIconImage(categoryId: Int) -> UIImage { + switch categoryId { + case 1: + return BudgetBuddiesAsset.AppImage.CategoryIcon.foodIcon2.image + case 2: + return BudgetBuddiesAsset.AppImage.CategoryIcon.shoppingIcon2.image + case 3: + return BudgetBuddiesAsset.AppImage.CategoryIcon.fashionIcon2.image + case 4: + return BudgetBuddiesAsset.AppImage.CategoryIcon.cultureIcon2.image + case 5: + return BudgetBuddiesAsset.AppImage.CategoryIcon.trafficIcon2.image + case 6: + return BudgetBuddiesAsset.AppImage.CategoryIcon.cafeIcon2.image + case 7: + return BudgetBuddiesAsset.AppImage.CategoryIcon.playIcon2.image + case 8: + return BudgetBuddiesAsset.AppImage.CategoryIcon.eventIcon2.image + case 9: + return BudgetBuddiesAsset.AppImage.CategoryIcon.regularPaymentIcon2.image + case 10: + return BudgetBuddiesAsset.AppImage.CategoryIcon.etcIcon2.image + default: + return BudgetBuddiesAsset.AppImage.CategoryIcon.personal2.image + } + } + + private func updateUI(with spendGoals: [SpendGoalModel]) { + self.spendGoals = spendGoals + DispatchQueue.main.async { + self.tableView.reloadData() + } + } + + // MARK: - Selector + @objc + private func didTapBarButton() { + self.navigationController?.popViewController(animated: true) + } + @objc private func editButtonTapped() { if let naviController = self.navigationController { let goalEditVC = GoalEditViewController() @@ -134,8 +179,84 @@ extension GoalTotalViewController: UITableViewDelegate, UITableViewDataSource { return cell } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + let currentOffset = scrollView.contentOffset.y + let offsetDifference = currentOffset - previousScrollOffset + + if currentOffset <= 0 { // 스크롤을 완전히 위로 올렸을 때 네비게이션 바 나타냄 + navigationController?.setNavigationBarHidden(false, animated: true) + + } else if offsetDifference > scrollThreshold { // 스크롤이 아래로 일정 이상 이동한 경우 네비게이션 바 숨김 + navigationController?.setNavigationBarHidden(true, animated: true) + + } else if offsetDifference < -scrollThreshold { // 스크롤이 위로 일정 이상 이동한 경우 네비게이션 바 나타냄 + navigationController?.setNavigationBarHidden(false, animated: true) + + } + + previousScrollOffset = currentOffset + } } -extension MonthReportViewController { +extension GoalTotalViewController { + + func loadTotalConsumeGoal() { + services.consumeGoalService.getConsumeGoal(date: "2024-08-18", userId: 1) { result in + switch result { + case .success(let response): + self.getConsumeGoalResponse = response + dump(response) + + // 받아온 값을 업데이트 + guard let totalGoalAmount = response.result?.totalGoalAmount, + let totalSpentAmount = response.result?.totalConsumptionAmount, + let totalRemainingBalance = response.result?.totalRemainingBalance, + let consumptionGoalList = response.result?.consumptionGoalList + else { + print("Some values are nil") + return + } + + let numberFormatter: NumberFormatter = { + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + formatter.groupingSeparator = "," + formatter.maximumFractionDigits = 0 // 소수점을 표시하지 않도록 설정 + return formatter + }() + let spendGoals = consumptionGoalList.map { item -> SpendGoalModel in + let goalAmount = + numberFormatter.string(from: NSNumber(value: item.goalAmount ?? 0)) ?? "0" + let consumeAmount = + numberFormatter.string(from: NSNumber(value: item.consumeAmount ?? 0)) ?? "0" + let remainingBalance = + numberFormatter.string(from: NSNumber(value: item.remainingBalance ?? 0)) ?? "0" + + return SpendGoalModel( + categoryImage: self.setCategoryIconImage(categoryId: item.categoryId!), + title: item.categoryName ?? "", + amount: "\(goalAmount)", + progress: item.goalAmount! > 0 + ? Float(item.consumeAmount!) / Float(item.goalAmount!) : 0.0, + consumption: "\(consumeAmount)", + remaining: "\(remainingBalance)" + ) + } + + self.updateUI(with: spendGoals) + + case .failure(let error): + print("Failed to load Top goal: \(error)") + } + } + } +} + +// MARK: - 뒤로 가기 슬라이드 제스처 추가 +extension GoalTotalViewController: UIGestureRecognizerDelegate { + func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/ViewControllers/MonthReportViewController.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/ViewControllers/MonthReportViewController.swift index 49765374..c5dddb80 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/ViewControllers/MonthReportViewController.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/ViewControllers/MonthReportViewController.swift @@ -12,6 +12,9 @@ import UIKit final class MonthReportViewController: UIViewController { // MARK: - Property + var previousScrollOffset: CGFloat = 0.0 + var scrollThreshold: CGFloat = 10.0 // 네비게이션 바가 나타나거나 사라질 스크롤 오프셋 차이 + var services = Services() var getConsumeGoalResponse: GetConsumeGoalResponse? = nil var getTopGoalResponse: GetTopGoalResponse? = nil @@ -60,10 +63,7 @@ final class MonthReportViewController: UIViewController { let view = UITableView() view.backgroundColor = .white view.layer.cornerRadius = 15 - view.layer.shadowColor = UIColor.black.withAlphaComponent(0.1).cgColor - view.layer.shadowOpacity = 1 - view.layer.shadowRadius = 10 - view.layer.masksToBounds = false + view.setShadow(opacity: 1, Radius: 5, offSet: CGSize(width: 0, height: 1)) return view }() @@ -74,7 +74,7 @@ final class MonthReportViewController: UIViewController { // 헤더 뷰에 라벨 추가 (예시) let label = UILabel() - label.text = "27일 목요일" + label.text = "24일 토요일" label.textColor = BudgetBuddiesAsset.AppColor.subGray.color label.font = BudgetBuddiesFontFamily.Pretendard.regular.font(size: 14) view.addSubview(label) @@ -109,7 +109,7 @@ final class MonthReportViewController: UIViewController { button.setTitle("더보기 >", for: .normal) button.setTitleColor(BudgetBuddiesAsset.AppColor.subGray.color, for: .normal) button.titleLabel?.font = BudgetBuddiesFontFamily.Pretendard.regular.font(size: 14) - button.addTarget(self, action: #selector(accountBookButtonTapped), for: .touchUpInside) + button.addTarget(self, action: #selector(accountBookTotalButtonTapped), for: .touchUpInside) view.addSubview(button) // 라벨의 제약 조건 설정 @@ -122,6 +122,8 @@ final class MonthReportViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { setNavi() + loadConsumeGoal() + } let topView = { @@ -136,14 +138,12 @@ final class MonthReportViewController: UIViewController { let faceChartView = { let view = FaceChartView() - view.backgroundColor = .white - view.layer.cornerRadius = 20 - view.layer.borderWidth = 1 - view.layer.borderColor = UIColor.white.cgColor - view.layer.shadowColor = UIColor.black.cgColor - view.layer.shadowOpacity = 0.3 - view.layer.shadowOffset = CGSize(width: 0, height: 2) - view.layer.shadowRadius = 4 + // view.backgroundColor = .white + // view.layer.cornerRadius = 20 + // view.layer.borderWidth = 1 + // view.layer.borderColor = UIColor.white.cgColor + // view.setShadow(opacity: 1, Radius: 5, offSet: CGSize(width: 0, height: -1)) + // return view }() @@ -164,7 +164,7 @@ final class MonthReportViewController: UIViewController { let remainingAmountLabel = UILabel() let spendingGoalsLabel = UILabel() - let spendGoals: [SpendGoalModel] = [ + var spendGoals: [SpendGoalModel] = [ SpendGoalModel( categoryImage: BudgetBuddiesAsset.AppImage.CategoryIcon.foodIcon2.image, title: "식비", amount: "123,180", progress: 0.66, @@ -186,7 +186,7 @@ final class MonthReportViewController: UIViewController { lazy var accountBookView = { let view = TopStackView() view.titleLabel.text = "가계부" - view.totalButton.setTitle("가계부 입력하기", for: .normal) + view.totalButton.setTitle("입력하기", for: .normal) view.totalButton.addTarget( self, action: #selector(accountBookButtonTapped), for: .touchUpInside) return view @@ -201,6 +201,7 @@ final class MonthReportViewController: UIViewController { description: "과자"), ] + // MARK: - Life Cycle override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = BudgetBuddiesAsset.AppColor.background.color @@ -220,20 +221,14 @@ final class MonthReportViewController: UIViewController { self.scrollView.contentInset.bottom = 15 } + // MARK: - Set Navi private func setNavi() { + // 뒤로가기 제스처 + self.navigationController?.interactivePopGestureRecognizer?.delegate = self + navigationItem.title = "이번달 리포트" - let appearance = UINavigationBarAppearance() - appearance.configureWithOpaqueBackground() - // appearance.backgroundColor = BudgetBuddiesAsset.AppColor.coreYellow.color - appearance.shadowColor = nil - - navigationController?.navigationBar.standardAppearance = appearance - navigationController?.navigationBar.compactAppearance = appearance - navigationController?.navigationBar.scrollEdgeAppearance = appearance - - let backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: self, action: nil) // title 부분 수정 - backBarButtonItem.tintColor = .black - self.navigationItem.backBarButtonItem = backBarButtonItem + self.setupDefaultNavigationBar(backgroundColor: BudgetBuddiesAsset.AppColor.coreYellow.color) + self.addBackButton(selector: #selector(didTapBarButton)) } private func setup() { @@ -249,6 +244,11 @@ final class MonthReportViewController: UIViewController { } private func setTableView() { + self.scrollView.showsVerticalScrollIndicator = false + self.scrollView.showsHorizontalScrollIndicator = false + + spendGoalTableView.backgroundColor = BudgetBuddiesAsset.AppColor.background.color + accountBookTableView.backgroundColor = BudgetBuddiesAsset.AppColor.background.color spendGoalTableView.delegate = self spendGoalTableView.dataSource = self spendGoalTableView.register( @@ -274,6 +274,7 @@ final class MonthReportViewController: UIViewController { tableFooterView.frame = CGRect(x: 0, y: 0, width: accountBookTableView.frame.width, height: 50) } + // MARK: - Set Const private func setConst() { scrollView.snp.makeConstraints { $0.edges.equalToSuperview() @@ -285,17 +286,16 @@ final class MonthReportViewController: UIViewController { } topView.snp.makeConstraints { - $0.top.equalTo(contentView.snp.top) $0.leading.trailing.equalTo(contentView) - $0.height.equalTo(280) + $0.height.equalTo(2000) $0.centerX.equalTo(contentView) + $0.bottom.equalTo(faceChartView.snp.bottom).offset(-170) } faceChartView.snp.makeConstraints { $0.top.equalTo(contentView).offset(20) - $0.leading.equalTo(contentView).offset(16) - $0.trailing.equalTo(contentView).offset(-16) - $0.height.equalTo(350) + $0.leading.trailing.equalToSuperview().inset(16) + $0.height.equalTo(faceChartView.backView.snp.height) } spendGoalView.snp.makeConstraints { @@ -307,8 +307,7 @@ final class MonthReportViewController: UIViewController { spendGoalTableView.snp.makeConstraints { $0.top.equalTo(spendGoalView.snp.bottom).offset(10) - $0.leading.equalTo(contentView).offset(8) - $0.trailing.equalTo(contentView).offset(-8) + $0.leading.trailing.equalTo(contentView) // $0.bottom.equalTo(contentView).offset(-100) $0.height.equalTo(600) // Adjust height as needed } @@ -343,13 +342,6 @@ final class MonthReportViewController: UIViewController { return formatter.string(from: NSNumber(value: amount)) ?? "\(amount)" } - // 소비 목표 함수 - // private func updateLabels() { - // let formattedSpent = formatCurrency(totalSpentAmount) - // let formattedRemaining = formatCurrency(totalGoalAmount - totalSpentAmount) - // faceChartView.updateLabels(spend: "\(formattedSpent)원", remain: "\(formattedRemaining)원") - // } - // func updateGoalAndConsumption(goal: Int, spent: Int, remaining: Int) { // 금액 포맷팅 let formattedGoal = formatCurrency(goal) @@ -392,6 +384,46 @@ final class MonthReportViewController: UIViewController { setChart() } + private func updateUI(with spendGoals: [SpendGoalModel]) { + self.spendGoals = spendGoals + DispatchQueue.main.async { + self.spendGoalTableView.reloadData() + } + } + + private func setCategoryIconImage(categoryId: Int) -> UIImage { + switch categoryId { + case 1: + return BudgetBuddiesAsset.AppImage.CategoryIcon.foodIcon2.image + case 2: + return BudgetBuddiesAsset.AppImage.CategoryIcon.shoppingIcon2.image + case 3: + return BudgetBuddiesAsset.AppImage.CategoryIcon.fashionIcon2.image + case 4: + return BudgetBuddiesAsset.AppImage.CategoryIcon.cultureIcon2.image + case 5: + return BudgetBuddiesAsset.AppImage.CategoryIcon.trafficIcon2.image + case 6: + return BudgetBuddiesAsset.AppImage.CategoryIcon.cafeIcon2.image + case 7: + return BudgetBuddiesAsset.AppImage.CategoryIcon.playIcon2.image + case 8: + return BudgetBuddiesAsset.AppImage.CategoryIcon.eventIcon2.image + case 9: + return BudgetBuddiesAsset.AppImage.CategoryIcon.regularPaymentIcon2.image + case 10: + return BudgetBuddiesAsset.AppImage.CategoryIcon.etcIcon2.image + default: + return BudgetBuddiesAsset.AppImage.CategoryIcon.personal2.image + } + } + + // MARK: - Selectors + @objc + private func didTapBarButton() { + self.navigationController?.popViewController(animated: true) + } + @objc func spendGoalButtonTapped() { if let naviController = self.navigationController { let goalTotalVC = GoalTotalViewController() @@ -405,6 +437,13 @@ final class MonthReportViewController: UIViewController { naviController.pushViewController(consumeVC, animated: true) } } + + @objc func accountBookTotalButtonTapped() { + if let naviController = self.navigationController { + let historyVC = ConsumedHistoryTableViewController() + naviController.pushViewController(historyVC, animated: true) + } + } } // MARK: - UITableViewDelegate, UITableViewDataSource @@ -412,9 +451,9 @@ extension MonthReportViewController: UITableViewDelegate, UITableViewDataSource func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if tableView == spendGoalTableView { - return spendGoals.count + return 4 } else if tableView == accountBookTableView { - return accountBooks.count + return 2 } return 0 } @@ -428,24 +467,6 @@ extension MonthReportViewController: UITableViewDelegate, UITableViewDataSource return 0 } - func scrollViewDidScroll(_ scrollView: UIScrollView) { - let offsetY = scrollView.contentOffset.y - - if offsetY > lastContentOffset && offsetY > 0 { // 스크롤을 아래로 내리는 중이고, offsetY가 0보다 클 때 - UIView.animate(withDuration: 0.3) { - self.navigationController?.setNavigationBarHidden(true, animated: true) - } - - } else if offsetY < lastContentOffset { // 스크롤을 위로 올리는 중 - UIView.animate(withDuration: 0.3) { - self.navigationController?.setNavigationBarHidden(false, animated: true) - } - } - - lastContentOffset = offsetY - - } - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if tableView == spendGoalTableView { guard @@ -476,6 +497,45 @@ extension MonthReportViewController: UITableViewDelegate, UITableViewDataSource } } +// MARK: - ScrollView Delegate +extension MonthReportViewController: UIScrollViewDelegate { + func scrollViewDidScroll(_ scrollView: UIScrollView) { + + // + let offsetY = scrollView.contentOffset.y + + let currentOffset = scrollView.contentOffset.y + let offsetDifference = currentOffset - previousScrollOffset + + if currentOffset <= 0 { // 스크롤을 완전히 위로 올렸을 때 네비게이션 바 나타냄 + navigationController?.setNavigationBarHidden(false, animated: true) + + } else if offsetDifference > scrollThreshold { // 스크롤이 아래로 일정 이상 이동한 경우 네비게이션 바 숨김 + navigationController?.setNavigationBarHidden(true, animated: true) + + } else if offsetDifference < -scrollThreshold { // 스크롤이 위로 일정 이상 이동한 경우 네비게이션 바 나타냄 + navigationController?.setNavigationBarHidden(false, animated: true) + + } + + previousScrollOffset = currentOffset + + // MARK: - 네비 색상 변경 + let threshold = 126.0 // 이 값은 조정 가능. 스크롤에 따른 색상 변화의 임계값 + + // 배경색 설정 (예제: 특정 offsetY에서 배경색을 변경) + let backgroundColor: UIColor + if offsetY > threshold { + backgroundColor = .clear + } else { + backgroundColor = BudgetBuddiesAsset.AppColor.coreYellow.color // 스크롤이 일정 위치를 넘었을 때 배경색을 흰색으로 + } + + // 네비게이션 바 업데이트 + setupDefaultNavigationBar(backgroundColor: backgroundColor) + } +} + // MARK: - 네트워킹 extension MonthReportViewController { func loadConsumeGoal() { @@ -488,7 +548,8 @@ extension MonthReportViewController { // 받아온 값을 업데이트 guard let totalGoalAmount = response.result?.totalGoalAmount, let totalSpentAmount = response.result?.totalConsumptionAmount, - let totalRemainingBalance = response.result?.totalRemainingBalance + let totalRemainingBalance = response.result?.totalRemainingBalance, + let consumptionGoalList = response.result?.consumptionGoalList else { print("Some values are nil") return @@ -500,77 +561,113 @@ extension MonthReportViewController { remaining: totalRemainingBalance ) - case .failure(let error): - print("Failed to load Top goal: \(error)") - } - } - } + let numberFormatter: NumberFormatter = { + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + formatter.groupingSeparator = "," + formatter.maximumFractionDigits = 0 // 소수점을 표시하지 않도록 설정 + return formatter + }() + + let spendGoals = consumptionGoalList.map { item -> SpendGoalModel in + let goalAmount = + numberFormatter.string(from: NSNumber(value: item.goalAmount ?? 0)) ?? "0" + let consumeAmount = + numberFormatter.string(from: NSNumber(value: item.consumeAmount ?? 0)) ?? "0" + let remainingBalance = + numberFormatter.string(from: NSNumber(value: item.remainingBalance ?? 0)) ?? "0" + + return SpendGoalModel( + categoryImage: self.setCategoryIconImage(categoryId: item.categoryId!), + title: item.categoryName ?? "", + amount: "\(goalAmount)", + progress: item.goalAmount! > 0 + ? Float(item.consumeAmount!) / Float(item.goalAmount!) : 0.0, + consumption: "\(consumeAmount)", + remaining: "\(remainingBalance)" + ) + } - func loadTopGoal() { - services.consumeGoalService.getTopGoal( - userId: 1, peerAgeStart: 23, peerAgeEnd: 25, peerGender: "male" - ) { result in - switch result { - case .success(let response): - self.getTopGoalResponse = response - dump(response) - case .failure(let error): - print("Failed to load Top goal: \(error)") - } - } - } + self.updateUI(with: spendGoals) - func loadTopConsumption() { - services.consumeGoalService.getTopConsumption( - userId: 1, peerAgeStart: 22, peerAgeEnd: 25, peerGender: "male" - ) { result in - switch result { - case .success(let response): - self.getTopConsumptionResponse = response - dump(response) case .failure(let error): - print("Failed to load Top Consumption: \(error)") - } - } - } - - func loadTopConsumptions() { - services.consumeGoalService.getTopConsumptions( - userId: 1, peerAgeStart: 22, peerAgeEnd: 25, peerGender: "male" - ) { result in - switch result { - case .success(let response): - self.getTopConsumptionsResponse = response - dump(response) - case .failure(let error): - print("Failed to load Top Consumptions: \(error)") + print("Failed to load Top goal: \(error)") } } } - func loadTopUser() { - services.consumeGoalService.getTopUser(userId: 1) { result in - switch result { - case .success(let response): - self.getTopUserResponse = response - dump(response) - case .failure(let error): - print("Failed to load Top User: \(error)") - } - } - } + // func loadTopGoal() { + // services.consumeGoalService.getTopGoal( + // userId: 1, peerAgeStart: 23, peerAgeEnd: 25, peerGender: "male" + // ) { result in + // switch result { + // case .success(let response): + // self.getTopGoalResponse = response + // dump(response) + // case .failure(let error): + // print("Failed to load Top goal: \(error)") + // } + // } + // } + // + // func loadTopConsumption() { + // services.consumeGoalService.getTopConsumption( + // userId: 1, peerAgeStart: 22, peerAgeEnd: 25, peerGender: "male" + // ) { result in + // switch result { + // case .success(let response): + // self.getTopConsumptionResponse = response + // dump(response) + // case .failure(let error): + // print("Failed to load Top Consumption: \(error)") + // } + // } + // } + // + // func loadTopConsumptions() { + // services.consumeGoalService.getTopConsumptions( + // userId: 1, peerAgeStart: 22, peerAgeEnd: 25, peerGender: "male" + // ) { result in + // switch result { + // case .success(let response): + // self.getTopConsumptionsResponse = response + // dump(response) + // case .failure(let error): + // print("Failed to load Top Consumptions: \(error)") + // } + // } + // } + // + // func loadTopUser() { + // services.consumeGoalService.getTopUser(userId: 1) { result in + // switch result { + // case .success(let response): + // self.getTopUserResponse = response + // dump(response) + // case .failure(let error): + // print("Failed to load Top User: \(error)") + // } + // } + // } + // + // func loadPeerInfo() { + // services.consumeGoalService.getPeerInfo( + // userId: 1, peerAgeStart: 25, peerAgeEnd: 25, peerGender: "male" + // ) { result in + // switch result { + // case .success(let response): + // self.consumePeerInfoResponse = response + // dump(response) + // case .failure(let error): + // print("Failed to load peer info: \(error)") + // } + // } + // } +} - func loadPeerInfo() { - services.consumeGoalService.getPeerInfo( - userId: 1, peerAgeStart: 25, peerAgeEnd: 25, peerGender: "male" - ) { result in - switch result { - case .success(let response): - self.consumePeerInfoResponse = response - dump(response) - case .failure(let error): - print("Failed to load peer info: \(error)") - } - } +// MARK: - 뒤로 가기 슬라이드 제스처 추가 +extension MonthReportViewController: UIGestureRecognizerDelegate { + func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + return true } } diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/Views/Cells/AccountBookCell.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/Views/Cells/AccountBookCell.swift index 7c08c850..689c8dfc 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/Views/Cells/AccountBookCell.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/Views/Cells/AccountBookCell.swift @@ -25,6 +25,7 @@ final class AccountBookCell: UITableViewCell { let iconImageView = { let image = UIImageView() image.contentMode = .scaleAspectFit + image.setShadow(opacity: 1, Radius: 3.6, offSet: CGSize(width: 0, height: 0)) return image }() @@ -77,17 +78,17 @@ final class AccountBookCell: UITableViewCell { iconImageView.snp.makeConstraints { $0.centerY.equalToSuperview() $0.leading.equalToSuperview().offset(24) - $0.width.height.equalTo(40) + $0.width.height.equalTo(48) } amountLabel.snp.makeConstraints { - $0.top.equalTo(iconImageView.snp.top) + $0.top.equalTo(iconImageView.snp.top).offset(5) $0.leading.equalTo(iconImageView.snp.trailing).offset(20) } descriptionLabel.snp.makeConstraints { $0.leading.equalTo(amountLabel) - $0.bottom.equalTo(iconImageView.snp.bottom) + $0.top.equalTo(amountLabel.snp.bottom).offset(2) } // moreButton.snp.makeConstraints { diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/Views/Cells/SpendGoalCell.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/Views/Cells/SpendGoalCell.swift index 84c58f63..9068425d 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/Views/Cells/SpendGoalCell.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/Views/Cells/SpendGoalCell.swift @@ -18,10 +18,7 @@ final class SpendGoalCell: UITableViewCell { let view = UIView() view.backgroundColor = .white view.layer.cornerRadius = 15 - view.layer.shadowColor = UIColor.black.withAlphaComponent(0.1).cgColor - view.layer.shadowOpacity = 1 - view.layer.shadowRadius = 10 - view.layer.masksToBounds = false + view.setShadow(opacity: 1, Radius: 5, offSet: CGSize(width: 0, height: 0)) return view }() @@ -30,20 +27,25 @@ final class SpendGoalCell: UITableViewCell { imageView.contentMode = .scaleAspectFit imageView.layer.cornerRadius = 15 imageView.layer.masksToBounds = true + imageView.setShadow(opacity: 1, Radius: 3.6, offSet: CGSize(width: 0, height: 0)) return imageView }() let titleLabel = { let label = UILabel() - label.font = UIFont.systemFont(ofSize: 16, weight: .semibold) - label.textColor = .black + label.text = " " + label.setCharacterSpacing(-0.35) + label.font = BudgetBuddiesFontFamily.Pretendard.medium.font(size: 14) + label.textColor = BudgetBuddiesAsset.AppColor.subGray.color return label }() let amountLabel = { let label = UILabel() - label.font = UIFont.systemFont(ofSize: 16, weight: .semibold) - label.textColor = .black + label.text = " " + label.setCharacterSpacing(-0.4) + label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 16) + label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color return label }() @@ -58,15 +60,19 @@ final class SpendGoalCell: UITableViewCell { let consumptionLabel = { let label = UILabel() - label.font = UIFont.systemFont(ofSize: 14, weight: .regular) - label.textColor = .gray + label.text = " " + label.setCharacterSpacing(-0.3) + label.font = BudgetBuddiesFontFamily.Pretendard.regular.font(size: 12) + label.textColor = BudgetBuddiesAsset.AppColor.subGray.color return label }() let remainingLabel = { let label = UILabel() - label.font = UIFont.systemFont(ofSize: 14, weight: .regular) - label.textColor = .gray + label.text = " " + label.setCharacterSpacing(-0.3) + label.font = BudgetBuddiesFontFamily.Pretendard.regular.font(size: 12) + label.textColor = BudgetBuddiesAsset.AppColor.subGray.color return label }() @@ -95,7 +101,8 @@ final class SpendGoalCell: UITableViewCell { // MARK: - Setup Constraints private func setConsts() { backView.snp.makeConstraints { - $0.edges.equalToSuperview().inset(8) + $0.leading.trailing.equalToSuperview().inset(16) + $0.top.bottom.equalToSuperview().inset(7) } logoImageView.snp.makeConstraints { @@ -104,13 +111,13 @@ final class SpendGoalCell: UITableViewCell { } titleLabel.snp.makeConstraints { - $0.top.equalToSuperview().inset(16) - $0.leading.equalTo(logoImageView.snp.trailing).offset(16) + $0.top.equalTo(logoImageView.snp.top).offset(5) + $0.leading.equalTo(logoImageView.snp.trailing).offset(11) } amountLabel.snp.makeConstraints { make in - make.top.equalTo(titleLabel.snp.bottom).offset(4) - make.leading.equalTo(logoImageView.snp.trailing).offset(16) + make.top.equalTo(titleLabel.snp.bottom) + make.leading.equalTo(logoImageView.snp.trailing).offset(11) } progressBar.snp.makeConstraints { make in diff --git a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/Views/FaceChartView.swift b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/Views/FaceChartView.swift index ce36a64f..b3622a38 100644 --- a/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/Views/FaceChartView.swift +++ b/BudgetBuddies/BudgetBuddies/Sources/Screen/Report/MonthReport/Views/FaceChartView.swift @@ -10,9 +10,19 @@ import SnapKit import UIKit final class FaceChartView: UIView { + + let backView: UIView = { + let view = UIView() + view.backgroundColor = .white + view.layer.cornerRadius = 20 + view.setShadow(opacity: 1, Radius: 5, offSet: CGSize(width: 0, height: -1)) + return view + }() + let monthLabel = { let label = UILabel() - label.text = "6월" + label.text = "8월" + label.setCharacterSpacing(-0.4) label.textColor = BudgetBuddiesAsset.AppColor.textBlack.color label.font = BudgetBuddiesFontFamily.Pretendard.semiBold.font(size: 16) label.textAlignment = .center @@ -56,10 +66,7 @@ final class FaceChartView: UIView { view.layer.cornerRadius = 14 view.layer.borderColor = BudgetBuddiesAsset.AppColor.logoLine1.color.cgColor view.layer.borderWidth = 1 - view.layer.shadowColor = UIColor.black.cgColor - view.layer.shadowOpacity = 0.3 - view.layer.shadowOffset = CGSize(width: 0, height: 2) - view.layer.shadowRadius = 4 + view.setShadow(opacity: 1, Radius: 3.28, offSet: CGSize(width: 0, height: 0.82)) return view }() @@ -125,6 +132,7 @@ final class FaceChartView: UIView { } private func setup() { + self.addSubviews(backView) [beforeButton, monthLabel, afterButton].forEach { self.monthStackView.addArrangedSubview($0) @@ -134,13 +142,18 @@ final class FaceChartView: UIView { monthStackView, pieChartView, centerImageView, commentView, commentLabel, spendTitleLabel, totalSpendLabel, separatorView, remainTitleLabel, totalRemainLabel, ].forEach { - self.addSubview($0) + backView.addSubview($0) } commentView.addSubview(commentLabel) } private func setConst() { + backView.snp.makeConstraints { make in + make.leading.trailing.top.equalToSuperview() + make.bottom.equalTo(self.separatorView.snp.bottom).offset(20) + } + beforeButton.snp.makeConstraints { $0.width.equalTo(4) $0.height.equalTo(14) @@ -170,13 +183,13 @@ final class FaceChartView: UIView { commentView.snp.makeConstraints { $0.top.equalTo(pieChartView.snp.bottom).offset(16) - $0.leading.equalToSuperview().offset(40) - $0.trailing.equalToSuperview().offset(-40) + $0.leading.trailing.equalToSuperview().inset(43) } commentLabel.snp.makeConstraints { $0.edges.equalToSuperview().inset(8) } + spendTitleLabel.snp.makeConstraints { $0.top.equalTo(commentView.snp.bottom).offset(30) $0.leading.equalToSuperview().offset(70) diff --git a/BudgetBuddies/Project.swift b/BudgetBuddies/Project.swift index e68702c2..5364002f 100644 --- a/BudgetBuddies/Project.swift +++ b/BudgetBuddies/Project.swift @@ -1,9 +1,13 @@ import ProjectDescription -let settings: Settings = .settings(configurations: [ - .debug(name: "Debug", xcconfig: "BudgetBuddies/Resources/Debug.xcconfig"), - .release(name: "Release", xcconfig: "BudgetBuddies/Resources/Release.xcconfig"), -]) +let budgetBuddiesSettings: Settings = .settings( + base: [ + "DEVELOPMENT_LANGUAGE": "ko" + ], + configurations: [ + .debug(name: "Debug", xcconfig: "BudgetBuddies/Resources/Debug.xcconfig"), + .release(name: "Release", xcconfig: "BudgetBuddies/Resources/Release.xcconfig"), + ]) let budgetBuddiesInfoPlist: InfoPlist = .extendingDefault(with: [ "UILaunchStoryboardName": "LaunchScreen.storyboard", @@ -18,21 +22,43 @@ let budgetBuddiesInfoPlist: InfoPlist = .extendingDefault(with: [ ] ], ], + // BASE URL을 xcconfig 파일에서 도메인 가져오기 "BASEURL": "http://$(Base_Domain)", + // HTTP 프로토콜 네트워크 통신 허용 "NSAppTransportSecurity": [ "NSAllowsArbitraryLoads": true ], + // 배포용 앱 한글 이름 + "CFBundleDisplayName": "빈주머니즈", + // 다크모드 제한 + "UIUserInterfaceStyle": "Light", + // 앱 카테고리 지정 + "LSApplicationCategoryType": "public.app-category.finance", + // iPhone Orientation 지정 + "UISupportedInterfaceOrientations": [ + "UIInterfaceOrientationPortrait" + ], + // 배포용 버전 지정 + "CFBundleVersion": "1.2.0", + "CFBundleShortVersionString": "1.2.0", + // 사용 국가 지정 + "CFBundleDevelopmentRegion": "ko", ]) let project = Project( name: "BudgetBuddies", - settings: settings, + options: .options( + defaultKnownRegions: ["ko"], + developmentRegion: "ko" + ), + settings: budgetBuddiesSettings, targets: [ .target( name: "BudgetBuddies", - destinations: .iOS, + destinations: [.iPhone], product: .app, bundleId: "com.budgetbuddiesteam.budgetbuddiesapp", + deploymentTargets: .iOS("17.0"), infoPlist: budgetBuddiesInfoPlist, sources: ["BudgetBuddies/Sources/**"], resources: ["BudgetBuddies/Resources/**"], @@ -42,11 +68,9 @@ let project = Project( .external(name: "DGCharts", condition: .none), .external(name: "Moya", condition: .none), .external(name: "Kingfisher", condition: .none), - .external(name: "RxSwift", condition: .none), - .external(name: "RxCocoa", condition: .none), .external(name: "PromiseKit", condition: .none), ], - settings: settings + settings: budgetBuddiesSettings ), .target( name: "BudgetBuddiesTests",