iOS ๐ŸŽ/SwiftUI

SwiftUI LifeCycle ํŒŒํ—ค์น˜๊ธฐ ๐Ÿฎ

fram 2023. 10. 31. 00:17

2023.10 ๊ธฐ์ค€ SwiftUI์—์„œ life cycle updates modifier๋กœ ์ง€์ •๋œ ๊ฒƒ์€ 4๊ฐœ์ด๋‹ค. ์—ฌ๊ธฐ์„œ ๋งˆ์ง€๋ง‰ task๋Š” task๋งŒ ๋”ฐ๋กœ ํ•œ๋ฒˆ ํŒŒ๋ณด๊ธฐ๋กœ ํ•˜๊ณ  onAppear, onDisappear, task ์•Œ์•„๋ด…์‹œ๋‹ค ๋‹ค ๋“œ๋ฃจ์™€~

 

SwiftUI์—์„œ ํ™”๋ฉด์„ ์ „ํ™˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค. UIKit์—์„œ ViewController๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ๋Š” ํ™”๋ฉด ์ „ํ™˜๋˜๋Š” View๋ฅผ ViewController๋กœ ๋ช…ํ™•ํžˆ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹จ์œ„๊ฐ€ ์žˆ์—ˆ์ง€๋งŒ SwiftUI ์—์„œ๋Š” view๋ผ๋Š” ๋‹จ์œ„๊ฐ€ ๋ช…ํ™•ํ•˜๊ฒŒ ๊ตฌ๋ถ„๋˜์–ด ์žˆ๊ธฐ ๋ณด๋‹ค๋Š” View๊ฐ€ View๋ฅผ ๊ฐ์‹ธ๊ณ  ์žˆ๊ฑฐ๋‚˜ ๋‹ค๋ฅธ View์˜ ์ผ๋ถ€๋ถ„์ด ๋  ์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ๊ณ ๋ คํ•ด์„œ ์•Œ์•„๋ด์•ผ ํ•œ๋‹ค.

 

์šฐ์„  ๊ฐ modifire์„ ๊ฐ„๋‹จํžˆ ์‚ดํŽด๋ณด๋ฉด

onAppear

func onAppear(perform action: (() -> Void)? = nil) -> some View

action ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ํด๋กœ์ €๋ฅผ ์ „๋‹ฌํ•œ๋‹ค. default ๊ฐ’์ด nil์ด๊ธฐ ๋•Œ๋ฌธ์— ๋”ฐ๋กœ action์„ ์ „๋‹ฌํ•ด ์ฃผ์ง€ ์•Š์•„๋„ ๋œ๋‹ค.(action์„  nil๋กœ ์ „๋‹ฌํ•˜๋Š” onAppear()์˜ ์“ฐ์ž„์ƒˆ๊ฐ€ ๋”ฐ๋กœ ์žˆ๋Š”๊ฑธ๊นŒ?ใ…Žใ…Žใ…Ž)

ํŠน์ • View์˜ ์œ ํ˜•์— ๋”ฐ๋ผ ํ˜ธ์ถœ๋˜๋Š” ์ •ํ™•ํ•œ ์‹œ์ ์ด ๋‹ค๋ฅด์ง€๋งŒ ์ฒ˜์Œ์œผ๋กœ ๋ Œ๋”๋ง ๋œ ํ”„๋ ˆ์ž„์ด ๋‚˜ํƒ€๋‚˜๊ธฐ ์ „์— ์•ก์…˜ ํด๋กœ์ €๊ฐ€ ์™„๋ฃŒ๋œ๋‹ค๊ณ  ํ•œ๋‹ค. 

ParentView์—์„œ ChildView๋กœ ์ด๋™ํ–ˆ์„ ๋•Œ ChildView๊ฐ€ ๋‚˜ํƒ€๋‚˜๊ธฐ ์ „์— onAppear์— action ์ด ํ˜ธ์ถœ๋˜๋Š”๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. 

onAppear๋Š” ๋ทฐ๊ฐ€ ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚˜๊ธฐ ์ด์ „์— action ํด๋กœ์ €๋ฅผ ์‹คํ–‰ํ•œ๋‹ค ๋ผ๊ณ  ์ •๋ฆฌํ•˜๋ฉด ๋ ๊ฒƒ ๊ฐ™๋‹ค.

 

onDisappear

func onDisappear(perform action: (() -> Void)? = nil) -> some View

onDisappear ๋˜ํ•œ onAppear์™€ ๊ฐ™์ด action ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ํด๋กœ์ €๋ฅผ ์ „๋‹ฌํ•˜๊ณ  default๊ฐ’์€ nil์ด๋‹ค. onAppear์™€ ๋‹ค๋ฅด๊ฒŒ ํ™”๋ฉด์—์„œ View๊ฐ€ ์‚ฌ๋ผ์ง€๊ธฐ ์ „๊นŒ์ง€ action ํด๋กœ์ €๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. 

ParentView์—์„œ ChildView๋กœ ์ด๋™ํ•œ ๋’ค dismiss ์‹œํ‚ค๋ฉด ํ™”๋ฉด์—์„œ ChildView๊ฐ€ ์‚ฌ๋ผ์ง€๊ณ  ๋‚œ ๋’ค onDiasappear์˜ action ํด๋กœ์ ธ๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.

 

task

func task(
    priority: TaskPriority = .userInitiated,
    _ action: @escaping () async -> Void
) -> some View

task๋Š” onAppear, onDisappear ์™€๋Š” ๋‹ฌ๋ฆฌ priority๋ผ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ , action ์€ async๋กœ ๋˜์–ด ์žˆ๋‹ค. task ๊ด€๋ จํ•ด์„œ ๋”ฐ๋กœ ํ•œ๋ฒˆ ๋‹ค๋ฃจ๊ธฐ๋กœ ํ•˜๊ณ  life cycle ๋ถ€๋ถ„๋งŒ ์‚ดํŽด ๋ณด๋ฉด

A closure that SwiftUI calls as an asynchronous task before the view appears. SwiftUI will automatically cancel the task at some point after the view disappears before the action completes.

view appears ์ด์ „์— async task๊ฐ€ ์‹คํ–‰๋œ๋‹ค๊ณ  ์ ํ˜€ ์žˆ์–ด์„œ onAppear ์ด์ „์— ํ˜ธ์ถœ๋˜๋Š” ์ค„ ์•Œ์•—๋Š”๋ฐ ํ•œ๋ฒˆ๋„ onAppear ์ด์ „์— ํ˜ธ์ถœ๋œ๊ฑฐ ๋ชป๋ดค์›€ (ํ˜น์‹œ appear์˜ ๊ฐœ๋…์ด onAppear์™€๋Š” ๋‹ค๋ฅธ๊ฑธ๊นŒ ๋˜๋ฅต... ใ… )

๋ทฐ๊ฐ€ ํ™”๋ฉด์— ๋‹ค ๋‚˜ํƒ€๋‚˜๊ณ  ๋‚œ ๋’ค์— task์˜ action์ด ์‹คํ–‰๋œ๋‹ค. ๋„๋Œ€์ฒด before the view appears ์˜ ์˜๋ฏธ๊ฐ€ ๋ญ˜๊นŒ ๐Ÿค”

 

ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ์ž‘์„ฑํ•œ ์ฝ”๋“œ์˜ ๊ตฌ์กฐ๋ฅผ ์‚ดํŽด๋ณด๋ฉด,

