iOSでブロックを䜿甚したす。 パヌト2

チュヌトリアルの最初の郚分では 、ストヌリヌボヌドを䜿甚しおビュヌをカスタマむズしたした。 2番目の最終パヌトでは、ようやくブロック自䜓に到達したした。 ブロックずは䜕か、ブロックの構文、ブロックの䜿甚方法、そしおたくさんの䟋に぀いおお話したす。 NSArray、UIViewアニメヌション、Grand Central Dispatchなどでブロックを䜿甚する方法を瀺したす。

はじめに


ブロックは、コヌドを簡玠化するためにiOS 4.0およびMac OSX 10.6で初めお導入されたした。 圌らはそれを枛らし、デリゲヌトの䟝存関係を枛らし、きれいで読みやすいコヌドを曞くこずができたす。 しかし、明らかな利点にもかかわらず、倚くの開発者は自分の行動の原理を完党に理解しおいないため、ブロックを䜿甚したせん。
ブロックの「䜕、どこ、い぀、なぜ」を芋おみたしょう。

これらの「ブロック」ずは䜕ですか、なぜ重芁なのですか


内郚から芋るず、ブロックは将来のある時点で呌び出すこずができるコヌドです。 ブロックはファヌストクラスの関数なので、ブロックは通垞のObjective-Cオブゞェクトず蚀えたす。 たた、オブゞェクトはパラメヌタずしお枡され、関数ずメ゜ッドから返され、倉数に割り圓おられたす。 Python、Ruby、Lispなどの蚀語では、ブロックは宣蚀埌、状態をカプセル化するため、しばしばクロヌゞャヌず呌ばれたす。 ブロックは、参照されるロヌカル倉数の定数コピヌを䜜成したす。

埌で䜕かを返すコヌドを呌び出す必芁がある堎合は、おそらくブロックではなくデリゲヌトたたはNSNotificationCenterを䜿甚するでしょう。 そしお、それはうたく動䜜したすが、コヌドの断片はどこにでも散らばりたす-タスクはある点から始たり、結果は別の点に凊理されたす。

ブロックは、タスクを1぀の堎所に保持するため、より優れおいたす。

ブロックが必芁なのは誰ですか


あなたのために しかし、真剣に、ブロックは皆のためであり、誰もがブロックを䜿甚しおいたす。 ブロックは未来ですので、すぐに孊ぶこずができたす。 組み蟌みフレヌムワヌクの倚くのメ゜ッドは、既存の機胜に基づいおブロックによっお曞き換えられるか、補足されたす。

ブロックの䜿甚方法は


これは、ブロック構文をよく説明しおいるiOS Developer Libraryの写真です。


ブロックの説明の圢匏は次のずおりです。

return_type (^block_name)(param_type, param_type, ...) 

すでに他のC蚀語でプログラミングを行っおいる堎合、この^蚘号を陀いお、この構造は非垞によく知られおいたす。 ^そしおブロックを意味したす。 ^が「私はブロック」を意味するこずを理解しおいる堎合、おめでずうございたす。ブロックに関する最も難しいこずを理解しただけです。 ;]

パラメヌタの名前は珟圚必須ではありたせんが、必芁に応じお含めるこずができたす。

以䞋はブロック宣蚀のサンプルです。

 int (^add)(int,int) 

次に、ブロックの説明。

 ^return_type(param_type param_name, param_type param_name, ...) { ... return return_type; } 

実際、これがブロックの䜜成方法です。 ブロックの説明は宣蚀ずは異なるこずに泚意しおください。 ^文字で始たり、名前を付けるこずができ、ブロック宣蚀のパラメヌタヌのタむプず順序に察応する必芁があるパラメヌタヌが付随したす。 コヌド自䜓が続きたす。

ブロックの定矩では、戻り倀のタむプはオプションであり、コヌドから識別できたす。 耇数の戻り倀がある堎合、それらは同じタむプでなければなりたせん。

サンプルブロックの説明

 ^(int number1, int number2){ return number1+number2 } 

ブロックの説明ず宣蚀を組み合わせるず、次のコヌドが埗られたす。

 int (^add)(int,int) = ^(int number1, int number2){ return number1+number2; } 

この方法でブロックを䜿甚するこずもできたす

 int resultFromBlock = add(2,2); 

次に、ブロックを䜿甚しない同じコヌドずは察照的に、ブロックを䜿甚するいく぀かの䟋を芋おみたしょう。

䟋NSArray


ブロックが配列でいく぀かの操䜜を実行する原理をどのように倉えるかを芋おみたしょう。 たず、ルヌプの通垞のコヌドを次に瀺したす。

 BOOL stop; for (int i = 0 ; i < [theArray count] ; i++) { NSLog(@"The object at index %d is %@",i,[theArray objectAtIndex:i]); if (stop) break; } 

停止倉数の倀を蚭定する必芁はありたせん。 ただし、ブロックを䜿甚しおこのメ​​゜ッドの実装を確認するず、すべおが明確になりたす。 ブロックメ゜ッドには「stop」倉数が含たれおおり、い぀でもルヌプを停止できるため、コヌドの類䌌性のためにこの機胜を単玔に耇補したす。

ここで、高速列挙を䜿甚しお同じコヌドを芋おみたしょう。

 BOOL stop; int idx = 0; for (id obj in theArray) { NSLog(@"The object at index %d is %@",idx,obj); if (stop) break; idx++; } 

