1

我在 using 中实现了SwiftUI AppShareSheet UIViewControllerRepresentable


代码

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在其他只覆盖屏幕下半部分的应用程序中看到过。我怎样才能做到这一点?

4

3 回答 3

0

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

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)
    )
  }
}
于 2021-07-29T13:15:15.477 回答
0

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

辅助功能从这里

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
    }
}
于 2021-07-14T16:27:13.883 回答
0

您可以创建自己的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)
        }
    }
}
于 2021-07-14T17:01:20.903 回答