SwiftUI

SwiftUI ์žฌ์‚ฌ์šฉ๊ฐ€๋Šฅํ•œ ๋ทฐ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ @ViewBuilder

ํ•˜์ดD:) 2023. 6. 5. 17:19

# ๐Ÿฅจ  @ViewBuilder๋ž€? 

์ •์˜ : ํด๋กœ์ €๋กœ ๋ฐ›์€ ๋ทฐ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” custom parameter attribute

์ฆ‰, ํด๋กœ์ €๋กœ ๋ฐ›์€ (child)view์— ๋Œ€ํ•œ ์–ดํŠธ๋ฆฌ๋ทฐํŠธ ํ‚ค์›Œ๋“œ์ด๋ฉฐ, ์šฐ๋ฆฌ๊ฐ€ ํ™œ์šฉํ•  ๋•Œ ์ค‘์š”ํ•œ๊ฑด closure ๋‚ด๋ถ€์— (child)view ๋ฅผ ์ „๋‹ฌํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. 

* swift์—์„œ ๋งํ•˜๋Š” @์–ดํŠธ๋ฆฌ๋ทฐํŠธ ํ‚ค์›Œ๋“œ๋ž€, ์ปดํŒŒ์ผ๋Ÿฌ์—๊ฒŒ ์ถ”๊ฐ€์ ์ธ ์ •๋ณด๋ฅผ ์•Œ๋ ค์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

 

# ๐Ÿฅจ  SwiftUI์—์„œ ์ •์˜ํ•œ Button์—์„œ๋„ ๋ณผ ์ˆ˜ ์žˆ๋Š”  @ViewBuilder  

https://developer.apple.com/documentation/swiftui/button

 

Button | Apple Developer Documentation

A control that initiates an action.

developer.apple.com

SwiftUI์—์„œ ์ •์˜ํ•œ Button ์ƒ์„ฑ์ž๋Š” ์•„๋ž˜์™€๊ฐ™๋‹ค.

init(action: @escaping () -> Void, @ViewBuilder label: () -> Label)

์šฐ๋ฆฌ๊ฐ€ ์œ„์˜ ์ •์˜์—์„œ ํ™•์ธํ•œ ์ฒ˜๋Ÿผ label ํด๋กœ์ €์— View๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ์žˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ด์ฒ˜๋Ÿผ ๋ทฐ๋กœ ๊ตฌ์„ฑ๋œ label์„ ํด๋กœ์ €๋กœ ์ „๋‹ฌ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๋„ SwiftUI์—์„œ ์ •์˜ํ•œ Button์—์„œ label ๋งค๊ฐœ๋ณ€์ˆ˜์— @ViewBuilder ํ‚ค์›Œ๋“œ๊ฐ€ ๋ถ™์–ด์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. 

Button{
	print("์ทจ์†Œ๋ฒ„ํŠผ")
} label : {
	Text("์ทจ์†Œ")
}

 

# ๐Ÿฅจ  @ViewBuilder ์–ธ์ œ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์„๊นŒ?

๊ณตํ†ต์ ์ธ ๋ถ€๋ชจ Container๋ฅผ ๋งŒ๋“œ๋Š”๋ฐ ์œ ์šฉํ•˜๋‹ค.

๋‚˜๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” ์ฃผ๋กœ ๋น„์Šทํ•œ ๋ ˆ์ด์•„์›ƒ์„ ๊ฐ–๋Š” ๋ทฐ์—์„œ ์ƒํ™ฉ๋งˆ๋‹ค ๋‚ด๋ถ€ ๋‚ด์šฉ์ด ๋ฐ”๋€Œ๋Š” ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•ด์ฃผ์—ˆ๋‹ค.์˜ˆ๋ฅผ๋“ค์–ด, ์•ฑ์—์„œ ์‚ฌ์šฉ์ž์—๊ฒŒ ์–ด๋–ค ์ •๋ณด๋ฅผ ์•ˆ๋‚ดํ•˜๊ฑฐ๋‚˜ ์˜์‚ฌ๋ฅผ ํ™•์ธํ•ด์•ผํ•  ๋•Œ ๋ชจ๋‹ฌ์„ ๋„์šฐ๊ณคํ•œ๋‹ค. ๋ชจ๋‹ฌ์˜ ๋‚ด๋ถ€ ๋ทฐ์˜ ๋ ˆ์ด์•„์›ƒ์ด๋‚˜ ๊ตฌ์กฐ๋Š” ํฌ๊ฒŒ ์ฐจ์ด๋‚˜์ง€ ์•Š๊ณ  ์ƒํ™ฉ๋ณ„๋กœ ๋ฒ„ํŠผ์ด๋‚˜ ๋‚ด์šฉ์˜ ๋ฌธ๋‹จ ๊ฐœ์ˆ˜ ๋“ฑ์ด ๋ฐ”๋€๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ์— ์„ธ๋ถ€์ ์œผ๋กœ ๋‹ค๋ฅธ ๋ทฐ๋“ค์„  ํด๋กœ์ €๋กœ ๋ฐ›์œผ๋ฉด ์žฌ์‚ฌ์šฉ๊ฐ€๋Šฅํ•œ ๋ชจ๋‹ฌ๋กœ ๋ชจ๋“ˆํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.

 

# ๐Ÿฅจ  ๊ณต์‹๋ฌธ์„œ

