SFSafariViewController: how to provide custom activities? - ios

The WWDC session of Safari View Controller mentioned that apps could provide custom activities through the method func safariViewController(controller: SFSafariViewController, activityItemsForURL URL: NSURL, title: String?) -> [UIActivity] of the delegate SFSafariViewControllerDelegate. I have tried to implement this method, but it is not called after I present the SFSafariViewCntroller. I also implemented another optional method of that delegate, func safariViewControllerDidFinish(_: SFSafariViewController), which does get called. I tried to add the "#objc" keyword to my method (required by some other protocols), but it seems not to change anything.
I am wondering what could go wrong.
Thanks!

Here's the example code for your reference. In your main view:
func safariViewController(controler: SFSafariViewController, activityItemsForURL: NSURL, title: String?) -> [UIActivity] {
//global variable for the url to be shared
webPageUrl = activityItemsForURL.absoluteString
//global variable for the title to be shared
webPageTitle = title!
let wcActivity = WeChatActivity()
let wcMoment = WeChatMoment()
return [wcActivity, wcMoment]
}
Custom activities 1
import UIKit
class WeChatActivity : UIActivity{
override init() {
self.text = ""
}
var text:String?
override func activityType()-> String {
return "WeChat"
}
override func activityImage()-> UIImage?
{
return UIImage(named: "WeChat")!
}
override func activityTitle() -> String
{
return "微信好友"
}
override class func activityCategory() -> UIActivityCategory{
return UIActivityCategory.Action
//you can change to .Share and it'll appear in the share line
}
func getURLFromMessage(message:String)-> NSURL
{
var url = "whatsapp://"
if (message != "")
{
url = "\(url)send?text=\(message)"
}
return NSURL(string: url)!
}
override func canPerformWithActivityItems(activityItems: [AnyObject]) -> Bool {
return true;
}
override func performActivity() {
shareToWeChat("ftcweixin://?url=\(webPageUrl)&title=\(webPageTitle)&description=\(webPageDescription)&img=\(webPageImageIcon)&to=chat")
}
}
Custom Activity 2:
import UIKit
class WeChatMoment : UIActivity{
override init() {
self.text = ""
}
var text:String?
override func activityType()-> String {
return "WeChatMoment"
}
override func activityImage()-> UIImage?
{
return UIImage(named: "Moment")!
}
override func activityTitle() -> String
{
return "微信朋友圈"
}
override class func activityCategory() -> UIActivityCategory{
return UIActivityCategory.Action
}
func getURLFromMessage(message:String)-> NSURL
{
var url = "whatsapp://"
if (message != "")
{
url = "\(url)send?text=\(message)"
}
return NSURL(string: url)!
}
override func canPerformWithActivityItems(activityItems: [AnyObject]) -> Bool {
return true;
}
override func performActivity() {
shareToWeChat("ftcweixin://?url=\(webPageUrl)&title=\(webPageTitle)&description=\(webPageDescription)&img=\(webPageImageIcon)&to=moment")
}
}
You'll be able to see the two new icons in the action line of the action sheet. You can also change it to appear in the share line, as explained in the code.
One final note, there are pitfalls for WeChat sharing on Safari View, as WeChat doesn't conform to Safari's sharing standard. You can click the WeChat Share icon and WeChat will be able to share. But you can only get the page title and url in Safari View's page, unlike with WKWebView where you can get everything using evaluateJavaScript. So you'll need to get the share image and description (for sharing to friends) from other places.

Related

Access data / functions from several view controllers

I am trying to build an app with several screens. Every screen should have different buttons which all call one function.
My problem is that I do not understand how to call one function from different view controllers with input parameters.
Also I want to have another variable defined accessible and changeable from every view controller.
This is what I kind of want my code to be:
import UIKit
var address = "address"
public func makeRequest(Command: String){
let url = URL(address + Command)
print(url)
}
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let command = "command"
makeRequest(Command: command)
}
}
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("address")
address = "address2"
}
}
If all classes are view controllers put the method in an extension of UIViewController and the address constant in a struct
struct Constant {
static let address = "address"
}
extension UIViewController {
public func makeRequest(command: String) -> URL? {
guard let url = URL(string: Constant.address + command) else { return nil }
print(url)
return url
}
}
extension UIViewController{
func getName(name: String)-> String{
print("hai \(name)")
return name
}
}
you can write a extension for Viewcontroller and try to call the method like this. if you write the extension for viewcontroller you can directly call them with its reference
self.getName(name:"Raghav")