๊ฐ€์žฅ ์ƒ์œ„๋ทฐ๋ฅผ ContentView๋ผ ํ•  ๋•Œ, ContentView๋Š” NavigationStack์œผ๋กœ ChildView ์ด๋™์ด ๊ฐ€๋Šฅํ•˜๊ณ , ZStack์œผ๋กœ ContentView ์ „์ฒด๋ฅผ ๋ฎ์„ ์ˆ˜ ์žˆ๋Š” ChildView๋„ ์กด์žฌํ•œ๋‹ค. 

์ฆ‰ NavigationStack์œผ๋กœ ๋ฐ€์–ด ๋„ฃ๋Š” ๋ทฐ๋ž‘ ํ™”๋ฉด์— ๊ฐ€๋ ค์กŒ๋‹ค ๋‚˜ํƒ€๋‚˜๋Š” ๋ทฐ๋ž‘ ์ฐจ์ด๊ฐ€ ์žˆ์„๊นŒ ์•Œ์•„๋ณด๋Š” ๊ฒƒ. ์ƒ๊ฐํ•ด๋ณด๋‹ˆ ํƒญ๋ฐ”๋„ ํ•ด์•ผ ํ•œ๋‹ค.

 

body property & init

View ๊ตฌ์กฐ์ฒด๋„ View ํ”„๋กœํ† ์ฝœ์„ ๋”ฐ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์ด๋‹› ์‹œ์ ์—์„œ appear์„ ๋ถ™์—ฌ์ค„ ์ˆ˜ ์žˆ์Œ

@main
struct LifeCycleTestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .task {
                    print("๐Ÿ’ฉ ContentView ์ด๋‹›: task Modifier before onAppear ๐Ÿ—‚")
                }
                .onAppear {
                    print("๐Ÿ’ฉ ContentView ์ด๋‹›: ContentView onAppear ๐Ÿ‘ป")
                }
                .task {
                    print("๐Ÿ’ฉ ContentView ์ด๋‹›: task Modifier after onAppear ๐Ÿ—‚")
                }
        }
    }
}

์ตœ์ƒ์œ„ ๋ทฐ๋Š” ContentView์ด๊ณ  ์ด๋‹› ์‹œ์ ์—์„œ onAppear์™€ task modifier์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

struct ContentView: View {
    @State private var isChildViewShow: Bool = false
    
    var body: some View {
        let _ = print("ContentView ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ ")
        NavigationStack {
            let _ = print("NavigationStack ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ")
            ZStack {
                ParentView(isChildViewShow: $isChildViewShow)
                
                if isChildViewShow {
                    ChildView(isSelfShow: $isChildViewShow)
                }
            } //: ZStack
        }
        .task {
            print("๐Ÿงก ContentView task modifier before onAppear ๐Ÿ—‚")
        }
        .onAppear {
            print("๐Ÿงก ContentView NavigationStack onAppear ๐Ÿ‘ป")
        }
        .task {
            print("๐Ÿงก ContentView task modifier after  onAppear ๐Ÿ—‚")
        }
        .onDisappear {
            print("๐Ÿงก ContentView disappear ๐ŸŒช")
        }
    }
}

ContentView์˜ body property ๋‚ด๋ถ€์˜ ๊ฐ€์žฅ ์ตœ์ƒ์œ„ ๋ทฐ์ธ NavigationonStack์˜ Appear์™€

ContentView์˜ ์ด๋‹› ์‹œ์ ์˜ onAppear ์ค‘์— ์–ด๋–ค๊ฒŒ ๋จผ์ € ํ˜ธ์ถœ ๋ ๊นŒ?

    var body: some View {
        let _ = print("ContentView ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ ")
    }

์šฐ์„  body ํ”„๋กœํผํ‹ฐ ๋‚ด๋ถ€์—์„œ print๋ฌธ์„ ์‚ฌ์šฉํ•ด์„œ body๊ฐ€ ๊ทธ๋ ค์งˆ ๋•Œ ์ฝ˜์†”์— ์ฐ์–ด๋ณผ ์ˆ˜ ์žˆ๋‹ค. 

ContentView ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ 
NavigationStack ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ
VStack ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ
๐Ÿงก ContentView NavigationStack onAppear ๐Ÿ‘ป
๐Ÿ’ฉ ContentView ์ด๋‹›: ContentView onAppear ๐Ÿ‘ป

์ฝ˜์†”์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€

body ํ”„๋กœํผํ‹ฐ๊ฐ€ ๋จผ์ € ๊ทธ๋ ค์ง€๊ณ  ๊ทธ ํ›„์— onAppear๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค

 

๐Ÿงก ContentView NavigationStack onAppear ๐Ÿ‘ป
๐Ÿ’ฉ ContentView ์ด๋‹›: ContentView onAppear ๐Ÿ‘ป

๊ทธ๋ฆฌ๊ณ  ๋ณผ ๊ฒƒ์ด ๋ฐ”๋กœ ๊ฐ€์žฅ ์•„๋ž˜ ๋‘ ์ค„! ๋ณด๋ฉด ContentView์˜ ์ตœ์ƒ์œ„ ๋ทฐ์˜ onAppear modifier๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  ๊ทธ ํ›„์— ์ด๋‹› ์‹œ์ ์— ๊ฑธ์–ด๋‘” onAppear๊ฐ€ ํ˜ธ์ถœ ๋œ๋‹ค. body ํ”„๋กœํผํ‹ฐ ๋‚ด๋ถ€์˜ ๊ฐ€์žฅ ์ตœ์ƒ์œ„ ๋ทฐ์˜ onAppear๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  ๊ทธ ํ›„์— ํ•ด๋‹น body ํ”„๋กœํผํ‹ฐ๊ฐ€ ํฌํ•จ๋œ View struct์˜ ์ด๋‹› ์‹œ์ ์— ๊ฑธ์–ด๋‘” onAppear๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค. (๋ฌผ๋ก  ์ด ๋ชจ๋“ ๊ฑด ๊ณต์‹์ ์ธ ๋‚ด์šฉ์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์–ธ์ œ๋“ ์ง€ ๋ฐ”๋€” ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•จ)

            NavigationLink {
                ChildView(isSelfShow: $isChildViewShow)
                    .onAppear {
                        print("ChildView ์ด๋‹›: ChildView onAppear")
                    }
            } label: {
                Text("๐Ÿ›ผ go to ChildView with NavigationLink")
            }

ContentView๊ฐ€ ์•ฑ์˜ ์ตœ์ƒ์œ„ ๋ทฐ๋ผ ํŠน์ˆ˜ํ•œ ๊ฒฝ์šฐ๋กœ ํ˜ธ์ถœ๋œ ๊ฑธ ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ ํ•œ ๊ฐœ๋” ํ™•์ธํ•ด ๋ณด๋ฉด, 

ParentView ๋‚ด๋ถ€์—๋Š” NavigationLink๋ฅผ ์ด์šฉํ•ด์„œ ChildView๋กœ ์ด๋™ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค. 

struct ChildView: View {
    @Binding var isSelfShow: Bool
    
