2013年9月26日 星期四

增加毛玻璃特效到你的 UIView 中

注意:以下教學只支援 iOS7 以上的 SDK!!

這次 iOS7 的特點就是毛玻璃效果,不過只有特定的 View 可以使用,像是 navagation bar 與 tool bar 以及 alert view 等等。
但是可以透過作弊的方式將這個效果加到你要顯示的 View 裡面,下面就是教學。

首先先建立一個 toolbar 的全域變數
@interface DTBlurView ()
 {
  UIToolbar *_blurToolbar;
 }

 @end

之後在 initial 的時候將 toolbar 的 layer 加到自己身上
- (id)initWithFrame:(CGRect)frame
 {
  self = [super initWithFrame:frame];
  if (self) {
   [self setClipsToBounds:YES];
        
   _blurToolbar = [[UIToolbar alloc] initWithFrame:self.bounds];
        
   [self.layer insertSublayer:_blurToolbar.layer atIndex:0];
  }
  return self;
 }

 - (void)dealloc
 {
  [_blurToolbar release];
    
  [super dealloc];
 }

這樣子就可以了,最後只要將它加到 ViewController 裡面就好了。
結果如下:


另外,本教學不能保證符合 Apple 的 iOS Human Interface Guidelines 的規定,
所以當你被退件的時候,請自行處理。

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 的列舉方法" 的教學,敬請期待。

2013年7月28日 星期日

第一次繪製漸層圖片就上手

這次是使用 UIBezierPath 製作圖片的進階應用繪製一個有漸層效果的圖片,
廢話不多說,趕快下面的教學吧。

// 這次就直接在 viewDidLoad 下面直接繪製
- (void)viewDidLoad
{
	[super viewDidLoad];
	
	// 建立要繪製圖片的座標
	CGRect frame = CGRectMake(20.0f, 20.0f, 200.0f, 200.0f);
	// 將座標中的尺寸取出以便之後使用
	CGSize imageSize = frame.size;
	// 設定邊線的寬度
	CGFloat linWidth = 3.0f;

	// 建立一個畫布,大小為剛剛取出的尺寸
	UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0);

	// 建立一個 RGB 的顏色空間
	CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
	// 建立繪製圖片用的 context
	CGContextRef context = UIGraphicsGetCurrentContext();

	// 建立漸層用的顏色,這邊只用兩個顏色,要更多顏色可以自行加入
	UIColor *beginColor = [UIColor colorWithRed:51.0f/255.0f green:102.0f/255.0f blue:204.0f/255.0f alpha:1];
	UIColor *endColor = [UIColor colorWithRed:0 green:0 blue:102.0f/255.0f alpha:1];

	// 將顏色加入陣列中
	NSArray *gradientColors = [NSArray arrayWithObjects:(id)beginColor.CGColor, (id)endColor.CGColor, nil];

	// 建立漸層顏色的啟始點,因為有兩個顏色,所以有兩個數值,如果有多個顏色就需要多個數值
	// 數值的範圍為 0~1。
	CGFloat gradientLocation[] = {0, 1};

	// 建立繪製漸層的基本資訊
	CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)gradientColors, gradientLocation);

	// 繪製一個矩型路徑,讓漸層的顏色能畫上去
	UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, imageSize.width, imageSize.height)];
	CGContextSaveGState(context);
	[bezierPath addClip];

	// 建立繪製漸層的起點
	CGPoint beginPoint = CGPointMake(imageSize.width / 2, 0);
	// 建立繪製漸層的終點
	CGPoint endPoint = CGPointMake(imageSize.width / 2, imageSize.height);

	// 將繪製的座標加入要繪製的漸層中
	CGContextDrawLinearGradient(context, gradient, beginPoint, endPoint, 0);
	// 建立圖片邊線的資訊
	CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
    
	// 將邊線繪製到路徑中
	[bezierPath setLineWidth:linWidth];
	// 將漸層色填滿路徑
	[bezierPath stroke];

	CGContextRestoreGState(context);

	// 將繪製完成的 context 輸出成 UIImage 格式
	UIImage *drawnImage = UIGraphicsGetImageFromCurrentImageContext();

	// 結束 context,並且釋放記憶體
	UIGraphicsEndImageContext();
	CGColorSpaceRelease(colorSpace);
	CGGradientRelease(gradient);

	// 將繪製完成的圖片加到 UIImageView 讓它顯示
	UIImageView *imageView = [[UIImageView alloc] initWithImage:drawnImage];
	[imageView setFrame:frame];

	[self.view addSubview:imageView];
	[imageView release];
}

完成結果如下: