HP Trivia (1)

 

๐Ÿ“ Folder vs Group (Xcode)

ํ•ญ๋ชฉ Folder (ํŒŒ๋ž€์ƒ‰) Image Group (ํšŒ์ƒ‰) Image
๊ธฐ๋ณธ ์ ์šฉ ๋ฒ„์ „ Xcode 16๋ถ€ํ„ฐ ๊ธฐ๋ณธ Xcode 15 ์ดํ•˜์—์„œ ๊ธฐ๋ณธ
ํŒŒ์ผ ์‹œ์Šคํ…œ ๋ฐ˜์˜ ์‹ค์ œ macOS ํŒŒ์ผ ์‹œ์Šคํ…œ์—๋„ ๋™์ผํ•œ ํด๋” ๊ตฌ์กฐ๋กœ ์ƒ์„ฑ๋จ Xcode ํ”„๋กœ์ ํŠธ ๋‚ด์—์„œ๋งŒ ์กด์žฌํ•˜๋ฉฐ, ์‹ค์ œ ํŒŒ์ผ ์‹œ์Šคํ…œ๊ณผ ์ผ์น˜ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Œ
Git ๋ณ‘ํ•ฉ ์ถฉ๋Œ ์ƒ๋Œ€์ ์œผ๋กœ ์ ์Œ ํ”„๋กœ์ ํŠธ ํŒŒ์ผ (.xcodeproj) ์ž์ฒด ๋ณ€๊ฒฝ์ด ๋งŽ์•„์ ธ ์ถฉ๋Œ ์œ„ํ—˜ ๋†’์Œ
ํ”„๋กœ์ ํŠธ ๋‚ด ์ •๋ ฌ ์ด๋ฆ„ ๊ธฐ์ค€ ์ž๋™ ์ •๋ ฌ (์•ŒํŒŒ๋ฒณ ์ˆœ) ์ˆ˜๋™ ์ •๋ ฌ ๊ฐ€๋Šฅ (๊ฐœ๋ฐœ์ž ์ž์œ  ๋ฐฐ์น˜)
ํ˜‘์—… ์‹œ ์žฅ์  Git ๋ณ‘ํ•ฉ ์ถฉ๋Œ ์ตœ์†Œํ™”๋กœ ํ˜‘์—…์— ์œ ๋ฆฌ ํ˜‘์—… ์‹œ ํ”„๋กœ์ ํŠธ ํŒŒ์ผ ์ถฉ๋Œ ๊ฐ€๋Šฅ์„ฑ ํผ
๋‹จ์  ํŒŒ์ผ ์œ„์น˜๊ฐ€ ๊ณ ์ •์ ์ด๋ฉฐ, ์ปค์Šคํ…€ ์ •๋ ฌ ์–ด๋ ค์›€ Git ์ถฉ๋Œ ์‹œ ํ•ด๊ฒฐ์ด ๊นŒ๋‹ค๋กญ๊ณ , ์‹ค์ˆ˜ ๊ฐ€๋Šฅ์„ฑ ์žˆ์Œ

๊ฐ•์˜์—์„  Git ์ถฉ๋Œ์„ ์ค„์ด๊ธฐ ์œ„ํ•ด Folder ๋ฐฉ์‹์„ ์„ ํ˜ธ
ํ•˜์ง€๋งŒ ๊ฐœ์ธ์ ์œผ๋กœ ํŒŒ์ผ ์ •๋ ฌ์„ ๋” ์œ ์—ฐํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด Group ๋ฐฉ์‹๋„ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ์Œ


Single Responsibility Principle (๋‹จ์ผ ์ฑ…์ž„ ์›์น™)