    var body: some View {
        let _ = print("์น ๋“œ ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ")
        VStack {
            // ์ƒ๋žต
        }
        .background(.yellow)
        .onAppear {
            print("๐Ÿค ChildView onAppear ๐Ÿ‘ป")
        }
    }
}

ChildView ๋‚ด๋ถ€์—๋„ body ํ”„๋กœํผํ‹ฐ์˜ ์ตœ์ƒ์œ„ ๋ทฐ์— onAppear๋ฅผ ๊ฑธ์–ด ๋‘์—ˆ๋‹ค. 

์น ๋“œ ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ
๐Ÿค ChildView onAppear ๐Ÿ‘ป
ChildView ์ด๋‹›: ChildView onAppear

ChildView์˜ body property๊ฐ€ ๋จผ์ € ๊ทธ๋ ค์ง€๊ณ  ๊ทธ ํ›„ ChildView์˜ ์ตœ์ƒ์œ„ ๋ทฐ์˜ onAppear๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค. ๊ทธ ํ›„์— ChildView์˜ ์ด๋‹› ์‹œ์ ์— ๊ฑธ์–ด๋‘” onAppear๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค. 

 

init Vs. onAppear

struct ParentView: View {
    @Binding var isChildViewShow: Bool
    
    init(isChildViewShow: Binding<Bool>) {
        self._isChildViewShow = isChildViewShow
        print("ParentView init!!!")
    }
    
    var body: some View {
        VStack {
            let _ = print("ParentView ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ ๐Ÿ’ช")
            NavigationLink {
                ChildView(isSelfShow: $isChildViewShow)
            } label: {
                Text("๐Ÿ›ผ go to ChildView with NavigationLink")
            }
            
            Button {
                print("๐Ÿ›ผ go to ChildView with ZStack")
                isChildViewShow = true
            } label: {
                Text("๐Ÿ›ผ go to ChildView with ZStack")
            }
            .buttonStyle(.borderedProminent)
        }
        .onAppear {
            print("๐Ÿ” ParentView onAppear ๐Ÿ‘ป")
        }
    }
}

๋ชจ๋“  View์—๋‹ค๊ฐ€ init์„ ๋ถ™์—ฌ๋‘์—ˆ๋Š”๋ฐ ParentView๋งŒ ํ™•์ธํ•ด๋ด…์‹œ๋‹ค. 

ParentView init!!!
ParentView ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ ๐Ÿ’ช
๐Ÿ” ParentView onAppear ๐Ÿ‘ป

์šฐ์„  init์ด ๊ฐ€์žฅ ๋จผ์ € ์‹คํ–‰๋œ๋‹ค. ๊ทธ ํ›„ body property๊ฐ€ ๊ทธ๋ ค์ง€๊ณ , body์˜ ์ตœ์ƒ์œ„ ๋ทฐ์˜ onAppear๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.

๊ทธ ํ›„ NavigationLink๋ฅผ ํ†ตํ•ด์„œ ChildView๋กœ ์ด๋™ํ•œ ๋’ค ๋‹ค์‹œ Parent๋กœ ๋Œ์•„์˜ค๋ฉด init๊ณผ onAppear์˜ ์ฐจ์ด๋ฅผ ์•Œ ์ˆ˜ ์žˆ๋‹ค. 

๐Ÿ” ParentView onAppear ๐Ÿ‘ป

onAppear๋Š” ํ˜ธ์ถœ๋˜์ง€๋งŒ init์€ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค. init์€ ๋ทฐ์˜ ์ดˆ๊ธฐํ™” ์‹œ์ ์— ํ•œ ๋ฒˆ ํ˜ธ์ถœ๋˜๋ฉฐ onAppear๋Š” View๊ฐ€ ํ™”๋ฉด์— ๋ณด์—ฌ์งˆ ๋•Œ ํ˜ธ์ถœ๋œ๋‹ค. NavigationStack์— ์˜ํ•ด ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚˜๋ฉด onAppear๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.

์—ฌ๊ธฐ์„œ SwiftUI ๋ฐฉ์‹ฌํ•˜๋ฉด ์•ˆ๋œ๋‹ค. View๊ฐ€ ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚  ๋•Œ ๊ผญ ํ˜ธ์ถœ๋˜๊ฒ ์ง€ ํ•˜๊ณ  onAppear์— ๋งก๊ฒจ ๋ฒ„๋ฆฌ๋Š” ์ˆœ๊ฐ„ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋™์ž‘์— ๋‹นํ™ฉํ•  ์ˆ˜ ์žˆ๋‹ค. NavigationStack์ด ์•„๋‹Œ ZStack์„ ์‚ฌ์šฉํ•ด์„œ ChildView๋กœ ParentView๋ฅผ ์™„์ „ํžˆ ๊ฐ€๋ฆฐ ๋’ค ChildView๋ฅผ ํ™”๋ฉด์—์„œ ์ œ๊ฑฐํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

            Button {
                print("๐Ÿ›ผ go to ChildView with ZStack")
                isChildViewShow = true
            } label: {
                Text("๐Ÿ›ผ go to ChildView with ZStack")
            }
            .buttonStyle(.borderedProminent)

ParentView์—๋Š” ๋ฒ„ํŠผ์ด ์žˆ๋Š”๋ฐ ์ด ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ContentView์™€ Binding ๋œ ๊ฐ’์ธ isChildViewShow๊ฐ€ true๊ฐ€ ๋œ๋‹ค. 

        NavigationStack {
            ZStack {
                ParentView(isChildViewShow: $isChildViewShow)
                
                if isChildViewShow {
                    ChildView(isSelfShow: $isChildViewShow)
                }
            } //: ZStack
        }

ContentView์—๋Š” isChildViewShow๊ฐ€ true๊ฐ€ ๋˜๋ฉด ZStack ์ „์ฒด๋ฅผ ๋ฎ๋Š” ChildView๊ฐ€ ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚˜๊ฒŒ ๋œ๋‹ค. 

NavigationStack์œผ๋กœ ์ด๋™ํ•˜์ง€ ์•Š์•„์„œ ์ƒ๋‹จ์— NavigationBar๊ฐ€ ์—†๋‹ค. 

ParentView init!!!
ParentView ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ ๐Ÿ’ช
๐Ÿ” ParentView onAppear ๐Ÿ‘ป

-------- ChildView๋กœ ์ด๋™ --------

๐Ÿ›ผ go to ChildView with ZStack
ParentView init!!!
ChildView init!!!
ParentView ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ ๐Ÿ’ช
ChildView init!!!
์น ๋“œ ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ ๐Ÿ’ช
๐Ÿค ChildView onAppear ๐Ÿ‘ป

-------- ParentView ์ด๋™ --------

ParentView init!!!
ParentView ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ ๐Ÿ’ช
ChildView init!!!
์น ๋“œ ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ ๐Ÿ’ช

NavigationStack์ด ์•„๋‹ˆ๋ผ ์ด๋ฏธ ๋‚˜ํƒ€๋‚œ ํ™”๋ฉด์— ์ƒˆ๋กœ์šด ํ™”๋ฉด์„ ๊ทธ๋ ค์ฃผ๊ธฐ ๋•Œ๋ฌธ์— onAppear๋Š” ํ•œ ๋ฒˆ ํ˜ธ์ถœ๋˜๊ณ , initializer๋Š” ํ™”๋ฉด์—์„œ ๊ฐ€๋ ค์งˆ ๋•Œ, ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚  ๋•Œ ํ˜ธ์ถœ๋œ๋‹ค. ์ •ํ™•ํžˆ ๋งํ•˜๋ฉด dependency์— ์˜ํ•ด body property๊ฐ€ ์ƒˆ๋กœ ๊ทธ๋ ค์ง€๊ธฐ ๋•Œ๋ฌธ์ธ๋ฐ

