Quiero permitir que el usuario pueda compartir una ubicación, pero no sé cómo mostrar UIActivityViewController en Swift UI
La implementación básica de UIActivityViewControlleren SwiftUIes
import UIKit
import SwiftUI
struct ActivityViewController: UIViewControllerRepresentable {
var activityItems: [Any]
var applicationActivities: [UIActivity]? = nil
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityViewController>) -> UIActivityViewController {
let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
return controller
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ActivityViewController>) {}
}
Y así es como se usa.
struct MyView: View {
@State private var isSharePresented: Bool = false
var body: some View {
Button("Share app") {
self.isSharePresented = true
}
.sheet(isPresented: $isSharePresented, onDismiss: {
print("Dismiss")
}, content: {
ActivityViewController(activityItems: [URL(string: "https://www.apple.com")!])
})
}
}
Es una cosa de una sola vez actualmente. .sheet lo mostrará como una hoja, pero volver a abrirlo desde la misma vista tendrá datos obsoletos. Esas presentaciones posteriores de la hoja tampoco activarán ningún controlador de finalización. Básicamente, makeUIViewController se llama solo una vez, que es la única forma de obtener los datos para compartir en UIActivityViewController. updateUIViewController no tiene forma de actualizar los datos en sus activityItems o restablecer el controlador porque no son visibles desde una instancia de UIActivityViewController.
Tenga en cuenta que tampoco funciona con UIActivityItemSource o UIActivityItemProvider. Usar esos es aún peor. El valor del marcador de posición no se muestra.
Hice un poco más y decidí que tal vez el problema con mi solución era una hoja que presentaba otra hoja, y cuando una se iba, la otra se quedaba.
Esta forma indirecta de hacer que un ViewController haga la presentación cuando aparece hizo que me funcionara.
class UIActivityViewControllerHost: UIViewController {
var message = ""
var completionWithItemsHandler: UIActivityViewController.CompletionWithItemsHandler? = nil
override func viewDidAppear(_ animated: Bool) {
share()
}
func share() {
// set up activity view controller
let textToShare = [ message ]
let activityViewController = UIActivityViewController(activityItems: textToShare, applicationActivities: nil)
activityViewController.completionWithItemsHandler = completionWithItemsHandler
activityViewController.popoverPresentationController?.sourceView = self.view // so that iPads won't crash
// present the view controller
self.present(activityViewController, animated: true, completion: nil)
}
}
struct ActivityViewController: UIViewControllerRepresentable {
@Binding var text: String
@Binding var showing: Bool
func makeUIViewController(context: Context) -> UIActivityViewControllerHost {
// Create the host and setup the conditions for destroying it
let result = UIActivityViewControllerHost()
result.completionWithItemsHandler = { (activityType, completed, returnedItems, error) in
// To indicate to the hosting view this should be "dismissed"
self.showing = false
}
return result
}
func updateUIViewController(_ uiViewController: UIActivityViewControllerHost, context: Context) {
// Update the text in the hosting controller
uiViewController.message = text
}
}
struct ContentView: View {
@State private var showSheet = false
@State private var message = "a message"
var body: some View {
VStack {
TextField("what to share", text: $message) Button("Hello World") { self.showSheet = true } if showSheet { ActivityViewController(text: $message, showing: $showSheet)
.frame(width: 0, height: 0)
}
Spacer()
}
.padding()
}
}
Basado en el de Tikhonov , el siguiente código agregó una solución para asegurarse de que la hoja de actividad se descarte correctamente (si no, posteriormente, la hoja no se presentará).
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>) {}
}
Puede intentar realizar la migración UIActivityViewControllerde la SwiftUIsiguiente manera:
struct ActivityView: UIViewControllerRepresentable {
let activityItems: [Any]
let applicationActivities: [UIActivity]?
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityView>) -> UIActivityViewController {
return UIActivityViewController(activityItems: activityItems,
applicationActivities: applicationActivities)
}
func updateUIViewController(_ uiViewController: UIActivityViewController,
context: UIViewControllerRepresentableContext<ActivityView>) {
}
}
pero la aplicación se bloqueará cuando intente mostrarla.
Probé: Modal, Popovery NavigationButton.
Para probarlo:
struct ContentView: View {
var body: some Body {
EmptyView
.presentation(Modal(ActivityView()))
}
}
No parece ser utilizable desde SwiftUI.
Lo hice funcionar ahora usando
.sheet(isPresented: $isSheet, content: { ActivityViewController() }
.presentación está obsoleta
Ocupa toda la pantalla al estilo iOS 13.
Quiero sugerir otra implementación que parezca más nativa (media altura de pantalla sin fondo de espacio blanco).
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()
}
}
FWIW: proporciona una ligera mejora a las respuestas que incluye una implementación para UIActivityItemSource. Código simplificado por brevedad, específicamente en torno al retorno predeterminado itemForActivityTypey activityViewControllerPlaceholderItem, siempre deben devolver el mismo tipo.
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 ""
}
}
Puede pasar la referencia ActivityViewControllero el subyacente, UIActivityViewControllerpero eso se siente innecesario.
Hay una biblioteca llamada SwiftUIX que ya tiene un contenedor para UIActivityViewController. Vea el esqueleto rápido de cómo presentarlo a través del .sheet()cual debe colocarse en algún lugar del var body: some View {}.
import SwiftUIX
/// ...
@State private var showSocialsInviteShareSheet: Bool = false
// ...
.sheet(isPresented: $showSocialsInviteShareSheet, onDismiss: {
print("Dismiss")
}, content: {
AppActivityView(activityItems: [URL(string: "https://www.apple.com")!])
})
Puede que no sea recomendable, pero es realmente fácil y tiene dos líneas de código para compartir texto.
Button(action: {
let shareActivity = UIActivityViewController(activityItems: ["Text To Share"], applicationActivities: nil)
UIApplication.shared.windows.first?.rootViewController?.present(shareActivity, animated: true, completion: nil)
}) {
Text("Share")
}
Gracias por las útiles respuestas en este hilo.
Intenté solucionar el stale dataproblema. El problema de no implementar updateUIViewControlleren UIViewControllerRepresentable. SwiftUI llama makeUIViewControllersolo una vez para crear el controlador de vista. El método updateUIViewControlleres responsable de realizar cambios en el controlador de vista según los cambios de la vista SwiftUI.
Como UIActivityViewControllerno permite cambiar activityItemsy applicationActivities, utilicé un controlador de vista de envoltura. UIViewControllerRepresentableactualizará el contenedor y el contenedor creará uno nuevo UIActivityViewControllersegún sea necesario para realizar la actualización.
Debajo de mi código para implementar un botón "compartir" en mi aplicación. El código se prueba en iOS 13.4 beta, que ha corregido varios errores de SwiftUI, no estoy seguro de si funciona en versiones anteriores.
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
}
}
Jana Duggar ha sido abierta sobre su búsqueda del amor. Aquí está todo lo que ha dicho sobre el tema y su ventana de cinco años para el matrimonio.
La estrella de 'Outlander', Sam Heughan, reveló recientemente qué se llevará del set para recordar a Jamie Fraser, el papel que lo convirtió en una estrella.
'She Loves You' fue escrita en una hora, grabada en un día y fue la canción de The Beatles con una de las mejores interpretaciones de su carrera.
Dolly Parton y su abuela Bessie tenían una relación especial. La abuela Parton estaba muy enferma, pero eso no impidió que Dolly le hiciera una broma.
Algunos monos tienen mucha suerte con la genética. ¿Otros? No tanto. El mono más feo no es una sola especie, sino toda una galería de primates que te hacen mirar dos veces y preguntarte si la evolución te estaba jugando una broma.
Puede que pienses que el roble o el nogal americano son maderas resistentes, pero cuando se trata de las maderas más duras del mundo, ni siquiera se acercan.
El océano rebosa belleza, pero también esconde algunas de las criaturas marinas más aterradoras del planeta. Muchos de estos animales acechan en las profundidades oceánicas, en el oscuro y opresivo mundo de alta presión del océano.
Si te enfrentas a monstruos hostiles o te preparas para combates PvP, conocer los mejores encantamientos para espadas en Minecraft puede darte una gran ventaja. Encantar espadas te permite infligir más daño, aumentar los objetos que sueltan los monstruos y prolongar la durabilidad de tu espada.
“Children” lanza un cameo de renombre y algunas noticias devastadoras
Se dice que este RAV4 está en excelentes condiciones y preparado para un juego de sillas musicales.
Imágenes de drones capturaron a los bomberos que parecían tener dificultades para apagar el incendio.
Eyes of Wakanda se vincula directamente con la MCU, además de actualizaciones sobre X-Men '97, What If..., Daredevil y más.
El anillo de compromiso de Kate Middleton perteneció a la princesa Diana antes de que el príncipe William le propusiera matrimonio con él. Descubra todo lo que hay que saber sobre esta pieza histórica aquí.
John Cleese se casó con su esposa, Jennifer Wade, en 2012. Aquí encontrará todo lo que debe saber sobre la esposa de John Cleese, Jennifer Wade.
Patton Oswalt se casó con su esposa, la también actriz Meredith Salenger, en 2017. Aquí encontrará todo lo que debe saber sobre la esposa de Patton Oswalt, Meredith Salenger.
Aquí encontrará todo lo que debe saber sobre Lauren Scruggs Kennedy, la bloguera de moda y autora de best sellers del New York Times que espera su segundo hijo con la personalidad de televisión Jason Kennedy.
Me llama la atención De repente, como ocurre con tanta frecuencia Es el punto medio de nuevo <No, no el del Pacífico, tú, el del carnaval> Todos los titulares en voz alta...
En 2022, con el lanzamiento de GPT-3, la habilidad de escritura parece perder importancia. Con IA, las personas con mala escritura también pueden generar personal de calidad.
En el mundo acelerado y competitivo de hoy, la carrera de uno juega un papel importante en el crecimiento personal, la estabilidad financiera y la satisfacción general con la vida. Sin embargo, muchas personas se encuentran atrapadas en un ciclo implacable de estancamiento e insatisfacción, y su vida profesional se desvanece gradualmente.
La semana pasada noté un comunicado de prensa, enviado a través de PressGazette (un sitio web de noticias de los medios británicos). El artículo anunciaba que Acast, la empresa de publicidad y alojamiento de podcasts de Scandi, lideraría un consorcio de editores "probablemente los más influyentes" en podcasting.