21

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)
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
Loanb222
  • 841
  • 1
  • 11
  • 29

5 Answers5

34

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 )
Satachito
  • 5,838
  • 36
  • 43
3

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)
Jannik Arndt
  • 447
  • 5
  • 13
  • The `windows` property of `UIApplication` has been deprecated since iOS 15.0 and really shouldn't be used after iOS 13.0 when using scenes. – HangarRash Jul 03 '23 at 17:34
  • Also note that as of iOS 13.2, `sourceRect` doesn't need to be set unless you want it to be some specific subset of `sourceView`. – HangarRash Jul 03 '23 at 17:35
-1

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

hectorsvill
  • 681
  • 8
  • 7
-1

iOS 16 ready-to-use example based on previous examples, without using deprecated windows

func shareAppIntent() {
    let subject = "Share with Friends"
    let extraText = "https://apps.apple.com/..."
    
    guard let mainWindowScene = UIApplication.shared.connectedScenes
            .compactMap({ $0 as? UIWindowScene })
            .first(where: { $0.activationState == .foregroundActive }),
          let mainWindow = mainWindowScene.windows.first,
          let rootViewController = mainWindow.rootViewController else {
        return
    }
    
    if let url = URL(string: extraText) {
        let activityViewController = UIActivityViewController(activityItems: [subject, url], applicationActivities: nil)
        
        // iPad needs extra context, otherwise it will crash
        if UIDevice.current.userInterfaceIdiom == .pad {
            activityViewController.popoverPresentationController?.sourceView = mainWindow
            activityViewController.popoverPresentationController?.sourceRect = CGRect(x: 0, y: 0, width: 300, height: 350)
            activityViewController.popoverPresentationController?.permittedArrowDirections = [.left]
        }
        
        rootViewController.present(activityViewController, animated: true, completion: nil)
    }
}
  • 1
    1) The question has nothing to do with `windows` being deprecated. 2) This code has a serious flaw. If running in split screen mode on an iPad, the code can easily choose the wrong scene and display the activity controller on the wrong half of the screen. 3) This will also fail if the chosen root view controller is already presenting some other view controller. – HangarRash Jul 03 '23 at 17:28
  • Although the initial question has nothing to do with `Windows`, it is appropriate to give an up-to-date working example. And as for your complaining about the popup displaying on the wrong half of the screen, it does indeed display as a sheet as on the iPhone when the iPad screen is split. Your reference to the root view controller was correct, even though it occurs extremely rarely (it never occurred in my extensive testing). I edited the answer appropriatly – Taltus1337 Jul 06 '23 at 08:58
  • But on the iPad your app could be running two scenes side by side and your code could show the controller on the wrong half of the screen. – HangarRash Jul 06 '23 at 15:47
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 12 '23 at 05:20
-1

In iPad SheetVC is not present. So you need to add a source View for a popover controller

@objc private func shareButtonTapped(_ sender: UIButton) {
        let image = UIImage(named: "image")
        let activityItem: [AnyObject] = [image as AnyObject]
        let activityViewController = UIActivityViewController(
            activityItems: activityItem as [AnyObject],
            applicationActivities: nil
        )
        if let popoverController = activityViewController.popoverPresentationController {
            popoverController.sourceView = sender
            popoverController.sourceRect = sender.bounds
        }
        present(activityViewController, animated: true, completion: nil)
    }