2013年8月24日 星期六

Array 的列舉

在早期 C 語言 Array 的列舉有兩種方法,分別為 for 與 for-in 兩種,
到了 Objective-C 這兩種方法也是繼續存在,不過除此之外多了另一個新的方法,
是採用 block 的 enumerate 的方式,它的好處是它會使用另一個 Thread 去處理,
不會影響原本的 Thread 導致畫面凍結的問題,至於這三個要怎麼使用就由我來做教學。

首先假設有一個 Array 名稱為 sampleArray,它的 Code 如下:
	NSArray *sampleArray = [NSArray arrayWithObjects:@"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9", @"0", nil];

首先是最簡單的 for 列舉的方式,另外故意在列舉到 8 的時候停止,
至於為什麼,我就慢點再解答了。
	NSLog(@"For 的結果:");
	for (int i = 0; i <= sampleArray.count - 1; i++) {
		NSString *symbol = (NSString *)sampleArray[i];
		NSLog(@"%@", symbol);
	
		if ([symbol isEqualToString:@"8"]) {
			break;
		}
	}
結果如下:








接下來就是 for-in 的列舉,同樣列舉到 8 的時候停止。
	NSLog(@"For in 的結果:");
	for (NSString *symbol in sampleArray) {
		NSLog(@"%@", symbol);
	
		if ([symbol isEqualToString:@"8"]) {
			break;
		}
	}
結果如下:








最後是上面所說的 enumerate 的方式,同樣列舉到 8 的時候停止,
這時停止列舉的方式就與之前的完全不一樣了,這是因為在 block 中無法使用 break; ,所以折衷方案就變成這樣子了。
另外預設的 block 回傳內容如右:^(id obj, NSUInteger idx, BOOL *stop),
其中的 id obj 可以改為對應的 Array 內容,如果你的 Array 內容不固定的話,那還是使用預設的內容。
	NSLog(@"Enumerate 的結果:");
	[sampleArray enumerateObjectsUsingBlock:^(NSString *symbol, NSUInteger idx, BOOL *stop) {
		NSLog(@"%@", symbol);
	
		if ([symbol isEqualToString:@"8"]) {
			*stop = YES;
		}
	}];
結果如下:








另外 enumerate 的列舉有另一個方法,就是反向列舉,使用方法如下:
	NSLog(@"Enumerate Reverse 的結果:");
	[sampleArray enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSString *symbol, NSUInteger idx, BOOL *stop) {
		NSLog(@"%@", symbol);
	
		if ([symbol isEqualToString:@"8"]) {
			*stop = YES;
		}
	}];
這時候的結果如下:




基本上 enumerate 的列舉的效能比起另外兩個好上很多,
主要是它是使用另外一個 Theard 來處理的,不過成也 Theard,敗也 Theard,
要是你在列舉的過程中需要使用非同步的處理的話,就 enumerate 的列舉會讓你進入一陣混亂,
因為非同步的處理也是使用另一個 Theard 來處理的,因此會導致在另一個 Theard 中又進入了另一個 Theard,
這會讓這個程式超出你的掌控,所以這時候必須改回原始的 for 或是 for-in 來列舉。


最後,這次的教學到此為止,大家下次再見了。

2013年8月20日 星期二

隱藏 UITableViewCell 的 Edit Control

此教學在 iOS 7 上已經無效了,請改看隱藏 UITableViewCell 的 Edit Control [For iOS 7]


在 UITableViewCell 有一個欄位叫做 Edit Control,這是根據官方的文件所得知的,
不過常常會有需要自定一個當多選 Cell 的時候的圖片,如下圖:


不過 Edit Control 卻是私有的 Code,你無法自定它,
既然不能自定原始的 Edit Control,那就將它藏起來,並做一個新的給覆蓋過去就好了,
那麼要怎麼做了?這篇教學正是教你怎麼做到這件事。

首先就是建立一個繼承 UITableViewCell 的 Class,接下來是讓它與 UITableView 做連動,
要如何做連動我就不教了,這不是本篇的重點,
再來就是在這個 Class 動點手腳了,動手腳的就是 - layoutSubviews 這個 Method,

先 log 現在 cell 裡的 subviews 的資訊
- (void)layoutSubviews
{
	[super layoutSubviews];
	
	if (self.editing) {
		NSLog(@"%@", self.subviews);
	}
	
}

這時候得到的資訊如下圖:

其中 UITableViewCellEditControl 正是 Edit Control 的 Class 名稱,
不過 UITableViewCellEditControl 是私有的 Class,所以無法使用 isKindOfClass: 做判斷,
所以改用另一個繞路的方式來判斷,就是 NSStringFromClass() 將 Class 名稱轉成 NSString 之後再使用 isEqualToString: 去判斷,
這時候再把 Code 寫成下面的樣式:

- (void)layoutSubviews
{
	[super layoutSubviews];
	
	if (!self.editing) {
		return;
	}
	
	void (^enumBlock) (id, NSUInteger, BOOL *) = ^(UIView *subview, NSUInteger idx, BOOL *stop) {
		if ([NSStringFromClass([subview class]) isEqualToString:@"UITableViewCellEditControl"]) {
			[subview setHidden:YES];
	    
			*stop = YES;
		}
	};
	
	[self.subviews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:enumBlock];
}

這時候礙眼的 Edit Control 就從這樣子:

變成了這樣子了:

這時候就能隨你去增加圖片來取代原始的 Edit Control 了。
本篇教學就到這邊告一個段落,下一次會是" Array 的列舉方法" 的教學,敬請期待。