diff --git a/Domain/Domain/Sources/Interface/Repository/WhiteboardRepositoryInterface.swift b/Domain/Domain/Sources/Interface/Repository/WhiteboardRepositoryInterface.swift index 1ae1bb7..7554f50 100644 --- a/Domain/Domain/Sources/Interface/Repository/WhiteboardRepositoryInterface.swift +++ b/Domain/Domain/Sources/Interface/Repository/WhiteboardRepositoryInterface.swift @@ -21,7 +21,7 @@ public protocol WhiteboardRepositoryInterface { /// 화이트보드 탐색을 중단 후 다시 시작합니다. func restartSearching() - + /// 화이트보드와 연결을 끊습니다. func disconnectWhiteboard() diff --git a/Domain/Domain/Sources/Interface/UseCase/WhiteboardUseCaseInterface.swift b/Domain/Domain/Sources/Interface/UseCase/WhiteboardUseCaseInterface.swift index 441b05b..277b57d 100644 --- a/Domain/Domain/Sources/Interface/UseCase/WhiteboardUseCaseInterface.swift +++ b/Domain/Domain/Sources/Interface/UseCase/WhiteboardUseCaseInterface.swift @@ -18,7 +18,7 @@ public protocol WhiteboardUseCaseInterface { /// 주변 화이트보드를 탐색합니다. func startSearchingWhiteboard() - + /// 화이트보드와 연결을 끊습니다. func disconnectWhiteboard() @@ -28,7 +28,7 @@ public protocol WhiteboardUseCaseInterface { /// 화이트보드 탐색을 중지합니다. func stopSearchingWhiteboard() - + /// 화이트보드 탐색을 중단 후 다시 시작합니다. func refreshWhiteboardList() } diff --git a/Domain/Domain/Sources/Interface/WhiteboardObjectSetInterface.swift b/Domain/Domain/Sources/Interface/WhiteboardObjectSetInterface.swift index 4884969..2d88a62 100644 --- a/Domain/Domain/Sources/Interface/WhiteboardObjectSetInterface.swift +++ b/Domain/Domain/Sources/Interface/WhiteboardObjectSetInterface.swift @@ -25,7 +25,7 @@ public protocol WhiteboardObjectSetInterface { /// 집합에 있는 오브젝트를 업데이트 합니다. /// - Parameter object: 업데이트할 오브젝트 func update(object: WhiteboardObject) async - + /// ID로 집합에있는 오브젝트를 가져옵니다. /// - Parameter id: 가져올 오브젝트의 ID /// - Returns: 오브젝트 diff --git a/Presentation/Presentation.xcodeproj/project.pbxproj b/Presentation/Presentation.xcodeproj/project.pbxproj index 32afa25..dba2b9c 100644 --- a/Presentation/Presentation.xcodeproj/project.pbxproj +++ b/Presentation/Presentation.xcodeproj/project.pbxproj @@ -24,6 +24,16 @@ 6F199EF12CE31A05005DC40F /* WhiteboardToolBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F199EF02CE31A05005DC40F /* WhiteboardToolBar.swift */; }; 6F199EF32CE3203A005DC40F /* WhiteboardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F199EF22CE3203A005DC40F /* WhiteboardViewController.swift */; }; 6F21477D2CE4EFCF00B55E2C /* TextObjectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F21477C2CE4EFCF00B55E2C /* TextObjectView.swift */; }; + A81E7ABB2CF5C771007E8414 /* Wordle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81E7ABA2CF5C771007E8414 /* Wordle.swift */; }; + A81E7ABD2CF5C7A7007E8414 /* WordleState.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81E7ABC2CF5C7A7007E8414 /* WordleState.swift */; }; + A81E7ABF2CF5C7FE007E8414 /* KeyboardState.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81E7ABE2CF5C7FE007E8414 /* KeyboardState.swift */; }; + A81E7AC12CF5C813007E8414 /* WordleKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81E7AC02CF5C813007E8414 /* WordleKeyboard.swift */; }; + A81E7AC62CF5CA18007E8414 /* WordleTileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81E7AC52CF5CA18007E8414 /* WordleTileView.swift */; }; + A81E7AC82CF5CAD5007E8414 /* KeyboardTileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81E7AC72CF5CAD5007E8414 /* KeyboardTileView.swift */; }; + A81E7BD42CF6E76D007E8414 /* WordleGuideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81E7BD32CF6E76D007E8414 /* WordleGuideView.swift */; }; + A81E7BD62CF6E797007E8414 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A81E7BD52CF6E797007E8414 /* Images.xcassets */; }; + A81E7BD82CF6E94F007E8414 /* WordleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81E7BD72CF6E94F007E8414 /* WordleView.swift */; }; + A81E7BDB2CF6E973007E8414 /* WordleViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81E7BDA2CF6E973007E8414 /* WordleViewModel.swift */; }; 6F25BE442CF5BC44005BCAA2 /* PeerMessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F25BE432CF5BC44005BCAA2 /* PeerMessageCell.swift */; }; 6F25BE472CF5BF79005BCAA2 /* ChatMessageCellModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F25BE462CF5BF6F005BCAA2 /* ChatMessageCellModel.swift */; }; 6FA7DBE92CF4B4E1007333C6 /* ChatTextFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FA7DBE82CF4B4E1007333C6 /* ChatTextFieldView.swift */; }; @@ -77,6 +87,16 @@ 6F199EF02CE31A05005DC40F /* WhiteboardToolBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhiteboardToolBar.swift; sourceTree = ""; }; 6F199EF22CE3203A005DC40F /* WhiteboardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhiteboardViewController.swift; sourceTree = ""; }; 6F21477C2CE4EFCF00B55E2C /* TextObjectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextObjectView.swift; sourceTree = ""; }; + A81E7ABA2CF5C771007E8414 /* Wordle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wordle.swift; sourceTree = ""; }; + A81E7ABC2CF5C7A7007E8414 /* WordleState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordleState.swift; sourceTree = ""; }; + A81E7ABE2CF5C7FE007E8414 /* KeyboardState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardState.swift; sourceTree = ""; }; + A81E7AC02CF5C813007E8414 /* WordleKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordleKeyboard.swift; sourceTree = ""; }; + A81E7AC52CF5CA18007E8414 /* WordleTileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordleTileView.swift; sourceTree = ""; }; + A81E7AC72CF5CAD5007E8414 /* KeyboardTileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardTileView.swift; sourceTree = ""; }; + A81E7BD32CF6E76D007E8414 /* WordleGuideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordleGuideView.swift; sourceTree = ""; }; + A81E7BD52CF6E797007E8414 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + A81E7BD72CF6E94F007E8414 /* WordleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordleView.swift; sourceTree = ""; }; + A81E7BDA2CF6E973007E8414 /* WordleViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordleViewModel.swift; sourceTree = ""; }; 6F25BE432CF5BC44005BCAA2 /* PeerMessageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeerMessageCell.swift; sourceTree = ""; }; 6F25BE462CF5BF6F005BCAA2 /* ChatMessageCellModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMessageCellModel.swift; sourceTree = ""; }; 6FA7DBE82CF4B4E1007333C6 /* ChatTextFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTextFieldView.swift; sourceTree = ""; }; @@ -138,6 +158,7 @@ A85260262CE344AF0089DA5E /* Profile */, 5B65412E2CE32B11000168AD /* WhiteboardList */, 0080E8C32CE2F0310095B958 /* Whiteboard */, + A81E7AB42CF5C6D4007E8414 /* Wordle */, 6FA7DBE52CF4682F007333C6 /* Chat */, ); path = Sources; @@ -242,32 +263,64 @@ name = Frameworks; sourceTree = ""; }; - 6F25BE452CF5BF63005BCAA2 /* Model */ = { + A81E7AB42CF5C6D4007E8414 /* Wordle */ = { isa = PBXGroup; children = ( - 6F25BE462CF5BF6F005BCAA2 /* ChatMessageCellModel.swift */, + A81E7AB92CF5C762007E8414 /* Model */, + A81E7AC42CF5CA07007E8414 /* View */, + A81E7BD92CF6E95C007E8414 /* ViewModel */, + ); + path = Wordle; + sourceTree = ""; + }; + A81E7AB92CF5C762007E8414 /* Model */ = { + isa = PBXGroup; + children = ( + A81E7ABC2CF5C7A7007E8414 /* WordleState.swift */, + A81E7ABA2CF5C771007E8414 /* Wordle.swift */, + A81E7ABE2CF5C7FE007E8414 /* KeyboardState.swift */, + A81E7AC02CF5C813007E8414 /* WordleKeyboard.swift */, ); path = Model; sourceTree = ""; }; - 6FA7DBE52CF4682F007333C6 /* Chat */ = { + A81E7AC42CF5CA07007E8414 /* View */ = { isa = PBXGroup; children = ( - 6F25BE452CF5BF63005BCAA2 /* Model */, - 6FA7DBE72CF4B4DA007333C6 /* View */, - 6FA7DBE62CF4685A007333C6 /* ViewModel */, + A81E7AC52CF5CA18007E8414 /* WordleTileView.swift */, + A81E7AC72CF5CAD5007E8414 /* KeyboardTileView.swift */, + A81E7BD32CF6E76D007E8414 /* WordleGuideView.swift */, + A81E7BD72CF6E94F007E8414 /* WordleView.swift */, ); - path = Chat; + path = View; sourceTree = ""; }; - 6FA7DBE62CF4685A007333C6 /* ViewModel */ = { + A81E7BD92CF6E95C007E8414 /* ViewModel */ = { isa = PBXGroup; children = ( - 6FA7DBEB2CF56103007333C6 /* ChatViewModel.swift */, + A81E7BDA2CF6E973007E8414 /* WordleViewModel.swift */, ); path = ViewModel; sourceTree = ""; }; + 6FA7DBE52CF4682F007333C6 /* Chat */ = { + isa = PBXGroup; + children = ( + 6F25BE452CF5BF63005BCAA2 /* Model */, + 6FA7DBE72CF4B4DA007333C6 /* View */, + 6FA7DBE62CF4685A007333C6 /* ViewModel */, + ); + path = Chat; + sourceTree = ""; + }; + 6FA7DBE62CF4685A007333C6 /* ViewModel */ = { + isa = PBXGroup; + children = ( + 6FA7DBEB2CF56103007333C6 /* ChatViewModel.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; 6FA7DBE72CF4B4DA007333C6 /* View */ = { isa = PBXGroup; children = ( @@ -288,10 +341,19 @@ path = Cell; sourceTree = ""; }; + 6F25BE452CF5BF63005BCAA2 /* Model */ = { + isa = PBXGroup; + children = ( + 6F25BE462CF5BF6F005BCAA2 /* ChatMessageCellModel.swift */, + ); + path = Model; + sourceTree = ""; + }; A8525DC12CE200110089DA5E /* Resources */ = { isa = PBXGroup; children = ( A8525DC22CE200230089DA5E /* Colors.xcassets */, + A81E7BD52CF6E797007E8414 /* Images.xcassets */, ); path = Resources; sourceTree = ""; @@ -427,6 +489,7 @@ buildActionMask = 2147483647; files = ( A8525DC32CE200230089DA5E /* Colors.xcassets in Resources */, + A81E7BD62CF6E797007E8414 /* Images.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -458,12 +521,17 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A81E7ABB2CF5C771007E8414 /* Wordle.swift in Sources */, 00CCC2672CE60BCD005FA747 /* AirplainFont.swift in Sources */, + A81E7AC12CF5C813007E8414 /* WordleKeyboard.swift in Sources */, 5BE0D1BC2CEC7D2300F43946 /* WhiteboardCell.swift in Sources */, A8E97AC12CE4E9A300B28063 /* ProfileIconCollectionViewCell.swift in Sources */, A8E979C72CE4945500B28063 /* LayoutConstant.swift in Sources */, A85260292CE344C60089DA5E /* ProfileIconView.swift in Sources */, + A81E7AC82CF5CAD5007E8414 /* KeyboardTileView.swift in Sources */, 004B21812CEB5BC000A5BEB8 /* PhotoObjectView.swift in Sources */, + A81E7BDB2CF6E973007E8414 /* WordleViewModel.swift in Sources */, + A81E7ABD2CF5C7A7007E8414 /* WordleState.swift in Sources */, 6FA7DBE92CF4B4E1007333C6 /* ChatTextFieldView.swift in Sources */, A8E97ABF2CE4E94D00B28063 /* SelectProfileIconViewController.swift in Sources */, A8E979C52CE493E900B28063 /* ProfileViewController.swift in Sources */, @@ -472,12 +540,15 @@ A85260252CE3447E0089DA5E /* UIColor+.swift in Sources */, 00D2DD982CE8CD300089F0BA /* DrawingView.swift in Sources */, A81E7BF52CF710EC007E8414 /* GameObjectView.swift in Sources */, + A81E7AC62CF5CA18007E8414 /* WordleTileView.swift in Sources */, A8E979CA2CE49A1800B28063 /* ProfileViewModel.swift in Sources */, 0080E8C62CE2F0510095B958 /* WhiteboardObjectView.swift in Sources */, 6FA7DBEC2CF56103007333C6 /* ChatViewModel.swift in Sources */, A8525DCB2CE203D50089DA5E /* UIView+.swift in Sources */, 6F199EF32CE3203A005DC40F /* WhiteboardViewController.swift in Sources */, + A81E7ABF2CF5C7FE007E8414 /* KeyboardState.swift in Sources */, 6F199EF12CE31A05005DC40F /* WhiteboardToolBar.swift in Sources */, + A81E7BD82CF6E94F007E8414 /* WordleView.swift in Sources */, 6FA7DBEE2CF56457007333C6 /* MessageCell.swift in Sources */, 6F21477D2CE4EFCF00B55E2C /* TextObjectView.swift in Sources */, 6F25BE442CF5BC44005BCAA2 /* PeerMessageCell.swift in Sources */, @@ -487,6 +558,7 @@ 5B2BC78E2CE4F66F00893B9E /* WhiteboardListViewController.swift in Sources */, 5B2BC78F2CE4F66F00893B9E /* WhiteboardListViewModel.swift in Sources */, 0080E8CA2CE2FF750095B958 /* WhiteboardObjectViewFactoryable.swift in Sources */, + A81E7BD42CF6E76D007E8414 /* WordleGuideView.swift in Sources */, 00683D762CE4B828000D28E4 /* DrawingRenderer.swift in Sources */, 6F25BE472CF5BF79005BCAA2 /* ChatMessageCellModel.swift in Sources */, ); diff --git a/Presentation/Presentation/Resources/Images.xcassets/Contents.json b/Presentation/Presentation/Resources/Images.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Presentation/Presentation/Resources/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Presentation/Presentation/Resources/Images.xcassets/WordleGuideImage.imageset/Contents.json b/Presentation/Presentation/Resources/Images.xcassets/WordleGuideImage.imageset/Contents.json new file mode 100644 index 0000000..d4ae642 --- /dev/null +++ b/Presentation/Presentation/Resources/Images.xcassets/WordleGuideImage.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "WordleGuideImage.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Presentation/Presentation/Resources/Images.xcassets/WordleGuideImage.imageset/WordleGuideImage.png b/Presentation/Presentation/Resources/Images.xcassets/WordleGuideImage.imageset/WordleGuideImage.png new file mode 100644 index 0000000..d52cfa4 Binary files /dev/null and b/Presentation/Presentation/Resources/Images.xcassets/WordleGuideImage.imageset/WordleGuideImage.png differ diff --git a/Presentation/Presentation/Sources/Wordle/Model/KeyboardState.swift b/Presentation/Presentation/Sources/Wordle/Model/KeyboardState.swift new file mode 100644 index 0000000..85b9a68 --- /dev/null +++ b/Presentation/Presentation/Sources/Wordle/Model/KeyboardState.swift @@ -0,0 +1,15 @@ +// +// KeyboardState.swift +// Presentation +// +// Created by 최정인 on 11/26/24. +// + +enum KeyboardState { + case unused + case wrong + case correct + case misplaced + case enter + case erase +} diff --git a/Presentation/Presentation/Sources/Wordle/Model/Wordle.swift b/Presentation/Presentation/Sources/Wordle/Model/Wordle.swift new file mode 100644 index 0000000..f10511b --- /dev/null +++ b/Presentation/Presentation/Sources/Wordle/Model/Wordle.swift @@ -0,0 +1,11 @@ +// +// Wordle.swift +// Presentation +// +// Created by 최정인 on 11/26/24. +// + +struct Wordle { + var alphabet: String? + var state: WordleState +} diff --git a/Presentation/Presentation/Sources/Wordle/Model/WordleKeyboard.swift b/Presentation/Presentation/Sources/Wordle/Model/WordleKeyboard.swift new file mode 100644 index 0000000..1b31181 --- /dev/null +++ b/Presentation/Presentation/Sources/Wordle/Model/WordleKeyboard.swift @@ -0,0 +1,11 @@ +// +// WordleKeyboard.swift +// Presentation +// +// Created by 최정인 on 11/26/24. +// + +struct WordleKeyboard { + let alphabet: String? + var keyboardState: KeyboardState +} diff --git a/Presentation/Presentation/Sources/Wordle/Model/WordleState.swift b/Presentation/Presentation/Sources/Wordle/Model/WordleState.swift new file mode 100644 index 0000000..2513eea --- /dev/null +++ b/Presentation/Presentation/Sources/Wordle/Model/WordleState.swift @@ -0,0 +1,15 @@ +// +// WordleState.swift +// Presentation +// +// Created by 최정인 on 11/26/24. +// + +enum WordleState { + case empty + case typing + case wrong + case correct + case invalid + case misplaced +} diff --git a/Presentation/Presentation/Sources/Wordle/View/KeyboardTileView.swift b/Presentation/Presentation/Sources/Wordle/View/KeyboardTileView.swift new file mode 100644 index 0000000..21527cb --- /dev/null +++ b/Presentation/Presentation/Sources/Wordle/View/KeyboardTileView.swift @@ -0,0 +1,68 @@ +// +// KeyboardTileView.swift +// Presentation +// +// Created by 최정인 on 11/26/24. +// + +import SwiftUI + +struct KeyboardTileView: View { + @Binding var wordleKeyboard: WordleKeyboard + let keyboardWidth: CGFloat + let keyboardHeight: CGFloat + let action: () -> Void + + private enum KeyboardTileViewLayoutConstant { + static let keyboardCornerRadius: CGFloat = 10 + static let enterFontSize: CGFloat = 13 + } + + // 타일 색상 + private var keywordColor: Color { + switch wordleKeyboard.keyboardState { + case .unused, .enter, .erase: + .gray200 + case .wrong: + .gray600 + case .correct: + .airplainBlue + case .misplaced: + .wordleYellow + } + } + + // Text 색상 + private var keyboardTextColor: Color { + switch wordleKeyboard.keyboardState { + case .unused, .enter, .erase: + .airplainBlack + default: + .white + } + } + + var body: some View { + Button(action: action) { + ZStack { + RoundedRectangle(cornerRadius: KeyboardTileViewLayoutConstant.keyboardCornerRadius) + .fill(keywordColor) + .frame(width: keyboardWidth, height: keyboardHeight) + + if let alphabet = wordleKeyboard.alphabet { + Text(alphabet) + .font(Font(AirplainFont.Heading3)) + .foregroundStyle(keyboardTextColor) + } else if wordleKeyboard.keyboardState == .erase { + Text("⌫") + .font(Font(AirplainFont.Heading3)) + .foregroundStyle(keyboardTextColor) + } else if wordleKeyboard.keyboardState == .enter { + Text("ENTER") + .font(.system(size: KeyboardTileViewLayoutConstant.enterFontSize, weight: .bold)) + .foregroundStyle(keyboardTextColor) + } + } + } + } +} diff --git a/Presentation/Presentation/Sources/Wordle/View/WordleGuideView.swift b/Presentation/Presentation/Sources/Wordle/View/WordleGuideView.swift new file mode 100644 index 0000000..1347d50 --- /dev/null +++ b/Presentation/Presentation/Sources/Wordle/View/WordleGuideView.swift @@ -0,0 +1,35 @@ +// +// WordleGuideView.swift +// Presentation +// +// Created by 최정인 on 11/27/24. +// + +import SwiftUI + +struct WordleGuideView: View { + @Binding var isShowingGuideView: Bool + + var body: some View { + VStack { + Spacer() + ZStack(alignment: .topTrailing) { + Image(.wordleGuide) + .resizable() + .scaledToFit() + + Button { + isShowingGuideView.toggle() + } label: { + Image(systemName: "xmark") + .foregroundStyle(.black) + } + .padding(horizontalMargin) + } + .padding(horizontalMargin) + Spacer() + } + .background(Color.airplainBlack.opacity(0.4)) + .ignoresSafeArea() + } +} diff --git a/Presentation/Presentation/Sources/Wordle/View/WordleTileView.swift b/Presentation/Presentation/Sources/Wordle/View/WordleTileView.swift new file mode 100644 index 0000000..cae8140 --- /dev/null +++ b/Presentation/Presentation/Sources/Wordle/View/WordleTileView.swift @@ -0,0 +1,68 @@ +// +// WordleTileView.swift +// Presentation +// +// Created by 최정인 on 11/26/24. +// + +import SwiftUI + +struct WordleTileView: View { + @Binding var wordleTile: Wordle + let size: CGFloat + + // 타일 색상 + private var wordleTileColor: Color { + switch wordleTile.state { + case .empty, .typing, .invalid: + .white + case .wrong: + .gray500 + case .correct: + .airplainBlue + case .misplaced: + .wordleYellow + } + } + + // Text 색상 + private var wordleTextColor: Color { + switch wordleTile.state { + case .typing: + .airplainBlack + case .invalid: + .wordleRed + default: + .white + } + } + + // Border 색상 + private var wordleBorderColor: Color { + switch wordleTile.state { + case .empty: + .gray400 + case .typing, .invalid: + .gray900 + case .correct: + .airplainBlue + case .wrong: + .gray500 + case .misplaced: + .wordleYellow + } + } + + var body: some View { + ZStack { + Rectangle() + .fill(wordleTileColor) + .border(wordleBorderColor, width: 2) + .frame(width: size, height: size) + + Text(wordleTile.alphabet ?? " ") + .font(Font(AirplainFont.Heading2)) + .foregroundStyle(wordleTextColor) + } + } +} diff --git a/Presentation/Presentation/Sources/Wordle/View/WordleView.swift b/Presentation/Presentation/Sources/Wordle/View/WordleView.swift new file mode 100644 index 0000000..2a5d3a4 --- /dev/null +++ b/Presentation/Presentation/Sources/Wordle/View/WordleView.swift @@ -0,0 +1,135 @@ +// +// WordleView.swift +// Presentation +// +// Created by 최정인 on 11/27/24. +// + +import SwiftUI + +struct WordleView: View { + @StateObject var viewModel: WordleViewModel + @State private var isShowingGuideView: Bool = false + + private enum WordleViewLayoutConstant { + static let wordleAnswerCornerRadius: CGFloat = 10 + static let wordleAnswerWidth: CGFloat = 90 + static let wordleAnswerHeight: CGFloat = 30 + static let wordleSpacing: CGFloat = 10 + static let keyboardHorizontalMargin: CGFloat = 12 + static let keyboardSpacing: CGFloat = 8 + } + + var body: some View { + GeometryReader { geometry in + NavigationView { + ZStack { + HStack { + Spacer() + VStack { + if viewModel.isGameOver { + wordleAnswerView + } + Spacer() + wordleWordView(geo: geometry) + Spacer() + wordleKeyboardView(geo: geometry) + Spacer() + } + Spacer() + } + + if isShowingGuideView { + WordleGuideView(isShowingGuideView: $isShowingGuideView) + } + } + } + .toolbar { + ToolbarItem(placement: .topBarLeading) { + Button { + // TODO: Dismiss + } label: { + Image(systemName: "xmark") + .foregroundStyle(.airplainBlack) + } + .disabled(isShowingGuideView) + } + + ToolbarItem(placement: .topBarTrailing) { + Button { + isShowingGuideView.toggle() + } label: { + Image(systemName: "questionmark.circle") + .foregroundStyle(.airplainBlue) + } + .disabled(isShowingGuideView) + } + } + } + } + + // wordle 정답 + private var wordleAnswerView: some View { + ZStack { + RoundedRectangle(cornerRadius: WordleViewLayoutConstant.wordleAnswerCornerRadius) + .fill(.airplainBlack) + .frame( + width: WordleViewLayoutConstant.wordleAnswerWidth, + height: WordleViewLayoutConstant.wordleAnswerHeight) + + Text(viewModel.answerWord) + .foregroundStyle(.white) + .font(Font(AirplainFont.Subtitle2)) + } + } + + // wordle 타일 + private func wordleWordView(geo: GeometryProxy) -> some View { + let wordleWordCount: CGFloat = 5 + let totalHorizontalMargin = horizontalMargin * 2 + let wordleSpacing = WordleViewLayoutConstant.wordleSpacing * 4.0 + + let wordleSizeByWidth = (geo.size.width - totalHorizontalMargin - wordleSpacing) / wordleWordCount + let wordleSizeByHeight = ((geo.size.height * 0.5) - wordleSpacing) / wordleWordCount + let wordleSize = min(wordleSizeByWidth, wordleSizeByHeight) + + return ForEach(0.. some View { + let totalHorizontalMargin = WordleViewLayoutConstant.keyboardHorizontalMargin * 2 + let keyboardSpacing = WordleViewLayoutConstant.keyboardSpacing * 9 + + let keyboardWidth = (geo.size.width - totalHorizontalMargin - keyboardSpacing) / 10 + let keyboardHeight = geo.size.height * 0.063 + let enterKeyboardWidth = keyboardWidth * 1.8 + + return VStack { + ForEach(0..