|
Exactly, I want to share different content when sharing to Facebook, Twitter or through email. The article shows how you can add a subject when sharing through email but I get lost on how to exactly code a ViewController class that supports that, and then how to use that ViewController class.
The article just shows the opening but I assume the class should be something like?
class ViewController: UIViewController, UIActivityItemSource {
let items = [self]
let ac = UIActivityViewController(activityItems: items, applicationActivities: nil)
present(ac, animated: true)
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return "The pig is in the poke"
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
return "The pig is in the poke"
}
func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivity.ActivityType?) -> String {
return "Secret message"
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
if activityType == .postToTwitter {
return "Download #MyAwesomeApp via @twostraws."
} else {
return "Download MyAwesomeApp from TwoStraws."
}
}
}
Now my question is how would I invoke this functionality now (e.g. show the activity sheet). I currently have this for invoking the out of the box share sheet:
let shareActivity = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
if let viewController = UIApplication.shared.windows.first?.rootViewController {
shareActivity.popoverPresentationController?.sourceView = viewController.view
// Setup share activity position on screen on bottom center
shareActivity.popoverPresentationController?.sourceRect = CGRect(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height, width: 0, height: 0)
shareActivity.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.down
viewController.present(shareActivity, animated: true, completion: nil)
}
but how can I adapt that for the ViewController class above? Should I instantiate the ViewController defined above? And if so, how?
|
|
I dont know how your UI setup looks like, but you probably already have a ViewController on the screen. So you can use it both as a source and a presenter of the activity view controller.
Alternatively you can create custom class/struct which will conform to the UIActivityItemSource and implement the required methods to change what gets shared depending on the destination. This is probably the cleaner pattern and will give you more flexibility. Since you can then instantiate this model object anytime you want to share something.
|
|
But the issue is that the sample simple doesn' work, or I do not understand it.
From the article I assume I can define a class like this:
class ViewController: UIViewController, UIActivityItemSource {
let items = [self]
let ac = UIActivityViewController(activityItems: items, applicationActivities: nil)
present(ac, animated: true)
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return "The pig is in the poke"
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
return "The pig is in the poke"
}
func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivity.ActivityType?) -> String {
return "Secret message"
}
}
But let items = [self] already throws an error: "Cannot use instance member 'items' within property initializer; property initializers run before 'self' is available" ...
|
|
Oh I see, the sample does not make it clear enough, but the code you have does not go directly into the ViewController class.
This:
let items = [self]
let ac = UIActivityViewController(activityItems: items, applicationActivities: nil)
present(ac, animated: true)
Should be part of some method, it cannot be on the class level.
|
|
By way of example, here is the DetailViewController from Paul's Project 3 from 100 Days of Swift, adapted to illustrate the topic at hand:
import UIKit
class DetailViewController: UIViewController {
@IBOutlet var imageView: UIImageView!
var selectedImage: String?
var selectedPictureNumber = 0
var totalPictures = 0
override func viewDidLoad() {
super.viewDidLoad()
if let imageToLoad = selectedImage {
imageView.image = UIImage(named: imageToLoad)
}
navigationItem.largeTitleDisplayMode = .never
title = "Picture \(selectedPictureNumber) of \(totalPictures)"
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(shareTapped))
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.hidesBarsOnTap = true
}
override func viewWillDisappear(_ animated: Bool) {
super.viewDidAppear(animated)
navigationController?.hidesBarsOnTap = false
}
@objc func shareTapped() {
let vc = UIActivityViewController(activityItems: [self], applicationActivities: [])
vc.popoverPresentationController?.barButtonItem = navigationItem.rightBarButtonItem
present(vc, animated: true)
}
}
extension DetailViewController: UIActivityItemSource {
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return "placeholder"
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
if let activityType = activityType, activityType == .postToTwitter {
return "post to Twitter"
} else {
return "placeholder"
}
}
}
Maybe this will help. In particular, check out the shareTapped() function, where self is used to construct the UIActivityViewController and the UIActivityItemSource protocol methods in the class extension, which are called to construct the array of items to share.
|
|
Ah, I think the issue is that his and your example are not based on SwiftUI but on Swift and UIKit?
How would I 'morph' that DetailViewController into a SwiftUI view? As that is what I am trying to accomplish.
|
|
You can do something like this:
import SwiftUI
class ItemSource: NSObject, UIActivityItemSource {
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return "placeholder"
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
if let activityType = activityType, activityType == .postToTwitter {
return "post to Twitter"
} else {
return "placeholder"
}
}
}
struct ShareSheetView: View {
var shareItems: ItemSource = {
ItemSource()
}()
var body: some View {
Button(action: share) {
Image(systemName: "square.and.arrow.up")
.font(.largeTitle)
}
}
func share() {
let activityController = UIActivityViewController(activityItems: [shareItems], applicationActivities: [])
UIApplication.shared.windows.first?.rootViewController!.present(activityController, animated: true)
}
}
|
|
Thanks a lot. That looks like what I am after. Alternatively I was looking at using UIViewControllerRepresentable to represent ActivityViewController with UIActivityItemSource in SwiftUI, but this looks way simpler.
Remaining question I do have is how to add more items, e.g. text and image to what you want to share. Do you need to define multipe ItemSource() objects or add logic to one ItemSource()?
|
|
Alternatively I was looking at using UIViewControllerRepresentable to represent ActivityViewController with UIActivityItemSource in SwiftUI, but this looks way simpler.
I tried that too, but the ActivityVC was always presented full height, not half-height like the system does it.
Remaining question I do have is how to add more items, e.g. text and image to what you want to share. Do you need to define multipe ItemSource() objects or add logic to one ItemSource()?
You can create multiple ItemSource objects. Maybe something like this:
import SwiftUI
struct ShareSheetView: View {
@State private var showShareSheet: Bool = false
var shareItems: [ItemSource] = {
[
ItemSource(dataToShare: "Placeholder"),
ItemSource(dataToShare: UIImage(systemName: "heart.fill")!),
//and so on...
]
}()
var body: some View {
Button(action: share) {
Image(systemName: "square.and.arrow.up")
.font(.largeTitle)
}
}
func share() {
//NOTE that since shareItems is now an array of ItemSource objects,
//we don't need the brackets when we pass it to the VC
let activityController = UIActivityViewController(
activityItems: shareItems,
applicationActivities: []
)
UIApplication.shared.windows.first?.rootViewController!.present(activityController, animated: true)
}
}
class ItemSource: NSObject, UIActivityItemSource {
let dataToShare: Any
init(dataToShare: Any) {
self.dataToShare = dataToShare
}
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return dataToShare
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
if let activityType = activityType, activityType == .postToTwitter {
return "post data to Twitter"
} else {
return dataToShare
}
}
}
|
|
Great feedback. Many thanks for your help!
|