@State private var isChildViewShow: Bool = false

ContentView์— ์ •์˜๋œ isChildViewShow์˜ ๊ฐ’์ด ๋ฐ”๋€Œ๋ฉด์„œ ContentView์˜ body ํ”„๋กœํผํ‹ฐ๋ฅผ ์ƒˆ๋กœ ๊ทธ๋ฆฌ๊ณ , ParentView, ChildView ๋˜ํ•œ isChildViewShow State์™€ Bind ๋˜์–ด ์žˆ์–ด body ํ”„๋กœํผํ‹ฐ๊ฐ€ ์—…๋ฐ์ดํŠธ ๋œ๋‹ค. 

์ดํ•ด๊ฐ€ ๋˜์ง€ ์•Š๋Š”๊ฑด, ParentView์˜ id๊ฐ€ ๊ฐ™๊ธฐ ๋•Œ๋ฌธ์— init ๋˜ํ•œ ํ•œ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ–ˆ๋Š”๋ฐ body property๊ฐ€ ์—…๋ฐ์ดํŠธ ๋˜๋ฉด์„œ ์ด๋‹ˆ์…œ๋ผ์ด์ €๋„ ํ•จ๊ป˜ ํ˜ธ์ถœ๋˜์—ˆ๋‹ค. (์ด์œ  ์•„๋Š” ์‚ฌ๋žŒ ์žˆ๋‚˜์š”... ์ œ๋ฐœ)

 

task and onAppear

struct ContentView: View {
    @State private var isChildViewShow: Bool = false
    
    init() {
        print("ContentView init!!!")
    }
    
    var body: some View {
        let _ = print("ContentView ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ ๐Ÿ’ช")
        NavigationStack {
            ZStack {
                ParentView(isChildViewShow: $isChildViewShow)
                   
                if isChildViewShow {
                    ChildView(isSelfShow: $isChildViewShow)
                }
            } //: ZStack
        }
        .task {
            print("๐Ÿงก ContentView task modifier before onAppear ๐Ÿ—‚")
        }
        .onAppear {
            print("๐Ÿงก ContentView NavigationStack onAppear ๐Ÿ‘ป")
        }
        .task {
            print("๐Ÿงก ContentView task modifier after  onAppear ๐Ÿ—‚")
        }
        .onDisappear {
            print("๐Ÿงก ContentView disappear ๐ŸŒช")
        }
    }
}

ContentView๋งŒ ๋†“๊ณ  ๋น„๊ตํ•ด ๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ์ˆœ์„œ๋กœ ํ˜ธ์ถœ๋œ๋‹ค. 

ContentView init!!!
ContentView ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ ๐Ÿ’ช
๐Ÿงก ContentView NavigationStack onAppear ๐Ÿ‘ป
๐Ÿงก ContentView task modifier before onAppear ๐Ÿ—‚
๐Ÿงก ContentView task modifier after  onAppear ๐Ÿ—‚

ContentView์˜ ์ด๋‹ˆ์…œ๋ผ์ด์ €๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ๊ฐ€ ๊ทธ๋ ค์ง„๋‹ค. body ํ”„๋กœํผํ‹ฐ์˜ ์ตœ์ƒ์œ„ ๋ทฐ์—๋Š” task1, onAppear, task2, onDisappear ์ˆœ์œผ๋กœ modifier๊ฐ€ ๋ถ™์–ด ์žˆ๋‹ค. 

๊ฐ€์žฅ ๋จผ์ € ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ modifier๊ฐ€ ๋ถ™์€ ์ˆœ์„œ ์ƒ๊ด€ ์—†์ด onAppear๊ฐ€ ํ˜ธ์ถœ๋œ ๋’ค task๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค. ์—ฌ๋Ÿฌ task๊ฐ€ ๋ถ™์–ด ์žˆ์„ ๋•Œ์—๋Š” ๊ฐ€์žฅ ์œ„์— ์ ํžŒ task๊ฐ€ ๋จผ์ € ์‹คํ–‰๋˜๊ณ , ๊ฐ€์žฅ ๋‚˜์ค‘์— ๋ถ™์€ task๊ฐ€ ๋งˆ์ง€๋ง‰์œผ๋กœ ์‹คํ–‰๋œ๋‹ค. 

๊ณต์‹์ ์ธ ๋‚ด์šฉ์€ ์•„๋‹ˆ๋ผ ์ด ์ˆœ์„œ๊ฐ€ ์–ธ์ œ๋“  ๋ณ€ํ•  ์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ ์—ฌ๋Ÿฌ๋ฒˆ ์‹คํ–‰ํ•ด๋„ onAppear๊ฐ€ ๋จผ์ € ํ˜ธ์ถœ๋˜๊ณ  ์œ„์—์„œ ์•„๋ž˜ task ์ˆœ์œผ๋กœ ํ˜ธ์ถœ๋œ๋‹ค.

 

๊ทธ๋ ‡๋‹ค๋ฉด ์ด์ œ ParentView๋ฅผ ํ†ตํ•ด ์ด๋‹› ์‹œ์ ๊ณผ body property ๋‚ด๋ถ€ modifier์„ ๋น„๊ตํ•ด ๋ณด์ž

struct ContentView: View {
    @State private var isChildViewShow: Bool = false
    
    var body: some View {
        NavigationStack {
            ZStack {
                ParentView(isChildViewShow: $isChildViewShow)
                    .task {
                        print("-> ๐Ÿ” ParentView task before onAppear ๐Ÿ—‚")
                    }
                    .onAppear {
                        print("-> ๐Ÿ” ParentView onAppear ๐Ÿ‘ป")
                    }
                    .task {
                        print("-> ๐Ÿ” ParentView task after onAppear ๐Ÿ—‚")
                    }
                    .onDisappear {
                        print("-> ๐Ÿ” ParentView onDisappear ๐ŸŒช")
                    }
                   
                if isChildViewShow {
                    ChildView(isSelfShow: $isChildViewShow)
                }
            } //: ZStack
        }
    }
}
struct ParentView: View {
    @Binding var isChildViewShow: Bool
    
    init(isChildViewShow: Binding<Bool>) {
        self._isChildViewShow = isChildViewShow
        print("ParentView init!!!")
    }
    
    var body: some View {
    	let _ = print("ParentView ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ ๐Ÿ’ช")
        VStack {
            
        }
        .task {
            print("๐Ÿ” ParentView task before onAppear ๐Ÿ—‚")
        }
        .onAppear {
            print("๐Ÿ” ParentView onAppear ๐Ÿ‘ป")
        }
        .task {
            print("๐Ÿ” ParentView task after onAppear ๐Ÿ—‚")
        }
        .onDisappear {
            print("๐Ÿ” ParentView onDisappear ๐ŸŒช")
        }
    }
}
ParentView init!!!
ParentView ๋ฐ”๋””ํ”„๋กœํผํ‹ฐ ๐Ÿ’ช
๐Ÿ” ParentView onAppear ๐Ÿ‘ป
-> ๐Ÿ” ParentView onAppear ๐Ÿ‘ป
๐Ÿ” ParentView task before onAppear ๐Ÿ—‚
๐Ÿ” ParentView task after onAppear ๐Ÿ—‚
-> ๐Ÿ” ParentView task before onAppear ๐Ÿ—‚
-> ๐Ÿ” ParentView task after onAppear ๐Ÿ—‚

