【问题标题】:SwiftUI • How to implement half-Screen ShareSheet?SwiftUI • 如何实现半屏ShareSheet?
【发布时间】:2021-09-23 13:36:12
【问题描述】:

我使用UIViewControllerRepresentable 在我的SwiftUI App 中实现了一个ShareSheet。


代码

struct ShareView: UIViewControllerRepresentable {
    typealias Callback = (_ activityType: UIActivity.ActivityType?, _ completed: Bool, _ returnedItems: [Any]?, _ error: Error?) -> Void
    
    let activityItems: [Any] = ["Some Text"]
    let applicationActivities: [UIActivity]? = nil
    let excludedActivityTypes: [UIActivity.ActivityType]? = nil
    let callback: Callback? = nil
    
    func makeUIViewController(context: Context) -> UIActivityViewController {
        let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
        controller.excludedActivityTypes = excludedActivityTypes
        controller.completionWithItemsHandler = callback
        return controller
    }
    
    func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {
        // ...
    }
}

// Presentation
View()
    .sheet(isPresented: $showShareSheet) {
        ShareView()
    }

问题: 这很好用,但是Share Sheet 总是覆盖整个屏幕。我在仅覆盖屏幕下半部分的其他应用程序中看到了Share Sheets我怎样才能做到这一点?

【问题讨论】:

标签: ios swift swiftui uikit uiviewcontrollerrepresentable


【解决方案1】:

UIKit 解决方案。

我已经在大多数控制器的顶部显示了UIActivityViewController

class ShareView {
    static let shared = ShareView()
    typealias Callback = (_ activityType: UIActivity.ActivityType?, _ completed: Bool, _ returnedItems: [Any]?, _ error: Error?) -> Void
    
    let activityItems: [Any] = ["Some Text"]
    let applicationActivities: [UIActivity]? = nil
    let excludedActivityTypes: [UIActivity.ActivityType]? = nil
    let callback: Callback? = nil
    
    func open(callback: Callback? = nil) {
        let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
        controller.excludedActivityTypes = excludedActivityTypes
        controller.completionWithItemsHandler = callback
        UIWindow.getTopViewController()?.present(controller, animated: true, completion: nil)
    }
}

用法:

struct ContentView: View {
    var body: some View {
        Button("Open Share Sheet") {
            ShareView.shared.open()
        }
    }
}

来自here的辅助函数

extension UIWindow {
    static func getTopViewController() -> UIViewController? {
        if #available(iOS 13, *){
            let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
            
            if var topController = keyWindow?.rootViewController {
                while let presentedViewController = topController.presentedViewController {
                    topController = presentedViewController
                }
                return topController
            }
        } else {
            if var topController = UIApplication.shared.keyWindow?.rootViewController {
                while let presentedViewController = topController.presentedViewController {
                    topController = presentedViewController
                }
                return topController
            }
        }
        return nil
    }
}

