Pictograph Contestで私が使用したIOS開発テクニック

最近、iOSプラットフォーム向けのVkontakteフォトコンテストが3回開催されました。 コンテストへのリンク: http : //vk.com/photo_contest 。 最初のアプリケーションを開発する過程で、いくつかの問題に対する興味深い解決策を見つけました。 これらの決定を一般の人々と共有したかった。 iOS開発者にとって新しいものを発見することはまずないでしょう。初心者にも適しているとは思いません。 この記事は、2〜5個のアプリケーションの経験があるiOS開発者にとって興味深いものになると思います。











スクロール可能な水平テープ

デバイスメモリからの最新の写真



まず、テープの最初から4枚または5枚ではなく、4および1/3の写真を見ることができるのは非常に有能です。 これにより、ユーザーは写真のリストが水平方向にスクロールしていることをすぐに理解できます。



いくつかの質問があります:



最初に、ダウンロード速度に問題がある場合にデバイスのメモリからすべての写真をテープに表示することに決めました。この問題に戻ることを約束しました。

テープの先頭に最新の写真を表示する必要があるため、デバイスのメモリからすべての写真を正しい順序で取得するのに問題がすぐにありました。 最新の写真は保存された写真のあるアルバムではなく、兄弟がその日に私と共有したフォトストリームにあることがすぐにわかりました。

テープの冒頭で、アルバムの最新の写真と保存済みの写真を表示することが決定され、すでにこのアルバムの背後にあるすべての写真が表示されています。 写真の各アルバムの中では、最後からアレンジし始めました。 以下は、記述された順序でALAsset



配列を受け取るソースコードです。



 @implementation ALAssetsLibrary (Extension) - (void)latestAssetsAndCall:(void (^)(NSMutableArray *))callback { __block NSMutableArray * assets = [NSMutableArray arrayWithCapacity:5000]; [self enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) { if (group == nil) { callback(assets); return; } ALAssetsGroupType groupType = [[group valueForProperty:ALAssetsGroupPropertyType] intValue]; int insertIndex = (groupType == ALAssetsGroupSavedPhotos) ? 0 : assets.count; [group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { if (result != nil) [assets insertObject:result atIndex:insertIndex]; }]; } failureBlock:^(NSError *error) { if (error) NSLog(@"%@", error); }]; } @end
      
      





2番目の質問に関しては、長いリストをスクロールし、コンテンツを動的にロードし、同様のテーブルセルを再利用するために作成されたため、 UITableView



を使用するよりも良いものは見つかりませんでした。 唯一のことは、テーブルを反時計回りに90度回転する必要があることです。 変換がオブジェクトの中心に対して実行されることをUITableView



に、テープの中心の提案された位置にUITableView



の中心を配置し、以下を実行します。



 self.tableView.transform = CGAffineTransformMakeRotation(-M_PI_2);
      
      





