SwiftUI 프로젝트 target versionIOS 14로 진행중 Sheet 사용해야 되서 사이즈를 지정하려했지만 IOS14에서는 안된다. ㅜㅜ

.presentationDetents([.medium]) 기능이 있지만 IOS16 부터 지원한다능.....

 

SheetView 를 위/아래로 조절하는 SheetView임..

 

[DynamicSheetView.swift]

import SwiftUI

struct BlurView: UIViewRepresentable {
    
    var style: UIBlurEffect.Style
    
    func makeUIView(context: Context) -> UIVisualEffectView {
        let view = UIVisualEffectView(effect: UIBlurEffect(style: style))
        return view
    }
    
    func updateUIView(_ uiView: UIVisualEffectView, context: Context) {
        
    }
}

struct CustomCorner: Shape {
    
    var corners: UIRectCorner
    var radius: CGFloat
    
    func path(in rect: CGRect) -> Path {
        let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        
        return Path(path.cgPath)
    }
}

struct DynamicSheetView<Content: View>: View {
    @Binding var isOpen: Bool
    
    // Gesture Preperties...
    @State var offset: CGFloat
    @State var lastOffset: CGFloat
    let content: Content
    @GestureState var gestureOffset: CGFloat = 0
    
    init(isOpen: Binding<Bool>, offset: CGFloat, @ViewBuilder content: () -> Content) {
        self._isOpen = isOpen
        self.content = content()
        self.offset = -offset
        self.lastOffset = -offset
    }
    
    var body: some View {
            
            GeometryReader { proxy  in
                let height = proxy.frame(in: .global).height
                
                ZStack {
                    BlurView(style: .systemThinMaterialDark)
                        .clipShape(CustomCorner(corners: [.topLeft, .topRight], radius: 30))
                    
                    VStack {
                        // Top Drag Bar
                        Capsule()
                            .fill(Color.white)
                            .frame(width: 60, height: 4)
                            .padding(.top)
                        
                        // Content Area
                        self.content
                    }
                    .padding(.horizontal)
                    .frame(maxHeight: .infinity, alignment: .top)
                }
                .offset(y: height - 100)
                // 위로 과도하게 드래그 할 경우 하단이 들리는 현상을 막기위함.
                .offset(y: -offset > 0 ? -offset <= (height - 100) ? offset : -(height - 100) : 0)
//                .offset(y: -offset > 0 ? offset : 0)
                .gesture(
                    DragGesture().updating($gestureOffset, body: { value, out, _ in
                        out = value.translation.height
                        onChange()
                    }).onEnded({ value in
                        let maxHeight = height - 100
                        withAnimation {
                            // Logic COnditions For Moving States...
                            // Up down or mid
                            if -offset > 100 && -offset < maxHeight / 2 {
                                //Mid...
                                offset = -(maxHeight / 2)
                            }
                            else if -offset > maxHeight / 2 {
                                //Top
                                offset = -(maxHeight-20)
                            }
                            else {
                                //Bottom
                                //Close
                                self.isOpen = value.translation.height < 0
                            }
                        }
                        // Storing Last Offset..
                        // So that the gesture can contiue from the last position...
                        lastOffset = offset
                    })
                )
            .ignoresSafeArea(.all, edges: .bottom)
        }
    }
    
    func onChange() {
        DispatchQueue.main.async {
            self.offset = gestureOffset + lastOffset
        }
    }
}

 

[사용방법]

import SwiftUI

// MARK: View 백그라운드 투명화
struct ClearViewBackground: UIViewRepresentable {
    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        DispatchQueue.main.async {
            view.superview?.superview?.backgroundColor = .clear
        }
        return view
    }

    func updateUIView(_ uiView: UIView, context: Context) {}
}

struct ContentView: View {
    
    @Binding var showingTagSheet: Bool
    
    var body: some View {
        
        GeometryReader { geometry in
            // 전체 배경 블랙 투명도
            Color(red: 0.0, green: 0.0, blue: 0.0, opacity: 0.5)
           
            // DynamicSheetView (default : 50%만 표시)
            DynamicSheetView(isOpen: self.$showingTagSheet,
                             offset: ((geometry.size.height - 100) / 2)) {
                VStack {
                    Button {
                        
                    } label: {
                        Text("Button 1")
                    }
                    
                    Button {
                        
                    } label: {
                        Text("Button 2")
                    }
                }
            }
        }
        .background(ClearViewBackground())    // fullScreenCover 투명
    }
}

+ Recent posts