【问题标题】:SwiftUI - Half modal?SwiftUI - 半模态?
【发布时间】:2019-11-04 03:30:43
【问题描述】:

我正在尝试在 SwiftUI 中重新创建类似于 iOS13 中的 Safari 的 Modal:

它是这样的:

有谁知道这在 SwiftUI 中是否可行?我想显示一个小的半模态,可以选择拖动到全屏,就像共享表一样。

非常感谢任何建议!

【问题讨论】:

  • 不就是overlay吗?
  • 我不太确定,我认为它可能是 Modal 或 popover 上的一个选项,但目前文档非常稀少
  • 我发现 SwiftUI 中的模式在选项上太少了。例如,我找不到将其演示文稿样式选择为“FormSheet”的方法。自 iOS 3.2 以来一直存在的非常基本的东西!我不得不使用 UIHostingController 技巧。
  • 在纯 SwiftUI 中制作。享受! github.com/cyrilzakka/SwiftUIModal。启用全屏和半模态功能。
  • @cyril 你是否允许任何人使用你的代码?

标签: swift xcode swiftui ios13 uiactivityviewcontroller


【解决方案1】:

Beta 2 Beta 3 开始,您不能将模态 View 显示为 .fullScreen。它显示为.automatic -> .pageSheet。但是,即使解决了这个问题,我也非常怀疑他们是否会免费为您提供拖动功能。它已经包含在文档中了。

您现在可以使用this answer 全屏显示。 Gist here.

然后,在演示之后,这是一个快速而肮脏的示例,说明如何重新创建该交互。

    @State var drag: CGFloat = 0.0

    var body: some View {
        ZStack(alignment: .bottom) {
            Spacer() // Use the full space
            Color.red
                .frame(maxHeight: 300 + self.drag) // Whatever minimum height you want, plus the drag offset
                .gesture(
                    DragGesture(coordinateSpace: .global) // if you use .local the frame will jump around
                        .onChanged({ (value) in
                            self.drag = max(0, -value.translation.height)
                        })
                )
        }
    }