์ •์˜
ํ•˜๋‚˜์˜ ํด๋ž˜์Šค, ๊ตฌ์กฐ์ฒด, ๋ทฐ, ํŒŒ์ผ ๋“ฑ์€ ์˜ค์ง ํ•˜๋‚˜์˜ ์ฑ…์ž„๋งŒ์„ ๊ฐ€์ ธ์•ผ ํ•˜๋ฉฐ, ํ•˜๋‚˜์˜ ์ด์œ ๋กœ๋งŒ ๋ณ€๊ฒฝ๋˜์–ด์•ผ ํ•œ๋‹ค๋Š” ์†Œํ”„ํŠธ์›จ์–ด ์„ค๊ณ„ ์›์น™.


์˜ˆ์‹œ

๋‚˜์œ ์˜ˆ

struct CurrencyView: View {  
    var body: some View {  
        // ํ†ตํ™” ์•„์ด์ฝ˜ ๋””์ž์ธ  
        // ๊ทธ๋ฆฌ๋“œ ๋ ˆ์ด์•„์›ƒ ์ฒ˜๋ฆฌ  
        // ํ†ตํ™” ์„ ํƒ ํ™”๋ฉด ์ „์ฒด ๊ตฌ์„ฑ  
    }  
}

๋ชจ๋“  ์—ญํ• ์ด ํ•˜๋‚˜์˜ ๋ทฐ์— ๋ชฐ๋ ค ์žˆ์–ด ์œ ์ง€๋ณด์ˆ˜ ๋ฐ ์žฌ์‚ฌ์šฉ ์–ด๋ ค์›€

์ข‹์€ ์˜ˆ

struct CurrencyIconView: View {  
    // ํ†ตํ™” ์•„์ด์ฝ˜ ๋””์ž์ธ๋งŒ ๋‹ด๋‹น  
}

struct IconGridView: View {  
    // ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์•„์ด์ฝ˜์„ ๊ทธ๋ฆฌ๋“œ๋กœ ๋ฐฐ์น˜  
}

struct SelectCurrencyView: View {  
    // ์ „์ฒด ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•˜๋ฉฐ ์œ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์กฐํ•ฉ  
}

๊ฐ ๋ทฐ๋Š” ํ•œ ๊ฐ€์ง€ ์—ญํ• ๋งŒ ํ•˜๋ฉฐ, ๋ช…ํ™•ํ•œ ์ฑ…์ž„ ๋ถ„๋ฆฌ


์žฅ์ 

  • ์ฝ”๋“œ ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ
  • ์œ ์ง€๋ณด์ˆ˜ ์šฉ์ด
  • ํ…Œ์ŠคํŠธ ๋‹จ์œ„ ์ž‘์•„์ง โ†’ ์œ ๋‹› ํ…Œ์ŠคํŠธ ์šฉ์ด
  • ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ํ™•์žฅ์„ฑ ํ–ฅ์ƒ

์œ ์—ฐํ•˜๊ฒŒ ์ ์šฉํ•˜์ž

๋ฐ˜๋“œ์‹œ ์—„๊ฒฉํžˆ ์ ์šฉํ•  ํ•„์š”๋Š” ์—†์Œ.
์ƒํ™ฉ๊ณผ ํ”„๋กœ์ ํŠธ์˜ ์„ฑ๊ฒฉ์— ๋งž๊ฒŒ ์ ์šฉํ•˜๋ฉด ๋จ.
ํ•ต์‹ฌ์€ โ€œํŒŒ์ผ/๊ตฌ์กฐ์ฒด/๋ทฐ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์€ ์—ญํ• ์„ ํ•˜์ง€ ์•Š๋„๋ก ํ•˜๋Š” ๊ฒƒโ€

์—ฌ๊ธฐ๊นŒ์ง€๊ฐ€ ๊ฐ•์˜์˜ ์ดˆ๋ฐ˜์„ ์ •๋ฆฌํ•œ ๋‚ด์šฉ.

ํ”„๋กœ์ ํŠธ์—์„œ๋Š”

Image Image


ํด๋” ๊ตฌ์„ฑํ•˜๊ธฐ

