UIWebView能否在不成为第一响应者的情况下处理用户交互?

fra*_*ous 11 uiwebview ios swift

我的应用程序有一个类似于iOS Messages应用程序的视图,工具栏样式视图包含停靠在屏幕底部的文本字段:

当用户点击文本字段以输入文本时,键盘会向上滑动,工具栏会随之滑动:

我已经通过使工具栏视图inputAccessoryView在其上UITableViewController并使其主要view成为第一响应者来实现此目的.主体文本"这是一个链接......"实现为UIWebView从Web服务填充HTML并需要支持格式和链接.

我遇到的问题是当用户点击它的任何地方时UIWebView,它成为第一响应者并inputAccessoryView隐藏自己:

我不想发生这种事.我需要用户能够点击Web视图中的链接,因此不能响应用户交互.

是否UIWebView 必须成为处理水龙头的第一响应者?如果是这样,webview可以处理链接上的点击,但是然后将触摸事件传递给主要view并立即再次制作第一个响应者吗?任何关于如何解决这个问题的指示都会感激不尽.

更新:

所以看来在网络视图不具备成为第一个响应者来处理链接的水龙头,我可以破解与周围的问题,我的方式:

extension UIView {

    public override func becomeFirstResponder() -> Bool {
        // Actual view is instance of private class UIWebBrowserView, its parent parent view is UIWebView
        if self.superview?.superview is UIWebView {
            return false
        } else {
            return super.becomeFirstResponder()
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

谁能告诉我如何以非黑客的方式实现上述目标?

Ger*_*ero 2

我在这里可以提供两件事。一个快速而肮脏的解决方案(我希望)和一个建议(或者你可以称其为我感到惊讶)。

肮脏的解决方案:实施这样的事情

func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
    self.shouldCloseKeyboard = false
    return true
}

func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
    return self.shouldCloseKeyboard
}

@IBAction func inputDone(_ sender: AnyObject) {
    if self.inputField.isFirstResponder {
        self.shouldCloseKeyboard = true
        self.inputField.resignFirstResponder()
    }
}
Run Code Online (Sandbox Code Playgroud)

inputDone:将连接到您的“发布”按钮,并且显然还包含您发布时需要执行的任何其他操作(或者在关闭它之前将其放入textFieldShouldEndEditing,即完成输入)。shouldCloseKeyboard是一个Bool初始化为 的 var false,但随后使用,如您在此处看到的那样。显然,这inputField是您的文本字段(不是整个附件视图!),并且整个类都符合,UITextFieldDelegate并且文本字段的实例设置为delegate通过 Interface Builder(或其他方式)。

这应该如您所愿。我在一个示例项目中尝试了它,尽管我没有像您一样使用工具栏(见下文)或将所有内容放入表格中。我只是做了一个简单的网络视图,下面有一个输入和一个关闭内容的按钮。一旦文本字段是第一响应者,即键盘打开,我可以在网络视图中单击,跟随链接等,并且键盘不会消失。单击按钮时,它正确关闭(当时我没有打扰第一响应者)。

现在让我感到惊讶的是:起初我认为简单地使用工具栏让它inputAccessoryView在键盘上移动是一个很好的技巧。然而,经过进一步的思考,我得出的结论是我不会这样做(而是在显示键盘时手动向上移动视图以及其他任何东西。您可能也必须这样做,以确保没有什么重要的事情发生)被键盘或附件视图遮挡)。原因是附属视图不应该已经存在于视图层次结构中的其他地方。事实上,当我在演示项目中以简单的方式快速尝试它时(我将按钮和文本字段嵌入到 中,为此定义UIView了一个出口,然后将其设置为inputAccessoryView代码中的方式,在不同的地方进行了尝试),应用程序崩溃了。这是合理的,因为文本字段本身是容器的子视图,这将是附件,但同时也是视图控制器主视图的子视图......我很惊讶你设法克服了这一切。

简而言之,这种方法似乎很麻烦。根据我理解输入附件视图的方式,它们更适合用于附加视图,而不是用于已在屏幕上呈现的控件(因此已经是视图层次结构的一部分)。在另一个项目中,我专门通过简单地伪造来避免弄乱它:我有一个作为附件加载的单独视图,它看起来就像与其“关联”的控件。显示这个假视图后,我将第一响应者切换到它(好吧,切换到它的一个子视图,一个文本字段,因此键盘保持打开状态),我的用户可以进行编辑。按下完成按钮后,我将输入复制到“原始”文本字段,一切都很整洁。

因此,从长远来看,我建议您(或任何人)重新考虑将现有视图作为附件,而是选择在需要时进行“常规”视图转换,并保留附件视图作为“附加助手”。

更新:

好吧,我上面描述的(在“快速而肮脏”的解决方案中)假设您只想要一种退出编辑的方法,即防止其他任何东西成为第一响应者,并且只在按下“完成”后让它失去第一响应者。

对于 webView,它完成了同样的事情。当尝试仅阻止webView成为第一响应者时,问题是如何识别导致文本字段失去第一响应者状态的原因。我认为你的“hacky”方法指向了正确的方向,但由于你不应该覆盖扩展(iirc)中的方法,我建议在这种情况下对 UIWebView 进行子类化。然后,您还可以给它一个属性来打开和关闭该行为(即,如果文本字段成为第一响应者/开始编辑,则将其打开,一旦丢失,则将其关闭)。这对于子类来说应该足够了:

class MyWebView: UIWebView {

    public var shouldNotBecomeFirstResponder = false;

    override public var canBecomeFirstResponder: Bool {
        if self.shouldNotBecomeFirstResponder {
            return false
        } else {
            return super.canBecomeFirstResponder
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

(我确保如果Bool未设置新的,则会模仿原始行为,这应该会导致该类在所有其他情况下的行为与 webView 完全相同。不过,我没有在您的设置中测试这一点。)设置shouldNotBecomeFirstResponder显然应该在所有适当的地方。例如,我想您可以监听键盘(消失)出现通知。

一般来说,可能还有其他不需要子类的方法。除了我上面的第一个建议之外,您也许还可以使用手势识别器来确定是否shouldCloseKeyboard必须设置,但这可能需要更多工作,并导致难以阅读/维护代码。可能很难弄清楚首先调用什么,计算 webView 是否被单击的逻辑(因此必须在它实际成为第一响应者之前shouldCloseKeyboard进行设置!)或其他东西(在这种情况下必须以某种方式取消设置)。

所以我#d会选择子类化,即使子类几乎没有给webView添加任何东西。:)