Prevent the user from copying text of HTML in WebKit View Xcode - html

I want to disable copying text of my html file that is displayed in a WebKit View. Here is my WebKit View code:
#IBOutlet weak var webView: WKWebView!
#IBOutlet weak var backgroundView: UIView!
var index = 0
var fileName = ""
override func viewDidLoad() {
super.viewDidLoad()
loadSubject()
// Load the appropriate file
let htmlPath = Bundle.main.path(forResource: fileName, ofType: "htm")
let url = URL(fileURLWithPath: htmlPath!)
let request = URLRequest(url: url)
webView.load(request)
}
I'm trying to find a solution and here's the closest one, but I'm not sure how to implement this if it is even the correct answer for me: Prevent user from copying text on browsers
I've been using Swift and Xcode for about 6 months, but I'm brand new to HTML and WebKit View, so I apologize if this is something simple.
Thanks!

try this code
func webViewDidFinishLoad(_ webView: UIWebView) {
webView.stringByEvaluatingJavaScript(from: "document.documentElement.style.webkitTouchCallout='none';")
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == Selector("customMethod:") {
return super.canPerformAction(action, withSender: sender)
}
return false
}

Related

Why my Collectionview cells are not showing when i move from that viewcontroller in swift?

I am able to parse JSON and adding cells in Collectionview.. but if i move from this Viewcontroller and coming to viewcontroller then collectionview is not showing.. but added data in JSON
code for adding collectionview and JSON parsing:
class ImageItemModel{
var title: String?
var profileImage: UIImage?
var pic_id: Double?
init(title: String?, imgTitle: UIImage?, pic_id: Double?) {
self.title = title
self.profileImage = imgTitle
self.pic_id = pic_id
}
}
class EditProfileImageViewController: UIViewController {
#IBOutlet weak var titleTextfield: UITextField!
private var imageProfile : UIImage?
private var imagePicker : EasyImagePicker?
#IBOutlet weak var collectionView: UICollectionView!
var arrImageItems = [ImageItemModel]()
#IBAction func imgtitleSaveBtn(_ sender: Any) {
postServiceCall()
}
fileprivate func postServiceCall(){
if titleTextfield.text?.trim() == ""{
return self.view.makeToast("please add service title")
}
let parameters = ["image_title" : titleTextfield.text?.trim() ?? ""]
APIReqeustManager.sharedInstance.uploadMultipartFormData(param: parameters, url: CommonUrl.edit_profile_images, image: imageProfile, fileName: "image", vc: self, isHeaderNeeded: true) {(responseData) in
print("edit profile result \(responseData)")
if let result = responseData.dict?["result"] as? NSDictionary{
let success = result["status"] as? [String : Any]
let message = success?["message"] as? String
if message == "Success"{
let image = result["image"] as? [String : Any]
let picId = image?["id"]
self.arrImageItems.append(ImageItemModel(title: self.titleTextfield.text, imgTitle: self.imageProfile, pic_id: picId as! Double))
self.collectionView.reloadData()
}
else{
self.view.makeToast(CommonMessages.somethingWentWrong)
}
}
}
}
extension EditProfileImageViewController : UICollectionViewDelegate,UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrImageItems.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCollectionViewCell", for: indexPath) as! ImageCollectionViewCell
cell.imgView.image = arrImageItems[indexPath.item].profileImage
cell.lblTitle.text = arrImageItems[indexPath.row].title
cell.deleteButton.tag = indexPath.row
cell.deleteButton.addTarget(self, action: #selector(deleteService(sender:)), for: UIControl.Event.touchUpInside)
return cell
}
}
with the above code i am able to add collectionview cells and able to store data in JSON but.. if i move from this viewcontroller and coming back to this viewcontroller then collectionview is not showing, why? whats wrong? please do help me with code.. i got stuck here from long time.
There are couple of issues that you should fix for this to work properly. I will give you reason for each.-
You are loading your data with the postServiceCall() method which has an asynchronous network call. There is no way to know when the controller is done fetching the data to the arrImageItems array. So, you should have used a completion handler.
Now you are updating the collectionView within the asynchronous dataTask which a background thread. BIG mistake. Whenever you have any UI related task, you do it under the main thread. So, you could refactor the APIReqeustManager.sharedInstance.uploadMultipartFormData() part of your code following way-
APIReqeustManager.sharedInstance.uploadMultipartFormData(param: parameters, url: CommonUrl.edit_profile_images, image: imageProfile, fileName: "image", vc: self, isHeaderNeeded: true) {(responseData) in
print("edit profile result \(responseData)")
if let result = responseData.dict?["result"] as? NSDictionary{
let success = result["status"] as? [String : Any]
let message = success?["message"] as? String
if message == "Success"{
let image = result["image"] as? [String : Any]
let picId = image?["id"]
self.arrImageItems.append(ImageItemModel(title: self.titleTextfield.text, imgTitle: self.imageProfile, pic_id: picId as! Double))
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
else{
DispatchQueue.main.async {
self.view.makeToast(CommonMessages.somethingWentWrong)
}
}
}
}
Now unless you want your viewcontroller to show the data in your collectionView only when the action, imgtitleSaveBtn(_:) is triggered, you need to get data everytime, when your view controller appeared on screen. To fix that issue, you should get the data in the viewWillAppear(_:) method like-
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
postServiceCall()
}
Now, the above two edits should fix your issue depending on how you want to load your collectionview but your code is breaking quite some coding standards. Coding standards sounds like a clique but trust me you want to follow those if you ever want to update the capability of your app without breaking it. The following is just some hints-
Whenever you are in an asynchronous call, you should consider calling a completion handler for returning your data.
Should look into your methods, you are dangerously breaking the single responsibility principal.
In more than one place, you force unwrapped. Bad idea. You need your system to have a fail safe rather than just crashing on you.
Update 2:
Updates with a design pattern:
Compartmentalise your code in MVC pattern. Put the ImageItemModel class in its own file. See the image below to understand the design-
Customize the collectionViewCell within the ImageCollectionViewCell. Let's assume your custom cell has only the outlets.
class ImageCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var imgView: UIImageView!
#IBOutlet weak var lblTitle: UILabel!
#IBOutlet weak var deleteButton: UIButton!{
didSet{
deleteButton.addTarget(self, action: #selector(deleteService(_:)), for: .touchUpInside)
}
}
// however this could easily be done with IBAction
#objc func deleteService(_ sender: UIButton){
}
}
Update the postServiceCall and return the data to your controller with a completion handler, means when the postServiceCall is done executing, an array of images or an empty array should be returned based on success or failure. Then the controller can decide what to do with the data, in your case update UI. with couple of refactoring, here is the updated controller code.
import UIKit
import EasyImagePicker
class EditProfileImageViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!{ //for troubleshooting
didSet{ //purpose, do it from code
collectionView.delegate = self
collectionView.dataSource = self
}
}
#IBOutlet weak var titleTextfield: UITextField!
private var imageProfile : UIImage?
private var imagePicker : EasyImagePicker? // you never used this var.
var arrImageItems = [ImageItemModel]()
// any time a view controller appears on screen this method gets called.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
reloadMyCollectionView()
}
#IBAction func imgtitleSaveBtn(_ sender: Any) {
reloadMyCollectionView()
}
fileprivate func reloadMyCollectionView(){
postServiceCall{ images in
self.arrImageItems = images
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
// look into escaping closures to understand, why you need it here
fileprivate func postServiceCall(completed: #escaping(_ images: [ImageItemModel])->Void){
// don't force unwrap, get optional values safely with guard let
guard let titleText = titleTextfield.text, titleText == "" else{
return
}
let parameters = ["image_title" : titleText]
APIReqeustManager.sharedInstance.uploadMultipartFormData(param: parameters, url: CommonUrl.edit_profile_images, image: imageProfile, fileName: "image", vc: self, isHeaderNeeded: true) {(responseData) in
print("edit profile result \(responseData)")
//capture the data in local scope and return that array with a completion handler
var imageItems = []
if let result = responseData.dict?["result"] as? NSDictionary{
let success = result["status"] as? [String : Any]
let message = success?["message"] as? String
if message == "Success"{
let image = result["image"] as? [String : Any]
let picId = image?["id"]
imageItems.append(ImageItemModel(title: self.titleTextfield.text, imgTitle: self.imageProfile, pic_id: picId as! Double))
}
}
self.completed(imageItems) // if there is nothing in result,
//imageItems will be empty, otherwise it will have imageItemModel data
}
}
}
extension EditProfileImageViewController : UICollectionViewDelegate,UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrImageItems.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCollectionViewCell", for: indexPath) as? ImageCollectionViewCell{
cell.imgView.image = arrImageItems[indexPath.item].profileImage
cell.lblTitle.text = arrImageItems[indexPath.row].title
cell.deleteButton.tag = indexPath.row
//cell.deleteButton.addTarget(self, action: #selector(deleteService(sender:)), for: UIControl.Event.touchUpInside)
return cell
}
else{
return UICollectionViewCell()
}
}
}
Notice postServiceCall and cellForItemAt methods.
If you still have the same issue then you need to show your whole code to get any further help.