ViewBuilder ๊ด€๋ จํ•œ ์• ํ”Œ ๊ณต์‹๋ฌธ์„œ

https://developer.apple.com/documentation/swiftui/viewbuilder

 

ViewBuilder | Apple Developer Documentation

A custom parameter attribute that constructs views from closures.

developer.apple.com

๊ณต์‹๋ฌธ์„œ์— ๋‚˜์˜จ ์˜ˆ์ œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ทฐ๋ฅผ ๊ทธ๋ ค์ฃผ์—ˆ๋‹ค.

import SwiftUI

struct MyStruct1 {
    func contextMenu<MenuItems: View>(
        @ViewBuilder menuItems: () -> MenuItems
    ) -> some View {
        VStack{
            Text("@ViewBuilder๋กœ ๋ฐ›์€ ๋ทฐ ๋ณด์—ฌ์ฃผ๊ธฐ")
                .foregroundColor(.red)

            menuItems()
        }  
    }
}


struct ViewBuilderPracticeView: View {
    let myStruct1 : MyStruct1 = MyStruct1()

    var body: some View {
        myStruct1.contextMenu { // ํด๋กœ์ €๋กœ ๋ทฐ๋ฅผ ์ „๋‹ฌํ•œ๋‹ค

            Text("Cut")

            Text("Copy")

            Text("Paste")
        }
    }
}

 

 

# ๐Ÿฅจ  ์‹ค์ œ ์•ฑ์— ํ™œ์šฉ

์‹ค์ œ๋กœ ์•ฑ์—์„œ ๋ชจ๋‹ฌ์„ ๋งŒ๋“ค ๋•Œ ํ™œ์šฉํ–ˆ๋˜ ์ฝ”๋“œ ์˜ˆ์‹œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

//ํด๋กœ์ €๋กœ ๋ฐ›๋Š” ๋ทฐ๋“ค์„ ์ œ๋„ค๋ฆญ์œผ๋กœ ์ •์˜ํ•ด์ค€๋‹ค

struct CustomModal<H, C, F>: View where H: View, C : View, F: View {
    @Binding var showingModal: Bool
    var header : () -> H
    var content : () -> C
    var footer : () -> F
 
    //์ดˆ๊ธฐํ™” ์ฝ”๋“œ์—์„œ @ViewBuilder ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์กฐ์ฒด ์ดˆ๊ธฐํ™” ์‹œ ํด๋กœ์ €๋กœ ๋ทฐ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋กํ•œ๋‹ค
    //@escaping ํ‚ค์›Œ๋“œ๊ฐ€ ์‚ฌ์šฉ๋œ ์ด์œ ๋Š” ๋ทฐ ์ดˆ๊ธฐํ™” ์ค‘์— ๋ฐ”๋กœ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค
    
    init(
        showingModal : Binding<Bool>,
        @ViewBuilder header : @escaping () -> H,
        @ViewBuilder content : @escaping () -> C,
        @ViewBuilder footer : @escaping () -> F
    ) {
        self._showingModal = showingModal
        self.header = header
        self.content = content
        self.footer = footer
    }
    
    
    var body: some View {
        VStack {
            HStack{
                Spacer()
                Button{
                    showingModal = false
                }label:{
                    Text("x")
                        .foregroundColor(.black)
                }
                
            }
            header()
                .padding(.vertical)
            
            content()
                .padding(.bottom)
            
            footer()
                .padding(.bottom)
        }
        .padding()
        .background(Color.gray)
        .frame(width: UIScreen.main.bounds.width*0.8)
        .cornerRadius(12)
    }
}

์œ„์—์„œ ์ •์˜ํ•œ ๋ชจ๋‹ฌ ๋ทฐ๋ฅผ ์•„๋ž˜์ฒ˜๋Ÿผ ํ™œ์šฉํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

struct ViewBuilderPracticeView: View {
    @State var isShown : Bool = false
    
    var body: some View {
        
        ZStack{
            
            Button{
                self.isShown = true
            } label : {
                Text("ํƒˆํ‡ดํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?")
                    .asGreenToggleButton(isGreen : true)
                    .frame(width : 200)
                    .padding()
            }
            
            if isShown {
                CustomModal(
                    showingModal : $isShown,
                    header : {
                        Text("์ •๋ง ํƒˆํ‡ดํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?")
                    },
                    content : {
                        Text("์ง€๊ธˆ ํƒˆํ‡ดํ•˜์‹œ๋ฉด ์‚ฌ์šฉํ–ˆ๋˜ ๊ธฐ๋ก๋“ค์ด ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค ๊ทธ๋ž˜๋„ ํƒˆํ‡ด๋ฅผ ์ง„ํ–‰ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?")
                    },
                    footer : {
                        HStack{

                            Button{
                                print("์ทจ์†Œ๋ฒ„ํŠผ")
                                self.isShown = false
                            } label : {
                                Text("์ทจ์†Œ")
                                    .asGreenToggleButton(isGreen : true)
                            }

                            Button{
                                print("ํ™•์ธ๋ฒ„ํŠผ")
                                self.isShown = false
                            } label : {
                                Text("ํ™•์ธ")
                                    .asGreenToggleButton(isGreen : false)
                            }
                        }

                    }

                )
            }
        }
        
    }
}

 

์•„๋ž˜์™€ ๊ฐ™์€ ๋ทฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.