【讨论】:

    【解决方案2】:

    您可以自己制作并将其放在 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
            }
        }
    }
    

    【讨论】:

    • 句柄未定义
    • 是的,如果你点击链接
    【解决方案3】:

    我编写了一个 Swift 包,其中包含一个自定义修饰符,允许您使用半模态表。

    这里是链接:https://github.com/AndreaMiotto/PartialSheet

    随意使用或贡献

    【讨论】:

    • 为什么不添加 .podspec?
    • 我不明白为什么这涉及修改 SceneDelegate... 我的应用是带有 SwiftUI 插件的 UIKit。有没有一种简单的方法可以将此组件与特定的 SwiftUI 视图一起使用?
    • 简单、易于使用且运行良好。 :+1:
    • @orkenstein 为什么你需要一个 .podspec 的 swift 包?这个想法不是不需要pod install命令
    【解决方案4】:

    这是我幼稚的底部表格,可缩放到其内容。无需拖动,但如果需要添加应该相对容易:)

    struct BottomSheet<SheetContent: View>: ViewModifier {
    @Binding var isPresented: Bool
    let sheetContent: () -> SheetContent
    
    func body(content: Content) -> some View {
        ZStack {
            content
    
            if isPresented {
                VStack {
                    Spacer()
    
                    VStack {
                        HStack {
                            Spacer()
                            Button(action: {
                                withAnimation(.easeInOut) {
                                    self.isPresented = false
                                }
                            }) {
                                Text("done")
                                    .padding(.top, 5)
                            }
                        }
    
                        sheetContent()
                    }
                    .padding()
                }
                .zIndex(.infinity)
                .transition(.move(edge: .bottom))
                .edgesIgnoringSafeArea(.bottom)
            }
        }
    }
    
    extension View {
        func customBottomSheet<SheetContent: View>(
            isPresented: Binding<Bool>,
            sheetContent: @escaping () -> SheetContent
        ) -> some View {
            self.modifier(BottomSheet(isPresented: isPresented, sheetContent: sheetContent))
        }
    }
    

    并像下面这样使用:

    .customBottomSheet(isPresented: $isPickerPresented) {
                    DatePicker(
                        "time",
                        selection: self.$time,
                        displayedComponents: .hourAndMinute
                    )
                    .labelsHidden()
            }
    

    【讨论】:

      【解决方案5】:

      Andre Carrera 的回答很棒,您可以随意使用他提供的本指南:https://www.mozzafiller.com/posts/swiftui-slide-over-card-like-maps-stocks

      我已经修改了 SlideOverCard 结构,因此它使用实际设备高度来测量卡片应该停止的位置(您可以使用 bounds.height 来调整以适应您的需要):

      struct SlideOverCard<Content: View>: View {
      
          var bounds = UIScreen.main.bounds
          @GestureState private var dragState = DragState.inactive
          @State var position = UIScreen.main.bounds.height/2
      
          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 + self.dragState.translation.height)
              .animation(self.dragState.isDragging ? nil : .interpolatingSpring(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 + drag.translation.height
              let positionAbove: CGFloat
              let positionBelow: CGFloat
              let closestPosition: CGFloat
      
              if cardTopEdgeLocation <= bounds.height/2 {
                  positionAbove = bounds.height/7
                  positionBelow = bounds.height/2
              } else {
                  positionAbove = bounds.height/2
                  positionBelow = bounds.height - (bounds.height/9)
              }
      
              if (cardTopEdgeLocation - positionAbove) < (positionBelow - cardTopEdgeLocation) {
                  closestPosition = positionAbove
              } else {
                  closestPosition = positionBelow
              }
      
              if verticalDirection > 0 {
                  self.position = positionBelow
              } else if verticalDirection < 0 {
                  self.position = positionAbove
              } else {
                  self.position = closestPosition
              }
          }
      }
      
      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
              }
          }
      }
      

      【讨论】:

        【解决方案6】:

        我编写了一个 SwiftUI 包,其中包括自定义 iOS 13,如半模态及其按钮。

        GitHub 仓库:https://github.com/ViktorMaric/HalfModal

        【讨论】:

          【解决方案7】:

          我认为几乎每个在 SwiftUI 中编写任何东西的 iOS 开发人员都必须反对这一点。我当然做到了,但我认为这里的大多数答案要么太复杂,要么并没有真正提供我想要的。

          我在 GitHub 上写了一个非常简单的部分工作表,可作为 Swift 包提供 - HalfASheet

          它可能没有其他一些解决方案的花里胡哨,但它做了它需要做的事情。此外,编写自己的代码总是有助于了解正在发生的事情。

          注意 - 有几件事 - 首先,这是一个正在进行中的工作,请随时改进,等等。其次,我故意不做一个 .podspec,就好像你是为 SwiftUI 开发,你至少在 iOS 13 上,我认为 Swift 包要好得多......

          【讨论】:

          • 使用声明式时如何设置大小??
          • @Farhandika 所以这在很大程度上是一项正在进行的工作,几乎可以肯定,它需要进一步的工作才能使其成为一个强大的、真实的解决方案。随意浏览代码并查看是否可以改进它
          【解决方案8】:

          我试图做同样的事情,在 SwiftUI 中以本机方式显示共享表,而不必实现/导入组件。 我在https://jeevatamil.medium.com/how-to-create-share-sheet-uiactivityviewcontroller-in-swiftui-cef64b26f073找到了这个解决方案

          struct ShareSheetView: View {
              var body: some View {
                  Button(action: actionSheet) {
                      Image(systemName: "square.and.arrow.up")
                          .resizable()
                          .aspectRatio(contentMode: .fit)
                          .frame(width: 36, height: 36)
                  }
              }
              
              func actionSheet() {
                  guard let data = URL(string: "https://www.zoho.com") else { return }
                  let av = UIActivityViewController(activityItems: [data], applicationActivities: nil)
                  UIApplication.shared.windows.first?.rootViewController?.present(av, animated: true, completion: nil)
              }
          }
          

          【讨论】:

            【解决方案9】:

            在 Swift 5.5 iOS 15+ 和 Mac Catalyst 15+ 中有一个

            adaptiveSheetPresentationController 有一个新的解决方案

            https://developer.apple.com/documentation/uikit/uipopoverpresentationcontroller/3810055-adaptivesheetpresentationcontrol?changes=__4

            @available(iOS 15.0, *)
            struct CustomSheetParentView: View {
                @State private var isPresented = false
                
                var body: some View {
                    VStack{
                        Button("present sheet", action: {
                            isPresented.toggle()
                        }).adaptiveSheet(isPresented: $isPresented, detents: [.medium()], smallestUndimmedDetentIdentifier: .large){
                            Rectangle()
                                .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
                                .foregroundColor(.clear)
                                .border(Color.blue, width: 3)
                                .overlay(Text("Hello, World!").frame(maxWidth: .infinity, maxHeight: .infinity)
                                            .onTapGesture {
                                    isPresented.toggle()
                                }
                                )
                        }
                        
                    }
                }
            }
            @available(iOS 15.0, *)
            struct AdaptiveSheet<T: View>: ViewModifier {
                let sheetContent: T
                @Binding var isPresented: Bool
                let detents : [UISheetPresentationController.Detent]
                let smallestUndimmedDetentIdentifier: UISheetPresentationController.Detent.Identifier?
                let prefersScrollingExpandsWhenScrolledToEdge: Bool
                let prefersEdgeAttachedInCompactHeight: Bool
                
                init(isPresented: Binding<Bool>, detents : [UISheetPresentationController.Detent] = [.medium(), .large()], smallestUndimmedDetentIdentifier: UISheetPresentationController.Detent.Identifier? = .medium, prefersScrollingExpandsWhenScrolledToEdge: Bool = false, prefersEdgeAttachedInCompactHeight: Bool = true, @ViewBuilder content: @escaping () -> T) {
                    self.sheetContent = content()
                    self.detents = detents
                    self.smallestUndimmedDetentIdentifier = smallestUndimmedDetentIdentifier
                    self.prefersEdgeAttachedInCompactHeight = prefersEdgeAttachedInCompactHeight
                    self.prefersScrollingExpandsWhenScrolledToEdge = prefersScrollingExpandsWhenScrolledToEdge
                    self._isPresented = isPresented
                }
                func body(content: Content) -> some View {
                    ZStack{
                        content
                        CustomSheet_UI(isPresented: $isPresented, detents: detents, smallestUndimmedDetentIdentifier: smallestUndimmedDetentIdentifier, prefersScrollingExpandsWhenScrolledToEdge: prefersScrollingExpandsWhenScrolledToEdge, prefersEdgeAttachedInCompactHeight: prefersEdgeAttachedInCompactHeight, content: {sheetContent}).frame(width: 0, height: 0)
                    }
                }
            }
            @available(iOS 15.0, *)
            extension View {
                func adaptiveSheet<T: View>(isPresented: Binding<Bool>, detents : [UISheetPresentationController.Detent] = [.medium(), .large()], smallestUndimmedDetentIdentifier: UISheetPresentationController.Detent.Identifier? = .medium, prefersScrollingExpandsWhenScrolledToEdge: Bool = false, prefersEdgeAttachedInCompactHeight: Bool = true, @ViewBuilder content: @escaping () -> T)-> some View {
                    modifier(AdaptiveSheet(isPresented: isPresented, detents : detents, smallestUndimmedDetentIdentifier: smallestUndimmedDetentIdentifier, prefersScrollingExpandsWhenScrolledToEdge: prefersScrollingExpandsWhenScrolledToEdge, prefersEdgeAttachedInCompactHeight: prefersEdgeAttachedInCompactHeight, content: content))
                }
            }
            
            @available(iOS 15.0, *)
            struct CustomSheet_UI<Content: View>: UIViewControllerRepresentable {
                
                let content: Content
                @Binding var isPresented: Bool
                let detents : [UISheetPresentationController.Detent]
                let smallestUndimmedDetentIdentifier: UISheetPresentationController.Detent.Identifier?
                let prefersScrollingExpandsWhenScrolledToEdge: Bool
                let prefersEdgeAttachedInCompactHeight: Bool
                
                init(isPresented: Binding<Bool>, detents : [UISheetPresentationController.Detent] = [.medium(), .large()], smallestUndimmedDetentIdentifier: UISheetPresentationController.Detent.Identifier? = .medium, prefersScrollingExpandsWhenScrolledToEdge: Bool = false, prefersEdgeAttachedInCompactHeight: Bool = true, @ViewBuilder content: @escaping () -> Content) {
                    self.content = content()
                    self.detents = detents
                    self.smallestUndimmedDetentIdentifier = smallestUndimmedDetentIdentifier
                    self.prefersEdgeAttachedInCompactHeight = prefersEdgeAttachedInCompactHeight
                    self.prefersScrollingExpandsWhenScrolledToEdge = prefersScrollingExpandsWhenScrolledToEdge
                    self._isPresented = isPresented
                }
                func makeCoordinator() -> Coordinator {
                    Coordinator(self)
                }
                func makeUIViewController(context: Context) -> CustomSheetViewController<Content> {
                    let vc = CustomSheetViewController(coordinator: context.coordinator, detents : detents, smallestUndimmedDetentIdentifier: smallestUndimmedDetentIdentifier, prefersScrollingExpandsWhenScrolledToEdge:  prefersScrollingExpandsWhenScrolledToEdge, prefersEdgeAttachedInCompactHeight: prefersEdgeAttachedInCompactHeight, content: {content})
                    return vc
                }
                
                func updateUIViewController(_ uiViewController: CustomSheetViewController<Content>, context: Context) {
                    if isPresented{
                        uiViewController.presentModalView()
                    }else{
                        uiViewController.dismissModalView()
                    }
                }
                class Coordinator: NSObject, UIAdaptivePresentationControllerDelegate {
                    var parent: CustomSheet_UI
                    init(_ parent: CustomSheet_UI) {
                        self.parent = parent
                    }
                    //Adjust the variable when the user dismisses with a swipe
                    func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
                        if parent.isPresented{
                            parent.isPresented = false
                        }
                        
                    }
                    
                }
            }
            
            @available(iOS 15.0, *)
            class CustomSheetViewController<Content: View>: UIViewController {
                let content: Content
                let coordinator: CustomSheet_UI<Content>.Coordinator
                let detents : [UISheetPresentationController.Detent]
                let smallestUndimmedDetentIdentifier: UISheetPresentationController.Detent.Identifier?
                let prefersScrollingExpandsWhenScrolledToEdge: Bool
                let prefersEdgeAttachedInCompactHeight: Bool
                private var isLandscape: Bool = UIDevice.current.orientation.isLandscape
                init(coordinator: CustomSheet_UI<Content>.Coordinator, detents : [UISheetPresentationController.Detent] = [.medium(), .large()], smallestUndimmedDetentIdentifier: UISheetPresentationController.Detent.Identifier? = .medium, prefersScrollingExpandsWhenScrolledToEdge: Bool = false, prefersEdgeAttachedInCompactHeight: Bool = true, @ViewBuilder content: @escaping () -> Content) {
                    self.content = content()
                    self.coordinator = coordinator
                    self.detents = detents
                    self.smallestUndimmedDetentIdentifier = smallestUndimmedDetentIdentifier
                    self.prefersEdgeAttachedInCompactHeight = prefersEdgeAttachedInCompactHeight
                    self.prefersScrollingExpandsWhenScrolledToEdge = prefersScrollingExpandsWhenScrolledToEdge
                    super.init(nibName: nil, bundle: .main)
                }
                
                required init?(coder: NSCoder) {
                    fatalError("init(coder:) has not been implemented")
                }
                func dismissModalView(){
                    dismiss(animated: true, completion: nil)
                }
                func presentModalView(){
                    
                    let hostingController = UIHostingController(rootView: content)
                    
                    hostingController.modalPresentationStyle = .popover
                    hostingController.presentationController?.delegate = coordinator as UIAdaptivePresentationControllerDelegate
                    hostingController.modalTransitionStyle = .coverVertical
                    if let hostPopover = hostingController.popoverPresentationController {
                        hostPopover.sourceView = super.view
                        let sheet = hostPopover.adaptiveSheetPresentationController
                        //As of 13 Beta 4 if .medium() is the only detent in landscape error occurs
                        sheet.detents = (isLandscape ? [.large()] : detents)
                        sheet.largestUndimmedDetentIdentifier =
                        smallestUndimmedDetentIdentifier
                        sheet.prefersScrollingExpandsWhenScrolledToEdge =
                        prefersScrollingExpandsWhenScrolledToEdge
                        sheet.prefersEdgeAttachedInCompactHeight =
                        prefersEdgeAttachedInCompactHeight
                        sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true
                        
                    }
                    if presentedViewController == nil{
                        present(hostingController, animated: true, completion: nil)
                    }
                }
                /// To compensate for orientation as of 13 Beta 4 only [.large()] works for landscape
                override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
                    super.viewWillTransition(to: size, with: coordinator)
                    if UIDevice.current.orientation.isLandscape {
                        isLandscape = true
                        self.presentedViewController?.popoverPresentationController?.adaptiveSheetPresentationController.detents = [.large()]
                    } else {
                        isLandscape = false
                        self.presentedViewController?.popoverPresentationController?.adaptiveSheetPresentationController.detents = detents
                    }
                }
            }
            
            @available(iOS 15.0, *)
            struct CustomSheetView_Previews: PreviewProvider {
                static var previews: some View {
                    CustomSheetParentView()
                }
            }
            

            【讨论】:

            • 这是在横向崩溃并出现错误:“至少一个制动器必须处于活动状态。”在 iOS 15 beta 4 上。有人有解决方案吗?
            • 这个错误是当 CustomSheet_UI(isPresented: $isPresented, detents: [.medium()], ... 如果我给它 [.medium(), .large()] 那么它不会崩溃。这与您的代码无关,而是与框架有关,抱歉之前的错误调用。
            • @Sverrisson NP。我喜欢反馈。我添加了一个解决方法。现在,如果在横向,它将默认为.large()。我会提交一个错误。如果其他人也提交一份可能会有所帮助
            • 这很好,但有没有类似的解决方案适用于 iOS 15 以下?
            • 不那么动态。我看过旧的 UIKit 帖子是人们在 SO 中手动设置框架的。新代码是与 AdaptiveSheetPresentationController 相关的东西,其余的应该在 iOS 13 和 14 中运行良好。此外,您可以检查版本并只使用 13 和 14 的工作表,当然还有所有其他解决方案。跨度>
            【解决方案10】:

            至少在 iOS 14、Swift 5、Xcode 12.5 中,我只需将 UIActivityViewController 包装在另一个视图控制器中即可相当轻松地完成此操作。它不需要检查视图层次结构或使用任何 3rd 方库。唯一骇人听闻的部分是异步呈现视图控制器,这甚至可能不是必需的。有更多 SwiftUI 经验的人或许可以提供改进建议。

            import Foundation
            import SwiftUI
            import UIKit
            
            struct ActivityViewController: UIViewControllerRepresentable {
                    
                @Binding var shareURL: URL?
                
                func makeCoordinator() -> Coordinator {
                    Coordinator(self)
                }
                
                func makeUIViewController(context: Context) -> some UIViewController {
                    let containerViewController = UIViewController()
                    
                    return containerViewController
            
                }
                
                func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
                    guard let shareURL = shareURL, context.coordinator.presented == false else { return }
                    
                    context.coordinator.presented = true
            
                    let activityViewController = UIActivityViewController(activityItems: [shareURL], applicationActivities: nil)
                    activityViewController.completionWithItemsHandler = { activity, completed, returnedItems, activityError in
                        self.shareURL = nil
                        context.coordinator.presented = false
            
                        if completed {
                            // ...
                        } else {
                            // ...
                        }
                    }
                    
                    // Executing this asynchronously might not be necessary but some of my tests
                    // failed because the view wasn't yet in the view hierarchy on the first pass of updateUIViewController
                    //
                    // There might be a better way to test for that condition in the guard statement and execute this
                    // synchronously if we can be be sure updateUIViewController is invoked at least once after the view is added
                    DispatchQueue.main.asyncAfter(deadline: .now()) {
                        uiViewController.present(activityViewController, animated: true)
                    }
                }
                
                class Coordinator: NSObject {
                    let parent: ActivityViewController
                    
                    var presented: Bool = false
                    
                    init(_ parent: ActivityViewController) {
                        self.parent = parent
                    }
                }
                
            }
            
            struct ContentView: View {
                
                @State var shareURL: URL? = nil
                
                var body: some View {
                    ZStack {
                        Button(action: { shareURL = URL(string: "https://apple.com") }) {
                            Text("Share")
                                .foregroundColor(.white)
                                .padding()
                        }
                        .background(Color.blue)
                        if shareURL != nil {
                            ActivityViewController(shareURL: $shareURL)
                        }
                    }
                    .frame(width: 375, height: 812)
                }
            }
            

            【讨论】:

              【解决方案11】:

              对于更通用的解决方案,我提出了以下想法: https://github.com/mtzaquia/UIKitPresentationModifier

              这是一个通用修饰符,允许您在 SwiftUI 视图中使用 UIKit 演示文稿。

              从那里开始,世界就是你的牡蛎。唯一的缺点是您可能需要将自定义环境值从 presenting 视图级联到 presenting 视图中。

              myPresentingView
                .presentation(isPresented: $isPresented) {
                  MyPresentedView()
                } controllerProvider: { content in
                  let controller = UIHostingController(rootView: content)
                  if #available(iOS 15, *) {
                    if let sheet = controller.sheetPresentationController {
                      sheet.preferredCornerRadius = 12
                      sheet.prefersGrabberVisible = true
                    }
                  }
                  
                  return controller
                }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2022-07-27
                • 1970-01-01
                • 1970-01-01
                • 2021-09-23
                • 2021-04-16
                • 1970-01-01
                相关资源
                最近更新 更多