Share JSON Data in TabBarController to view controllers

I am using a tabbarcontroller to show 3 xib's. I would like to decode JSON data in the UITabBarController subclass, and then share the data with the view controllers (as I understand that is the preferred way to do this). I had already successfully accomplished this individually in each view controller, where the same JSON data was getting decoded separately 3 times, but I am now trying to make the process more efficient by only dealing with JSON once.
I am currently getting the following error
"Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffee7ab7d98)".
Below is the code I am currently using. I'm mostly only including the code for the first view controller, but it is the same for the others
Here is one of the view controllers. Any help would be appreciated, thank you!
class FirstCollectionViewController: UIViewController {
var tbvc = CustomTabBar()
var statisticsData = [Model]()
let firstCellIdentifier = "FirstCellIdentifier"
#IBOutlet weak var FirstCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
FirstCollectionView.delegate = self
FirstCollectionView.dataSource = self
FirstCollectionView.register(UINib(nibName: "FirstCollectionViewCell", bundle: nil),forCellWithReuseIdentifier: firstCellIdentifier)
}
}
Here is the subclasses UITabBarController
import UIKit
class CustomTabBar: UITabBarController {
let website = "https:......."
var statisticsData = [Model]()
override func viewDidLoad() {
super.viewDidLoad()
let firstTab = FirstCollectionViewController(nibName: "FirstCollectionViewController", bundle: nil)
let secondTab = SecondCollectionViewController(nibName: "SecondCollectionViewController", bundle: nil)
let thirdTab = ThirdCollectionViewController(nibName: "ThirdCollectionViewController", bundle: nil)
viewControllers = [firstTab, secondTab, thirdTab]
downloadJSON(website: website) {
firstTab.statisticsData = self.statisticsData
secondTab.statisticsData = self.statisticsData
thirdTab.statisticsData = self.statisticsData
firstTab.FirstCollectionView.reloadData()
secondTab.SecondCollectionView.reloadData()
thirdTab.ThirdCollectionView.reloadData()
}
}
func downloadJSON(website:String, completed:#escaping ()->()){
guard let qurl = URL(string: website) else { return }
URLSession.shared.dataTask(with: qurl) { (data, response, error) in
if error == nil {
do{
self.statisticsData = try JSONDecoder().decode([Model].self, from: data!)
DispatchQueue.main.async{
completed()
}
} catch {
print("JSON Error")
}}
}.resume()
}
}
Once the data is loaded, you should assign the data to the viewControllers that are added in the tabBarController's Child list as below,
downloadJSON(website: website) {
firstTab.statisticsData = self.statisticsData
secondTab.statisticsData = self.statisticsData
thirdTab.statisticsData = self.statisticsData
firstTab.FirstCollectionView.reloadData()
secondTab.SecondCollectionView.reloadData()
thirdTab.ThirdCollectionView.reloadData()
}
You can also remove the below lines from viewDidLoad of FirstCollectionViewController, SecondCollectionViewController and ThirdCollectionViewController
tbvc = tabBarController as! CustomTabBar
statisticsData = tbvc.statisticsData

