简体   繁体   English

如何在webapp中禁用iOS“Shake to Undo”

[英]How to disable iOS “Shake to Undo” in a webapp

I'm building a game as a webapp for the iPad. 我正在构建一个游戏作为iPad的webapp。 It incorporates a shake action to trigger an event for one of the screens, but, since it also has a information collection form, sometimes that shake action triggers the annoying "Shake to Undo" dialog. 它结合了一个摇动动作来触发其中一个屏幕的事件,但是,由于它还有一个信息收集表单,有时摇动动作会触发恼人的“摇动撤消”对话框。 The form input the user has typed in don't even exist in the DOM anymore when the user reaches the shake action, so in my mind the Undo function shouldn't be triggered, but unfortunately it is. 当用户到达摇动动作时,用户输入的表单输入甚至不再存在于DOM中,所以在我看来不应该触发撤销功能,但不幸的是它是。 There is no way this can be wrapped in a native wrapper (PhoneGap or other), so I was wondering if anyone knows if it's at all possible to disable this functionality for a specific web page/app, through CSS or JavaScript? 没有办法将它包装在本机包装器(PhoneGap或其他)中,所以我想知道是否有人知道是否可以通过CSS或JavaScript禁用特定网页/应用程序的此功能?

Thanks. 谢谢。

This blog describes a very simple shake detection using the DeviceMotionEvent listener: 此博客描述了使用DeviceMotionEvent侦听器进行的非常简单的抖动检测:

http://www.jeffreyharrell.com/blog/2010/11/creating-a-shake-event-in-mobile-safari/ http://www.jeffreyharrell.com/blog/2010/11/creating-a-shake-event-in-mobile-safari/

Try the following code to disable to undo event happening: 尝试使用以下代码禁用撤消事件发生:

window.addEventListener('devicemotion', function (e) {
    // This is where you put your code for dealing with the shake event here

    // Stop the default behavior from triggering the undo dialog (hopefully)
    e.preventDefault();
});

There is one other thing I can think of which would be to turn the text input into a readonly item after you are done with it. 我还可以想到另一件事就是在完成后将文本输入转换为只读项目。 Presumably, a read only input field cannot make use of the undo function, though I haven't tested it. 据推测,只读输入字段不能使用撤销功能,但我还没有测试过。 It's odd that the feature fires even after the input is no longer part of the DOM. 奇怪的是,即使输入不再是DOM的一部分,该功能也会触发。

I'm not sure if preventing a shake action can be done through the -webkit- properties in CSS. 我不确定是否可以通过CSS中的-webkit-属性来防止抖动操作。 Here is a list of webkit specific CSS properties (which may not contain 100% of the properties accessible). 以下是webkit特定CSS属性的列表(可能不包含100%可访问的属性)。

http://qooxdoo.org/documentation/general/webkit_css_styles http://qooxdoo.org/documentation/general/webkit_css_styles

For PhoneGap I just inserted this line: [UIApplication sharedApplication].applicationSupportsShakeToEdit = NO; 对于PhoneGap,我刚刚插入了这一行:[UIApplication sharedApplication] .applicationSupportsShakeToEdit = NO; in the webViewDidFinishLoad class and it worked wonders for me. 在webViewDidFinishLoad类中,它为我创造了奇迹。

Just go to the MainViewController.m and change webViewDidFinishLoad to look like this: 只需转到MainViewController.m并将webViewDidFinishLoad更改为如下所示:

- (void)webViewDidFinishLoad:(UIWebView*)theWebView
{
    // Black base color for background matches the native apps
    theWebView.backgroundColor = [UIColor blackColor];

[UIApplication sharedApplication].applicationSupportsShakeToEdit = NO;

    return [super webViewDidFinishLoad:theWebView];
}

I recently ran into this issue while developing a game where a user pairs devices using WebSockets. 我最近遇到了这个问题,同时开发了一个用户使用WebSockets配对设备的游戏。 Unfortunately none of the solutions mentioned above work. 遗憾的是,上述解决方案都不起作用。