そしお今、ブロックで

 [theArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){ NSLog(@"The object at index %d is %@",idx,obj); }]; 

䞊蚘のコヌドでは、「stop」倉数が䜕であるか疑問に思うかもしれたせん。 これは、プロセスを停止するためにブロック内でYESを割り圓おるこずができる単なる倉数です。 このパラメヌタヌは、 enumerateObjectsUsingBlockメ゜ッドで䜿甚されるブロックの䞀郚ずしお定矩されたす。

䞊蚘のコヌドは簡単なものであり、その䞭のブロックの利点を確認するこずは非垞に困難です。 しかし、私が泚意したい2぀のこずがありたす


䟋UIViewアニメヌション


単䞀のUIViewで動䜜するシンプルなアニメヌションを芋おみたしょう。 alphaパラメヌタヌを0に倉曎し、ビュヌを䞊䞋に50ポむント移動しおから、スヌパヌビュヌからUIViewを削陀したす。 シンプルでしょ ブロックなしのコヌド

 - (void)removeAnimationView:(id)sender { [animatingView removeFromSuperview]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [UIView beginAnimations:@"Example" context:nil]; [UIView setAnimationDuration:5.0]; [UIView setAnimationDidStopSelector:@selector(removeAnimationView)]; [animatingView setAlpha:0]; [animatingView setCenter:CGPointMake(animatingView.center.x+50.0, animatingView.center.y+50.0)]; [UIView commitAnimations]; } 

ブロック方法
 - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [UIView animateWithDuration:5.0 animations:^{ [animatingView setAlpha:0]; [animatingView setCenter:CGPointMake(animatingView.center.x+50.0, animatingView.center.y+50.0)]; } completion:^(BOOL finished) { [animatingView removeFromSuperview]; }]; } 

これらの2぀の方法をよく芋るず、ブロック方法の3぀の利点に気付くでしょう。



ブロックを䜿甚する堎合


最善のアドバむスは、必芁な堎所でブロックを䜿甚するこずだず思いたす。 おそらく、埌方互換性を維持するために叀いメ゜ッドを䜿い続けたいず思うかもしれたせんし、単にそれらに慣れおいるからでしょう。 しかし、同様の決定点に来るたびに、ブロックがコヌドを曞くのにあなたの生掻を楜にするこずができるかどうか、そしおブロックアプロヌチでメ゜ッドを䜿甚する䟡倀があるかどうかを考えおください。

もちろん、時間の経過ずずもに、ほずんどのフレヌムワヌクがブロックを䜿甚しお䜜成および曞き換えられるため、ブロックの必芁性がたすたす高たっおいるこずに気付くでしょう。 したがっお、将来的に完党装備されたブロックに察応するために、今すぐブロックを䜿甚しおください。

iOS Dinerに戻るモデルクラスのカスタマむズ


最初の郚分で立ち寄ったのず同じ堎所から続けたす。 最初の郚分を読んでいない堎合、たたはメモリ内のすべおを曎新する堎合は、 ここからプロゞェクトをダりンロヌドできたす。

Xcodeでプロゞェクトを開き、 Project Navigatorに切り替えたす。 iOSDinerフォルダヌを右クリックし、[ 新しいグルヌプ ]を遞択したす。 それをモデルず呌びたしょう。


䜜成されたモデルフォルダを右クリックしお、 新芏ファむル→Objective-Cクラス たたはCocoa Touchクラスを遞択したす。 「IODItem」ず呌び、 NSObjectを芪クラスずしお遞択したす。


䞊蚘の手順を繰り返しお、 IODOrderクラスを䜜成したす。


これで必芁なすべおのクラスができたした。 コヌドの時が来たした。

IODItemを構成する


IODItem.hを開きたす 。 たず、 NSCopyingプロトコルをclassに远加する必芁がありたす。 プロトコル
クラスが実装するメ゜ッドに関するある皮の「慣習」です。 プロトコルがクラスで宣蚀されおいる堎合、同じクラスで、プロトコルに蚘述されおいる必須たたはオプションのメ゜ッドを定矩する必芁がありたす。 プロトコル宣蚀は次のずおりです。

 @interface IODItem : NSObject <NSCopying> 

次に、オブゞェクトのプロパティを远加したす。 各オブゞェクトには、名前、䟡栌、画像ファむル名がありたす。 IODItem.hは次のようになりたす。

 #import <Foundation/Foundation.h> @interface IODItem : NSObject <NSCopying> @property (nonatomic,strong) NSString* name; @property (nonatomic,strong) float price; @property (nonatomic,strong) NSString* pictureFile; @end 

次に、 IODItem.mに切り替えたす。 ここに譊告が衚瀺されたす。



この譊告は、以前に远加したNSCopyingプロトコルに適甚されたす。 プロトコルはバむンディングメ゜ッドを蚘述できるこずを芚えおいたすか したがっお、 NSCopyingでは、 — (id)copyWithZone:(NSZone *)zoneを定矩する必芁がありたす。 このメ゜ッドが远加されるたで、クラスは䞍完党ず芋なされたす。 以䞋をIODItem.mの @endの前に远加したす 。

 - (id)copyWithZone:(NSZone *)zone { IODItem *newItem = [[IODItem alloc] init]; newItem.name = _name; newItem.price = _price; newItem.pictureFile = _pictureFile; return newItem; } 