How to get text from Web page in macOS via Swift?

How to get a string of text like this:
Simple Text Example...
from macOS' Safari page via Swift 4?
#IBOutlet weak var label: NSTextField!
#IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let string = "http://127.0.0.1:8080/101"
let url = NSURL(string: string)
let request = URLRequest(url: url! as URL)
self.webView.load(request as URLRequest)
//this doesn't work
label.stringValue = webView.webFrame.frameElement.innerText
}
Here is HTML structure:
<html>
<head></head>
<body>
Simple Text Example...
</body>
</html>
UPDATE:
I've tried the following method but it has Void return...
webView.evaluateJavaScript("document.body.innerText") { (result, error) in
if error != nil {
print(result)
}
}
for iOS, in the webview's delegate method
func webViewDidFinishLoad(_ webView: UIWebView) {
var string = webView.stringByEvaluatingJavaScript(from: "document.body.textContent")
print(string)
}
for macOS, in the webview's navigation delegate:
override func viewDidLoad() {
webview.nagivationDelegate = self
...
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.evaluateJavaScript("document.body.textContent") { (string, error) in
print(string)
}
}
You have to check outerhtml and innerhtml, textContent and many more tag like that:
if let htmlOuter = self.webView.stringByEvaluatingJavaScript(from: "document.body.outerHTML"){
}
Or
if let htmlInner = self.webView.stringByEvaluatingJavaScript(from: "document.body.innerHTML"){
}
Or
if let content = self.webView.stringByEvaluatingJavaScript(from: "document.body. textContent"){
}

