ユーザーが場所を共有できるようにしたいのですが、Swift UIにUIActivityViewControllerを表示する方法がわかりません
よりネイティブに見える別の実装を提案したいと思います(画面の高さの半分で下部に白いギャップがない)。
import SwiftUI
struct ActivityView: UIViewControllerRepresentable {
var activityItems: [Any]
var applicationActivities: [UIActivity]? = nil
@Binding var isPresented: Bool
func makeUIViewController(context: Context) -> ActivityViewWrapper {
ActivityViewWrapper(activityItems: activityItems, applicationActivities: applicationActivities, isPresented: $isPresented)
}
func updateUIViewController(_ uiViewController: ActivityViewWrapper, context: Context) {
uiViewController.isPresented = $isPresented
uiViewController.updateState()
}
}
class ActivityViewWrapper: UIViewController {
var activityItems: [Any]
var applicationActivities: [UIActivity]?
var isPresented: Binding<Bool>
init(activityItems: [Any], applicationActivities: [UIActivity]? = nil, isPresented: Binding<Bool>) {
self.activityItems = activityItems
self.applicationActivities = applicationActivities
self.isPresented = isPresented
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didMove(toParent parent: UIViewController?) {
super.didMove(toParent: parent)
updateState()
}
fileprivate func updateState() {
guard parent != nil else {return}
let isActivityPresented = presentedViewController != nil
if isActivityPresented != isPresented.wrappedValue {
if !isActivityPresented {
let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
controller.completionWithItemsHandler = { (activityType, completed, _, _) in
self.isPresented.wrappedValue = false
}
present(controller, animated: true, completion: nil)
}
else {
self.presentedViewController?.dismiss(animated: true, completion: nil)
}
}
}
}
struct ActivityViewTest: View {
@State private var isActivityPresented = false
var body: some View {
Button("Preset") {
self.isActivityPresented = true
}.background(ActivityView(activityItems: ["Hello, World"], isPresented: $isActivityPresented))
}
}
struct ActivityView_Previews: PreviewProvider {
static var previews: some View {
ActivityViewTest()
}
}
このスレッドで役立つ回答をありがとう。
stale data
の問題を解決しようとしました。 updateUIViewController
にUIViewControllerRepresentable
を実装しないことによる問題。 SwiftUIはmakeUIViewController
を一度だけ呼び出して、ビューコントローラーを作成します。メソッドupdateUIViewController
は、SwiftUIビューの変更に基づいてビューコントローラーに変更を加える責任があります。
UIActivityViewController
ではactivityItems
とapplicationActivities
を変更できないため、ラッパービューコントローラーを使用しました。 UIViewControllerRepresentable
はラッパーを更新し、ラッパーは更新を実行するために必要に応じて新しいUIActivityViewController
を作成します。
アプリケーションの下に「共有」ボタンを実装するためのコードの下。このコードはiOS 13.4ベータ版でテストされており、SwiftUIのいくつかのバグが修正されています。以前のリリースで機能するかどうかは不明です。
struct Share: View {
var document: ReaderDocument // UIDocument subclass
@State var showShareSheet = false
var body: some View {
Button(action: {
self.document.save(to: self.document.fileURL, for: .forOverwriting) { success in
self.showShareSheet = true
}
}) {
Image(systemName: "square.and.arrow.up")
}.popover(isPresented: $showShareSheet) {
ActivityViewController(activityItems: [ self.document.text, self.document.fileURL,
UIPrintInfo.printInfo(), self.printFormatter ])
.frame(minWidth: 320, minHeight: 500) // necessary for iPad
}
}
var printFormatter: UIPrintFormatter {
let fontNum = Preferences.shared.readerFontSize.value
let fontSize = ReaderController.readerFontSizes[fontNum < ReaderController.readerFontSizes.count ? fontNum : 1]
let printFormatter = UISimpleTextPrintFormatter(text: self.document.text)
printFormatter.startPage = 0
printFormatter.perPageContentInsets = UIEdgeInsets(top: 72, left: 72, bottom: 72, right: 72)
return printFormatter
}
}
struct ActivityViewController: UIViewControllerRepresentable {
var activityItems: [Any]
var applicationActivities: [UIActivity]? = nil
@Environment(\.presentationMode) var presentationMode
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityViewController>)
-> WrappedViewController<UIActivityViewController> {
let controller = WrappedViewController(wrappedController: activityController)
return controller
}
func updateUIViewController(_ uiViewController: WrappedViewController<UIActivityViewController>,
context: UIViewControllerRepresentableContext<ActivityViewController>) {
uiViewController.wrappedController = activityController
}
private var activityController: UIActivityViewController {
let avc = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
avc.completionWithItemsHandler = { (_, _, _, _) in
self.presentationMode.wrappedValue.dismiss()
}
return avc
}
}
class WrappedViewController<Controller: UIViewController>: UIViewController {
var wrappedController: Controller {
didSet {
if (wrappedController != oldValue) {
oldValue.removeFromParent()
oldValue.view.removeFromSuperview()
addChild(wrappedController)
view.addSubview(wrappedController.view)
wrappedController.view.frame = view.bounds
}
}
}
init(wrappedController: Controller) {
self.wrappedController = wrappedController
super.init(nibName: nil, bundle: nil)
addChild(wrappedController)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
super.loadView()
view.addSubview(wrappedController.view)
wrappedController.view.frame = view.bounds
}
}
FWIW-UIActivityItemSource
の実装を含む、回答のわずかな改善を提供します。特にitemForActivityType
とactivityViewControllerPlaceholderItem
のデフォルトの戻り値を中心に、コードを簡潔にするために簡略化しました。常に同じ型を返す必要があります。
ActivityViewController
struct ActivityViewController: UIViewControllerRepresentable {
var activityItems: [Any]
var shareable : ActivityShareable?
var applicationActivities: [UIActivity]? = nil
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityViewController>) -> UIActivityViewController {
let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
controller.modalPresentationStyle = .automatic
return controller
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ActivityViewController>) {}
func makeCoordinator() -> ActivityViewController.Coordinator {
Coordinator(self.shareable)
}
class Coordinator : NSObject, UIActivityItemSource {
private let shareable : ActivityShareable?
init(_ shareable: ActivityShareable?) {
self.shareable = shareable
super.init()
}
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
guard let share = self.shareable else { return "" }
return share.getPlaceholderItem()
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
guard let share = self.shareable else { return "" }
return share.itemForActivityType(activityType: activityType)
}
func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivity.ActivityType?) -> String {
guard let share = self.shareable else { return "" }
return share.subjectForActivityType(activityType: activityType)
}
}
}
ActivityShareable
protocol ActivityShareable {
func getPlaceholderItem() -> Any
func itemForActivityType(activityType: UIActivity.ActivityType?) -> Any?
/// Optional
func subjectForActivityType(activityType: UIActivity.ActivityType?) -> String
}
extension ActivityShareable {
func subjectForActivityType(activityType: UIActivity.ActivityType?) -> String {
return ""
}
}
ActivityViewController
または基礎となるUIActivityViewController
への参照を渡すこともできますが、それは不必要に感じられます。
Tikhonov's に基づいて、次のコードは、アクティビティシートが適切に閉じられるように修正を追加しました(後でないと、シートは表示されません)。
struct ActivityViewController: UIViewControllerRepresentable {
var activityItems: [Any]
var applicationActivities: [UIActivity]? = nil
@Environment(\.presentationMode) var presentationMode
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityViewController>) -> UIActivityViewController {
let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
controller.completionWithItemsHandler = { (activityType, completed, returnedItems, error) in
self.presentationMode.wrappedValue.dismiss()
}
return controller
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ActivityViewController>) {}
}