In short, the game involved a user subscribing to a channel via a form on their phone. 简而言之,游戏涉及用户通过手机上的表格订阅频道。 Once subscribed they would shake the device and a scenario would play out on their desktop. 一旦订阅,他们就会动摇设备并在他们的桌面上播放一个场景。 The problem was that any time someone shook an iOS device the "undo typing" alert would pop up. 问题是每当有人震动iOS设备时,都会弹出“撤消打字”警报。

Here are the only two solutions that I've found for this issue. 以下是我为此问题找到的唯一两个解决方案。

  1. Prevent your input from ever losing focus. 防止您的输入失去焦点。 This will require you to prevent form submission if the input is part of a form. 如果输入是表单的一部分,这将要求您阻止表单提交。 You must also listen for "touchstart" instead of "click" on any anchors and prevent the touchstart event from propagating. 您还必须在任何锚点上侦听“touchstart”而不是“click”,并防止touchstart事件传播。 This will prevent that anchor from gaining focus and your input from losing focus. 这样可以防止锚点获得焦点,输入也不会失去焦点。 This approach has some disadvantages for sure. 这种方法肯定有一些缺点。

  2. Add a "keydown" event handler to the window and prevent the event from propagating. 向窗口添加“keydown”事件处理程序并阻止事件传播。 This prevents any values from getting inserted into the input on iOS devices. 这可以防止任何值插入到iOS设备上的输入中。 I also had to create an object that mapped the keycodes to the proper character. 我还必须创建一个将键码映射到正确字符的对象。 This is the route I ended going because all I needed in my input was a 6 character code so I only needed numbers. 这是我结束的路线,因为我输入的所有内容都是6个字符的代码所以我只需要数字。 If you need more than just numbers this may be a pain in the ass. 如果你需要的不仅仅是数字,这可能是一个痛苦的屁股。 On key down, snag that character via the keycode and set the value of the input. 在按键时,通过键码捕获该字符并设置输入值。 This tricks iOS into thinking that no value was ever inserted into the input via the keyboard so there is nothing for it to undo. 这让iOS认为没有任何值通过键盘输入到输入中,所以没有任何东西可以撤消。

Keep in mind preventing the keydown event from propagating does not prevent input on the stock android browser, so it will just function normally. 请记住,防止keydown事件传播不会阻止在股票android浏览器上输入,因此它将正常运行。 But that is OK since this is not an android issue. 但这没关系,因为这不是一个Android问题。 You can prevent duplicate values from getting inserted by listening for the input event on the input itself and removing the keydown listener if this event is received. 您可以通过侦听输入本身的输入事件来防止插入重复值,并在收到此事件时删除keydown侦听器。

var codes = {
  48: 0,
  49: 1,
  50: 2,
  51: 3,
  52: 4,
  53: 5,
  54: 6,
  55: 7,
  56: 8,
  57: 9
},
myInput = document.getElementById("myInput");

var keydownHelper = function (e) {
  e.preventDefault();
  myInput.removeEventListener("input", inputHelper); 

  var val = myInput.value;

  // Delete
  if (e.keyCode === 8 && val.length) {
    myInput.value = val.slice(0, val.length - 1);
    return;
  }

  // If not a number, do nada
  if (typeof codes[e.keyCode] === "undefined") { return; }

  val += codes[e.keyCode];
  myInput.value = val;
};

var inputHelper = function (e) {
  e.preventDefault();
  window.removeEventListener("keydown", keydownHelper);
};

myInput.addEventListener("input", inputHelper);
window.addEventListener("keydown", keydownHelper); 

Objective-C Objective-C的

Just add the line of code inside "- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions" 只需在“ - (BOOL)应用程序:(UIApplication *)应用程序中添加代码行didFinishLaunchingWithOptions:(NSDictionary *)launchOptions”

[application setApplicationSupportsShakeToEdit:NO];

Swift 2.x Swift 2.x