์ด๋ฒˆ์—๋Š” ๊ธฐ์กด ํ”„๋กœ์ ํŠธ์™€ ๋‹ฌ๋ฆฌ ํด๋”๋ฅผ ๋งŒ๋“ค๊ณ  swiftํŒŒ์ผ๋“ค์„ ๊ด€๋ฆฌํ•˜๋ ค๊ณ  ํ•œ๋‹ค.

Image

์ดํ›„, ํ”„๋กœ์ ํŠธ์— ํ•„์š”ํ•œ ํŒŒ์ผ์„ ๋„ฃ์–ด์ฃผ์ž.

์ด๋ฒˆ์—๋„ ์—ญ์‹œ Swift ๋ฒ„์ „์€ 6์œผ๋กœ.

๋ชจ๋ธ๋ง

์ด๋ฒˆ์—๋„ ์—ญ์‹œ json์„ ์‚ฌ์šฉํ•˜๊ธฐ์— ๋ชจ๋ธ๋ง์„ ํ•ด์ฃผ๋„๋กํ•œ๋‹ค.

struct Question: Decodable {
    let id: Int
    let question: String
    let answer: String
    let wrong: [String]
    let book: Int
    let hint: String
}

๋ชจ๋ธ๋ง์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.


์•ฑ ๊ตฌํ˜„ ํ”Œ๋žœ ์„ธ์šฐ๊ธฐ

์ด๋ฒˆ์—๋Š” ์ฒ˜์Œ๋ถ€ํ„ฐ ํ”Œ๋žœ์„ ์„ธ์šฐ๊ณ  ๋“ค์–ด๊ฐ„๋‹ค.

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

๊ธฐ๋Šฅ์„ ๋‚˜์—ด์„ ํ•ด๋ณด์•˜๋‹ค (์ˆœ์„œ๋Š” ๋ฌด๊ด€)

์•„๋งˆ๋„ ๊ฒฐ๊ณผ๋Š” ์ด๋ ‡๊ฒŒ ๋‚˜์˜ฌ๋“ฏ

Image


Instruction View ๋””์ž์ธ

Image

๋”ฑํžˆ ์–ธ๊ธ‰ํ•  ๋ถ€๋ถ„์ด ์—†์–ด์„œ ์ด๊ฑด ์ „์ฒด ์ฝ”๋“œ๋กœ ๋Œ€์ฒดํ•œ๋‹ค.

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

Image

์šฐ์„  ์ด๋ ‡๊ฒŒ ๋””์ž์ธ์„ ํ•ด์ค€๋‹ค.

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 ๋ฐ‘์— ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ๋‹ค.

.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์ดˆ๋กœ ์„ค์ •)

Image

์ด๋ ‡๊ณผ ์ขŒ์šฐ๋กœ ์›€์ง์ธ๋‹ค.

  • phaseAnimator ?
    • phaseAnimator๋Š” SwiftUI์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ทฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋„๊ตฌ๋กœ, ์—ฌ๋Ÿฌ ์ƒํƒœ(phase) ๊ฐ„ ์ „ํ™˜์„ ์ž๋™์œผ๋กœ ๋ฐ˜๋ณตํ•˜๋ฉด์„œ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. Docs}๋Š” ์—ฌ๊ธฐ
  • ์‚ฌ์šฉ ๋ชฉ์ 
    • ํŠน์ • ๋ทฐ ์†์„ฑ์„ ์ƒํƒœ๋ณ„๋กœ ๋ณ€ํ™”์‹œ์ผœ ๋ฐ˜๋ณต ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ƒ์„ฑ
    • ์˜ˆ: ์ด๋ฏธ์ง€์˜ ์ขŒ์šฐ ์ด๋™, ํˆฌ๋ช…๋„ ๋ณ€ํ™”, ํฌ๊ธฐ ๋ณ€ํ™” ๋“ฑ
