iOS/SwiftUI

[SwiftUI] ์ปค์Šคํ…€ BottomSheet์„ ๋งŒ๋“ค์–ด๋ณด์ž

๊ฒฝํ‘ธ 2024. 1. 22. 10:30
๋ฐ˜์‘ํ˜•

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ผ์ „์— ํฌ์ŠคํŒ…ํ–ˆ๋˜ ๋ฉ”๋ชจ์•ฑ์—์„œ ์‚ฌ์šฉํ–ˆ๋˜ BottomSheet์— ๋Œ€ํ•ด์„œ ์ž‘์„ฑํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

๋ฉ”๋ชจ๋ฅผ ์ถ”๊ฐ€, ์‚ญ์ œ, ์ˆ˜์ •ํ–ˆ์„ ๋•Œ๋งˆ๋‹ค ํ•˜๋‹จ์—์„œ ํ•ด๋‹น ์ž‘์—…์„ ์„ฑ๊ณตํ–ˆ๋‹ค๋Š” ์•Œ๋ฆผ์„ ๋…ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ๋ชฉ์ ์ด์—ˆ๋‹ค.

๋ฐ”๋กœ ์•Œ์•„๋ณด์ž.

 

BottomSheetType

์•ž์„œ ๋งํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ์ถ”๊ฐ€, ์‚ญ์ œ, ์ˆ˜์ • ์„ธ ๊ฐ€์ง€๋ฅผ ์•Œ๋ ค์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด์— ๋งž๋Š” ํ…์ŠคํŠธ๋“ค์ด ํ•„์š”ํ•˜๋‹ค.

enum BottomSheetType {
    
    enum BottomSheetResult {
        case fail
        case success
        
        var description: String {
            switch self {
            case .fail:
                return "์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค :("
            case .success:
                return "์„ฑ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค :)"
            }
        }
    }
    
    case update(BottomSheetResult)
    case delete(BottomSheetResult)
    case add(BottomSheetResult)
    
    var title: String {
        switch self {
        case .update:
            return "๋ฉ”๋ชจ ์—…๋ฐ์ดํŠธ ์•ˆ๋‚ด"
        case .delete:
            return "๋ฉ”๋ชจ ์‚ญ์ œ ์•ˆ๋‚ด"
        case .add:
            return "๋ฉ”๋ชจ ์ถ”๊ฐ€ ์•ˆ๋‚ด"
        }
    }
    
    var message: String {
        switch self {
        case .update(let type):
            return "์—…๋ฐ์ดํŠธ์— \(type.description)"
        case .delete(let type):
            return "์‚ญ์ œ์— \(type.description)"
        case .add(let type):
            return "์ƒˆ๋กœ์šด ๋ฉ”๋ชจ ์ถ”๊ฐ€์— \(type.description)"
        }
    }
}

ํ•ด๋‹น ์ž‘์—…์— ์‹คํŒจํ–ˆ๋Š”์ง€ ์„ฑ๊ณตํ–ˆ๋Š”์ง€ ์•Œ๋ ค์ค„ ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค.

๊ทธ๋‹ค์ง€ ์ค‘์š”ํ•˜์ง„ ์•Š๋‹ค.

๋‹ค์Œ.

 

BottomSheet View

์ผ๋‹จ ํ•˜๋‹จ์— ๋ณด์ด๊ฒŒ๋  ์•Œ๋ฆผ๊ณผ ๋’ค์— ๋ฐฑ๊ทธ๋ผ์šด๋“œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

๋”ฐ๋ผ์„œ ZStack์„ ์ด์šฉํ•ด์„œ Color๋ฅผ ํ†ตํ•ด ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋ฅผ ๋จผ์ € ๋„ฃ๊ณ , ์•Œ๋ฆผ ๋ฉ”์‹œ์ง€๊ฐ€ ์žˆ๋Š” ํ™”๋ฉด์„ ๋ณด์—ฌ์ฃผ๊ธฐ๋กœ ํ–ˆ๋‹ค.

