顯示具有 iOS 標籤的文章。 顯示所有文章
顯示具有 iOS 標籤的文章。 顯示所有文章

2012年2月9日 星期四

iOS - OfflineApp - 在HTML中讀取local image

目的
  • 將HTML中圖片的網址轉為本地端的路徑
  • 使用UIWebView元件將上述轉換後的HTML顯示出內容
問題
  1. 轉換後的路徑為file://xxx/.../xxx.jpg,但發現圖片不能正常顯示出來(文字部份可以)
解決
  1. 使用API取出的路徑除了要加上"file://"外,還要加上"localhost";故完整的路徑應為"file://localhost/xxx/.../xxx.jpg",才能顯示出來

2012年1月11日 星期三

iOS - Thread - NSOperation


目的
  • 使用NSOperationQueue達成執行緒實作
工具
  • NSOperation
    • 執行緒
  • NSOperationQueue
    • 執行緒池,將NSOperation放入交由系統執行
  • NSCondition
    • 類似JAVA中的synchronized機制,使用"lock" & "unlock"防止多執行緒同時存取相同的變數
使用範例
﹣實現Producer & Consumer

Store.h
// Store用來管理Producer及Consumer一同存取的容器 
// 故需要考慮到同步的問題,不可發生同時存取相同容器的問題 

@interface Store : NSObject 
{ 
  NSMutableArray* mProducts; NSCondition* mLocker; 
} 

- (id) initStore; 
- (void) add:(NSNumber*) product; 
- (NSNumber*) get; 

@end

Store.m
#import "Store.h"  

@implementation Store 

- (id) initStore
{
self = [super init];
if (self){
mProducts = [[NSMutableArray alloc] init];
mLocker = [[NSCondition alloc] init];
}

return self;
}

- (void) add:(NSNumber*) product
{
// 同步add product階段
[mLocker lock];
while ([mProducts count] >= 2)
{
[mLocker wait];
}
[mProducts addObject:product];
[mLocker signal];
// wake up one thread
[mLocker unlock];
}