テーブルセルを作成する場合、逆変換を実行する必要があります。時計回りに90度回転します。



 - (UItableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BottomRollCell"]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"BottomRollCell"]; cell.contentView.transform = CGAffineTransformMakeRotation(M_PI_2); //     } //       }
      
      





最後に何がありますか? テーブルがスクロールすると、セルの内容を尋ねられます。 ALAsset



の配列をALAsset



サムネイル画像を取得し、テーブルセルで埋めます。 テーブルは非常にスムーズにスクロールし、写真のアップロードに遅れはありません。 すべての写真を撮る時間について-2500枚の写真を撮るのにかかる時間は1秒未満ですが、アプリケーションを起動することが重要です。 ALAsset



を受信すると、テーブルが右から左に落ちるアニメーションを作成します。 それは非常に良いことがわかり、半秒の遅延は実際には目立ちません。 さらに、クエリはすべての資産に関するものではありませんが、速度を上げる複数のインデックスを設定しても意味がありません。 したがって、最初の写真をすばやくプリロードして最適化することはできませんでした。



開閉写真のアニメーション



写真を開いたときにテープのサムネイルから直接展開し、編集がキャンセルされたときに元に戻すことにしました。 テーブル内の特定のセルの四角形の座標を取得するために、クラスメソッドUITableView



を使用しました。



 - (CGRect)rectForRowAtIndexPath:(NSIndexPath *)indexPath;
      
      





回転などを考慮して、写真の正確な座標を決定するために少しマナを費やす必要がありました。 メインビューでサムネイル画像の座標を受け取った後、サムネイルの上に縮小されたフルサイズの画像を配置し、画像のサイズと位置をアニメーションで変更しました。 理想的には、画像が非線形のパスでテープから飛び出したいのですが、残り時間がありませんでした...



デバイスの写真の変更を追跡するには、 ALAssetsLibraryChangedNotificationイベントにサブスクライブする必要があります。これにより、 ALAsset



の配列をリロードする必要があります。 アプリケーション自体で写真を保存するときにテープが更新されないようにするには、内部フラグを使用して、次の更新時にテープの再描画をキャンセルし、新しいALAsset



ALAsset



配列の先頭に手動で追加する必要があります。

私での保存は左端の位置で行われ、テーブルを1つの画像の幅だけ右に移動し、画像をテープにアニメートし、アニメーションなしでテーブルを戻し、手動でreloadData



を呼び出します。



画像を開閉するアニメーションをできるだけスムーズかつ迅速に実行するために、私は1つの興味深いことをしなければなりませんでした。 写真を開いて、縮尺を変更し、キャンセルボタンを押すと、写真はスケーリング後にそのまま残した形でリボンに飛びます。 写真は、画面の境界線の後ろに隠れて再度読み込まれないまで、このフォームに残ります。 この効果を達成するために、 NSValue



を使用し、キーとしてNSMutableDictionary



URLを、値としてNSValue



を含むCGRect



CGRect



ました。 残念ながら、ビデオレビューでこのプロパティを削除するのを忘れましたが、これは私にとって最も興味深い問題の1つでした。



エフェクトを使用して写真をスムーズに拡大縮小および配置



選択したエフェクトとプレビューの同時適用で写真のスケーリングと配置を行い、すべてを同期的に移動してエフェクトを適用したかったのです。 一般に、あなたがそうしようとすると、この喜びを遅くすることは不敬になります。 興味深い解決策が見つかりました。選択したエフェクトをメイン写真に適用し、5倍(正確には320.0 / 56.0)に縮小した写真を撮り、残りのエフェクトをそれに適用し、スケーリングと位置決め中にサムネイルをメインUIScrollView



と同期します。 この方法は、ジャムなしで迅速かつスムーズに機能します。











サムネイルスクロールをメインスクロールと同期させるコード(これらはUIScrollViewDelegate



デリゲートメソッドです):



 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { for (UITableViewCell * cell in [self.filtersTable visibleCells]) { UIScrollView * filterScrollView = (UIScrollView *)[cell.contentView viewWithTag:125]; filterScrollView.contentOffset = CGPointMake(scrollView.contentOffset.x*56/320, scrollView.contentOffset.y*56/320); } } - (void)scrollViewDidZoom:(UIScrollView *)scrollView { for (UITableViewCell * cell in [self.filtersTable visibleCells]) { UIScrollView * filterScrollView = (UIScrollView *)[cell.contentView viewWithTag:125]; filterScrollView.zoomScale = self.scrollView.zoomScale; filterScrollView.contentOffset = CGPointMake(scrollView.contentOffset.x*56/320, scrollView.contentOffset.y*56/320); } }
      
      







結果を保存する



私たちにとって最も重要なことは、アプリケーションの速度と画像全体の品質であるため、競争条件は規制されていないため、640x640(網膜上)のサイズで写真を保存することにしました。 そして、これを行う最も簡単な方法は、イメージのコンテキストでメインビューを上方向にシフトしてレンダリングすることです。



 - (UIImage *)renderImageForSaving { UIGraphicsBeginImageContextWithOptions(self.scrollView.bounds.size, YES, 0.0); CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0, -self.scrollView.frame.origin.y); [self.view.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage * image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; }
      
      





それは高速で、碑文などに追加の問題がありません。 はい、解像度をさらに節約できたかもしれませんが、これは完全に異なる問題であり、時間と忍耐が必要です)



私のいたずらな犬がアプリケーションを実演するビデオ:







リンクを提供できると思います(OK?)。 アプリケーションはそれぞれ無料で広告なしです。

アプリリンク: https : //itunes.apple.com/app/pictography/id570470169

iOS5では、いくつかの異なるグリッチが現在確認されており、修正のための更新が検証のために送信されており、今週の終わりまでに予想されています...



PSそして最後に、Vkontakteがこのようなコンテストを開催してくれたことに感謝します。 結局のところ、彼らはプログラマーに新しい有望なプラットフォームの開発を開始するようにプログラマーを動機付け/奨励しています(何らかの理由で、参加者の中にはプラットフォームに多くの新人がいるようです)。 コンテストの入力データは非常に満足しています-すべての画像は選択のようなものでした。 余分なピクセルがどこにも貼り付いていない...



All Articles