我如何对一个changeAttributes有意义的反应:从WebView的代理传递? [英] How can I react meaningfully to a changeAttributes: delegation pass-through from WebView?

查看:181
本文介绍了我如何对一个changeAttributes有意义的反应:从WebView的代理传递?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

WebView 通过 WebEditingDelegate 支持委托人为各种操作实现自定义行为的机制 WebView (或私人 WebHTMLView )接收。当一个动作如:

   - (void)changeAttributes:(id)sender 

WebHTMLView 中接收,它被传递到委托方法:

   - (BOOL)webView:(WebView *)webView doCommandBySelector:(SEL)command 
pre>

不幸的是,该机制并未规定在原始操作方法中传送 sender 。 / p>

对于绝大多数操作,发送者并不重要,但是对于changeAttributes和changeFont,合同要求 sender 由收件人调用以便例如 convertAttributes: convertFont:



code> changeFont case,结果是调用 [[NSFontManager sharedFontManager] convertFont:] 是足够的,巧合的是,这是什么



changeAttributes 案例中,特别是当删除线改变时,发送者可能是私有类 NSFontEffectsBox ,其大概对应于字体面板中负责更改删除/设置的子部分。



不幸的是,调用 [[NSFontManager sharedFontManager] convertAttributes:] 不会获得预期的属性更改。这使得一个代表有兴趣在一个难题中有意义地实现这个方法:


  1. WebKit不传达发送者,因此委托不能使合同 [sender convertAttributes:] 调用。


  2. code> changeAttributes:调用发送到一个私有WebKit类, WebHTMLView ,它不能被子类化,例如,自定义行为 changeAttributes:


  3. $ c> call, NSFontEffectsBox ,是一个私人类,无法访问 [NSFontEffectsBox sharedFontEffectsBox]


似乎没有办法让开发人员有意义地覆盖 changeAttributes: for WebView 的行为。



任何想法?

解决方案

这是一个邪恶的。一个合适的邪恶的动作(它们都不是特别干净或理想的)将是:


  1. 做一些内联汇编器回头看堆栈从堆栈中读取发送者参数(或调用者的调用者,视情况而定)。这当然假定当调用 WebHTMLView 时发送者被放置在栈上而不是在%eax


  2. WebHTMLView 使用类似于 __ my_evil_hacky_nasty_ugly_changeAttributes_thing:的方法,并在运行时使用objC运行时的method_exchangeImplementations()函数来交换你的类别的实现。你的方法变成 changeAttributes:,他们的变成 __ my_evil_hacky_nasty_ugly_changeAttributes_thing:,然后你可以调用传递原始调用。 / p>


正如我所说,两者都不是特别理想,但第二个优点是完全的运行时支持明确设计让你这样做),因为你在运行时查找类和方法,它是容错的。然而,在这种情况下,失败会让你回到方形。



真的,它需要一个错误记录对WebKit,让他们通过发送方,使其有意义。你的覆盖版本可能寻找一个方法 - (BOOL)webView:(WebView *)webView doCommandBySelector:(SEL)selector sender:(id)sender ,否则只是调用到原来的方法。这是苹果的代码应该做的,TBH。


WebView supports, through the WebEditingDelegate, a mechanism for the delegate to implement custom behavior for a variety of actions the WebView (or the private WebHTMLView) receives. When an action such as:

-(void)changeAttributes:(id)sender

is received in WebHTMLView, it is passed through to the delegate method:

-(BOOL)webView:(WebView *)webView doCommandBySelector:(SEL)command

Unfortunately, the mechanism does not provide for conveyance of the "sender" in the original action method.

For the vast majority of actions, the sender is unimportant, but for changeAttributes, and changeFont, for example, the contract requires that "sender" be called by the recipient in order to e.g. convertAttributes: or convertFont:.

For the changeFont case, it turns out that calling [[NSFontManager sharedFontManager] convertFont:] is sufficient, as coincidentally this is what the sender is.

In the changeAttributes case, in particular when strikethrough is changed, the sender may be a private class "NSFontEffectsBox" which presumably corresponds to the subsection of the font panel that is responsible for changing strikethrough/etc settings.

Unfortunately, calling [[NSFontManager sharedFontManager] convertAttributes:] does NOT obtain the expected attribute changes. This leaves a delegate who is interested in implementing this method meaningfully in a bit of a conundrum:

  1. WebKit does not convey the sender, so the delegate can't make the contractual [sender convertAttributes:] call.

  2. The changeAttributes: call is sent to a private WebKit class, WebHTMLView, which cannot be subclassed to, e.g., customize the behavior of changeAttributes:.

  3. The sender for the changeAttributes: call, NSFontEffectsBox, is a private class and cannot be accessed e.g. as [NSFontEffectsBox sharedFontEffectsBox].

In short: there appears to be no way for a developer to meaningfully override the behavior of changeAttributes: for a WebView.

Any ideas?

解决方案

This is an evil one. A suitably evil pair of actions (neither of them particularly clean or ideal) would be:

  1. Do some inline assembler to look back up the stack to read the sender argument from the caller's stack (or the caller's caller, as the case should be). This of course assumes that the sender is placed on the stack and not in %eax when the call to WebHTMLView was made. That will always apply to PowerPC code however, so it's likely a non-starter there.

  2. Put a category on WebHTMLView with a method named something like __my_evil_hacky_nasty_ugly_changeAttributes_thing: and at runtime use method_exchangeImplementations() from the ObjC runtime to swap your category's implementation with theirs. Your method becomes changeAttributes: and theirs becomes __my_evil_hacky_nasty_ugly_changeAttributes_thing:, which you can then call to pass on the original call.

As I said, neither is particularly ideal, but the second has the advantage of full runtime support (i.e. the runtime is explicitly designed to let you do this), and since you're looking up the class and methods at runtime, it's failure-tolerant. Failure in this case gets you back to square one however.

Really it needs a bug logged against WebKit to have them pass on the sender to make it meaningful at all. Your overridden version could potentially look for a method -(BOOL)webView:(WebView*)webView doCommandBySelector:(SEL)selector sender:(id)sender and call that if found, otherwise just call through to the original method. This is what Apple's code should be doing, TBH.

这篇关于我如何对一个changeAttributes有意义的反应:从WebView的代理传递?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