- (NSNumber*) get
{  
// 同步get product階段
[mLocker lock];
while ([mProducts count] <= 0)  
{
[mLocker wait];
NSNumber* product = [mProducts objectAtIndex:0];
[mProducts removeObjectAtIndex:0];
[mLocker signal];
// wake up one thread
[mLocker unlock]; return product; 
}
@end

Producer.h
// 將數字放入容器中  #import "Store.h" 
@interface Producer : NSOperation
{
Store* mStore;
}

- (id) initProducer:(Store*) store;
- (void) main;

@end
Producer.m
#import "Producer.h" 
@implementation Producer
- (id) initProducer:(Store*) store
{
self = [super init];
if (self) 
{ mStore = store; } return self; } - (void) main { for (int iProduct = 1; iProduct <= 10; iProduct++) { // 隨機睡眠 0~4秒 [NSThread sleepForTimeInterval:(arc4random() % 5)]; [mStore add:[NSNumber numberWithInt:iProduct]]; NSLog(@"Produce %d", iProduct); } } @end
Consumer.h
// 將收字從容器中拿出 #import "Store.h" 
@interface Consumer : NSOperation
{
Store* mSotre;
}

- (id) initConsumer:(Store*) store;
- (void) main;

@end

Consumer.m
#import "Consumer.h"

@implementation Consumer

- (id) initConsumer:(Store*) store
{
self = [super init];
if (self)
{
mSotre = store;
}

return self;
}

- (void) main
{
for (int iLoop = 0; iLoop <= 10; iLoop++)
{
[NSThread sleepForTimeInterval:(arc4random() % 5)];

NSLog(@"Consume %d", [[mSotre get] intValue]);
}
}
@end
ViewController.h
#import "Producer.h"
#import "Consumer.h"
#import "Store.h"

@interface ViewController : UIViewController
{
NSOperationQueue* mQueue;
Producer* mProducer;
Consumer* mConsumer;
Store* mStore;
}

@end
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];

mQueue = [[NSOperationQueue alloc] init];
mStore = [[Store alloc] initStore];

// 將Store放入Producer & Consumer裡
mProducer = [[Producer alloc] initProducer:mStore];
mConsumer = [[Consumer alloc] initConsumer:mStore];

// 將Producer & Consumer放入Operation queue中,
// 開始執行Producer & Consumer
[mQueue addOperation:mProducer];
[mQueue addOperation:mConsumer];
}
Note
  • 其中"[mLocker signal];" 可代換成"[mLocker broadcast];",差別在於前者"signal"喚醒一個等待此thread的thread;而後者喚醒所有在等待此thread的thread

2012年1月10日 星期二

iOS - ASIHTTPRequest

目的
  • 用於網路溝通
工具
使用範例
﹣欲下載網路圖片如下
圖片來源
﹣Code
.h
#import <UIKit/UIKit.h>
#import "ASIHTTPRequest.h"
#import "ASINetworkQueue.h"

@interface ViewController : UIViewController <ASIHTTPRequestDelegate>
{
    NSMutableData* mDataBuf;
    ASINetworkQueue* mQue;
    NSArray* mUrls
}

@end

.m
- (void)viewDidLoad
{
  [super viewDidLoad];  
  // 下載的緩衝區
  mDataBuf = [[NSMutableData alloc] init];
  
  // 執行Request區域,類似NSOperationQueue,
  // 故在執行Request時是以執行緒方式進行,不影響原執行緒  
  mQue = [[ASINetworkQueue alloc] init];
  [mQue reset];
  [mQue setShowAccurateProgress:YES];
  [mQue go];


  // 設定下載位址

  ASIHTTPRequest* request = [[ASIHTTPRequest alloc] initWithURL:url];

  // 設ASIHTTPRequest delegate,用於回傳訊息並處理
  request.delegate = self;

  // 開始處理Request
  [que addOperation:request];
}

// ASIHTTPRequestDelegate 接收到資料後

- (void)request:(ASIHTTPRequest *)request didReceiveData:(NSData *)data
{
    [mDataBuf appendData:data];
    
    NSLog(@"Recevie %d bytes...", [data length]);
}

// ASIHTTPRequestDelegate 資料接收完成
- (void)requestFinished:(ASIHTTPRequest *)request 
{
  // 取得存放位置
  NSArray *pathList = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  NSString *path = [pathList objectAtIndex:0];    
  
  // 指定
資料夾
  path = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"/tmp/test"]];
    
  // 若指定資料夾不存在則創建
  NSFileManager* fileM = [NSFileManager defaultManager];
  NSError* error;
  if(![fileM fileExistsAtPath:path]) [fileM createDirectoryAtPath:path withIntermediateDirectories:true attributes:nil error:&error];

  //加上檔名
  path = [path stringByAppendingPathComponent: [NSString stringWithFormat: @"/00.jpg"]];
  NSLog(@"儲存路徑:%@", path);

  //寫入檔案
  [mDataBuf writeToFile:path atomically:NO];
  mDataBuf = [[NSMutableData alloc] init];
    
  NSLog(@"Download success");
}

Note
  • ASIHTTPRequestDelegate還有許多回傳方法,依需要來實作
  • 加入專案時可能會發生一堆類似的參考錯誤 "Apple Mach-O Linker (id) Error"
上述錯誤需加入sqlite3的library
進入TARGETS

依紅框指示加入參數和library,以解除其它參考的錯誤

2012年1月2日 星期一

iOS - Regular Expression

目的
  • 使NSString型態可以使用Regular Expression來表示

