如何在web应用程序中禁用iOS的“摇一摇撤销”功能

13
我正在为iPad构建一个Web应用程序游戏。它包含一个摇晃操作来触发其中一个屏幕的事件。但是由于它还具有信息收集表单,有时该摇晃动作会触发令人讨厌的“撤消摇晃”对话框。当用户到达摇晃操作时,用户输入的表单数据甚至在DOM中也不存在了,因此在我看来,不应触发撤消功能。但不幸的是它确实触发了。这个应用程序不能被包装在原生包装器(PhoneGap或其他)中,所以我想知道是否有可能通过CSS或JavaScript禁用特定Web页面/应用程序的此功能?谢谢。
9个回答

10
这篇博客描述了使用DeviceMotionEvent监听器进行非常简单的晃动检测:

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

尝试以下代码以禁用撤销事件的发生:
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();
});

我能想到的另一件事是在完成输入后将文本输入转换为只读项目。 可以推测,只读输入字段无法使用撤消功能,尽管我没有测试过。奇怪的是,即使输入不再是DOM的一部分,该功能仍然会触发。

我不确定是否可以通过CSS中的-webkit-属性来防止抖动操作。这是一份Webkit特定的CSS属性列表(可能不包含所有可访问的属性)。

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


好主意,但不幸的是没有成功。即使将事件附加到输入元素本身(或使用Zepto的委托函数),撤消对话框仍然会弹出。 - Johan Sahlén
哦,将输入设置为只读仍会使对话框弹出,不幸的是。 - Johan Sahlén
1
这在当前版本的cordova(7.0.1)中不起作用。Tobias Christian Jensen的答案完美地解决了这个问题。只需将[UIApplication sharedApplication].applicationSupportsShakeToEdit = NO;添加到Classes/AppDelegate.mdidFinishLaunchingWithOptions函数中即可。 - David Schumann

9

对于PhoneGap,我只需在webViewDidFinishLoad类中插入以下行:

[UIApplication sharedApplication].applicationSupportsShakeToEdit = NO;

这对我非常有效。

只需进入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];
}

4

我最近在开发一个游戏时遇到了这个问题,其中一个用户通过WebSockets配对设备。不幸的是,上面提到的解决方案都不起作用。

简而言之,游戏包括用户通过手机上的表单订阅频道。一旦订阅,他们会摇晃设备,然后在桌面上播放场景。问题是每当有人摇动iOS设备时,“撤消输入”警报就会弹出。

以下是我找到的两种解决方法。

  1. 永远不要让您的输入失去焦点。如果输入是表单的一部分,则需要防止表单提交。您还必须在任何锚点上侦听“touchstart”而不是“click”,并防止touchstart事件传播。这将防止该锚点获得焦点并使您的输入失去焦点。这种方法确实有一些缺点。

  2. 向窗口添加“keydown”事件处理程序,并防止事件传播。这会防止任何值被插入到iOS设备的输入中。我还不得不创建一个将键码映射到正确字符的对象。这是我选择的路线,因为我只需要6个字符代码,所以我只需要数字。如果您需要更多内容,这可能会很麻烦。在按下键时,通过键码抓取该字符并设置输入的值。这会欺骗iOS,使其认为从未通过键盘插入任何值,因此没有任何内容可供撤消。

请注意,防止keydown事件传播不会防止在原生安卓浏览器上进行输入,因此它将正常运行。但是这没关系,因为这不是安卓问题。如果接收到此事件,则可以通过在输入本身上侦听输入事件并删除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); 

谢谢分享!我现在没有条件设置测试用例,但是你的答案似乎经过了很好的研究,并且完成了我在原始问题中所要求的,所以如果没有人有任何异议,我将接受它作为正确答案 :) - Johan Sahlén

4

Objective-C

只需在"- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions"方法中添加这一行代码。

[application setApplicationSupportsShakeToEdit:NO];

Swift 2.x

appDelegate.swift文件的didFinishLaunchingWithOptions方法中添加单行代码。

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

    application.applicationSupportsShakeToEdit = false
    return true
}

Swift 3.x

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

    application.applicationSupportsShakeToEdit = false
    return true
}

1
我找到了一种可靠的解决方法:将文本输入框放在一个框架中进行托管。当你完成输入后,从页面中删除框架即可。这似乎是行得通的。
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;
    });
  });
}

这是一个概念验证:https://codepen.io/joshduck/full/RJMYRd/

1

1

1

第一种解决方案

最简单的方法是使用这个插件:

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

第二种解决方案

如果您不想使用上述插件,那么您应该手动打开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];
}

第三种解决方案

这是我设计的另一种解决方案,用于禁用文本输入中的“摇一摇撤销”功能。请注意,用户不允许插入表情符号。

<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>

非常感谢解决方案3! 正是我所需要的! 一些表情符号似乎可以使用(例如闪光)。 - vinni

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接