简体   繁体   中英

Disable scrolling animation in a WebView in Mac OS X Lion?

How do you disable the animation that occurs when you use the arrow keys to navigate within a WebView in Mac OS X Lion?

The behavior I'm trying to change appears to be the default for WebViews on Mac OS X Lion. If you load a document into a WebView, set the insertion point, and then use the Up Arrow and Down Arrow keys to navigate, scrolling is not instantaneous — there's an animation (the view visibly scrolls up or down).

Here's an Xcode project you can use to see this behavior (just run the application, set the insertion point within the document, and then use the Up Arrow and Down Arrow keys to navigate such that the view scrolls): http://dl.dropbox.com/u/78928597/WebViewTest.zip

The behavior I'm trying to achieve is what happens in Safari. If you open an html document whose contenteditable attribute is set to true in Safari, you can set the insertion point within the document, and then navigate by using the Up Arrow and Down Arrow keys. When you navigate in this way, scrolling is not animated. The view scrolls instantaneously.

Here is an html document you can use to see this behavior: http://dl.dropbox.com/u/78928597/WebViewTest.html

Since Safari uses a WebView, and it scrolls instantaneously, it seems like there should be a way to change the scrolling behavior of any WebView, but I've had no luck in finding it.

Note that you need to set the insertion point before you navigate with the arrow keys, otherwise you'll see different behavior.

I think there is a way to do this, but it requires using the Objective-C runtime to modify a private method of a private class.

To use the Objective-C runtime, add

#import <objc/runtime.h>

to the #import directives at the top of AppDelegate.m in your Xcode project.

Scroll animation appears to occur in the private method

- (BOOL)_scrollTo:(const CGPoint *)pointRef animate:(NSInteger)animationSpecifier flashScrollerKnobs:(NSUInteger)knobFlashSpecifier

of NSClipView .

We cannot modify the NSClipView object (actually an instance of a private class WebClipView ) managed by a WebView through subclassing. Instead, we can use a technique called method swizzling .

In the @implementation of your AppDelegate class, add

static BOOL (*kOriginalScrollTo)(id, SEL, const CGPoint *, NSInteger, NSUInteger);

static BOOL scrollTo_override(id self, SEL _cmd, const CGPoint *pointRef, NSInteger animationSpecifier, NSUInteger knobFlashSpecifier)
{
    return kOriginalScrollTo(self, _cmd, pointRef, 2, knobFlashSpecifier);
}

+ (void)load
{
    SEL selector = @selector(_scrollTo:animateScroll:flashScrollerKnobs:);
    id WebClipViewClass = objc_getClass("WebClipView");
    Method originalMethod = class_getInstanceMethod(WebClipViewClass, selector);
    kOriginalScrollTo = (void *)method_getImplementation(originalMethod);
    if(!class_addMethod(WebClipViewClass, selector, (IMP)scrollTo_override, method_getTypeEncoding(originalMethod))) {
        method_setImplementation(originalMethod, (IMP)scrollTo_override);
    }
}

You can read more about what's happening here in Mike Ash's article, “Method Replacement for Fun and Profit” ; I'm using “Direct Override” method swizzling.

As a result of this code, scrollTo_override() will be called instead of the WebClipView method -[_scrollTo:animateScroll:flashScrollerKnobs:] . All scrollTo_override() does is call the original -[_scrollTo:animateScroll:flashScrollerKnobs:] with 2 as the animationSpecifier . This seems to prevent scroll animation from occurring.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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