Skip to content

Latest commit

 

History

History
54 lines (33 loc) · 7.35 KB

README.md

File metadata and controls

54 lines (33 loc) · 7.35 KB

SHCWebView

SHCWebView is WebView compatible with NSTextFinderClient protocol. It supports find functionality pretty much like in the Apple Safari browser. Best results can be accomplished when NSTextFinder object is configured to use "Incremental searching" and "Dim Content View".

Features

SHCWebView can properly handle a Unicode composite characters when the stripCombiningMarks property is set to YES, this property has also an another effect - offers a special mode of searching. When set to YES parsing text nodes from the DOM tree use a kCFStringTransformStripCombiningMarks transform of CFStringTransform foundation method to ease searching of non-ASCII characters by ex."

  • content text is "zażółć" (polish language), stripCombiningMarks set to YES, to match content search string could be "zazolc" if stripCombiningMarks set to NO you should enter exact string to match "zażółć".

Usage

Use it like normal WebView (in Interface Builder remember to change class to SHCWebView). Configure textFinder property to your NSTextFinder object - it's necessary to communicate between WebView and NSTextFinder (by ex. when the WebView change its size).

Setup

To achieve proper working with NSTextFinder interface we need to embed WebView into NSScrollView (because it already implements NSTextFinderBarContainer protocol). It's possible to work only with WebView internal scroll object (webView.mainFrame.frameView.documentView.enclosingScrollView) but it has some drawbacks - when NSTextFinder bar is showing at the top of WebView links coordinates inside WebView are not updated with new control size (shrunk down by the NSTextFinder bar height) and 'clickable' links appear to browser with offset equal to NSTextFinder bar height. I don't know remedy to this behavior. When NSTextFinder bar is displayed at the bottom of the control, it works as intended without any glitches.

So, common setup is to use SHCWebView inside NSScrollView. If you don't use Auto Layout, remember to set NSClipView (NSScrollView.contentView) property autoresizeSubviews to NO.

Limitations

NSTextFinder is well suited for a non-layered text, but with WebView where today we have many layers (DIVs), some of them can be hidden or floated above other content or dynamic content updated with JavaScript, it shows its weakness.

NSTextFinder assumes that found text is visible all the time and in situation when we have a floating header (like menu by ex.) the 'holes' in dimming view will be displayed over our header (not exactly what we want) - but the same problem you can observe in the Apple Safari browser.

Inner workings

NSTextFinder uses client object implementing NSTextFinderClient protocol to achieve its functionality:

  1. Asks client for a string representation of the content. (string method)
  2. Using a regular expression on that string NSTextFinder makes a text ranges of a matched phrase.
  3. For every matched text range it asks the client for a bounding rect of that range - to display 'holes' in dimming view (rectsForCharacterRange: method)
  4. Asks client to draw the current text range (drawCharactersInRange:forContentView:)
  5. When navigating next/prev the search result, it asks client to scroll client view to visible rect of the search result. (scrollRangeToVisible: method)
Step 1

To make string representation of the content we have to walk DOM tree and extract all text nodes content, remembering DOM nodes and offset positions of the text content in that nodes (as an NSArray of SHCWebViewTextRange objects). If the stripCombiningMarks property is set to YES also a transform on the text content is performed.

Step 2

It's the NSTextFinder role, we don't need to do anything.

Step 3

To get bounding rects of text range we use Java Script by WebView.evaluateWebScript method.

  • Firstly create the DOMRange object and configure it using remembered earlier data (from SHCWebViewTextRange objects)
  • Next for this DOMRange execute Java Script to get array of a bounding rects of that range.
Step 4

As we can get bounding rects of the text range, to display that range we simply use WebView documentView drawRect: