Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI - Half modal?

I'm trying to recreate a Modal just like Safari in iOS13 in SwiftUI:

Here's what it looks like:

enter image description here

Does anyone know if this is possible in SwiftUI? I want to show a small half modal, with the option to drag to fullscreen, just like the sharing sheet.

Any advice is much appreciated!

like image 677
ryannn Avatar asked Jun 21 '19 09:06

ryannn


People also ask

How do I create a bottom sheet in SwiftUI?

Starting from iOS 16, the SwiftUI framework comes with a new modifier called presentationDetents for presenting a resizable bottom sheet. Text("This is the expandable bottom sheet." You specify a set of detents in the presentationDetents modifier. As shown above, the bottom sheet supports both medium and large size.

How do I use sheets in SwiftUI?

To use a sheet, give it something to show (some text, an image, a custom view, etc), add a Boolean that defines whether the detail view should be showing, then attach it to your main view as a modal sheet. Important: If you're targeting iOS 14 or below, you should use @Environment(\.


2 Answers

I've written a Swift Package that includes a custom modifier that allows you to use the half modal sheet.

Here is the link: https://github.com/AndreaMiotto/PartialSheet

Feel free to use it or to contribute

enter image description here

like image 141
Andrea Miotto Avatar answered Oct 04 '22 00:10

Andrea Miotto


You can make your own and place it inside of a zstack: https://www.mozzafiller.com/posts/swiftui-slide-over-card-like-maps-stocks

struct SlideOverCard<Content: View> : View {     @GestureState private var dragState = DragState.inactive     @State var position = CardPosition.top      var content: () -> Content     var body: some View {         let drag = DragGesture()             .updating($dragState) { drag, state, transaction in                 state = .dragging(translation: drag.translation)             }             .onEnded(onDragEnded)          return Group {             Handle()             self.content()         }         .frame(height: UIScreen.main.bounds.height)         .background(Color.white)         .cornerRadius(10.0)         .shadow(color: Color(.sRGBLinear, white: 0, opacity: 0.13), radius: 10.0)         .offset(y: self.position.rawValue + self.dragState.translation.height)         .animation(self.dragState.isDragging ? nil : .spring(stiffness: 300.0, damping: 30.0, initialVelocity: 10.0))         .gesture(drag)     }      private func onDragEnded(drag: DragGesture.Value) {         let verticalDirection = drag.predictedEndLocation.y - drag.location.y         let cardTopEdgeLocation = self.position.rawValue + drag.translation.height         let positionAbove: CardPosition         let positionBelow: CardPosition         let closestPosition: CardPosition          if cardTopEdgeLocation <= CardPosition.middle.rawValue {             positionAbove = .top             positionBelow = .middle         } else {             positionAbove = .middle             positionBelow = .bottom         }          if (cardTopEdgeLocation - positionAbove.rawValue) < (positionBelow.rawValue - cardTopEdgeLocation) {             closestPosition = positionAbove         } else {             closestPosition = positionBelow         }          if verticalDirection > 0 {             self.position = positionBelow         } else if verticalDirection < 0 {             self.position = positionAbove         } else {             self.position = closestPosition         }     } }  enum CardPosition: CGFloat {     case top = 100     case middle = 500     case bottom = 850 }  enum DragState {     case inactive     case dragging(translation: CGSize)      var translation: CGSize {         switch self {         case .inactive:             return .zero         case .dragging(let translation):             return translation         }     }      var isDragging: Bool {         switch self {         case .inactive:             return false         case .dragging:             return true         }     } } 
like image 28
Andre Carrera Avatar answered Oct 04 '22 00:10

Andre Carrera