Parameters ์„ค๋ช…
phases ์ˆœํ™˜ํ•  ์ƒํƒœ(phase)๋“ค์˜ ์‹œํ€€์Šค. ๋น„์–ด ์žˆ์œผ๋ฉด ๋Ÿฐํƒ€์ž„ ๊ฒฝ๊ณ ์™€ ์‹œ๊ฐ์  ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•จ
content ๋‘ ๊ฐœ์˜ ์ธ์ž๋ฅผ ๋ฐ›๋Š” ๋ทฐ ๋นŒ๋” ํด๋กœ์ €: ์ˆ˜์ •๋œ ๋ทฐ ํ”„๋ก์‹œ์™€ ํ˜„์žฌ phase
animation phase ๊ฐ„ ์ „ํ™˜ ์‹œ ์‚ฌ์šฉํ•  ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํด๋กœ์ €. nil์ด๋ฉด ์• ๋‹ˆ๋ฉ”์ด์…˜ ์—†์Œ

๊ฐ•์˜์—์„œ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ 2~3๊ฐœ์˜ ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ด๋ผ๊ณ  ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  [false, true]๋ฅผ ์‚ฌ์šฉํ•œ ์ด์œ ๋Š” Apple์˜ ๊ณต์‹ ์˜ˆ์ œ ๋Œ€๋ถ€๋ถ„์ด false โ†’ true์˜ ํ๋ฆ„์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค๋ผ๊ณ  ํ•œ๋‹ค.

Transition Animation

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:)์ด ์žˆ์–ด์•ผ ํ•จ
  • Transition ์ž‘๋™์„ ์œ„ํ•œ ํ•„์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ
    1. @State๋กœ Boolean ๋ณ€์ˆ˜ ์„ ์–ธ
    2. .onAppear์—์„œ ์ƒํƒœ๊ฐ’ ๋ณ€๊ฒฝ (withAnimation ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ์™ธ๋ถ€์—์„œ .animation() ์ ์šฉ)
    3. if ๋ฌธ์œผ๋กœ ๋ทฐ ์กฐ๊ฑด ๋ถ„๊ธฐ
    4. .transition(.move(edge: .top)) ์ ์šฉ
    5. .animation(_:value:)์„ if๋ฌธ์„ ๊ฐ์‹ผ ์™ธ๋ถ€ VStack์— ์ ์šฉ

Group ๋Œ€์‹  VStack์„ ์“ด ์ด์œ 
Group์€ ์—ฌ๋Ÿฌ ๋ทฐ๋ฅผ ๋ฌถ๋Š” ๋ฐ ์“ฐ์ด๋Š” ๊ฒฝ๋Ÿ‰ ์ปจํ…Œ์ด๋„ˆ์ด์ง€๋งŒ, ์• ๋‹ˆ๋ฉ”์ด์…˜ ํŠธ๋ฆฌ๊ฑฐ์™€ ๊ด€๋ จํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค:

  1. ๋‚ด๋ถ€ ์ƒํƒœ ๋ณ€ํ™” ๊ฐ์ง€๊ฐ€ ๋ถˆ์•ˆ์ •ํ•  ์ˆ˜ ์žˆ์Œ
  2. .animation(_:value:)์„ ๋ถ™์—ฌ๋„ ๊ธฐ๋Œ€ํ•œ ๋Œ€๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Œ
    ๊ทธ๋ž˜์„œ ๊ฐ•์˜์—์„œ๋Š” Group ๋Œ€์‹  VStack์„ ์‚ฌ์šฉํ•˜์—ฌ ๋” ์•ˆ์ •์ ์œผ๋กœ ์ž‘๋™ํ•˜๊ฒŒ ํ–ˆ๋‹ค.

์ฝ”๋“œ๋ฅผ ๋ณด๋ฉฐ ๊ฐ„๋‹จํžˆ ์ž‘๋™์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ธฐ

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์ฒ˜๋Ÿผ ๋ถ€์ • ์กฐ๊ฑด์„ ์จ์•ผ ํ•ด์„œ ์ฝ”๋“œ ํ๋ฆ„์ด ์ง๊ด€์ ์œผ๋กœ ์ฝํžˆ์ง€ ์•Š๊ณ , ์ „ํ™˜ ์‹œ์ ๋„ ๋ช…ํ™•ํ•˜์ง€ ์•Š๋‹ค.