์ˆœ์„œ๋ฅผ ๋ณด๋ฉด ์ด๋‹›์‹œ์ ์— ๋ถ™์€ modifier๋ณด๋‹ค body ํ”„๋กœํผํ‹ฐ์˜ ์ตœ์ƒ์œ„ ๋ทฐ์—์„œ ํ˜ธ์ถœ๋œ modifier๊ฐ€ ๋จผ์ € ์‹คํ–‰๋œ๋‹ค. 

์šฐ์„  ์ˆœ์œ„ ๐Ÿ”ฝ ์šฐ์„  ์ˆœ์œ„ ๐Ÿ”ผ
ParentView(isChildViewShow: $isChildViewShow)
.onAppear {}
.task {}
var body: some View {
VStack {}
.onAppear {}
.task {}
}

init ์‹œ์  ๋˜ํ•œ View์ด๊ธฐ ๋•Œ๋ฌธ์— View life cycle์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” modifier์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, body property ๋‚ด๋ถ€์™€ ์ด๋‹› ์‹œ์  ๋‘ ๋ถ€๋ถ„์„ ํ˜ผ์šฉํ•ด์„œ ์“ฐ๋Š” ๊ฒƒ ๋ณด๋‹ค ํ•˜๋‚˜๋ฅผ ์ •ํ•ด ๋†“๊ณ  ์‚ฌ์šฉํ•˜๋Š”๊ฒŒ ์ข‹์„ ๋“ฏ ํ•˜๋‹ค. 

๊ฐœ์ธ์ ์œผ๋กœ๋Š” ๋ฐ”๋”” ํ”„๋กœํผํ‹ฐ์˜ ์ตœ์ƒ์œ„ ๋ทฐ์—์„œ onAppear๋‚˜ task๋ฅผ ํ˜ธ์ถœํ•˜๋Š”๊ฒŒ ์ข‹๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ํ•ด๋‹น ๋ทฐ์—์„œ ์‚ฌ์šฉํ•  ๋น„๋™๊ธฐ ๋กœ์ง์ด๋‚˜ appear ์‹œ ํ˜ธ์ถœ๋˜์–ด์•ผ ํ•˜๋Š” ๋กœ์ง์„ View struct ๋‚ด๋ถ€์—์„œ ํ•จ๊ป˜ ์ •์˜ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋งŒ์•ฝ ์ด๋‹› ์‹œ์ ์—์„œ onAppear๋‚˜ task modifier์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์ค‘๋ณต๋˜๋Š” ์ฝ”๋“œ๊ฐ€ ์ƒ๊ธฐ๊ฒŒ ๋œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด์„œ ParentView๋ฅผ ํ•˜๋‚˜์˜ ํ™”๋ฉด์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์ด๋‹› ์‹œ์ ์—์„œ onAppear์™€ task๋ฅผ ์ •์˜ํ–ˆ๋‹ค๋ฉด ๋‹ค๋ฅธ ํ™”๋ฉด์—์„œ ParentView๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋˜ ๋‹ค์‹œ onAppear์™€ task๋ฅผ ์ •์˜ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. 

 

onDisappear

struct ParentView: View {
    @Binding var isChildViewShow: Bool
    
    var body: some View {
        VStack {
            NavigationLink {
                ChildView(isSelfShow: $isChildViewShow)
            } label: {
                Text("๐Ÿ›ผ go to ChildView with NavigationLink")
            }
            
            Button {
                print("๐Ÿ›ผ go to ChildView with ZStack")
                isChildViewShow = true
            } label: {
                Text("๐Ÿ›ผ go to ChildView with ZStack")
            }
            .buttonStyle(.borderedProminent)
        }
        .onDisappear {
            print("๐Ÿ” ParentView onDisappear ๐ŸŒช")
        }
    }
}
struct ChildView: View {
    @Binding var isSelfShow: Bool
    
    var body: some View {
        VStack {
            Spacer()
            Text("Child View")
                .frame(maxWidth: .infinity)
            Button {
                isSelfShow = false
            } label: {
                Text("go to ContentView dismiss ZStack")
            }
            .buttonStyle(.borderedProminent)
            Spacer()
        }
        .background(.yellow)
        .onDisappear {
            print("๐Ÿค ChildView disappear ๐ŸŒช")
        }
    }
}

NavigationStack์œผ๋กœ ChildView๋ฅผ ์Œ“์•˜๋‹ค๊ฐ€ pop ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ˜ธ์ถœ๋œ๋‹ค. 

ChildView ์ด๋‹›: ChildView onAppear
๐Ÿ” ParentView onDisappear ๐ŸŒช
๐Ÿค ChildView disappear ๐ŸŒช

NavigationStack์œผ๋กœ ChildView๊ฐ€ ๋“ค์–ด์˜ค๋ฉด ParentView๋Š” onDisappear ๋œ๋‹ค. ์ฆ‰ NavigationStack์—์„œ ํ™”๋ฉด์— ๋ณด์ด์ง€ ์•Š๊ฒŒ ๋˜๋ฉด onDisappear๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค. 

ChildView ๋˜ํ•œ NavigationStack์—์„œ ์ œ๊ฑฐ๋˜๋ฉด onDisappear๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. 

onAppear modifier์„ ์‚ฌ์šฉํ•˜๋ฉด onDisappear์˜ ์‹œ์ ์„ ๋” ๋ช…ํ™•ํžˆ ์•Œ ์ˆ˜ ์žˆ๋‹ค. 

๐Ÿ” ParentView onAppear ๐Ÿ‘ป
๐Ÿค ChildView onAppear ๐Ÿ‘ป
๐Ÿ” ParentView onDisappear ๐ŸŒช
๐Ÿ” ParentView onAppear ๐Ÿ‘ป
๐Ÿค ChildView disappear ๐ŸŒช

์ฒ˜์Œ ํ™”๋ฉด์— ParentView๊ฐ€ ๋‚˜ํƒ€๋‚  ๋•Œ onAppear๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  NavigationStack์œผ๋กœ ChildView๋กœ ์ด๋™ํ•˜๋ฉด ChildView์˜ onAppear๊ฐ€ ํ˜ธ์ถœ๋œ ๋’ค ParentView๋Š” ํ™”๋ฉด์—์„œ ๋ณด์ด์ง€ ์•Š์œผ๋ฏ€๋กœ onDisappear๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค. ๊ทธ ํ›„ pop์„ ํ†ตํ•ด ChildView๋ฅผ ํ™”๋ฉด์—์„œ ์•ˆ๋ณด์ด๊ฒŒ ํ•˜๋ฉด ParentView์˜ onAppear๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  ChildView์˜ onDisappear๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.

 

        NavigationStack {
            ZStack {
                ParentView(isChildViewShow: $isChildViewShow)
                   
                if isChildViewShow {
                    ChildView(isSelfShow: $isChildViewShow)
                }
            } //: ZStack
        }

