17

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)

    }
rmaddy
  • 314,917
  • 42
  • 532
  • 579
Tom Coomer
  • 6,227
  • 12
  • 45
  • 82
  • UIACtivityViewController support ios 8 or above, While compile is it show any warning message in Xcode 7 – Manikandan D Oct 01 '15 at 14:17
  • Hello. No there is no warning message unfortunately. It works on iPhone, just not iPad. – Tom Coomer Oct 01 '15 at 14:31
  • i think you dont need to show the activity controller on a popup, what happens if you handle it like iphone? – Björn Ro Oct 01 '15 at 14:56
  • 1
    When I just use the iPhone part it crashes and says this: `UIPopoverPresentationController (<_UIAlertControllerActionSheetRegularPresentationController: 0x135db4490>) should have a non-nil sourceView or barButtonItem set before the presentation occurs.` – Tom Coomer Oct 01 '15 at 18:49

7 Answers7

21

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)
Mattia Righetti
  • 1,265
  • 1
  • 18
  • 31
Tom Coomer
  • 6,227
  • 12
  • 45
  • 82
20

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.

Donovan Marsh
  • 279
  • 2
  • 9
13

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)
Aayushi
  • 787
  • 10
  • 14
2

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:

enter image description here

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:

enter image description here

Portrait:

enter image description here

Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
0

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.

Madiyar
  • 779
  • 11
  • 7
0

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.

Maverick
  • 11
  • 1
0

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)
}
Coder ACJHP
  • 1,940
  • 1
  • 20
  • 34