์‹คํ–‰ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋œ๋‹ค.

Image


.animation vs withAnimation

๊ฐ‘์ž๊ธฐ ๋ฌธ๋“ ๊ฐ„๋‹จํžˆ ์ •๋ฆฌํ•˜๋ฉด ์ข‹์„๊ฒƒ ๊ฐ™์•„ ์—ฌ๊ธฐ์— ์ ์–ด๋ณธ๋‹ค.


  • withAnimation
    • SwiftUI์˜ ํ•จ์ˆ˜ ๊ธฐ๋ฐ˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํŠธ๋ฆฌ๊ฑฐ
    • ๋‚ด๋ถ€ ํด๋กœ์ €์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ƒํƒœ ๋ณ€ํ™”๊ฐ€ ๋ทฐ๋ฅผ ๋ฐ”๊พธ๋ฉด, ํ•ด๋‹น ๋ณ€ํ™”์— ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ
    • ์ฃผ๋กœ onAppear, onTapGesture, Button ์•ก์…˜ ๋“ฑ ์ด๋ฒคํŠธ์„ฑ ๋™์ž‘๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉ๋จ

์˜ˆ์‹œ:

withAnimation(.easeInOut) {
    isVisible.toggle()
}
  • isVisible์˜ ๋ณ€๊ฒฝ์œผ๋กœ ์ธํ•ด if isVisible ์กฐ๊ฑด ํ•˜์˜ ๋ทฐ๊ฐ€ ์‚ฝ์ž… ๋˜๋Š” ์ œ๊ฑฐ๋˜๋ฉด, ํ•ด๋‹น ๋ทฐ์— transition์ด ์ ์šฉ๋˜์–ด ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ฐœ์ƒ

  • .animation(_:value:)
    • SwiftUI์˜ modifier ๊ธฐ๋ฐ˜ ์„ ์–ธ์  ์• ๋‹ˆ๋ฉ”์ด์…˜
    • ํŠน์ • ์ƒํƒœ๊ฐ’(value)์˜ ๋ณ€ํ™”๊ฐ€ ํ•ด๋‹น ๋ทฐ ํŠธ๋ฆฌ์˜ ์™ธํ˜• ๋ณ€ํ™”๋กœ ์ด์–ด์งˆ ๊ฒฝ์šฐ, ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ
    • if ์กฐ๊ฑด์ด ๋ฐ”๋€Œ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ๋ทฐ์˜ ๋“ฑ์žฅ/ํ‡ด์žฅ์ด ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋ฐ”๋€Œ๋Š” ์ƒํ™ฉ์— ์ ํ•ฉ

์˜ˆ์‹œ:

.animation(.easeInOut, value: isVisible)
  • isVisible์ด ๋ฐ”๋€” ๋•Œ, ์ด ๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ ๋ทฐ ํŠธ๋ฆฌ ๋ณ€ํ™”๊ฐ€ ์ƒ๊ธฐ๋ฉด transition๊ณผ ํ•จ๊ป˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ฒ˜๋ฆฌ

์š”์•ฝ

ํ•ญ๋ชฉ withAnimation .animation(_:value:)
ํƒ€์ž… ํ•จ์ˆ˜ modifier
์œ„์น˜ ์ƒํƒœ ๋ณ€๊ฒฝ์„ ๊ฐ์‹ธ๋Š” ํด๋กœ์ € View ํŠธ๋ฆฌ์— ์ง์ ‘ ์ ์šฉ
์ฃผ ์šฉ๋„ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํŠธ๋ฆฌ๊ฑฐ ์ƒํƒœ ๋ณ€ํ™” ๊ธฐ๋ฐ˜ ๋ทฐ ์—…๋ฐ์ดํŠธ ๊ฐ์ง€