How can you customize UIActivityViewController to ONLY include custom UIActivity types?

I'm trying to customize the native ios UIActivityViewController so it will restrict every UIActivity except for the custom UIActivity types that I initialize the UIActivityViewController with.
Essentially, I only want the user to see my four custom UIActivitys.
let items = ["Hello world"]
let activitySheet = CustomUIActivityViewController(
activityItems: items,
applicationActivities: [
CustomMailUIActivity(),
CustomMessagesUIActivity(),
CustomTwitterUIActivity(),
CustomFacebookUIActivity()
]
)
I understand that you can use activitySheet.excludedActivityTypes = [] to exclude types that you do not need, however you are not able to exclude third party applications such as Slack.
I was wondering if there was any way to get around that and ONLY include custom applicationActivies. It would also be great to remove the "More" button that appears on the share sheet as well. Any help would be greatly appreciated.
This is what I'm trying to achieve.
screenshot
My colleague was able to figure this out. For anyone wondering how to display only the applicationActivities (custom activities) on the share sheet, this is how I did it.
For activityItems rather than creating an array of strings, create an array of your custom data objects. Then override the canPerform(withActivityItems activityItems: [Any]) method in your custom UIActivity (subclass of UIActivity) to return true if activityItems[o] is CustomItem. Since your activityItems are custom, system will not display other apps on the share sheet. That's it.
In the example below, only your CustomUIActivity will be displayed on the share sheet.
let items = [CustomItem("Hello world")]
let activitySheet = UIActivityViewController(
activityItems: items,
applicationActivities: [
CustomUIActivity()
]
)
class CustomItem {
let value: String
required init(value: String) {
self.value = value
}
func getValue() -> String {
return self.value
}
}
#objc(CustomUIActivity)
class CustomUIActivity: UIActivity {
override class var activityCategory: UIActivity.Category {
return .share
}
override var activityType: UIActivity.ActivityType? {
return .customuiactivity
}
override var activityTitle: String? {
return "Custom"
}
override var activityImage: UIImage? {
return UIImage(named: "custom-icon")
}
override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
if activityItems.count == 1 && activityItems[0] is CustomItem {
return true
}
return false
}
var textToShare: String?
override func prepare(withActivityItems activityItems: [Any]) {
if let activityItem = activityItems.first as? CustomItem {
self.textToShare = activityItem.getValue
}
}
override func perform() {
// perform your custom activity with `textToShare`
activityDidFinish(true)
}
}
extension UIActivity.ActivityType {
static let customuiactivity =
UIActivity.ActivityType("com.ceylonese.mobile.customuiactivity")
}

Add Stickers Background Color With Swift

I just made my first iMessage Stickers with Swift but I can't change the background color in the box where my stickers displays. I don't want it to be white. How can I do this? and where do I put the code?
I created a new Swift file under MessagesExtensions and used this code. I added all my images and connected them to the class in Storyboard. I have tried all type of codes that I have found online including this:
self.view.backgroundColor = UIColor.black
self.view.backgroundColor = UIColor(patternImage: UIImage(named: "Pattern")!)
I am a newborn to this and maybe I did not add the code in the correct place?
I did not change the MessagesViewController.Swift code but am I supposed to?
Anyways, here is the code that I used:
var stickers :[MSSticker]!
var stickerNames = ["Brokkoli8","Carrot2","Eggplant4","Gresskar9","Lettuce6","Mushroom3","Onion10","Paprika2","Potato5","Tomato7"]
override func viewDidLoad() {
super.viewDidLoad()
self.stickers = [MSSticker]()
self.stickers = loadStickers()
}
override func numberOfStickers(in stickerBrowserView: MSStickerBrowserView) -> Int {
return self.stickers.count
}
override func stickerBrowserView(_ stickerBrowserView: MSStickerBrowserView, stickerAt index: Int) -> MSSticker {
return self.stickers[index]
}
private func loadStickers() -> [MSSticker] {
var stickers = [MSSticker]()
for stickerName in self.stickerNames {
guard let url = Bundle.main.url(forResource: stickerName,withExtension: "png") else {
fatalError("resource does not exist")
}
let sticker = try! MSSticker (contentsOfFileURL: url,
localizedDescription: stickerName)
stickers.append(sticker)
}
return stickers
}
}
check out wwdc presentation on this: https://developer.apple.com/videos/play/wwdc2016/204/
particularly start on 20 min.
in short: you need to subclass MSStickerBrowserView on which you can set backgroundColor