工具
  • RegexKitLite: 開源碼,輕量化的regular expression物件。
  • Download (官網
  • 解壓縮後將RegexKitLite.h 和 .m複製到要使用的專案中
  • 再加入"-fno-objc-arc"參數,避免ARC機制的啟動

範例
NSError* error;

// 取得Yahoo的HTML內容

NSString* tmp = [NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://www.yahoo.com.tw"] encoding:NSUTF8StringEncoding error:&error];

// 使用regular expression過濾內容
    NSString* regEx = @"<title>(.*)</title>";
    for(NSString* sub in [tmp componentsMatchedByRegex:regEx])
    {
        NSLog(sub);
    }

結果

2012年1月1日 星期日

iOS - 爬 HTML

目標
  • 抓取網頁HTML
  • 分析HTML
工具
  • libxml2: iOS原有的library,parse xml
  • 點選紅框標示的地方
    點選紅框標示的地方,最後使用“+”把libxml2加入


    在紅框的地方搜尋“other linker”,再加入“-lxml2”參數

  • Hpple: 開源碼,幫助分析HTML
    • Download
    • 將資料夾中的TFHpple, TFHppleElement, XPathQuery複製到要開發的資料夾中
    • 再加入“-fno-objc-arc”參數關閉ARC功能

範例
- (void)viewDidLoad
{
    [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
    
    NSError* error;

    // 取得yahoo首頁的HTML
    NSData* data = [[NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://www.yahoo.com.tw"] encoding:NSUTF8StringEncoding error:&error] dataUsingEncoding:NSUTF8StringEncoding];

    // 取出title標籤中的值
    TFHpple* parser = [[TFHpple alloc] initWithHTMLData:data];   
    NSArray* values  = [parser searchWithXPathQuery:@"//title"]; // get the title
    TFHppleElement* value = [values objectAtIndex:0];
    NSString* result = [value content];

    NSLog(@"result = %@", result);
    mLabel.text = result
}

問題
  • Compile後會發生"<libxml/tree.h> not found"
    • 解:在Header search path中加入“${SDKROOT}/usr/include/libxml2”參數(${SDKROOT}會自動變為root路徑)
先在搜尋欄中搜尋“header search",再將參數輸入


結果

2011年12月15日 星期四

iOS - SQLIte - FMDB

FMDB為一個方便使用iOS裡的sqlite框架,主要物件為:
  • FMDatabase 提供方法來透過SQL與DB溝通
  • FMResultSet 與Java的Cursor物件類似

產生DB
    // 設定Label元件在文字內容超過框架時的樣式
    content.lineBreakMode = UILineBreakModeWordWrap;
    // 設定Label元件最多顯示的行數,0為不限
    content.numberOfLines = 0;
    // 透過NSFileManager取得Document的路徑
    NSURL* url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    // Document下新增DB檔案
    NSString* dbPath = [[url path] stringByAppendingPathComponent:@"myDB.db"];
    
    // 透過FMDatabase物件與DB溝通
    db = [FMDatabase databaseWithPath:dbPath];
    
    if(![db open])
    {
        NSLog(@"Could not open database.");
    }
    if(![db executeUpdate:@"CREATE TABLE IF NOT EXISTS info (id INTEGER PRIMARY KEY, name TEXT, tel TEXT)"])
         {
             NSLog(@"Could not creat table (%@)", [db lastErrorCode]);
         }

插入資料
// VALUES後的格式需使用範例這樣的方法,否則會出錯

    if(![db executeUpdate:@"INSERT INTO info (name, tel) VALUES (?, ?)",@"Vince",@"9988765"])
    {
        NSLog(@"Could not insert");
    }

取得資料
FMResultSet* rs = [db executeQuery:@"SELECT * FROM info"];

    
    // 印出所有Query到的資料
    while([rs next])
    {
        int _id = [rs intForColumn:@"id"];
        NSString* name = [rs stringForColumn:@"name"];
        NSString* tel = [rs stringForColumn:@"tel"];
        
        NSMutableString* strTemp = [NSMutableString stringWithString:content.text];
        if([strTemp compare:@"Label"])
        {
            [strTemp appendFormat:@"%d-%@-%@\n", _id, name, tel];
            content.text = strTemp;
        }else
        {
            content.text = [NSString stringWithFormat:@"%d-%@-%@\n", _id, name, tel];
        }
    }

2011年12月14日 星期三

iOS - 指定不使用ARC機制的程式

Q:在iOS 5之後的版本新增了ARC機制來讓系統自動回收記憶體,但遇到需要自行管理記憶體時怎麼辨?


A:可以在TARGETS > Build Phases > Compiler Sources中,在想要自行管理記憶體的程式上點兩下,並加入“-fno-objc-arc”參數即可


2011年12月12日 星期一

iOS - Training (13) - 修飾詞

static, public -----
  • "+": 與Java的static相同,執行完後面的行為後隨時會被回收
  • "-": 與Java的public類似(但private方法的前面也是使用此符號),執行完後面的行為後因為有實體的記憶體位置,故會放入release pool裡等待釋放

@interface TEST: NSObject 
// 公開方法
- (void)publicMethod;

// 靜態方法
+ (void)staticMethod;
@end

使用公開方法
TEST* test = [TEST alloc] init];
[test publicMethod];
[test release];

使用靜態方法
[TEST staticMethod]
-----

@property -----
  • 默認屬性
    • readwrite 自動生成存取器setter、getter
    • assign 處理基礎類型,如int、float...
  • 修飾屬性
    • natomic 與Java的synchronized類似,在多執行緒時保護參數的同步
    • readonly 只生成getter不會有setter方法
    • copy 與Java的clone類似,複製一個與原物件不同記憶體位置的新物件
    • retain 自動retain物件
TEST.h
@interface TEST: NSObject {
    NSString* param;
}
@property  (nonatomic, retain) NSString *param;
@end
TEST.m
@implementation TEST


@synthesize param;


- (id)init{}
@end
-----

參數的@public, @protected, @private -----


@interface TEST: NSObject {
    @public
        NSString* publicString;
   
    @protected
        NSString* protectedString;
   
    @private
        NSString* privateString;
}
@end

-----

方法的private宣告 -----
  • 寫在實現的文件中,此範例為TEST.m
  • 需在@implementation上描述
#import "TEST.h"

@interface TEST (private)
- (void) privateMethod
@end

@implementation TEST

- (id)init
{
  ...
}

- (void)privateMethod
{
  ...
}
@end
-----

2011年12月11日 星期日

iOS - Xcode 4 加入 Library 的方法

與原來Xcode 3的方法不大一樣,步驟如下:

這個範例目的要加入sqlite3的library。

1. 點選Project中的TARGETS選項

2. 在右邊的顯示中選擇"Build Phases",點擊"Link Binary With Libraries“下方的”+“,會出現很多library,這裡選取了libsqlite3.0.dylib。

2011年12月7日 星期三

iOS - Training (12) - UITableView

繼承UIScrollView。適用在要list出清單時。


屬性說明
  • 風格上分為Plain與Grouped。Plain較為方正,Grouped較為圓滑。
  • Table view由多個section組成,section包含多個cell。
  • NSIndexPath物件用來指引點擊的cell在哪個section中的第幾個cell。
TableView.m
- (void)viewDidLoad

{
    [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
    myTable.dataSource = self;
    myTable.delegate = self;
    
    dataArray = [[NSArray alloc] initWithObjects:@"one", @"two", @"three", @"four", @"five", nil];
}

// 決定section的個數,return 1表示只有一個section
- (NSInteger)numberOfSectionInTableView:(UITableView *)tableView
{
    return 1;
}

// 因為只有一個section,故直接回傳dataArray count,若有多個section要加if-else
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [dataArray count];
}

// 決定所要回傳的cell
- (UITableView *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 定義一個名稱,供reuse queue使用
    static NSString* CellIdentifier = @"Cell";
    
    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    // 一開始還沒有queue時,會initial一個新的queue
    if(cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
    }
    cell.textLabel.text = [dataArray objectAtIndex:indexPath.row];
    
    return cell;
}

// 取得使用者所按到的cell
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"Selected %@", [dataArray objectAtIndex:indexPath.row]);
}

UITableViewCell 樣式-----
AccessoryType = UITableViewCellAccessoryDisclosureIndicator

- (void)viewDidLoad
{
    [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
    myTable.dataSource = self;
    myTable.delegate = self;
    
    dataArray = [[NSArray alloc] initWithObjects:@"one", @"two", @"three", @"four", @"five", nil];
    subArray = [[NSArray alloc] initWithObjects:@"1", @"2", @"3", @"4", @"5", nil];
}


// 決定所要回傳的cell

- (UITableView *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 定義一個名稱,供reuse queue使用
    static NSString* CellIdentifier = @"Cell";
    
    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    // 一開始還沒有queue時,會initial一個新的queue
    if(cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }
    cell.textLabel.text = [dataArray objectAtIndex:indexPath.row];
    cell.detailTextLabel.text = [subArray objectAtIndex:indexPath.row];
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    
    return cell;
}

AccessoryType = UITableViewCellAccessoryDetailDisclosureButton


// 決定所要回傳的cell

- (UITableView *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 定義一個名稱,供reuse queue使用
    static NSString* CellIdentifier = @"Cell";
    
    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    // 一開始還沒有queue時,會initial一個新的queue
    if(cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }
    cell.textLabel.text = [dataArray objectAtIndex:indexPath.row];
    cell.detailTextLabel.text = [subArray objectAtIndex:indexPath.row];
    cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
    
    return cell;
}

// 當按下藍色按鈕時,呼叫此方法
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"Press %@ button", [dataArray objectAtIndex:indexPath.row]);
}
-----

資料出處《Object-C與iOS開發入門》