์ด์ œ ZStack์„ ์‚ฌ์šฉํ•ด์„œ ChildView๋กœ ParentView๋ฅผ ์™„์ „ํžˆ ๊ฐ€๋ฆฌ๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

๐Ÿ” ParentView onAppear ๐Ÿ‘ป
๐Ÿ›ผ go to ChildView with ZStack
๐Ÿค ChildView onAppear ๐Ÿ‘ป
๐Ÿค ChildView disappear ๐ŸŒช

ParentView๋Š” ์ด๋ฏธ ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚œ ์ƒํƒœ์ด๊ธฐ ๋•Œ๋ฌธ์— onAppear๋Š” ํ•œ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋œ๋‹ค. ChildView๋Š” ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚˜๋ฉด onAppear ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ํ™”๋ฉด์—์„œ ์‚ฌ๋ผ์ง€๋Š” ์ˆœ๊ฐ„ onDisappear๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. Navigation๊ณผ ํ™”๋ฉด์— ๋‹จ์ˆœ ํ‘œ์‹œ ๋ฐ ์ œ๊ฑฐํ•  ๋•Œ์˜ modifier ํ˜ธ์ถœ์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ์˜ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

 

๋์ด ์•„๋‹ˆ๋‹ค ์ด์ œ TabView๋ฅผ ํ…Œ์ŠคํŠธ ํ•ด๋ณด์ž ๐Ÿ‘๐Ÿ˜Œ

 

TabView

struct TabBarTest: View {
    var body: some View {
        TabView {
            FirstView()
                .tabItem {
                    Image(systemName: "01.circle.fill")
                    Text("1")
                }
            SecondView()
                .tabItem {
                    Image(systemName: "02.circle.fill")
                    Text("2")
                }
            ThirdView()
                .tabItem {
                    Image(systemName: "03.circle.fill")
                    Text("3")
                }
        }
    }
}
struct FirstView: View {
    var body: some View {
        Text("First")
            .task {
                print("1. before task")
            }
            .onAppear {
                print("1. appear")
            }
            .task {
                print("1. after task")
            }
            .onDisappear {
                print("1. disappear")
            }
    }
}

๊ฐ ํƒญ์— ํ•ด๋‹น ํ•˜๋Š” ๋ทฐ์—๋Š” task ๋‘๊ฐœ์™€ appear, disappear modifier์„ ํ˜ธ์ถœํ•˜๋„๋ก ํ•ด๋’€๋‹ค. ์˜ˆ์ƒ์œผ๋กœ๋Š” 1, 2, 3 ๋ฒˆ์งธ ํƒญ์— ํ•ด๋‹นํ•˜๋Š” ๋ชจ๋“  View์˜ appear๊ฐ€ ํ˜ธ์ถœ๋  ๊ฒƒ ๊ฐ™์•˜๋Š”๋ฐ ์•„๋‹ˆ๋‹ค!

1. appear
1. before task
1. after task

์šฐ์„  ์ฒ˜์Œ์— ํ™”๋ฉด์— ํƒญ๋ทฐ๊ฐ€ ๋‚˜ํƒ€๋‚˜๋ฉด ์ฒซ ๋ฒˆ์งธ ํƒญ์˜ appear์™€ task๋งŒ ํ˜ธ์ถœ๋œ๋‹ค. 

2. appear
1. disappear
2. before task
2. after task

์ดํ›„ 2๋ฒˆ ํƒญ์„ ๋ˆ„๋ฅด๋ฉด 2๋ฒˆ view์˜ appear๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  ์ฒซ ๋ฒˆ์งธ ํƒญ์˜ 1๋ฒˆ ๋ทฐ๋Š” disappear๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ํ™”๋ฉด์— ๋‹ค๋ฅธ ๋ทฐ๊ฐ€ appear ๋˜๋ฉด -> ๊ธฐ์กด์˜ ๋ทฐ๊ฐ€ disappear ๋˜๋Š” ์ˆœ์„œ๋Š” ์•ž์—์„œ ํ™•์ธํ•œ ๊ฒƒ๊ณผ ๋™์ผํ•˜๋‹ค. ์ดํ›„ 2๋ฒˆ ์งธ ๋ทฐ์˜ task๊ฐ€ ์ˆœ์„œ๋Œ€๋กœ ํ˜ธ์ถœ๋œ๋‹ค.

3. appear
3. before task
3. after task
2. disappear

์ด๋ฒˆ์—๋Š” 2๋ฒˆ ํƒญ์—์„œ 3๋ฒˆ ํƒญ์œผ๋กœ ์ด๋™ํ•  ๋•Œ์ธ๋ฐ, 2๋ฒˆ ๋ทฐ์˜ disappear ๋ณด๋‹ค 3๋ฒˆ ๋ทฐ์˜ task๊ฐ€ ๋จผ์ € ์‹คํ–‰๋˜์—ˆ๋‹ค. task์™€ ๋‹ค๋ฅธ ๋ทฐ์— ์ •์˜๋œ disappear ๊ฐ„์˜ ์ˆœ์„œ๋Š” ์ •ํ•ด์ง€์ง€ ์•Š๊ณ  ๋žœ๋คํ•˜๊ฒŒ ํ˜ธ์ถœ๋˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค. 

1. appear
1. before task
1. after task
3. disappear

๋‹ค์‹œ 3๋ฒˆ์—์„œ 1๋ฒˆ ํƒญ์œผ๋กœ ๋Œ์•„์˜ค๋ฉด 1๋ฒˆ์˜ onAppear๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  ์‚ฌ๋ผ์ง„ 3๋ฒˆ ๋ทฐ๋Š” disappear๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

TabView ๋˜ํ•œ NavigationStack๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚˜๋ฉด onAppear๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ํ™”๋ฉด์—์„œ ์‚ฌ๋ผ์ง€๋ฉด onDisappear๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. task ๋˜ํ•œ ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚  ๋•Œ๋งˆ๋‹ค ํ˜ธ์ถœํ•˜๋Š” ๊ฑธ ์•Œ ์ˆ˜ ์žˆ๋‹ค. 

struct SecondView: View {
    init() {
        print("2. init")
    }
    
    var body: some View {
        let _ = print("2. body property")
        Text("Second")
            .task {
                print("2. before task")
            }
            .onAppear {
                print("2. appear")
            }
            .task {
                print("2. after task")
            }
            .onDisappear {
                print("2. disappear")
            }
    }
}

์ด๋ฒˆ์—๋Š” ๊ฐ ํƒญ์— ํ•ด๋‹น๋˜๋Š” ๋ชจ๋“  ๋ทฐ์— initializer๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๋กœ๊ทธ๋ฅผ ์ฐ์–ด์ฃผ์—ˆ๋‹ค. body property๊ฐ€ ๊ทธ๋ ค์ง€๋Š” ์‹œ์ ์—๋„ ๋กœ๊ทธ๋ฅผ ์ฐ์—ˆ๋‹ค. 

1. init
2. init
3. init
1. body property
1. appear
1. before task
1. after task

์˜ˆ์ƒํ•œ๋ฐ๋กœ init์€ ๋ชจ๋‘ ํ˜ธ์ถœ๋˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋Š”๋ฐ