How to print a UIWebView via UIActivityViewController?

I have a view controller containing a UIWebView and a toolbar with an action/share button. This initializes and presents a UIActivityViewController object. Depending on whether I supply the activityItems parameter with either the web view's URL or the URL's corresponding absoluteString, different actions are offered, but the Print option is never shown (nor offered in the "more" section).
I do know how to print the web view contents explicitly using UIPrintInfo and UIPrintInteractionController, but that would be a separate toolbar button whereas I want to simply include the system's Print option into the activity button row. I assume printing a web view does not need any explicit coding.
What can I do?
You can create Custom Activity For UIActivityCotnroller like this,
import UIKit
protocol CustomActivityDelegate : NSObjectProtocol
{
func performActionCompletion(actvity: CustomActivity)
}
class CustomActivity: UIActivity {
var delegate: CustomActivityDelegate?
override class var activityCategory: UIActivityCategory {
return .action
}
override var activityType: UIActivityType? {
guard let bundleId = Bundle.main.bundleIdentifier else {return nil}
return UIActivityType(rawValue: bundleId + "\(self.classForCoder)")
}
override var activityTitle: String? {
return "You title"
}
override var activityImage: UIImage? {
return <Your activity image >
}
override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
return true
}
override func prepare(withActivityItems activityItems: [Any]) {
//
}
override func perform() {
self.delegate?.performActionCompletion(actvity: self)
activityDidFinish(true)
}
}
You can initialize this activity some thing like this
let customActivity = CustomActivity()
customActivity.delegate = self
And you can add this custom activity while preparing UIActivityController
let activityViewController : UIActivityViewController = UIActivityViewController(activityItems: [customActivity], applicationActivities: nil)
and you will also need to implement the call back method
func performActionCompletion(actvity: CustomActivity)
{
//Perform you task
}
Note : This is just pseudo code, might contain error or syntax problems

Trying to export to csv but how do i implement from delegate?

[new to swift] I testing this function to export some simple file
#IBAction func exportFile(delegate: UIDocumentInteractionControllerDelegate) {
print("export csv")
let fileName = tmpDir.stringByAppendingPathComponent("myFile.csv")
let url: NSURL! = NSURL(fileURLWithPath: fileName)
if url != nil {
let docController = UIDocumentInteractionController(URL: url)
docController.UTI = "public.comma-separated-values-text"
docController.delegate = delegate
docController.presentPreviewAnimated(true)
}
}
// Return the view controller from which the UIDocumentInteractionController will present itself.
func documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController)-> UIViewController {
return self
}
But when i clicked the export button i am getting the message
UIDocumentInteractionController delegate must implement documentInteractionControllerViewControllerForPreview: to allow preview
I thought
class ViewController: UIViewController, UIDocumentInteractionControllerDelegate {
Would be sufficient?
I tried
Self.documentInteractionControllerViewForPreview(docController)
[edit]
turned out i had made the following mistake
docController.delegate = self//delegate
You need to implement following delegate methods. Delegates are the callbacks from the service provider to service consumer to be prepared for the action that is about to occur. On some occasions you must provide details (called data source) in order to utilise the said functionality.
func documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController!) -> UIViewController! {
return self
}
func documentInteractionControllerViewForPreview(controller: UIDocumentInteractionController!) -> UIView! {
return self.view
}
func documentInteractionControllerRectForPreview(controller: UIDocumentInteractionController!) -> CGRect {
return self.view.frame
}
Read through how delegation works. Also, take a look at your specific case here.
For Swift 3.0
First extend your class
UIDocumentInteractionControllerDelegate
Then only implement the following method
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
return self
}
Calling the Pdf Viewer
func MyViewDocumentsmethod(){
let controladorDoc = UIDocumentInteractionController(url: PdfUrl! as URL)
controladorDoc.delegate = self
controladorDoc.presentPreview(animated: true)
}
This should render the following

Resources