実際のプロジェクトのLiang-Knutアルゴリズム、またはiOSのリーダーをどのようにしたか

みなさんこんにちは! 今回は、iBooksに代わるものをどのように実装したかを伝えたいと思います。 以前の投稿で、テキストにソフトハイフンを配置するアルゴリズムについて書きました。 リーダーを作成するときに便利でした。 アプリケーションでその作業を視覚的に評価できます 。 しかし、これに加えて、プロジェクトの実装中に、CSSを使用したHTMLの解析とレンダリング、カスタムデザインコントロールの実装など、他の多くの興味深いことに出くわす必要がありました。 デザイナーのrashapastaは、ペンで実装する必要がある非標準のインターフェイスの問題を私に投げかけるのが大好きですが、 まず最初にやることです。



UI(またはタンバリンと踊る)



プロジェクトのUIに関しては、水平ページングを使用してグリッドテーブルを作成するのは簡単な作業ではありませんでした。 いつものように、既製のソリューションを探して、私はstackoverflow.comに登りましたが、残念ながら、私が経験したことは、ある程度不適当でした。

AQGridViewには大きな期待がありましたが、判明したように、水平方向の塗りつぶしとページングからは、空のスタブしかありません。 彼女に2度目のチャンスを与え、テーブルを90度回転させて、多くの人に使い慣れたトリックを適用することにしました。 最初は、このオプションは機能しているようで、多かれ少なかれ受け入れられましたが、ここでも石がありました。



AQGridView自体と標準のUIScrollViewのバグにより、このコンポーネントを使用することはできませんでした。 状況によっては、グリッドが絶えず壊れ、一部のセルが脱落し、注文が絶えず飛んでしまいました。 曲率に関する疑念を払拭するために、キットのデモで問題を再現しようとしました-バグが確認されました。

UIScrollViewとその派生物については-ここでも最初にAQGridViewに罪を犯しましたが、 UITableViewを使い始めたとき、問題が繰り返されました。 バグの本質は、 UIScrollViewが変換によって切り替えられると 、バウンス効果が落ちることであり、これはiOSにとって非常にく不自然です。



経験的に、 UIScrollViewのサイズ変更と移動は、デバイスが回転したときに非難されることが判明しました。これは、 layoutSubviewsハンドラーを介して手で行われました。 シャーマニックタンバリンをとると、すべてがcenterプロパティを介して回転したUIScrollViewの位置を壊すことがわかりました。 おそらく他のいくつかの条件がありましたが、非常に多くのオプションが試行されたため、もはや思い出すことができません。



この長い歴史はすべて、古き良きUITableViewで歪曲されなければなりませんでした。 バウンスを修正し、回転の問題は解決しました。 彼はテーブルセルを1ページのサイズにし、いくつかのサブセルで構成され、各サブセルは個別のクラスのインスタンスとして実装されます。 次のようになりました。







HTMLとLiang-Knuthアルゴリズムを使用します。



人気のある電子書籍形式の解析とレンダリングも別の話です。 HTMLは原則として難しくありません。libxmlは素晴らしい仕事をしました。 HTMLファイルは再帰的に処理され、テキストのブロックに分割され、対応する属性が各ブロックに設定されます。 このすべてをCoreTextからフレームセッターに駆動することは残り、これで完了です。 しかし、そこにありました! ハイフネーションと幅の整列を行う必要があります。 レベルを下げて、フレームセッターではなくタイプセッターを使用する必要がありました。 これを使用すると、たとえば次の関数を使用して、テキストを行に簡単にカットできます。



CFIndex CTTypesetterSuggestClusterBreak( CTTypesetterRef typesetter, CFIndex startIndex, double width);
      
      





行に分割するプロセスでは、ギャップの場所を決定する必要があります。 単語の途中でギャップが発生する場合は、ハイフンを正しく配置する必要があります。 ここで、上記のLiang-Knutアルゴリズムの実装が役立ちます



レンダリング(またはユーザーを待たせないでください!)



残っているのは、結果として生じるテキスト行の山をページにカットすることであり、レンダリングすることができます。 経験的に、レンダリング前のこの一連のテキスト処理操作はすべて、かなりの時間がかかることが判明しました。 プロファイラーから、ハイフネーションが原因であることに気付きました。 私は本の計算をバックグラウンドで別のスレッドで実行しました-より速く動作し始めました。



唯一のマイナスは、レンダリングの進行中は、巻き戻しスライダーを使用できないことです。 まだ処理されていないチャプタに移動する必要がある場合は、できるだけ早く画面に表示するために、最初に処理キューに入れてください。



その結果、かなり良い結果が得られ、iPadでは書籍が非常に迅速に処理されます(これがオンザフライでレンダリングされる場合)。



さまざまな画面の向きでレンダリングされたページは次のようになります。







HTTP AFNetworkingの作業では、通常どおり、強くお勧めしました。 確かに、「リーク」が1つありました。メモリリークのアプリケーションを分析すると、循環リンクに関連するファイルダウンロードの進行状況の表示に問題が明らかになりました。 setDownloadProgressBlockメソッドに 、次のようなブロックがありました。



 if ([self.progressDelegate respondsToSelector:@selector(fileDownloadRequest:progressBytes:withTotalBytes:)]) { [self.progressDelegate fileDownloadRequest:self progressBytes:alreadyDownloadedBytes+totalBytesRead withTotalBytes:alreadyDownloadedBytes+totalBytesExpectedToRead]; }
      
      





ブロックのコードにselfが存在すると、循環依存が発生しました。 これは、デリゲートへのポインタがコピーされる別のローカル変数を作成することで解決され、この変数はすでにブロックで使用されています。 次のようになりました。



 id<FileDownloadProgressDelegate> progress = self.progressDelegate; [self.request setDownloadProgressBlock:^(NSInteger bytesRead, NSInteger totalBytesRead, NSInteger totalBytesExpectedToRead) { if ([progress respondsToSelector:@selector(fileDownloadRequest:progressBytes:withTotalBytes:)]) { [progress fileDownloadRequest:self progressBytes:alreadyDownloadedBytes+totalBytesRead withTotalBytes:alreadyDownloadedBytes+totalBytesExpectedToRead]; } }];
      
      





将来的には、自由な時間があるので、引き続きiOSの開発経験について説明しますが、今のところ、コメントで私の作業の結果について議論することを勧めます。



All Articles