HP Trivia (1)
π Folder vs Group (Xcode)
κ°μμμ Git μΆ©λμ μ€μ΄κΈ° μν΄ Folder λ°©μμ μ νΈ
νμ§λ§ κ°μΈμ μΌλ‘ νμΌ μ λ ¬μ λ μ μ°νκ² κ΄λ¦¬νκ³ μΆλ€λ©΄ Group λ°©μλ κ³ λ €ν μ μμ
Single Responsibility Principle (λ¨μΌ μ± μ μμΉ)
μ μ
νλμ ν΄λμ€, ꡬ쑰체, λ·°, νμΌ λ±μ μ€μ§ νλμ μ±
μλ§μ κ°μ ΈμΌ νλ©°, νλμ μ΄μ λ‘λ§ λ³κ²½λμ΄μΌ νλ€λ μννΈμ¨μ΄ μ€κ³ μμΉ.
μμ
λμ μ
1
2
3
4
5
6
7
struct CurrencyView: View {
var body: some View {
// ν΅ν μμ΄μ½ λμμΈ
// 그리λ λ μ΄μμ μ²λ¦¬
// ν΅ν μ ν νλ©΄ μ 체 ꡬμ±
}
}
λͺ¨λ μν μ΄ νλμ λ·°μ λͺ°λ € μμ΄ μ μ§λ³΄μ λ° μ¬μ¬μ© μ΄λ €μ
μ’μ μ
1
2
3
4
5
6
7
8
9
10
11
struct CurrencyIconView: View {
// ν΅ν μμ΄μ½ λμμΈλ§ λ΄λΉ
}
struct IconGridView: View {
// μ¬λ¬ κ°μ μμ΄μ½μ 그리λλ‘ λ°°μΉ
}
struct SelectCurrencyView: View {
// μ 체 νλ©΄μ ꡬμ±νλ©° μ μ»΄ν¬λνΈλ₯Ό μ‘°ν©
}
κ° λ·°λ ν κ°μ§ μν λ§ νλ©°, λͺ νν μ± μ λΆλ¦¬
μ₯μ
- μ½λ κ°λ μ± ν₯μ
- μ μ§λ³΄μ μ©μ΄
- ν μ€νΈ λ¨μ μμμ§ β μ λ ν μ€νΈ μ©μ΄
- μ¬μ¬μ©μ±κ³Ό νμ₯μ± ν₯μ
μ μ°νκ² μ μ©νμ
λ°λμ μ격ν μ μ©ν νμλ μμ.
μν©κ³Ό νλ‘μ νΈμ μ±κ²©μ λ§κ² μ μ©νλ©΄ λ¨.
ν΅μ¬μ βνμΌ/ꡬ쑰체/λ·°κ° λ무 λ§μ μν μ νμ§ μλλ‘ νλ κ²β
μ¬κΈ°κΉμ§κ° κ°μμ μ΄λ°μ μ 리ν λ΄μ©.
νλ‘μ νΈμμλ
ν΄λ ꡬμ±νκΈ°
μ΄λ²μλ κΈ°μ‘΄ νλ‘μ νΈμ λ¬λ¦¬ ν΄λλ₯Ό λ§λ€κ³ swiftνμΌλ€μ κ΄λ¦¬νλ €κ³ νλ€.
μ΄ν, νλ‘μ νΈμ νμν νμΌμ λ£μ΄μ£Όμ.
μ΄λ²μλ μμ Swift λ²μ μ 6μΌλ‘.
λͺ¨λΈλ§
μ΄λ²μλ μμ jsonμ μ¬μ©νκΈ°μ λͺ¨λΈλ§μ ν΄μ£Όλλ‘νλ€.
1
2
3
4
5
6
7
8
struct Question: Decodable {
let id: Int
let question: String
let answer: String
let wrong: [String]
let book: Int
let hint: String
}
λͺ¨λΈλ§μ λ€μκ³Ό κ°λ€.
μ± κ΅¬ν νλ μΈμ°κΈ°
μ΄λ²μλ μ²μλΆν° νλμ μΈμ°κ³ λ€μ΄κ°λ€.
1
2
3
4
5
6
7
8
9
10
11
12
App Development Plan:
- Game Intro Screen
- Gameplay Screen
- Game logic (questions, scores, etc.)
- Celebration
- Audio
- Animations
- In-app purchases
- Store
- Instructions screen
- Books
- Persist scores
κΈ°λ₯μ λμ΄μ ν΄λ³΄μλ€ (μμλ 무κ΄)
μλ§λ κ²°κ³Όλ μ΄λ κ² λμ¬λ―
Instruction View λμμΈ
λ±ν μΈκΈν λΆλΆμ΄ μμ΄μ μ΄κ±΄ μ 체 μ½λλ‘ λ체νλ€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
struct InstructionsView: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
ZStack {
Image(.parchment)
.resizable()
.ignoresSafeArea()
.background(.brown)
VStack {
Image(.appiconwithradius)
.resizable()
.scaledToFit()
.frame(width: 100)
.padding(.top)
ScrollView {
Text("How To Play")
.font(.largeTitle)
.padding()
VStack(alignment: .leading, spacing: 20) {
Text("Welcome to HP Trivia! In this game you will be asked random questions from the HP books and you must guess the right answer or you will lose points!π±")
Text("Each question is worth 5 points, but if you guess a wrong answer, you lose 1 point.")
Text("If you are struggling with a question, there is an option to reveal a hint or reveal the book that answers the question. But beware! Using these also removes 1 point each.")
Text("When you select the correct answer, you will be awarded all the points left for that question and they will be added to your total score.")
}
.font(.title3)
.padding(.horizontal)
Text("Good Luck!")
.font(.title)
}
Button("Done") {
dismiss()
}
.font(.largeTitle)
.padding()
.buttonStyle(.borderedProminent)
.tint(.brown.mix(with: .black, by: 0.2))
.foregroundStyle(.white)
}
μ΄λ κ² κΈ°μ΄μμ μ€ νλκ° λλ¬λ€.
PhaseAnimator
μ°μ μ΄λ κ² λμμΈμ ν΄μ€λ€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct ContentView: View {
var body: some View {
GeometryReader { geo in
ZStack {
Image(.hogwarts)
.resizable()
.frame(width: geo.size.width * 3, height: geo.size.height)
.padding(.top, 3)
}
.frame(width: geo.size.width, height: geo.size.height)
}
.ignoresSafeArea()
}
}
μ½λλ μ΄λ κ² μμ±.
μ΄μ μ¬κΈ°μ μ‘°κΈ λ UIλ₯Ό λ°μ μν€κΈ°μν΄ phaseAnimator
Modifierλ₯Ό μ¬μ©νλ€.
padding λ°μ μΆκ°ν΄μ£Όμλ€.
1
2
3
4
5
6
7
.padding(.top, 3)
.phaseAnimator([false, true]) { content, phase in
content
.offset(x: phase ? geo.size.width/1.1 : -geo.size.width/1.1)
} animation: { _ in
.linear(duration: 60)
}
μ€ννλ©΄ μλμκ°λ€. (β» μλ μμλ durationμ ν μ€νΈμ©μΌλ‘ 2μ΄λ‘ μ€μ )
μ΄λ κ³Ό μ’μ°λ‘ μμ§μΈλ€.
- phaseAnimator ?
phaseAnimator
λ SwiftUIμμ μ¬μ©νλ λ·° μ λλ©μ΄μ λꡬλ‘, μ¬λ¬ μν(phase) κ° μ νμ μλμΌλ‘ λ°λ³΅νλ©΄μ μ λλ©μ΄μ μ μ μ©ν μ μλ€. Docs}λ μ¬κΈ°
- μ¬μ© λͺ©μ
- νΉμ λ·° μμ±μ μνλ³λ‘ λ³νμμΌ λ°λ³΅ μ λλ©μ΄μ μμ±
- μ: μ΄λ―Έμ§μ μ’μ° μ΄λ, ν¬λͺ λ λ³ν, ν¬κΈ° λ³ν λ±
Parameters | μ€λͺ |
---|---|
phases | μνν μν(phase)λ€μ μνμ€. λΉμ΄ μμΌλ©΄ λ°νμ κ²½κ³ μ μκ°μ κ²½κ³ κ° λ°μν¨ |
content | λ κ°μ μΈμλ₯Ό λ°λ λ·° λΉλ ν΄λ‘μ : μμ λ λ·° νλ‘μμ νμ¬ phase |
animation | phase κ° μ ν μ μ¬μ©ν μ λλ©μ΄μ
μ λ°ννλ ν΄λ‘μ . nil μ΄λ©΄ μ λλ©μ΄μ
μμ |
κ°μμμλ μΌλ°μ μΌλ‘ 2~3κ°μ μνλ₯Ό μ¬μ©νλ κ²μ΄ μΌλ°μ μ΄λΌκ³ νλ€. κ·Έλ¦¬κ³ [false, true]
λ₯Ό μ¬μ©ν μ΄μ λ Appleμ 곡μ μμ λλΆλΆμ΄ false β trueμ νλ¦μΌλ‘ ꡬμ±λμ΄ μκΈ° λλ¬Έμ΄λ€λΌκ³ νλ€.
Transition Animation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
struct ContentView: View {
@State private var animateViewsIn = false // new
var body: some View {
GeometryReader { geo in
ZStack {
// μλ΅
VStack {
VStack {
if animateViewsIn {
VStack {
Image(systemName: "bolt.fill")
.imageScale(.large)
.font(.largeTitle)
Text("HP")
.font(.custom("PartyLetPlain", size: 70))
.padding(.bottom, -50)
Text("Trivia")
.font(.custom("PartyLetPlain", size: 60))
}
.padding(.top, 70)
.transition(.move(edge: .top))
}
}
.animation(.easeInOut(duration: 0.7).delay(2), value: animateViewsIn)
Spacer()
}
}
.frame(width: geo.size.width, height: geo.size.height)
}
.ignoresSafeArea()
.onAppear {
animateViewsIn = true
}
}
}
μ΄λ κ² μ½λλ₯Ό κ°μ±νλ€.
- Transition
- SwiftUIμμ
.transition(.move(edge: .top))
μ λ·°κ° νλ©΄μ λ±μ₯νκ±°λ μ¬λΌμ§ λ μ λλ©μ΄μ ν¨κ³Όλ₯Ό μ μ©νλ λ° μ¬μ©λλ€. λ¨, λ€μ μ‘°κ±΄μ΄ μΆ©μ‘±λμ΄μΌ μ λλ‘ μλνλ€:- λ·°λ
if
쑰건μ ν΅ν΄ μ½μ /μμ λμ΄μΌ ν¨ - ν΄λΉ
if
λ₯Ό κ°μΈλ μΈλΆ λ·°μ.animation(_:value:)
μ΄ μμ΄μΌ ν¨
- λ·°λ
- SwiftUIμμ
- Transition μλμ μν νμ κ΅¬μ± μμ
@State
λ‘ Boolean λ³μ μ μΈ.onAppear
μμ μνκ° λ³κ²½ (withAnimation
μ¬μ©νκ±°λ μΈλΆμμ.animation()
μ μ©)if
λ¬ΈμΌλ‘ λ·° 쑰건 λΆκΈ°.transition(.move(edge: .top))
μ μ©.animation(_:value:)
μ ifλ¬Έμ κ°μΌ μΈλΆ VStackμ μ μ©
Group λμ VStackμ μ΄ μ΄μ
Group
μ μ¬λ¬ λ·°λ₯Ό λ¬Άλ λ° μ°μ΄λ κ²½λ 컨ν μ΄λμ΄μ§λ§, μ λλ©μ΄μ νΈλ¦¬κ±°μ κ΄λ ¨ν΄ λ€μκ³Ό κ°μ λ¬Έμ κ° λ°μν μ μλ€:
- λ΄λΆ μν λ³ν κ°μ§κ° λΆμμ ν μ μμ
.animation(_:value:)
μ λΆμ¬λ κΈ°λν λλ‘ λμνμ§ μμ μ μμ
κ·Έλμ κ°μμμλ Group λμ VStackμ μ¬μ©νμ¬ λ μμ μ μΌλ‘ μλνκ² νλ€.
μ½λλ₯Ό 보며 κ°λ¨ν μλμ λν΄ μμ보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
GeometryReader { geo in
ZStack {
// μλ΅
VStack {
VStack {
if animateViewsIn {
VStack {
// μλ΅
}
.padding(.top, 70)
.transition(.move(edge: .top))
}
}
.animation(.easeInOut(duration: 0.7).delay(2), value: animateViewsIn)
Spacer()
}
}
.frame(width: geo.size.width, height: geo.size.height)
}
.ignoresSafeArea()
.onAppear {
animateViewsIn = true
}
.transition()
μif
λ΄λΆμ μλ λ·°(VStack)μ μ μ©λ¨- μ¬κΈ°μ μλ¨μμ μμ§μ΄κ² λ€λ κ².
- .animation(_:value:)μ ifλ₯Ό κ°μΈλ μΈλΆ VStackμ μ μ©λμ΄μΌ νλ©°, ν΄λΉ VStackμ΄ λνλ λ easeInOut μ λλ©μ΄μ μ΄ 2μ΄ λ€μ μ€νλ¨
if
λ Viewκ° μλλ―λ‘ modifierλ₯Ό λΆμΌ μ μκ³ , wrappingμ΄ νμν¨- κ°μμμλ Group λμ VStackμ μ¬μ©νμ¬ μμ μ μΈ μ λλ©μ΄μ μ²λ¦¬λ₯Ό μ λν¨
.onAppear
μμ μνκ°μ λ³κ²½νμ¬ μ λλ©μ΄μ μ΄ νΈλ¦¬κ±°λ¨
μ¦, value
μ μ λ¬λ μν(animateViewsIn
)κ° false
μμ onAppear
λ true
λ‘ λ³κ²½λλ©΄μ animationμ΄ νΈλ¦¬κ±°λκ³ , ν΄λΉ μμ μ if λ΄λΆμ VStackμ΄ μμ±λλ©΄μ μμμ λ΄λ €μ€λ μ λλ©μ΄μ
κ³Ό ν¨κ» νλ©΄μ λνλλ€.
κ·Έλ¦¬κ³ νΉμλ μ΄κΈ°κ°μ true
λ‘ μ€μ νλ©΄ !animateViewsIn
μ²λΌ λΆμ 쑰건μ μ¨μΌ ν΄μ μ½λ νλ¦μ΄ μ§κ΄μ μΌλ‘ μ½νμ§ μκ³ , μ ν μμ λ λͺ
ννμ§ μλ€.
μ€ννλ©΄ μλμ κ°μ΄ λλ€.
- μ°Έκ³
- Animations Docs
- Transition Docs
- animation(Method) Docs μμ Docsμλ λ€λ¦
- withAnimation Docs
.animation vs withAnimation
κ°μκΈ° λ¬Έλ κ°λ¨ν μ 리νλ©΄ μ’μκ² κ°μ μ¬κΈ°μ μ μ΄λ³Έλ€.
- withAnimation
- SwiftUIμ ν¨μ κΈ°λ° μ λλ©μ΄μ νΈλ¦¬κ±°
- λ΄λΆ ν΄λ‘μ μμ λ°μνλ μν λ³νκ° λ·°λ₯Ό λ°κΎΈλ©΄, ν΄λΉ λ³νμ μ λλ©μ΄μ μ μ©
- μ£Όλ‘ onAppear, onTapGesture, Button μ‘μ λ± μ΄λ²€νΈμ± λμκ³Ό ν¨κ» μ¬μ©λ¨
μμ:
1
2
3
withAnimation(.easeInOut) {
isVisible.toggle()
}
- isVisibleμ λ³κ²½μΌλ‘ μΈν΄ if isVisible 쑰건 νμ λ·°κ° μ½μ λλ μ κ±°λλ©΄, ν΄λΉ λ·°μ transitionμ΄ μ μ©λμ΄ μ λλ©μ΄μ λ°μ
.animation(_:value:)
- SwiftUIμ modifier κΈ°λ° μ μΈμ μ λλ©μ΄μ
- νΉμ μνκ°(value)μ λ³νκ° ν΄λΉ λ·° νΈλ¦¬μ μΈν λ³νλ‘ μ΄μ΄μ§ κ²½μ°, μ λλ©μ΄μ μ μ©
- if μ‘°κ±΄μ΄ λ°λλ κ²μ²λΌ, λ·°μ λ±μ₯/ν΄μ₯μ΄ μ‘°κ±΄μ λ°λΌ λ°λλ μν©μ μ ν©
μμ:
1
.animation(.easeInOut, value: isVisible)
- isVisibleμ΄ λ°λ λ, μ΄ κ°μ κΈ°μ€μΌλ‘ λ·° νΈλ¦¬ λ³νκ° μκΈ°λ©΄ transitionκ³Ό ν¨κ» μ λλ©μ΄μ μ²λ¦¬
μμ½
νλͺ© | withAnimation | .animation(_:value:) |
---|---|---|
νμ | ν¨μ | modifier |
μμΉ | μν λ³κ²½μ κ°μΈλ ν΄λ‘μ | View νΈλ¦¬μ μ§μ μ μ© |
μ£Ό μ©λ | μ΄λ²€νΈ κΈ°λ° μ λλ©μ΄μ νΈλ¦¬κ±° | μν λ³ν κΈ°λ° λ·° μ λ°μ΄νΈ κ°μ§ |