2010年8月5日 星期四

DOM-based XML utility For iPhone

在Mac OSX上,分別有NSXMLParserNSXMLDocument來處理有關SAX與DOM方式的XML parsing。然而在iOS上的Cocoa Touch,卻只剩下SAX的NSXMLParser的處理方式。相較於DOM來說,SAX的處理方式是較為快速與節省資源的,也或許是這樣才會在iOS上只保留這種方式的parsing。

然而,如果只是簡單格式的XML讀取,用NSXMLParser來parse可能是很快速簡便。但是如果要處理的XML格式眾多或是格式複雜,那就可能不是那麼好過了...而且NSXMLParser也只能處理讀取時的parsing。如果是要將資料輸出成XML格式,那不使用DOM的處理將會變得比較難以處理。

後來在internet上,發現這篇APXML: NSXMLDocument 』substitute' for iPhone/iPod Touch,才知道有這個APXML的DOM-based簡易套件可以使用。說它是簡易套件還真不為過,含Header file在內,全部也才不過6~7個source code files,每個也都不算太大、太複雜。
既然是DOM-based parser,當然除了可以讀取,也可以輸出成為XML。使用上很簡單,只要
#import "APXML.h"
就可以使用到全部的相關類別,不必一一去import個別的class。
Parse XML:
APDocument *doc = [APDocument documentWithXMLString:xmlstring];
取得Root Element:
APElement *rootElem = [doc rootElement];
讀取所有子element:
NSArray *childElements = [rootElem childElements];
讀取element name:
NSString *elementName = elem.name;
讀取element value:
NSString *elementValue = elem.value;
讀取特定名稱之attribute:
NSString *attrValue = [elem valueForAttributeNamed:attributeName];

輸出部分則為
Create Element:
APElement *elem = [APElement elementWithName:elementName];
Set attribute & element value:
[elem addAttributeNamed:attrName withValue:attiValue];
[elem appendValue:elementValue];
Add child element:
[elem addChild: childElem];
Create XML Document:
APDocument *doc = [[APDocument alloc] initWithRootElement: elem];
Output XML string:
NSString *xmlString = [doc xml];

使用語法算是十分簡單、直覺。
其實,要自己實作DOM-based parser也不算太難,但是總是比較費工夫、品質也需要時間來鍛鍊。既然有現成又不錯的lib,當然能善加利用會更好囉~

好用的HTTP lib(for iPhone & Mac OSX)

HTTP API其實在Cocoa裡面是有的,透過NSURL、NSURLRequest、NSURLConnection等類別來達成,也分別支援同步與非同步傳輸。使用上雖然不算是太難,但是仍然有不少繁瑣的手續要做,要處理的delegate method也不算少,讓人感覺還是有點不是那麼方便。

因此,後來在internet上,找到一個叫做ASIHTTPRequest的lib,這個lib是以BSD license的方式open source的。顧名思義,這個lib的主要工作就是提供HTTP protocol的通訊機制,而且用很簡單的方式讓developer可以操作request、response,以及設定POST parameter,甚至是上下傳檔案等等。本身也內建啟用Cookie機制,所以對於一些有session tracking的web services也很方便。當然也提供有同步與非同步的傳輸。
官網位置:http://allseeing-i.com/ASIHTTPRequest/

心得:該Lib除了可以在Mac OSX上使用外,也可以在iOS上使用,而且目前已經與iOS4相容。使用上算是很簡單,也沒碰過啥大問題。如果要說缺點,就是放到project中,會導致project code在compile之後多出不少容量吧XD

2010年8月4日 星期三

動態調整TableVewCell的高度

因為工作需求,原本要使用UITextView來顯示多行文字,但是發現使用了UITextView之後,Cell可以觸發select動作的區域將變得很小(受到UITextView的區域影響)。所以改用別的方式處理。

首先,發現原來UILabel是可以顯示多行文字的,只要把property numberOfLines設為0,就可以支援多行文字,但是必須呼叫[label sizeToFit],來調整UILabel的大小。

而預設的UITableViewCell的textLabel也可以這樣調整,所以就可以顯示多行文字。然而,UITableView還有個問題,就是有cell height的問題。因為cell height似乎是在cell顯示前就會設定好,所以即使在tableView:cellForRowAtIndexPath:這個method裡面去調整cell的高度也是無效。必須透過tableView:heightForRowAtIndexPath:來回傳特定cell的高度。

但是,對於多行文字來說,因為內容行數是不固定的,所以必須在顯示時計算實際行數文字的總高度。幸好在參考iPhone SDK: Resizing a UITableViewCell to Hold Variable Amounts of Text, Part 2 of 2這篇之後,得到解答。就是利用NSString裡面的sizeWithFont:constrainedToSize:lineBreakMode:來根據指定的字型高度跟斷行方式來計算。可以使用Objective-C的Category機制,將此method放置到NSString中。以下是參考上述文章所寫的code:
@implementation NSString (TextHeight)
-(CGFloat) textHeightForSystemFontOfSize:(CGFloat)size {
//Calculate the expected size based on the font and linebreak mode of the label
CGFloat maxWidth = [UIScreen mainScreen].bounds.size.width - 50;
CGFloat maxHeight = 9999;
CGSize maximumLabelSize = CGSizeMake(maxWidth,maxHeight);
CGSize expectedLabelSize = [self sizeWithFont:[UIFont systemFontOfSize:size] constrainedToSize:maximumLabelSize lineBreakMode:UILineBreakModeWordWrap];
return expectedLabelSize.height;
}
@end

透過此method,就可以在tableView:heightForRowAtIndexPath:中回傳正確的文字內容高度了。