【讨论】:

    【解决方案2】:

    您可以创建自己的UIViewController

    struct CustomModalParent: View {
        @State var presentShareSheet = false
        
        var body: some View {
            VStack{
                Button(presentShareSheet ? "dismiss share sheet" : "present share sheet", action: {
                    presentShareSheet.toggle()
                }).adaptiveShareSheet(isPresented: $presentShareSheet, activityItems: ["from view modifier"]) { activityType, completed, returnedItems, error in
                    //Your code
                    
                    //This is needed here to update the Binding variable when closing isn't detected otherwise
                    presentShareSheet = false
                }
            }
        }
    }
    ///View extention for the ShareSheet
    extension View {
        typealias Callback = (_ activityType: UIActivity.ActivityType?, _ completed: Bool, _ returnedItems: [Any]?, _ error: Error?) -> Void
        
        func adaptiveShareSheet(isPresented: Binding<Bool>, swipeToDismiss: Bool = true, transitionStyle: UIModalTransitionStyle = .coverVertical, activityItems: [Any] = ["Some Text"], applicationActivities: [UIActivity]? = nil, excludedActivityTypes: [UIActivity.ActivityType]? = nil,callback: Callback? = nil) -> some View {
            ZStack{
                self
                CustomShareSheet_UI(isPresented: isPresented, swipeToDismiss: swipeToDismiss, transitionStyle: transitionStyle, activityItems: activityItems, applicationActivities: applicationActivities,callback: callback).frame(width: 0, height: 0, alignment: .center)
            }
        }
    }
    ///Interface for UIActivityViewController with SwiftUI
    struct CustomShareSheet_UI: UIViewControllerRepresentable {
        //Share
        typealias Callback = (_ activityType: UIActivity.ActivityType?, _ completed: Bool, _ returnedItems: [Any]?, _ error: Error?) -> Void
        
        let activityItems: [Any]
        let applicationActivities: [UIActivity]?
        let excludedActivityTypes: [UIActivity.ActivityType]?
        let callback: Callback?
        //Presentattion
        @Binding var isPresented: Bool
        //Adaptive
        let swipeToDismiss: Bool
        let transitionStyle: UIModalTransitionStyle
        //MARK: init
        init(isPresented: Binding<Bool>, swipeToDismiss: Bool = true, transitionStyle: UIModalTransitionStyle = .coverVertical, activityItems: [Any] = ["Some Text"], applicationActivities: [UIActivity]? = nil, excludedActivityTypes: [UIActivity.ActivityType]? = nil,callback: Callback? = nil) {
            
            self.swipeToDismiss = swipeToDismiss
            self.transitionStyle = transitionStyle
            //Presentation
            self._isPresented = isPresented
            //Share
            self.activityItems = activityItems
            self.applicationActivities = applicationActivities
            self.excludedActivityTypes = excludedActivityTypes
            self.callback = callback
        }
        func makeCoordinator() -> Coordinator {
            Coordinator(self)
        }
        func makeUIViewController(context: Context) -> CustomShareSheetViewController{
            let vc = CustomShareSheetViewController(coordinator: context.coordinator, swipeToDismiss: swipeToDismiss, transitionStyle: transitionStyle, activityItems: activityItems, applicationActivities: applicationActivities,callback: callback)
            return vc
        }
        
        func updateUIViewController(_ uiViewController: CustomShareSheetViewController, context: Context) {
            if isPresented{
                
                uiViewController.presentModalView()
            }else{
                uiViewController.dismissModalView()
            }
            if uiViewController.presentedViewController == nil && isPresented == true{
                isPresented = false
            }
        }
        class Coordinator: NSObject, UIAdaptivePresentationControllerDelegate {
            var parent: CustomShareSheet_UI
            init(_ parent: CustomShareSheet_UI) {
                self.parent = parent
            }
            //Adjust the variable when the user dismisses with a swipe
            func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
                if parent.isPresented{
                    parent.isPresented = false
                }
                
            }
            
        }
    }
    class CustomShareSheetViewController: UIViewController {
        //MARK: Share
        typealias Callback = (_ activityType: UIActivity.ActivityType?, _ completed: Bool, _ returnedItems: [Any]?, _ error: Error?) -> Void
        
        let activityItems: [Any]
        let applicationActivities: [UIActivity]?
        let excludedActivityTypes: [UIActivity.ActivityType]?
        let callback: Callback?
        
        //MARK: Coordinator
        let coordinator: CustomShareSheet_UI.Coordinator
        
        //MARK: Adaptive Options
        let swipeToDismiss: Bool
        let transitionStyle: UIModalTransitionStyle
        init(coordinator: CustomShareSheet_UI.Coordinator, swipeToDismiss: Bool = true, transitionStyle: UIModalTransitionStyle = .coverVertical, activityItems: [Any] = ["Some Text"], applicationActivities: [UIActivity]? = nil, excludedActivityTypes: [UIActivity.ActivityType]? = nil,callback: Callback? = nil) {
            //Coordinator
            self.coordinator = coordinator
            //Adaptive
            self.swipeToDismiss = swipeToDismiss
            self.transitionStyle = transitionStyle
            //Share
            self.activityItems = activityItems
            self.applicationActivities = applicationActivities
            self.excludedActivityTypes = excludedActivityTypes
            self.callback = callback
            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(){
            print(#function)
            let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
            controller.excludedActivityTypes = excludedActivityTypes
            controller.completionWithItemsHandler = callback
            controller.modalPresentationStyle = .popover
            controller.presentationController?.delegate = coordinator as UIAdaptivePresentationControllerDelegate
            controller.isModalInPresentation = !swipeToDismiss
            controller.modalTransitionStyle = UIModalTransitionStyle.coverVertical
            if presentedViewController == nil{
                present(controller, animated: true, completion: nil)
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      您可以创建空视图控制器来呈现工作表

      struct ShareSheet: UIViewControllerRepresentable {
        // To setup the share sheet
        struct Config {
          let activityItems: [Any]
          var applicationActivities: [UIActivity]?
          var excludedActivityTypes: [UIActivity.ActivityType]?
        }
      
        // Result object
        struct Result {
          let error: Error?
          let activityType: UIActivity.ActivityType?
          let completed: Bool
          let returnedItems: [Any]?
        }
      
        @Binding var isPresented: Bool
      
        private var handler: ((Result) -> Void)?
        private let shareSheet: UIActivityViewController
      
        init(
          isPresented: Binding<Bool>,
          config: Config,
          onEnd: ((Result) -> Void)? = nil
        ) {
          self._isPresented = isPresented
          shareSheet = UIActivityViewController(
            activityItems: config.activityItems,
            applicationActivities: config.applicationActivities
          )
          shareSheet.excludedActivityTypes = config.excludedActivityTypes
          shareSheet.completionWithItemsHandler = { activityType, completed, returnedItems, error in
            onEnd?(
              .init(
                error: error,
                activityType: activityType,
                completed: completed,
                returnedItems: returnedItems
              )
            )
            // Set isPresented to false after complete
            isPresented.wrappedValue = false
          }
        }
      
        func makeUIViewController(context: Context) -> UIViewController {
          UIViewController()
        }
      
        func updateUIViewController(
          _ uiViewController: UIViewController,
          context: Context
        ) {
          if isPresented, shareSheet.view.window == nil {
            uiViewController.present(shareSheet, animated: true, completion: nil)
          } else if !isPresented, shareSheet.view.window != nil {
            shareSheet.dismiss(animated: true)
          }
        }
      }
      

      你也可以在视图扩展中创建操作符

      extension View {
        func shareSheet(
          isPresented: Binding<Bool>,
          config: ShareSheet.Config,
          onEnd: ((ShareSheet.Result) -> Void)? = nil
        ) -> some View {
          self.background(
            ShareSheet(isPresented: isPresented, config: config, onEnd: onEnd)
          )
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-06-25
        • 2022-11-14
        • 2011-10-17
        • 1970-01-01
        • 2022-01-14
        • 1970-01-01
        • 2019-01-17
        • 2020-02-11
        相关资源
        最近更新 更多