When you open url that contain PDF file safari ask you if you want to open it on safari or in iBook.
I want to do the same thing ,
in my project i had a collection view contains videos and photos,
i want the user to chose if he want to open the file on the app or to open it with other media player.
For loading into your own app it depends on which class you're using to display content on the exact code you'd use but for opening in another app you'd normally use a share button. Here is example code that will work if you wire up the #IBAction and #IBOutlet to the same bar button in your UI (and place a file at the fileURL that you specify):
import UIKit
class ViewController: UIViewController {
// UIDocumentInteractionController instance is a class property
var docController:UIDocumentInteractionController!
#IBOutlet weak var shareButton: UIBarButtonItem!
// called when bar button item is pressed
#IBAction func shareDoc(sender: AnyObject) {
// present UIDocumentInteractionController
if let barButton = sender as? UIBarButtonItem {
docController.presentOptionsMenuFromBarButtonItem(barButton, animated: true)
}
else {
print("Wrong button type, check that it is a UIBarButton")
}
}
override func viewDidLoad() {
super.viewDidLoad()
// retrieve URL to file in main bundle
if let fileURL = NSBundle.mainBundle().URLForResource("MyImage", withExtension: "jpg") {
// Instantiate the interaction controller
self.docController = UIDocumentInteractionController(URL: fileURL)
}
else {
shareButton.enabled = false
print("File missing! Button has been disabled")
}
}
}
Notes
A UIDocumentInteractionController is used to enable the sharing of documents between your app and other apps installed on a user's device. It is simple to set up as long as you remember three rules:
Always make the UIDocumentInteractionController instance a class
(type) property. If you only retain a reference to the controller
for the life of the method that is triggered by the button press
your app will crash.
Configure the UIDocumentInteractionController
before the button calling the method is pressed so that there is not
a wait in which the app is waiting for the popover to appear. This is important because while
the presentation of the controller happens asynchronously, the instantiation does not. And you may find that there is a noticeable delay to open the popover if
you throw all the code for instantiation and presentation inside a
single method called on the press of a button. (When testing you might see a delay anyway because the share button is likely going to be pressed almost straightaway but in real world use there should be more time for the controller to prepare itself and so the possibility of lag is less likely.)
The third rule is that you must test this on a real device not in the simulator.
More can be found in my blogpost on the subject.
Edit: Using a UIActivityViewController
Code for using UIActivityViewController instead of UIDocumentInteractionController
import UIKit
class ViewController: UIViewController {
// UIDocumentInteractionController instance is a class property
var activityController: UIActivityViewController!
#IBOutlet weak var shareButton: UIBarButtonItem!
// called when bar button item is pressed
#IBAction func shareStuff(sender: AnyObject) {
if let barButton = sender as? UIBarButtonItem {
self.presentViewController(activityController, animated: true, completion: nil)
let presCon = activityController.popoverPresentationController
presCon?.barButtonItem = barButton
}
else {
print("not a bar button!")
}
}
override func viewDidLoad() {
super.viewDidLoad()
// retrieve URL to file in main bundle
if let img = UIImage(named:"MyImage.jpg") {
// Instantiate the interaction controller
activityController = UIActivityViewController(activityItems: [img], applicationActivities: nil)
}
else {
shareButton.enabled = false
print("file missing!")
}
}
}
You can also add custom activities to the UIActivityViewController and here is code for adding an "Open In..." button to a UIActivityViewController so that you can switch to a UIDocumentInteractionController from a UIActivityViewController.
I did the same code for saving a PDF file from a URL (whether it's a local URL in your device storage, or it's a URL from somewhere on the internet)
Here is the Code for Swift 3 :
#IBOutlet weak var pdfWebView: UIWebView!
#IBOutlet weak var shareBtnItem: UIBarButtonItem!
var pdfURL : URL!
var docController : UIDocumentInteractionController!
then in viewDidLoad()
// retrieve URL to file in main bundle`
let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("YOUR_FILE_NAME.pdf")
//Instantiate the interaction controller`
self.docController = UIDocumentInteractionController(url: fileURL)`
and in your barButtonItem tapped method (which I have called openIn(sender)):
#IBAction func openIn(_ sender: UIBarButtonItem)
{
// present UIDocumentInteractionController`
docController.presentOptionsMenu(from: sender, animated: true)
}
FYI: You need a webView in your storyboard if you wish to show the pdf file as well
Hope this helps.
Related
import AVFoundation
import UIKit
class ViewController: UIViewController {
#IBOutlet var button: UIButton!
var player: AVPlayer?
var playerItem:AVPlayerItem?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func didTapButton(urlRadio: String){
if let player = player, player.isMuted{
//stop playback
}
else{
do {
try AVAudioSession.sharedInstance().setMode(.default)
try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
guard let urlRadio = URL.init(string: "http://live.shtorm.fm:8000/mp3_rushtorm") else {
return
}
let playerItem:AVPlayerItem = AVPlayerItem.init(url: urlRadio)
player = AVAudioPlayer.init(playerItem: playerItem)
}
catch {
print("something went wrong")
}
}
}
}
shows error Exception NSException * "-[RozetkaRadio.ViewController didTapButton]: unrecognized selector sent to instance 0x7ff070f07a50" 0x00006000008c7210 after pressing button in emulator
using storyboard
Your issue is with this line of code. Specifically, the parameter urlRadio.
#IBAction func didTapButton(urlRadio: String){
It looks like you have changed the standard IBAction from what was set in storyboard. You cannot change the parameters in an IBAction as the storyboard no longer recognizes where to send the tap to.
Your IBAction should look something like this
#IBAction func didTapButton(_ sender: Any)
Whenever you see Unrecognized selector sent to instance you should immediately think "Something is not hooked up properly between my storyboard and my ViewController"
One quick way to look is by right clicking your controller scene name on the storyboard. You can see I commented out my SignIn IBAction function. If I wanted to remove it I would have to also remove the connection on the storyboard. You can see the storyboard will let me know by leaving a yellow warning label.
This is my first app to iOS and i do the same app to Android without problems and now i'm stucked to this problem.
When the user click on "choose file" into webview the phone ask me where i want pick the photo (or file), and if i select "Close" the entire WebView get closed!
How can i manage that events? (image below show the windows that take me this problems)
Same problem if i click to a loaded attachment a appear a window that show me the image and if i long press the image some buttons appear (copy, download or close).... if i press one of them the WebView get closed...
The back button is disabled because the user can't use back in this case and must follow the workflow forced by webview.
I try with this code but still close the webview
import UIKit
import WebKit
class MyWebViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
//Variable used for take received data from other ViewControllers
var urlString = String()
var callerString = String()
// MARK: - Properties
#IBOutlet weak var myWebView: WKWebView!
#IBOutlet weak var backButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
if callerString == "CodeController"{
backButton.isEnabled = false
} else {
backButton.isEnabled = true
}
myWebView.navigationDelegate = self
view.addSubview(myWebView)
// Do any additional setup after loading the view.
let url = URL(string: urlString)
myWebView.load(URLRequest(url : url!))
}
#IBAction func backButtonAction(_ sender: Any) {
if callerString != "CodeController"{
if myWebView.canGoBack{
myWebView.goBack()
}else {
//Go to Main View
self.navigationController?.popViewController(animated: true)
self.performSegue(withIdentifier: "backOnViewControllerSegue", sender: self)
}
}
}
Here some images of which buttons close the entire WebView and how manage them in a different way?
It was a bug from WkWebView libraries.
To avoid that just upgrade OSX and XCODE to last versions and the problem is solved.
Not sure if it's solved for all iOS OS but sure for iOS 12, and maybe 10
I'm trying to make an app with a button which launch a webview.
I've followed many tutorial and read differents topic about the subject but I cant get it working : I'm getting this message when I test my code :
"Cannot call value of non-function type UIWebView!"
Here's the steps I did until now
Adding a button in the principal view Controller
Creating an another view Controller named 'WebViewController'
Adding a segue to link the button to WebViewController
Creating a new Cocoa Touch Class file 'WebViewController'
Setting the WebViewController custom class with the WebViewController class
Adding a webView in the WebViewController ViewController named 'myWebView'
Here's the WebViewController class (in which I got the error when I run the project)
import UIKit
class WebViewController: UIViewController{
#IBOutlet weak var myWebView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//define url
let url = NSURL (string: "http://www.my-url.com")
//request
let req = NSURLRequest(url: url as! URL)
//load request into the webview
myWebview(req as URLRequest) //error happens here :
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
Here's a screenshot (picture talks more than long text, right =)
Thanks !
You can use SFSafariViewController:
import SafariServices
let url = URL(string: "https://www.google.com")
let safariVC: SFSafariViewController = SFSafariViewController(url: url)
self.present(safariVC, animated: true, completion: nil)
I used swift 3 syntax.
That code opens a Safari Web view and you dont need to create segues and view controlles in storyboard.
Try to use:
let url = NSURL (string: "https://google.com")
let request = NSURLRequest(url: url as! URL)
self. myWebView.loadRequest(request as URLRequest)
This code works for me
I need open url programmatically when user click to cell, but not need segue to Safari browser. It is need for statistic on site.
This is need to imitation like post request
I do:
UIApplication.shared.openURL(URL(string: url)!)
But this open Safari browser.
If I'm not mistaking, you want to open the URL in the application it self (without navigating to external -Safari- browser). Well, if that's the case, you should use UIWebView:
You can use the UIWebView class to embed web content in your app. To
do so, create a UIWebView object, attach it to a window, and send it a
request to load web content. You can also use this class to move back
and forward in the history of webpages, and you can even set some web
content properties programmatically.
WebViewController:
I suggest to add a UIWebView into a ViewController (called WebViewController) and present the ViewController when needed; ViewController on storyboard should looks like:
DON'T forget to assign a Storyboard ID for it.
And the ViewController:
class WebViewController: UIViewController {
#IBOutlet weak private var webVIew: UIWebView!
var urlString: String?
override func viewDidLoad() {
if let unwrappedUrlString = urlString {
let urlRequest = URLRequest(url: URL(string: unwrappedUrlString)!)
webVIew.loadRequest(urlRequest)
}
}
#IBAction private func donePressed(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
}
Usage:
Consider that you want present it when the user taps a button in another ViewController:
class ViewController: UIViewController {
#IBAction private func donePressed(_ sender: Any) {
let stoyrboard = UIStoryboard(name: "Main", bundle: nil)
// withIdentifier: the used storyboard ID:
let webViewController = stoyrboard.instantiateViewController(withIdentifier: "WebViewController") as! WebViewController
webViewController.urlString = "https://www.google.com/"
}
}
Or specifically for your case (selecting a cell):
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// withIdentifier: the used storyboard ID:
let webViewController = stoyrboard.instantiateViewController(withIdentifier: "WebViewController") as! WebViewController
webViewController.urlString = "URL GOES HERE..."
}
Hope this helped.
if your question really means that you have some data on an external website that you need to access, so that you can extract a piece of information from it, then you probably don't need to display the rendered webpage at all.
You can extract the html content of a page like this
var text = ""
let url = URL(string: "https://www.bbc.co.uk")
do
{
text = try String(contentsOf: url!, encoding: String.Encoding.utf8)
}
catch
{
print("error \(error.localizedDescription)")
}
if !text.isEmpty
{
parseThisPageSomehow(text)
}
How you parse the text depends on what you need to get out of it, but this approach will give you the data you need.
If you use http rather than https, you will run into the well-documented transport security issue, which will force you to either set up a temporary override for http, or start using https
Transport security has blocked a cleartext HTTP
I'm wondering how to open a linked pdf file with the turbolinks-ios framework in iOS.
Currently, I'm experiencing the issue that when a turbolinks page links to a pdf or other file, then the link will open in safari rather than the embedded view.
Background
The turbolinks-5 library together with the turbolinks-ios framework provide a way to connect a web application to the native navigation controllers of the corresponding mobile app.
The screenshot is taken from the turbolinks README.
Desired behavior
When clicking a link that refers to a pdf, a seaparate view controller should be pushed to the current navigation controller, such that the user can read the pdf and easily navigate back to the document index.
Observed behavior
The linked pdf is opened in safari rather than within the app. Unfortunately, safari asks for authentication, again. Furthermore, the user has to leave the application.
Intercept the click of the pdf link
For a link to a pdf file, the didProposeVisitToURL mechanism is not triggered for the session delegate. Thus, one can't decide from there how to handle the linked pdf.
Instead, one could intercept clicking the link by becoming turbolinks' web view's navigation delegate as shown in the README:
extension NavigationController: SessionDelegate {
// ...
func sessionDidLoadWebView(session: Session) {
session.webView.navigationDelegate = self
}
}
extension NavigationController: WKNavigationDelegate {
func webView(webView: WKWebView,
decidePolicyForNavigationAction navigationAction: WKNavigationAction,
decisionHandler: (WKNavigationActionPolicy) -> ()) {
// This method is called whenever the webView within the
// visitableView attempts a navigation action. By default, the
// navigation has to be cancelled, since when clicking a
// turbolinks link, the content is shown in a **new**
// visitableView.
//
// But there are exceptions: When clicking on a PDF, which
// is not handled by turbolinks, we have to handle showing
// the pdf manually.
//
// We can't just allow the navigation since this would not
// create a new visitable controller, i.e. there would be
// no back button to the documents index. Therefore, we have
// to create a new view controller manually.
let url = navigationAction.request.URL!
if url.pathExtension == "pdf" {
presentPdfViewController(url)
}
decisionHandler(WKNavigationActionPolicy.Cancel)
}
}
Present the pdf view controller
Similarly to presenting the visitable view as shown in the turbolinks-ios demo application, present the pdf view controller:
extension NavigationController {
func presentPdfViewController(url: NSURL) {
let pdfViewController = PdfViewController(URL: url)
pushViewController(pdfViewController, animated: true)
}
}
Or, if you'd like to show other file types as well, call it fileViewController rather than pdfViewController.
PdfViewController
The new view controller inherits from turbolinks' VisitableViewController to make use of the initialization by url.
class PdfViewController: FileViewController {
}
class FileViewController: Turbolinks.VisitableViewController {
lazy var fileView: WKWebView = {
return WKWebView(frame: CGRectZero)
}()
lazy var filename: String? = {
return self.visitableURL?.pathComponents?.last
}()
override func viewDidLoad() {
view.addSubview(fileView)
fileView.bindFrameToSuperviewBounds() // https://stackoverflow.com/a/32824659/2066546
self.title = filename // https://stackoverflow.com/a/39022302/2066546
fileView.loadRequest(NSURLRequest(URL: visitableURL))
}
}
To get the web view to the correct size, I used bindFrameToSuperviewBounds as shown in this stackoverflow answer, but I'm sure there are other methods.
Optional: Sharing cookies
If loading the pdf needs authentication, it's convenient to share the cookies with the turbolinks-ios webview as described in the README.
For example, create a webViewConfiguration which can be passed to the pdfViewController:
extension NavigationController {
let webViewProcessPool = WKProcessPool()
lazy var webViewConfiguration: WKWebViewConfiguration = {
let configuration = WKWebViewConfiguration()
configuration.processPool = self.webViewProcessPool
// ...
return configuration
}()
lazy var session: Session = {
let session = Session(webViewConfiguration: self.webViewConfiguration)
session.delegate = self
return session
}()
}
The same webViewConfiguration needs to be passed to the session (shown above) as well as to the new pdf view controller.
extension NavigationController {
func presentPdfViewController(url: NSURL) {
let pdfViewController = PdfViewController(URL: url)
pdfViewController.webViewConfiguration = self.webViewConfiguration
pushViewController(pdfViewController, animated: true)
}
}
class FileViewController: Turbolinks.VisitableViewController {
var webViewConfiguration: WKWebViewConfiguration
lazy var fileView: WKWebView = {
return WKWebView(frame: CGRectZero, configuration: self.webViewConfiguration)
}()
// ...
}
Demo