struct TabBarTest: View {
    var body: some View {
        TabView {
            FirstView()
                .tabItem {
                    Image(systemName: "01.circle.fill")
                    Text("1")
                }
            SecondView()
                .tabItem {
                    Image(systemName: "02.circle.fill")
                    Text("2")
                }
            ThirdView()
                .tabItem {
                    Image(systemName: "03.circle.fill")
                    Text("3")
                }
        }
    }
}

TabView๋ฅผ ๊ทธ๋ฆด ๋•Œ init ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ฐ ํƒญ์˜ ๋ทฐ๊ฐ€ ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚  ๋•Œ ๋น„๋™๊ธฐ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด์•ผ ํ•œ๋‹ค๋ฉด init์ด ์•„๋‹ˆ๋ผ task๊ฐ€ ์ ๋‹นํ•˜๋‹ค (์‚ฌ์‹ค ๊ทธ๋Ÿฌ๋ผ๊ณ  ์• ํ”Œ์ด ๋งŒ๋“ค์–ด ๋‘” modifier์ด๊ธฐ๋„ ํ•˜๋‹ค)

1. init
2. init
3. init
1. body property
1. appear
1. before task
1. after task

๋‹ค์‹œ ์‚ดํŽด๋ณด๋ฉด appear ๋˜๊ธฐ ์ด์ „์— 1๋ฒˆ ๋ทฐ์˜ body๋ฅผ ๊ทธ๋ฆฐ๋‹ค. 1๋ฒˆ ํƒญ์—์„œ 2๋ฒˆ ํƒญ์œผ๋กœ ์ด๋™ํ•˜๋ฉด 

2. body property
2. appear
1. disappear
2. before task
2. after task

2๋ฒˆ ๋˜ํ•œ body property๋ฅผ ๊ทธ๋ฆฌ๊ณ  appear๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค. ์—ฌ๊ธฐ์„œ ๋‹ค์‹œ 1๋ฒˆ ํƒญ์„ ๋ˆ„๋ฅด๋ฉด

1. appear
2. disappear
1. before task
1. after task

1๋ฒˆ์˜ body property ๊ฐ€ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ ์ด์œ ๋Š” TabView์—์„œ ์ง€์ •ํ•ด ์ค€ ๊ฐ ํƒญ์˜ ๋ทฐ๊ฐ€ ์ด๋ฏธ ์•”์‹œ์  ID๋ฅผ ๋ถ€์—ฌ ๋ฐ›๊ณ , ์—…๋ฐ์ดํŠธ ๋˜๋Š” dependency๋„ ์—†๊ธฐ ๋•Œ๋ฌธ์— body property๋ฅผ ์ƒˆ๋กœ ๊ทธ๋ ค์ฃผ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ฆ‰ ํƒญ์„ ๋ˆŒ๋Ÿฌ ๋ทฐ๋ฅผ ์ด๋™ํ•  ๋•Œ ๋ณด์—ฌ์ง€๋Š” ๋ทฐ๋“ค์€ ํ•œ ๋ฒˆ ๊ทธ๋ ค์ง€๊ณ  ๊ทธ ๊ทธ๋ ค์ง„ ๋ทฐ๋ฅผ ๋‹ค์‹œ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ๋Š” ๊ฒƒ์ด๋‹ค. (๋งŒ์•ฝ 1๋ฒˆ ํƒญ์—์„œ 2๋ฒˆ ํƒญ์œผ๋กœ, ๋‹ค์‹œ 1๋ฒˆ ํƒญ์œผ๋กœ ๋Œ์•„์˜จ๋‹ค๊ณ  ํ–ˆ์„ ๋•Œ ์ด์ „์˜ 1๋ฒˆ ๋ทฐ์™€ ์ง€๊ธˆ์˜ 1๋ฒˆ ๋ทฐ๋Š” ๊ฐ™์€ ๋ทฐ์ด๋‹ค. SwiftUI์—์„œ ๊ตฌ์กฐ์ ์œผ๋กœ ๊ฐ™์€ ID๋ฅผ ๊ฐ€์ง„ ๋ทฐ๋กœ ์ธ์‹ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด์ „ ๋ทฐ๋ฅผ ์ฆ‰์‹œ ํŒŒ๊ดดํ•˜๊ณ  ์ƒˆ๋กœ ๊ทธ๋ฆฌ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์ด์ „ ๋ทฐ๋ฅผ ๋‹ค์‹œ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ๋Š” ๊ฒƒ์ด๋‹ค)

 

+) TabView ์ž์ฒด์— ๊ฑธ์–ด๋‘” onAppear, task, onDisappear ์ค‘ onAppear์™€ task๋Š” ํ™”๋ฉด์— ๋ณด์ผ ๋•Œ ๊ฐ๊ฐ ํ•œ ๋ฒˆ ํ˜ธ์ถœ๋œ๋‹ค. 

custom tabbar๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค๋ฅธ ๋™์ž‘์„ ํ•˜๊ฒ ์ง€๋งŒ ์ด ์ •๋„ ์‚ดํŽด๋ดค์œผ๋ฉด ์ถฉ๋ถ„ํ•œ ๊ฒƒ ๊ฐ™๋‹ค.

 

์—ฌ๊ธฐ์„œ ๋์ด ์•„๋‹ˆ๋‹ค! ํ•˜๋‚˜ ๋” ๋‚จ์•˜๋‹ค ์ด์ œ ์ง„์งœ ๋งˆ์ง€๋ง‰!! ๐Ÿ’ช๐Ÿ™ƒ

 

modal

struct ModalTestView: View {
    @State private var isShowModal = false
    
    var body: some View {
        VStack {
            Button("๋ชจ๋‹ฌ") {
                isShowModal.toggle()
            }
        }
        .task {
            print("๐Ÿ” ParentView task before onAppear ๐Ÿ—‚")
        }
        .onAppear {
            print("๐Ÿ” ParentView onAppear ๐Ÿ‘ป")
        }
        .task {
            print("๐Ÿ” ParentView task after onAppear ๐Ÿ—‚")
        }
        .onDisappear {
            print("๐Ÿ” ParentView onDisappear ๐ŸŒช")
        }
        .sheet(isPresented: $isShowModal) {
            ModalChildView()
        }
    }
}
struct ModalChildView: View {
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        VStack {
            Button("dismiss ๋ฒ„ํŠผ") {
                dismiss()
            }
        }
        .task {
            print("๐Ÿค ChildView task before onAppear ๐Ÿ—‚")
        }
        .onAppear {
            print("๐Ÿค ChildView onAppear ๐Ÿ‘ป")
        }
        .task {
            print("๐Ÿค ChildView task after onAppear ๐Ÿ—‚")
        }
        .onDisappear {
            print("๐Ÿค ChildView disappear ๐ŸŒช")
        }
    }
}

๋ฒ„ํŠผ ๋ˆŒ๋Ÿฌ์„œ ModalChildView๋ฅผ ํ™”๋ฉด์— ๋„์šฐ๋ฉด 

๐Ÿ” ParentView onAppear ๐Ÿ‘ป
๐Ÿ” ParentView task after onAppear ๐Ÿ—‚
๐Ÿ” ParentView task before onAppear ๐Ÿ—‚
๐Ÿค ChildView onAppear ๐Ÿ‘ป
๐Ÿค ChildView task before onAppear ๐Ÿ—‚
๐Ÿค ChildView task after onAppear ๐Ÿ—‚

