How to print a UIWebView via UIActivityViewController? - ios

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

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")
}

QLPreviewController change title?

Is it possible to change the title of an item in an QLPreviewController?
I've already tried with:
Subclassing QLPreviewController
Add
override func viewDidAppear(_ animated: Bool) {
self.navigationController?.navigationBar.topItem?.title = "Bericht"
}
But you see the title only for maybe 1/4 second.
Any ideas?
If you need to display a different title other than the lastPathComponent from your url, you can subclass QLPreviewItem and provide your own title implementing the optional property:
Instance Property Declaration:
var previewItemTitle: String? { get }
The title to display for the preview item.
If you do not implement a getter method for this property, or if your
method returns nil, QuickLook examines the URL or content of the item
being previewed to determine an appropriate title for display to the
user. Return a non-nil value for this property to provide a custom
title.
protocol QLPreviewItem : NSObjectProtocol
Description
The QLPreviewItem protocol defines properties you implement to make your
application’s content visible in a QuickLook preview
(QLPreviewController in iOS or QLPreviewPanel in macOS). The methods
in this protocol are also declared as a category on the NSURL class.
As a result, you can use NSURL objects directly as preview
items—provided that you want to use the default titles of those items.
A default title is the last path component of an item’s URL. If you
want to supply your own preview item titles, create your own preview
item objects that adopt this protocol.
First Subclass QLPreviewItem:
import UIKit
import QuickLook
class PreviewItem: NSObject, QLPreviewItem {
var previewItemURL: URL?
var previewItemTitle: String?
init(url: URL? = nil, title: String? = nil) {
previewItemURL = url
previewItemTitle = title
}
}
Then in your controller you return the QLPreviewItem instead of the URL:
Xcode 11 • Swift 5.1
import UIKit
import QuickLook
class ViewController: UIViewController, QLPreviewControllerDelegate, QLPreviewControllerDataSource {
var previewItems: [PreviewItem] = []
override func viewDidLoad() {
super.viewDidLoad()
previewItems = [
.init(url: Bundle.main.url(forResource: "your file 1", withExtension: "ext"),
title: "Custom Title 1"),
.init(url: Bundle.main.url(forResource: "your file 2", withExtension: "ext"),
title: "Custom Title 2"),
]
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
quickLook()
}
func numberOfPreviewItems(in controller: QLPreviewController) -> Int { previewItems.count }
func quickLook(at index: Int = 0) {
let controller = QLPreviewController()
controller.delegate = self
controller.dataSource = self
controller.currentPreviewItemIndex = index
present(controller, animated: true)
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem { previewItems[index] }
}

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

SFSafariViewController: how to provide custom activities?

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.

Resources