Add single line of code in appDelegate.swift didFinishLaunchingWithOptions method 在appDelegate.swift didFinishLaunchingWithOptions方法中添加单行代码

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { func应用程序(应用程序:UIApplication,didFinishLaunchingWithOptions launchOptions:[NSObject:AnyObject]?) - > Bool {

    application.applicationSupportsShakeToEdit = false
    return true
}

Swift 3.x Swift 3.x

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

    application.applicationSupportsShakeToEdit = false
    return true
}

See: How to stop the UITextField from responding to the shake gesture? 请参阅: 如何阻止UITextField响应摇动手势?

For phonegap, set this property within the webViewFinishLoad method in AppDelegate.m 对于phonegap,请在AppDelegate.m中的webViewFinishLoad方法中设置此属性

First solution 第一解决方案

The easiest way is to use this plugin: 最简单的方法是使用这个插件:

https://github.com/nleclerc/cordova-plugin-ios-disableshaketoedit https://github.com/nleclerc/cordova-plugin-ios-disableshaketoedit

Second solution 二解决方案

If you don't want to use the above plugin then you should do it manually by opening the MainViewController.m and change webViewDidFinishLoad to look like this: 如果你不想使用上面的插件,那么你应该通过打开MainViewController.m手动完成它,并将webViewDidFinishLoad更改为如下所示:

- (void)webViewDidFinishLoad:(UIWebView*)theWebView
{
    // Black base color for background matches the native apps
    theWebView.backgroundColor = [UIColor blackColor];

[UIApplication sharedApplication].applicationSupportsShakeToEdit = NO;

    return [super webViewDidFinishLoad:theWebView];
}

Third solution 第三种方案

it's another solution i designed to disable "Shake To Undo" which works on text inputs. 这是我设计的另一个解决方案,禁用“Shake To Undo”,它可以处理文本输入。 note that users are not allowed to insert emojis. 请注意,不允许用户插入表情符号。

<body>
    <textarea cols="40" rows="8" name="textarea" id="textarea"></textarea>

    <script>
    textArea = document.getElementById("textarea");

    textArea.onkeypress =  function(event){
        event.preventDefault();
        var nationUnicode=event.which;
        var utf8=encodeURIComponent(String.fromCharCode(parseInt(nationUnicode, 10)));
        if  (utf8.search("%EF") != 0) //value is not an Emoji
            textArea.value= textArea.value + String.fromCharCode(nationUnicode);
    }

    textArea.onkeydown = function (event){
        if (event.keyCode == 8){
            event.preventDefault();
            var txtval = textArea.value;
            textArea.value = txtval.slice(0, - 1);
        }   
    }
    </script>
</body>

Perhaps try event.preventDefault(); 也许尝试event.preventDefault(); in your DeviceMotionEvent listener ? 在您的DeviceMotionEvent侦听器中?

Looking around Stack Overflow, I can see other people have had success disabling default "events" using CSS (oddly). 看看Stack Overflow,我可以看到其他人已成功使用CSS禁用默认“事件”(奇怪)。

Prevent default Press but not default Drag in iOS MobileSafari? 防止默认按iOS而不是默认拖动iOS MobileSafari?

将输入放入iframe,在不需要输入时重新加载iframe。

I found a workaround that works consistently: you can host the text input in a frame. 我找到了一致的解决方法:您可以在框架中托管文本输入。 When you're done with the input, remove the frame from the page. 完成输入后,从页面中删除框架。 This seems to do the trick. 这似乎可以解决问题。

function resetIframe() {
  // Remove the old frame
  document.body.removeChild(frame);

  // Create a new frame that has an input internally
  frame = document.createElement('iframe');
  frame.src = '/html-with-input.html';
  frame.style = 'border: 0; height: 30px; width: 200px;'
  document.body.appendChild(frame);

  // When the frame is ready, grab a reference to the input
  frame.addEventListener('load', () => {  
    const input = frame.contentDocument.body.querySelector('input');
    console.log(frame, frame.contentDocument.body)

    // When the input changes, grab the value
    input.addEventListener('input', e => {
      document.getElementById('output').innerText = e.target.value;
    });
  });
}

Here's a proof of concept: https://codepen.io/joshduck/full/RJMYRd/ 这是一个概念证明: https//codepen.io/joshduck/full/RJMYRd/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM