In July 2013 WhatsApp opened their URL schemes for our apps. I have sent text to Whatsapp from my application, but now I would like send a image. How send a image to Whatsapp?
I'm not sure how do it.
Thank you.
Per their documentation, you need to use UIDocumentInteractionController. To selectively display only Whatsapp in the document controller (it is presented to the user, at which point they can select Whatsapp to share to), follow their instructions:
Alternatively, if you want to show only WhatsApp in the application list (instead of WhatsApp plus any other public/*-conforming apps) you can specify a file of one of aforementioned types saved with the extension that is exclusive to WhatsApp:
images - «.wai» which is of type net.whatsapp.image
videos - «.wam» which is of type net.whatsapp.movie
audio files - «.waa» which is of type net.whatsapp.audio
You need to save the image to disk, and then create a UIDocumentInteractionController with that file URL.
Here is some example code:
_documentController = [UIDocumentInteractionController interactionControllerWithURL:_imageFileURL];
_documentController.delegate = self;
_documentController.UTI = #"net.whatsapp.image";
[_documentController presentOpenInMenuFromRect:CGRectZero inView:self.view animated:YES]
Here is a finally working solution for Swift. The method is triggered by a button. you can find some more explanations here
import UIKit
class ShareToWhatsappViewController: UIViewController, UIDocumentInteractionControllerDelegate {
var documentController: UIDocumentInteractionController!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func shareToWhatsapp(sender: UIButton) {
let image = UIImage(named: "my_image") // replace that with your UIImage
let filename = "myimage.wai"
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, false)[0] as! NSString
let destinationPath = documentsPath.stringByAppendingString("/" + filename).stringByExpandingTildeInPath
UIImagePNGRepresentation(image).writeToFile(destinationPath, atomically: false)
let fileUrl = NSURL(fileURLWithPath: destinationPath)! as NSURL
documentController = UIDocumentInteractionController(URL: fileUrl)
documentController.delegate = self
documentController.UTI = "net.whatsapp.image"
documentController.presentOpenInMenuFromRect(CGRectZero, inView: self.view, animated: false)
}
}
Related
I have been working on ios app which now i am facing some problems. I want to load some files like docx, xls using UIWebView. It gets me white screen on my actual device.
#IBOutlet weak var fileServerWebView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
if let path = Bundle.main.path(forResource: "easychair", ofType: "docx"){
let targetUrl: URL = URL(fileURLWithPath: path)
let request = URLRequest(url: targetUrl as URL)
fileServerWebView.delegate = self
fileServerWebView.scalesPageToFit = true
fileServerWebView.loadRequest(request)
}else{
print("cannot get path")
}
// Do any additional setup after loading the view.
}
That is code that i tried to load that file from local. Thank you for your help.
I'm working on my first app, and it involves searching for a specific string of text from a large amount of text on a website. I want to be able to search for a specific string and highlight all instances, and as the user types more, it will automatically update the highlighted occurrences. Similar to a find function in any browser.
I found this question on here where they reference using UIWebViewSearch.js to implement this, but I'm not sure how to add that file to my project, or how to use it.
I've added a search bar to my app so far, but haven't done anything with it. Want to use this function along with the search bar to search for the text.
Here is my ViewController code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var webView: UIWebView!
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// Place custom HTML onto the screen
let myURL = Bundle.main.url(forResource: "myHTML", withExtension: "html")
let requestObj = URLRequest(url: myURL!)
webView.loadRequest(requestObj)
// Code that provides a string containing
// JS code, ready to add to a webview.
if let path = Bundle.main.path(forResource: "UIWebViewSearch", ofType: "js"),
let jsString = try? String(contentsOfFile: path, encoding: .utf8) {
print(jsString)
} // end if
func searchBarSearchButtonClicked() {
let startSearch = "uiWebview_HighlightAllOccurencesOfString('\(str)')"
self.newsWebView .stringByEvaluatingJavaScript(from: startSearch)
}
} // end of viewDidLoad
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
} // end of didReceiveMemoryWarning
} // end of class ViewController
As #jon-saw says: Welcome to StackOverflow :)
I'll just answer how you can add the JavaScript file to your project...that should get things started right :)
Add a File
You can add a file to Xcode in several ways, but here's one.
In Xcode, in your project, navigate in the left hand Project navigator to where you would like to add your file.
Right click and select "New File..."
Select "Empty" and click "Next"
Name your file UIWebViewSearch.js and click "Create"
You now have an empty file called UIWebViewSearch.js in your project in which you can paste in content.
Use a File
Now you have the file on your project, so now you just need to refer to it. To do so you use Bundle which can load resources in your project for you.
If you look at the answer you referred to, you can see these lines of ObjC code in - (NSInteger)highlightAllOccurencesOfString:(NSString*)str
NSString *path = [[NSBundle mainBundle] pathForResource:#"UIWebViewSearch" ofType:#"js"];
NSString *jsCode = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
Translated to Swift that would look like this:
if let path = Bundle.main.path(forResource: "UIWebViewSearch", ofType: "js"),
let jsString = try? String(contentsOfFile: path, encoding: .utf8) {
print(jsString)
}
And that should give you a String containing all your JS code, ready to add to a WebView for instance.
Hope that gives you something to start with.
Update (after you added a comment to this post)
OK, so you say:
I added my ViewController code to better communicate where I'm at. Kind of confused on how to implement the second part of his code you're referring to. Also not sure how to call the search from the searchBar. Is there a method I need to call within 'searchBarSearchButtonClicked()' method? Just found that method on the apple developer documentation, don't know if it's the correct one, though.
Lets break it down into pieces, I'll start with your ViewController, as there are some problems in your viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// Place custom HTML onto the screen
let myURL = Bundle.main.url(forResource: "myHTML", withExtension: "html")
let requestObj = URLRequest(url: myURL!)
webView.loadRequest(requestObj)
// Code that provides a string containing
// JS code, ready to add to a webview.
if let path = Bundle.main.path(forResource: "UIWebViewSearch", ofType: "js"),
let jsString = try? String(contentsOfFile: path, encoding: .utf8) {
print(jsString)
} // end if
func searchBarSearchButtonClicked() {
let startSearch = "uiWebview_HighlightAllOccurencesOfString('\(str)')"
self.newsWebView .stringByEvaluatingJavaScript(from: startSearch)
}
}
You've added your searchBarSearchButtonClicked inside viewDidLoad, but it should be declared as a function by itself (we'll get back to it later).
Furthermore, as I wrote in the comments below:
...One part that is run when the view is loaded and which loads the JavaScript from the bundle.
So lets fix your viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// Place custom HTML onto the screen
let myURL = Bundle.main.url(forResource: "myHTML", withExtension: "html")
let requestObj = URLRequest(url: myURL!)
webView.loadRequest(requestObj)
// Code that provides a string containing
// JS code, ready to add to a webview.
if let path = Bundle.main.path(forResource: "UIWebViewSearch", ofType: "js"),
let jsString = try? String(contentsOfFile: path, encoding: .utf8) {
print(jsString)
//Use your jsString on the web view here.
newsWebView.stringByEvaluatingJavaScript(from: jsString)
}
}
As you can see, the searchBarSearchButtonClicked function has been removed and we now use out jsString on the newsWebView.
OK, next part:
Also not sure how to call the search from the searchBar. Is there a method I need to call within 'searchBarSearchButtonClicked()' method? Just found that method on the apple developer documentation, don't know if it's the correct one, though.
You have your searchBarSearchButtonClicked() method which you have found in the Apple Developer documentation, I'm guessing you're meaning this one
As you can see in the documentation (top bar), this function is from the UISearchBarDelete class.
Delegation is a design pattern used in many of Apples frameworks. It allows you to write some generic components and let "someone else" (the delegate) decide what to do with the information from the component (bad explanation I know).
Think of your UISearchBar for instance. If you were to implement a component that other developers could use as a search bar, how would you handle when the user searches for something? I'm thinking, how should your "customers" (the developers using your components) handle this? One way of doing this could be that the developers should subclass UISearchBar and override a "user did search method". You could also use NSNotifications and have the developer register for these notifications. Both of these methods are not pretty or clever.
Enter...the delegate.
UISearchBar has a UISearchBarDelegate and UISearchBarDelegate is just a protocol as you can see. Now the UISearchBar can ask its delegate for certain things, or inform the delegate about important events. So for instance, when the user taps the "Search" button UISearchBar can call delegate.searchBarSearchButtonClicked(self) which means "hey delegate, the user has clicked the search button on me...just thought you should know". And the delegate can then respond as it sees fit.
This means that any class can conform to UISearchBarDelegate and handle the various tasks as it suits in their current situation.
OK, long story, hope you get the gist of it and I think you should read a bit more on delegation as it is a pattern used all over in Apples frameworks :)
So, in your case, you have a UISearchBar, you should give that a delegate like so:
searchBar.delegate = self
And you need to tell the compiler that you intend to implement the UISearchBarDelegate protocol. The Swift convention is to do this in an extension after your ViewController, just to keep things separated:
extension ViewController: UISearchBarDelegate {
}
And then you must also implement the methods of UISearchBarDelegate that you are interested in listening to (note that some protocols have required methods, meaning that you MUST implement them but I don't think UISearchBar has (otherwise you'll find out when the app crashes the first time you run it :)).
So in your case, you mentioned searchBarSearchButtonPressed. As you can see it needs a UISearchBar as a parameter. So your function ends out looking like this:
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
guard let currentSearchText = searchBar.text else {
return
}
let startSearch = "uiWebview_HighlightAllOccurencesOfString('\(currentSearchText)')"
newsWebView.stringByEvaluatingJavaScript(from: startSearch)
}
So... you fetch the current text from your searchBar, add it as a parameter to your startSearch string and hand that startSearch string to your newsWebView.
The entire extensions ends out looking like this.
.... last part of your ViewController class
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
} // end of didReceiveMemoryWarning
} // end of class ViewController
extension ViewController: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
guard let currentSearchText = searchBar.text else {
return
}
let startSearch = "uiWebview_HighlightAllOccurencesOfString('\(currentSearchText)')"
newsWebView.stringByEvaluatingJavaScript(from: startSearch)
}
}
And your viewDidLoad ends out looking like this (you must add yourself as a delegate to the searchBar)
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self //delegate set up
// Do any additional setup after loading the view, typically from a nib.
// Place custom HTML onto the screen
let myURL = Bundle.main.url(forResource: "myHTML", withExtension: "html")
let requestObj = URLRequest(url: myURL!)
webView.loadRequest(requestObj)
// Code that provides a string containing
// JS code, ready to add to a webview.
if let path = Bundle.main.path(forResource: "UIWebViewSearch", ofType: "js"),
let jsString = try? String(contentsOfFile: path, encoding: .utf8) {
print(jsString)
//Use your jsString on the web view here.
newsWebView.stringByEvaluatingJavaScript(from: jsString)
}
}
Wow...that was a lot of writing...hope it helps you.
I'd like to be able to AirDrop a text file and an image at the same time using the UIActivityViewController. The code below works fine to send both file types via iMessage or eMail, but it fails when I try to use AirDrop. The code works fine for AirDropping 2 images or 2 text files, but not for one of each.
#IBAction func shareImage(_ sender: UIButton)
{
// can't seem to AirDrop a mixture of file types. ie. can send 2 images, or 2 data files, but not an image and a data file
let fileToSend: NSURL = NSURL(fileURLWithPath: dataFile!)
let image = imageView.image!
let objectsToShare = [fileToSend, image] as [Any]
let controller = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
controller.excludedActivityTypes = [UIActivityType.postToFacebook, UIActivityType.postToTwitter, UIActivityType.postToWeibo, UIActivityType.print, UIActivityType.copyToPasteboard, UIActivityType.assignToContact, UIActivityType.saveToCameraRoll, UIActivityType.postToFlickr, UIActivityType.postToTencentWeibo]
self.present(controller, animated: true, completion: nil)
}
AirDrop to an iOS device does not support sending multiple different types, but sending to a mac does. Until Apple changes that there is no code change that you can do to "fix" this.
I'm trying to read a QR code from a static image using Swift.
I can easily read it using a video source although it seems to be very different for images and I can't find too many resources online for this.
Any help appreciated, thanks.
You can make a great QRCode scanner using ZXingObjC. It's a barcode image processing library designed to be used on both iOS devices and in Mac applications. It scans from live video or from images in your photo library and supports all the major QRCode formats.
This is only to get you started in the right direction. You'll need more methods to set up the camera etc. ZXingObjC includes sample projects and there are camera set up solutions all over SO so it's pretty straight forward.
You'll need to install ZXingObjC pods pod 'ZXingObjC' as well as create a bridging-header.h file of course to be able to use the ZXingObjC library.
ViewController.swift
import UIKit
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var labelOutput: UILabel!
#IBOutlet weak var QRImage: UIImageView!
var imagePicker = UIImagePickerController()
// imagePicker delegate is itself (UIImagePickerController)
override func viewDidLoad() {
super.viewDidLoad()
imagePicker.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func scanQRCode(sender: AnyObject) {
imagePicker.sourceType = .PhotoLibrary
imagePicker.allowsEditing = false
presentViewController(imagePicker, animated: true, completion: nil)
}
// set up the picker
// initialize luminance source, scanning algorithm, decoding of bitmap, reader helpers, decoder
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let placeHolderImage:UIImage = info[UIImagePickerControllerOriginalImage] as! UIImage
QRImage.contentMode = .ScaleAspectFit
QRImage.image = placeHolderImage
dismissViewControllerAnimated(true, completion: nil)
let luminanceSource: ZXLuminanceSource = ZXCGImageLuminanceSource(CGImage: placeHolderImage.CGImage)
let binarizer = ZXHybridBinarizer(source: luminanceSource)
let bitmap = ZXBinaryBitmap(binarizer: binarizer)
let hints: ZXDecodeHints = ZXDecodeHints.hints() as! ZXDecodeHints
let QRReader = ZXMultiFormatReader()
// throw/do/catch and all that jazz
do {
let result = try QRReader.decode(bitmap, hints: hints)
labelOutput.text = result.text
} catch let err as NSError {
print(err)
}
}
// Conform to ZXCaptureDelegate
func captureResult(capture: ZXCapture!, result: ZXResult!) {
// do some stuff
return
}
}
One note: As of this post there is a known initializer error in the library's ZXParsedResult.m file. After installing the library the location of the file in Xcode is: Project -> Pods -> ZXingObjC -> All -> ZXParsedResult.m
On line 29 Change the Objective-C code
+ (id)parsedResultWithType:(ZXParsedResultType)type {
return [[self alloc] initWithType:type];
}
to
+ (id)parsedResultWithType:(ZXParsedResultType)type {
return [(ZXParsedResult *)[self alloc] initWithType:type];
}
I'm trying to figure out how to save a WebView to a PDF and totally stuck, would really appreciate some help?
I'm doing this in Cocoa & Swift on OSX, here's my code so far:
import Cocoa
import WebKit
class ViewController: NSViewController {
override func loadView() {
super.loadView()
}
override func viewDidLoad() {
super.viewDidLoad()
loadHTMLString()
}
func loadHTMLString() {
let webView = WKWebView(frame: self.view.frame)
webView.loadHTMLString("<html><body><p>Hello, World!</p></body></html>", baseURL: nil)
self.view.addSubview(webView)
createPDFFromView(webView, saveToDocumentWithFileName: "test.pdf")
}
func createPDFFromView(view: NSView, saveToDocumentWithFileName fileName: String) {
let pdfData = view.dataWithPDFInsideRect(view.bounds)
if let documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first {
let documentsFileName = documentDirectories + "/" + fileName
debugPrint(documentsFileName)
pdfData.writeToFile(documentsFileName, atomically: false)
}
}
}
It's pretty simple, what I'm doing is creating a WebView and writing some basic html content to it which renders this:
And then takes the view and saves it to a PDF file but that comes out blank:
I've tried grabbing the contents from the webView and View but no joy.
I've found a similar problem here How to take a screenshot when a webview finished rending regarding saving the webview to an image, but so far no luck with an OSX Solution.
Could it be something to do with the document dimensions?
or that the contents is in a subview?
maybe if you capture the View you can't capture the SubView?
Any ideas?
iOS 11.0 and above, Apple has provided following API to capture snapshot of WKWebView.
#available(iOS 11.0, *)
open func takeSnapshot(with snapshotConfiguration: WKSnapshotConfiguration?, completionHandler: #escaping (UIImage?, Error?) -> Swift.Void)
Sample usage:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
if #available(iOS 11.0, *) {
webView.takeSnapshot(with: nil) { (image, error) in
//Do your stuff with image
}
}
}
iOS 10 and below, UIWebView has to be used to capture snapshot. Following method can be used to achieve that.
func webViewDidFinishLoad(_ webView: UIWebView) {
let image = captureScreen(webView: webView)
//Do your stuff with image
}
func captureScreen(webView: UIWebView) -> UIImage {
UIGraphicsBeginImageContext(webView.bounds.size)
webView.layer.render(in: UIGraphicsGetCurrentContext()!)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
Here's another relevant answer
So I kind of figured out how to solve it, it turns out you can't (especially on OSX) access and print a webview from a WKWebView.
You have to use a WebView and NOT a WKWebView (I originally started with WKWebView because a few of the articles I read said to use that).
A WebView object is pretty much similar to a WKWebView object, which is fun as hell :-)
But it gives you access to .mainFrame & .frameView which you'll need to print it's content.
Here's my code:
let webView = WebView(frame: self.view.frame)
let localfilePath = NSBundle.mainBundle().URLForResource(fileName, withExtension: "html");
let req = NSURLRequest(URL: localfilePath!);
webView.mainFrame.loadRequest(req)
self.view.addSubview(webView)
Once it's rendered I then added a 1 second delay just to make sure the content has rendered before I print it,
// needs 1 second delay
let delay = 1 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue()) {
// works!
let data = webView.dataWithPDFInsideRect(webView.frame)
let doc = PDFDocument.init(data: data)
doc.writeToFile("/Users/john/Desktop/test.pdf")
// works!
let printInfo = NSPrintInfo.sharedPrintInfo()
let printOperation = NSPrintOperation(view: webView.mainFrame.frameView, printInfo: printInfo)
printOperation.runOperation()
}
Here I'm printing it and saving it as a PDF, just so I'm doubly sure it works in all circumstances.
I'm sure it can be improved, I hate the delay hack, should replace that with some kind of callback or delegate to run when the content has fully loaded.