UIWebViewの同期ロード

こんにちは、Habr!

それはすべて、厳密に指定されたフォーマット(特定のフォントでUILabelのセットを使用できる)ではなく、任意のフォーマットのUITableViewCell内にフォーマットされたテキストを表示する問題の解決策を見つけることから始まりました。 はい、フォーマットは単純なhtmlタグで設定できます。 この問題はさまざまな方法で解決できます。


記事のタイトルから推測したかもしれないが、私は最後の方法に決めたが、この方法を試した人たちは非常に不快な落とし穴を知っている。 それを取り除く方法は、カットの下で教えます。

UITableViewCell内のUIWebView


そして、この問題のちょっとしたこと-UIWebViewはコンテンツを非同期にロードします。 UIWebViewをUITableViewCell内およびcellForRowAtIndexPathに配置すると、コンテンツ自体をロードします。実際のロードは、スクロールパスに関連するすべてのタッチイベントの後に非同期で発生します。 見るのはあまり楽しくありません。スクロールが停止した後にのみコンテンツが更新され始めます。
この制限を回避するには、理論を少し理解する必要があります。 この場合の非同期は、RunLoopsを使用して実装されます。 runloopはスレッドに設定されているため、同じスレッドで非同期に呼び出しを行うことができます。 さらに、各呼び出しは独自のRunLoopModeで行われます。これは優先度に類似しているため、最初に、優先度が最も高いすべての呼び出しが現在のループから選択され、降順になります。 もう1つの重要なポイントは、ネストされたrunloopを実行する機能です。
UIWebViewでloadHTMLSTring:を呼び出すとどうなりますか? ほとんどの場合、performSelectorは、非優先RunLoopModeの指示で実行されます。 原則として、これは正しいです。UIWebViewへのコンテンツのロードは簡単なタスクではなく、時間がかかります。テーブルをスクロールするときにこれを行うと、スクロール自体が著しく遅くなる可能性があります。 しかし、コンテンツが十分に軽い場合(2行の最も単純なHTML、たとえば)-ダウンロードは十分に高速です。
loadHTMLStringを呼び出した後にすべてのタッチイベントが動作するのを待たずにこのダウンロードを完了するには、ネストされた実行ループを優先度の低いRunLoopMode-NSDefaultRunLoopModeで実行する必要があります。
CFRunLoopRunInMode CFStringRef NSDefaultRunLoopMode、 1NO ;

WebViewがまだロードされた後、このRunLoopを停止することが重要です。そうしないと、UIがハングするだけです。
CFRunLoopRef runLoop = [ [ NSRunLoop currentRunLoop ] getCFRunLoop ] ;
CFRunLoopStop runLoop ;

UISynchedWebView


ネストされたランループを開始し、WebView自体で停止することですべてを接続しましょう。 サブクラスUIWebViewを作成し、UISynchedWebViewという名前を付け、その中にloadHTMLStringを再定義して、基本実装を呼び出した後にネストされたrunloopを実行します。 問題は、いつ停止するかです。 コンテンツが読み込まれたらすぐに停止する必要があります。 これを判断するには、外部ユーザーコードの透明性を維持するために、新しいWebViewを独自のデリゲートにする必要があります。 これを行うには、外部デリゲートのクラスメンバー変数を作成し、そのセッターとゲッターを再定義し、デリゲートのコードを呼び出した後、外部デリゲートの対応するメソッドを呼び出します。 このようなもの:

@interface UISynchedWebView UIWebView <UIWebViewDelegate>
{
id anotherDelegate;
}
@end


- void webView UIWebView * webView didFailLoadWithError NSError * エラー
{
[ self performSelector @selector stopRunLoop withObject nil afterDelay .01 ] ;

if [ anotherDelegate respondsToSelector @selector webView didFailLoadWithError :) ]
[ anotherDelegate webView webView didFailLoadWithError error ] ;
}

- BOOL webView UIWebView * webView shouldStartLoadWithRequest NSURLRequest * request navigationType UIWebViewNavigationType navigationType
{
if [ anotherDelegate respondsToSelector @selector webView shouldStartLoadWithRequest navigationType :) ]
return [ anotherDelegate webView webView shouldStartLoadWithRequest request navigationType navigationType ] ;
YESを 返します。
}

- void webViewDidFinishLoad UIWebView * webView
{
[ self performSelector @selector stopRunLoop withObject nil afterDelay .01 ] ;
if [ anotherDelegate respondsToSelector @selector webViewDidFinishLoad :) ]
[ anotherDelegate webViewDidFinishLoad webView ] ;
}

- void stopRunLoop
{
CFRunLoopRef runLoop = [ [ NSRunLoop currentRunLoop ] getCFRunLoop ] ;
CFRunLoopStop runLoop ;

}

- void webViewDidStartLoad UIWebView * webView
{
if [ anotherDelegate respondsToSelector @selector webViewDidStartLoad :) ]
[ anotherDelegate webViewDidStartLoad webView ] ;
}


警告


最後に、このメソッドを使用すると、太字で強調表示するか、背景で強調表示する必要がある美しいフォーマットのテキストを得ることができます-表のセルで多くの労力なしでiOS3.xと互換性があります。 ただし、前述したように、そのようなコンテンツをダウンロードするとUIがブロックされ、コンテンツが十分に重い場合、ブロックが目立つようになり、非常に悪くなります。 この方法を使用できる場所はどこでもないことに注意してください!

PS Demoprojectはここから入手できます

Source: https://habr.com/ru/post/J125931/


All Articles