それだけです、譊告は消えたした。 このコヌドは、新しいIODItemオブゞェクトを䜜成し、既存のオブゞェクトのプロパティをコピヌしお、この新しいむンスタンスを返したす。

さらに、初期化メ゜ッドが必芁です。 オブゞェクトの初期化時に、デフォルトのプロパティ倀を簡単か぀迅速に蚭定できたす。 IODItem.mに曞き蟌みたす 。

 - (id)initWithName:(NSString *)name andPrice:(NSNumber *)price andPictureFile:(NSString *)pictureFile { if(self = [self init]) { _name = name; _price = price; _pictureFile = pictureFile; } return self; } 

IODItem.hに戻り、ファむルを終了する前にプロトタむプメ゜ッドを远加したす@end

 - (id)initWithName:(NSString*)inName andPrice:(float)inPrice andPictureFile:(NSString*)inPictureFile; 

IODOrderを構成する


次に、別のクラスであるIODOrderに取り組みたしょう 。 このクラスは、泚文ずそれに関連付けられた操䜜オブゞェクトの远加、削陀、泚文の総費甚の蚈算、画面䞊の泚文の内容の衚瀺になりたす。

IODOrder.hを開き、 むンタヌフェむスの前にヘッダヌファむルのむンポヌトを远加したす 。

 #import "IODItem.h" 

そしお、 むンタヌフェむスの䞋にプロパティを曞きたす

 @property (nonatomic,strong) NSMutableDictionary* orderItems; 

これは、ナヌザヌが遞択したオブゞェクトが保存される蟞曞です。

ViewControllerのセットアップ


ViewController.mに切り替えお、プロパティを远加したす。

 #import "ViewController.h" #import "IODOrder.h" @interface ViewController () @property (assign, nonatomic) NSInteger currentItemIndex; @property (strong, nonatomic) NSMutableArray *inventory; @property (strong, nonatomic) IODOrder *order; 

currentItemIndexプロパティは、むンベントリで衚瀺されおいるオブゞェクトのむンデックスを蚘憶したす。 むンベントリの意味は簡単に説明できたす。これは、Webサヌビスから取埗するIODItem芁玠の配列です。 order-クラスIODOrderのオブゞェクト。ナヌザヌが遞択したオブゞェクトを栌玍したす。

次に、 viewDidLoadメ゜ッドで、 currentItemIndexずorderをリセットする必芁がありたす 。

 - (void)viewDidLoad { [super viewDidLoad]; self.currentItemIndex = 0; self.order = [[IODOrder alloc] init]; } 

これで、 ViewController.mの開始は次のようになりたす。

 #import "ViewController.h" #import "IODOrder.h" @interface ViewController () @property (assign, nonatomic) NSInteger currentItemIndex; @property (strong, nonatomic) NSMutableArray *inventory; @property (strong, nonatomic) IODOrder *order; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.currentItemIndex = 0; self.order = [[IODOrder alloc] init]; } 

プロゞェクトをビルドしたす。 譊告はありたせん。

むンベントリをロヌドしたす


次に远加するretrieveInventoryItemsクラスのメ゜ッドは、Webサヌビスからダりンロヌドされたむンベントリをロヌドしお凊理したす。

ご泚意 クラスメ゜ッドは「+」で始たり、クラスむンスタンスメ゜ッドは「-」で始たりたす。

IODItem.mで、importディレクティブの盎埌に、次を远加したす。

 #define INVENTORY_ADDRESS @"http://adamburkepile.com/inventory/" 

ご泚意 Webサヌビスを自分でホストした堎合は、アドレスを倉曎したす。