์ฒ˜์Œ ์„ธ ์ค„์€ ์ฒ˜์Œ ์‹คํ–‰ํ–ˆ์„ ๋•Œ ParentView๊ฐ€ ํ™”๋ฉด์— ๋ณด์ด๋Š” ์ˆœ๊ฐ„ ํ˜ธ์ถœ๋˜๋Š” modifier์ด๋‹ค. ์ดํ›„ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ๋ชจ๋‹ฌ์„ ๋„์šฐ๋ฉด ์•„๋ž˜ ์„ธ ์ค„์ด ๋œฌ๋‹ค. 

๐Ÿค ChildView disappear ๐ŸŒช

์ดํ›„ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ๋ชจ๋‹ฌ์„ ๋‹ซ์œผ๋ฉด childview์˜ onDisappear๋งŒ ํ˜ธ์ถœ๋˜๊ณ , ParentView์˜ onAppear๋Š” ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค. ์Šค์™€์ดํ”„ ์ œ์Šค์ฒ˜๋„ ๋งˆ์ฐฌ๊ฐ€์ง€!

 

1. sheet์œผ๋กœ ๋ชจ๋‹ฌ์„ ๋„์šฐ๋ฉด ๋ชจ๋‹ฌ์„ ๋„์šด ParentView๋Š” onDisappear๊ฐ€ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค.

2. sheet์„ ๋‹ซ์•„๋„ ParentView์˜ onAppear๋Š” ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค.

 

        .fullScreenCover(isPresented: $isShowModal, content: {
            ModalChildView()
        })

.sheet modifier์„ .fullScreenCover modifier๋กœ ๋ฐ”๊ฟ”๋„ ๋™์ž‘์€ ๋™์ผํ•˜๋‹ค.

        .sheet(isPresented: $isShowModal) {
            print("ParentView๋กœ ๋Œ์•„์˜ด")
        } content: {
            ModalChildView()
        }

๋งŒ์•ฝ ChildView์—์„œ ParentView๋กœ ๋Œ์•„์™”์„ ๋•Œ ์‹คํ–‰ํ•  ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๋ฉด onDismiss ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. sheet, fullScreenCover modifier ๋ชจ๋‘ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ์ด๋‹ค.

ParentView๋กœ ๋Œ์•„์˜ด
๐Ÿค ChildView disappear ๐ŸŒช

ChildView์˜ onDisappear๊ฐ€ ํ˜ธ์ถœ๋˜๊ธฐ ์ด์ „์— onDismiss๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.

        .sheet(isPresented: $isShowModal, content: {
            ModalChildView()
                .presentationDetents([.medium])
        })

ํ˜น์‹œ ์ ˆ๋ฐ˜๋งŒ ๊ฐ€๋ฆฌ๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ ํ–ˆ๋Š”๋ฐ sheet, fullScreenCover์—์„œ ๋™์ž‘ํ•œ ๊ฒƒ๊ณผ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•œ๋‹ค. 

 

๊ฒฐ๋ก 

life cycle modifier ํŠน์ง•
onAppear - ๋ทฐ๊ฐ€ ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚˜๊ธฐ ์ด์ „์— ์‹คํ–‰
- NavigationStack : ํ™”๋ฉด์— ์ตœ์ดˆ ๋‚˜ํƒ€๋‚  ๋•Œ ํ˜ธ์ถœ, ChildView๊ฐ€ push๋˜๊ณ  pop ๋ ๋•Œ ํ˜ธ์ถœ
- ZStack : ์ฒ˜์Œ ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚  ๋•Œ ํ•œ๋ฒˆ๋งŒ ํ˜ธ์ถœ, ChildView๊ฐ€ ํ™”๋ฉด์„ ์ „์ฒด ๋‹ค ๊ฐ€๋ ธ๋‹ค๊ฐ€ ์‚ฌ๋ผ์ ธ๋„ onAppear๋Š” ํ˜ธ์ถœ๋˜์ง€ ์•Š์Œ
-TabView: ํƒญ์„ ๋ˆŒ๋Ÿฌ ํ™”๋ฉด์—์„œ ๋‚˜ํƒ€๋‚  ๋•Œ ๋งˆ๋‹ค
- sheet: ๋ชจ๋‹ฌ๋กœ ๋„์›Œ์กŒ์„ ๋•Œ ํ˜ธ์ถœ, ๋ชจ๋‹ฌ์„ ๋„์šฐ๋Š” View๋ผ๋ฉด modal์„ ๋‹ซ์•„๋„ ํ˜ธ์ถœ๋˜์ง€ ์•Š์Œ
onDisappear - ํ™”๋ฉด์—์„œ View๊ฐ€ ์‚ฌ๋ผ์ง€๊ณ  ๋‚œ ํ›„์— ์‹คํ–‰
- NavigationStack์—์„œ ์ œ๊ฑฐ๋  ๋•Œ
- TabView์—์„œ ํƒญ์„ ๋ฐ”๊ฟ” ํ™”๋ฉด์—์„œ ์‚ฌ๋ผ์งˆ ๋•Œ ๋งˆ๋‹ค
- sheet: ๋ชจ๋‹ฌ๋กœ ๋„์›Œ์กŒ๋‹ค๊ฐ€ ์‚ฌ๋ผ์งˆ ๋•Œ, ๋ชจ๋‹ฌ์„ ๋„์šฐ๋Š” View๋ผ๋ฉด ๋ชจ๋‹ฌ์— ์˜ํ•ด ํ™”๋ฉด์— ๊ฐ€๋ ค์ ธ๋„ ํ˜ธ์ถœ๋˜์ง€ ์•Š์Œ
task - ๋น„๊ณต์‹์ ์œผ๋กœ onAppear ๋’ค์— ํ˜ธ์ถœ๋จ
- ์—ฌ๋Ÿฌ๊ฐœ์˜ task๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ์œ„์—์„œ ์•„๋ž˜ ์ˆœ์œผ๋กœ ์‹คํ–‰

init -> body property ๊ทธ๋ ค์ง -> onAppear ํ˜ธ์ถœ

(init๊ณผ body property update์˜ ๊ฒฝ์šฐ depdency์— ์˜ํ•ด ํ˜ธ์ถœ๋  ์ˆ˜๋„, ๋˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ์˜)

 

์ด ์ •๋„๋ฉด,,, ์‚ฌ์šฉํ•  ๋•Œ๋งˆ๋‹ค ์ฐ์–ด๋ณด๋Š”๊ฒŒ ๊ฐ€์žฅ ๋ฒ ์ŠคํŠธ์ด์ง€ ์•Š์„๊นŒ ์‹ถ๊ธฐ๋„ ํ•˜๋‹ค ๋˜๋ฅต...๐Ÿฅฒ

<a href="https://pixabay.com/ko//?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=1176406">Pixabay</a>๋กœ๋ถ€ํ„ฐ ์ž…์ˆ˜๋œ <a href="https://pixabay.com/ko/users/walkersalmanac-1995717/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=1176406">walkersalmanac</a>๋‹˜์˜ ์ด๋ฏธ์ง€ ์ž…๋‹ˆ๋‹ค.