Cancelling a javaScript event from a UIWebViewDelegate - ios

Before I deem my weak-long custom re-implementation of UITextView (using an UIWebView in designMode) useless, is there any way to handle/cancel javaScript onKeyUp, etc. events?
AFAIK there is only messaging via -shouldLoadRequest: & stringByEvaluatingScriptWithString:. However, these calls are asynchronous and the javaScript event handler has already exited it's function by the time stringByEvaluatingScriptWithString: is performed thus event cancellation methods do not work.
If not for this capability, implementing shouldReplaceCharactersInString: seems impossible. :(

Maybe you can check my open source implementation of a "safari like" browser :
https://github.com/sylverb/CIALBrowser
In this one, I did reimplement the long tap handling, and to disable the standard one, I use this :
- (void) webViewDidFinishLoad:(UIWebView *) sender
{
// Disable the defaut actionSheet when doing a long press
[webView stringByEvaluatingJavaScriptFromString:#"document.body.style.webkitTouchCallout='none';"];
[webView stringByEvaluatingJavaScriptFromString:#"document.documentElement.style.webkitTouchCallout='none';"];
}

Related

Detect iOS 13 undo & redo

I have a WKWebView with a custom implementation of undo and redo. I would like to be able to know when the system undo / redo are triggered (via gestures or via tapping on the keyboard assistant button in iPadOS) so that I can use my custom implementation.
Is there a public API to do that?
There are multiple ways. On the iOS native side there is no public API available to take over full control AFAIK, but you can listen to UNDO notifications like this to get to know about them:
[NSNotificationCenter.defaultCenter addObserverForName:NSUndoManagerWillUndoChangeNotification object:nil queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull note) {
NSLog(#"Undo Notification: %#", note);
}];
You will then see that the NSUndoManager responsible for that lives in WKContentView. To get there only swizzling helps with the known risks...
But there is another way that works in WebKit based browser views (as WKWebView is) and this is to listen to beforeinput events. For example for a contenteditable element you can add the following listener:
editor.addEventListener('beforeinput', event => {
if (event.inputType === 'historyUndo') {
console.log('undo')
event.preventDefault()
}
if (event.inputType === 'historyRedo') {
console.log('redo')
event.preventDefault()
}
console.log('some other inputType', event.inputType)
})
This idea was was taken from this discussion: https://discuss.prosemirror.net/t/native-undo-history/1823/3?u=holtwick
Here is a JSFiddle to test: https://jsfiddle.net/perenzo/bhztrgw3/4/
See also the corresponding TipTap plugin: https://github.com/scrumpy/tiptap/issues/468

Connection Error Occurs When Browsing Too Fast

In the app that I'm writing, I check to see if the device has an internet connection. I put a connection error image over the screen, and hide it unless the device is not connected. There is an odd issue though. I implemented a simple back button for the UIWebView, but when I press it too fast, the connection error occurs. Here is the code I use to check for connection, and decide whether to display the error:
-(void)webView:(UIWebView *)myWebView didFailLoadWithError:(NSError *)error {
_connectionError.hidden = NO;
}
So, I think the only way to solve this issue would be to have it check if there is a connection one time, only when the app first launches, and never run again for the remainder of the time. I'm extremely new to Objective-C, and have no idea how to do this. I'm thinking that I should put something in viewDidLoad, or implement some way to have the method run only once, but I have no idea how to do that.
Here's the code for the back button:
- (IBAction)backButtonTapped:(id)sender {
[_viewWeb goBack];
}
Call the method stopLoading on the webView before the goBack method to make sure there is no multiple request going which can cause the connection error:
- (IBAction)backButtonTapped:(id)sender {
[_viewWeb stopLoading];
[_viewWeb goBack];
}
To check for a connection you can use Reachability in your project. You can then use this answer to see how to use it. This would be more efficient and cleaner than using a UIWebview.

Firefox SDK - Detecting back button in extension

I'd like to do the equivalent of chrome.tabs.onUpdated in Firefox. tabs.on('ready', function(tab){}) does not work because it does not detect the back button. How do I fire an action on every page load such that it also detects the back button using the Firefox SDK?
You'd have to use require('window-utils').WindowTracker to all windows, filter for browser windows with the require('sdk/window/utils').isBrowser(window) method, then listen to click events on the back button.
It's currently impossible, but will be possible in a future version of Firefox:
https://github.com/mozilla/addon-sdk/commit/e4ce238090a6e243c542c2b421f5906ef465acd0
A bit of a late answer, but for anyone reading this now (from 2016), it is now possible to do using the SDK!
Using the High-Level API tabs, you need to listen for the pageshow event. (More about this on MDN)
An example:
tabs.on('pageshow', function(tab) {
// Your code here
})
It is very similar to the load and ready events, the main difference being that is is also fired when a page is loaded from BFCache (which it is when the back button is pressed).
I think the following snippet gives the functionality of chrome.tabs.onUpdated
var tabs = require("sdk/tabs");
tabs.on('ready', function(tab){
console.log(tab.url);
});

Custom NSURLProtocol not used in second webview

I've subclassed NSURLProtocoland registered it in didFinishLaunchingWithOptions with this code:
+ (void) registerProtocol
{
static BOOL registered = NO;
if (!registered)
{
[NSURLProtocol registerClass:[MyURLProtocol class]];
registered = YES;
}
}
For the first UIWebView in my app (in the mainwindow) the method canInitWithRequest is triggered, and I can execute my custom code.
However I have a second UIWebView, that is inside an UIViewController which is pushed at some point in the app (presented modally). The canInitWithRequest will NOT be called for the second UIWebView, thus I cannot execute my custom code. This is even true when the protocol is registered after both instances of UIWebView are created.
Anyone knows why?
[edit]
d'oh! i just found it was just a plain error in the javascript that is loaded in the second webview :(
works like a charm in both webviews now!
Not sure if this is related to your situation and solution, but posting for the record. Discovered today after much trial and tribulation that an ajax call sending a message to objective c which is to be intercepted by a subclass of NSURLProtocol should have cache set to false if you expect to be sending the same url more than once. Like so:
$.ajax({
url:"atavistcommand://" + name,
data:JSON.stringify(data),
type:"GET",
processData:false,
cache:false )};
If cache = true then objective c will never receive subsequent requests on the same url.

how to response before the server return on iOS

my situation is like this:
when you press a button, it will first hide the button, and then send a http request to the server, the request is sent to a php script and execute an "exe" to use the printer to print something.
But when I test the app, I find the button hides when the "exe" on the server exits, the result is that when I press the button, the application is like "not response", the button
dose not hide and keep the state of "press down", it is not user-friendly. So any ideas
to help me solve this ?
Finally, I used slef performSelectorInBackground, since I did not care the result php return, it works well and easier than Asynchronous call.
It may be that you are making a synchronous call to the server. Check here for how to make asynchronous calls.
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html
interface updates are only done after the method finishes. try running the php part in an extra method with performSelector:
- (void)yourCurrentMethod {
button.hidden = YES;
[self performSelector:#selector(doSomething)];
}
- (void)doSomething {
/* call php script, etc */
}
Sorry, what I meant was performSelector:withObject:afterDelay:
- (void)yourCurrentMethod {
button.hidden = YES;
[self performSelector:#selector(doSomething) withObject:nil afterDelay:0.1];
}
- (void)doSomething {
/* call php script, etc */
}
That will process the hidden in the UI and then call the doSomething-method without making it a background thread, so you can use a synchronous request. Using asynchronous requests is a little more work (but cleaner), which you don't really need, if you only want to call the php and don't care about the result.

Resources