struct BottomSheet: View {

    @Binding var isShowing: Bool
    @Binding var bottomSheetType: BottomSheetType 
    
    var body: some View {
        ZStack(alignment: .bottom) { 
            if isShowing {
                Color.black
                    .opacity(0.3)
                    .ignoresSafeArea()
                    .onTapGesture { isShowing.toggle() }
                
                BottomSheetMessageView(bottomSheetType: $bottomSheetType)
                    .setRoundedCorner(radius: 12)
                    .transition(.move(edge: .bottom))
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
        .ignoresSafeArea()
        .animation(.easeInOut, value: isShowing)
    }
}

BottomSheet์„ ์ƒ์œ„๋ทฐ์—์„œ ์‚ฌ์šฉํ•  ์˜ˆ์ •์ด๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์œ„๋ทฐ๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ํ”„๋กœํผํ‹ฐ๋“ค์„ Binding ํ•ด์„œ ์‚ฌ์šฉํ–ˆ๋‹ค. ๊ฐ€๋ น ๋ฉ”์‹œ์ง€๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ์€ ์ƒ์œ„๋ทฐ๋งŒ ์•Œ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— BottomSheetType์„ Binding ํ•ด "์ถ”๊ฐ€"์™€ ๊ด€๋ จ๋œ ๋‚ด์šฉ์œผ๋กœ ์ฑ„์šฐ๊ธฐ ์œ„ํ•ด์„œ๋‹ค.

 

BottomSheetMessageView

์œ„์— ๋ณด์ด๋Š” ์‹ค์ œ ์•Œ๋ฆผ ๋‚ด์šฉ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋ทฐ๋‹ค.

struct BottomSheetMessageView: View {
    
    @Binding var bottomSheetType: BottomSheetType
    
    var body: some View {
        
        VStack(spacing: 8) {
            Text(bottomSheetType.title)
                .font(.system(size: 18, weight: .bold))
                .foregroundStyle(.black)
            
            Text(bottomSheetType.message)
                .font(.system(size: 16, weight: .regular))
                .foregroundStyle(.black)
        }
        .frame(maxWidth: .infinity, minHeight: 150)
        .background(.white)
    }
}

BottomSheet์—์„œ Binding๋œ ๊ฐ’์„ ์ „๋‹ฌ๋ฐ›์•„ ์‚ฌ์šฉํ•œ๋‹ค.

 

์‚ฌ์šฉ๋ฐฉ๋ฒ•

๊ฐ„๋‹จํ•˜๋‹ค. ZStack์œผ๋กœ ๊ตฌ์„ฑ๋œ ์ƒ์œ„๋ทฐ์—์„œ ํ•˜๋‹จ์— ์ž‘์„ฑํ•˜๊ณ  @State๋กœ ์„ ์–ธ๋œ ํ”„๋กœํผํ‹ฐ๋งŒ ์ž˜ ๋ฐ”์ธ๋”ฉํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

@State private var isShowingBottomSheet = false
@State private var bottomSheetType: BottomSheetType = .add(.success)

var body: some View {
        WithViewStore(self.store, observe: { $0 }) { viewStore in
            ZStack {
                NavigationStack {
                    List {
                         ...
                        }
                    }
               }
               ...
               BottomSheet(isShowing: $isShowingBottomSheet, bottomSheetType: $bottomSheetType)
            }
        }
    }

 

๋” ์ข‹์€ ๋ฐฉ๋ฒ•์ด ์žˆ๊ฑฐ๋‚˜ ์ž˜๋ชป๋œ ๋ถ€๋ถ„์ด ์žˆ์œผ๋ฉด ๋Œ“๊ธ€๋กœ ์•Œ๋ ค์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ ์ด๋งŒ.

๋ฐ˜์‘ํ˜•