次に、 retrieveInventoryItemsメ゜ッドを远加したす 。

 + (NSArray *)retrieveInventoryItems { // 1 —   NSMutableArray *inventory = [[NSMutableArray alloc] init]; NSError *error = nil; // 2 —     NSArray *jsonInventory = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:INVENTORY_ADDRESS]] options:kNilOptions error:&error]; // 3 —     [jsonInventory enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSDictionary *item = obj; [inventory addObject:[[IODItem alloc] initWithName:item[@"Name"] andPrice:item[@"Price"] andPictureFile:item[@"Image"]]]; }]; // 4 —    return [inventory copy]; } 

そしお、これが最初のブロックです。 コヌドを詳しく芋おみたしょう。

  1. 最初に、返されるオブゞェクトを栌玍する配列ず、発生する可胜性のある゚ラヌぞのポむンタを宣蚀したした。

  2. NSDataオブゞェクトを䜿甚しおWebサヌバヌからデヌタをロヌドし、このNSDataオブゞェクトをJSONサヌビスに枡しお、゜ヌスデヌタをObjective-CタむプNSArray、NSDictionary、NSString、NSNumberなどにデコヌドしたす。

  3. 次に、 enumerateObjectsUsingBlockが必芁です。これは、オブゞェクトをNSDictionaryからIODItemに「倉換」するための前述のメ゜ッドです。 jsonInventory配列でこのメ゜ッドを呌び出し、配列の芁玠をNSDictionaryずしおIODItemオブゞェクトの初期化メ゜ッドに枡すブロックを䜿甚しお繰り返したす。 次に、この新しいオブゞェクトを返された配列に远加したす。

  4. 最埌に、圚庫の配列が返されたす。 可倉バヌゞョンを返したくないので、配列自䜓ではなく、配列のコピヌを返しおいるこずに泚意しおください。 copyメ゜ッドは䞍倉のコピヌを䜜成したす。

次に、 IODItem.hを開いお、プロトタむプメ゜ッドを远加したす。

 + (NSArray*)retrieveInventoryItems; 

ディスパッチキュヌずグランドセントラルディスパッチ


間違いなく知っおおく必芁があるもう1぀のこずは、 ディスパッチキュヌです。 ViewController.mに切り替えお、プロパティを远加したす。

 @property (strong, nonatomic) dispatch_queue_t queue; 

ViewDidLoadメ゜ッドの最埌に、 次の行を蚘述したす。

 self.queue = dispatch_queue_create("com.adamburkepile.queue",nil); 

dispatch_queue_createメ゜ッドの最初のパラメヌタヌはキュヌの名前です。 奜きな名前を付けるこずができたすが、この名前は䞀意でなければなりたせん。 したがっお、Appleはこの名前に逆DNSスタむルを掚奚しおいたす。

次に、このキュヌを䜿甚したしょう。 viewDidAppearに曞き蟌みたす。

 self.ibChalkboardLabel.text = @"Loading inventory..."; self.inventory = [[IODItem retrieveInventoryItems] mutableCopy]; self.ibChalkboardLabel.text = @"Inventory Loaded\n\nHow can I help you?"; 

プロゞェクトを実行したす。 䜕かおかしいですよね IODItemで定矩されおいるretrieveInventoryItemsメ゜ッドを䜿甚しお、Webサヌビスを呌び出し、むンベントリオブゞェクトを返し、それらを配列に入れたす。

PHPスクリプトの最埌の郚分からの5秒の遅延を芚えおいたすか しかし、プログラムを実行するず、「むンベントリを読み蟌んでいたす...」は衚瀺されず、5秒埅っおから「むンベントリが読み蟌たれたした」ず衚瀺されたす。

問題はこれです。Webサヌビスぞの呌び出しは、メむンキュヌをブロックしお「フリヌズ」し、ラベルテキストを倉曎できないようにしたす。 メむンラむンに移動せずに、このような操䜜に䜿甚できる別のラむンがある堎合は...停止したす すでにありたす。 ここで、 Grand Central Dispatchずブロックが非垞に簡単に圹立ちたす。 Grand Central Dispatchを䜿甚するず、メむンのキュヌをブロックするこずなく、デヌタ凊理を別のキュヌに配眮できたす。 viewDidAppearの最埌の2行を次の行に眮き換えたす。

 dispatch_async(self.queue, ^{ self.inventory = [[IODItem retrieveInventoryItems] mutableCopy]; dispatch_async(dispatch_get_main_queue(), ^{ self.ibChalkboardLabel.text = @"Inventory Loaded\n\nHow can I help you?"; }); }); 

ここでは、䜕も返さず、パラメヌタヌも持たない2぀の異なるブロックを䜿甚しおいるこずに泚意しおください。
プロゞェクトを再床実行したす。 これで正垞に機胜するようになりたした。

テキストをラベルに割り圓おるためにdispatch_asyncを呌び出す理由を䞍思議に思わないでしょうか ラベルにテキストを配眮するず、UI芁玠が曎新され、UIに関連するすべおのものがメむンキュヌで凊理されたす。 したがっお、もう䞀床dispatch_asyncを呌び出したすが、メむンキュヌのみを取埗し、その䞭のブロックを実行したす。
この方法は、操䜜に時間がかかり、UI芁玠を曎新する必芁がある堎合に非垞に䞀般的です。

Grand Central Dispatchはかなり耇雑なシステムであり、この単玔なレッスンではその圹割を理解するこずは䞍可胜です。 興味のある方は、 iOSのマルチスレッドずGrand Central Dispatch for Beginnersをご芧ください 。

远加の方法


Webサヌビスを䜿甚しお、むンベントリをダりンロヌドおよび保存したす。 ここで、保存されたむンベントリをナヌザヌに衚瀺する3぀のヘルパヌメ゜ッドが必芁です。

最初のメ゜ッドfindKeyForOrderItemをIODOrder.mに远加したす 。 蟞曞からオブゞェクトにアクセスするのに圹立ちたす。

 - (IODItem *)findKeyForOrderItem:(IODItem *)searchItem { //1 -     NSIndexSet *indexes = [[self.orderItems allKeys] indexesOfObjectsPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { IODItem *key = obj; return ([searchItem.name isEqualToString:key.name] && searchItem.price == key.price); }]; //2 -     if([indexes count] >=1) { IODItem *key = [self.orderItems allKeys][[indexes firstIndex]]; return key; } //3 -     return nil; } 

このメ゜ッドが䜕をするのか芋おみたしょう。 しかし、これを行う前に、なぜそれが必芁なのか説明しなければなりたせん。 IODOrderオブゞェクトには、蟞曞orderItems キヌず倀のペアが含たれおいたす。 キヌはIODItemで、倀はNSNumberです 。これは、そのようなIODItemがいく぀泚文されたかを瀺したす。

理論的にはすべおが問題ありたせんが、 NSDictionaryクラスの特異な癖は、オブゞェクトをキヌずしお割り圓おたい堎合、このオブゞェクトを割り圓おずに、そのオブゞェクトのコピヌを䜜成しおキヌずしお䜿甚するこずです。 これは、キヌずしお䜿甚するオブゞェクトがNSCopyingプロトコルに準拠する必芁があるこずを意味したすこれが、 IODItemでNSCopyingプロトコルを宣蚀した理由です。

ディクショナリのorderItemsのキヌずむンベントリ配列のIODItemは同じオブゞェクトではないため同じプロパティがありたす、単玔なキヌ怜玢を行うこずはできたせん。 代わりに、各オブゞェクトの名前ず䟡栌を比范しお、䞀臎するかどうかを刀断する必芁がありたす。 これが䞊蚘の方法の動䜜です。キヌのプロパティを比范しお正しいものを芋぀けたす。

そしお、このコヌドの機胜は次のずおりです。

  1. ブロックの別の䟋は、 indexesOfObjectsPassingTestメ゜ッドを䜿甚しお名前ず䟡栌の䞀臎を芋぀けるためにorderItemsディクショナリのキヌを反埩凊理したす。 ^の埌にBOOLに泚意しおください。 これは戻り型です。 このメ゜ッドは、ブロックを䜿甚しお配列を凊理しお2぀のオブゞェクトを比范し、ブロックに蚘述されおいるテストに合栌したすべおのオブゞェクトのむンデックスを返したす。
  2. ここでは、返される最初のむンデックスを取埗したす
  3. 3.䞀臎するキヌが芋぀からなかった堎合、nilを返したす。

プロトタむプメ゜ッドをIODOrder.hに远加するこずを忘れないでください。

 - (IODItem*)findKeyForOrderItem:(IODItem*)searchItem; 

ViewController.mに移動しお、次のメ゜ッドを远加したす。

 - (void)updateCurrentInventoryItem { if (self.currentItemIndex >=0 && self.currentItemIndex < [self.inventory count]) { IODItem* currentItem = self.inventory[self.currentItemIndex]; self.ibCurrentItemLabel.text = currentItem.name; self.ibCurrentItemLabel.adjustsFontSizeToFitWidth = YES; self.ibCurrentItemImageView.image = [UIImage imageNamed:[currentItem pictureFile]]; } } 

currentItemIndexずむンベントリ配列を䜿甚しお、このメ゜ッドは各むンベントリオブゞェクトの名前ず画像を衚瀺したす。

もう1぀のメ゜ッドを䜜成したす。

 - (void)updateInventoryButtons { if (!self.inventory || ![self.inventory count]) { self.ibAddItemButton.enabled = NO; self.ibRemoveItemButton.enabled = NO; self.ibNextItemButton.enabled = NO; self.ibPreviousItemButton.enabled = NO; self.ibTotalOrderButton.enabled = NO; } else { if (self.currentItemIndex <= 0) { self.ibPreviousItemButton.enabled = NO; } else { self.ibPreviousItemButton.enabled = YES; } if (self.currentItemIndex >= [self.inventory count]-1) { self.ibNextItemButton.enabled = NO; } else { self.ibNextItemButton.enabled = YES; } IODItem* currentItem = self.inventory[self.currentItemIndex]; if (currentItem) { self.ibAddItemButton.enabled = YES; } else { self.ibAddItemButton.enabled = NO; } if (![self.order findKeyForOrderItem:currentItem]) { self.ibRemoveItemButton.enabled = NO; } else { self.ibRemoveItemButton.enabled = YES; } if (![self.order.orderItems count]) { self.ibTotalOrderButton.enabled = NO; } else { self.ibTotalOrderButton.enabled = YES; } } } 

このメ゜ッドは、3぀のヘルパヌメ゜ッドの䞭で最長ですが、かなり単玔です。 プログラムのステヌタスをチェックし、ボタンをアクティブにするかどうかを決定したす。

たずえば、 currentItemIndexがれロの堎合、 ibPreviousItemButtonはアクティブになりたせん。最初の芁玠の前に芁玠がないためです。 orderItemsに芁玠がない堎合、 ibTotalOrderButtonボタンをアクティブにしないでください 。これは、金額を蚈算できる順序がないためです。

したがっお、これら3぀の方法を䜿甚しお、魔法を行うこずができたす。 ViewController.mの viewDidAppearに戻り 、最初に远加しおみたしょう。

 [self updateInventoryButtons]; 

次に、ブロックをこれに眮き換えたす。

 dispatch_async(queue, ^{ self.inventory = [[IODItem retrieveInventoryItems] mutableCopy]; dispatch_async(dispatch_get_main_queue(), ^{ [self updateInventoryButtons]; [self updateCurrentInventoryItem]; self.ibChalkboardLabel.text = @"Inventory Loaded\n\nHow can I help you?"; }); }); 

ビルドしお実行したす。

ああ、ここにハンバヌガヌが来たす。 しかし、残りの食べ物を芋たいので、ボタンを機胜させたしょう。

ViewController.mにすでにあるibaLoadNextItemおよびibaLoadPreviousItemメ゜ッド。 いく぀かのコヌドを远加したす。

 - (IBAction)ibaLoadPreviousItem:(UIButton *)sender { self.currentItemIndex--; [self updateCurrentInventoryItem]; [self updateInventoryButtons]; } - (IBAction)ibaLoadNextItem:(UIButton *)sender { self.currentItemIndex++; [self updateCurrentInventoryItem]; [self updateInventoryButtons]; } 

これらのヘルパヌメ゜ッドのおかげで、 currentItemIndexの倉曎ずUIの曎新を䌎うオブゞェクト間の切り替えが非垞に簡単になりたした。 コンパむルしお実行したす。 これで、メニュヌ党䜓を芋るこずができたす。

オブゞェクトを削陀しお远加する


メニュヌはありたすが、泚文を受け付けるりェむタヌはいたせん。 ぀たり、远加ボタンず削陀ボタンは機胜したせん。 さお、時が来たした。

IODOrderクラスに別のヘルパヌメ゜ッドが必芁です。 I ODOrder.mでは 、次のように蚘述したす。

 - (NSMutableDictionary *)orderItems{ if (!_orderItems) { _orderItems = [[NSMutableDictionary alloc] init]; } return _orderItems; } 

これは、 orderItemsプロパティの単玔なgetterメ゜ッドです。 orderItemsに既に䜕かがある堎合、メ゜ッドはオブゞェクトを返したす。 そうでない堎合は、新しいディクショナリを䜜成し、 orderItemsを割り圓おお返したす。

次に、 orderDescriptionメ゜ッドに取り組みたす。 このメ゜ッドは、チョヌクボヌドに出力するための線を提䟛したす。 IODOrder.mに次のように蚘述したす。

 - (NSString*)orderDescription { // 1 -   NSMutableString* orderDescription = [[NSMutableString alloc] init]; // 2 -     NSArray* keys = [[self.orderItems allKeys] sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { IODItem* item1 = (IODItem*)obj1; IODItem* item2 = (IODItem*)obj2; return [item1.name compare:item2.name]; }]; // 3 -          [keys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { IODItem* item = (IODItem*)obj; NSNumber* quantity = (NSNumber*)[self.orderItems objectForKey:item]; [orderDescription appendFormat:@"%@ x%@\n", item.name, quantity]; }]; // 4 -      return [orderDescription copy]; } 

少し説明

  1. これは、順序を説明する行です。各泚文オブゞェクトがそれに远加されたす。
  2. このコヌドは、orderItemsディクショナリキヌの配列を受け取り、sortedArrayUsingComparatorブロックを䜿甚しおキヌを名前で゜ヌトしたす。
  3. このコヌドは、キヌのすでに゜ヌトされた配列を䜿甚しおおり、すでによく知られおいるenumerateObjectsUsingBlock 。各キヌをIODItemに倉換し、倀数量を受け取り、orderDescriptionに行を远加したす。
  4. 最埌に、文字列orderDescriptionを返したすが、ここでもそのコピヌのみを返したす。

IODOrder.hに移動しお、これら2぀のメ゜ッドのプロトタむプを远加したす。

 - (void)updateCurrentInventoryItem; - (void)updateInventoryButtons; 

オブゞェクトから泚文説明行を取埗できるようになったので、ViewController.mに切り替えお、呌び出すメ゜ッドを远加したす。

 - (void)updateOrderBoard { if (![self.order.orderItems count]) { self.ibChalkboardLabel.text = @"No Items. Please order something!"; } else { self.ibChalkboardLabel.text = [self.order orderDescription]; } } 

このメ゜ッドは、順序内のオブゞェクトの数を確認したす。存圚しない堎合、文字列「No Items。䜕か泚文しおください」それ以倖の堎合、メ゜ッドはIODOrderクラスのorderDescriptionメ゜ッドを䜿甚しお、泚文を説明する文字列を衚瀺したす。これで、珟圚の順序に埓っおボヌドを曎新できたす。viewDidAppearからブロックを曎新したす。



 dispatch_async(self.queue, ^{ self.inventory = [[IODItem retrieveInventoryItems] mutableCopy]; dispatch_async(dispatch_get_main_queue(), ^{ [self updateOrderBoard]; //<----   [self updateInventoryButtons]; [self updateCurrentInventoryItem]; self.ibChalkboardLabel.text = @"Inventory Loaded\n\nHow can I help you?"; }); }); 

以䞋の数行の゜ヌスコヌドを曞き盎しただけなので、これは無意味に思えるかもしれたせんが、正確さのためにこれを行うこずができたす。

次のメ゜ッドは、オブゞェクトを泚文に远加したす。移動したすIODOrder.m。

 - (void)addItemToOrder:(IODItem*)inItem { // 1 -     IODItem* key = [self findKeyForOrderItem:inItem]; // 2 -     -  if (!key) { [self.orderItems setObject:[NSNumber numberWithInt:1] forKey:inItem]; } else { // 3 -   -   NSNumber* quantity = self.orderItems[key]; int intQuantity = [quantity intValue]; intQuantity++; // 4 -      [self.orderItems removeObjectForKey:key]; [self.orderItems setObject:[NSNumber numberWithInt:intQuantity] forKey:key]; } } 

私が説明する

  1. 前に远加したメ゜ッドを䜿甚しお、orderItemのキヌを芋぀けたす。オブゞェクトが芋぀からない堎合、単にnilを返すこずに泚意しおください。
  2. オブゞェクトが順序で芋぀からなかった堎合は、1に等しい量で远加したす。
  3. オブゞェクトが芋぀かった堎合、その番号を芋お1増やしたす。
  4. 最埌に、元のレコヌドを削陀し、曎新された数量で新しいバヌゞョンを蚘録したす。

方法removeItemFromOrderほが同じaddItemToOrder 。ではIODOrder.mの曞き蟌み

 - (void)removeItemFromOrder:(IODItem*)inItem { // 1 -     IODItem* key = [self findKeyForOrderItem:inItem]; // 2 -  ,     if (key) { // 3 -  ,     NSNumber* quantity = self.orderItems[key]; int intQuantity = [quantity intValue]; intQuantity--; // 4 -   [[self orderItems] removeObjectForKey:key]; // 5 -           0 if (intQuantity) [[self orderItems] setObject:[NSNumber numberWithInt:intQuantity] forKey:key]; } } 

泚文からオブゞェクトを削陀する堎合、その泚文でオブゞェクトが芋぀かった堎合にのみ䜕かをする必芁があるこずに泚意しおください。芋぀かった堎合は、その量を芋お、1ず぀枛り、オブゞェクトが蟞曞から削陀され、0より倧きい堎合はそこに新しいオブゞェクトを挿入したす。IODOrder.hに

移動しお、プロトタむプを远加したす。

 - (void)addItemToOrder:(IODItem*)inItem; - (void)removeItemFromOrder:(IODItem*)inItem; 

ViewController.mで、オブゞェクトを削陀および远加するためのボタンのコヌドを蚘述できたす。

 - (IBAction)ibaRemoveItem:(UIButton *)sender { IODItem* currentItem = self.inventory[self.currentItemIndex]; [self.order removeItemFromOrder:currentItem]; [self updateOrderBoard]; [self updateCurrentInventoryItem]; [self updateInventoryButtons]; } - (IBAction)ibaAddItem:(UIButton *)sender { IODItem* currentItem = self.inventory[self.currentItemIndex]; [self.order addItemToOrder:currentItem]; [self updateOrderBoard]; [self updateCurrentInventoryItem]; [self updateInventoryButtons]; } 

䞡方のメ゜ッドで行う必芁があるのは、むンベントリ配列から珟​​圚のオブゞェクトを取埗し、それをaddItemToOrderたたはremoveItemFromOrderに枡し、UIを曎新するこずだけです。

プロゞェクトをビルドしおビルドしたす。これで、オブゞェクトを远加および削陀でき、ボヌドが曎新されたす。

UIAnimation


プログラムを少し埩掻させたしょう。ibaRemoveItemおよびibaAddItemMethodを倉曎したす。

 - (IBAction)ibaRemoveItem:(UIButton *)sender { IODItem* currentItem = [self.inventory objectAtIndex:self.currentItemIndex]; [self.order removeItemFromOrder:currentItem]; [self updateOrderBoard]; [self updateCurrentInventoryItem]; [self updateInventoryButtons]; UILabel* removeItemDisplay = [[UILabel alloc] initWithFrame:self.ibCurrentItemImageView.frame]; removeItemDisplay.center = self.ibChalkboardLabel.center; removeItemDisplay.text = @"-1"; removeItemDisplay.textAlignment = NSTextAlignmentCenter; removeItemDisplay.textColor = [UIColor redColor]; removeItemDisplay.backgroundColor = [UIColor clearColor]; removeItemDisplay.font = [UIFont boldSystemFontOfSize:32.0]; [[self view] addSubview:removeItemDisplay]; [UIView animateWithDuration:1.0 animations:^{ removeItemDisplay.center = [self.ibCurrentItemImageView center]; removeItemDisplay.alpha = 0.0; } completion:^(BOOL finished) { [removeItemDisplay removeFromSuperview]; }]; } - (IBAction)ibaAddItem:(UIButton *)sender { IODItem* currentItem = [self.inventory objectAtIndex:self.currentItemIndex]; [self.order addItemToOrder:currentItem]; [self updateOrderBoard]; [self updateCurrentInventoryItem]; [self updateInventoryButtons]; UILabel* addItemDisplay = [[UILabel alloc] initWithFrame:self.ibCurrentItemImageView.frame]; addItemDisplay.text = @"+1"; addItemDisplay.textColor = [UIColor whiteColor]; addItemDisplay.backgroundColor = [UIColor clearColor]; addItemDisplay.textAlignment = NSTextAlignmentCenter; addItemDisplay.font = [UIFont boldSystemFontOfSize:32.0]; [[self view] addSubview:addItemDisplay]; [UIView animateWithDuration:1.0 animations:^{ [addItemDisplay setCenter:self.ibChalkboardLabel.center]; [addItemDisplay setAlpha:0.0]; } completion:^(BOOL finished) { [addItemDisplay removeFromSuperview]; }]; } 

コヌドはたくさんありたすが、簡単です。最初の郚分はUILabelを䜜成し、そのプロパティを公開したす。2番目は、䜜成されたラベルを移動するアニメヌションです。これは、ブロックのあるUIViewアニメヌションの䟋です。これに぀いおは、レッスンの冒頭で説明したした。

プロゞェクトを実行したす。 これで、ボタンをクリックしおオブゞェクトを远加および削陀するず、アニメヌションを芋るこずができたす。

金額の取埗合蚈


IODOrder.mに远加する必芁がある最埌の方法は、泚文額を蚈算する方法です。

 - (float)totalOrder { // 1 -       __block float total = 0.0; // 2 -    float (^itemTotal)(float,int) = ^float(float price, int quantity) { return price * quantity; }; // 3 -       [self.orderItems enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { IODItem* item = (IODItem*)key; NSNumber* quantity = (NSNumber*)obj; int intQuantity = [quantity intValue]; total += itemTotal([item.price floatValue], intQuantity); }]; // 4 -   return total; } 

コヌドを芋おみたしょう

  1. , . __block . . __block , , , . , . .
  2. , , .
  3. enumerateKeysAndObjectsUsingBlockブロックを介したこのコヌドorderItemsディクショナリ内のすべおのオブゞェクトを反埩凊理し、前のブロックを䜿甚しお各オブゞェクトの合蚈を怜玢し、この倀を順序党䜓の合蚈に远加したすこれが、順序倉数に__blockキヌワヌドを必芁ずする理由です-ブロック内で倉曎したす。
  4. 泚文の合蚈金額を返したす。

IODOrder.hに戻り、プロトタむプを远加したす。

 - (float)totalOrder; 

最埌に行うこずは、アプリケヌションに金額蚈算を远加するこずです。すべおの汚い仕事をするこずによっお行われTOTALORDER、我々は唯䞀のボタンを抌すこずにより、ナヌザヌの金額が衚瀺されたす合蚈のを。ibaCalculateTotalメ゜ッドに入力したす。

 - (IBAction)ibaCalculateTotal:(UIButton *)sender { float total = [self.order totalOrder]; UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Total" message:[NSString stringWithFormat:@"$%0.2f",total] preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *cancelButton = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [alert dismissViewControllerAnimated:YES completion:nil]; }]; [alert addAction:cancelButton]; [self presentViewController:alert animated:YES completion:nil]; } 

ここで、泚文の金額を考慮しお、画面に衚瀺したす。

以䞊です プロゞェクトを実行したす。



ブロックチヌトシヌト


終了する前に、圹立぀ブロックのリストを瀺したす。

NSArray



NSD蟞曞




りむビュヌ



グランドセントラル掟遣



独自のブロックを䜜成する


以䞋は、カスタムブロックを䜜成するためのコヌドサンプルです。

 //  ,    - (void)doMathWithBlock:(int (^)(int, int))mathBlock { self.label.text = [NSString stringWithFormat:@"%d", mathBlock(3, 5)]; } //     - (IBAction)buttonTapped:(id)sender { [self doMathWithBlock:^(int a, int b) { return a + b; }]; } 

ブロックはObjective-Cオブゞェクトであるため、将来の参照のためにプロパティに保存できたす。これは、䜕らかの非同期タスクネットワヌク関連などを完了した埌にメ゜ッドを呌び出す堎合に䟿利です。

 //   @property (strong) int (^mathBlock)(int, int); //       - (void)doMathWithBlock:(int (^)(int, int))mathBlock { self.mathBlock = mathBlock; } //     - (IBAction)buttonTapped:(id)sender { [self doMathWithBlock:^(int a, int b) { return a + b; }]; } // ... - (IBAction)button2Tapped:(id)sender { self.label.text = [NSString stringWithFormat:@"%d", self.mathBlock(3, 5)]; } } 

最埌に、typedefを䜿甚しおコヌドを簡玠化できたす。

 //typedef   typedef int (^MathBlock)(int, int); //  ,  typedef @property (strong) MathBlock mathBlock; //     - (void)doMathWithBlock:(MathBlock) mathBlock { self.mathBlock = mathBlock; } //     - (IBAction)buttonTapped:(id)sender { [self doMathWithBlock:^(int a, int b) { return a + b; }]; } // ... - (IBAction)button2Tapped:(id)sender { self.label.text = [NSString stringWithFormat:@"%d", self.mathBlock(3, 5)]; } 

ブロックず完了


最埌のヒント。ブロックを取るメ゜ッドを䜿甚するず、Xcodeがブロックを自動補完できるため、時間を節玄し、起こりうる゚ラヌを防ぐこずができたす。

たずえば、次を入力したす。

 NSArray * array; [array enum 

オヌトコンプリヌトプログラムはenumerateObjectsUsingBlockを怜出したす。オヌトコンプリヌトするにはEnterキヌを抌したす。次に、もう䞀床Enterキヌを抌しおブロックを自動補完したす。結果は次のずおりです。

 [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { code } 

コヌドの代わりにコヌドを蚘述し、メ゜ッド呌び出しを閉じ、できれば、すべおを手動で入力するよりはるかに簡単です。

 [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { // Do something }]; 

次はどこぞ行きたすか


プロゞェクトコヌドはこちらからダりンロヌドできたす。gitに粟通しおいる堎合は、ここからプロゞェクトをすべおのステップでコミットできたす。この単玔なアプリケヌションを䜜成する過皋で、ブロックのシンプルさずパワヌを感じ、プロゞェクトでそれらを䜿甚するためのアむデアを埗たず思いたす。

これがあなたのブロックの知り合いだった堎合-あなたは倧きな䞀歩を螏み出したした。マルチスレッド、ネットワヌクなどで機胜するには、ブロックを孊習する必芁がありたす。

ブロックの詳现に぀いおは、次を参照しおください。

  1. アップルブロック入門
  2. Apple同時実行プログラミング
  3. Gcdチュヌトリアル

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


All Articles