I have been using the code below to show a UIActivityViewController which worked fine when I was using Xcode 6, Swift 1.2 and iOS 8. However when I updated it shows the UIActivityViewController but it is completely blank without any of the sharing options. Do you have any suggestions?
if UIDevice.currentDevice().userInterfaceIdiom == .Pad {
let textToShare = textViewOne.text
let objectsToShare = [textToShare]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
let nav = UINavigationController(rootViewController: activityVC)
nav.modalPresentationStyle = UIModalPresentationStyle.Popover
let popover = nav.popoverPresentationController as UIPopoverPresentationController!
popover.sourceView = self.view
popover.sourceRect = sender.frame
self.presentViewController(nav, animated: true, completion: nil)
} else {
let textToShare = textViewOne.text
let objectsToShare = [textToShare]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
self.presentViewController(activityVC, animated: true, completion: nil)
}
This has fixed it.
let objectsToShare = [textToShare]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityVC.title = "Share One"
activityVC.excludedActivityTypes = []
activityVC.popoverPresentationController?.sourceView = self.view
activityVC.popoverPresentationController?.sourceRect = sender.frame
self.presentViewController(activityVC, animated: true, completion: nil)
in swift 3.0:
let objectsToShare = [textToShare]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityVC.title = "Share One"
activityVC.excludedActivityTypes = []
activityVC.popoverPresentationController?.sourceView = self.view
activityVC.popoverPresentationController?.sourceRect = sender.frame
self.present(activityVC, animated: true, completion: nil)
I was struggling with the above suggestion with SWIFT 5 since:
activity.popoverPresentationController?.sourceRect = sender.frame
does NOT WORK in some cases, since sender is not available in scope.
Try instead to set with a CGRECT, something like:
activityController.popoverPresentationController?.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY,width: 0,height: 0)
I hope this helps some people.
Swift 4.0
let shareText = "Hi"
let activity = UIActivityViewController(activityItems: shareText, applicationActivities: nil)
activity.excludedActivityTypes = []
if UIDevice.current.userInterfaceIdiom == .pad {
activity.popoverPresentationController?.sourceView = self.view
activity.popoverPresentationController?.sourceRect = sender.frame
}
self.present(activity, animated: true, completion: nil)
I had sort of a different issue. I wanted the UIActivityViewController to stay in the center of the screen but when rotating the iPad it was off centered when in Landscape.
Landscape wrong:
The 2 fixes was to make the UIActivityViewController a class property and most importantly set its sourceRect in viewDidLayoutSubviews. Follow the 5 steps-
// 1. make the activityVC a class property
var activityVC: UIActivityViewController?
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if UIDevice.current.userInterfaceIdiom == .pad {
// 2. set its sourceRect here. It's the same as in step 4
activityVC?.popoverPresentationController?.sourceRect = CGRect(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height / 2, width: 0, height: 0)
}
}
// 3. present the UIActivityViewController
func presentActivityVC() {
let objectsToShare = [textToShare]
activityVC = nil
activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityVC?.excludedActivityTypes = [.addToReadingList, .openInIBooks, .print]
activityVC?.popoverPresentationController?.sourceView = self.view
if UIDevice.current.userInterfaceIdiom == .phone {
activityVC?.modalPresentationStyle = .overFullScreen
}
if UIDevice.current.userInterfaceIdiom == .pad {
// 4. set its sourceRect here. It's the same as in step 2
activityVC?.popoverPresentationController?.sourceRect = CGRect(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height / 2, width: 0, height: 0)
activityVC?.popoverPresentationController?.permittedArrowDirections = []
}
present(activityVC!, animated: true, completion: nil)
activityVC?.completionWithItemsHandler = { [weak self](activityType, completed:Bool, returnedItems:[Any]?, error: Error?) in
if let error = error {
print(error.localizedDescription)
return
}
// 5. set the activityVC to nil after the user is done
DispatchQueue.main.async { [weak self] in
self?.activityVC = nil
}
}
}
Now when rotating it's centered both in landscape and portrait.
Landscape correct:
Portrait:
if you want to attach the popover to the sender view's bounds, then the following code will work:
let vc = UIActivityViewController(activityItems: [url], applicationActivities: nil)
if UIDevice.current.userInterfaceIdiom == .pad {
vc.popoverPresentationController?.sourceView = sender
vc.popoverPresentationController?.sourceRect = CGRect(origin: CGPoint.zero, size: sender.frame.size)
}
sourceRect is defined in the coordinate space of the sourceView, thus we need to specify the origin of the sourceRect as CGPoint.zero rather than sender.frame.origin.
We would need to set the sourceView and sourceRect both specially for iPad.
We may try below snippet
activityViewController.popoverPresentationController?.sourceView = sender.self
activityViewController.popoverPresentationController?.sourceRect = CGRect(x: self.view.bounds.minX + sender.frame.width/2, y: self.view.bounds.minY, width: 0, height: 0)
activityViewController.popoverPresentationController?.permittedArrowDirections = []
It would set the sourceView as sender's and sourceRect at center to the UI.
We are adding sender.frame.width/2 to x coordinate and removing the anchor arrow as well to get the pop-up exactly at center.
Updated for swift 4 ~ 5 with action event example, it'll show arrow also.Tested on iPadOS 15.7.2 & iOS 16.1, 13.6
#objc
private final func handleShareButtonPress(_ sender: UIButton) {
let urlString = Manager.getShareEventBaseURL
guard let url = URL(string: urlString) else { return }
let items: [Any] = [url]
let activityController = UIActivityViewController(activityItems: items, applicationActivities: nil)
activityController.title = "\(user.name) sharing with you."
if let popoverController = activityController.popoverPresentationController {
popoverController.sourceView = self.view // to set the source of your alert
popoverController.sourceRect = sender.convert(sender.frame, to: self.view) /// Get correct rect of sender view
popoverController.permittedArrowDirections = [.up] // declare the direction of sender to show arrow
}
present(activityController, animated: true)
}
Related
Share button works in iPhone but causes a crash in iPad.
Code :
let finaltext = "Text to share"
let objectsToShare = [finaltext]
let activityController = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityController.popoverPresentationController?.sourceView = self.view
present(activityController, animated: true, completion: nil)
Thank for Help
Try to add sourceRect like this:
let finaltext = "Text to share"
let objectsToShare = [finaltext]
let activityController = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityController.popoverPresentationController?.sourceView = self.view
activityController.popoverPresentationController?.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.maxY, width: 0, height: 0) //center bottom
present(activityController, animated: true, completion: nil)
This is the some peace of code for twitter... I want to know how to get the share action view like we get in iOS stock photos app...
#IBAction func twitterButton(sender: AnyObject) {
let image: UIImage = UIImage(named: "LaunchScreenImage.png")!
let twitterControl = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
twitterControl.setInitialText("")
twitterControl.addImage(image)
let completionHandler = {(result:SLComposeViewControllerResult) -> () in
twitterControl.dismissViewControllerAnimated(true, completion: nil)
switch(result){
case SLComposeViewControllerResult.Cancelled:
print("User canceled", terminator: "")
case SLComposeViewControllerResult.Done:
print("User tweeted", terminator: "")
}
}
twitterControl.completionHandler = completionHandler
self.presentViewController(twitterControl, animated: true, completion: nil)
}
Swift 5:
// Setting description
let firstActivityItem = "Description you want.."
// Setting url
let secondActivityItem : NSURL = NSURL(string: "http://your-url.com/")!
// If you want to use an image
let image : UIImage = UIImage(named: "your-image-name")!
let activityViewController : UIActivityViewController = UIActivityViewController(
activityItems: [firstActivityItem, secondActivityItem, image], applicationActivities: nil)
// This lines is for the popover you need to show in iPad
activityViewController.popoverPresentationController?.sourceView = (sender as! UIButton)
// This line remove the arrow of the popover to show in iPad
activityViewController.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.down
activityViewController.popoverPresentationController?.sourceRect = CGRect(x: 150, y: 150, width: 0, height: 0)
// Pre-configuring activity items
activityViewController.activityItemsConfiguration = [
UIActivity.ActivityType.message
] as? UIActivityItemsConfigurationReading
// Anything you want to exclude
activityViewController.excludedActivityTypes = [
UIActivity.ActivityType.postToWeibo,
UIActivity.ActivityType.print,
UIActivity.ActivityType.assignToContact,
UIActivity.ActivityType.saveToCameraRoll,
UIActivity.ActivityType.addToReadingList,
UIActivity.ActivityType.postToFlickr,
UIActivity.ActivityType.postToVimeo,
UIActivity.ActivityType.postToTencentWeibo,
UIActivity.ActivityType.postToFacebook
]
activityViewController.isModalInPresentation = true
self.present(activityViewController, animated: true, completion: nil)
This is how I implemented sharing with Swift 4/5 using a right button on the Navigation Controller. It includes an image, text and link.
SWIFT 4/5
On ViewDidLoad
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Share", style: .plain, target: self, action: #selector(share(sender:)))
Create the function
#objc func share(sender:UIView){
UIGraphicsBeginImageContext(view.frame.size)
view.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let textToShare = "Check out my app"
if let myWebsite = URL(string: "http://itunes.apple.com/app/idXXXXXXXXX") {//Enter link to your app here
let objectsToShare = [textToShare, myWebsite, image ?? #imageLiteral(resourceName: "app-logo")] as [Any]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
//Excluded Activities
activityVC.excludedActivityTypes = [UIActivity.ActivityType.airDrop, UIActivity.ActivityType.addToReadingList]
//
activityVC.popoverPresentationController?.sourceView = sender
self.present(activityVC, animated: true, completion: nil)
} }
#IBAction func shareButtonClicked(sender: AnyObject)
{
//Set the default sharing message.
let message = "Message goes here."
//Set the link to share.
if let link = NSURL(string: "http://yoururl.com")
{
let objectsToShare = [message,link]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityVC.excludedActivityTypes = [UIActivityTypeAirDrop, UIActivityTypeAddToReadingList]
self.presentViewController(activityVC, animated: true, completion: nil)
}
}
This will allow you to present a UIActivityViewController to share a link and a message with any application that will accept them.
Details
Xcode 11.4.1 (11E503a), Swift 5.2
Solution
TopViewController solution
extension UIApplication {
class var topViewController: UIViewController? { return getTopViewController() }
private class func getTopViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController { return getTopViewController(base: nav.visibleViewController) }
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController { return getTopViewController(base: selected) }
}
if let presented = base?.presentedViewController { return getTopViewController(base: presented) }
return base
}
private class func _share(_ data: [Any],
applicationActivities: [UIActivity]?,
setupViewControllerCompletion: ((UIActivityViewController) -> Void)?) {
let activityViewController = UIActivityViewController(activityItems: data, applicationActivities: nil)
setupViewControllerCompletion?(activityViewController)
UIApplication.topViewController?.present(activityViewController, animated: true, completion: nil)
}
class func share(_ data: Any...,
applicationActivities: [UIActivity]? = nil,
setupViewControllerCompletion: ((UIActivityViewController) -> Void)? = nil) {
_share(data, applicationActivities: applicationActivities, setupViewControllerCompletion: setupViewControllerCompletion)
}
class func share(_ data: [Any],
applicationActivities: [UIActivity]? = nil,
setupViewControllerCompletion: ((UIActivityViewController) -> Void)? = nil) {
_share(data, applicationActivities: applicationActivities, setupViewControllerCompletion: setupViewControllerCompletion)
}
}
Usage
UIApplication.share("Text to share")
let data = ["Text, Image and url", image, url] as [Any]
UIApplication.share(data)
Full sample
Do not forget to add the solution code here (look above)
import UIKit
class ViewController: UIViewController {
private weak var imageView: UIImageView?
override func viewDidLoad() {
super.viewDidLoad()
var button = UIButton(frame: CGRect(x: 50, y: 50, width: 200, height: 40))
button.setTitle("Share text", for: .normal)
button.addTarget(self, action: #selector(shareButtonTapped), for: .touchUpInside)
button.setTitleColor(.blue, for: .normal)
view.addSubview(button)
button = UIButton(frame: CGRect(x: 50, y: 80, width: 200, height: 40))
button.setTitle("Share text & image", for: .normal)
button.addTarget(self, action: #selector(shareCombinedData), for: .touchUpInside)
button.setTitleColor(.blue, for: .normal)
view.addSubview(button)
let imageView = UIImageView(frame: CGRect(x: 50, y: 120, width: 200, height: 200))
imageView.image = UIImage(named: "image")
imageView.isUserInteractionEnabled = true
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(imageViewTapped)))
view.addSubview(imageView)
self.imageView = imageView
}
#objc func shareButtonTapped() { UIApplication.share("Text to share") }
#objc func imageViewTapped() {
guard let image = imageView?.image else { return }
UIApplication.share(image)
}
#objc func shareCombinedData() {
guard let image = imageView?.image, let url = URL(string: "http://google.com") else { return }
let data = ["Text, Image and url", image, url] as [Any]
UIApplication.share(data)
}
}
Sample result
I develop #onemillion 's answer:) You can use this for Swift 3
override func viewDidLoad() {
super.viewDidLoad()
share(message: "selam", link: "htttp://google.com")
}
func share(message: String, link: String) {
if let link = NSURL(string: link) {
let objectsToShare = [message,link] as [Any]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
self.present(activityVC, animated: true, completion: nil)
}
}
UPDATED FOR SWIFT 3.0
// first function to add the button to your navigation bar
func addingNavBarBtn () {
// setting button's image
let comunicateImage = UIImage(named: "NavfShare")
let comunicateBtn = UIBarButtonItem(image: comunicateImage, style: .plain, target: self, action: #selector(shareButtonPressed))
comunicateBtn.tintColor = UIColor.white
self.navigationItem.rightBarButtonItem = comunicateBtn
}
//setting button's action
func shareButtonPressed(){
//checking the object and the link you want to share
let urlString = "https://www.google.com"
let linkToShare = [urlString!]
let activityController = UIActivityViewController(activityItems: linkToShare, applicationActivities: nil)
self.present(activityController, animated: true, completion: nil)
}
let items = ["Your Sharing Content"];
let activity = UIActivityViewController(activityItems: items, applicationActivities: nil);
self.present(activity, animated: true, completion: nil)
Improving on JP Aquino's code for Swift 5 for share action rightBarButtonItem on the Navigation Controller.
In viewDidLoad of your ViewController, do this:
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(shareBarButtonItemClicked(_:)))
Then implement the shareBarButtonItemClicked method as below somewhere inside your ViewController:
#objc func shareBarButtonItemClicked(_ sender: UIBarButtonItem) {
// Text to share with other apps
let textToShare = String(describing: "My awesome app")
// URL, and or, and image to share with other apps
guard let myAppURLToShare = URL(string: "http://itunes.apple.com/app/idXXXXXXXXX"), let image = UIImage(named: "image.jpg") else {
return
}
let items = [textToShare, myAppURLToShare, image] as [Any]
let avc = UIActivityViewController(activityItems: items, applicationActivities: nil)
//Apps to exclude sharing to
avc.excludedActivityTypes = [
UIActivityType.airDrop,
UIActivityType.print,
UIActivityType.saveToCameraRoll,
UIActivityType.addToReadingList
]
//If user on iPad
if UIDevice.current.userInterfaceIdiom == .pad {
if avc.responds(to: #selector(getter: UIViewController.popoverPresentationController)) {
avc.popoverPresentationController?.barButtonItem = sender
}
}
//Present the shareView on iPhone
self.present(avc, animated: true, completion: nil)
}
Happy coding!
#IBAction func shareButtonAction(_ sender: UIButton) {
let activityVC = UIActivityViewController(activityItems: ["Whatever you want to share"], applicationActivities: nil)
activityVC.popoverPresentationController?.sourceView = sender
present(activityVC, animated: true, completion: nil)
activityVC.completionWithItemsHandler = { (activityType, completed:Bool, returnedItems:[Any]?, error: Error?) in
if completed {
self.dismiss(animated: true, completion: nil)
}
}
}
IOS 16
As of IOS 16 there is now a sharelink button, just pass in your link as the link.
#available(iOS 16.0, *)
ShareLink(item: link) {
Label("Click To Share", systemImage: "swift")
}
I started my search by wanting to know how I could share to other apps in iOS. I discovered that two important ways are
UIActivityViewController
UIDocumentInteractionController
These and other methods are compared in this SO answer.
Often when I am learning a new concept I like to see a basic example to get me started. Once I get something basic set up I can modify it how I like later.
There are many SO questions related to UIActivityViewController, but I couldn't find any that were just asking for a simple example. Since I just learned how to do this, I will provide my own answer below. Feel free to add a better one (or an Objective-C version).
UIActivityViewController Example Project
Set up your storyboard with two buttons and hook them up to your view controller (see code below).
Add an image to your Assets.xcassets. I called mine "lion".
Code
import UIKit
class ViewController: UIViewController {
// share text
#IBAction func shareTextButton(_ sender: UIButton) {
// text to share
let text = "This is some text that I want to share."
// set up activity view controller
let textToShare = [ text ]
let activityViewController = UIActivityViewController(activityItems: textToShare, applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view // so that iPads won't crash
// exclude some activity types from the list (optional)
activityViewController.excludedActivityTypes = [ UIActivity.ActivityType.airDrop, UIActivity.ActivityType.postToFacebook ]
// present the view controller
self.present(activityViewController, animated: true, completion: nil)
}
// share image
#IBAction func shareImageButton(_ sender: UIButton) {
// image to share
let image = UIImage(named: "Image")
// set up activity view controller
let imageToShare = [ image! ]
let activityViewController = UIActivityViewController(activityItems: imageToShare, applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view // so that iPads won't crash
// exclude some activity types from the list (optional)
activityViewController.excludedActivityTypes = [ UIActivity.ActivityType.airDrop, UIActivity.ActivityType.postToFacebook ]
// present the view controller
self.present(activityViewController, animated: true, completion: nil)
}
}
Result
Clicking "Share some text" gives result on the left and clicking "Share an image" gives the result on the right.
Notes
I retested this with iOS 11 and Swift 4. I had to run it a couple times in the simulator before it worked because it was timing out. This may be because my computer is slow.
If you wish to hide some of these choices, you can do that with excludedActivityTypes as shown in the code above.
Not including the popoverPresentationController?.sourceView line will cause your app to crash when run on an iPad.
This does not allow you to share text or images to other apps. You probably want UIDocumentInteractionController for that.
See also
Add sharing to your Swift app via UIActivityViewController
UIActivity​View​Controller from NSHipster
UIActivityViewController documentation
Share extension documentation
comparison with UIDocumentInteractionController
Share : Text
#IBAction func shareOnlyText(_ sender: UIButton) {
let text = "This is the text....."
let textShare = [ text ]
let activityViewController = UIActivityViewController(activityItems: textShare , applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view
self.present(activityViewController, animated: true, completion: nil)
}
}
Share : Image
#IBAction func shareOnlyImage(_ sender: UIButton) {
let image = UIImage(named: "Product")
let imageShare = [ image! ]
let activityViewController = UIActivityViewController(activityItems: imageShare , applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view
self.present(activityViewController, animated: true, completion: nil)
}
Share : Text - Image - URL
#IBAction func shareAll(_ sender: UIButton) {
let text = "This is the text...."
let image = UIImage(named: "Product")
let myWebsite = NSURL(string:"https://stackoverflow.com/users/4600136/mr-javed-multani?tab=profile")
let shareAll= [text , image! , myWebsite]
let activityViewController = UIActivityViewController(activityItems: shareAll, applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view
self.present(activityViewController, animated: true, completion: nil)
}
Just as a note you can also use this for iPads:
activityViewController.popoverPresentationController?.sourceView = sender
So the popover pops from the sender (the button in that case).
I found this to work flawlessly if you want to share whole screen.
#IBAction func shareButton(_ sender: Any) {
let bounds = UIScreen.main.bounds
UIGraphicsBeginImageContextWithOptions(bounds.size, true, 0.0)
self.view.drawHierarchy(in: bounds, afterScreenUpdates: false)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let activityViewController = UIActivityViewController(activityItems: [img!], applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view
self.present(activityViewController, animated: true, completion: nil)
}
I've used the implementation above and just now I came to know that it doesn't work on iPad running iOS 13.
I had to add these lines before present() call in order to make it work
//avoiding to crash on iPad
if let popoverController = activityViewController.popoverPresentationController {
popoverController.sourceRect = CGRect(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height / 2, width: 0, height: 0)
popoverController.sourceView = self.view
popoverController.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0)
}
That's how it works for me
func shareData(_ dataToShare: [Any]){
let activityViewController = UIActivityViewController(activityItems: dataToShare, applicationActivities: nil)
//exclude some activity types from the list (optional)
//activityViewController.excludedActivityTypes = [
//UIActivity.ActivityType.postToFacebook
//]
//avoiding to crash on iPad
if let popoverController = activityViewController.popoverPresentationController {
popoverController.sourceRect = CGRect(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height / 2, width: 0, height: 0)
popoverController.sourceView = self.view
popoverController.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0)
}
self.present(activityViewController, animated: true, completion: nil)
}
You may use the following functions which I wrote in one of my helper class in a project.
just call
showShareActivity(msg:"message", image: nil, url: nil, sourceRect: nil)
and it will work for both iPhone and iPad. If you pass any view's CGRect value by sourceRect it will also shows a little arrow in iPad.
func topViewController()-> UIViewController{
var topViewController:UIViewController = UIApplication.shared.keyWindow!.rootViewController!
while ((topViewController.presentedViewController) != nil) {
topViewController = topViewController.presentedViewController!;
}
return topViewController
}
func showShareActivity(msg:String?, image:UIImage?, url:String?, sourceRect:CGRect?){
var objectsToShare = [AnyObject]()
if let url = url {
objectsToShare = [url as AnyObject]
}
if let image = image {
objectsToShare = [image as AnyObject]
}
if let msg = msg {
objectsToShare = [msg as AnyObject]
}
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityVC.modalPresentationStyle = .popover
activityVC.popoverPresentationController?.sourceView = topViewController().view
if let sourceRect = sourceRect {
activityVC.popoverPresentationController?.sourceRect = sourceRect
}
topViewController().present(activityVC, animated: true, completion: nil)
}
iOS share text or image
present UIActivityViewController
let controller = UIActivityViewController(activityItems: [someObject], applicationActivities: nil) //someObject can be UIImage, NSURL, String... iOS decide how to handle it properly
controller.popoverPresentationController?.sourceView = self.view
controller.completionWithItemsHandler = {
(
activityType: UIActivity.ActivityType?,
completed: Bool,
arrayReturnedItems: [Any]?,
error: Error?
) in
if let error = error {
//error occured
return
}
if completed {
if let activityType = activityType {
switch activityType {
case .saveToCameraRoll:
break
case .copyToPasteboard:
break
case .addToReadingList:
break
case .airDrop:
break
default:
//all others
break
}
}
} else {
//Cancel
}
}
self.present(controller, animated: true)
If you are going to save image into library add NSPhotoLibraryAddUsageDescription into app's .plist file or you get runtime error
This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSPhotoLibraryAddUsageDescription key with a string value explaining to the user how the app uses this data.
or just exclude this opportunity:
controller.excludedActivityTypes = [.saveToCameraRoll]
variant with completionWithItemsHandler which can helps to add post logic or handle errors.
For example I run into next error when saving UIImage into Photo library in a corresponding handler:
Error Domain=ALAssetsLibraryErrorDomain Code=-1 "Unknown error" UserInfo={NSLocalizedDescription=Unknown error, NSUnderlyingError=0x600003f85110 {Error Domain=PHPhotosErrorDomain Code=3303 "(null)"}}
As figured out I tried to save CIImage. As a variant you can convert it to CGImage
let context = CIContext()
guard let cgImage = context.createCGImage(output, from: output.extent) else { return nil }
return UIImage(cgImage: cgImage)
I am making a share function in my game and I have the code and it works fine on iPhone but when I test it on a iPad, when I tap the share button the app crashes. I am using the following code for the share button
let textToShare = "Check out this website!"
if let myWebsite = NSURL(string: "http://www.apple.com/") {
let objectsToShare = [textToShare, myWebsite]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
self.view?.window?.rootViewController?.presentViewController(activityVC, animated: true, completion: nil)
}
The UIActivityViewController's has non-null popoverPresentationController property when running on iPad. So, try below.
if let wPPC = activityVC.popoverPresentationController {
wPPC.sourceView = some view
// or
wPPC.barButtonItem = some bar button item
}
presentViewController( activityVC, animated: true, completion: nil )
Building on #Satachito's answer: As the sourceView you can create an (invisible) CGRect at the place the popup should point to, and set the arrow in that direction:
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
if UIDevice.current.userInterfaceIdiom == .pad {
activityVC.popoverPresentationController?.sourceView = UIApplication.shared.windows.first
activityVC.popoverPresentationController?.sourceRect = CGRect(x: 0, y: 0, width: 300, height: 350)
activityVC.popoverPresentationController?.permittedArrowDirections = [.left]
}
UIApplication.shared.windows.first?.rootViewController?.present(activityVC, animated: true, completion: nil)
The popoverPresentationController sourceView needs to be set to current view.
let activityVC = UIActivityViewController(activityItems: [quoteController.attributedString, view.screenShot()], applicationActivities: [])
present(activityVC, animated: true)
activityVC.popoverPresentationController?.sourceView = view
I have the following code below to toggle UIActivityViewController
#IBAction func shareButtonClicked(sender: UIButton)
{
let textToShare = "Text"
if let myWebsite = NSURL(string: "http://www.example.com/")
{
let objectsToShare = [textToShare, myWebsite]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
self.presentViewController(activityVC, animated: true, completion: nil)
}
}
But it crashes on iPad. How do I present it modally in Swift?
It will crash because in iPad you can not present UIActivityViewController as like in iPhone.
You need to present it in a popover as per the design guidelines suggested by Apple.
The below code will demonstrate how to show present it in a UIPopoverPresentationController.
#IBAction func shareButtonClicked(sender: UIButton)
{
let textToShare = "Text"
if let myWebsite = NSURL(string: "http://www.example.com/")
{
let objectsToShare = [textToShare, myWebsite]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
var nav = UINavigationController(rootViewController: activityVC)
nav.modalPresentationStyle = UIModalPresentationStyle.Popover
var popover = nav.popoverPresentationController as UIPopoverPresentationController!
activityVC.preferredContentSize = CGSizeMake(500,600)
popover.sourceView = self.view
popover.sourceRect = CGRectMake(100,100,0,0)
self.presentViewController(nav, animated: true, completion: nil)
}
}
Note: Extend your view controller with UIPopoverPresentationController delegate.
For example:
class MyViewController : UIViewController, UIPopoverPresentationControllerDelegate {
//........
}