UIWebView white flashing when load page

i have build a WebApp for iOS. When i click/tap to move to a page for example from page - index.html to page2.html it flashing white. Did you know why, and how to solve this problem?
The Websites works fantastic in Browser (Safari, IE, Firefox and Chrome) but not as an Application.
My code is:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
webView.loadRequest(NSURLRequest(URL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("index", ofType: "html")!)))
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Try my code will help you!
Step 1: Fetch UIWebView and UIActivityIndicatorView to your view controller.
Step 2: Delegate UIWebView to viewController and Create outlets for UIWebView and UIActivityIndicatorView.
Step 3: Select UIActivityIndicatorView on view controller then go to Attributes Inspector ->Activity Indicator View -> check Animating and Hides when Stopped on Behaviour
Step 4: Add Below codes to your ViewController.
import UIKit
class ViewController: UIViewController,UIWebViewDelegate{
#IBOutlet var loader: UIActivityIndicatorView!
#IBOutlet var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
self.automaticallyAdjustsScrollViewInsets = false //to avoid auto scrolling.
functionOfWebView()
}
func functionOfWebView()
{
let URL = NSURL(string: "http://www.google.com")
//let URL = NSBundle.mainBundle().URLForResource("index", withExtension: "html") //For local html file(index.html) with local file hyperlink(file.html) see on video tutorial
let request = NSURLRequest(URL: URL!)
webView.loadRequest(request)
}
func webViewDidStartLoad(webView: UIWebView)
{
loader.startAnimating()
}
func webViewDidFinishLoad(webView: UIWebView)
{
loader.stopAnimating()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
In Swift 3.0
import UIKit
class ViewController: UIViewController,UIWebViewDelegate{
#IBOutlet var loader: UIActivityIndicatorView!
#IBOutlet var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
self.automaticallyAdjustsScrollViewInsets = false //to avoid auto scrolling.
functionOfWebView()
}
func functionOfWebView()
{
let URL = NSURL(string: "http://www.google.com")
//let URL = Bundle.main.url(forResource: "index", withExtension: "html") //For local html file(index.html) with local file hyperlink(file.html) see on video tutorial
let request = NSURLRequest(url: URL! as URL)
webView.loadRequest(request as URLRequest)
}
func webViewDidStartLoad(_ webView: UIWebView)
{
loader.startAnimating()
}
func webViewDidFinishLoad(_ webView: UIWebView)
{
loader.stopAnimating()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
if you confuse or for Using Local html files then see this Youtube tutorial

Open specific link in Safari from UIWebView

I have a UIWebView which loads a local index.html file.
However I have an external link in this html file that I'd like to open in Safari instead of internally in the UIWebView.
Opening a link in safari from say a UIButton was simple enough:
UIApplication.sharedApplication().openURL(NSURL(string: "http://www.stackoverflow.com"))
Opening the Instagram app from a external link also works like a charm.
instagram://media?id=434784289393782000_15903882
So my first though was to do something like this:
Open in Safari
However that doesn't seem to work, then I read something about using webView:shouldStartLoadWithRequest:navigationType:
But everyone who's managed to open an external link in Safari is writing in Obj-C which I'm not too familiar with as I'm writing in Swift.
Update with Swift Code:
import UIKit
class AccessoriesViewController: UIViewController, UIWebViewDelegate {
#IBOutlet weak var webView:UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
if let url = NSBundle.mainBundle().URLForResource("accessories", withExtension: "html") {
webView.loadRequest(NSURLRequest(URL: url))
}
}
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return UIStatusBarStyle.LightContent;
}
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if let url = request.URL where navigationType == UIWebViewNavigationType.LinkClicked {
UIApplication.sharedApplication().openURL(url)
return false
}
return true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Here is how you can do it!
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if let url = request.URL where navigationType == UIWebViewNavigationType.LinkClicked {
UIApplication.sharedApplication().openURL(url)
return false
}
return true
}