SwiftUI Draggable Bottom Sheet

Overview

Class Custom Corner Radius

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)
    }
    
}

BlurView(style: .systemThinMaterialDark)
      .clipShape(CustomCorner(corners: [.topLeft, .topRight], radius: 30))

Advanced BlurView

AnyView in let height = proxy.frame(in: .global).height print("geometry height: \(height)") print("screen height: \(UIScreen.main.bounds.height)") return AnyView( ZStack { // MARK: - BOTTOM SHEET BACKGROUND BlurView(style: .systemThinMaterialDark) .clipShape(CustomCorner(corners: [.topLeft, .topRight], radius: 30)) VStack { // MARK: - TOP DRAG INDICATOR Capsule() .fill(.white) .frame(width: 60, height: 4) .padding(.top) // MARK: - CUSTOM CONTENT // Content Spacer() } //: VSTACK } //: ZSTACK .offset(y: height - 100) .offset(y: -offset > 0 ? -offset <= (height - 100) ? offset : -(height - 100) : 0) .gesture(DragGesture().updating($gestureOffset, body: { value, out, _ in out = value.translation.height onChange() }).onEnded({ value in let maxHeight = height - 100 withAnimation { // offset = 0 // Logic Conditions For Moving States.... // Up down or mid... if -offset > 100 && -offset < maxHeight / 2 { // Mid... offset = -(maxHeight / 3) } else if -offset > maxHeight / 2 { offset = -maxHeight } else { offset = 0 } } // Storing Last Offset... // So that the gesture can contine from the last position.... lastOffset = offset })) ) } .ignoresSafeArea(.all, edges: .bottom) } } func onChange() { DispatchQueue.main.async { self.offset = gestureOffset + lastOffset } } // Blur Radius for BG... func getBlurRadius() -> CGFloat { let progress = -offset / (UIScreen.main.bounds.height - 100) return progress * 30 } } ">
struct BlurView: UIViewRepresentable {
    
    var style: UIBlurEffect.Style
    
    func makeUIView(context: Context) -> some UIVisualEffectView {
        let view = UIVisualEffectView(effect: UIBlurEffect(style: style))
        return view
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) {
        
    }
    
}

struct Home: View {
    
    @State var searchText = ""
    
    // Gesture Properties...
    @State var offset: CGFloat = 0
    @State var lastOffset: CGFloat = 0
    @GestureState var gestureOffset: CGFloat = 0
    
    var body: some View {
        ZStack {
            
            // MARK: - BACKGROUND
            // For getting Frame For Image
            GeometryReader { proxy in
                
                let frame = proxy.frame(in: .global)
                //                print("first geometry height: \(frame.height)")
                
                Image("image1")
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: frame.width, height:frame.height)
            }
            .blur(radius: getBlurRadius())
            .ignoresSafeArea()
            
            // Bottom Sheet...
            
            // MARK: - BOTTOM SHEET
            // For Getting Height For Drag Gesture
            GeometryReader { proxy -> AnyView in
                let height = proxy.frame(in: .global).height
                print("geometry height: \(height)")
                print("screen height: \(UIScreen.main.bounds.height)")
                return AnyView(
                    ZStack {
                        
                        // MARK: - BOTTOM SHEET BACKGROUND
                        BlurView(style: .systemThinMaterialDark)
                            .clipShape(CustomCorner(corners: [.topLeft, .topRight], radius: 30))
                        
                        VStack {
                            
                            // MARK: - TOP DRAG INDICATOR
                            Capsule()
                                .fill(.white)
                                .frame(width: 60, height: 4)
                                .padding(.top)
                            
                            // MARK: - CUSTOM CONTENT
                            // Content

                                                        Spacer()
            
                        } //: VSTACK
                    } //: ZSTACK
                        .offset(y: height - 100)
                        .offset(y: -offset > 0 ? -offset <= (height - 100) ? offset : -(height - 100) : 0)
                        .gesture(DragGesture().updating($gestureOffset, body: { value, out, _ in
                            out = value.translation.height
                            onChange()
                        }).onEnded({ value in
                            let maxHeight = height - 100
                            withAnimation {
                                // offset = 0
                                
                                // Logic Conditions For Moving States....
                                // Up down or mid...
                                if -offset > 100 && -offset < maxHeight / 2 {
                                    // Mid...
                                    offset = -(maxHeight / 3)
                                } else if -offset > maxHeight / 2 {
                                    offset = -maxHeight
                                } else {
                                    offset = 0
                                }
                            }
                            
                            // Storing Last Offset...
                            // So that the gesture can contine from the last position....
                            lastOffset = offset
                            
                        }))
                    
                    
                )
            }
            .ignoresSafeArea(.all, edges: .bottom)
            
        }
    }
    
    func onChange() {
        DispatchQueue.main.async {
            self.offset = gestureOffset + lastOffset
        }
    }
    
    // Blur Radius for BG...
    func getBlurRadius() -> CGFloat {
        let progress = -offset / (UIScreen.main.bounds.height - 100)
        return progress * 30
    }
}

Draggable BottomSheet

AnyView in let height = proxy.frame(in: .global).height print("geometry height: \(height)") print("screen height: \(UIScreen.main.bounds.height)") return AnyView( ZStack { // MARK: - BOTTOM SHEET BACKGROUND BlurView(style: .systemThinMaterialDark) .clipShape(CustomCorner(corners: [.topLeft, .topRight], radius: 30)) VStack { // MARK: - TOP DRAG INDICATOR Capsule() .fill(.white) .frame(width: 60, height: 4) .padding(.top) // MARK: - CUSTOM CONTENT // Content Spacer() } //: VSTACK } //: ZSTACK .offset(y: height - 100) .offset(y: -offset > 0 ? -offset <= (height - 100) ? offset : -(height - 100) : 0) .gesture(DragGesture().updating($gestureOffset, body: { value, out, _ in out = value.translation.height print("offset: \(offset)") onChange() }).onEnded({ value in let maxHeight = height - 100 withAnimation { // offset = 0 // Logic Conditions For Moving States.... // Up down or mid... if -offset > 100 && -offset < maxHeight / 2 { // Mid... offset = -(maxHeight / 3) } else if -offset > maxHeight / 2 { offset = -maxHeight } else { offset = 0 } } // Storing Last Offset... // So that the gesture can contine from the last position.... lastOffset = offset })) ) } .ignoresSafeArea(.all, edges: .bottom) } } func onChange() { DispatchQueue.main.async { self.offset = gestureOffset + lastOffset } } // Blur Radius for BG... func getBlurRadius() -> CGFloat { let progress = -offset / (UIScreen.main.bounds.height - 100) return progress * 30 } } 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 BlurView: UIViewRepresentable { var style: UIBlurEffect.Style func makeUIView(context: Context) -> some UIVisualEffectView { let view = UIVisualEffectView(effect: UIBlurEffect(style: style)) return view } func updateUIView(_ uiView: UIViewType, context: Context) { } } ">
//
//  ContentView.swift
//  BottomSheetDrawer
//
//  Created by paige on 2021/11/07.
//

import SwiftUI

struct ContentView: View {
    var body: some View {
        Home()
    }
}

struct Home: View {
    
    @State var searchText = ""
    
    // Gesture Properties...
    @State var offset: CGFloat = 0
    @State var lastOffset: CGFloat = 0
    @GestureState var gestureOffset: CGFloat = 0
    
    var body: some View {
        ZStack {
            
            // MARK: - BACKGROUND
            // For getting Frame For Image
            GeometryReader { proxy in
                
                let frame = proxy.frame(in: .global)
                
                Image("image1")
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: frame.width, height:frame.height)
            }
            .blur(radius: getBlurRadius())
            .ignoresSafeArea()
            
            // Bottom Sheet...
            
            // MARK: - BOTTOM SHEET
            // For Getting Height For Drag Gesture
            GeometryReader { proxy -> AnyView in
                let height = proxy.frame(in: .global).height
                print("geometry height: \(height)")
                print("screen height: \(UIScreen.main.bounds.height)")
                return AnyView(
                    ZStack {
                        
                        // MARK: - BOTTOM SHEET BACKGROUND
                        BlurView(style: .systemThinMaterialDark)
                            .clipShape(CustomCorner(corners: [.topLeft, .topRight], radius: 30))
                        
                        VStack {
                            
                            // MARK: - TOP DRAG INDICATOR
                            Capsule()
                                .fill(.white)
                                .frame(width: 60, height: 4)
                                .padding(.top)
                            
                            // MARK: - CUSTOM CONTENT
                            // Content
            
                            
                            Spacer()
                        } //: VSTACK
                    } //: ZSTACK
                        .offset(y: height - 100)
                        .offset(y: -offset > 0 ? -offset <= (height - 100) ? offset : -(height - 100) : 0)
                        .gesture(DragGesture().updating($gestureOffset, body: { value, out, _ in
                            out = value.translation.height
                            print("offset: \(offset)")
                            onChange()
                        }).onEnded({ value in
                            let maxHeight = height - 100
                            withAnimation {
                                // offset = 0
                                
                                // Logic Conditions For Moving States....
                                // Up down or mid...
                                if -offset > 100 && -offset < maxHeight / 2 {
                                    // Mid...
                                    offset = -(maxHeight / 3)
                                } else if -offset > maxHeight / 2 {
                                    offset = -maxHeight
                                } else {
                                    offset = 0
                                }
                            }
                            
                            // Storing Last Offset...
                            // So that the gesture can contine from the last position....
                            lastOffset = offset
                            
                        }))
                    
                    
                )
            }
            .ignoresSafeArea(.all, edges: .bottom)
            
        }
    }
    
    func onChange() {
        DispatchQueue.main.async {
            self.offset = gestureOffset + lastOffset
        }
    }
    
    // Blur Radius for BG...
    func getBlurRadius() -> CGFloat {
        let progress = -offset / (UIScreen.main.bounds.height - 100)
        return progress * 30
    }
}

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 BlurView: UIViewRepresentable {
    
    var style: UIBlurEffect.Style
    
    func makeUIView(context: Context) -> some UIVisualEffectView {
        let view = UIVisualEffectView(effect: UIBlurEffect(style: style))
        return view
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) {
        
    }
    
}

Entire Code

AnyView in let height = proxy.frame(in: .global).height print("geometry height: \(height)") print("screen height: \(UIScreen.main.bounds.height)") return AnyView( ZStack { // MARK: - BOTTOM SHEET BACKGROUND BlurView(style: .systemThinMaterialDark) .clipShape(CustomCorner(corners: [.topLeft, .topRight], radius: 30)) VStack { // MARK: - TOP DRAG INDICATOR Capsule() .fill(.white) .frame(width: 60, height: 4) .padding(.top) // MARK: - CUSTOM CONTENT // Content TextField("Search", text: $searchText) .padding(.vertical, 10) .padding(.horizontal) // .background(BlurView(style: .dark)) .cornerRadius(20) .colorScheme(.dark) .padding(.top, 10) VStack { HStack { Text("Favorites") .fontWeight(.bold) .foregroundColor(.white) Spacer() Button { } label: { Text("See All") .fontWeight(.bold) .foregroundColor(.gray) } } .padding(.top, 20) Divider() .background(.white) } ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 15) { VStack(spacing: 8) { Button { } label: { Image(systemName: "house.fill") .font(.title) .frame(width: 50, height: 50) .background(BlurView(style: .dark)) .clipShape(Circle()) } Text("Home") .foregroundColor(.white) } VStack(spacing: 8) { Button { } label: { Image(systemName: "briefcase.fill") .font(.title) .frame(width: 50, height: 50) .background(BlurView(style: .dark)) .clipShape(Circle()) } Text("Work") .foregroundColor(.white) } VStack(spacing: 8) { Button { } label: { Image(systemName: "house.fill") .font(.title) .frame(width: 50, height: 50) .background(BlurView(style: .dark)) .clipShape(Circle()) } Text("Add") .foregroundColor(.white) } } } //: ScrollView .padding(.top) HStack { Text("Editor's Pick") .fontWeight(.bold) .foregroundColor(.white) Spacer() Button { } label: { Text("See All") .fontWeight(.bold) .foregroundColor(.gray) } } .padding(.top, 25) Divider() .background(.white) ScrollView(.vertical, showsIndicators: false) { ForEach(1...5, id: \.self) { index in Image("image\(index)") .resizable() .aspectRatio(contentMode: .fill) .frame(width: UIScreen.main.bounds.width - 30, height: 250) .cornerRadius(15) .padding(.top) } } Spacer() } //: VSTACK .padding(16) } //: ZSTACK .offset(y: height - 100) .offset(y: -offset > 0 ? -offset <= (height - 100) ? offset : -(height - 100) : 0) .gesture(DragGesture().updating($gestureOffset, body: { value, out, _ in out = value.translation.height onChange() }).onEnded({ value in let maxHeight = height - 100 withAnimation { // offset = 0 // Logic Conditions For Moving States.... // Up down or mid... if -offset > 100 && -offset < maxHeight / 2 { // Mid... offset = -(maxHeight / 3) } else if -offset > maxHeight / 2 { offset = -maxHeight } else { offset = 0 } } // Storing Last Offset... // So that the gesture can contine from the last position.... lastOffset = offset })) ) } .ignoresSafeArea(.all, edges: .bottom) } } func onChange() { DispatchQueue.main.async { self.offset = gestureOffset + lastOffset } } // Blur Radius for BG... func getBlurRadius() -> CGFloat { let progress = -offset / (UIScreen.main.bounds.height - 100) return progress * 30 } } struct BlurView: UIViewRepresentable { var style: UIBlurEffect.Style func makeUIView(context: Context) -> some UIVisualEffectView { let view = UIVisualEffectView(effect: UIBlurEffect(style: style)) return view } func updateUIView(_ uiView: UIViewType, 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) } } ">
//
//  ContentView.swift
//  BottomSheetDrawer
//
//  Created by paige on 2021/11/07.
//

import SwiftUI

struct ContentView: View {
    var body: some View {
        Home()
    }
}

struct Home: View {
    
    @State var searchText = ""
    
    // Gesture Properties...
    @State var offset: CGFloat = 0
    @State var lastOffset: CGFloat = 0
    @GestureState var gestureOffset: CGFloat = 0
    
    var body: some View {
        ZStack {
            
            // MARK: - BACKGROUND
            // For getting Frame For Image
            GeometryReader { proxy in
                
                let frame = proxy.frame(in: .global)
                //                print("first geometry height: \(frame.height)")
                
                Image("image1")
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: frame.width, height:frame.height)
            }
            .blur(radius: getBlurRadius())
            .ignoresSafeArea()
            
            // Bottom Sheet...
            
            // MARK: - BOTTOM SHEET
            // For Getting Height For Drag Gesture
            GeometryReader { proxy -> AnyView in
                let height = proxy.frame(in: .global).height
                print("geometry height: \(height)")
                print("screen height: \(UIScreen.main.bounds.height)")
                return AnyView(
                    ZStack {
                        
                        // MARK: - BOTTOM SHEET BACKGROUND
                        BlurView(style: .systemThinMaterialDark)
                            .clipShape(CustomCorner(corners: [.topLeft, .topRight], radius: 30))
                        
                        VStack {
                            
                            // MARK: - TOP DRAG INDICATOR
                            Capsule()
                                .fill(.white)
                                .frame(width: 60, height: 4)
                                .padding(.top)
                            
                            // MARK: - CUSTOM CONTENT
                            // Content
                            TextField("Search", text: $searchText)
                                .padding(.vertical, 10)
                                .padding(.horizontal)
                            //                                .background(BlurView(style: .dark))
                                .cornerRadius(20)
                                .colorScheme(.dark)
                                .padding(.top, 10)
                            
                            
                            VStack {
                                HStack {
                                    Text("Favorites")
                                        .fontWeight(.bold)
                                        .foregroundColor(.white)
                                    
                                    Spacer()
                                    
                                    Button {
                                        
                                    } label: {
                                        Text("See All")
                                            .fontWeight(.bold)
                                            .foregroundColor(.gray)
                                    }
                                    
                                }
                                .padding(.top, 20)
                                
                                Divider()
                                    .background(.white)
                            }
                            ScrollView(.horizontal, showsIndicators: false) {
                                HStack(spacing: 15) {
                                    VStack(spacing: 8) {
                                        Button {
                                            
                                        } label: {
                                            Image(systemName: "house.fill")
                                                .font(.title)
                                                .frame(width: 50, height: 50)
                                                .background(BlurView(style: .dark))
                                                .clipShape(Circle())
                                        }
                                        Text("Home")
                                            .foregroundColor(.white)
                                    }
                                    VStack(spacing: 8) {
                                        Button {
                                            
                                        } label: {
                                            Image(systemName: "briefcase.fill")
                                                .font(.title)
                                                .frame(width: 50, height: 50)
                                                .background(BlurView(style: .dark))
                                                .clipShape(Circle())
                                        }
                                        Text("Work")
                                            .foregroundColor(.white)
                                    }
                                    VStack(spacing: 8) {
                                        Button {
                                            
                                        } label: {
                                            Image(systemName: "house.fill")
                                                .font(.title)
                                                .frame(width: 50, height: 50)
                                                .background(BlurView(style: .dark))
                                                .clipShape(Circle())
                                        }
                                        Text("Add")
                                            .foregroundColor(.white)
                                    }
                                }
                            } //: ScrollView
                            .padding(.top)
                            
                            HStack {
                                Text("Editor's Pick")
                                    .fontWeight(.bold)
                                    .foregroundColor(.white)
                                Spacer()
                                Button {
                                    
                                } label: {
                                    Text("See All")
                                        .fontWeight(.bold)
                                        .foregroundColor(.gray)
                                }
                            }
                            .padding(.top, 25)
                            
                            Divider()
                                .background(.white)
                            
                            ScrollView(.vertical, showsIndicators: false) {
                                ForEach(1...5, id: \.self) { index in
                                    Image("image\(index)")
                                        .resizable()
                                        .aspectRatio(contentMode: .fill)
                                        .frame(width: UIScreen.main.bounds.width - 30, height: 250)
                                        .cornerRadius(15)
                                        .padding(.top)
                                }
                            }
                            
                            Spacer()
                            
                        } //: VSTACK
                        .padding(16)
                        
                    } //: ZSTACK
                        .offset(y: height - 100)
                        .offset(y: -offset > 0 ? -offset <= (height - 100) ? offset : -(height - 100) : 0)
                        .gesture(DragGesture().updating($gestureOffset, body: { value, out, _ in
                            out = value.translation.height
                            onChange()
                        }).onEnded({ value in
                            let maxHeight = height - 100
                            withAnimation {
                                // offset = 0
                                
                                // Logic Conditions For Moving States....
                                // Up down or mid...
                                if -offset > 100 && -offset < maxHeight / 2 {
                                    // Mid...
                                    offset = -(maxHeight / 3)
                                } else if -offset > maxHeight / 2 {
                                    offset = -maxHeight
                                } else {
                                    offset = 0
                                }
                            }
                            
                            // Storing Last Offset...
                            // So that the gesture can contine from the last position....
                            lastOffset = offset
                            
                        }))
                    
                    
                )
            }
            .ignoresSafeArea(.all, edges: .bottom)
            
        }
    }
    
    func onChange() {
        DispatchQueue.main.async {
            self.offset = gestureOffset + lastOffset
        }
    }
    
    // Blur Radius for BG...
    func getBlurRadius() -> CGFloat {
        let progress = -offset / (UIScreen.main.bounds.height - 100)
        return progress * 30
    }
}

struct BlurView: UIViewRepresentable {
    
    var style: UIBlurEffect.Style
    
    func makeUIView(context: Context) -> some UIVisualEffectView {
        let view = UIVisualEffectView(effect: UIBlurEffect(style: style))
        return view
    }
    
    func updateUIView(_ uiView: UIViewType, 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)
    }
    
}
You might also like...
TTGSnackbar shows simple message and action button on the bottom or top of the screen with multi kinds of animation, which is written in Swift3 and inspired by Snackbar in Android. It also support showing custom view, icon image or multi action button. A Swift library to provide a bouncy action sheet
A Swift library to provide a bouncy action sheet

Hokusai is a Swift library that provides a bouncy action sheet. It will give the users a fancy experience without taking pains coding the cool animati

Fully customizable and extensible action sheet controller written in Swift
Fully customizable and extensible action sheet controller written in Swift

XLActionController By XMARTLABS. XLActionController is an extensible library to quickly create any custom action sheet controller. Examples The action

Dice roller, character sheet/ creator, and monster/item info app on the iphone12

DnD-LordDogMaw This file will be the start of a side project in the hopes of creating an iphone12 app for Dungeons and Dragons! This app will have 3 m

Action sheet allows including your custom views and buttons.
Action sheet allows including your custom views and buttons.

CustomizableActionSheet Action sheet allows including your custom views and buttons. Installation CocoaPods Edit your Podfile: pod 'CustomizableAction

A Google like action sheet for iOS written in Swift.
A Google like action sheet for iOS written in Swift.

MaterialActionSheetController Lightweight and totally customizable. Create and present it the way you do with UIAlertController. Screenshots Demo | De

Present a sheet ViewController easily and control ViewController height with pangesture
Present a sheet ViewController easily and control ViewController height with pangesture

PanControllerHeight is designed to present a sheet ViewController easily and control ViewController height with pangesture.

Share-sheet-example - A sample project that reproduces an issue with Share Sheets

Hello, DTS! This project demonstrates the issue I'm having with the Share Sheet.

SplitSheet - A lightweight, fully interactive split-screen sheet.
SplitSheet - A lightweight, fully interactive split-screen sheet.

SplitSheet A lightweight, fully interactive split-screen sheet. Powered by UIScrollView for super-smooth gestures. Show/hide either programmatically o

Owner
paigeshin
paigeshin
An iOS library for SwiftUI to create draggable sheet experiences similar to iOS applications like Maps and Stocks.

An iOS library for SwiftUI to create draggable sheet experiences similar to iOS applications like Maps and Stocks.

Wouter 63 Jan 5, 2023
Customizable Dynamic Bottom Sheet Library for iOS

DynamicBottomSheet Powerd by Witi Corp., Seoul, South Korea. Fully Customizable Dynamic Bottom Sheet Library for iOS. This library doesn't support sto

Witi Official 10 May 7, 2022
Bottom Sheet component is widely used in Joom application

Bottom Sheet Bottom Sheet component is widely used in Joom application Installation Swift Package Manager Swift Package Manager is a tool for managing

Joom 101 Dec 29, 2022
BottomSheetDemo - Bottom sheet modal view controller with swift

当我们想弹出一个预览视图,bottom sheet modal view controller 非常实用。在 iOS 中,长按拖拽手势可以让 controlle

null 8 Oct 29, 2022
DGBottomSheet - The lightest swift bottom sheet library

DGBottomSheet Requirements Installation Usage DGBottomSheet The lightest swift b

donggyu 9 Aug 6, 2022
Custom-action-sheet- - Custom action sheet with swift

Custom-action-sheet- Usage let alertController: UIAlertControllerDimmed = UIAler

Girisankar G 0 Jan 19, 2022
A customizable framework to create draggable views

CFNotify CFNotify is written in Swift. Using UIKit Dynamics as animator. It can make ANY UIView object draggable and throwable. This library mainly us

Johnny Tsoi 491 Nov 20, 2022
A customizable framework to create draggable views

CFNotify CFNotify is written in Swift. Using UIKit Dynamics as animator. It can make ANY UIView object draggable and throwable. This library mainly us

Johnny Tsoi 491 Nov 20, 2022
BottomSheet makes it easy to add custom bottom sheets to your SwiftUI apps.

BottomSheet About BottomSheet BottomSheet makes it easy to add custom bottom sheets to your SwiftUI apps. The result can look like this...or completel

Daniel Saidi 174 Jan 2, 2023
A SwiftUI Partial Sheet fully customizable with dynamic height

A SwiftUI Partial Sheet fully customizable with dynamic height

Andrea Miotto 1.4k Jan 5, 2023