From d9109ae4611982dde3b6254cbe872d3c98123cca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Fri, 5 Aug 2022 15:43:08 +0900 Subject: [PATCH 01/22] Make week1 assignment MVVM --- week1/Memorize/Memorize/ContentView.swift | 13 +- .../Memorize.xcodeproj/project.pbxproj | 388 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 93 +++++ .../Memorize/Assets.xcassets/Contents.json | 6 + week2/Memorize/Memorize/MemorizeApp.swift | 18 + .../Memorize/Memorize/Model/MemoryGame.swift | 36 ++ .../Preview Assets.xcassets/Contents.json | 6 + .../Memorize/Memorize/View/ContentView.swift | 37 ++ .../Memorize/View/SubViews/CardView.swift | 34 ++ .../Memorize/ViewModel/EmojiMemoryGame.swift | 29 ++ 13 files changed, 681 insertions(+), 5 deletions(-) create mode 100644 week2/Memorize/Memorize.xcodeproj/project.pbxproj create mode 100644 week2/Memorize/Memorize.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 week2/Memorize/Memorize.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 week2/Memorize/Memorize/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 week2/Memorize/Memorize/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 week2/Memorize/Memorize/Assets.xcassets/Contents.json create mode 100644 week2/Memorize/Memorize/MemorizeApp.swift create mode 100644 week2/Memorize/Memorize/Model/MemoryGame.swift create mode 100644 week2/Memorize/Memorize/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 week2/Memorize/Memorize/View/ContentView.swift create mode 100644 week2/Memorize/Memorize/View/SubViews/CardView.swift create mode 100644 week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift diff --git a/week1/Memorize/Memorize/ContentView.swift b/week1/Memorize/Memorize/ContentView.swift index aa12427..1192c44 100644 --- a/week1/Memorize/Memorize/ContentView.swift +++ b/week1/Memorize/Memorize/ContentView.swift @@ -8,8 +8,9 @@ import SwiftUI struct ContentView: View { - @State var countOfCards = 8 - @State var contents = Emoji.vehicles.emojiList +// @State var countOfCards = 8 +// @State var contents = Emoji.vehicles.emojiList + @ObservedObject var viewModel: EmojiMemoryGame var body: some View { VStack { @@ -18,12 +19,14 @@ struct ContentView: View { .padding() ScrollView(showsIndicators: false) { LazyVGrid(columns: [GridItem(.adaptive(minimum: 80, maximum: 100))], spacing: 10) { - ForEach(0.. + + + + diff --git a/week2/Memorize/Memorize.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/week2/Memorize/Memorize.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/week2/Memorize/Memorize.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/week2/Memorize/Memorize/Assets.xcassets/AccentColor.colorset/Contents.json b/week2/Memorize/Memorize/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/week2/Memorize/Memorize/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week2/Memorize/Memorize/Assets.xcassets/AppIcon.appiconset/Contents.json b/week2/Memorize/Memorize/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..5a3257a --- /dev/null +++ b/week2/Memorize/Memorize/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,93 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week2/Memorize/Memorize/Assets.xcassets/Contents.json b/week2/Memorize/Memorize/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/week2/Memorize/Memorize/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week2/Memorize/Memorize/MemorizeApp.swift b/week2/Memorize/Memorize/MemorizeApp.swift new file mode 100644 index 0000000..975b7bc --- /dev/null +++ b/week2/Memorize/Memorize/MemorizeApp.swift @@ -0,0 +1,18 @@ +// +// MemorizeApp.swift +// Memorize +// +// Created by 임주민 on 2022/07/28. +// + +import SwiftUI + +@main +struct MemorizeApp: App { + + var body: some Scene { + WindowGroup { + ContentView(viewModel: EmojiMemoryGame()) + } + } +} diff --git a/week2/Memorize/Memorize/Model/MemoryGame.swift b/week2/Memorize/Memorize/Model/MemoryGame.swift new file mode 100644 index 0000000..1f4969d --- /dev/null +++ b/week2/Memorize/Memorize/Model/MemoryGame.swift @@ -0,0 +1,36 @@ +// +// MemoryGame.swift +// Memorize +// +// Created by 임주민 on 2022/08/05. +// + +import Foundation + +struct MemoryGame { + + var cards: [Card] + + init(numberOfPairsOfCard: Int, createContent: (Int) -> CardContent) { + cards = [Card]() + for pairIndex in 0...Card + + var body: some View { + ZStack{ + let shape = RoundedRectangle(cornerRadius: 15) + if card.isFaceUp { + shape.strokeBorder(lineWidth: 5) + Text(card.content).font(.system(size: 40)) + } else { + shape.fill() + } + } + .foregroundColor(Color.red) + .aspectRatio(2 / 3, contentMode: .fit) + } +} + +struct CardView_Previews: PreviewProvider { + static var previews: some View { + let card = MemoryGame.Card(isFaceUp: true, isMatched: false, content: "🫡", id: 0) + CardView(card: card) + } +} diff --git a/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift b/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift new file mode 100644 index 0000000..086567a --- /dev/null +++ b/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift @@ -0,0 +1,29 @@ +// +// EmojiMemoryGame.swift +// Memorize +// +// Created by 임주민 on 2022/08/05. +// + +import Foundation + +class EmojiMemoryGame: ObservableObject { + + static let emojis = ["🚗", "🚕", "🚙", "🚌", "🚜", "🚚", "🚛", "🛻", "🚎", "🚐", "🚒", "🚑", "🚓", "🏎", "🚞", "🚆", "🚁", "🚀"] + + static func createEmojiMemoryGame() -> MemoryGame { + MemoryGame(numberOfPairsOfCard: 5) { pairIndex in emojis[pairIndex] } + } + + @Published private var model = createEmojiMemoryGame() + + var cards: [MemoryGame.Card] { + model.cards + } + + // MARK: - Intentions + + func choose(_ card: MemoryGame.Card) { + model.choose(card) + } +} From 98e5748a0275840b675458b641433fea91658141 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Sun, 7 Aug 2022 14:59:20 +0900 Subject: [PATCH 02/22] Make memorize game logic --- week1/Memorize/Memorize/ContentView.swift | 3 +-- .../AppIcon.appiconset/Contents.json | 5 ++++ .../Memorize/Memorize/Model/MemoryGame.swift | 26 ++++++++++++++++--- .../Memorize/View/SubViews/CardView.swift | 6 +++-- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/week1/Memorize/Memorize/ContentView.swift b/week1/Memorize/Memorize/ContentView.swift index 1192c44..be059c7 100644 --- a/week1/Memorize/Memorize/ContentView.swift +++ b/week1/Memorize/Memorize/ContentView.swift @@ -8,8 +8,7 @@ import SwiftUI struct ContentView: View { -// @State var countOfCards = 8 -// @State var contents = Emoji.vehicles.emojiList + @ObservedObject var viewModel: EmojiMemoryGame var body: some View { diff --git a/week2/Memorize/Memorize/Assets.xcassets/AppIcon.appiconset/Contents.json b/week2/Memorize/Memorize/Assets.xcassets/AppIcon.appiconset/Contents.json index 5a3257a..9221b9b 100644 --- a/week2/Memorize/Memorize/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/week2/Memorize/Memorize/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -70,6 +70,11 @@ "scale" : "2x", "size" : "40x40" }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, { "idiom" : "ipad", "scale" : "2x", diff --git a/week2/Memorize/Memorize/Model/MemoryGame.swift b/week2/Memorize/Memorize/Model/MemoryGame.swift index 1f4969d..31de3c4 100644 --- a/week2/Memorize/Memorize/Model/MemoryGame.swift +++ b/week2/Memorize/Memorize/Model/MemoryGame.swift @@ -7,9 +7,11 @@ import Foundation -struct MemoryGame { +struct MemoryGame { var cards: [Card] + private var firstChosenCardIndex: Int? + private var secondChosenCardIndex: Int? init(numberOfPairsOfCard: Int, createContent: (Int) -> CardContent) { cards = [Card]() @@ -21,14 +23,30 @@ struct MemoryGame { } mutating func choose(_ card: Card) { - if let chosenIndex = cards.firstIndex(where: { $0.id == card.id }) { - cards[chosenIndex].isFaceUp.toggle() + if let currentChosenIndex = cards.firstIndex(where: { $0.id == card.id }) { + cards[currentChosenIndex].isFaceUp.toggle() + if let prevChosenCardIndex = firstChosenCardIndex { + if let afterChosenCardIndex = secondChosenCardIndex { + if cards[afterChosenCardIndex].content == cards[prevChosenCardIndex].content { + cards[afterChosenCardIndex].isMatched = true + cards[prevChosenCardIndex].isMatched = true + } + cards[afterChosenCardIndex].isFaceUp.toggle() + cards[prevChosenCardIndex].isFaceUp.toggle() + secondChosenCardIndex = nil + firstChosenCardIndex = currentChosenIndex + } else { + secondChosenCardIndex = currentChosenIndex + } + } else { + firstChosenCardIndex = currentChosenIndex + } } } struct Card: Identifiable { - var isFaceUp = true + var isFaceUp = false var isMatched = false var content: CardContent var id: Int diff --git a/week2/Memorize/Memorize/View/SubViews/CardView.swift b/week2/Memorize/Memorize/View/SubViews/CardView.swift index c585c37..b4890a6 100644 --- a/week2/Memorize/Memorize/View/SubViews/CardView.swift +++ b/week2/Memorize/Memorize/View/SubViews/CardView.swift @@ -14,7 +14,9 @@ struct CardView: View { var body: some View { ZStack{ let shape = RoundedRectangle(cornerRadius: 15) - if card.isFaceUp { + if card.isMatched { + shape.hidden() + } else if card.isFaceUp { shape.strokeBorder(lineWidth: 5) Text(card.content).font(.system(size: 40)) } else { @@ -28,7 +30,7 @@ struct CardView: View { struct CardView_Previews: PreviewProvider { static var previews: some View { - let card = MemoryGame.Card(isFaceUp: true, isMatched: false, content: "🫡", id: 0) + let card = MemoryGame.Card(isFaceUp: true, isMatched: true, content: "🫡", id: 0) CardView(card: card) } } From e9bf9cd94318a38fb35b9fb13e3e4193f56ecb6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Sun, 7 Aug 2022 17:00:18 +0900 Subject: [PATCH 03/22] Add game state and done messge --- week1/Memorize/Memorize/ContentView.swift | 29 +++++++++++-------- .../Memorize/Memorize/Model/MemoryGame.swift | 27 ++++++++++++----- .../Memorize/Memorize/View/ContentView.swift | 29 ++++++++++++------- .../Memorize/View/SubViews/CardView.swift | 1 + .../Memorize/ViewModel/EmojiMemoryGame.swift | 7 ++++- 5 files changed, 62 insertions(+), 31 deletions(-) diff --git a/week1/Memorize/Memorize/ContentView.swift b/week1/Memorize/Memorize/ContentView.swift index be059c7..5f8c088 100644 --- a/week1/Memorize/Memorize/ContentView.swift +++ b/week1/Memorize/Memorize/ContentView.swift @@ -13,24 +13,29 @@ struct ContentView: View { var body: some View { VStack { - Text("Memorize!") - .font(.largeTitle) - .padding() - ScrollView(showsIndicators: false) { - LazyVGrid(columns: [GridItem(.adaptive(minimum: 80, maximum: 100))], spacing: 10) { - ForEach(viewModel.cards) { card in - CardView(card: card) - .onTapGesture{ - viewModel.choose(card) - } + + Text("Memorize!") + .font(.largeTitle) + .padding() + ScrollView(showsIndicators: false) { + LazyVGrid(columns: [GridItem(.adaptive(minimum: 80, maximum: 100))], spacing: 10) { + ForEach(viewModel.cards) { card in + CardView(card: card) + .onTapGesture{ + print("state") + print(viewModel.state) + viewModel.choose(card) + } + } } - } - }.padding() + }.padding() + } } } struct ContentView_Previews: PreviewProvider { + static var previews: some View { ContentView() } diff --git a/week2/Memorize/Memorize/Model/MemoryGame.swift b/week2/Memorize/Memorize/Model/MemoryGame.swift index 31de3c4..45e82b8 100644 --- a/week2/Memorize/Memorize/Model/MemoryGame.swift +++ b/week2/Memorize/Memorize/Model/MemoryGame.swift @@ -10,12 +10,19 @@ import Foundation struct MemoryGame { var cards: [Card] + var gameState: GameState = .playing private var firstChosenCardIndex: Int? private var secondChosenCardIndex: Int? + private var numberOfMatchedCard = 0 - init(numberOfPairsOfCard: Int, createContent: (Int) -> CardContent) { + enum GameState { + case playing + case done + } + + init(numberOfCardPairs: Int, createContent: (Int) -> CardContent) { cards = [Card]() - for pairIndex in 0.. { } mutating func choose(_ card: Card) { - if let currentChosenIndex = cards.firstIndex(where: { $0.id == card.id }) { - cards[currentChosenIndex].isFaceUp.toggle() + if let currentChosenCardIndex = cards.firstIndex(where: { $0.id == card.id }) { + cards[currentChosenCardIndex].isFaceUp.toggle() if let prevChosenCardIndex = firstChosenCardIndex { if let afterChosenCardIndex = secondChosenCardIndex { if cards[afterChosenCardIndex].content == cards[prevChosenCardIndex].content { cards[afterChosenCardIndex].isMatched = true cards[prevChosenCardIndex].isMatched = true + numberOfMatchedCard += 2 } cards[afterChosenCardIndex].isFaceUp.toggle() cards[prevChosenCardIndex].isFaceUp.toggle() secondChosenCardIndex = nil - firstChosenCardIndex = currentChosenIndex + firstChosenCardIndex = currentChosenCardIndex } else { - secondChosenCardIndex = currentChosenIndex + if (numberOfMatchedCard == cards.count-2) { + cards[currentChosenCardIndex].isMatched = true + cards[prevChosenCardIndex].isMatched = true + gameState = .done + } + secondChosenCardIndex = currentChosenCardIndex } } else { - firstChosenCardIndex = currentChosenIndex + firstChosenCardIndex = currentChosenCardIndex } } } diff --git a/week2/Memorize/Memorize/View/ContentView.swift b/week2/Memorize/Memorize/View/ContentView.swift index 1567bfa..2e290ab 100644 --- a/week2/Memorize/Memorize/View/ContentView.swift +++ b/week2/Memorize/Memorize/View/ContentView.swift @@ -13,24 +13,31 @@ struct ContentView: View { var body: some View { VStack { - Text("Memorize!") - .font(.largeTitle) - .padding() - ScrollView(showsIndicators: false) { - LazyVGrid(columns: [GridItem(.adaptive(minimum: 80, maximum: 100))], spacing: 10) { - ForEach(viewModel.cards) { card in - CardView(card: card) - .onTapGesture{ - viewModel.choose(card) - } + switch viewModel.state { + case .playing: + Text("Memorize!") + ScrollView(showsIndicators: false) { + LazyVGrid(columns: [GridItem(.adaptive(minimum: 80, maximum: 100))], spacing: 10) { + ForEach(viewModel.cards) { card in + CardView(card: card) + .onTapGesture{ + viewModel.choose(card) + } + } } } - }.padding() + case .done: + Text("Well Done!") + Text("You got Points!!") + } } + .font(.largeTitle) + .padding() } } struct ContentView_Previews: PreviewProvider { + static var previews: some View { ContentView(viewModel: EmojiMemoryGame()) } diff --git a/week2/Memorize/Memorize/View/SubViews/CardView.swift b/week2/Memorize/Memorize/View/SubViews/CardView.swift index b4890a6..3b1e315 100644 --- a/week2/Memorize/Memorize/View/SubViews/CardView.swift +++ b/week2/Memorize/Memorize/View/SubViews/CardView.swift @@ -29,6 +29,7 @@ struct CardView: View { } struct CardView_Previews: PreviewProvider { + static var previews: some View { let card = MemoryGame.Card(isFaceUp: true, isMatched: true, content: "🫡", id: 0) CardView(card: card) diff --git a/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift b/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift index 086567a..704269d 100644 --- a/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift +++ b/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift @@ -12,7 +12,7 @@ class EmojiMemoryGame: ObservableObject { static let emojis = ["🚗", "🚕", "🚙", "🚌", "🚜", "🚚", "🚛", "🛻", "🚎", "🚐", "🚒", "🚑", "🚓", "🏎", "🚞", "🚆", "🚁", "🚀"] static func createEmojiMemoryGame() -> MemoryGame { - MemoryGame(numberOfPairsOfCard: 5) { pairIndex in emojis[pairIndex] } + MemoryGame(numberOfCardPairs: 5) { pairIndex in emojis[pairIndex] } } @Published private var model = createEmojiMemoryGame() @@ -21,9 +21,14 @@ class EmojiMemoryGame: ObservableObject { model.cards } + var state: MemoryGame.GameState { + model.gameState + } + // MARK: - Intentions func choose(_ card: MemoryGame.Card) { + print(state) model.choose(card) } } From 8d0f3881f308e1eebea577dcf8d35ccb09f602fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Mon, 8 Aug 2022 20:12:27 +0900 Subject: [PATCH 04/22] Make point function and rename --- .../Memorize.xcodeproj/project.pbxproj | 4 ++ week2/Memorize/Memorize/MemorizeApp.swift | 2 +- .../Memorize/Memorize/Model/MemoryGame.swift | 52 ++++++++++--------- .../Memorize/Memorize/View/ContentView.swift | 17 +++--- .../Memorize/View/SubViews/ButtonTabbar.swift | 28 ++++++++++ .../Memorize/View/SubViews/CardView.swift | 4 +- .../Memorize/ViewModel/EmojiMemoryGame.swift | 22 +++++--- 7 files changed, 88 insertions(+), 41 deletions(-) create mode 100644 week2/Memorize/Memorize/View/SubViews/ButtonTabbar.swift diff --git a/week2/Memorize/Memorize.xcodeproj/project.pbxproj b/week2/Memorize/Memorize.xcodeproj/project.pbxproj index 14f0995..af3b684 100644 --- a/week2/Memorize/Memorize.xcodeproj/project.pbxproj +++ b/week2/Memorize/Memorize.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 6F8BD4F6289FA933003F9AF8 /* ButtonTabbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F8BD4F5289FA933003F9AF8 /* ButtonTabbar.swift */; }; 6FC460F72892A6D1003DA739 /* MemorizeApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FC460F62892A6D1003DA739 /* MemorizeApp.swift */; }; 6FC460F92892A6D1003DA739 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FC460F82892A6D1003DA739 /* ContentView.swift */; }; 6FC460FB2892A6D4003DA739 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FC460FA2892A6D4003DA739 /* Assets.xcassets */; }; @@ -17,6 +18,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 6F8BD4F5289FA933003F9AF8 /* ButtonTabbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonTabbar.swift; sourceTree = ""; }; 6FC460F32892A6D1003DA739 /* Memorize.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Memorize.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6FC460F62892A6D1003DA739 /* MemorizeApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemorizeApp.swift; sourceTree = ""; }; 6FC460F82892A6D1003DA739 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -42,6 +44,7 @@ isa = PBXGroup; children = ( 6FC461062892BC27003DA739 /* CardView.swift */, + 6F8BD4F5289FA933003F9AF8 /* ButtonTabbar.swift */, ); path = SubViews; sourceTree = ""; @@ -178,6 +181,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 6F8BD4F6289FA933003F9AF8 /* ButtonTabbar.swift in Sources */, 6FC460F92892A6D1003DA739 /* ContentView.swift in Sources */, 6FC461072892BC27003DA739 /* CardView.swift in Sources */, 6FE0DA9F289CE2A3000A93C5 /* MemoryGame.swift in Sources */, diff --git a/week2/Memorize/Memorize/MemorizeApp.swift b/week2/Memorize/Memorize/MemorizeApp.swift index 975b7bc..cd0841d 100644 --- a/week2/Memorize/Memorize/MemorizeApp.swift +++ b/week2/Memorize/Memorize/MemorizeApp.swift @@ -12,7 +12,7 @@ struct MemorizeApp: App { var body: some Scene { WindowGroup { - ContentView(viewModel: EmojiMemoryGame()) + ContentView(memoryGameManager: EmojiMemoryGame()) } } } diff --git a/week2/Memorize/Memorize/Model/MemoryGame.swift b/week2/Memorize/Memorize/Model/MemoryGame.swift index 45e82b8..8b4e1d7 100644 --- a/week2/Memorize/Memorize/Model/MemoryGame.swift +++ b/week2/Memorize/Memorize/Model/MemoryGame.swift @@ -7,17 +7,16 @@ import Foundation -struct MemoryGame { +struct MemoryGame where CardContent: Equatable { - var cards: [Card] - var gameState: GameState = .playing - private var firstChosenCardIndex: Int? - private var secondChosenCardIndex: Int? + private(set) var cards: [Card] + private(set) var gameState: GameState = .playing + private(set) var point = 0 + private var prevChosenCardIndex: Int? private var numberOfMatchedCard = 0 enum GameState { - case playing - case done + case playing, done } init(numberOfCardPairs: Int, createContent: (Int) -> CardContent) { @@ -27,32 +26,34 @@ struct MemoryGame { cards.append(Card(content: content, id: 2 * pairIndex)) cards.append(Card(content: content, id: 2 * pairIndex + 1)) } + cards.shuffle() } mutating func choose(_ card: Card) { if let currentChosenCardIndex = cards.firstIndex(where: { $0.id == card.id }) { - cards[currentChosenCardIndex].isFaceUp.toggle() - if let prevChosenCardIndex = firstChosenCardIndex { - if let afterChosenCardIndex = secondChosenCardIndex { - if cards[afterChosenCardIndex].content == cards[prevChosenCardIndex].content { - cards[afterChosenCardIndex].isMatched = true - cards[prevChosenCardIndex].isMatched = true - numberOfMatchedCard += 2 - } - cards[afterChosenCardIndex].isFaceUp.toggle() - cards[prevChosenCardIndex].isFaceUp.toggle() - secondChosenCardIndex = nil - firstChosenCardIndex = currentChosenCardIndex - } else { - if (numberOfMatchedCard == cards.count-2) { + if cards[currentChosenCardIndex].isAlreadyOpened == true { + point -= 1 + } + if let alreadyChosenCardIndex = prevChosenCardIndex { + if cards[currentChosenCardIndex].id != cards[alreadyChosenCardIndex].id { + if cards[currentChosenCardIndex].content == cards[alreadyChosenCardIndex].content { cards[currentChosenCardIndex].isMatched = true - cards[prevChosenCardIndex].isMatched = true - gameState = .done + cards[alreadyChosenCardIndex].isMatched = true + numberOfMatchedCard += 2 + point += 3 } - secondChosenCardIndex = currentChosenCardIndex + prevChosenCardIndex = nil } } else { - firstChosenCardIndex = currentChosenCardIndex + for index in 0.. { var isFaceUp = false var isMatched = false + var isAlreadyOpened = false var content: CardContent var id: Int } diff --git a/week2/Memorize/Memorize/View/ContentView.swift b/week2/Memorize/Memorize/View/ContentView.swift index 2e290ab..e514b35 100644 --- a/week2/Memorize/Memorize/View/ContentView.swift +++ b/week2/Memorize/Memorize/View/ContentView.swift @@ -9,29 +9,34 @@ import SwiftUI struct ContentView: View { - @ObservedObject var viewModel: EmojiMemoryGame + @ObservedObject var memoryGameManager: EmojiMemoryGame var body: some View { VStack { - switch viewModel.state { + switch memoryGameManager.state { case .playing: Text("Memorize!") + Text("point : \(memoryGameManager.point)").font(.body) ScrollView(showsIndicators: false) { LazyVGrid(columns: [GridItem(.adaptive(minimum: 80, maximum: 100))], spacing: 10) { - ForEach(viewModel.cards) { card in + ForEach(memoryGameManager.cards) { card in CardView(card: card) .onTapGesture{ - viewModel.choose(card) + memoryGameManager.choose(card) + print(card) } } } } case .done: Text("Well Done!") - Text("You got Points!!") + Text("You got \(memoryGameManager.point) Points!!") } + ButtonTabbar(memoryGameManager: EmojiMemoryGame()) + .padding() } .font(.largeTitle) + .accentColor(.pink) .padding() } } @@ -39,6 +44,6 @@ struct ContentView: View { struct ContentView_Previews: PreviewProvider { static var previews: some View { - ContentView(viewModel: EmojiMemoryGame()) + ContentView(memoryGameManager: EmojiMemoryGame()) } } diff --git a/week2/Memorize/Memorize/View/SubViews/ButtonTabbar.swift b/week2/Memorize/Memorize/View/SubViews/ButtonTabbar.swift new file mode 100644 index 0000000..07252a5 --- /dev/null +++ b/week2/Memorize/Memorize/View/SubViews/ButtonTabbar.swift @@ -0,0 +1,28 @@ +// +// ButtonTabbar.swift +// Memorize +// +// Created by 임주민 on 2022/08/07. +// + +import SwiftUI + +struct ButtonTabbar: View { + + @ObservedObject var memoryGameManager: EmojiMemoryGame + + var body: some View { + Button { + memoryGameManager.newGame() + } label: { + Text("New Game") + .font(.body) + } + } +} + +struct ButtonTabbar_Previews: PreviewProvider { + static var previews: some View { + ButtonTabbar(memoryGameManager: EmojiMemoryGame()) + } +} diff --git a/week2/Memorize/Memorize/View/SubViews/CardView.swift b/week2/Memorize/Memorize/View/SubViews/CardView.swift index 3b1e315..edb0aab 100644 --- a/week2/Memorize/Memorize/View/SubViews/CardView.swift +++ b/week2/Memorize/Memorize/View/SubViews/CardView.swift @@ -15,7 +15,7 @@ struct CardView: View { ZStack{ let shape = RoundedRectangle(cornerRadius: 15) if card.isMatched { - shape.hidden() + shape.fill().opacity(0) } else if card.isFaceUp { shape.strokeBorder(lineWidth: 5) Text(card.content).font(.system(size: 40)) @@ -31,7 +31,7 @@ struct CardView: View { struct CardView_Previews: PreviewProvider { static var previews: some View { - let card = MemoryGame.Card(isFaceUp: true, isMatched: true, content: "🫡", id: 0) + let card = MemoryGame.Card(isFaceUp: true, isMatched: false, content: "🫡", id: 0) CardView(card: card) } } diff --git a/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift b/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift index 704269d..0582a7c 100644 --- a/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift +++ b/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift @@ -9,26 +9,34 @@ import Foundation class EmojiMemoryGame: ObservableObject { - static let emojis = ["🚗", "🚕", "🚙", "🚌", "🚜", "🚚", "🚛", "🛻", "🚎", "🚐", "🚒", "🚑", "🚓", "🏎", "🚞", "🚆", "🚁", "🚀"] + private static let emojis = ["🚗", "🚕", "🚙", "🚌", "🚜", "🚚", "🚛", "🛻", "🚎", "🚐", "🚒", "🚑", "🚓", "🏎", "🚞", "🚆", "🚁", "🚀"] - static func createEmojiMemoryGame() -> MemoryGame { + private static func createEmojiMemoryGame() -> MemoryGame { MemoryGame(numberOfCardPairs: 5) { pairIndex in emojis[pairIndex] } } - @Published private var model = createEmojiMemoryGame() + @Published private var memoryGameModel = createEmojiMemoryGame() var cards: [MemoryGame.Card] { - model.cards + memoryGameModel.cards } var state: MemoryGame.GameState { - model.gameState + memoryGameModel.gameState } + var point: Int { + memoryGameModel.point + } + + // MARK: - Intentions func choose(_ card: MemoryGame.Card) { - print(state) - model.choose(card) + memoryGameModel.choose(card) + } + + func newGame() { + memoryGameModel = EmojiMemoryGame.createEmojiMemoryGame() } } From c719368c48c819acecf44a275629017b5596b931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Mon, 8 Aug 2022 20:39:54 +0900 Subject: [PATCH 05/22] Add theme model --- .../Memorize.xcodeproj/project.pbxproj | 4 ++ .../Memorize/Memorize/Model/EmojiTheme.swift | 52 +++++++++++++++++++ .../Memorize/View/SubViews/ButtonTabbar.swift | 1 + .../Memorize/ViewModel/EmojiMemoryGame.swift | 4 +- 4 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 week2/Memorize/Memorize/Model/EmojiTheme.swift diff --git a/week2/Memorize/Memorize.xcodeproj/project.pbxproj b/week2/Memorize/Memorize.xcodeproj/project.pbxproj index af3b684..be01b29 100644 --- a/week2/Memorize/Memorize.xcodeproj/project.pbxproj +++ b/week2/Memorize/Memorize.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 6F89DC0C28A1274A00C73BBE /* EmojiTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F89DC0B28A1274A00C73BBE /* EmojiTheme.swift */; }; 6F8BD4F6289FA933003F9AF8 /* ButtonTabbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F8BD4F5289FA933003F9AF8 /* ButtonTabbar.swift */; }; 6FC460F72892A6D1003DA739 /* MemorizeApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FC460F62892A6D1003DA739 /* MemorizeApp.swift */; }; 6FC460F92892A6D1003DA739 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FC460F82892A6D1003DA739 /* ContentView.swift */; }; @@ -18,6 +19,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 6F89DC0B28A1274A00C73BBE /* EmojiTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiTheme.swift; sourceTree = ""; }; 6F8BD4F5289FA933003F9AF8 /* ButtonTabbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonTabbar.swift; sourceTree = ""; }; 6FC460F32892A6D1003DA739 /* Memorize.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Memorize.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6FC460F62892A6D1003DA739 /* MemorizeApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemorizeApp.swift; sourceTree = ""; }; @@ -90,6 +92,7 @@ isa = PBXGroup; children = ( 6FE0DA9E289CE2A3000A93C5 /* MemoryGame.swift */, + 6F89DC0B28A1274A00C73BBE /* EmojiTheme.swift */, ); path = Model; sourceTree = ""; @@ -182,6 +185,7 @@ buildActionMask = 2147483647; files = ( 6F8BD4F6289FA933003F9AF8 /* ButtonTabbar.swift in Sources */, + 6F89DC0C28A1274A00C73BBE /* EmojiTheme.swift in Sources */, 6FC460F92892A6D1003DA739 /* ContentView.swift in Sources */, 6FC461072892BC27003DA739 /* CardView.swift in Sources */, 6FE0DA9F289CE2A3000A93C5 /* MemoryGame.swift in Sources */, diff --git a/week2/Memorize/Memorize/Model/EmojiTheme.swift b/week2/Memorize/Memorize/Model/EmojiTheme.swift new file mode 100644 index 0000000..3c86b17 --- /dev/null +++ b/week2/Memorize/Memorize/Model/EmojiTheme.swift @@ -0,0 +1,52 @@ +// +// EmojiTheme.swift +// Memorize +// +// Created by 임주민 on 2022/08/08. +// + +import Foundation +import SwiftUI + +enum EmojiTheme { + case vehicles + case faces + case sports + case hearts + case flags + case foods + + var content: [String] { + switch self { + case .vehicles: + return ["🚗", "🚕", "🚙", "🚌", "🚜", "🚚", "🚛", "🛻", "🚎", "🚐", "🚒", "🚑", "🚓", "🏎", "🚞"] + case .faces: + return ["😓", "🫡", "🫥", "😡", "😈", "💀", "👹" ,"💩", "👻", "🤖", "🎃"] + case .sports: + return ["⚽️", "⚾️", "🏈", "🏀", "🥎", "🎾", "🏐", "🏉", "🥏", "🎱", "🪀", "🏓", "🏸", "🏒", "🏑", "🥍"] + case .hearts: + return ["❤️", "🧡", "💛", "💔", "❤️‍🔥", "❤️‍🩹", "💚", "💙", "💝", "💖", "💜", "🤍", "🖤"] + case .flags: + return ["🏴󠁧󠁢󠁳󠁣󠁴󠁿", "🇧🇬", "🇮🇸", "🇰🇷", "🇭🇰", "🇵🇷", "🇩🇰", "🇦🇪", "🇯🇵", "🇲🇱", "🇺🇸", "🇬🇧", "🇨🇳" ,"🇵🇭", "🇨🇦", "🇹🇷"] + case .foods: + return ["🍟", "🍮", "🍡", "🍭", "🥐", "🥝", "🍬", "🍅", "🍋", "🍇", "🍓", "🌽"] + } + } + + var color: Color { + switch self { + case .vehicles: + return .pink + case .faces: + return .yellow + case .sports: + return .orange + case .hearts: + return .green + case .flags: + return .black + case .foods: + return .blue + } + } +} diff --git a/week2/Memorize/Memorize/View/SubViews/ButtonTabbar.swift b/week2/Memorize/Memorize/View/SubViews/ButtonTabbar.swift index 07252a5..a3c0a42 100644 --- a/week2/Memorize/Memorize/View/SubViews/ButtonTabbar.swift +++ b/week2/Memorize/Memorize/View/SubViews/ButtonTabbar.swift @@ -22,6 +22,7 @@ struct ButtonTabbar: View { } struct ButtonTabbar_Previews: PreviewProvider { + static var previews: some View { ButtonTabbar(memoryGameManager: EmojiMemoryGame()) } diff --git a/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift b/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift index 0582a7c..cb0b4db 100644 --- a/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift +++ b/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift @@ -9,10 +9,8 @@ import Foundation class EmojiMemoryGame: ObservableObject { - private static let emojis = ["🚗", "🚕", "🚙", "🚌", "🚜", "🚚", "🚛", "🛻", "🚎", "🚐", "🚒", "🚑", "🚓", "🏎", "🚞", "🚆", "🚁", "🚀"] - private static func createEmojiMemoryGame() -> MemoryGame { - MemoryGame(numberOfCardPairs: 5) { pairIndex in emojis[pairIndex] } + MemoryGame(numberOfCardPairs: 5) { pairIndex in EmojiTheme.vehicles.content[pairIndex] } } @Published private var memoryGameModel = createEmojiMemoryGame() From 6a36c5352bb399f1dbaa46fa316bb375a4e36fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Sat, 13 Aug 2022 03:40:28 +0900 Subject: [PATCH 06/22] Add theme change feature --- .../Memorize.xcodeproj/project.pbxproj | 12 +++++ week2/Memorize/Memorize/MemorizeApp.swift | 4 +- .../Memorize/Memorize/Model/EmojiTheme.swift | 36 +++++++++++---- .../Memorize/Memorize/Model/MemoryGame.swift | 46 ++++++++++--------- .../Memorize/Memorize/View/ContentView.swift | 31 ++++--------- .../Memorize/View/SubViews/ButtonTabbar.swift | 3 +- .../Memorize/View/SubViews/CardSetView.swift | 34 ++++++++++++++ .../Memorize/View/SubViews/CardView.swift | 7 ++- .../Memorize/View/SubViews/TopInfoView.swift | 28 +++++++++++ .../Memorize/View/SubViews/WellDoneView.swift | 27 +++++++++++ .../Memorize/ViewModel/EmojiMemoryGame.swift | 33 +++++++++++-- 11 files changed, 198 insertions(+), 63 deletions(-) create mode 100644 week2/Memorize/Memorize/View/SubViews/CardSetView.swift create mode 100644 week2/Memorize/Memorize/View/SubViews/TopInfoView.swift create mode 100644 week2/Memorize/Memorize/View/SubViews/WellDoneView.swift diff --git a/week2/Memorize/Memorize.xcodeproj/project.pbxproj b/week2/Memorize/Memorize.xcodeproj/project.pbxproj index be01b29..290c9f0 100644 --- a/week2/Memorize/Memorize.xcodeproj/project.pbxproj +++ b/week2/Memorize/Memorize.xcodeproj/project.pbxproj @@ -16,6 +16,9 @@ 6FC461072892BC27003DA739 /* CardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FC461062892BC27003DA739 /* CardView.swift */; }; 6FE0DA9F289CE2A3000A93C5 /* MemoryGame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FE0DA9E289CE2A3000A93C5 /* MemoryGame.swift */; }; 6FE0DAA1289CECCE000A93C5 /* EmojiMemoryGame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FE0DAA0289CECCE000A93C5 /* EmojiMemoryGame.swift */; }; + 6FFE685F28A6C68900F3FB66 /* WellDoneView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFE685E28A6C68900F3FB66 /* WellDoneView.swift */; }; + 6FFE686128A6C81E00F3FB66 /* TopInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFE686028A6C81E00F3FB66 /* TopInfoView.swift */; }; + 6FFE686328A6C89200F3FB66 /* CardSetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFE686228A6C89200F3FB66 /* CardSetView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -29,6 +32,9 @@ 6FC461062892BC27003DA739 /* CardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardView.swift; sourceTree = ""; }; 6FE0DA9E289CE2A3000A93C5 /* MemoryGame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryGame.swift; sourceTree = ""; }; 6FE0DAA0289CECCE000A93C5 /* EmojiMemoryGame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiMemoryGame.swift; sourceTree = ""; }; + 6FFE685E28A6C68900F3FB66 /* WellDoneView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WellDoneView.swift; sourceTree = ""; }; + 6FFE686028A6C81E00F3FB66 /* TopInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopInfoView.swift; sourceTree = ""; }; + 6FFE686228A6C89200F3FB66 /* CardSetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardSetView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -45,8 +51,11 @@ 6F347EB02893DAA9005329DE /* SubViews */ = { isa = PBXGroup; children = ( + 6FFE686028A6C81E00F3FB66 /* TopInfoView.swift */, 6FC461062892BC27003DA739 /* CardView.swift */, + 6FFE685E28A6C68900F3FB66 /* WellDoneView.swift */, 6F8BD4F5289FA933003F9AF8 /* ButtonTabbar.swift */, + 6FFE686228A6C89200F3FB66 /* CardSetView.swift */, ); path = SubViews; sourceTree = ""; @@ -186,8 +195,11 @@ files = ( 6F8BD4F6289FA933003F9AF8 /* ButtonTabbar.swift in Sources */, 6F89DC0C28A1274A00C73BBE /* EmojiTheme.swift in Sources */, + 6FFE685F28A6C68900F3FB66 /* WellDoneView.swift in Sources */, 6FC460F92892A6D1003DA739 /* ContentView.swift in Sources */, 6FC461072892BC27003DA739 /* CardView.swift in Sources */, + 6FFE686328A6C89200F3FB66 /* CardSetView.swift in Sources */, + 6FFE686128A6C81E00F3FB66 /* TopInfoView.swift in Sources */, 6FE0DA9F289CE2A3000A93C5 /* MemoryGame.swift in Sources */, 6FC460F72892A6D1003DA739 /* MemorizeApp.swift in Sources */, 6FE0DAA1289CECCE000A93C5 /* EmojiMemoryGame.swift in Sources */, diff --git a/week2/Memorize/Memorize/MemorizeApp.swift b/week2/Memorize/Memorize/MemorizeApp.swift index cd0841d..d2a6c60 100644 --- a/week2/Memorize/Memorize/MemorizeApp.swift +++ b/week2/Memorize/Memorize/MemorizeApp.swift @@ -10,9 +10,11 @@ import SwiftUI @main struct MemorizeApp: App { + let memoryGameManager = EmojiMemoryGame() + var body: some Scene { WindowGroup { - ContentView(memoryGameManager: EmojiMemoryGame()) + ContentView(memoryGameManager: memoryGameManager) } } } diff --git a/week2/Memorize/Memorize/Model/EmojiTheme.swift b/week2/Memorize/Memorize/Model/EmojiTheme.swift index 3c86b17..658f2e3 100644 --- a/week2/Memorize/Memorize/Model/EmojiTheme.swift +++ b/week2/Memorize/Memorize/Model/EmojiTheme.swift @@ -6,9 +6,8 @@ // import Foundation -import SwiftUI -enum EmojiTheme { +enum EmojiTheme: String, CaseIterable { case vehicles case faces case sports @@ -23,7 +22,7 @@ enum EmojiTheme { case .faces: return ["😓", "🫡", "🫥", "😡", "😈", "💀", "👹" ,"💩", "👻", "🤖", "🎃"] case .sports: - return ["⚽️", "⚾️", "🏈", "🏀", "🥎", "🎾", "🏐", "🏉", "🥏", "🎱", "🪀", "🏓", "🏸", "🏒", "🏑", "🥍"] + return ["⚽️", "⚾️", "🏈", "🏀", "🏓"] case .hearts: return ["❤️", "🧡", "💛", "💔", "❤️‍🔥", "❤️‍🩹", "💚", "💙", "💝", "💖", "💜", "🤍", "🖤"] case .flags: @@ -33,20 +32,37 @@ enum EmojiTheme { } } - var color: Color { + var color: String { switch self { case .vehicles: - return .pink + return "pink" case .faces: - return .yellow + return "yellow" case .sports: - return .orange + return "orange" case .hearts: - return .green + return "green" case .flags: - return .black + return "black" case .foods: - return .blue + return "blue" + } + } + + var numberOfCardPairsToShow: Int { + switch self { + case .vehicles: + return 5 + case .faces: + return 6 + case .sports: + return 6 + case .hearts: + return 4 + case .flags: + return 6 + case .foods: + return 5 } } } diff --git a/week2/Memorize/Memorize/Model/MemoryGame.swift b/week2/Memorize/Memorize/Model/MemoryGame.swift index 8b4e1d7..4dd7d5e 100644 --- a/week2/Memorize/Memorize/Model/MemoryGame.swift +++ b/week2/Memorize/Memorize/Model/MemoryGame.swift @@ -10,13 +10,14 @@ import Foundation struct MemoryGame where CardContent: Equatable { private(set) var cards: [Card] - private(set) var gameState: GameState = .playing private(set) var point = 0 - private var prevChosenCardIndex: Int? private var numberOfMatchedCard = 0 - - enum GameState { - case playing, done + private var prevChosenCardIndex: Int? { + get { cards.indices.filter({cards[$0].isFaceUp}).oneAndOnly } + set { cards.indices.forEach{cards[$0].isFaceUp = ($0 == newValue)} } + } + var isGameDone: Bool { + numberOfMatchedCard == cards.count } init(numberOfCardPairs: Int, createContent: (Int) -> CardContent) { @@ -31,30 +32,24 @@ struct MemoryGame where CardContent: Equatable { mutating func choose(_ card: Card) { if let currentChosenCardIndex = cards.firstIndex(where: { $0.id == card.id }) { - if cards[currentChosenCardIndex].isAlreadyOpened == true { + if cards[currentChosenCardIndex].isFaceUpAtLeastOne == true { point -= 1 } if let alreadyChosenCardIndex = prevChosenCardIndex { - if cards[currentChosenCardIndex].id != cards[alreadyChosenCardIndex].id { - if cards[currentChosenCardIndex].content == cards[alreadyChosenCardIndex].content { - cards[currentChosenCardIndex].isMatched = true - cards[alreadyChosenCardIndex].isMatched = true - numberOfMatchedCard += 2 - point += 3 - } - prevChosenCardIndex = nil + if cards[currentChosenCardIndex].id != cards[alreadyChosenCardIndex].id, + cards[currentChosenCardIndex].content == cards[alreadyChosenCardIndex].content { + cards[currentChosenCardIndex].isMatched = true + cards[alreadyChosenCardIndex].isMatched = true + numberOfMatchedCard += 2 + point += 3 } } else { for index in 0.. where CardContent: Equatable { var isFaceUp = false var isMatched = false - var isAlreadyOpened = false - var content: CardContent - var id: Int + var isFaceUpAtLeastOne = false + let content: CardContent + let id: Int + } +} + +extension Array{ + + var oneAndOnly: Element?{ + return self.count == 1 ? self.first : nil } } diff --git a/week2/Memorize/Memorize/View/ContentView.swift b/week2/Memorize/Memorize/View/ContentView.swift index e514b35..e478649 100644 --- a/week2/Memorize/Memorize/View/ContentView.swift +++ b/week2/Memorize/Memorize/View/ContentView.swift @@ -13,30 +13,16 @@ struct ContentView: View { var body: some View { VStack { - switch memoryGameManager.state { - case .playing: - Text("Memorize!") - Text("point : \(memoryGameManager.point)").font(.body) - ScrollView(showsIndicators: false) { - LazyVGrid(columns: [GridItem(.adaptive(minimum: 80, maximum: 100))], spacing: 10) { - ForEach(memoryGameManager.cards) { card in - CardView(card: card) - .onTapGesture{ - memoryGameManager.choose(card) - print(card) - } - } - } - } - case .done: - Text("Well Done!") - Text("You got \(memoryGameManager.point) Points!!") + if memoryGameManager.isGameDone { + WellDoneView(point: memoryGameManager.point) + } else { + TopInfoView(title: memoryGameManager.title, point: memoryGameManager.point) + CardSetView(memoryGameManager: memoryGameManager) } - ButtonTabbar(memoryGameManager: EmojiMemoryGame()) - .padding() + ButtonTabbar(memoryGameManager: memoryGameManager) } .font(.largeTitle) - .accentColor(.pink) + .foregroundColor(memoryGameManager.themecolor) .padding() } } @@ -44,6 +30,7 @@ struct ContentView: View { struct ContentView_Previews: PreviewProvider { static var previews: some View { - ContentView(memoryGameManager: EmojiMemoryGame()) + let memoryGameManager = EmojiMemoryGame() + ContentView(memoryGameManager: memoryGameManager) } } diff --git a/week2/Memorize/Memorize/View/SubViews/ButtonTabbar.swift b/week2/Memorize/Memorize/View/SubViews/ButtonTabbar.swift index a3c0a42..68c0e04 100644 --- a/week2/Memorize/Memorize/View/SubViews/ButtonTabbar.swift +++ b/week2/Memorize/Memorize/View/SubViews/ButtonTabbar.swift @@ -24,6 +24,7 @@ struct ButtonTabbar: View { struct ButtonTabbar_Previews: PreviewProvider { static var previews: some View { - ButtonTabbar(memoryGameManager: EmojiMemoryGame()) + let memoryGameManager = EmojiMemoryGame() + ButtonTabbar(memoryGameManager: memoryGameManager) } } diff --git a/week2/Memorize/Memorize/View/SubViews/CardSetView.swift b/week2/Memorize/Memorize/View/SubViews/CardSetView.swift new file mode 100644 index 0000000..cf26f47 --- /dev/null +++ b/week2/Memorize/Memorize/View/SubViews/CardSetView.swift @@ -0,0 +1,34 @@ +// +// CardSetView.swift +// Memorize +// +// Created by 임주민 on 2022/08/13. +// + +import SwiftUI + +struct CardSetView: View { + + @ObservedObject var memoryGameManager: EmojiMemoryGame + + var body: some View { + ScrollView(showsIndicators: false) { + LazyVGrid(columns: [GridItem(.adaptive(minimum: 80, maximum: 100))], spacing: 10) { + ForEach(memoryGameManager.cards) { card in + CardView(card: card) + .onTapGesture{ + memoryGameManager.choose(card) + } + } + } + } + } +} + +struct CardSetView_Previews: PreviewProvider { + + static var previews: some View { + let memoryGameManager = EmojiMemoryGame() + CardSetView(memoryGameManager: memoryGameManager) + } +} diff --git a/week2/Memorize/Memorize/View/SubViews/CardView.swift b/week2/Memorize/Memorize/View/SubViews/CardView.swift index edb0aab..4b69c65 100644 --- a/week2/Memorize/Memorize/View/SubViews/CardView.swift +++ b/week2/Memorize/Memorize/View/SubViews/CardView.swift @@ -14,16 +14,15 @@ struct CardView: View { var body: some View { ZStack{ let shape = RoundedRectangle(cornerRadius: 15) - if card.isMatched { - shape.fill().opacity(0) - } else if card.isFaceUp { + if card.isFaceUp { shape.strokeBorder(lineWidth: 5) Text(card.content).font(.system(size: 40)) + } else if card.isMatched { + shape.fill().opacity(0) } else { shape.fill() } } - .foregroundColor(Color.red) .aspectRatio(2 / 3, contentMode: .fit) } } diff --git a/week2/Memorize/Memorize/View/SubViews/TopInfoView.swift b/week2/Memorize/Memorize/View/SubViews/TopInfoView.swift new file mode 100644 index 0000000..e10ec6a --- /dev/null +++ b/week2/Memorize/Memorize/View/SubViews/TopInfoView.swift @@ -0,0 +1,28 @@ +// +// TopInfoView.swift +// Memorize +// +// Created by 임주민 on 2022/08/13. +// + +import SwiftUI + +struct TopInfoView: View { + + let title: String + let point: Int + + var body: some View { + VStack{ + Text(title) + Text("point : \(point)").font(.body) + } + } +} + +struct TopInfoView_Previews: PreviewProvider { + + static var previews: some View { + TopInfoView(title: "Title", point: 0) + } +} diff --git a/week2/Memorize/Memorize/View/SubViews/WellDoneView.swift b/week2/Memorize/Memorize/View/SubViews/WellDoneView.swift new file mode 100644 index 0000000..b75905e --- /dev/null +++ b/week2/Memorize/Memorize/View/SubViews/WellDoneView.swift @@ -0,0 +1,27 @@ +// +// WellDoneView.swift +// Memorize +// +// Created by 임주민 on 2022/08/13. +// + +import SwiftUI + +struct WellDoneView: View { + + let point: Int + + var body: some View { + VStack{ + Text("Well Done!") + Text("You got \(point) Points!!") + } + } +} + +struct WellDoneView_Previews: PreviewProvider { + + static var previews: some View { + WellDoneView(point: 0) + } +} diff --git a/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift b/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift index cb0b4db..e81c414 100644 --- a/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift +++ b/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift @@ -6,27 +6,53 @@ // import Foundation +import SwiftUI class EmojiMemoryGame: ObservableObject { private static func createEmojiMemoryGame() -> MemoryGame { - MemoryGame(numberOfCardPairs: 5) { pairIndex in EmojiTheme.vehicles.content[pairIndex] } + let numberOfCardPairs = theme.numberOfCardPairsToShow > theme.content.count ? theme.content.count : theme.numberOfCardPairsToShow + return MemoryGame(numberOfCardPairs: numberOfCardPairs) { pairIndex in theme.content[pairIndex] } } @Published private var memoryGameModel = createEmojiMemoryGame() + static var theme = EmojiTheme.allCases.randomElement() ?? .vehicles var cards: [MemoryGame.Card] { memoryGameModel.cards } - var state: MemoryGame.GameState { - memoryGameModel.gameState + var isGameDone: Bool { + memoryGameModel.isGameDone } var point: Int { memoryGameModel.point } + var title: String { + let firstUppercasedTitle = EmojiMemoryGame.theme.rawValue.prefix(1).uppercased() + EmojiMemoryGame.theme.rawValue.dropFirst() + return firstUppercasedTitle + } + + var themecolor: Color { + switch EmojiMemoryGame.theme.color { + case "pink": + return .pink + case "yellow": + return .yellow + case "orange": + return .orange + case "green": + return .green + case "black": + return .black + case "blue": + return .blue + default: + return .pink + } + } // MARK: - Intentions @@ -35,6 +61,7 @@ class EmojiMemoryGame: ObservableObject { } func newGame() { + EmojiMemoryGame.theme = EmojiTheme.allCases.randomElement()! memoryGameModel = EmojiMemoryGame.createEmojiMemoryGame() } } From 057aaaf0ff730e378b6b2a7792a49ead2aa86d2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Sat, 20 Aug 2022 14:38:07 +0900 Subject: [PATCH 07/22] Fix code by feedback --- week1/Memorize/Memorize/ContentView.swift | 31 +++++------ week1/Memorize/Memorize/Data/EmojiData.swift | 9 +--- .../Memorize/SubViews/ButtonTabbar.swift | 2 + .../Memorize/Memorize/SubViews/CardView.swift | 2 + .../Memorize.xcodeproj/project.pbxproj | 4 ++ .../Memorize/Memorize/Model/EmojiTheme.swift | 25 ++++++--- .../Memorize/Memorize/Model/MemoryGame.swift | 10 ++-- .../Memorize/View/SubViews/CardSetView.swift | 2 +- .../Memorize/View/SubViews/TopInfoView.swift | 2 +- .../Memorize/View/SubViews/WellDoneView.swift | 2 +- .../Memorize/Memorize/View/SwiftUIView.swift | 54 +++++++++++++++++++ .../Memorize/ViewModel/EmojiMemoryGame.swift | 18 +++---- 12 files changed, 110 insertions(+), 51 deletions(-) create mode 100644 week2/Memorize/Memorize/View/SwiftUIView.swift diff --git a/week1/Memorize/Memorize/ContentView.swift b/week1/Memorize/Memorize/ContentView.swift index 5f8c088..2efd8b5 100644 --- a/week1/Memorize/Memorize/ContentView.swift +++ b/week1/Memorize/Memorize/ContentView.swift @@ -9,33 +9,28 @@ import SwiftUI struct ContentView: View { - @ObservedObject var viewModel: EmojiMemoryGame + @State var countOfCards = 8 + @State var contents = Emoji.vehicles.emojiList var body: some View { VStack { - - Text("Memorize!") - .font(.largeTitle) - .padding() - ScrollView(showsIndicators: false) { - LazyVGrid(columns: [GridItem(.adaptive(minimum: 80, maximum: 100))], spacing: 10) { - ForEach(viewModel.cards) { card in - CardView(card: card) - .onTapGesture{ - print("state") - print(viewModel.state) - viewModel.choose(card) - } - } + Text("Memorize!") + .font(.largeTitle) + .padding() + ScrollView(showsIndicators: false) { + LazyVGrid(columns: [GridItem(.adaptive(minimum: 80, maximum: 100))], spacing: 10) { + ForEach(0.. where CardContent: Equatable { private(set) var cards: [Card] private(set) var point = 0 private var numberOfMatchedCard = 0 - private var prevChosenCardIndex: Int? { + private var previousChosenCardIndex: Int? { get { cards.indices.filter({cards[$0].isFaceUp}).oneAndOnly } set { cards.indices.forEach{cards[$0].isFaceUp = ($0 == newValue)} } } @@ -32,10 +32,10 @@ struct MemoryGame where CardContent: Equatable { mutating func choose(_ card: Card) { if let currentChosenCardIndex = cards.firstIndex(where: { $0.id == card.id }) { - if cards[currentChosenCardIndex].isFaceUpAtLeastOne == true { + if cards[currentChosenCardIndex].isFaceUpAtLeastOne { point -= 1 } - if let alreadyChosenCardIndex = prevChosenCardIndex { + if let alreadyChosenCardIndex = previousChosenCardIndex { if cards[currentChosenCardIndex].id != cards[alreadyChosenCardIndex].id, cards[currentChosenCardIndex].content == cards[alreadyChosenCardIndex].content { cards[currentChosenCardIndex].isMatched = true @@ -63,9 +63,9 @@ struct MemoryGame where CardContent: Equatable { } } -extension Array{ +extension Array { - var oneAndOnly: Element?{ + var oneAndOnly: Element? { return self.count == 1 ? self.first : nil } } diff --git a/week2/Memorize/Memorize/View/SubViews/CardSetView.swift b/week2/Memorize/Memorize/View/SubViews/CardSetView.swift index cf26f47..e5c3f8f 100644 --- a/week2/Memorize/Memorize/View/SubViews/CardSetView.swift +++ b/week2/Memorize/Memorize/View/SubViews/CardSetView.swift @@ -16,7 +16,7 @@ struct CardSetView: View { LazyVGrid(columns: [GridItem(.adaptive(minimum: 80, maximum: 100))], spacing: 10) { ForEach(memoryGameManager.cards) { card in CardView(card: card) - .onTapGesture{ + .onTapGesture { memoryGameManager.choose(card) } } diff --git a/week2/Memorize/Memorize/View/SubViews/TopInfoView.swift b/week2/Memorize/Memorize/View/SubViews/TopInfoView.swift index e10ec6a..3c191aa 100644 --- a/week2/Memorize/Memorize/View/SubViews/TopInfoView.swift +++ b/week2/Memorize/Memorize/View/SubViews/TopInfoView.swift @@ -13,7 +13,7 @@ struct TopInfoView: View { let point: Int var body: some View { - VStack{ + VStack { Text(title) Text("point : \(point)").font(.body) } diff --git a/week2/Memorize/Memorize/View/SubViews/WellDoneView.swift b/week2/Memorize/Memorize/View/SubViews/WellDoneView.swift index b75905e..9c24e75 100644 --- a/week2/Memorize/Memorize/View/SubViews/WellDoneView.swift +++ b/week2/Memorize/Memorize/View/SubViews/WellDoneView.swift @@ -12,7 +12,7 @@ struct WellDoneView: View { let point: Int var body: some View { - VStack{ + VStack { Text("Well Done!") Text("You got \(point) Points!!") } diff --git a/week2/Memorize/Memorize/View/SwiftUIView.swift b/week2/Memorize/Memorize/View/SwiftUIView.swift new file mode 100644 index 0000000..d8e795a --- /dev/null +++ b/week2/Memorize/Memorize/View/SwiftUIView.swift @@ -0,0 +1,54 @@ +// +// SwiftUIView.swift +// Memorize +// +// Created by 임주민 on 2022/08/20. +// + +import SwiftUI + +struct SwiftUIView: View { + var body: some View { + VStack{ + Text("자신의 이름을 화면에 써보세요!") + .font(.largeTitle) + + List { + + Text("이소희") + .foregroundColor(.pink) + + Text("김은호") + .foregroundColor(.purple) + + Text("정제혁") + .foregroundColor(.black) + + Text("임주민") + .foregroundColor(.red) + + Text("김나연") + .foregroundColor(.blue) + + Text("김태정") + .foregroundColor(.pink) + + Text("김산아") + .foregroundColor(.purple) + + Text("김기준") + .foregroundColor(.blue) + + } + } + .font(.body) + .padding() + } +} + + +struct SwiftUIView_Previews: PreviewProvider { + static var previews: some View { + SwiftUIView() + } +} diff --git a/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift b/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift index e81c414..f8c1401 100644 --- a/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift +++ b/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift @@ -11,7 +11,9 @@ import SwiftUI class EmojiMemoryGame: ObservableObject { private static func createEmojiMemoryGame() -> MemoryGame { - let numberOfCardPairs = theme.numberOfCardPairsToShow > theme.content.count ? theme.content.count : theme.numberOfCardPairsToShow + let numberOfCardPairs = theme.numberOfCardPairsToShow > theme.content.count + ? theme.content.count + : theme.numberOfCardPairsToShow return MemoryGame(numberOfCardPairs: numberOfCardPairs) { pairIndex in theme.content[pairIndex] } } @@ -37,20 +39,18 @@ class EmojiMemoryGame: ObservableObject { var themecolor: Color { switch EmojiMemoryGame.theme.color { - case "pink": + case .pink: return .pink - case "yellow": + case .yellow: return .yellow - case "orange": + case .orange: return .orange - case "green": + case .green: return .green - case "black": + case .black: return .black - case "blue": + case .blue: return .blue - default: - return .pink } } From 3bf72f2860260b4cbebf68482759b66b621bb124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Sat, 20 Aug 2022 15:11:22 +0900 Subject: [PATCH 08/22] Fix code by feedback --- .../Memorize.xcodeproj/project.pbxproj | 4 -- .../Memorize/View/SubViews/CardSetView.swift | 2 +- .../Memorize/View/SubViews/CardView.swift | 8 ++- .../Memorize/Memorize/View/SwiftUIView.swift | 54 ------------------- .../Memorize/ViewModel/EmojiMemoryGame.swift | 8 +-- 5 files changed, 11 insertions(+), 65 deletions(-) delete mode 100644 week2/Memorize/Memorize/View/SwiftUIView.swift diff --git a/week2/Memorize/Memorize.xcodeproj/project.pbxproj b/week2/Memorize/Memorize.xcodeproj/project.pbxproj index c3f7734..290c9f0 100644 --- a/week2/Memorize/Memorize.xcodeproj/project.pbxproj +++ b/week2/Memorize/Memorize.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 6F30B01428B06EBB00994A69 /* SwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F30B01328B06EBB00994A69 /* SwiftUIView.swift */; }; 6F89DC0C28A1274A00C73BBE /* EmojiTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F89DC0B28A1274A00C73BBE /* EmojiTheme.swift */; }; 6F8BD4F6289FA933003F9AF8 /* ButtonTabbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F8BD4F5289FA933003F9AF8 /* ButtonTabbar.swift */; }; 6FC460F72892A6D1003DA739 /* MemorizeApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FC460F62892A6D1003DA739 /* MemorizeApp.swift */; }; @@ -23,7 +22,6 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 6F30B01328B06EBB00994A69 /* SwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIView.swift; sourceTree = ""; }; 6F89DC0B28A1274A00C73BBE /* EmojiTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiTheme.swift; sourceTree = ""; }; 6F8BD4F5289FA933003F9AF8 /* ButtonTabbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonTabbar.swift; sourceTree = ""; }; 6FC460F32892A6D1003DA739 /* Memorize.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Memorize.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -113,7 +111,6 @@ children = ( 6FC460F82892A6D1003DA739 /* ContentView.swift */, 6F347EB02893DAA9005329DE /* SubViews */, - 6F30B01328B06EBB00994A69 /* SwiftUIView.swift */, ); path = View; sourceTree = ""; @@ -198,7 +195,6 @@ files = ( 6F8BD4F6289FA933003F9AF8 /* ButtonTabbar.swift in Sources */, 6F89DC0C28A1274A00C73BBE /* EmojiTheme.swift in Sources */, - 6F30B01428B06EBB00994A69 /* SwiftUIView.swift in Sources */, 6FFE685F28A6C68900F3FB66 /* WellDoneView.swift in Sources */, 6FC460F92892A6D1003DA739 /* ContentView.swift in Sources */, 6FC461072892BC27003DA739 /* CardView.swift in Sources */, diff --git a/week2/Memorize/Memorize/View/SubViews/CardSetView.swift b/week2/Memorize/Memorize/View/SubViews/CardSetView.swift index e5c3f8f..b1b0063 100644 --- a/week2/Memorize/Memorize/View/SubViews/CardSetView.swift +++ b/week2/Memorize/Memorize/View/SubViews/CardSetView.swift @@ -15,7 +15,7 @@ struct CardSetView: View { ScrollView(showsIndicators: false) { LazyVGrid(columns: [GridItem(.adaptive(minimum: 80, maximum: 100))], spacing: 10) { ForEach(memoryGameManager.cards) { card in - CardView(card: card) + CardView(card) .onTapGesture { memoryGameManager.choose(card) } diff --git a/week2/Memorize/Memorize/View/SubViews/CardView.swift b/week2/Memorize/Memorize/View/SubViews/CardView.swift index 4b69c65..517b3bf 100644 --- a/week2/Memorize/Memorize/View/SubViews/CardView.swift +++ b/week2/Memorize/Memorize/View/SubViews/CardView.swift @@ -8,9 +8,13 @@ import SwiftUI struct CardView: View { - + let card: MemoryGame.Card + init(_ card: MemoryGame.Card) { + self.card = card + } + var body: some View { ZStack{ let shape = RoundedRectangle(cornerRadius: 15) @@ -31,6 +35,6 @@ struct CardView_Previews: PreviewProvider { static var previews: some View { let card = MemoryGame.Card(isFaceUp: true, isMatched: false, content: "🫡", id: 0) - CardView(card: card) + CardView(card) } } diff --git a/week2/Memorize/Memorize/View/SwiftUIView.swift b/week2/Memorize/Memorize/View/SwiftUIView.swift deleted file mode 100644 index d8e795a..0000000 --- a/week2/Memorize/Memorize/View/SwiftUIView.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// SwiftUIView.swift -// Memorize -// -// Created by 임주민 on 2022/08/20. -// - -import SwiftUI - -struct SwiftUIView: View { - var body: some View { - VStack{ - Text("자신의 이름을 화면에 써보세요!") - .font(.largeTitle) - - List { - - Text("이소희") - .foregroundColor(.pink) - - Text("김은호") - .foregroundColor(.purple) - - Text("정제혁") - .foregroundColor(.black) - - Text("임주민") - .foregroundColor(.red) - - Text("김나연") - .foregroundColor(.blue) - - Text("김태정") - .foregroundColor(.pink) - - Text("김산아") - .foregroundColor(.purple) - - Text("김기준") - .foregroundColor(.blue) - - } - } - .font(.body) - .padding() - } -} - - -struct SwiftUIView_Previews: PreviewProvider { - static var previews: some View { - SwiftUIView() - } -} diff --git a/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift b/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift index f8c1401..283ceb1 100644 --- a/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift +++ b/week2/Memorize/Memorize/ViewModel/EmojiMemoryGame.swift @@ -10,14 +10,14 @@ import SwiftUI class EmojiMemoryGame: ObservableObject { - private static func createEmojiMemoryGame() -> MemoryGame { + private static func create() -> MemoryGame { let numberOfCardPairs = theme.numberOfCardPairsToShow > theme.content.count ? theme.content.count : theme.numberOfCardPairsToShow return MemoryGame(numberOfCardPairs: numberOfCardPairs) { pairIndex in theme.content[pairIndex] } } - @Published private var memoryGameModel = createEmojiMemoryGame() + @Published private var memoryGameModel = create() static var theme = EmojiTheme.allCases.randomElement() ?? .vehicles var cards: [MemoryGame.Card] { @@ -61,7 +61,7 @@ class EmojiMemoryGame: ObservableObject { } func newGame() { - EmojiMemoryGame.theme = EmojiTheme.allCases.randomElement()! - memoryGameModel = EmojiMemoryGame.createEmojiMemoryGame() + EmojiMemoryGame.theme = EmojiTheme.allCases.randomElement() ?? .vehicles + memoryGameModel = EmojiMemoryGame.create() } } From 23cb56813f726c374a8ce2b1fc4a6765ce635bae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Sun, 21 Aug 2022 16:34:33 +0900 Subject: [PATCH 09/22] Make calculator model & viewmodel --- .../Calculator.xcodeproj/project.pbxproj | 392 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 98 +++++ .../Calculator/Assets.xcassets/Contents.json | 6 + .../Calculator/Calculator/CalculatorApp.swift | 17 + .../Calculator/Model/CalculatorModel.swift | 80 ++++ .../Preview Assets.xcassets/Contents.json | 6 + .../Calculator/View/ContentView.swift | 21 + .../ViewModel/CaculatorManager.swift | 65 +++ 11 files changed, 711 insertions(+) create mode 100644 week4/Calculator/Calculator.xcodeproj/project.pbxproj create mode 100644 week4/Calculator/Calculator.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 week4/Calculator/Calculator.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 week4/Calculator/Calculator/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 week4/Calculator/Calculator/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 week4/Calculator/Calculator/Assets.xcassets/Contents.json create mode 100644 week4/Calculator/Calculator/CalculatorApp.swift create mode 100644 week4/Calculator/Calculator/Model/CalculatorModel.swift create mode 100644 week4/Calculator/Calculator/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 week4/Calculator/Calculator/View/ContentView.swift create mode 100644 week4/Calculator/Calculator/ViewModel/CaculatorManager.swift diff --git a/week4/Calculator/Calculator.xcodeproj/project.pbxproj b/week4/Calculator/Calculator.xcodeproj/project.pbxproj new file mode 100644 index 0000000..6166e64 --- /dev/null +++ b/week4/Calculator/Calculator.xcodeproj/project.pbxproj @@ -0,0 +1,392 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 55; + objects = { + +/* Begin PBXBuildFile section */ + 6F1A921828B13A71009FBF28 /* CalculatorApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A921728B13A71009FBF28 /* CalculatorApp.swift */; }; + 6F1A921A28B13A71009FBF28 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A921928B13A71009FBF28 /* ContentView.swift */; }; + 6F1A921C28B13A72009FBF28 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6F1A921B28B13A72009FBF28 /* Assets.xcassets */; }; + 6F1A921F28B13A72009FBF28 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6F1A921E28B13A72009FBF28 /* Preview Assets.xcassets */; }; + 6F1A922928B13AE8009FBF28 /* CalculatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922828B13AE8009FBF28 /* CalculatorModel.swift */; }; + 6F1A922B28B13B04009FBF28 /* CaculatorManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922A28B13B04009FBF28 /* CaculatorManager.swift */; }; + 6F1A922D28B2159B009FBF28 /* ResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922C28B2159B009FBF28 /* ResultView.swift */; }; + 6F1A922F28B215AB009FBF28 /* FunctionButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922E28B215AA009FBF28 /* FunctionButtons.swift */; }; + 6F1A923128B215B8009FBF28 /* ArithmeticOperationButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923028B215B8009FBF28 /* ArithmeticOperationButtons.swift */; }; + 6F1A923328B215C2009FBF28 /* NumberPadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923228B215C2009FBF28 /* NumberPadView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 6F1A921428B13A71009FBF28 /* Calculator.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Calculator.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 6F1A921728B13A71009FBF28 /* CalculatorApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorApp.swift; sourceTree = ""; }; + 6F1A921928B13A71009FBF28 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 6F1A921B28B13A72009FBF28 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 6F1A921E28B13A72009FBF28 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 6F1A922828B13AE8009FBF28 /* CalculatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorModel.swift; sourceTree = ""; }; + 6F1A922A28B13B04009FBF28 /* CaculatorManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaculatorManager.swift; sourceTree = ""; }; + 6F1A922C28B2159B009FBF28 /* ResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultView.swift; sourceTree = ""; }; + 6F1A922E28B215AA009FBF28 /* FunctionButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FunctionButtons.swift; sourceTree = ""; }; + 6F1A923028B215B8009FBF28 /* ArithmeticOperationButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArithmeticOperationButtons.swift; sourceTree = ""; }; + 6F1A923228B215C2009FBF28 /* NumberPadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberPadView.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6F1A921128B13A71009FBF28 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6F1A920B28B13A71009FBF28 = { + isa = PBXGroup; + children = ( + 6F1A921628B13A71009FBF28 /* Calculator */, + 6F1A921528B13A71009FBF28 /* Products */, + ); + sourceTree = ""; + }; + 6F1A921528B13A71009FBF28 /* Products */ = { + isa = PBXGroup; + children = ( + 6F1A921428B13A71009FBF28 /* Calculator.app */, + ); + name = Products; + sourceTree = ""; + }; + 6F1A921628B13A71009FBF28 /* Calculator */ = { + isa = PBXGroup; + children = ( + 6F1A921728B13A71009FBF28 /* CalculatorApp.swift */, + 6F1A922528B13A93009FBF28 /* Model */, + 6F1A922628B13A9B009FBF28 /* ViewModel */, + 6F1A922728B13AA3009FBF28 /* View */, + 6F1A921B28B13A72009FBF28 /* Assets.xcassets */, + 6F1A921D28B13A72009FBF28 /* Preview Content */, + ); + path = Calculator; + sourceTree = ""; + }; + 6F1A921D28B13A72009FBF28 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 6F1A921E28B13A72009FBF28 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 6F1A922528B13A93009FBF28 /* Model */ = { + isa = PBXGroup; + children = ( + 6F1A922828B13AE8009FBF28 /* CalculatorModel.swift */, + ); + path = Model; + sourceTree = ""; + }; + 6F1A922628B13A9B009FBF28 /* ViewModel */ = { + isa = PBXGroup; + children = ( + 6F1A922A28B13B04009FBF28 /* CaculatorManager.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; + 6F1A922728B13AA3009FBF28 /* View */ = { + isa = PBXGroup; + children = ( + 6F1A921928B13A71009FBF28 /* ContentView.swift */, + 6F1A922C28B2159B009FBF28 /* ResultView.swift */, + 6F1A922E28B215AA009FBF28 /* FunctionButtons.swift */, + 6F1A923028B215B8009FBF28 /* ArithmeticOperationButtons.swift */, + 6F1A923228B215C2009FBF28 /* NumberPadView.swift */, + ); + path = View; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 6F1A921328B13A71009FBF28 /* Calculator */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6F1A922228B13A72009FBF28 /* Build configuration list for PBXNativeTarget "Calculator" */; + buildPhases = ( + 6F1A921028B13A71009FBF28 /* Sources */, + 6F1A921128B13A71009FBF28 /* Frameworks */, + 6F1A921228B13A71009FBF28 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Calculator; + productName = Calculator; + productReference = 6F1A921428B13A71009FBF28 /* Calculator.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6F1A920C28B13A71009FBF28 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1340; + LastUpgradeCheck = 1340; + TargetAttributes = { + 6F1A921328B13A71009FBF28 = { + CreatedOnToolsVersion = 13.4.1; + }; + }; + }; + buildConfigurationList = 6F1A920F28B13A71009FBF28 /* Build configuration list for PBXProject "Calculator" */; + compatibilityVersion = "Xcode 13.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6F1A920B28B13A71009FBF28; + productRefGroup = 6F1A921528B13A71009FBF28 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6F1A921328B13A71009FBF28 /* Calculator */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6F1A921228B13A71009FBF28 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6F1A921F28B13A72009FBF28 /* Preview Assets.xcassets in Resources */, + 6F1A921C28B13A72009FBF28 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6F1A921028B13A71009FBF28 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6F1A922928B13AE8009FBF28 /* CalculatorModel.swift in Sources */, + 6F1A921A28B13A71009FBF28 /* ContentView.swift in Sources */, + 6F1A922D28B2159B009FBF28 /* ResultView.swift in Sources */, + 6F1A922B28B13B04009FBF28 /* CaculatorManager.swift in Sources */, + 6F1A921828B13A71009FBF28 /* CalculatorApp.swift in Sources */, + 6F1A923128B215B8009FBF28 /* ArithmeticOperationButtons.swift in Sources */, + 6F1A922F28B215AB009FBF28 /* FunctionButtons.swift in Sources */, + 6F1A923328B215C2009FBF28 /* NumberPadView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 6F1A922028B13A72009FBF28 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 6F1A922128B13A72009FBF28 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 6F1A922328B13A72009FBF28 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Calculator/Preview Content\""; + DEVELOPMENT_TEAM = 3FAK98S72C; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = jum.Calculator; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 6F1A922428B13A72009FBF28 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Calculator/Preview Content\""; + DEVELOPMENT_TEAM = 3FAK98S72C; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = jum.Calculator; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6F1A920F28B13A71009FBF28 /* Build configuration list for PBXProject "Calculator" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6F1A922028B13A72009FBF28 /* Debug */, + 6F1A922128B13A72009FBF28 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6F1A922228B13A72009FBF28 /* Build configuration list for PBXNativeTarget "Calculator" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6F1A922328B13A72009FBF28 /* Debug */, + 6F1A922428B13A72009FBF28 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6F1A920C28B13A71009FBF28 /* Project object */; +} diff --git a/week4/Calculator/Calculator.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/week4/Calculator/Calculator.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/week4/Calculator/Calculator.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/week4/Calculator/Calculator.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/week4/Calculator/Calculator.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/week4/Calculator/Calculator.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/week4/Calculator/Calculator/Assets.xcassets/AccentColor.colorset/Contents.json b/week4/Calculator/Calculator/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/week4/Calculator/Calculator/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week4/Calculator/Calculator/Assets.xcassets/AppIcon.appiconset/Contents.json b/week4/Calculator/Calculator/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/week4/Calculator/Calculator/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week4/Calculator/Calculator/Assets.xcassets/Contents.json b/week4/Calculator/Calculator/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/week4/Calculator/Calculator/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week4/Calculator/Calculator/CalculatorApp.swift b/week4/Calculator/Calculator/CalculatorApp.swift new file mode 100644 index 0000000..7398d7f --- /dev/null +++ b/week4/Calculator/Calculator/CalculatorApp.swift @@ -0,0 +1,17 @@ +// +// CalculatorApp.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import SwiftUI + +@main +struct CalculatorApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/week4/Calculator/Calculator/Model/CalculatorModel.swift b/week4/Calculator/Calculator/Model/CalculatorModel.swift new file mode 100644 index 0000000..5f72f38 --- /dev/null +++ b/week4/Calculator/Calculator/Model/CalculatorModel.swift @@ -0,0 +1,80 @@ +// +// Calculator.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import Foundation + +struct Caculator { + + var operation: arithmeticOperation? + var state: inputState = .newPreviousNumber + private(set) var showingText: Double + private(set) var previousNumber: Double? { didSet { showingText = previousNumber ?? 0 } } + private(set) var nextNumber: Double? { didSet { showingText = nextNumber ?? 0 } } + private(set) var result: Double { didSet { showingText = result } } + + mutating func enterNumber(_ number: Double) { + switch state { + case .newPreviousNumber: + previousNumber = number + state = .ongoingPreviousNumber + case .newNextNumber: + nextNumber = number + state = .ongoingNextNumber + case .ongoingPreviousNumber, .ongoingNextNumber: + joinNumbers(nextElement: number) + } + } + + mutating func caculate() { + guard let priorNum = previousNumber else { return } + guard let laterNum = nextNumber else { return } + guard let operation = operation else { return } + + switch operation { + case .multiply: + result = priorNum * laterNum + case .divide: + result = priorNum / laterNum + case .minus: + result = priorNum - laterNum + case .plus: + result = priorNum + laterNum + } + } + + mutating func joinNumbers(nextElement: Double) { + if let previousElement = previousNumber { + if let previousElement = nextNumber { + let joinedNumber = String(previousElement) + String(nextElement) + nextNumber = Double(joinedNumber) + } + let joinedNumber = String(previousElement) + String(nextElement) + previousNumber = Double(joinedNumber) + } + } + + mutating func changeSign() { + showingText = -showingText + } + + mutating func percent() { + result = showingText / 100 + } + + func addPoint() { } +} + +enum arithmeticOperation { + case multiply, divide, minus, plus +} + +enum inputState { + case newPreviousNumber + case newNextNumber + case ongoingPreviousNumber + case ongoingNextNumber +} diff --git a/week4/Calculator/Calculator/Preview Content/Preview Assets.xcassets/Contents.json b/week4/Calculator/Calculator/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/week4/Calculator/Calculator/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week4/Calculator/Calculator/View/ContentView.swift b/week4/Calculator/Calculator/View/ContentView.swift new file mode 100644 index 0000000..20bf42f --- /dev/null +++ b/week4/Calculator/Calculator/View/ContentView.swift @@ -0,0 +1,21 @@ +// +// ContentView.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import SwiftUI + +struct ContentView: View { + var body: some View { + Text("Hello, world!") + .padding() + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} diff --git a/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift b/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift new file mode 100644 index 0000000..0066a67 --- /dev/null +++ b/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift @@ -0,0 +1,65 @@ +// +// CaculatorManager.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import Foundation +import SwiftUI + +class CalculatorManager: ObservableObject { + + private static func create() -> Caculator { + return Caculator(operation: nil, showingText: 0, previousNumber: nil, nextNumber: nil, result: 0) + } + + @Published private var calculateModel = create() + + var text: Double { + calculateModel.showingText + } + + var priorNum: Double? { + calculateModel.previousNumber + } + + var laterNum: Double? { + calculateModel.nextNumber + } + + var result: Double { + calculateModel.result + } + + // MARK: - Intentions + + func enterNumber(_ number: Double) { + calculateModel.enterNumber(number) + } + + func clickOperation(_ operation: arithmeticOperation) { + calculateModel.operation = operation + calculateModel.state = .newNextNumber + } + + func calculate() { + calculateModel.caculate() + } + + func clear() { + calculateModel = CalculatorManager.create() + } + + func changeSign() { + calculateModel.changeSign() + } + + func percent() { + calculateModel.percent() + } + + func addPoint() { + calculateModel.addPoint() + } +} From 93217f4385085419f7988f20a9b173be9665ea28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Sun, 21 Aug 2022 16:38:49 +0900 Subject: [PATCH 10/22] Make contentView UI --- .../View/ArithmeticOperationButtons.swift | 20 +++++++++++++++++ .../Calculator/View/ContentView.swift | 22 ++++++++++++++----- .../Calculator/View/FunctionButtons.swift | 20 +++++++++++++++++ .../Calculator/View/NumberPadView.swift | 20 +++++++++++++++++ .../Calculator/View/ResultView.swift | 20 +++++++++++++++++ 5 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 week4/Calculator/Calculator/View/ArithmeticOperationButtons.swift create mode 100644 week4/Calculator/Calculator/View/FunctionButtons.swift create mode 100644 week4/Calculator/Calculator/View/NumberPadView.swift create mode 100644 week4/Calculator/Calculator/View/ResultView.swift diff --git a/week4/Calculator/Calculator/View/ArithmeticOperationButtons.swift b/week4/Calculator/Calculator/View/ArithmeticOperationButtons.swift new file mode 100644 index 0000000..a1bc2f8 --- /dev/null +++ b/week4/Calculator/Calculator/View/ArithmeticOperationButtons.swift @@ -0,0 +1,20 @@ +// +// ArithmeticOperationButtons.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import SwiftUI + +struct ArithmeticOperationButtons: View { + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} + +struct ArithmeticOperationButtons_Previews: PreviewProvider { + static var previews: some View { + ArithmeticOperationButtons() + } +} diff --git a/week4/Calculator/Calculator/View/ContentView.swift b/week4/Calculator/Calculator/View/ContentView.swift index 20bf42f..d3bb0ca 100644 --- a/week4/Calculator/Calculator/View/ContentView.swift +++ b/week4/Calculator/Calculator/View/ContentView.swift @@ -8,14 +8,24 @@ import SwiftUI struct ContentView: View { - var body: some View { - Text("Hello, world!") - .padding() + + var body: some View { + VStack { + ResultView() + HStack { + VStack { + FunctionButtons() + NumberPadView() + } + ArithmeticOperationButtons() + } } + } } struct ContentView_Previews: PreviewProvider { - static var previews: some View { - ContentView() - } + + static var previews: some View { + ContentView() + } } diff --git a/week4/Calculator/Calculator/View/FunctionButtons.swift b/week4/Calculator/Calculator/View/FunctionButtons.swift new file mode 100644 index 0000000..4414669 --- /dev/null +++ b/week4/Calculator/Calculator/View/FunctionButtons.swift @@ -0,0 +1,20 @@ +// +// FunctionButtons.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import SwiftUI + +struct FunctionButtons: View { + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} + +struct FunctionButtons_Previews: PreviewProvider { + static var previews: some View { + FunctionButtons() + } +} diff --git a/week4/Calculator/Calculator/View/NumberPadView.swift b/week4/Calculator/Calculator/View/NumberPadView.swift new file mode 100644 index 0000000..eb3875f --- /dev/null +++ b/week4/Calculator/Calculator/View/NumberPadView.swift @@ -0,0 +1,20 @@ +// +// NumberPadView.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import SwiftUI + +struct NumberPadView: View { + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} + +struct NumberPadView_Previews: PreviewProvider { + static var previews: some View { + NumberPadView() + } +} diff --git a/week4/Calculator/Calculator/View/ResultView.swift b/week4/Calculator/Calculator/View/ResultView.swift new file mode 100644 index 0000000..7556d44 --- /dev/null +++ b/week4/Calculator/Calculator/View/ResultView.swift @@ -0,0 +1,20 @@ +// +// ResultView.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import SwiftUI + +struct ResultView: View { + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} + +struct ResultView_Previews: PreviewProvider { + static var previews: some View { + ResultView() + } +} From ad7da24f7e8f05e39b0d111fb400cc68205f145e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Sun, 21 Aug 2022 17:09:40 +0900 Subject: [PATCH 11/22] Make ResultView & double extension --- .../Calculator.xcodeproj/project.pbxproj | 12 ++++++++++ .../Calculator/Calculator/CalculatorApp.swift | 5 +++- .../Calculator/Extensions/Ex+Double.swift | 15 ++++++++++++ .../Calculator/View/ContentView.swift | 24 ++++++++++++------- .../Calculator/View/ResultView.swift | 18 ++++++++++---- 5 files changed, 59 insertions(+), 15 deletions(-) create mode 100644 week4/Calculator/Calculator/Extensions/Ex+Double.swift diff --git a/week4/Calculator/Calculator.xcodeproj/project.pbxproj b/week4/Calculator/Calculator.xcodeproj/project.pbxproj index 6166e64..8a93e17 100644 --- a/week4/Calculator/Calculator.xcodeproj/project.pbxproj +++ b/week4/Calculator/Calculator.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 6F1A922F28B215AB009FBF28 /* FunctionButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922E28B215AA009FBF28 /* FunctionButtons.swift */; }; 6F1A923128B215B8009FBF28 /* ArithmeticOperationButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923028B215B8009FBF28 /* ArithmeticOperationButtons.swift */; }; 6F1A923328B215C2009FBF28 /* NumberPadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923228B215C2009FBF28 /* NumberPadView.swift */; }; + 6F1A923628B21E70009FBF28 /* Ex+Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923528B21E70009FBF28 /* Ex+Double.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -31,6 +32,7 @@ 6F1A922E28B215AA009FBF28 /* FunctionButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FunctionButtons.swift; sourceTree = ""; }; 6F1A923028B215B8009FBF28 /* ArithmeticOperationButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArithmeticOperationButtons.swift; sourceTree = ""; }; 6F1A923228B215C2009FBF28 /* NumberPadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberPadView.swift; sourceTree = ""; }; + 6F1A923528B21E70009FBF28 /* Ex+Double.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Ex+Double.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -64,6 +66,7 @@ isa = PBXGroup; children = ( 6F1A921728B13A71009FBF28 /* CalculatorApp.swift */, + 6F1A923428B21E45009FBF28 /* Extensions */, 6F1A922528B13A93009FBF28 /* Model */, 6F1A922628B13A9B009FBF28 /* ViewModel */, 6F1A922728B13AA3009FBF28 /* View */, @@ -109,6 +112,14 @@ path = View; sourceTree = ""; }; + 6F1A923428B21E45009FBF28 /* Extensions */ = { + isa = PBXGroup; + children = ( + 6F1A923528B21E70009FBF28 /* Ex+Double.swift */, + ); + path = Extensions; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -179,6 +190,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 6F1A923628B21E70009FBF28 /* Ex+Double.swift in Sources */, 6F1A922928B13AE8009FBF28 /* CalculatorModel.swift in Sources */, 6F1A921A28B13A71009FBF28 /* ContentView.swift in Sources */, 6F1A922D28B2159B009FBF28 /* ResultView.swift in Sources */, diff --git a/week4/Calculator/Calculator/CalculatorApp.swift b/week4/Calculator/Calculator/CalculatorApp.swift index 7398d7f..5bb77cd 100644 --- a/week4/Calculator/Calculator/CalculatorApp.swift +++ b/week4/Calculator/Calculator/CalculatorApp.swift @@ -9,9 +9,12 @@ import SwiftUI @main struct CalculatorApp: App { + + let caculatorManager = CalculatorManager() + var body: some Scene { WindowGroup { - ContentView() + ContentView(calculatorManager: caculatorManager) } } } diff --git a/week4/Calculator/Calculator/Extensions/Ex+Double.swift b/week4/Calculator/Calculator/Extensions/Ex+Double.swift new file mode 100644 index 0000000..3450af2 --- /dev/null +++ b/week4/Calculator/Calculator/Extensions/Ex+Double.swift @@ -0,0 +1,15 @@ +// +// Ex+Double.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import Foundation + +extension Double { + + var clean: String { + return self.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(self) + } +} diff --git a/week4/Calculator/Calculator/View/ContentView.swift b/week4/Calculator/Calculator/View/ContentView.swift index d3bb0ca..2f4b1dc 100644 --- a/week4/Calculator/Calculator/View/ContentView.swift +++ b/week4/Calculator/Calculator/View/ContentView.swift @@ -9,23 +9,29 @@ import SwiftUI struct ContentView: View { + @ObservedObject var calculatorManager: CalculatorManager + var body: some View { - VStack { - ResultView() - HStack { - VStack { - FunctionButtons() - NumberPadView() + ZStack{ + Color.black.ignoresSafeArea() + VStack { + ResultView(showingText: calculatorManager.showingText) + HStack { + VStack { + FunctionButtons() + NumberPadView() + } + ArithmeticOperationButtons() } - ArithmeticOperationButtons() } - } + }.foregroundColor(.white) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { - ContentView() + let caculatorManager = CalculatorManager() + ContentView(calculatorManager: caculatorManager) } } diff --git a/week4/Calculator/Calculator/View/ResultView.swift b/week4/Calculator/Calculator/View/ResultView.swift index 7556d44..cf56f1d 100644 --- a/week4/Calculator/Calculator/View/ResultView.swift +++ b/week4/Calculator/Calculator/View/ResultView.swift @@ -8,13 +8,21 @@ import SwiftUI struct ResultView: View { - var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + + let showingText: Double + + var body: some View { + HStack { + Spacer() + Text(showingText.clean) + .font(.system(size: 60)) } + } } struct ResultView_Previews: PreviewProvider { - static var previews: some View { - ResultView() - } + static var previews: some View { + let showingText = 10.0 + ResultView(showingText: showingText) + } } From 76232f12f9cea089ec425ce995b99ac8328d8421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Sun, 21 Aug 2022 19:03:59 +0900 Subject: [PATCH 12/22] Make caculator UI --- .../Calculator.xcodeproj/project.pbxproj | 28 +++++++-- .../Model/ArithmeticOperation.swift | 15 +++++ .../Calculator/Model/CalculatorModel.swift | 58 ++++++++--------- .../Calculator/Model/InputState.swift | 15 +++++ .../View/ArithmeticOperationButtons.swift | 20 ------ .../Calculator/View/ContentView.swift | 6 +- .../Calculator/View/FunctionButtons.swift | 20 ------ .../Calculator/View/NumberPadView.swift | 20 ------ .../SubViews/ArithmeticOperationButtons.swift | 63 +++++++++++++++++++ .../View/SubViews/CircleButton.swift | 24 +++++++ .../View/SubViews/FunctionButtons.swift | 45 +++++++++++++ .../View/SubViews/NumberPadView.swift | 61 ++++++++++++++++++ .../View/{ => SubViews}/ResultView.swift | 8 +-- .../ViewModel/CaculatorManager.swift | 20 ++---- 14 files changed, 282 insertions(+), 121 deletions(-) create mode 100644 week4/Calculator/Calculator/Model/ArithmeticOperation.swift create mode 100644 week4/Calculator/Calculator/Model/InputState.swift delete mode 100644 week4/Calculator/Calculator/View/ArithmeticOperationButtons.swift delete mode 100644 week4/Calculator/Calculator/View/FunctionButtons.swift delete mode 100644 week4/Calculator/Calculator/View/NumberPadView.swift create mode 100644 week4/Calculator/Calculator/View/SubViews/ArithmeticOperationButtons.swift create mode 100644 week4/Calculator/Calculator/View/SubViews/CircleButton.swift create mode 100644 week4/Calculator/Calculator/View/SubViews/FunctionButtons.swift create mode 100644 week4/Calculator/Calculator/View/SubViews/NumberPadView.swift rename week4/Calculator/Calculator/View/{ => SubViews}/ResultView.swift (64%) diff --git a/week4/Calculator/Calculator.xcodeproj/project.pbxproj b/week4/Calculator/Calculator.xcodeproj/project.pbxproj index 8a93e17..084ab3e 100644 --- a/week4/Calculator/Calculator.xcodeproj/project.pbxproj +++ b/week4/Calculator/Calculator.xcodeproj/project.pbxproj @@ -18,6 +18,9 @@ 6F1A923128B215B8009FBF28 /* ArithmeticOperationButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923028B215B8009FBF28 /* ArithmeticOperationButtons.swift */; }; 6F1A923328B215C2009FBF28 /* NumberPadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923228B215C2009FBF28 /* NumberPadView.swift */; }; 6F1A923628B21E70009FBF28 /* Ex+Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923528B21E70009FBF28 /* Ex+Double.swift */; }; + 6F1A923B28B22610009FBF28 /* CircleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923A28B22610009FBF28 /* CircleButton.swift */; }; + 6F1A923D28B23326009FBF28 /* ArithmeticOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923C28B23326009FBF28 /* ArithmeticOperation.swift */; }; + 6F1A923F28B23330009FBF28 /* InputState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923E28B23330009FBF28 /* InputState.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -33,6 +36,9 @@ 6F1A923028B215B8009FBF28 /* ArithmeticOperationButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArithmeticOperationButtons.swift; sourceTree = ""; }; 6F1A923228B215C2009FBF28 /* NumberPadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberPadView.swift; sourceTree = ""; }; 6F1A923528B21E70009FBF28 /* Ex+Double.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Ex+Double.swift"; sourceTree = ""; }; + 6F1A923A28B22610009FBF28 /* CircleButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleButton.swift; sourceTree = ""; }; + 6F1A923C28B23326009FBF28 /* ArithmeticOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArithmeticOperation.swift; sourceTree = ""; }; + 6F1A923E28B23330009FBF28 /* InputState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputState.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -88,6 +94,8 @@ isa = PBXGroup; children = ( 6F1A922828B13AE8009FBF28 /* CalculatorModel.swift */, + 6F1A923C28B23326009FBF28 /* ArithmeticOperation.swift */, + 6F1A923E28B23330009FBF28 /* InputState.swift */, ); path = Model; sourceTree = ""; @@ -104,10 +112,7 @@ isa = PBXGroup; children = ( 6F1A921928B13A71009FBF28 /* ContentView.swift */, - 6F1A922C28B2159B009FBF28 /* ResultView.swift */, - 6F1A922E28B215AA009FBF28 /* FunctionButtons.swift */, - 6F1A923028B215B8009FBF28 /* ArithmeticOperationButtons.swift */, - 6F1A923228B215C2009FBF28 /* NumberPadView.swift */, + 6F1A923928B22476009FBF28 /* SubViews */, ); path = View; sourceTree = ""; @@ -120,6 +125,18 @@ path = Extensions; sourceTree = ""; }; + 6F1A923928B22476009FBF28 /* SubViews */ = { + isa = PBXGroup; + children = ( + 6F1A922C28B2159B009FBF28 /* ResultView.swift */, + 6F1A922E28B215AA009FBF28 /* FunctionButtons.swift */, + 6F1A923028B215B8009FBF28 /* ArithmeticOperationButtons.swift */, + 6F1A923228B215C2009FBF28 /* NumberPadView.swift */, + 6F1A923A28B22610009FBF28 /* CircleButton.swift */, + ); + path = SubViews; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -194,11 +211,14 @@ 6F1A922928B13AE8009FBF28 /* CalculatorModel.swift in Sources */, 6F1A921A28B13A71009FBF28 /* ContentView.swift in Sources */, 6F1A922D28B2159B009FBF28 /* ResultView.swift in Sources */, + 6F1A923F28B23330009FBF28 /* InputState.swift in Sources */, 6F1A922B28B13B04009FBF28 /* CaculatorManager.swift in Sources */, + 6F1A923B28B22610009FBF28 /* CircleButton.swift in Sources */, 6F1A921828B13A71009FBF28 /* CalculatorApp.swift in Sources */, 6F1A923128B215B8009FBF28 /* ArithmeticOperationButtons.swift in Sources */, 6F1A922F28B215AB009FBF28 /* FunctionButtons.swift in Sources */, 6F1A923328B215C2009FBF28 /* NumberPadView.swift in Sources */, + 6F1A923D28B23326009FBF28 /* ArithmeticOperation.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/week4/Calculator/Calculator/Model/ArithmeticOperation.swift b/week4/Calculator/Calculator/Model/ArithmeticOperation.swift new file mode 100644 index 0000000..4d58ff0 --- /dev/null +++ b/week4/Calculator/Calculator/Model/ArithmeticOperation.swift @@ -0,0 +1,15 @@ +// +// ArithmeticOperation.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import Foundation + +enum InputState { + case newPreviousNumber + case newNextNumber + case ongoingPreviousNumber + case ongoingNextNumber +} diff --git a/week4/Calculator/Calculator/Model/CalculatorModel.swift b/week4/Calculator/Calculator/Model/CalculatorModel.swift index 5f72f38..d7ac18b 100644 --- a/week4/Calculator/Calculator/Model/CalculatorModel.swift +++ b/week4/Calculator/Calculator/Model/CalculatorModel.swift @@ -8,15 +8,20 @@ import Foundation struct Caculator { - - var operation: arithmeticOperation? - var state: inputState = .newPreviousNumber - private(set) var showingText: Double + + var operation: ArithmeticOperation? + var state: InputState = .newPreviousNumber + private(set) var showingText: Double private(set) var previousNumber: Double? { didSet { showingText = previousNumber ?? 0 } } private(set) var nextNumber: Double? { didSet { showingText = nextNumber ?? 0 } } - private(set) var result: Double { didSet { showingText = result } } + private(set) var result: Double { + didSet { + showingText = result + previousNumber = result + } + } - mutating func enterNumber(_ number: Double) { + mutating func clickNumber(_ number: Double) { switch state { case .newPreviousNumber: previousNumber = number @@ -30,35 +35,37 @@ struct Caculator { } mutating func caculate() { - guard let priorNum = previousNumber else { return } - guard let laterNum = nextNumber else { return } + guard let priorNumber = previousNumber else { return } + guard let laterNumber = nextNumber else { return } guard let operation = operation else { return } - switch operation { case .multiply: - result = priorNum * laterNum + result = priorNumber * laterNumber case .divide: - result = priorNum / laterNum + result = priorNumber / laterNumber case .minus: - result = priorNum - laterNum + result = priorNumber - laterNumber case .plus: - result = priorNum + laterNum + result = priorNumber + laterNumber } } mutating func joinNumbers(nextElement: Double) { - if let previousElement = previousNumber { - if let previousElement = nextNumber { - let joinedNumber = String(previousElement) + String(nextElement) - nextNumber = Double(joinedNumber) - } - let joinedNumber = String(previousElement) + String(nextElement) + if state == .ongoingPreviousNumber { + guard let previousElement = previousNumber else { return } + let joinedNumber = String(previousElement.clean) + String(nextElement.clean) previousNumber = Double(joinedNumber) + } else if state == .ongoingNextNumber { + guard let previousElement = nextNumber else { return } + let joinedNumber = String(previousElement.clean) + String(nextElement.clean) + nextNumber = Double(joinedNumber) } } mutating func changeSign() { - showingText = -showingText + if let priorNumber = previousNumber { + previousNumber = -priorNumber + } } mutating func percent() { @@ -67,14 +74,3 @@ struct Caculator { func addPoint() { } } - -enum arithmeticOperation { - case multiply, divide, minus, plus -} - -enum inputState { - case newPreviousNumber - case newNextNumber - case ongoingPreviousNumber - case ongoingNextNumber -} diff --git a/week4/Calculator/Calculator/Model/InputState.swift b/week4/Calculator/Calculator/Model/InputState.swift new file mode 100644 index 0000000..da4fb3e --- /dev/null +++ b/week4/Calculator/Calculator/Model/InputState.swift @@ -0,0 +1,15 @@ +// +// InputState.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import Foundation + +enum ArithmeticOperation { + case multiply + case divide + case minus + case plus +} diff --git a/week4/Calculator/Calculator/View/ArithmeticOperationButtons.swift b/week4/Calculator/Calculator/View/ArithmeticOperationButtons.swift deleted file mode 100644 index a1bc2f8..0000000 --- a/week4/Calculator/Calculator/View/ArithmeticOperationButtons.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// ArithmeticOperationButtons.swift -// Calculator -// -// Created by 임주민 on 2022/08/21. -// - -import SwiftUI - -struct ArithmeticOperationButtons: View { - var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) - } -} - -struct ArithmeticOperationButtons_Previews: PreviewProvider { - static var previews: some View { - ArithmeticOperationButtons() - } -} diff --git a/week4/Calculator/Calculator/View/ContentView.swift b/week4/Calculator/Calculator/View/ContentView.swift index 2f4b1dc..4987094 100644 --- a/week4/Calculator/Calculator/View/ContentView.swift +++ b/week4/Calculator/Calculator/View/ContentView.swift @@ -18,10 +18,10 @@ struct ContentView: View { ResultView(showingText: calculatorManager.showingText) HStack { VStack { - FunctionButtons() - NumberPadView() + FunctionButtons(calculatorManager: calculatorManager) + NumberPadView(calculatorManager: calculatorManager) } - ArithmeticOperationButtons() + ArithmeticOperationButtons(calculatorManager: calculatorManager) } } }.foregroundColor(.white) diff --git a/week4/Calculator/Calculator/View/FunctionButtons.swift b/week4/Calculator/Calculator/View/FunctionButtons.swift deleted file mode 100644 index 4414669..0000000 --- a/week4/Calculator/Calculator/View/FunctionButtons.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// FunctionButtons.swift -// Calculator -// -// Created by 임주민 on 2022/08/21. -// - -import SwiftUI - -struct FunctionButtons: View { - var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) - } -} - -struct FunctionButtons_Previews: PreviewProvider { - static var previews: some View { - FunctionButtons() - } -} diff --git a/week4/Calculator/Calculator/View/NumberPadView.swift b/week4/Calculator/Calculator/View/NumberPadView.swift deleted file mode 100644 index eb3875f..0000000 --- a/week4/Calculator/Calculator/View/NumberPadView.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// NumberPadView.swift -// Calculator -// -// Created by 임주민 on 2022/08/21. -// - -import SwiftUI - -struct NumberPadView: View { - var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) - } -} - -struct NumberPadView_Previews: PreviewProvider { - static var previews: some View { - NumberPadView() - } -} diff --git a/week4/Calculator/Calculator/View/SubViews/ArithmeticOperationButtons.swift b/week4/Calculator/Calculator/View/SubViews/ArithmeticOperationButtons.swift new file mode 100644 index 0000000..9279526 --- /dev/null +++ b/week4/Calculator/Calculator/View/SubViews/ArithmeticOperationButtons.swift @@ -0,0 +1,63 @@ +// +// ArithmeticOperationButtons.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import SwiftUI + +struct ArithmeticOperationButtons: View { + + @ObservedObject var calculatorManager: CalculatorManager + + var body: some View { + VStack { + divideButton + multiplyButton + minusButton + plusButton + calculateButton + } + } + + var divideButton: some View { + Button { + calculatorManager.clickOperation(.divide) + } label: { + CircleButton(content: "/", color: .orange) + } + } + + var multiplyButton: some View { + Button { + calculatorManager.clickOperation(.multiply) + } label: { + CircleButton(content: "x", color: .orange) + } + } + + var minusButton: some View { + Button { + calculatorManager.clickOperation(.minus) + } label: { + CircleButton(content: "-", color: .orange) + } + } + + var plusButton: some View { + Button { + calculatorManager.clickOperation(.plus) + } label: { + CircleButton(content: "+", color: .orange) + } + } + + var calculateButton: some View { + Button { + calculatorManager.calculate() + } label: { + CircleButton(content: "=", color: .orange) + } + } +} diff --git a/week4/Calculator/Calculator/View/SubViews/CircleButton.swift b/week4/Calculator/Calculator/View/SubViews/CircleButton.swift new file mode 100644 index 0000000..099c11c --- /dev/null +++ b/week4/Calculator/Calculator/View/SubViews/CircleButton.swift @@ -0,0 +1,24 @@ +// +// CircleButton.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import SwiftUI + +struct CircleButton: View { + + let content: String + let color: Color + + var body: some View { + Circle() + .frame(width: 70, height: 70) + .foregroundColor(color) + .overlay { + Text(content) + .font(.system(size: 40)) + } + } +} diff --git a/week4/Calculator/Calculator/View/SubViews/FunctionButtons.swift b/week4/Calculator/Calculator/View/SubViews/FunctionButtons.swift new file mode 100644 index 0000000..6226efe --- /dev/null +++ b/week4/Calculator/Calculator/View/SubViews/FunctionButtons.swift @@ -0,0 +1,45 @@ +// +// FunctionButtons.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import SwiftUI + +struct FunctionButtons: View { + + @ObservedObject var calculatorManager: CalculatorManager + + var body: some View { + HStack { + clearButton + changeSign + percentButton + } + } + + var clearButton: some View { + Button { + calculatorManager.clear() + } label: { + CircleButton(content: "C", color: .green) + } + } + + var changeSign: some View { + Button { + calculatorManager.changeSign() + } label: { + CircleButton(content: "+/-", color: .green) + } + } + + var percentButton: some View { + Button { + calculatorManager.percent() + } label: { + CircleButton(content: "%", color: .green) + } + } +} diff --git a/week4/Calculator/Calculator/View/SubViews/NumberPadView.swift b/week4/Calculator/Calculator/View/SubViews/NumberPadView.swift new file mode 100644 index 0000000..c69e1d5 --- /dev/null +++ b/week4/Calculator/Calculator/View/SubViews/NumberPadView.swift @@ -0,0 +1,61 @@ +// +// NumberPadView.swift +// Calculator +// +// Created by 임주민 on 2022/08/21. +// + +import SwiftUI + +struct NumberPadView: View { + + @ObservedObject var calculatorManager: CalculatorManager + + let positiveNumbers: [Double] = [1,2,3,4,5,6,7,8,9] + + var body: some View { + VStack { + positiveNumberButtons + HStack{ + zeroButton + pointButton + } + }.frame(width: 220) + } + + var positiveNumberButtons: some View { + LazyVGrid(columns: [GridItem(.adaptive(minimum: 60, maximum: 200))], spacing: 10) { + ForEach(positiveNumbers, id: \.self) { number in + CircleButton(content: number.clean, color: .gray) + .onTapGesture { + calculatorManager.clickNumber(number) + } + } + } + } + + var zeroButton: some View { + RoundedRectangle(cornerRadius: 35) + .frame(width: 140, height: 70) + .foregroundColor(.gray) + .overlay { + Text("0") + .font(.system(size: 40)) + .padding(.trailing, 60) + .onTapGesture { + calculatorManager.clickNumber(0) + } + } + } + + var pointButton: some View { + CircleButton(content: ".", color: .gray) + } +} + +struct NumberPadView_Previews: PreviewProvider { + static var previews: some View { + let caculatorManager = CalculatorManager() + NumberPadView(calculatorManager: caculatorManager) + } +} diff --git a/week4/Calculator/Calculator/View/ResultView.swift b/week4/Calculator/Calculator/View/SubViews/ResultView.swift similarity index 64% rename from week4/Calculator/Calculator/View/ResultView.swift rename to week4/Calculator/Calculator/View/SubViews/ResultView.swift index cf56f1d..9af92ea 100644 --- a/week4/Calculator/Calculator/View/ResultView.swift +++ b/week4/Calculator/Calculator/View/SubViews/ResultView.swift @@ -16,13 +16,7 @@ struct ResultView: View { Spacer() Text(showingText.clean) .font(.system(size: 60)) + .padding(.trailing, 50) } } } - -struct ResultView_Previews: PreviewProvider { - static var previews: some View { - let showingText = 10.0 - ResultView(showingText: showingText) - } -} diff --git a/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift b/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift index 0066a67..c06ba4b 100644 --- a/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift +++ b/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift @@ -16,29 +16,17 @@ class CalculatorManager: ObservableObject { @Published private var calculateModel = create() - var text: Double { + var showingText: Double { calculateModel.showingText } - var priorNum: Double? { - calculateModel.previousNumber - } - - var laterNum: Double? { - calculateModel.nextNumber - } - - var result: Double { - calculateModel.result - } - // MARK: - Intentions - func enterNumber(_ number: Double) { - calculateModel.enterNumber(number) + func clickNumber(_ number: Double) { + calculateModel.clickNumber(number) } - func clickOperation(_ operation: arithmeticOperation) { + func clickOperation(_ operation: ArithmeticOperation) { calculateModel.operation = operation calculateModel.state = .newNextNumber } From 49ac17a1bea3672f8309831d4b4f1b76667a9e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Sun, 21 Aug 2022 19:44:33 +0900 Subject: [PATCH 13/22] Fix model structure and optional variable --- .../Calculator.xcodeproj/project.pbxproj | 8 ----- .../Model/ArithmeticOperation.swift | 15 ---------- .../Calculator/Model/CalculatorModel.swift | 30 +++++++++++++++++-- .../Calculator/Model/InputState.swift | 15 ---------- .../ViewModel/CaculatorManager.swift | 4 +-- 5 files changed, 29 insertions(+), 43 deletions(-) delete mode 100644 week4/Calculator/Calculator/Model/ArithmeticOperation.swift delete mode 100644 week4/Calculator/Calculator/Model/InputState.swift diff --git a/week4/Calculator/Calculator.xcodeproj/project.pbxproj b/week4/Calculator/Calculator.xcodeproj/project.pbxproj index 084ab3e..92632db 100644 --- a/week4/Calculator/Calculator.xcodeproj/project.pbxproj +++ b/week4/Calculator/Calculator.xcodeproj/project.pbxproj @@ -19,8 +19,6 @@ 6F1A923328B215C2009FBF28 /* NumberPadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923228B215C2009FBF28 /* NumberPadView.swift */; }; 6F1A923628B21E70009FBF28 /* Ex+Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923528B21E70009FBF28 /* Ex+Double.swift */; }; 6F1A923B28B22610009FBF28 /* CircleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923A28B22610009FBF28 /* CircleButton.swift */; }; - 6F1A923D28B23326009FBF28 /* ArithmeticOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923C28B23326009FBF28 /* ArithmeticOperation.swift */; }; - 6F1A923F28B23330009FBF28 /* InputState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923E28B23330009FBF28 /* InputState.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -37,8 +35,6 @@ 6F1A923228B215C2009FBF28 /* NumberPadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberPadView.swift; sourceTree = ""; }; 6F1A923528B21E70009FBF28 /* Ex+Double.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Ex+Double.swift"; sourceTree = ""; }; 6F1A923A28B22610009FBF28 /* CircleButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleButton.swift; sourceTree = ""; }; - 6F1A923C28B23326009FBF28 /* ArithmeticOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArithmeticOperation.swift; sourceTree = ""; }; - 6F1A923E28B23330009FBF28 /* InputState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputState.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -94,8 +90,6 @@ isa = PBXGroup; children = ( 6F1A922828B13AE8009FBF28 /* CalculatorModel.swift */, - 6F1A923C28B23326009FBF28 /* ArithmeticOperation.swift */, - 6F1A923E28B23330009FBF28 /* InputState.swift */, ); path = Model; sourceTree = ""; @@ -211,14 +205,12 @@ 6F1A922928B13AE8009FBF28 /* CalculatorModel.swift in Sources */, 6F1A921A28B13A71009FBF28 /* ContentView.swift in Sources */, 6F1A922D28B2159B009FBF28 /* ResultView.swift in Sources */, - 6F1A923F28B23330009FBF28 /* InputState.swift in Sources */, 6F1A922B28B13B04009FBF28 /* CaculatorManager.swift in Sources */, 6F1A923B28B22610009FBF28 /* CircleButton.swift in Sources */, 6F1A921828B13A71009FBF28 /* CalculatorApp.swift in Sources */, 6F1A923128B215B8009FBF28 /* ArithmeticOperationButtons.swift in Sources */, 6F1A922F28B215AB009FBF28 /* FunctionButtons.swift in Sources */, 6F1A923328B215C2009FBF28 /* NumberPadView.swift in Sources */, - 6F1A923D28B23326009FBF28 /* ArithmeticOperation.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/week4/Calculator/Calculator/Model/ArithmeticOperation.swift b/week4/Calculator/Calculator/Model/ArithmeticOperation.swift deleted file mode 100644 index 4d58ff0..0000000 --- a/week4/Calculator/Calculator/Model/ArithmeticOperation.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// ArithmeticOperation.swift -// Calculator -// -// Created by 임주민 on 2022/08/21. -// - -import Foundation - -enum InputState { - case newPreviousNumber - case newNextNumber - case ongoingPreviousNumber - case ongoingNextNumber -} diff --git a/week4/Calculator/Calculator/Model/CalculatorModel.swift b/week4/Calculator/Calculator/Model/CalculatorModel.swift index d7ac18b..616c14b 100644 --- a/week4/Calculator/Calculator/Model/CalculatorModel.swift +++ b/week4/Calculator/Calculator/Model/CalculatorModel.swift @@ -12,8 +12,18 @@ struct Caculator { var operation: ArithmeticOperation? var state: InputState = .newPreviousNumber private(set) var showingText: Double - private(set) var previousNumber: Double? { didSet { showingText = previousNumber ?? 0 } } - private(set) var nextNumber: Double? { didSet { showingText = nextNumber ?? 0 } } + private(set) var previousNumber: Double? { + didSet { + guard let previousNumber = previousNumber else { return } + showingText = previousNumber + } + } + private(set) var nextNumber: Double? { + didSet { + guard let nextNumber = nextNumber else { return } + showingText = nextNumber + } + } private(set) var result: Double { didSet { showingText = result @@ -21,6 +31,20 @@ struct Caculator { } } + enum ArithmeticOperation { + case multiply + case divide + case minus + case plus + } + + enum InputState { + case newPreviousNumber + case newNextNumber + case ongoingPreviousNumber + case ongoingNextNumber + } + mutating func clickNumber(_ number: Double) { switch state { case .newPreviousNumber: @@ -34,7 +58,7 @@ struct Caculator { } } - mutating func caculate() { + mutating func calculate() { guard let priorNumber = previousNumber else { return } guard let laterNumber = nextNumber else { return } guard let operation = operation else { return } diff --git a/week4/Calculator/Calculator/Model/InputState.swift b/week4/Calculator/Calculator/Model/InputState.swift deleted file mode 100644 index da4fb3e..0000000 --- a/week4/Calculator/Calculator/Model/InputState.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// InputState.swift -// Calculator -// -// Created by 임주민 on 2022/08/21. -// - -import Foundation - -enum ArithmeticOperation { - case multiply - case divide - case minus - case plus -} diff --git a/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift b/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift index c06ba4b..f0118e8 100644 --- a/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift +++ b/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift @@ -26,13 +26,13 @@ class CalculatorManager: ObservableObject { calculateModel.clickNumber(number) } - func clickOperation(_ operation: ArithmeticOperation) { + func clickOperation(_ operation: Caculator.ArithmeticOperation?) { calculateModel.operation = operation calculateModel.state = .newNextNumber } func calculate() { - calculateModel.caculate() + calculateModel.calculate() } func clear() { From 158b5c49628a9b4ec68ecafd34de7b0487acd697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Sun, 21 Aug 2022 20:48:50 +0900 Subject: [PATCH 14/22] Change number view to button --- .../Calculator/View/SubViews/NumberPadView.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/week4/Calculator/Calculator/View/SubViews/NumberPadView.swift b/week4/Calculator/Calculator/View/SubViews/NumberPadView.swift index c69e1d5..c30f9fa 100644 --- a/week4/Calculator/Calculator/View/SubViews/NumberPadView.swift +++ b/week4/Calculator/Calculator/View/SubViews/NumberPadView.swift @@ -26,10 +26,11 @@ struct NumberPadView: View { var positiveNumberButtons: some View { LazyVGrid(columns: [GridItem(.adaptive(minimum: 60, maximum: 200))], spacing: 10) { ForEach(positiveNumbers, id: \.self) { number in - CircleButton(content: number.clean, color: .gray) - .onTapGesture { - calculatorManager.clickNumber(number) - } + Button { + calculatorManager.clickNumber(number) + } label: { + CircleButton(content: number.clean, color: .gray) + } } } } From 0dbeb396f406ea5c453d098c9b7031a50ab03085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Fri, 26 Aug 2022 15:02:59 +0900 Subject: [PATCH 15/22] Add point feature --- .../Calculator.xcodeproj/project.pbxproj | 12 ----- .../Calculator/Extensions/Ex+Double.swift | 15 ------ .../Calculator/Model/CalculatorModel.swift | 54 ++++++++++--------- .../View/SubViews/NumberPadView.swift | 17 +++--- .../Calculator/View/SubViews/ResultView.swift | 4 +- .../ViewModel/CaculatorManager.swift | 13 +++-- 6 files changed, 48 insertions(+), 67 deletions(-) delete mode 100644 week4/Calculator/Calculator/Extensions/Ex+Double.swift diff --git a/week4/Calculator/Calculator.xcodeproj/project.pbxproj b/week4/Calculator/Calculator.xcodeproj/project.pbxproj index 92632db..ec25c96 100644 --- a/week4/Calculator/Calculator.xcodeproj/project.pbxproj +++ b/week4/Calculator/Calculator.xcodeproj/project.pbxproj @@ -17,7 +17,6 @@ 6F1A922F28B215AB009FBF28 /* FunctionButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922E28B215AA009FBF28 /* FunctionButtons.swift */; }; 6F1A923128B215B8009FBF28 /* ArithmeticOperationButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923028B215B8009FBF28 /* ArithmeticOperationButtons.swift */; }; 6F1A923328B215C2009FBF28 /* NumberPadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923228B215C2009FBF28 /* NumberPadView.swift */; }; - 6F1A923628B21E70009FBF28 /* Ex+Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923528B21E70009FBF28 /* Ex+Double.swift */; }; 6F1A923B28B22610009FBF28 /* CircleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923A28B22610009FBF28 /* CircleButton.swift */; }; /* End PBXBuildFile section */ @@ -33,7 +32,6 @@ 6F1A922E28B215AA009FBF28 /* FunctionButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FunctionButtons.swift; sourceTree = ""; }; 6F1A923028B215B8009FBF28 /* ArithmeticOperationButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArithmeticOperationButtons.swift; sourceTree = ""; }; 6F1A923228B215C2009FBF28 /* NumberPadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberPadView.swift; sourceTree = ""; }; - 6F1A923528B21E70009FBF28 /* Ex+Double.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Ex+Double.swift"; sourceTree = ""; }; 6F1A923A28B22610009FBF28 /* CircleButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleButton.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -68,7 +66,6 @@ isa = PBXGroup; children = ( 6F1A921728B13A71009FBF28 /* CalculatorApp.swift */, - 6F1A923428B21E45009FBF28 /* Extensions */, 6F1A922528B13A93009FBF28 /* Model */, 6F1A922628B13A9B009FBF28 /* ViewModel */, 6F1A922728B13AA3009FBF28 /* View */, @@ -111,14 +108,6 @@ path = View; sourceTree = ""; }; - 6F1A923428B21E45009FBF28 /* Extensions */ = { - isa = PBXGroup; - children = ( - 6F1A923528B21E70009FBF28 /* Ex+Double.swift */, - ); - path = Extensions; - sourceTree = ""; - }; 6F1A923928B22476009FBF28 /* SubViews */ = { isa = PBXGroup; children = ( @@ -201,7 +190,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 6F1A923628B21E70009FBF28 /* Ex+Double.swift in Sources */, 6F1A922928B13AE8009FBF28 /* CalculatorModel.swift in Sources */, 6F1A921A28B13A71009FBF28 /* ContentView.swift in Sources */, 6F1A922D28B2159B009FBF28 /* ResultView.swift in Sources */, diff --git a/week4/Calculator/Calculator/Extensions/Ex+Double.swift b/week4/Calculator/Calculator/Extensions/Ex+Double.swift deleted file mode 100644 index 3450af2..0000000 --- a/week4/Calculator/Calculator/Extensions/Ex+Double.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// Ex+Double.swift -// Calculator -// -// Created by 임주민 on 2022/08/21. -// - -import Foundation - -extension Double { - - var clean: String { - return self.truncatingRemainder(dividingBy: 1) == 0 ? String(format: "%.0f", self) : String(self) - } -} diff --git a/week4/Calculator/Calculator/Model/CalculatorModel.swift b/week4/Calculator/Calculator/Model/CalculatorModel.swift index 616c14b..cbbcee7 100644 --- a/week4/Calculator/Calculator/Model/CalculatorModel.swift +++ b/week4/Calculator/Calculator/Model/CalculatorModel.swift @@ -9,24 +9,24 @@ import Foundation struct Caculator { - var operation: ArithmeticOperation? var state: InputState = .newPreviousNumber - private(set) var showingText: Double - private(set) var previousNumber: Double? { + var operation: ArithmeticOperation? + private(set) var showingText: String + private(set) var previousNumber: Decimal? { didSet { guard let previousNumber = previousNumber else { return } - showingText = previousNumber + showingText = "\(previousNumber)" } } - private(set) var nextNumber: Double? { + private(set) var nextNumber: Decimal? { didSet { guard let nextNumber = nextNumber else { return } - showingText = nextNumber + showingText = "\(nextNumber)" } } - private(set) var result: Double { + private(set) var result: Decimal { didSet { - showingText = result + showingText = "\(result)" previousNumber = result } } @@ -45,7 +45,7 @@ struct Caculator { case ongoingNextNumber } - mutating func clickNumber(_ number: Double) { + mutating func clickNumber(_ number: Decimal) { switch state { case .newPreviousNumber: previousNumber = number @@ -53,15 +53,17 @@ struct Caculator { case .newNextNumber: nextNumber = number state = .ongoingNextNumber - case .ongoingPreviousNumber, .ongoingNextNumber: - joinNumbers(nextElement: number) + case .ongoingPreviousNumber: + previousNumber = joinNumbers(with: number) + case .ongoingNextNumber: + nextNumber = joinNumbers(with: number) } } mutating func calculate() { - guard let priorNumber = previousNumber else { return } - guard let laterNumber = nextNumber else { return } - guard let operation = operation else { return } + guard let priorNumber = previousNumber, + let laterNumber = nextNumber, + let operation = operation else { return } switch operation { case .multiply: result = priorNumber * laterNumber @@ -74,16 +76,10 @@ struct Caculator { } } - mutating func joinNumbers(nextElement: Double) { - if state == .ongoingPreviousNumber { - guard let previousElement = previousNumber else { return } - let joinedNumber = String(previousElement.clean) + String(nextElement.clean) - previousNumber = Double(joinedNumber) - } else if state == .ongoingNextNumber { - guard let previousElement = nextNumber else { return } - let joinedNumber = String(previousElement.clean) + String(nextElement.clean) - nextNumber = Double(joinedNumber) - } + mutating func joinNumbers(with nextElement: Decimal) -> Decimal { + let joinedString = showingText + "\(nextElement)" + guard let joinedNumber = Decimal(string: joinedString) else { return 0 } + return joinedNumber } mutating func changeSign() { @@ -93,8 +89,16 @@ struct Caculator { } mutating func percent() { + guard let showingText = Decimal(string: showingText) else { return } result = showingText / 100 } - func addPoint() { } + mutating func addPoint() { + if !showingText.contains(".") { + showingText = showingText + "." + if showingText.compare("0") == .orderedSame { + state = .ongoingPreviousNumber + } + } + } } diff --git a/week4/Calculator/Calculator/View/SubViews/NumberPadView.swift b/week4/Calculator/Calculator/View/SubViews/NumberPadView.swift index c30f9fa..a1418b2 100644 --- a/week4/Calculator/Calculator/View/SubViews/NumberPadView.swift +++ b/week4/Calculator/Calculator/View/SubViews/NumberPadView.swift @@ -11,7 +11,7 @@ struct NumberPadView: View { @ObservedObject var calculatorManager: CalculatorManager - let positiveNumbers: [Double] = [1,2,3,4,5,6,7,8,9] + let positiveNumbers: [Decimal] = [1,2,3,4,5,6,7,8,9] var body: some View { VStack { @@ -29,13 +29,16 @@ struct NumberPadView: View { Button { calculatorManager.clickNumber(number) } label: { - CircleButton(content: number.clean, color: .gray) + CircleButton(content: "\(number)", color: .gray) } } } } var zeroButton: some View { + Button { + calculatorManager.clickNumber(0) + } label: { RoundedRectangle(cornerRadius: 35) .frame(width: 140, height: 70) .foregroundColor(.gray) @@ -43,14 +46,16 @@ struct NumberPadView: View { Text("0") .font(.system(size: 40)) .padding(.trailing, 60) - .onTapGesture { - calculatorManager.clickNumber(0) - } } + } } var pointButton: some View { - CircleButton(content: ".", color: .gray) + Button { + calculatorManager.addPoint() + } label: { + CircleButton(content: ".", color: .gray) + } } } diff --git a/week4/Calculator/Calculator/View/SubViews/ResultView.swift b/week4/Calculator/Calculator/View/SubViews/ResultView.swift index 9af92ea..7f7323a 100644 --- a/week4/Calculator/Calculator/View/SubViews/ResultView.swift +++ b/week4/Calculator/Calculator/View/SubViews/ResultView.swift @@ -9,12 +9,12 @@ import SwiftUI struct ResultView: View { - let showingText: Double + let showingText: String var body: some View { HStack { Spacer() - Text(showingText.clean) + Text(showingText) .font(.system(size: 60)) .padding(.trailing, 50) } diff --git a/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift b/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift index f0118e8..cde48e5 100644 --- a/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift +++ b/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift @@ -5,24 +5,23 @@ // Created by 임주민 on 2022/08/21. // -import Foundation import SwiftUI class CalculatorManager: ObservableObject { - private static func create() -> Caculator { - return Caculator(operation: nil, showingText: 0, previousNumber: nil, nextNumber: nil, result: 0) - } - @Published private var calculateModel = create() - var showingText: Double { + var showingText: String { calculateModel.showingText } + private static func create() -> Caculator { + return Caculator(showingText: "0", result: 0) + } + // MARK: - Intentions - func clickNumber(_ number: Double) { + func clickNumber(_ number: Decimal) { calculateModel.clickNumber(number) } From 89def301af634715634b9c6470fd948ee73883b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Fri, 26 Aug 2022 15:48:32 +0900 Subject: [PATCH 16/22] Fix error of changing sign & percentage feature --- .../Calculator/Model/CalculatorModel.swift | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/week4/Calculator/Calculator/Model/CalculatorModel.swift b/week4/Calculator/Calculator/Model/CalculatorModel.swift index cbbcee7..46cb1a7 100644 --- a/week4/Calculator/Calculator/Model/CalculatorModel.swift +++ b/week4/Calculator/Calculator/Model/CalculatorModel.swift @@ -28,6 +28,7 @@ struct Caculator { didSet { showingText = "\(result)" previousNumber = result + nextNumber = nil } } @@ -84,13 +85,22 @@ struct Caculator { mutating func changeSign() { if let priorNumber = previousNumber { - previousNumber = -priorNumber + if let afterNumber = nextNumber { + nextNumber = -afterNumber + } else { + previousNumber = -priorNumber + } } } mutating func percent() { - guard let showingText = Decimal(string: showingText) else { return } - result = showingText / 100 + if let priorNumber = previousNumber { + if let afterNumber = nextNumber { + nextNumber = afterNumber / 100 + } else { + previousNumber = priorNumber / 100 + } + } } mutating func addPoint() { From 16e3a037e3af2cf90a8d6bd383c9c94ddb4f20c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Fri, 26 Aug 2022 20:44:07 +0900 Subject: [PATCH 17/22] Combine project --- .../Calculator.xcodeproj/project.pbxproj | 36 +++--- .../Calculator/Extensions/Ex+Color.swift | 23 ++++ .../Calculator/Model/CalculatorModel.swift | 116 ++++++++++++++++-- .../Calculator/View/ContentView.swift | 21 ++-- .../SubViews/ArithmeticOperationButtons.swift | 63 ---------- .../View/SubViews/CalculatorButtonGrid.swift | 88 +++++++++++++ .../View/SubViews/CircleButton.swift | 38 ++++-- .../View/SubViews/FunctionButtons.swift | 45 ------- .../View/SubViews/NumberPadView.swift | 67 ---------- .../Calculator/View/SubViews/ResultView.swift | 15 ++- .../ViewModel/CaculatorManager.swift | 62 +++++++++- 11 files changed, 344 insertions(+), 230 deletions(-) create mode 100644 week4/Calculator/Calculator/Extensions/Ex+Color.swift delete mode 100644 week4/Calculator/Calculator/View/SubViews/ArithmeticOperationButtons.swift create mode 100644 week4/Calculator/Calculator/View/SubViews/CalculatorButtonGrid.swift delete mode 100644 week4/Calculator/Calculator/View/SubViews/FunctionButtons.swift delete mode 100644 week4/Calculator/Calculator/View/SubViews/NumberPadView.swift diff --git a/week4/Calculator/Calculator.xcodeproj/project.pbxproj b/week4/Calculator/Calculator.xcodeproj/project.pbxproj index ec25c96..96e28b4 100644 --- a/week4/Calculator/Calculator.xcodeproj/project.pbxproj +++ b/week4/Calculator/Calculator.xcodeproj/project.pbxproj @@ -14,10 +14,9 @@ 6F1A922928B13AE8009FBF28 /* CalculatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922828B13AE8009FBF28 /* CalculatorModel.swift */; }; 6F1A922B28B13B04009FBF28 /* CaculatorManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922A28B13B04009FBF28 /* CaculatorManager.swift */; }; 6F1A922D28B2159B009FBF28 /* ResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922C28B2159B009FBF28 /* ResultView.swift */; }; - 6F1A922F28B215AB009FBF28 /* FunctionButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922E28B215AA009FBF28 /* FunctionButtons.swift */; }; - 6F1A923128B215B8009FBF28 /* ArithmeticOperationButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923028B215B8009FBF28 /* ArithmeticOperationButtons.swift */; }; - 6F1A923328B215C2009FBF28 /* NumberPadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923228B215C2009FBF28 /* NumberPadView.swift */; }; - 6F1A923B28B22610009FBF28 /* CircleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A923A28B22610009FBF28 /* CircleButton.swift */; }; + 6F2AB33028B8D9970054B4BB /* CalculatorButtonGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB32F28B8D9970054B4BB /* CalculatorButtonGrid.swift */; }; + 6F2AB33228B8D9A60054B4BB /* CircleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB33128B8D9A60054B4BB /* CircleButton.swift */; }; + 6F2AB33528B8DC930054B4BB /* Ex+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB33428B8DC930054B4BB /* Ex+Color.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -29,10 +28,9 @@ 6F1A922828B13AE8009FBF28 /* CalculatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorModel.swift; sourceTree = ""; }; 6F1A922A28B13B04009FBF28 /* CaculatorManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaculatorManager.swift; sourceTree = ""; }; 6F1A922C28B2159B009FBF28 /* ResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultView.swift; sourceTree = ""; }; - 6F1A922E28B215AA009FBF28 /* FunctionButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FunctionButtons.swift; sourceTree = ""; }; - 6F1A923028B215B8009FBF28 /* ArithmeticOperationButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArithmeticOperationButtons.swift; sourceTree = ""; }; - 6F1A923228B215C2009FBF28 /* NumberPadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberPadView.swift; sourceTree = ""; }; - 6F1A923A28B22610009FBF28 /* CircleButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleButton.swift; sourceTree = ""; }; + 6F2AB32F28B8D9970054B4BB /* CalculatorButtonGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorButtonGrid.swift; sourceTree = ""; }; + 6F2AB33128B8D9A60054B4BB /* CircleButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleButton.swift; sourceTree = ""; }; + 6F2AB33428B8DC930054B4BB /* Ex+Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Ex+Color.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -66,6 +64,7 @@ isa = PBXGroup; children = ( 6F1A921728B13A71009FBF28 /* CalculatorApp.swift */, + 6F2AB33328B8DC800054B4BB /* Extensions */, 6F1A922528B13A93009FBF28 /* Model */, 6F1A922628B13A9B009FBF28 /* ViewModel */, 6F1A922728B13AA3009FBF28 /* View */, @@ -112,14 +111,20 @@ isa = PBXGroup; children = ( 6F1A922C28B2159B009FBF28 /* ResultView.swift */, - 6F1A922E28B215AA009FBF28 /* FunctionButtons.swift */, - 6F1A923028B215B8009FBF28 /* ArithmeticOperationButtons.swift */, - 6F1A923228B215C2009FBF28 /* NumberPadView.swift */, - 6F1A923A28B22610009FBF28 /* CircleButton.swift */, + 6F2AB32F28B8D9970054B4BB /* CalculatorButtonGrid.swift */, + 6F2AB33128B8D9A60054B4BB /* CircleButton.swift */, ); path = SubViews; sourceTree = ""; }; + 6F2AB33328B8DC800054B4BB /* Extensions */ = { + isa = PBXGroup; + children = ( + 6F2AB33428B8DC930054B4BB /* Ex+Color.swift */, + ); + path = Extensions; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -190,15 +195,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 6F2AB33528B8DC930054B4BB /* Ex+Color.swift in Sources */, 6F1A922928B13AE8009FBF28 /* CalculatorModel.swift in Sources */, 6F1A921A28B13A71009FBF28 /* ContentView.swift in Sources */, 6F1A922D28B2159B009FBF28 /* ResultView.swift in Sources */, 6F1A922B28B13B04009FBF28 /* CaculatorManager.swift in Sources */, - 6F1A923B28B22610009FBF28 /* CircleButton.swift in Sources */, 6F1A921828B13A71009FBF28 /* CalculatorApp.swift in Sources */, - 6F1A923128B215B8009FBF28 /* ArithmeticOperationButtons.swift in Sources */, - 6F1A922F28B215AB009FBF28 /* FunctionButtons.swift in Sources */, - 6F1A923328B215C2009FBF28 /* NumberPadView.swift in Sources */, + 6F2AB33228B8D9A60054B4BB /* CircleButton.swift in Sources */, + 6F2AB33028B8D9970054B4BB /* CalculatorButtonGrid.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/week4/Calculator/Calculator/Extensions/Ex+Color.swift b/week4/Calculator/Calculator/Extensions/Ex+Color.swift new file mode 100644 index 0000000..999d5fa --- /dev/null +++ b/week4/Calculator/Calculator/Extensions/Ex+Color.swift @@ -0,0 +1,23 @@ +// +// Ex+Color.swift +// Calculator +// +// Created by 임주민 on 2022/08/26. +// + +import SwiftUI + +extension Color { + init(hex: String) { + let scanner = Scanner(string: hex) + _ = scanner.scanString("#") + + var rgb: UInt64 = 0 + scanner.scanHexInt64(&rgb) + + let r = Double((rgb >> 16) & 0xFF) / 255.0 + let g = Double((rgb >> 8) & 0xFF) / 255.0 + let b = Double((rgb >> 0) & 0xFF) / 255.0 + self.init(red: r, green: g, blue: b) + } +} diff --git a/week4/Calculator/Calculator/Model/CalculatorModel.swift b/week4/Calculator/Calculator/Model/CalculatorModel.swift index 46cb1a7..0ded00e 100644 --- a/week4/Calculator/Calculator/Model/CalculatorModel.swift +++ b/week4/Calculator/Calculator/Model/CalculatorModel.swift @@ -7,11 +7,11 @@ import Foundation -struct Caculator { +struct Calculator { var state: InputState = .newPreviousNumber var operation: ArithmeticOperation? - private(set) var showingText: String + var showingText: String private(set) var previousNumber: Decimal? { didSet { guard let previousNumber = previousNumber else { return } @@ -32,13 +32,6 @@ struct Caculator { } } - enum ArithmeticOperation { - case multiply - case divide - case minus - case plus - } - enum InputState { case newPreviousNumber case newNextNumber @@ -74,6 +67,8 @@ struct Caculator { result = priorNumber - laterNumber case .plus: result = priorNumber + laterNumber + default: + print("error") } } @@ -112,3 +107,106 @@ struct Caculator { } } } + +extension Calculator { + + enum ArithmeticOperation: Identifiable, Equatable { + case equal + case plus(Bool) + case minus(Bool) + case multiply(Bool) + case divide(Bool) + case percent + case sign + case clear(clearType) + case number(Decimal) + case point + + var id: String { return content } + + var content: String { + switch self { + case .equal: + return "=" + case .plus: + return "+" + case .minus: + return "-" + case .multiply: + return "*" + case .divide: + return "/" + case .percent: + return "%" + case .sign: + return "±" + case .clear(.displayResult): + return "C" + case .clear(.allData): + return "AC" + case .number(let value): + return "\(value)" + case .point: + return "." + } + } + + var isActive: Bool { + switch self { + case .plus(let isActive), .multiply(let isActive), .minus(let isActive), .divide(let isActive): + return isActive + default: + return false + } + } + + var backgroundColor: String { + switch self { + case .clear, .percent, .sign: + return "#A0A0A0" + case .number, .point: + return "#323232" + case .plus, .divide, .multiply, .minus, .equal: + return isActive ? "#FFFFFF" : "#E99D39" + } + } + + var textColor: String { + switch self { + case .clear, .percent, .sign: + return "#000000" + case .plus, .divide, .multiply, .minus, .equal, .number, .point: + return isActive ? "#E99D39" : "#FFFFFF" + } + } + + var buttonSize: Int { + switch self { + case .number(0): + return 2 + default: + return 1 + } + } + + mutating func activeButton(active: Bool) { + switch self { + case .divide: + self = .divide(active) + case .multiply: + self = .multiply(active) + case .minus: + self = .minus(active) + case .plus: + self = .plus(active) + default: + break + } + } + } + + enum clearType { + case allData + case displayResult + } +} diff --git a/week4/Calculator/Calculator/View/ContentView.swift b/week4/Calculator/Calculator/View/ContentView.swift index 4987094..562218e 100644 --- a/week4/Calculator/Calculator/View/ContentView.swift +++ b/week4/Calculator/Calculator/View/ContentView.swift @@ -15,23 +15,16 @@ struct ContentView: View { ZStack{ Color.black.ignoresSafeArea() VStack { + Spacer() ResultView(showingText: calculatorManager.showingText) - HStack { - VStack { - FunctionButtons(calculatorManager: calculatorManager) - NumberPadView(calculatorManager: calculatorManager) + CalculatorButtonGrid(items: calculatorManager.buttonList , columnsCount: 4) { item in + Button { + calculatorManager.click(item) + } label: { + CircleButton(buttonData: item) } - ArithmeticOperationButtons(calculatorManager: calculatorManager) } } - }.foregroundColor(.white) - } -} - -struct ContentView_Previews: PreviewProvider { - - static var previews: some View { - let caculatorManager = CalculatorManager() - ContentView(calculatorManager: caculatorManager) + } } } diff --git a/week4/Calculator/Calculator/View/SubViews/ArithmeticOperationButtons.swift b/week4/Calculator/Calculator/View/SubViews/ArithmeticOperationButtons.swift deleted file mode 100644 index 9279526..0000000 --- a/week4/Calculator/Calculator/View/SubViews/ArithmeticOperationButtons.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// ArithmeticOperationButtons.swift -// Calculator -// -// Created by 임주민 on 2022/08/21. -// - -import SwiftUI - -struct ArithmeticOperationButtons: View { - - @ObservedObject var calculatorManager: CalculatorManager - - var body: some View { - VStack { - divideButton - multiplyButton - minusButton - plusButton - calculateButton - } - } - - var divideButton: some View { - Button { - calculatorManager.clickOperation(.divide) - } label: { - CircleButton(content: "/", color: .orange) - } - } - - var multiplyButton: some View { - Button { - calculatorManager.clickOperation(.multiply) - } label: { - CircleButton(content: "x", color: .orange) - } - } - - var minusButton: some View { - Button { - calculatorManager.clickOperation(.minus) - } label: { - CircleButton(content: "-", color: .orange) - } - } - - var plusButton: some View { - Button { - calculatorManager.clickOperation(.plus) - } label: { - CircleButton(content: "+", color: .orange) - } - } - - var calculateButton: some View { - Button { - calculatorManager.calculate() - } label: { - CircleButton(content: "=", color: .orange) - } - } -} diff --git a/week4/Calculator/Calculator/View/SubViews/CalculatorButtonGrid.swift b/week4/Calculator/Calculator/View/SubViews/CalculatorButtonGrid.swift new file mode 100644 index 0000000..f568cb7 --- /dev/null +++ b/week4/Calculator/Calculator/View/SubViews/CalculatorButtonGrid.swift @@ -0,0 +1,88 @@ +// +// CalculatorButtonGrid.swift +// Calculator +// +// Created by changgyo seo on 2022/08/26. +// + +import SwiftUI + +struct CalculatorButtonGrid: View { + + let buttonDataArray: [ButtonDataArray] + let columnsCount: Int + let content: (Calculator.ArithmeticOperation) -> buttonListView + + var body: some View { + VStack(spacing: 0) { + ForEach(buttonDataArray) { items in + HStack(spacing: 0) { + ForEach(items.array) { item in + let width = widthForColumnsCount(in: UIScreen.main.bounds.size, + buttonWidth: item.buttonSize) + let height = width / CGFloat(item.buttonSize) + content(item) + .padding(5) + .frame(width: width, height: height, alignment: .center) + } + } + } + } + } + + init(items: [Calculator.ArithmeticOperation], columnsCount: Int, @ViewBuilder content: @escaping (Calculator.ArithmeticOperation) -> buttonListView) { + self.buttonDataArray = ButtonDataArray.makeTwoDemsionArrayForColumnsCount(items: items, columnsCount: columnsCount) + self.columnsCount = columnsCount + self.content = content + } + + private func widthForColumnsCount(in size: CGSize, buttonWidth width: Int) -> CGFloat { + let sizeForOne = size.width / CGFloat(columnsCount) + return sizeForOne * CGFloat(width) + } +} + +extension CalculatorButtonGrid { + + struct ButtonDataArray: Identifiable { + var id: Int + var array: [Calculator.ArithmeticOperation] + var totalWidth: Int + + init() { + array = [Calculator.ArithmeticOperation]() + id = 1 + totalWidth = 0 + } + + mutating func append(_ newItem: Calculator.ArithmeticOperation) { + array.append(newItem) + } + + mutating func changeId() { + id += 1 + totalWidth = 0 + array.removeAll() + } + + static func makeTwoDemsionArrayForColumnsCount(items: [Calculator.ArithmeticOperation], columnsCount: Int) -> [ButtonDataArray] { + var arrayWithColumnsCount = ButtonDataArray() + var TwoDemesionArray = [ButtonDataArray]() + var rowWidth = 0 + items.forEach { item in + if rowWidth + item.buttonSize <= columnsCount { + arrayWithColumnsCount.append(item) + rowWidth += item.buttonSize + } else { + arrayWithColumnsCount.totalWidth = rowWidth + TwoDemesionArray.append(arrayWithColumnsCount) + arrayWithColumnsCount.changeId() + arrayWithColumnsCount.append(item) + rowWidth = item.buttonSize + } + } + TwoDemesionArray.append(arrayWithColumnsCount) + return TwoDemesionArray + } + } +} diff --git a/week4/Calculator/Calculator/View/SubViews/CircleButton.swift b/week4/Calculator/Calculator/View/SubViews/CircleButton.swift index 099c11c..3527b32 100644 --- a/week4/Calculator/Calculator/View/SubViews/CircleButton.swift +++ b/week4/Calculator/Calculator/View/SubViews/CircleButton.swift @@ -1,24 +1,42 @@ // -// CircleButton.swift +// ButtonView.swift // Calculator // -// Created by 임주민 on 2022/08/21. +// Created by changgyo seo on 2022/08/26. // import SwiftUI struct CircleButton: View { - let content: String - let color: Color + var buttonData: Calculator.ArithmeticOperation var body: some View { - Circle() - .frame(width: 70, height: 70) - .foregroundColor(color) - .overlay { - Text(content) - .font(.system(size: 40)) + GeometryReader { geometry in + ZStack { + RoundedRectangle(cornerRadius: cornerRadius(in: geometry.size)) + .foregroundColor(Color(hex: buttonData.backgroundColor)) + Text(buttonData.content) + .foregroundColor(Color(hex: buttonData.textColor)) + .font(font(in: geometry.size)) } + } + } + + private func cornerRadius(in size: CGSize) -> CGFloat { + size.width * DrawConstans.buttonCornerRadius + } + + private func font(in size: CGSize) -> Font { + Font.system(size: size.height * DrawConstans.fontSizeScale) } } + +extension CircleButton { + + private enum DrawConstans { + static let buttonCornerRadius = 0.5 + static let fontSizeScale = 0.4 + } +} + diff --git a/week4/Calculator/Calculator/View/SubViews/FunctionButtons.swift b/week4/Calculator/Calculator/View/SubViews/FunctionButtons.swift deleted file mode 100644 index 6226efe..0000000 --- a/week4/Calculator/Calculator/View/SubViews/FunctionButtons.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// FunctionButtons.swift -// Calculator -// -// Created by 임주민 on 2022/08/21. -// - -import SwiftUI - -struct FunctionButtons: View { - - @ObservedObject var calculatorManager: CalculatorManager - - var body: some View { - HStack { - clearButton - changeSign - percentButton - } - } - - var clearButton: some View { - Button { - calculatorManager.clear() - } label: { - CircleButton(content: "C", color: .green) - } - } - - var changeSign: some View { - Button { - calculatorManager.changeSign() - } label: { - CircleButton(content: "+/-", color: .green) - } - } - - var percentButton: some View { - Button { - calculatorManager.percent() - } label: { - CircleButton(content: "%", color: .green) - } - } -} diff --git a/week4/Calculator/Calculator/View/SubViews/NumberPadView.swift b/week4/Calculator/Calculator/View/SubViews/NumberPadView.swift deleted file mode 100644 index a1418b2..0000000 --- a/week4/Calculator/Calculator/View/SubViews/NumberPadView.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// NumberPadView.swift -// Calculator -// -// Created by 임주민 on 2022/08/21. -// - -import SwiftUI - -struct NumberPadView: View { - - @ObservedObject var calculatorManager: CalculatorManager - - let positiveNumbers: [Decimal] = [1,2,3,4,5,6,7,8,9] - - var body: some View { - VStack { - positiveNumberButtons - HStack{ - zeroButton - pointButton - } - }.frame(width: 220) - } - - var positiveNumberButtons: some View { - LazyVGrid(columns: [GridItem(.adaptive(minimum: 60, maximum: 200))], spacing: 10) { - ForEach(positiveNumbers, id: \.self) { number in - Button { - calculatorManager.clickNumber(number) - } label: { - CircleButton(content: "\(number)", color: .gray) - } - } - } - } - - var zeroButton: some View { - Button { - calculatorManager.clickNumber(0) - } label: { - RoundedRectangle(cornerRadius: 35) - .frame(width: 140, height: 70) - .foregroundColor(.gray) - .overlay { - Text("0") - .font(.system(size: 40)) - .padding(.trailing, 60) - } - } - } - - var pointButton: some View { - Button { - calculatorManager.addPoint() - } label: { - CircleButton(content: ".", color: .gray) - } - } -} - -struct NumberPadView_Previews: PreviewProvider { - static var previews: some View { - let caculatorManager = CalculatorManager() - NumberPadView(calculatorManager: caculatorManager) - } -} diff --git a/week4/Calculator/Calculator/View/SubViews/ResultView.swift b/week4/Calculator/Calculator/View/SubViews/ResultView.swift index 7f7323a..5b35549 100644 --- a/week4/Calculator/Calculator/View/SubViews/ResultView.swift +++ b/week4/Calculator/Calculator/View/SubViews/ResultView.swift @@ -15,8 +15,19 @@ struct ResultView: View { HStack { Spacer() Text(showingText) - .font(.system(size: 60)) - .padding(.trailing, 50) + .lineLimit(1) + .foregroundColor(.white) + .font(.system(size: DrawConstans.fontSize)) + .minimumScaleFactor(DrawConstans.fontMinimumSacleFactor) + .padding(.trailing, DrawConstans.textPadding) } } } + +extension ResultView { + private enum DrawConstans { + static let fontSize = 80.0 + static let fontMinimumSacleFactor = 0.5 + static let textPadding = 20.0 + } +} diff --git a/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift b/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift index cde48e5..10886d2 100644 --- a/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift +++ b/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift @@ -10,22 +10,76 @@ import SwiftUI class CalculatorManager: ObservableObject { @Published private var calculateModel = create() + @Published var buttonList: [Calculator.ArithmeticOperation] = + [ + .clear(.allData), .sign, .percent, .divide(false), + .number(7), .number(8), .number(9), .multiply(false), + .number(4), .number(5), .number(6), .minus(false), + .number(1), .number(2), .number(3), .plus(false), + .number(0), .point, .equal + ] var showingText: String { - calculateModel.showingText + get { + if calculateModel.showingText != "0" { + guard let clearIndex = buttonList.firstIndex(of: .clear(.allData)) else { return calculateModel.showingText } + buttonList[clearIndex] = .clear(.displayResult) + } else { + guard let clearIndex = buttonList.firstIndex(of: .clear(.displayResult)) else { return calculateModel.showingText } + buttonList[clearIndex] = .clear(.allData) + } + return calculateModel.showingText + } + set { calculateModel.showingText = newValue } } - private static func create() -> Caculator { - return Caculator(showingText: "0", result: 0) + private static func create() -> Calculator { + return Calculator(showingText: "0", result: 0) } // MARK: - Intentions + func click(_ clickType: Calculator.ArithmeticOperation) { + switch clickType { + case .number(let decimal): + for index in buttonList.indices { + if buttonList[index].isActive { + switch buttonList[index] { + case .plus, .minus, .divide, .multiply: + buttonList[index].activeButton(active: false) + default: + print("error") + } + } + } + clickNumber(decimal) + case .equal: + calculate() + case .clear(.allData): + clear() + case .clear(.displayResult): + showingText = "0" + case .sign: + changeSign() + case .percent: + percent() + case .point: + addPoint() + default: + for index in buttonList.indices { + buttonList[index] == clickType + ? buttonList[index].activeButton(active: true) + : buttonList[index].activeButton(active: false) + } + clickOperation(clickType) + } + } + func clickNumber(_ number: Decimal) { calculateModel.clickNumber(number) } - func clickOperation(_ operation: Caculator.ArithmeticOperation?) { + func clickOperation(_ operation: Calculator.ArithmeticOperation?) { calculateModel.operation = operation calculateModel.state = .newNextNumber } From 6e606ecb8a20e411d7cb455591cac6d5becf1845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Fri, 26 Aug 2022 23:32:18 +0900 Subject: [PATCH 18/22] Add swipe gesture and rename --- .../Calculator.xcodeproj/project.pbxproj | 20 +++++++++++-------- .../Calculator/Extensions/Ex+Color.swift | 3 +-- .../Calculator/Extensions/Ex+String.swift | 17 ++++++++++++++++ .../Calculator/Model/CalculatorModel.swift | 10 ++++++++-- .../Calculator/View/ContentView.swift | 8 ++++++-- .../{CircleButton.swift => NumberPad.swift} | 7 +++---- ...orButtonGrid.swift => NumberPadGrid.swift} | 7 ++++--- .../Calculator/View/SubViews/ResultView.swift | 2 ++ .../ViewModel/CaculatorManager.swift | 8 ++++++++ 9 files changed, 61 insertions(+), 21 deletions(-) create mode 100644 week4/Calculator/Calculator/Extensions/Ex+String.swift rename week4/Calculator/Calculator/View/SubViews/{CircleButton.swift => NumberPad.swift} (91%) rename week4/Calculator/Calculator/View/SubViews/{CalculatorButtonGrid.swift => NumberPadGrid.swift} (95%) diff --git a/week4/Calculator/Calculator.xcodeproj/project.pbxproj b/week4/Calculator/Calculator.xcodeproj/project.pbxproj index 96e28b4..026e5f3 100644 --- a/week4/Calculator/Calculator.xcodeproj/project.pbxproj +++ b/week4/Calculator/Calculator.xcodeproj/project.pbxproj @@ -14,9 +14,10 @@ 6F1A922928B13AE8009FBF28 /* CalculatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922828B13AE8009FBF28 /* CalculatorModel.swift */; }; 6F1A922B28B13B04009FBF28 /* CaculatorManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922A28B13B04009FBF28 /* CaculatorManager.swift */; }; 6F1A922D28B2159B009FBF28 /* ResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1A922C28B2159B009FBF28 /* ResultView.swift */; }; - 6F2AB33028B8D9970054B4BB /* CalculatorButtonGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB32F28B8D9970054B4BB /* CalculatorButtonGrid.swift */; }; - 6F2AB33228B8D9A60054B4BB /* CircleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB33128B8D9A60054B4BB /* CircleButton.swift */; }; + 6F2AB33028B8D9970054B4BB /* NumberPadGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB32F28B8D9970054B4BB /* NumberPadGrid.swift */; }; + 6F2AB33228B8D9A60054B4BB /* NumberPad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB33128B8D9A60054B4BB /* NumberPad.swift */; }; 6F2AB33528B8DC930054B4BB /* Ex+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB33428B8DC930054B4BB /* Ex+Color.swift */; }; + 6F2AB33728B90EB10054B4BB /* Ex+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2AB33628B90EB10054B4BB /* Ex+String.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -28,9 +29,10 @@ 6F1A922828B13AE8009FBF28 /* CalculatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorModel.swift; sourceTree = ""; }; 6F1A922A28B13B04009FBF28 /* CaculatorManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaculatorManager.swift; sourceTree = ""; }; 6F1A922C28B2159B009FBF28 /* ResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultView.swift; sourceTree = ""; }; - 6F2AB32F28B8D9970054B4BB /* CalculatorButtonGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorButtonGrid.swift; sourceTree = ""; }; - 6F2AB33128B8D9A60054B4BB /* CircleButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleButton.swift; sourceTree = ""; }; + 6F2AB32F28B8D9970054B4BB /* NumberPadGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberPadGrid.swift; sourceTree = ""; }; + 6F2AB33128B8D9A60054B4BB /* NumberPad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberPad.swift; sourceTree = ""; }; 6F2AB33428B8DC930054B4BB /* Ex+Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Ex+Color.swift"; sourceTree = ""; }; + 6F2AB33628B90EB10054B4BB /* Ex+String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Ex+String.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -111,8 +113,8 @@ isa = PBXGroup; children = ( 6F1A922C28B2159B009FBF28 /* ResultView.swift */, - 6F2AB32F28B8D9970054B4BB /* CalculatorButtonGrid.swift */, - 6F2AB33128B8D9A60054B4BB /* CircleButton.swift */, + 6F2AB32F28B8D9970054B4BB /* NumberPadGrid.swift */, + 6F2AB33128B8D9A60054B4BB /* NumberPad.swift */, ); path = SubViews; sourceTree = ""; @@ -121,6 +123,7 @@ isa = PBXGroup; children = ( 6F2AB33428B8DC930054B4BB /* Ex+Color.swift */, + 6F2AB33628B90EB10054B4BB /* Ex+String.swift */, ); path = Extensions; sourceTree = ""; @@ -199,10 +202,11 @@ 6F1A922928B13AE8009FBF28 /* CalculatorModel.swift in Sources */, 6F1A921A28B13A71009FBF28 /* ContentView.swift in Sources */, 6F1A922D28B2159B009FBF28 /* ResultView.swift in Sources */, + 6F2AB33728B90EB10054B4BB /* Ex+String.swift in Sources */, 6F1A922B28B13B04009FBF28 /* CaculatorManager.swift in Sources */, 6F1A921828B13A71009FBF28 /* CalculatorApp.swift in Sources */, - 6F2AB33228B8D9A60054B4BB /* CircleButton.swift in Sources */, - 6F2AB33028B8D9970054B4BB /* CalculatorButtonGrid.swift in Sources */, + 6F2AB33228B8D9A60054B4BB /* NumberPad.swift in Sources */, + 6F2AB33028B8D9970054B4BB /* NumberPadGrid.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/week4/Calculator/Calculator/Extensions/Ex+Color.swift b/week4/Calculator/Calculator/Extensions/Ex+Color.swift index 999d5fa..2838f78 100644 --- a/week4/Calculator/Calculator/Extensions/Ex+Color.swift +++ b/week4/Calculator/Calculator/Extensions/Ex+Color.swift @@ -8,13 +8,12 @@ import SwiftUI extension Color { + init(hex: String) { let scanner = Scanner(string: hex) _ = scanner.scanString("#") - var rgb: UInt64 = 0 scanner.scanHexInt64(&rgb) - let r = Double((rgb >> 16) & 0xFF) / 255.0 let g = Double((rgb >> 8) & 0xFF) / 255.0 let b = Double((rgb >> 0) & 0xFF) / 255.0 diff --git a/week4/Calculator/Calculator/Extensions/Ex+String.swift b/week4/Calculator/Calculator/Extensions/Ex+String.swift new file mode 100644 index 0000000..69c9861 --- /dev/null +++ b/week4/Calculator/Calculator/Extensions/Ex+String.swift @@ -0,0 +1,17 @@ +// +// Ex+String.swift +// Calculator +// +// Created by 임주민 on 2022/08/26. +// + +import Foundation + +extension String { + + mutating func removeWithDecimal() { + let removedDecimal = (Decimal(string: self) ?? 0) / 10 + let removedInteger = NSDecimalNumber(decimal: removedDecimal).intValue + self = String(removedInteger) + } +} diff --git a/week4/Calculator/Calculator/Model/CalculatorModel.swift b/week4/Calculator/Calculator/Model/CalculatorModel.swift index 0ded00e..eb0e7c3 100644 --- a/week4/Calculator/Calculator/Model/CalculatorModel.swift +++ b/week4/Calculator/Calculator/Model/CalculatorModel.swift @@ -10,7 +10,11 @@ import Foundation struct Calculator { var state: InputState = .newPreviousNumber - var operation: ArithmeticOperation? + var operation: ArithmeticOperation? { + willSet { + if nextNumber != nil { calculate() } + } + } var showingText: String private(set) var previousNumber: Decimal? { didSet { @@ -29,6 +33,7 @@ struct Calculator { showingText = "\(result)" previousNumber = result nextNumber = nil + state = .finishInput } } @@ -37,11 +42,12 @@ struct Calculator { case newNextNumber case ongoingPreviousNumber case ongoingNextNumber + case finishInput } mutating func clickNumber(_ number: Decimal) { switch state { - case .newPreviousNumber: + case .newPreviousNumber, .finishInput: previousNumber = number state = .ongoingPreviousNumber case .newNextNumber: diff --git a/week4/Calculator/Calculator/View/ContentView.swift b/week4/Calculator/Calculator/View/ContentView.swift index 562218e..e55c5e3 100644 --- a/week4/Calculator/Calculator/View/ContentView.swift +++ b/week4/Calculator/Calculator/View/ContentView.swift @@ -17,11 +17,15 @@ struct ContentView: View { VStack { Spacer() ResultView(showingText: calculatorManager.showingText) - CalculatorButtonGrid(items: calculatorManager.buttonList , columnsCount: 4) { item in + .gesture( + DragGesture() + .onEnded { _ in calculatorManager.slideToRemove() } + ) + NumberPadGrid(items: calculatorManager.buttonList , columnsCount: 4) { item in Button { calculatorManager.click(item) } label: { - CircleButton(buttonData: item) + NumberPad(buttonData: item) } } } diff --git a/week4/Calculator/Calculator/View/SubViews/CircleButton.swift b/week4/Calculator/Calculator/View/SubViews/NumberPad.swift similarity index 91% rename from week4/Calculator/Calculator/View/SubViews/CircleButton.swift rename to week4/Calculator/Calculator/View/SubViews/NumberPad.swift index 3527b32..29444a8 100644 --- a/week4/Calculator/Calculator/View/SubViews/CircleButton.swift +++ b/week4/Calculator/Calculator/View/SubViews/NumberPad.swift @@ -1,5 +1,5 @@ // -// ButtonView.swift +// NumberPad.swift // Calculator // // Created by changgyo seo on 2022/08/26. @@ -7,7 +7,7 @@ import SwiftUI -struct CircleButton: View { +struct NumberPad: View { var buttonData: Calculator.ArithmeticOperation @@ -32,11 +32,10 @@ struct CircleButton: View { } } -extension CircleButton { +extension NumberPad { private enum DrawConstans { static let buttonCornerRadius = 0.5 static let fontSizeScale = 0.4 } } - diff --git a/week4/Calculator/Calculator/View/SubViews/CalculatorButtonGrid.swift b/week4/Calculator/Calculator/View/SubViews/NumberPadGrid.swift similarity index 95% rename from week4/Calculator/Calculator/View/SubViews/CalculatorButtonGrid.swift rename to week4/Calculator/Calculator/View/SubViews/NumberPadGrid.swift index f568cb7..1bb1db6 100644 --- a/week4/Calculator/Calculator/View/SubViews/CalculatorButtonGrid.swift +++ b/week4/Calculator/Calculator/View/SubViews/NumberPadGrid.swift @@ -1,5 +1,5 @@ // -// CalculatorButtonGrid.swift +// NumberPadGrid.swift // Calculator // // Created by changgyo seo on 2022/08/26. @@ -7,7 +7,7 @@ import SwiftUI -struct CalculatorButtonGrid: View { +struct NumberPadGrid: View { let buttonDataArray: [ButtonDataArray] let columnsCount: Int @@ -42,9 +42,10 @@ struct CalculatorButtonGrid: View { } } -extension CalculatorButtonGrid { +extension NumberPadGrid { struct ButtonDataArray: Identifiable { + var id: Int var array: [Calculator.ArithmeticOperation] var totalWidth: Int diff --git a/week4/Calculator/Calculator/View/SubViews/ResultView.swift b/week4/Calculator/Calculator/View/SubViews/ResultView.swift index 5b35549..6c52efe 100644 --- a/week4/Calculator/Calculator/View/SubViews/ResultView.swift +++ b/week4/Calculator/Calculator/View/SubViews/ResultView.swift @@ -21,10 +21,12 @@ struct ResultView: View { .minimumScaleFactor(DrawConstans.fontMinimumSacleFactor) .padding(.trailing, DrawConstans.textPadding) } + .contentShape(Rectangle()) } } extension ResultView { + private enum DrawConstans { static let fontSize = 80.0 static let fontMinimumSacleFactor = 0.5 diff --git a/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift b/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift index 10886d2..0181495 100644 --- a/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift +++ b/week4/Calculator/Calculator/ViewModel/CaculatorManager.swift @@ -42,6 +42,10 @@ class CalculatorManager: ObservableObject { func click(_ clickType: Calculator.ArithmeticOperation) { switch clickType { case .number(let decimal): + if calculateModel.operation == nil, + calculateModel.state != .ongoingPreviousNumber { + calculateModel.state = .newPreviousNumber + } for index in buttonList.indices { if buttonList[index].isActive { switch buttonList[index] { @@ -96,6 +100,10 @@ class CalculatorManager: ObservableObject { calculateModel.changeSign() } + func slideToRemove() { + showingText.removeWithDecimal() + } + func percent() { calculateModel.percent() } From f343a4a2dc9aacbb5535f7a53afcd58462027ad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Thu, 1 Sep 2022 21:46:14 +0900 Subject: [PATCH 19/22] Add book model --- .../ApiTest/ApiTest.xcodeproj/project.pbxproj | 396 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + week5/ApiTest/ApiTest/APIConstants.swift | 18 + week5/ApiTest/ApiTest/ApiTestApp.swift | 18 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 93 ++++ .../ApiTest/Assets.xcassets/Contents.json | 6 + week5/ApiTest/ApiTest/Model/Book.swift | 44 ++ .../Preview Assets.xcassets/Contents.json | 6 + 10 files changed, 607 insertions(+) create mode 100644 week5/ApiTest/ApiTest.xcodeproj/project.pbxproj create mode 100644 week5/ApiTest/ApiTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 week5/ApiTest/ApiTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 week5/ApiTest/ApiTest/APIConstants.swift create mode 100644 week5/ApiTest/ApiTest/ApiTestApp.swift create mode 100644 week5/ApiTest/ApiTest/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 week5/ApiTest/ApiTest/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 week5/ApiTest/ApiTest/Assets.xcassets/Contents.json create mode 100644 week5/ApiTest/ApiTest/Model/Book.swift create mode 100644 week5/ApiTest/ApiTest/Preview Content/Preview Assets.xcassets/Contents.json diff --git a/week5/ApiTest/ApiTest.xcodeproj/project.pbxproj b/week5/ApiTest/ApiTest.xcodeproj/project.pbxproj new file mode 100644 index 0000000..eea4ba8 --- /dev/null +++ b/week5/ApiTest/ApiTest.xcodeproj/project.pbxproj @@ -0,0 +1,396 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 55; + objects = { + +/* Begin PBXBuildFile section */ + 6FB0595328C073A5004C8C20 /* ApiTestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0595228C073A5004C8C20 /* ApiTestApp.swift */; }; + 6FB0595728C073A6004C8C20 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FB0595628C073A6004C8C20 /* Assets.xcassets */; }; + 6FB0595A28C073A6004C8C20 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FB0595928C073A6004C8C20 /* Preview Assets.xcassets */; }; + 6FB0596128C073F0004C8C20 /* BookList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0596028C073F0004C8C20 /* BookList.swift */; }; + 6FB0596328C0760C004C8C20 /* BookFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0596228C0760C004C8C20 /* BookFinder.swift */; }; + 6FB0596528C07622004C8C20 /* Book.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0596428C07622004C8C20 /* Book.swift */; }; + 6FB0596A28C08B98004C8C20 /* APIConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0596928C08B98004C8C20 /* APIConstants.swift */; }; + 6FB0596E28C0C084004C8C20 /* BookCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0596D28C0C084004C8C20 /* BookCell.swift */; }; + 6FB0597128C0D353004C8C20 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0597028C0D353004C8C20 /* String+Extension.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 6FB0594F28C073A5004C8C20 /* ApiTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ApiTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 6FB0595228C073A5004C8C20 /* ApiTestApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiTestApp.swift; sourceTree = ""; }; + 6FB0595628C073A6004C8C20 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 6FB0595928C073A6004C8C20 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 6FB0596028C073F0004C8C20 /* BookList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookList.swift; sourceTree = ""; }; + 6FB0596228C0760C004C8C20 /* BookFinder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookFinder.swift; sourceTree = ""; }; + 6FB0596428C07622004C8C20 /* Book.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Book.swift; sourceTree = ""; }; + 6FB0596928C08B98004C8C20 /* APIConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIConstants.swift; sourceTree = ""; }; + 6FB0596D28C0C084004C8C20 /* BookCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookCell.swift; sourceTree = ""; }; + 6FB0597028C0D353004C8C20 /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6FB0594C28C073A5004C8C20 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6FB0594628C073A5004C8C20 = { + isa = PBXGroup; + children = ( + 6FB0595128C073A5004C8C20 /* ApiTest */, + 6FB0595028C073A5004C8C20 /* Products */, + ); + sourceTree = ""; + }; + 6FB0595028C073A5004C8C20 /* Products */ = { + isa = PBXGroup; + children = ( + 6FB0594F28C073A5004C8C20 /* ApiTest.app */, + ); + name = Products; + sourceTree = ""; + }; + 6FB0595128C073A5004C8C20 /* ApiTest */ = { + isa = PBXGroup; + children = ( + 6FB0595228C073A5004C8C20 /* ApiTestApp.swift */, + 6FB0596928C08B98004C8C20 /* APIConstants.swift */, + 6FB0596F28C0D325004C8C20 /* Extensions */, + 6FB0596628C07628004C8C20 /* Model */, + 6FB0596728C0762B004C8C20 /* ViewModel */, + 6FB0596828C07638004C8C20 /* View */, + 6FB0595628C073A6004C8C20 /* Assets.xcassets */, + 6FB0595828C073A6004C8C20 /* Preview Content */, + ); + path = ApiTest; + sourceTree = ""; + }; + 6FB0595828C073A6004C8C20 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 6FB0595928C073A6004C8C20 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 6FB0596628C07628004C8C20 /* Model */ = { + isa = PBXGroup; + children = ( + 6FB0596428C07622004C8C20 /* Book.swift */, + ); + path = Model; + sourceTree = ""; + }; + 6FB0596728C0762B004C8C20 /* ViewModel */ = { + isa = PBXGroup; + children = ( + 6FB0596228C0760C004C8C20 /* BookFinder.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; + 6FB0596828C07638004C8C20 /* View */ = { + isa = PBXGroup; + children = ( + 6FB0596028C073F0004C8C20 /* BookList.swift */, + 6FB0596D28C0C084004C8C20 /* BookCell.swift */, + ); + path = View; + sourceTree = ""; + }; + 6FB0596F28C0D325004C8C20 /* Extensions */ = { + isa = PBXGroup; + children = ( + 6FB0597028C0D353004C8C20 /* String+Extension.swift */, + ); + path = Extensions; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 6FB0594E28C073A5004C8C20 /* ApiTest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6FB0595D28C073A6004C8C20 /* Build configuration list for PBXNativeTarget "ApiTest" */; + buildPhases = ( + 6FB0594B28C073A5004C8C20 /* Sources */, + 6FB0594C28C073A5004C8C20 /* Frameworks */, + 6FB0594D28C073A5004C8C20 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ApiTest; + productName = ApiTest; + productReference = 6FB0594F28C073A5004C8C20 /* ApiTest.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6FB0594728C073A5004C8C20 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1340; + LastUpgradeCheck = 1340; + TargetAttributes = { + 6FB0594E28C073A5004C8C20 = { + CreatedOnToolsVersion = 13.4.1; + }; + }; + }; + buildConfigurationList = 6FB0594A28C073A5004C8C20 /* Build configuration list for PBXProject "ApiTest" */; + compatibilityVersion = "Xcode 13.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6FB0594628C073A5004C8C20; + productRefGroup = 6FB0595028C073A5004C8C20 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6FB0594E28C073A5004C8C20 /* ApiTest */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6FB0594D28C073A5004C8C20 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6FB0595A28C073A6004C8C20 /* Preview Assets.xcassets in Resources */, + 6FB0595728C073A6004C8C20 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6FB0594B28C073A5004C8C20 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6FB0596E28C0C084004C8C20 /* BookCell.swift in Sources */, + 6FB0597128C0D353004C8C20 /* String+Extension.swift in Sources */, + 6FB0596A28C08B98004C8C20 /* APIConstants.swift in Sources */, + 6FB0596528C07622004C8C20 /* Book.swift in Sources */, + 6FB0596128C073F0004C8C20 /* BookList.swift in Sources */, + 6FB0596328C0760C004C8C20 /* BookFinder.swift in Sources */, + 6FB0595328C073A5004C8C20 /* ApiTestApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 6FB0595B28C073A6004C8C20 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 6FB0595C28C073A6004C8C20 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 6FB0595E28C073A6004C8C20 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"ApiTest/Preview Content\""; + DEVELOPMENT_TEAM = 3FAK98S72C; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = jum.ApiTest; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 6FB0595F28C073A6004C8C20 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"ApiTest/Preview Content\""; + DEVELOPMENT_TEAM = 3FAK98S72C; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = jum.ApiTest; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6FB0594A28C073A5004C8C20 /* Build configuration list for PBXProject "ApiTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6FB0595B28C073A6004C8C20 /* Debug */, + 6FB0595C28C073A6004C8C20 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6FB0595D28C073A6004C8C20 /* Build configuration list for PBXNativeTarget "ApiTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6FB0595E28C073A6004C8C20 /* Debug */, + 6FB0595F28C073A6004C8C20 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6FB0594728C073A5004C8C20 /* Project object */; +} diff --git a/week5/ApiTest/ApiTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/week5/ApiTest/ApiTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/week5/ApiTest/ApiTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/week5/ApiTest/ApiTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/week5/ApiTest/ApiTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/week5/ApiTest/ApiTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/week5/ApiTest/ApiTest/APIConstants.swift b/week5/ApiTest/ApiTest/APIConstants.swift new file mode 100644 index 0000000..6ea5a89 --- /dev/null +++ b/week5/ApiTest/ApiTest/APIConstants.swift @@ -0,0 +1,18 @@ +// +// APIConstants.swift +// ApiTest +// +// Created by 임주민 on 2022/09/01. +// + +import Foundation + +enum APIConstants: String { + case getBookInfo = "/v1/search/book.json" + + static let clientID = "_hygi4FZdNOUpQsd7K__" + static let clientSecret = "CFBpqq7uVg" + static let scheme = "https" + static let host = "openapi.naver.com" + static let successRange = 200..<300 +} diff --git a/week5/ApiTest/ApiTest/ApiTestApp.swift b/week5/ApiTest/ApiTest/ApiTestApp.swift new file mode 100644 index 0000000..617b42d --- /dev/null +++ b/week5/ApiTest/ApiTest/ApiTestApp.swift @@ -0,0 +1,18 @@ +// +// ApiTestApp.swift +// ApiTest +// +// Created by 임주민 on 2022/09/01. +// + +import SwiftUI + +@main +struct ApiTestApp: App { + var body: some Scene { + WindowGroup { + let viewModel = BookFinder() + BookList(viewModel: viewModel) + } + } +} diff --git a/week5/ApiTest/ApiTest/Assets.xcassets/AccentColor.colorset/Contents.json b/week5/ApiTest/ApiTest/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/week5/ApiTest/ApiTest/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week5/ApiTest/ApiTest/Assets.xcassets/AppIcon.appiconset/Contents.json b/week5/ApiTest/ApiTest/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..5a3257a --- /dev/null +++ b/week5/ApiTest/ApiTest/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,93 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week5/ApiTest/ApiTest/Assets.xcassets/Contents.json b/week5/ApiTest/ApiTest/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/week5/ApiTest/ApiTest/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/week5/ApiTest/ApiTest/Model/Book.swift b/week5/ApiTest/ApiTest/Model/Book.swift new file mode 100644 index 0000000..6cdfffe --- /dev/null +++ b/week5/ApiTest/ApiTest/Model/Book.swift @@ -0,0 +1,44 @@ +// +// Book.swift +// ApiTest +// +// Created by 임주민 on 2022/09/01. +// + +import Foundation + +struct BookSearch { + + var searchKeyword = "" + var books: [Book] = [] +} + +struct Response: Codable { + + let lastBuildDate: String + let total, start, display: Int + let items: [Book] +} + +struct Book: Codable, Identifiable { + + let title: String + let link: String + let image: String + let author, discount, publisher, pubdate: String + let isbn, description: String + let id: Int? + + init(_ book: Book, id: Int) { + title = book.title + link = book.link + image = book.image + author = book.author + discount = book.discount + publisher = book.publisher + pubdate = book.pubdate + isbn = book.isbn + description = book.description + self.id = id + } +} diff --git a/week5/ApiTest/ApiTest/Preview Content/Preview Assets.xcassets/Contents.json b/week5/ApiTest/ApiTest/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/week5/ApiTest/ApiTest/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} From 152994624a67d4143904a2df09c989bf8336b200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Thu, 1 Sep 2022 21:46:37 +0900 Subject: [PATCH 20/22] Add viewmodel about book search --- .../ApiTest/ViewModel/BookFinder.swift | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 week5/ApiTest/ApiTest/ViewModel/BookFinder.swift diff --git a/week5/ApiTest/ApiTest/ViewModel/BookFinder.swift b/week5/ApiTest/ApiTest/ViewModel/BookFinder.swift new file mode 100644 index 0000000..0f2996c --- /dev/null +++ b/week5/ApiTest/ApiTest/ViewModel/BookFinder.swift @@ -0,0 +1,54 @@ +// +// BookFinder.swift +// ApiTest +// +// Created by 임주민 on 2022/09/01. +// + +import SwiftUI + +class BookFinder: ObservableObject { + + @Published var model = BookSearch() + + private func makeURLComponents(path: String, query: String) -> URLComponents { + var urlComponents = URLComponents() + urlComponents.scheme = APIConstants.scheme + urlComponents.host = APIConstants.host + urlComponents.path = path + urlComponents.queryItems = [URLQueryItem(name: "query", value: query)] + + return urlComponents + } + + private func makeURLGETRequest(url: URL) -> URLRequest { + var urlRequest = URLRequest(url: url) + urlRequest.httpMethod = "GET" + urlRequest.addValue(APIConstants.clientID, forHTTPHeaderField: "X-Naver-Client-Id") + urlRequest.addValue(APIConstants.clientSecret, forHTTPHeaderField: "X-Naver-Client-Secret") + + return urlRequest + } + + func fetchBookList() { + guard let componentsURL = makeURLComponents(path: APIConstants.getBookInfo.rawValue, + query: model.searchKeyword).url + else { return } + let requestURL = makeURLGETRequest(url: componentsURL) + let task = URLSession.shared.dataTask(with: requestURL) { data, response, error in + guard error == nil, + let httpURLResponse = response as? HTTPURLResponse, + APIConstants.successRange.contains(httpURLResponse.statusCode) + else { return } + guard let data = data, + let parsedData = try? JSONDecoder().decode(Response.self, from: data) + else { return } + DispatchQueue.main.async { [weak self] in + self?.model.books = parsedData.items.indices.map { + Book(parsedData.items[$0], id: parsedData.start + $0) + } + } + } + task.resume() + } +} From 516b1656ebe64d7545bcf44866ce1a45f45e591e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Thu, 1 Sep 2022 21:47:27 +0900 Subject: [PATCH 21/22] Add views about book search --- .../ApiTest/Extensions/String+Extension.swift | 20 +++++++ week5/ApiTest/ApiTest/View/BookCell.swift | 52 +++++++++++++++++++ week5/ApiTest/ApiTest/View/BookList.swift | 41 +++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 week5/ApiTest/ApiTest/Extensions/String+Extension.swift create mode 100644 week5/ApiTest/ApiTest/View/BookCell.swift create mode 100644 week5/ApiTest/ApiTest/View/BookList.swift diff --git a/week5/ApiTest/ApiTest/Extensions/String+Extension.swift b/week5/ApiTest/ApiTest/Extensions/String+Extension.swift new file mode 100644 index 0000000..5bde28a --- /dev/null +++ b/week5/ApiTest/ApiTest/Extensions/String+Extension.swift @@ -0,0 +1,20 @@ +// +// String+Extension.swift +// ApiTest +// +// Created by 임주민 on 2022/09/01. +// + +import Foundation + +extension String { + + var insertComma: String { + let numberFormatter = NumberFormatter() + numberFormatter.numberStyle = .decimal + guard let doubleValue = Double(self), + let insertedCommaValue = numberFormatter.string(from: NSNumber(value: doubleValue)) + else { return self } + return insertedCommaValue + } +} diff --git a/week5/ApiTest/ApiTest/View/BookCell.swift b/week5/ApiTest/ApiTest/View/BookCell.swift new file mode 100644 index 0000000..446bc37 --- /dev/null +++ b/week5/ApiTest/ApiTest/View/BookCell.swift @@ -0,0 +1,52 @@ +// +// BookCell.swift +// ApiTest +// +// Created by 임주민 on 2022/09/01. +// + +import SwiftUI + +struct BookCell: View { + + let book: Book + + var body: some View { + HStack(spacing: 20) { + AsyncImage(url: URL(string: book.image)) { phase in + switch phase { + case .success(let image): + image + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 80, height: 120, alignment: .center) + case .failure: + Image(systemName: "questionmark.square") + .resizable() + .aspectRatio(contentMode: .fit) + .padding() + .frame(width: 80, height: 120, alignment: .center) + case .empty: + ProgressView() + .frame(width: 80, height: 120, alignment: .center) + @unknown default: + EmptyView() + .frame(width: 80, height: 120, alignment: .center) + } + } + VStack(alignment: .leading, spacing: 5) { + Text(book.title) + .font(.body) + .fontWeight(.bold) + .foregroundColor(.indigo) + Text(book.author + "(지은이)") + Text(book.publisher + " | " + book.pubdate) + Text(book.discount == "0" + ? "가격정보 없음" + : book.discount.insertComma + "원") + .foregroundColor(.pink) + .fontWeight(.bold) + }.font(.footnote) + } + } +} diff --git a/week5/ApiTest/ApiTest/View/BookList.swift b/week5/ApiTest/ApiTest/View/BookList.swift new file mode 100644 index 0000000..dde86fb --- /dev/null +++ b/week5/ApiTest/ApiTest/View/BookList.swift @@ -0,0 +1,41 @@ +// +// BookList.swift +// ApiTest +// +// Created by 임주민 on 2022/09/01. +// + +import SwiftUI + +struct BookList: View { + + @ObservedObject var viewModel: BookFinder + + var body: some View { + ZStack { + List { + HStack { + TextField(text: $viewModel.model.searchKeyword, label: { Text("ex. 해리포터") }) + Button(action: { viewModel.fetchBookList() }) { + Text("검색") + }.buttonStyle(.bordered) + } + Section { + ForEach(viewModel.model.books) { book in + VStack { + BookCell(book: book) + Spacer() + } + } + } + } + } + } +} + +struct BookList_Previews: PreviewProvider { + static var previews: some View { + let viewModel = BookFinder() + BookList(viewModel: viewModel) + } +} From 23a2cd0e41295d215e8095cdf531d3b881d5cc6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=8C=E1=85=AE=E1=84=86?= =?UTF-8?q?=E1=85=B5=E1=86=AB?= Date: Sat, 3 Sep 2022 00:00:56 +0900 Subject: [PATCH 22/22] Add blog search about book --- .../ApiTest/ApiTest.xcodeproj/project.pbxproj | 24 +++++++-- week5/ApiTest/ApiTest/APIConstants.swift | 2 + week5/ApiTest/ApiTest/ApiTestApp.swift | 3 +- .../ApiTest/Extensions/String+Extension.swift | 13 +++++ week5/ApiTest/ApiTest/Info.plist | 11 ++++ week5/ApiTest/ApiTest/Model/Blog.swift | 41 +++++++++++++++ week5/ApiTest/ApiTest/Model/Book.swift | 2 +- week5/ApiTest/ApiTest/View/BlogCell.swift | 29 +++++++++++ week5/ApiTest/ApiTest/View/BlogWebView.swift | 25 ++++++++++ week5/ApiTest/ApiTest/View/BookCell.swift | 28 ++++++----- week5/ApiTest/ApiTest/View/BookList.swift | 41 --------------- week5/ApiTest/ApiTest/View/SearchList.swift | 50 +++++++++++++++++++ .../ApiTest/ViewModel/BookFinder.swift | 32 ++++++++++-- 13 files changed, 237 insertions(+), 64 deletions(-) create mode 100644 week5/ApiTest/ApiTest/Info.plist create mode 100644 week5/ApiTest/ApiTest/Model/Blog.swift create mode 100644 week5/ApiTest/ApiTest/View/BlogCell.swift create mode 100644 week5/ApiTest/ApiTest/View/BlogWebView.swift delete mode 100644 week5/ApiTest/ApiTest/View/BookList.swift create mode 100644 week5/ApiTest/ApiTest/View/SearchList.swift diff --git a/week5/ApiTest/ApiTest.xcodeproj/project.pbxproj b/week5/ApiTest/ApiTest.xcodeproj/project.pbxproj index eea4ba8..170005a 100644 --- a/week5/ApiTest/ApiTest.xcodeproj/project.pbxproj +++ b/week5/ApiTest/ApiTest.xcodeproj/project.pbxproj @@ -10,12 +10,15 @@ 6FB0595328C073A5004C8C20 /* ApiTestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0595228C073A5004C8C20 /* ApiTestApp.swift */; }; 6FB0595728C073A6004C8C20 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FB0595628C073A6004C8C20 /* Assets.xcassets */; }; 6FB0595A28C073A6004C8C20 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FB0595928C073A6004C8C20 /* Preview Assets.xcassets */; }; - 6FB0596128C073F0004C8C20 /* BookList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0596028C073F0004C8C20 /* BookList.swift */; }; + 6FB0596128C073F0004C8C20 /* SearchList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0596028C073F0004C8C20 /* SearchList.swift */; }; 6FB0596328C0760C004C8C20 /* BookFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0596228C0760C004C8C20 /* BookFinder.swift */; }; 6FB0596528C07622004C8C20 /* Book.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0596428C07622004C8C20 /* Book.swift */; }; 6FB0596A28C08B98004C8C20 /* APIConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0596928C08B98004C8C20 /* APIConstants.swift */; }; 6FB0596E28C0C084004C8C20 /* BookCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0596D28C0C084004C8C20 /* BookCell.swift */; }; 6FB0597128C0D353004C8C20 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0597028C0D353004C8C20 /* String+Extension.swift */; }; + 6FB0597328C1E6CF004C8C20 /* Blog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0597228C1E6CF004C8C20 /* Blog.swift */; }; + 6FB0597928C1FC2F004C8C20 /* BlogCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0597828C1FC2F004C8C20 /* BlogCell.swift */; }; + 6FB0597B28C20940004C8C20 /* BlogWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB0597A28C20940004C8C20 /* BlogWebView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -23,12 +26,16 @@ 6FB0595228C073A5004C8C20 /* ApiTestApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiTestApp.swift; sourceTree = ""; }; 6FB0595628C073A6004C8C20 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 6FB0595928C073A6004C8C20 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - 6FB0596028C073F0004C8C20 /* BookList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookList.swift; sourceTree = ""; }; + 6FB0596028C073F0004C8C20 /* SearchList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchList.swift; sourceTree = ""; }; 6FB0596228C0760C004C8C20 /* BookFinder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookFinder.swift; sourceTree = ""; }; 6FB0596428C07622004C8C20 /* Book.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Book.swift; sourceTree = ""; }; 6FB0596928C08B98004C8C20 /* APIConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIConstants.swift; sourceTree = ""; }; 6FB0596D28C0C084004C8C20 /* BookCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookCell.swift; sourceTree = ""; }; 6FB0597028C0D353004C8C20 /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; + 6FB0597228C1E6CF004C8C20 /* Blog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Blog.swift; sourceTree = ""; }; + 6FB0597828C1FC2F004C8C20 /* BlogCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlogCell.swift; sourceTree = ""; }; + 6FB0597A28C20940004C8C20 /* BlogWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlogWebView.swift; sourceTree = ""; }; + 6FB0597C28C209BF004C8C20 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -61,6 +68,7 @@ 6FB0595128C073A5004C8C20 /* ApiTest */ = { isa = PBXGroup; children = ( + 6FB0597C28C209BF004C8C20 /* Info.plist */, 6FB0595228C073A5004C8C20 /* ApiTestApp.swift */, 6FB0596928C08B98004C8C20 /* APIConstants.swift */, 6FB0596F28C0D325004C8C20 /* Extensions */, @@ -85,6 +93,7 @@ isa = PBXGroup; children = ( 6FB0596428C07622004C8C20 /* Book.swift */, + 6FB0597228C1E6CF004C8C20 /* Blog.swift */, ); path = Model; sourceTree = ""; @@ -100,8 +109,10 @@ 6FB0596828C07638004C8C20 /* View */ = { isa = PBXGroup; children = ( - 6FB0596028C073F0004C8C20 /* BookList.swift */, + 6FB0596028C073F0004C8C20 /* SearchList.swift */, 6FB0596D28C0C084004C8C20 /* BookCell.swift */, + 6FB0597828C1FC2F004C8C20 /* BlogCell.swift */, + 6FB0597A28C20940004C8C20 /* BlogWebView.swift */, ); path = View; sourceTree = ""; @@ -184,11 +195,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 6FB0597928C1FC2F004C8C20 /* BlogCell.swift in Sources */, 6FB0596E28C0C084004C8C20 /* BookCell.swift in Sources */, 6FB0597128C0D353004C8C20 /* String+Extension.swift in Sources */, + 6FB0597B28C20940004C8C20 /* BlogWebView.swift in Sources */, 6FB0596A28C08B98004C8C20 /* APIConstants.swift in Sources */, 6FB0596528C07622004C8C20 /* Book.swift in Sources */, - 6FB0596128C073F0004C8C20 /* BookList.swift in Sources */, + 6FB0596128C073F0004C8C20 /* SearchList.swift in Sources */, + 6FB0597328C1E6CF004C8C20 /* Blog.swift in Sources */, 6FB0596328C0760C004C8C20 /* BookFinder.swift in Sources */, 6FB0595328C073A5004C8C20 /* ApiTestApp.swift in Sources */, ); @@ -322,6 +336,7 @@ DEVELOPMENT_TEAM = 3FAK98S72C; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ApiTest/Info.plist; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -351,6 +366,7 @@ DEVELOPMENT_TEAM = 3FAK98S72C; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = ApiTest/Info.plist; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; diff --git a/week5/ApiTest/ApiTest/APIConstants.swift b/week5/ApiTest/ApiTest/APIConstants.swift index 6ea5a89..bb60ad3 100644 --- a/week5/ApiTest/ApiTest/APIConstants.swift +++ b/week5/ApiTest/ApiTest/APIConstants.swift @@ -8,7 +8,9 @@ import Foundation enum APIConstants: String { + case getBookInfo = "/v1/search/book.json" + case getBookBlog = "/v1/search/blog.json" static let clientID = "_hygi4FZdNOUpQsd7K__" static let clientSecret = "CFBpqq7uVg" diff --git a/week5/ApiTest/ApiTest/ApiTestApp.swift b/week5/ApiTest/ApiTest/ApiTestApp.swift index 617b42d..4be1d76 100644 --- a/week5/ApiTest/ApiTest/ApiTestApp.swift +++ b/week5/ApiTest/ApiTest/ApiTestApp.swift @@ -9,10 +9,11 @@ import SwiftUI @main struct ApiTestApp: App { + var body: some Scene { WindowGroup { let viewModel = BookFinder() - BookList(viewModel: viewModel) + SearchList(bookFinder: viewModel) } } } diff --git a/week5/ApiTest/ApiTest/Extensions/String+Extension.swift b/week5/ApiTest/ApiTest/Extensions/String+Extension.swift index 5bde28a..9e5f04d 100644 --- a/week5/ApiTest/ApiTest/Extensions/String+Extension.swift +++ b/week5/ApiTest/ApiTest/Extensions/String+Extension.swift @@ -18,3 +18,16 @@ extension String { return insertedCommaValue } } + +extension AttributedString { + + /// HTML형식의 문자열을 기본 문자열로 변환합니다. + /// - Parameter htmlString: HTML 형식의 문자열. + init?(htmlString: String) { + let option: [NSAttributedString.DocumentReadingOptionKey: NSAttributedString.DocumentType] = [.documentType: .html] + guard let htmlData = htmlString.data(using: .utf16), + let nsStr = try? NSAttributedString(data: htmlData, options: option, documentAttributes: nil) + else { return nil } + self.init(nsStr.string) + } +} diff --git a/week5/ApiTest/ApiTest/Info.plist b/week5/ApiTest/ApiTest/Info.plist new file mode 100644 index 0000000..6a6654d --- /dev/null +++ b/week5/ApiTest/ApiTest/Info.plist @@ -0,0 +1,11 @@ + + + + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + + diff --git a/week5/ApiTest/ApiTest/Model/Blog.swift b/week5/ApiTest/ApiTest/Model/Blog.swift new file mode 100644 index 0000000..e4716dc --- /dev/null +++ b/week5/ApiTest/ApiTest/Model/Blog.swift @@ -0,0 +1,41 @@ +// +// BlogSearch.swift +// ApiTest +// +// Created by 임주민 on 2022/09/02. +// + +import Foundation + +struct BookBlogSearch { + + var searchKeyword = "" + var blogs: [Blog] = [] +} + +struct ResponseBlog: Codable { + + let lastBuildDate: String + let total, start, display: Int + let items: [Blog] +} + +struct Blog: Codable, Identifiable { + + let title: String + let link: String + let description, bloggername: String + let bloggerlink: String + let postdate: String + let id: Int? + + init(_ blog: Blog, id: Int) { + title = blog.title + link = blog.link + description = blog.description + bloggername = blog.bloggername + bloggerlink = blog.bloggerlink + postdate = blog.postdate + self.id = id + } +} diff --git a/week5/ApiTest/ApiTest/Model/Book.swift b/week5/ApiTest/ApiTest/Model/Book.swift index 6cdfffe..1120579 100644 --- a/week5/ApiTest/ApiTest/Model/Book.swift +++ b/week5/ApiTest/ApiTest/Model/Book.swift @@ -13,7 +13,7 @@ struct BookSearch { var books: [Book] = [] } -struct Response: Codable { +struct ResponseBook: Codable { let lastBuildDate: String let total, start, display: Int diff --git a/week5/ApiTest/ApiTest/View/BlogCell.swift b/week5/ApiTest/ApiTest/View/BlogCell.swift new file mode 100644 index 0000000..3978418 --- /dev/null +++ b/week5/ApiTest/ApiTest/View/BlogCell.swift @@ -0,0 +1,29 @@ +// +// BlogCell.swift +// ApiTest +// +// Created by 임주민 on 2022/09/02. +// + +import SwiftUI + +struct BlogCell: View { + + let blog: Blog + + var body: some View { + VStack(alignment: .leading) { + Text(AttributedString(htmlString: blog.title) ?? "") + .font(.body) + .fontWeight(.bold) + .lineLimit(2) + .fixedSize(horizontal: false, vertical: true) + Text(blog.bloggername) + Text(blog.postdate).foregroundColor(.indigo) + Text(AttributedString(htmlString: blog.description) ?? "") + .foregroundColor(.gray) + .lineLimit(3) + .fixedSize(horizontal: false, vertical: true) + }.font(.footnote) + } +} diff --git a/week5/ApiTest/ApiTest/View/BlogWebView.swift b/week5/ApiTest/ApiTest/View/BlogWebView.swift new file mode 100644 index 0000000..47637f1 --- /dev/null +++ b/week5/ApiTest/ApiTest/View/BlogWebView.swift @@ -0,0 +1,25 @@ +// +// BlogWebView.swift +// ApiTest +// +// Created by 임주민 on 2022/09/02. +// + +import SwiftUI +import WebKit + +struct BlogWebView: UIViewRepresentable { + + var urlToLoad: String + + func makeUIView(context: Context) -> WKWebView { + guard let url = URL(string: self.urlToLoad) + else { return WKWebView() } + let webView = WKWebView() + webView.load(URLRequest(url: url)) + return webView + } + + func updateUIView(_ uiView: WKWebView, context: UIViewRepresentableContext) { + } +} diff --git a/week5/ApiTest/ApiTest/View/BookCell.swift b/week5/ApiTest/ApiTest/View/BookCell.swift index 446bc37..8f0b05a 100644 --- a/week5/ApiTest/ApiTest/View/BookCell.swift +++ b/week5/ApiTest/ApiTest/View/BookCell.swift @@ -12,7 +12,7 @@ struct BookCell: View { let book: Book var body: some View { - HStack(spacing: 20) { + VStack(alignment: .leading, spacing: 5) { AsyncImage(url: URL(string: book.image)) { phase in switch phase { case .success(let image): @@ -34,19 +34,21 @@ struct BookCell: View { .frame(width: 80, height: 120, alignment: .center) } } - VStack(alignment: .leading, spacing: 5) { - Text(book.title) - .font(.body) - .fontWeight(.bold) - .foregroundColor(.indigo) - Text(book.author + "(지은이)") - Text(book.publisher + " | " + book.pubdate) - Text(book.discount == "0" - ? "가격정보 없음" - : book.discount.insertComma + "원") - .foregroundColor(.pink) + Text(book.title) .fontWeight(.bold) - }.font(.footnote) + .foregroundColor(.indigo) + .lineLimit(2) + .fixedSize(horizontal: false, vertical: true) + Text(book.author + "(지은이)") + Text(book.publisher + " | " + book.pubdate) + Text(book.discount == "0" + ? "가격정보 없음" + : book.discount.insertComma + "원") + .foregroundColor(.pink) + .fontWeight(.bold) + Spacer() } + .font(.footnote) + .frame(width: 100, height: 230) } } diff --git a/week5/ApiTest/ApiTest/View/BookList.swift b/week5/ApiTest/ApiTest/View/BookList.swift deleted file mode 100644 index dde86fb..0000000 --- a/week5/ApiTest/ApiTest/View/BookList.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// BookList.swift -// ApiTest -// -// Created by 임주민 on 2022/09/01. -// - -import SwiftUI - -struct BookList: View { - - @ObservedObject var viewModel: BookFinder - - var body: some View { - ZStack { - List { - HStack { - TextField(text: $viewModel.model.searchKeyword, label: { Text("ex. 해리포터") }) - Button(action: { viewModel.fetchBookList() }) { - Text("검색") - }.buttonStyle(.bordered) - } - Section { - ForEach(viewModel.model.books) { book in - VStack { - BookCell(book: book) - Spacer() - } - } - } - } - } - } -} - -struct BookList_Previews: PreviewProvider { - static var previews: some View { - let viewModel = BookFinder() - BookList(viewModel: viewModel) - } -} diff --git a/week5/ApiTest/ApiTest/View/SearchList.swift b/week5/ApiTest/ApiTest/View/SearchList.swift new file mode 100644 index 0000000..6c2b6c2 --- /dev/null +++ b/week5/ApiTest/ApiTest/View/SearchList.swift @@ -0,0 +1,50 @@ +// +// BookList.swift +// ApiTest +// +// Created by 임주민 on 2022/09/01. +// + +import SwiftUI + +struct SearchList: View { + + @ObservedObject var bookFinder: BookFinder + + var body: some View { + NavigationView { + List { + HStack { + TextField(text: $bookFinder.bookModel.searchKeyword, label: { Text("ex. 해리포터") }) + Button { + bookFinder.fetchBookList() + bookFinder.fetchBookBlog() + } label: { + Text("검색") + }.buttonStyle(.bordered) + } + Section { + ScrollView(.horizontal) { + LazyHStack { + ForEach(bookFinder.bookModel.books) { book in + BookCell(book: book) + Spacer() + } + } + } + } + Section { + VStack(spacing: 8) { + ForEach(bookFinder.blogModel.blogs) { blog in + BlogCell(blog: blog) + NavigationLink(destination: BlogWebView(urlToLoad: blog.link)) { + Text("글 보러가기").font(.footnote) + } + Spacer() + } + } + } + } + } + } +} diff --git a/week5/ApiTest/ApiTest/ViewModel/BookFinder.swift b/week5/ApiTest/ApiTest/ViewModel/BookFinder.swift index 0f2996c..ebbcdff 100644 --- a/week5/ApiTest/ApiTest/ViewModel/BookFinder.swift +++ b/week5/ApiTest/ApiTest/ViewModel/BookFinder.swift @@ -9,7 +9,8 @@ import SwiftUI class BookFinder: ObservableObject { - @Published var model = BookSearch() + @Published var bookModel = BookSearch() + @Published var blogModel = BookBlogSearch() private func makeURLComponents(path: String, query: String) -> URLComponents { var urlComponents = URLComponents() @@ -32,7 +33,7 @@ class BookFinder: ObservableObject { func fetchBookList() { guard let componentsURL = makeURLComponents(path: APIConstants.getBookInfo.rawValue, - query: model.searchKeyword).url + query: bookModel.searchKeyword).url else { return } let requestURL = makeURLGETRequest(url: componentsURL) let task = URLSession.shared.dataTask(with: requestURL) { data, response, error in @@ -41,14 +42,37 @@ class BookFinder: ObservableObject { APIConstants.successRange.contains(httpURLResponse.statusCode) else { return } guard let data = data, - let parsedData = try? JSONDecoder().decode(Response.self, from: data) + let parsedData = try? JSONDecoder().decode(ResponseBook.self, from: data) else { return } DispatchQueue.main.async { [weak self] in - self?.model.books = parsedData.items.indices.map { + self?.bookModel.books = parsedData.items.indices.map { Book(parsedData.items[$0], id: parsedData.start + $0) } } } task.resume() } + + func fetchBookBlog() { + guard let componentsURL = makeURLComponents(path: APIConstants.getBookBlog.rawValue, + query: bookModel.searchKeyword + "책").url + else { return } + let requestURL = makeURLGETRequest(url: componentsURL) + let task = URLSession.shared.dataTask(with: requestURL) { data, response, error in + guard error == nil, + let httpURLResponse = response as? HTTPURLResponse, + APIConstants.successRange.contains(httpURLResponse.statusCode) + else { return } + guard let data = data, + let parsedData = try? JSONDecoder().decode(ResponseBlog.self, from: data) + else { return } + DispatchQueue.main.async { [weak self] in + self?.blogModel.blogs = parsedData.items + self?.blogModel.blogs = parsedData.items.indices.map { + Blog(parsedData.items[$0], id: parsedData.start + $0) + } + } + } + task.resume() + } }