2010年4月30日 星期五

PCMan for Firefox 3.6 ,好用的逛 BBS 工具/Plugin/Extension @ Ubuntu 10.04

pcmanfx


雖然兩年前就知道這個套件,但還是很習慣用 PCMan Lite 而已,只是隨著工作環境的改變,不再只是 Windows 平台,有時是 Linux 或 Mac ,這時候若可以透過瀏覽器幫忙作跨平台,加上只是單純找找資料,也是個不錯的方式!


PCMan 已經有一段時期了,早年跟 KKMan 一樣,透過一個瀏覽介面,提供便利地逛網頁跟逛 BBS 的結合,畢竟台灣最大的地下活動就屬 BBS 了!連現在 2010 年,國外都在用 Web 論壇,台灣現在各大學仍在瘋 BBS 哩!如台灣最大的 BBS 站批踢踢,同時可上線的人數已常常破 10 萬關卡,甚至對岸也會來使用。


關於 Firefox Plugin 的下載,請到這邊:pcmanfx, PCMan Telnet/BBS extension for FIrefox - enable telnet in web browser.


直接下載:pcmanfx-0.1.8.xpi
補充資訊:


By cc 於 2010/04/30 12:59:

pcmanfx 0.1.8版還有一些嚴重的bug,
但是目前開發者似乎沒空處理,
不過有好心人士幫忙修正了,
可以從專案網頁 http://code.google.com/p/pcmanfx/
左上方的Updates進去,再進入issue 27,就可以看到了。
下面是他提供的下載點:
http://cid-2e6c8f3d9767a9d7.skydrive.live.com/browse.aspx/.Public/pcmanfx
抓pcmanfx-0.1.8_r40+_alpha_1.xpi這個回來安裝。


下載後,直接拖拉到 Firefox 瀏覽器上即可安裝,使用上就像另起新頁,用 telnet:// 即可,如 telnet://ptt.cc


2010年4月29日 星期四

Mac Mini 散熱架:鍋墊 + 防滑貼

鍋墊 + Mini


之前一直把 Mini 擺在主機上頭,但我主機上頭有接硬碟,而 Mini 底部是防滑的材質,將導致擺在主機上頭會有聚熱的現象,所以,我就跑去找一下可以拉高間距的東西,第一個想到的就是鍋墊啦!


鍋墊 防滑


並且要找間隔較高的,所以就找到這個竹子做的鍋墊,然而,它的底部是用釘子定在一起,恰好擺在主機上會有滑動的感覺,不穩,所以我又去找防滑貼,逛了 3C 賣場倒沒看到,結果是在一間書店裡,找到勉強可以用的,那個主要是用在椅子下頭的墊子,最後的成品:


鍋墊 + 防滑


只用到兩個防滑墊,把它切成兩半剛剛好四個角,只是這種防滑墊是設計給重物的,所以擺在像機殼這種材質上面,在加上 Mini 的重量,防滑的效果有限,最後我就給它反過來用,把原本鍋墊的正面當反面來用,接觸面積增大,剛好就比較不滑囉!所以,若真的要省錢,大概連防滑墊都不需要買。此次花費:鍋墊 14 元、防滑墊 39 元(8個圓墊,只用兩個)


鍋墊 + Mini


iOS 開發教學 - 使用 Property List 和 SQLite 處理資料儲存

這筆記來自於 CS 193P iPhone Application Development 2010 Winter
課程 - 9. Data in Your iPhone App (February 2, 2010)

無論寫什模樣大小的程式,都是需處理資料儲存的問題,例如依使用者的偏好、遊戲進行的進度,甚至可以增加程式的容錯性等等,這些都需要處理資料儲存的問題。在 iPhone Apps 裡,最簡單的存取資料的方式就是使用 Property List ,也就是應用程式常見的 *.plist 檔案,這非常適合用在小量資料的儲存,但倘若資料量進入了 KB 階段,還可以用用 SQLite 囉!至於什麼時候則不適用 SQLite 呢?以下是投影片提到的:


  • Multi-gigabytes databases

  • High concurrency (multiple writers)

  • Client-server applicaitons

  • “Appropriate Uses for SQLite”

所幸的 iPhone 也可以跑 C 程式,所以也還是可以維持用 C 處理檔案的部份啦,唯一要留意的是了解自己寫的程式,他們儲存資料的相對位置囉。

在進入資料的存取操作前,先簡介一下,對 APP 而言的目錄結構:

<Applocation Home>

MyApp.app

MyApp

MainWindow.nib

SomeImage.png

Documents

Library

Caches

Preferences

並且基於安全問題,限制只能在自己的家目錄進行檔案存取的行為,並且可以利用以下的方式取得對應位置:


  • Basic

    • NSString *homePath = NSHomeDirectory();

    • NSString *tmpPath = NSTemporaryDirectory();

    • NSString *filePath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
      NSString *fileDirPath = [filePath stringByDeletingLastPathComponent];

  • Document

    • NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

    • NSString *documentsPath = [paths objectAtIndex:0];


  • 例子

    • Application Home>/Documents/my.db

      • NSArray *paths =
        NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
        NSUserDomainMask, YES);

        NSString *documentsPath = [paths objectAtIndex:0];
        NSString *dbPath = [documentsPath stringByAppendingPathComponent:@"my.db"];

  • 補充

    • 第一次執行程式時,有些情境會替程式準備好預備用的資料,這時就必須進行適當的處理,把原先以預備好的資料複製對應的位置,例如一開始在打包程式時,已經有先做好一個 default.db ,裡面已經有些 table 資料等等,當程式第一次執行時,那就把它複製到對應的位置,如 my.db 等,往後則都是對 my.db 進行操作囉!

      • BOOL check_exists = [[NSFileManager defaultManager] fileExistsAtPath:dbPath];

使用 Property Lists:

用途:處理小量資料

用法:

// Writing
- (BOOL)writeToFile:(NSString *)aPath atomically:(BOOL)flag;
- (BOOL)writeToURL:(NSURL *)aURL atomically:(BOOL)flag;

// Reading
- (id)initWithContentsOfFile:(NSString *)aPath;
- (id)initWithContentsOfURL:(NSURL *)aURL;

或是更強大的 NSPropertyListSerialization

+ (NSData *)dataFromPropertyList:(id)plist format:(NSPropertyListFormat)format errorDescription:(NSString **)errorString;
+ (id)propertyListFromData:(NSData *)data mutabilityOption:(NSPropertyListMutabilityOptions)opt format:(NSPropertyListFormat *)format errorDescription:(NSString **)errorString;

例如將 Array 或 Dictionary 內容寫到檔案,將會以 XML 格式儲存,可使用 initWithContentsOfFile 轉回來:

// write an array to disk

NSArray *my_array = ...
[my_array writeToFile:@"my_array.plist" atomically:YES];

// write a dictionary to disk
NSDictionary *my_dict = ...
[my_dict writeToFile:@"my_dict.plist" atomically:YES];

參考資料:


使用 SQLite:

由於這邊跟 C 語言一樣,細節可參考這篇完整的範例  [C] 使用 SQLite 教學筆記 - 簡單的 C 語言程式範例 ,下面則會附上與 Objective C 的完整例子,主要留意的是 CallBack function 的撰寫與資料的存取使用。

完整的範例程式:


  • [Xcode]->[Create a new Xcode project]->[iPhone
    OS]->[Application]->[Window-based Application]-> 此例以 MyDataHandle
    為例


    • [Xcode]->[File]->[Cocoa Touch Class]->[UIViewController
      subclass] (勾選 UITableViewController subclass) -> 此例以 MyTableList 為例

  • 加入 sqlite3 的函式庫

    • [MyDataHandle]->[Frameworks]->按右鍵 Add -> Existing Frameworks -> 選擇加入 libsqlite3.dylib

程式碼:

MyDataHandleAppDelegate.h

#import <UIKit/UIKit.h>
#import "MyTableList.h"

@interface MyDataHandleAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    MyTableList *table;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@end

MyDataHandleAppDelegate.m

#import "MyDataHandleAppDelegate.h"

@implementation MyDataHandleAppDelegate

@synthesize window;

- (void)applicationDidFinishLaunching:(UIApplication *)application {  
    table = [[MyTableList alloc] initWithStyle:UITableViewStylePlain];
    [window addSubview:table.view];

    // Override point for customization after application launch
    [window makeKeyAndVisible];
}

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

@end

MyTableList.h

#import <UIKit/UIKit.h>

@interface MyTableList : UITableViewController {
    NSMutableArray *dataSource;
}

@end

MyTableList.m

#import "MyTableList.h"
#import <sqlite3.h>

#define SQL_CREATE_TABLE    "CREATE TABLE IF NOT EXISTS my_table( filed1 char(20) );"
#define SQL_INSERT_ITEM        "INSERT INTO my_table VALUES( 'Hello World' );"
#define SQL_QUERY            "SELECT * FROM my_table;"


static int CallBackFetchRowHandling( void * context , int count , char **value, char **column ) {
    NSMutableArray *dataSource = (NSMutableArray*) context;
    for ( int i=0 ; i<count ; ++i ) {
        //NSLog( @"%s" , value[i] );
        //[dataSource addObject:[NSString stringWithUTF8String:value[i]]];
        [dataSource addObject: [NSString stringWithFormat:@"[db] %s", value[i]]];
    }
    return SQLITE_OK;
}


@implementation MyTableList

- (void)dataReload {
    // get db path
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsPath = [paths objectAtIndex:0];
    NSString *dbPath = [documentsPath stringByAppendingPathComponent:@"my.db"];
  
    // check db exists
    BOOL firstUse = ![[NSFileManager defaultManager] fileExistsAtPath:dbPath];
    //NSLog( firstUse ? @"First-use" : @"Not" );
  
    // open & init & read data
    sqlite3 *db;
    char *err_report;
  
    if ( sqlite3_open( [dbPath UTF8String] , &db ) == SQLITE_OK ) {
        //NSLog( @"%s" , dbPath );
        if ( firstUse ) {
            [dataSource addObject:@"== init my.db =="];
            if( sqlite3_exec( db , SQL_CREATE_TABLE , NULL , NULL , &err_report ) != SQLITE_OK )
                NSLog( @"%s" , err_report );
            if( sqlite3_exec( db , SQL_INSERT_ITEM , NULL , NULL , &err_report ) != SQLITE_OK )
                NSLog( @"%s" , err_report );
        }
        if ( sqlite3_exec( db , SQL_QUERY , CallBackFetchRowHandling , dataSource , &err_report ) != SQLITE_OK ) {
            NSLog( @"%s" , err_report );
        }
    } else {
        NSLog( @"Cannot open databases: %s" , dbPath);
    }
    sqlite3_close( db );
}

- (id)initWithStyle:(UITableViewStyle)style {
    // Override initWithStyle: if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
    if (self = [super initWithStyle:style]) {
        dataSource = [[NSMutableArray alloc] init];
      
        // from property list
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsPath = [paths objectAtIndex:0];
        NSString *plistPath = [documentsPath stringByAppendingPathComponent:@"my.plist"];
        if ([[NSFileManager defaultManager] fileExistsAtPath:plistPath] ) {
            NSArray *data = [[NSArray alloc] initWithContentsOfFile:plistPath];
            [dataSource addObjectsFromArray:data];
            [data release];
        } else {
            [dataSource addObject:@"== init my.plist =="];
            NSArray *data = [[NSArray alloc] initWithObjects:@"[plist] Hello World", nil];
            [data writeToFile:plistPath atomically:YES];
            [dataSource addObjectsFromArray:data];
            [data release];
        }
      
        // from databases
        [self dataReload];

    }
    return self;
}
- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
  
    // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

#pragma mark Table view methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [dataSource count];
}

// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  
    static NSString *CellIdentifier = @"Cell";
  
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
  
    // Set up the cell...
    cell.textLabel.text = [dataSource objectAtIndex:indexPath.row];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // Navigation logic may go here. Create and push another view controller.
    // AnotherViewController *anotherViewController = [[AnotherViewController alloc] initWithNibName:@"AnotherView" bundle:nil];
    // [self.navigationController pushViewController:anotherViewController];
    // [anotherViewController release];
}

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

@end

程式第一次執行時,因為沒有 my.plist ,所以會印出 "== init my.plist ==" ,並且建一個 array 把資料存到 file ,至於對 db 而言,第一次因為沒有 db 資料,所以也會建一個,並印出 "== init my.db ==" ,還會去建 table 以及把資料存進去。

展示:

第一次執行:

init use

之後的執行:

next use



2010年4月28日 星期三

[C] 使用 SQLite 教學筆記 - 簡單的 C 語言程式範例

好久沒用 databases 囉!前一次自己使用已經是三、四年前的事了,碩班都是自己維護甚至用 C 語言開發資料的處理部分。回到正題,SQLite 是一套很簡潔的資料庫,適用於不少地方,細節請參考:Appropriate Uses For SQLite


相關學習文章:



此篇僅筆記如何使用,附上完整的範例程式:


#include <sqlite3.h>
#include <stdlib.h>
#include <stdio.h>

#define CMD_CREATE_TABLE    "CREATE TABLE IF NOT EXISTS MyTable( column1 int, column2 char(20) );"
#define CMD_INSERT_ITEM        "INSERT INTO MyTable VALUES( 1 , 'Hello World' );"
#define CMD_QUERY        "SELECT * FROM MyTable;"

int Callback_ShowList( void *context, int count, char **values, char ** columnName )
{
    int i;
    context = NULL;
    for( i=0 ; i<count ; ++i )
        printf( "\t\t%s = %s\n" , columnName[i] , values[i] ? values[i] : "NULL" );
    printf( "\n" );
    return SQLITE_OK;
}

int main( int argc , char* argv[] )
{
    char *error_report = NULL;
    sqlite3 *db = NULL;

    if( argc < 2 )
    {
        fprintf( stderr , "Usage> %s sqlite_db_file\n" , argv[0] );
        exit(1);
    }

    printf( "1> Open a database\n" );
    if( sqlite3_open( argv[1] , &db ) != SQLITE_OK )
    {
        fprintf( stderr , "\t> Cannot open databases: %s\n" , sqlite3_errmsg(db) );
        sqlite3_close( db );
        exit(1);
    }
    else
        printf( "\t> Finish\n" );

    printf( "2> Create a table\n" );
    if( sqlite3_exec( db , CMD_CREATE_TABLE , Callback_ShowList , NULL , &error_report ) != SQLITE_OK )
    {
        fprintf( stderr , "\t> CMD: %s , Error: %s\n" , CMD_CREATE_TABLE , error_report );
        sqlite3_close(db);
        exit(1);
    }
    else
        printf( "\t> Finish\n" );

    printf( "3> Insert a data\n" );
    if( sqlite3_exec( db , CMD_INSERT_ITEM , Callback_ShowList , NULL , &error_report ) != SQLITE_OK )
    {
        fprintf( stderr , "\t> CMD: %s , Error: %s\n" , CMD_INSERT_ITEM , error_report );
        sqlite3_close(db);
        exit(1);
    }
    else
        printf( "\t> Finish\n" );

    printf( "4> Query\n" );
    if( sqlite3_exec( db , CMD_QUERY , Callback_ShowList , NULL , &error_report ) != SQLITE_OK )
    {
        fprintf( stderr , "\t> CMD: %s , Error: %s\n" , CMD_QUERY , error_report );
        sqlite3_close(db);
        exit(1);
    }
    else
        printf( "\t> Finish\n" );


    if( argc > 2 )
    {
        printf( "N> Do argv[2] ...\n" );
        if( sqlite3_exec( db , argv[2] , Callback_ShowList , NULL , &error_report ) != SQLITE_OK )
        {
            fprintf( stderr , "\t> CMD: %s, Error: %s\n" , argv[2] , error_report );
            sqlite3_close(db);
            exit(1);
        }
        else
            printf( "\t> Finish\n" );
    }
    return 0;
}


流程:


依序是建立資料庫、資料表、新增資料、查詢和最後如果有另外傳參數的話,幫你執行一下!


編譯:


此例在 Mac OS 10.6.2
$ gcc -lsqlite3 main.c


成果:


$ ./a.out 1 "select * from sqlite_master;"
1> Open a database
        > Finish
2> Create a table
        > Finish
3> Insert a data
        > Finish
4> Query
                column1 = 1
                column2 = Hello World

        > Finish
N> Do argv[2] ...
                type = table
                name = MyTable
                tbl_name = MyTable
                rootpage = 2
                sql = CREATE TABLE MyTable( column1 int, column2 char(20) )

        > Finish


2010年4月24日 星期六

小小工作室

+ 防塵


中午買了 KVM Switch 後,就嘗試把環境架一下,當初買 UPS 是打算永不關機的,只是因為目前網路環境是 Private IP ,申請個 ADSL 一個月又要多八百,所以就暫時把 UPS 拿來當穩壓了,的確在目前的環境中,有印象有聽到 2~3 次 UPS 自行切換到自己供電的,不過,室內燈並沒有閃爍不穩,所以,我猜主機 Power 不接 UPS 穩壓也都沒啥問題啦


當初就有打算擺兩台機器的,所以我有準備"跳線"(紅色那條),再加上去年底舊主機壞掉,所以有多一張網卡,目前底下那台機器就是一張內建網卡 + 一張舊 PCI 網卡,但不曉得是不是太久沒過電,一開始裝上去還沒反應,剛剛重插了一下,終於活過來了。所以緊接著就把環境設定一下啦,在 Windows 7 的環境中,只需要對連外網卡去設定"共用網路",一下就設定好囉


此例是共有兩張網卡,對外連線稱作"區域連線",用來對內網路稱作"區域連線2"


[控制台]->[網路和網際網路]->[網路和共用中心]->點選目前的連外網卡(如:區域連線)->[內容]->[共用]->[網際網路連線共用] 勾選"允許其他網路使用者透過這台電腦的網際網路連線來來線",並對"家用網路連線"選擇你的內部網卡(如:區域連線2)


如此一來"區域連線2"就會被設定成一個 static ip 並提供 DHCP 功能,接著我就是利用跳線將兩台電腦接起來,對另一台電腦而言,那就只需用 DHCP 就能夠上網囉!印象中在 Windows XP 時可以透過家用網路設定,那個設定精靈會一步步幫你弄好。


防塵這部分參考碩班大神 Adios 在 Lab 處理機櫃的心得,好像是專門去買門簾來用的。幾個月前,已在光南買了兩支棒子,就是那種加上門上用來加門簾的工具,一支好像 20 元不到吧?接著在搭一條小時就存在的浴巾,可能是買奶粉送的?稍微再把一端用針線縫起來,可以較穩固套住,就這樣變成防塵設備啦!


不敢想像,大一別人在用 P4-1800 時,我還在用 P-200 玩 Win98 寫 VC 程式,說真的我覺得電腦好壞跟程式功力沒甚麼差,大二轉系時才託好友 Kudo 帶我組了人生中的第一台電腦 AMD 1800+ ,我記得那時還在他住了一晚並且看了看南投的建築和好喝的綠豆沙!之後開始慢慢有跟上潮流,不過幾乎都是選當時最低階來用,碩一不久後,那台 AMD 1800+ 在宿舍常常跳電的情境下永長眠,那時起我開始用大學買的二手品 AMD 800+ 的主機,因為家裡也需要電腦,所以我搬了一台最不穩的來用,畢竟出問題我還會調教嘛,直到一台台主機先後長眠後,幾個月前,我用的電腦才正式進入多核心的時代,從 1 跳到 4 ,哈。


不小心回憶起老故事了,從幾乎都用最舊的電腦,變成現在這個樣子,自己多看起眼都覺得非常奢侈啊~一定更要努力賺錢囉!應該不會有其他理由在敗電腦產品了!!


2-Port USB Cable KVM Switch - ATEN CS22U

2-Port USB KVM Switch, ATEN CS22U


看來這一個月特別敗家!雖然這個也是規劃好要買的,但買下去還是兩個字 - 敗家!這台是 ATEN CS22U , 全名是 2-Port USB Cable KVM Switch ,以前大學在玩 SA 時,用兩台電腦都是很合情合理的(誤),那時我的主機分別是 AMD XP 1800+ 跟 P-200 ,我幾乎都用滿舊的電腦,哈。那時有買過 KVM Switch ( D-sub & PS/2 ) ,買二手的,也是可以接兩台電腦的,含線材才賣 250 元,後來在 NCTU Talk 板上又看到別人不要,所以我又撿到了一台,當時曾去順發看過,好像光那個盒子就要兩百多了,且最貴的其實是接線的線材。


這次花了 729 買這東西,我在 PCHome 線上購物看到有賣 799 ,隨意 Google 也有賣 699 ,中午時去 NOVA 附近看看,先看了順發,沒賣這款,燦坤則賣 8xx ,回到 NOVA 時,才想起這邊其實很少賣這種東西,最後問問店家,他叫我去二樓看看,有間跟順發燦坤一樣的賣法,就看到唯一一盒,價錢比我看到的貴了一點而已,所以就買了(會員賣699 ),或許還有更便宜的,但因為只剩一盒,就只好收手囉。


USB KVM Switch + USB -> PS/2


雖然也有看到相關的討論串 - [其他週邊設施] 請問有人用過ATEN(宏正)的KVM嗎? ,但最後還是嘗試買這款,目前使用正常!之前為了新主機買過一條 USB -> PS/2 ,剛好搭起來一起使用,實在是我的電腦設備都挺舊的。需注意的,因為它是前面有兩個 USB 孔,後面接主機的也有兩條線延長出去,但一開始我在接主機時,只接了一條,想說可以省電,但接錯條線導致沒反應,還以為自己踩到地雷品了,所幸只是暫時耍笨囉!


ATEN CS22U ATEN CS22U


2010年4月23日 星期五

iOS 開發教學 - 使用 JSON 處理 Web Service 資料,以 Flickr API 為例

flickr api on iphone

這筆記來自於 CS 193P iPhone Application Development 2010 Winter 課程 - 9. Data in Your iPhone App (February 2, 2010) ,另外,關於申請使用 Flickr API 的部份,可以參考 [PHP] 申請使用 Flickr API 教學筆記 - 以 flickr.photos.search 為例 ,另外,此例還會用到 UITableView ,可參考 iPhone 開發教學 - 使用 UITableView & UITableViewController 提供表單服務

越來越多網路服務提供 API 讓其他平台可以更簡單地連結到,並且常常使用 JSON 的資料格式來提供多元且方便的 UI 應用,在此,挑選 Flickr API 當作範例,透過對 Flickr 搜尋特定 Tag 後,把找到的圖片資訊利用 UITableView 顯示出來!

此篇目的:


  • 使用 JSON 的方式

  • 使用 NSDictionary 的 Key-Value Pair 來製作 REQUEST URL(如 PHP - http_build_query

流程:


  1. 下載 JSON (json-framework)

    • This framework implements a strict JSON parser and generator in Objective-C.

  2. [Xcode]->[Create a new Xcode project]->[iPhone OS]->[Application]->[Window-based Application]-> 此例以 MyJSONTest 為例

    • [Xcode]->[File]->[Cocoa Touch Class]->[UIViewController subclass] (勾選 UITableViewController subclass) -> 此例以 MyTableList 為例

  3. 將下載回來的 JSON 打開,並把其中一個目錄 JSON 移到 Project 適當的位置

    • 可以一開始先用 #import "JSON.h" 並編譯看看,若沒問題代表可以找到
      add json framework

程式碼:

MyJSONTestAppDelegate.h

#import <UIKit/UIKit.h>
#import "MyTableList.h"

@interface MyJSONTestAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    MyTableList *myList;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@end

MyJSONTestAppDelegate.m

#import "MyJSONTestAppDelegate.h"

@implementation MyJSONTestAppDelegate

@synthesize window;

- (void)applicationDidFinishLaunching:(UIApplication *)application {  
    myList = [[MyTableList alloc] init];
  
    [window addSubview:myList.view];
    // Override point for customization after application launch
    [window makeKeyAndVisible];
}

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

@end

MyTableList.h

#import <UIKit/UIKit.h>
#define FLICKR_API_KEY @"YOUR_API_KEY"

@interface MyTableList : UITableViewController {
    NSMutableArray *dataSource;
}

@end

MyTableList.m

#import "MyTableList.h"
#import "JSON.h"

@implementation MyTableList

- (NSString*)http_build_query:(NSString *)targetUrl options:(NSMutableDictionary *) args {
    NSMutableString * url = [[NSMutableString alloc] initWithString:targetUrl];
    NSStringEncoding encoding = NSUTF8StringEncoding;
    BOOL first_in = YES;
    for ( id key in [args allKeys] ) {
        if (first_in) {
            first_in = NO;
            [url appendFormat:@"%@=%@",
                [[NSString stringWithFormat:@"%@", key] stringByAddingPercentEscapesUsingEncoding:encoding],
                [[NSString stringWithFormat:@"%@",[args objectForKey:key]] stringByAddingPercentEscapesUsingEncoding:encoding]
            ];
        } else {
            [url appendFormat:@"&%@=%@",
                [[NSString stringWithFormat:@"%@", key] stringByAddingPercentEscapesUsingEncoding:encoding],
                [[NSString stringWithFormat:@"%@",[args objectForKey:key]] stringByAddingPercentEscapesUsingEncoding:encoding]
            ];
        }
    }
    return [url autorelease];
}


- (void)viewDidLoad {
    [super viewDidLoad];

    dataSource = [[NSMutableArray alloc] init];

    NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
    [data setValue:@"flickr.photos.search" forKey:@"method"];
    [data setValue:@"json" forKey:@"format"];
    [data setValue:@"1" forKey:@"nojsoncallback"];
    [data setValue:FLICKR_API_KEY forKey:@"api_key"];
    [data setValue:@"10" forKey:@"per_page"];
    [data setValue:@"台灣 正妹" forKey:@"tags"];
  
    NSURL *target = [[NSURL alloc] initWithString:[self http_build_query:@"http://api.flickr.com/services/rest/?" options:data]];
    //NSLog( [NSString stringWithFormat:@"%@" , target] );
  
    NSString *jsonResult = [NSString stringWithContentsOfURL:target encoding:NSUTF8StringEncoding error:nil];
    //NSLog( jsonResult  );
  
    //SBJSON *parser = [[SBJSON alloc] init];
    //NSDictionary *formatedData = [parser objectWithString:result error:nil];
  
    NSDictionary *result = [jsonResult JSONValue];
    //NSLog( [NSString stringWithFormat:@"JSONParsing:%d", [result count] ]);
    //NSLog( [NSString stringWithFormat:@"Photos:%d", [[result objectForKey:@"photos"] count] ]);
    //NSLog( [NSString stringWithFormat:@"PhotosArray:%d", [[[result objectForKey:@"photos"] valueForKeyPath:@"photo"] count]]);
  
    // http://www.flickr.com/services/api/misc.urls.html
    id obj;
    for( NSDictionary *photo in [[result objectForKey:@"photos"] valueForKeyPath:@"photo"] ) {
        //NSLog( [NSString stringWithFormat:@"Title:%@", [photo objectForKey:@"title"] ] );
        //NSLog( [NSString stringWithFormat:@"URL:%@", [NSString stringWithFormat:@"http://farm%@.static.flickr.com/%@/%@_%@_s.jpg", [photo objectForKey:@"farm"] , [photo objectForKey:@"server"] , [photo objectForKey:@"id"] , [photo objectForKey:@"secret"]] ] );
      
        [dataSource addObject:
            [[NSDictionary alloc] initWithObjectsAndKeys:
                ( ( (obj = [photo objectForKey:@"title"]) && [obj length] > 0 ) ? obj : @"Untitled" ) ,
                @"title" ,
                [NSString stringWithFormat:@"http://farm%@.static.flickr.com/%@/%@_%@_s.jpg", [photo objectForKey:@"farm"] , [photo objectForKey:@"server"] , [photo objectForKey:@"id"] , [photo objectForKey:@"secret"]],
                @"url" ,
                nil
            ]
        ];
    }
  
    [target release];
    [data release];

  
    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;
}

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
  
    // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}


#pragma mark Table view methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}


// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    //NSLog( [NSString stringWithFormat:@"Cnt:%d" , [dataSource count]] );
    return [dataSource count];

}


// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  
    static NSString *CellIdentifier = @"Cell";
  
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }

    // Set up the cell...
    cell.textLabel.text = [[dataSource objectAtIndex:indexPath.row] objectForKey:@"title"];
    //NSLog( [NSString stringWithFormat:@"%@", [[dataSource objectAtIndex:indexPath.row] objectForKey:@"title"]] );

    cell.imageView.image = [UIImage imageWithData:
                                [NSData dataWithContentsOfURL:
                                    [NSURL URLWithString:
                                        [[dataSource objectAtIndex:indexPath.row] objectForKey:@"url"]
                                    ]
                                ]
                            ];


    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // Navigation logic may go here. Create and push another view controller.
    // AnotherViewController *anotherViewController = [[AnotherViewController alloc] initWithNibName:@"AnotherView" bundle:nil];
    // [self.navigationController pushViewController:anotherViewController];
    // [anotherViewController release];
}

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

@end

在此實作一個 http_build_query 來使用,目前流程上可以取得 Flickr API 回傳的訊息,並且在組出照片出來,但是,我在模擬器上跑的結果還滿慢的,此例利用 "台灣正妹" 找 10 張照片來呈現,若只印照片名稱不慢,但加上讀出縮圖部分,花 5 秒鐘才跑出來,或許還有一些技術可以嘗試看看。

2010年4月22日 星期四

[PHP] 申請使用 Flickr API 教學筆記 - 以 flickr.photos.search 為例

The App Garden on Flickr - http://www.flickr.com/services/


想要使用 Flickr API 來玩一些有趣的東西,那必須先到官網申請一組 KEY 來使用。



  1. 連到 The App Garden on Flickr - http://www.flickr.com/services/

  2. 點選 [Create an App] -> [Get your API Key] -> [Request an API Key] ,在此挑選 [Non-Commercial] ,接著輸入你想要的應用程式的名字跟簡介。

  3. 最後,就會得到一組序號,分別是 Key 和 Secret


接著寫簡單的 PHP 程式作為 Demo 方式:



程式碼(在此使用 REST Request
Format
與 JSON 格式):


<?php

$target_url = 'http://api.flickr.com/services/rest/?';
$data = array(
        'method' => 'flickr.photos.search' ,
        'format' => 'json' ,
        'nojsoncallback' => 1 ,
        'api_key' => 'YOUR_API_KEY' ,
        'per_page' => 3 ,
        'tags' => 'hello world'
);      

$target_url .= http_build_query( $data );

$ch = curl_init();
curl_setopt( $ch , CURLOPT_URL , $target_url );
curl_setopt( $ch , CURLOPT_RETURNTRANSFER , true );
$d = curl_exec( $ch );
curl_close( $ch );

$d = json_decode( $d );
print_r( $d );
exit;
?>


輸出:


stdClass Object
(
    [photos] => stdClass Object
        (
            [page] => 1
            [pages] => 391
            [perpage] => 3
            [total] => 1171
            [photo] => Array
                (
                    [0] => stdClass Object
                        (
                            [id] => 4539740346
                            [owner] => 21229296@N03
                            [secret] => 3b10921450
                            [server] => 4040
                            [farm] => 5
                            [title] => ~ Berry One ~
                            [ispublic] => 1
                            [isfriend] => 0
                            [isfamily] => 0
                        )

                    [1] => stdClass Object
                        (
                            [id] => 4539723084
                            [owner] => 21229296@N03
                            [secret] => d5fe05dba2
                            [server] => 2698
                            [farm] => 3
                            [title] => ||  Narcissistic Paradox - Single Flower  ||
                            [ispublic] => 1
                            [isfriend] => 0
                            [isfamily] => 0
                        )

                    [2] => stdClass Object
                        (
                            [id] => 4538993631
                            [owner] => 21229296@N03
                            [secret] => afe8fd4a2a
                            [server] => 2793
                            [farm] => 3
                            [title] => Snowzuki X90 #3
                            [ispublic] => 1
                            [isfriend] => 0
                            [isfamily] => 0
                        )

                )

        )

    [stat] => ok
)


另外,也可以直接用以下方便的 framework




以 phpFlickr 為例:


程式碼:


<?php

require_once( "phpFlickr/phpFlickr.php" );

$o = new phpFlickr( 'YOUR_API_KEY' );
$d = $o->photos_search( array(
                                'tags' => 'hello world' ,
                                'per_page' => 3
                        )   
        );  
print_r( $d );
?>


輸出:


Array
(
    [page] => 1
    [pages] => 391
    [perpage] => 3
    [total] => 1171
    [photo] => Array
        (
            [0] => Array
                (
                    [id] => 4539740346
                    [owner] => 21229296@N03
                    [secret] => 3b10921450
                    [server] => 4040
                    [farm] => 5
                    [title] => ~ Berry One ~
                    [ispublic] => 1
                    [isfriend] => 0
                    [isfamily] => 0
                )

            [1] => Array
                (
                    [id] => 4539723084
                    [owner] => 21229296@N03
                    [secret] => d5fe05dba2
                    [server] => 2698
                    [farm] => 3
                    [title] => ||  Narcissistic Paradox - Single Flower  ||
                    [ispublic] => 1
                    [isfriend] => 0
                    [isfamily] => 0
                )

            [2] => Array
                (
                    [id] => 4538993631
                    [owner] => 21229296@N03
                    [secret] => afe8fd4a2a
                    [server] => 2793
                    [farm] => 3
                    [title] => Snowzuki X90 #3
                    [ispublic] => 1
                    [isfriend] => 0
                    [isfamily] => 0
                )

        )

)


最後,則是要組出圖片的 static url ,請參考相片來源 http://www.flickr.com/services/api/misc.urls.html



  • http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}.jpg

  • http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}_[mstb].jpg

  • http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{o-secret}_o.(jpg|gif|png)


2010年4月19日 星期一

不知覺地過了半年

公園宿舍人工池


沒想到工作已超過半年了!日子過得真快。


最近的工作內容中,有一個案子跟文化有點相關,讓我覺得能夠把自身的技術或學習的事物,跟當地文化結合應用,真的很讚。記得大學時,總是在全校臨時停電時,跟室友就在寢室床上聊天,一開始是一聲聲的髒話,後來是不間斷的拖鞋聲,大家齊步去清夜買東西吧!偶後在久一點點,開始聽到吉他聲。我喜歡,也佩服那些不需要電腦的情境下,還可以豐富生活的方式。


最近工作有感 OOP 的特性,封裝、繼承、多型和資訊隱藏,說穿了根本就是人生的體悟啊!以 Java 來說,當你進入一個單位時,若有完善的訓練時,那就可以"繼承"到單位的基本能力,倘若不行,那只好用"實作"來實現,接著,漸漸地與人互動多了,開始要把一些技能做成多型,見招拆招啊!畢竟本質上是一樣的東西,至於"封裝"呢?算是瞭解單位生態後,那就要用合適的方式跟大家互動,才不至於溝通不良,最後最為重要是資訊隱藏,有些訊息,不再像小娃般地一五一十地回報出來,因為太多的資訊也會使得組織裡的人方向不明,而面對客戶時,只需適時提供恰當的支援,而不是一股腦兒地全部都把訊息丟給對方,以免資訊過多反而變成雜訊了,這我稱作資訊隱藏。


也難怪, OOP 是人設計出來的,其實便會帶有人性了嘛


秒速5センチメートル One More Time, One More Chance








因緣際會,讓我看到這個 秒速5センチメートル ,雖然圖片中不是夏天的情景,卻讓我感到很有年輕的活力氣息?後來去找找這部動畫的資訊 - 新海誠,也找到了這首歌 "One More Time, One More Chance",細細體會歌詞中的意境,恰恰好跟現在的心境可以結合,但不是愛情類的啦,只是最近因為工作內容不是自己習慣的,而有一點點倦怠感。在淡淡的心境中,不停地繞著耳邊轉著,像似告訴自己有多少東西該珍惜、接觸以及學習,還有,活著真好!哈,完全跟歌詞沒啥關係,大概又是另一種人生體悟吧。


歌詞:


これ以上何を失えば 心は許されるの
究竟還要再失去什麼 我的心才會得到寬恕
どれ程の痛みならば もういちど君に會える
到底要痛到什麼程度 才能夠再次見到妳
One more time 季節よ うつろわないで
One more time 季節啊 希望你別轉變
One more time ふざけあった 時間よ
One more time 與妳嬉鬧的時光啊

くいちがう時はいつも 僕が先に折れたね
發生爭執的時候 每次都是我先讓步
わがままな性格が なおさら愛しくさせた
這種任性的個性 卻更加地讓人憐愛
One more chance 記憶に足を取られて
One more chance 被記憶絆住
One more chance 次の場所を選べない
One more chance 無法選擇下一個地方

いつでも搜しているよ どっかに君の姿を
無論何時都在尋找 希望能在某處找到妳
向いのホーム 路地裡の窓
對面的月台 小巷的窗戶裡
こんなとこにいるはずもないのに
明明知道妳不可能會在這裡
願いがもしも葉うなら 今すぐ君のもとへ
如果願望能夠實現 我希望馬上到妳身邊
できないことは もうなにもない
如今沒有我辦不到的事
すべてかけて抱きしめてみせるよ
我會賭上一切緊緊擁抱妳

寂しさ紛らすだけなら
如果只是為了排遣寂寞
誰でもいいはずなのに
應該不管是誰都無所謂
星が落ちそうな夜だから
但是在星辰要落下的夜晚
自分をいつわれない
我無法對自己說謊
One more time 季節よ うつろわないで
One more time 季節啊 希望你別轉變
One more time ふざけあった時間よ
One more time 與妳嬉鬧的時光啊

いつでも搜しているよ どっかに君の姿を
無論何時都在尋找 希望能在某處找到妳
交差點でも 夢の中でも
就算在路口 在算在夢中
こんなとこにいるはずもないのに
明知道妳不可能會在這裡
奇蹟がもしも起こるなら 今すぐ君に見せたい
如果奇蹟會發生的話 希望馬上能讓妳看到
新しい朝 これからの僕
全新的早晨 從今以後的我
言えなかった「好き」という言葉も
還有過去說不出口的「喜歡妳」

夏の想い出がまわる ふいに消えた鼓動
夏日的回憶在腦中盤旋 突然消失的悸動

いつでも搜しているよ どっかに君の姿を
無論何時都在尋找 希望能在某處找到妳
明け方の街 桜木町で
在黎明的街頭 櫻木町
こんなとこに來るはずもないのに
明明知道妳不可能會來這裡
願いがもしも葉うなら 今すぐ君のもとへ
如果願望能夠實現 我希望馬上到妳身邊
できないことはもう何もない
如今沒有我辦不到的事
すべてかけて抱きしめてみせるよ
我會賭上一切緊緊擁抱妳

いつでも搜しているよ
無論何時都在尋找
どっかに君の破片を
希望在某處找到妳的線索
旅先の店 新聞の隅
旅途上的小店 新聞的角落
こんなとこにあるはずもないのに
明明知道妳根本不可能會出現
奇蹟がもしも起こるなら 今すぐ君に見せたい
如果奇蹟會發生的話 希望馬上能讓妳看到
新しい朝 これからの僕
全新的早晨 從今以後的我
言えなかった「好き」という言葉も
還有過去說不出口的「喜歡妳」

いつでも搜してしまう どっかに君の笑顔を
無論何時都在尋找 希望在某處找到妳的笑容
急行待ちの 踏切あたり
在等待快車通過的 平交道
こんなとこにいるはずもないのに
明知道妳不可能會在這裡
命が繰り返すならば 何度も君のもとへ
如果生命能夠重來 無論幾次我都要到妳身邊
欲しいものなど もう何もない
現在我已經 別無所求
君のほかに大切なものなど
除了妳以外我什麼都不想要


2010年4月18日 星期日

Mac mini

Mac Mini


週日跟大學同學到飛翔的魚聚餐,聚餐前就是去敗了這個東西。扣除筆電外,這個算是第一次為了正版軟體使用而購買一台相關性的主機。雖然 Mac OS 並沒有很嚴格的安裝規定,但其中有一條就是要搭配使用他們出的機器,這陣子也請教過幾位高手後,決定花下這筆錢了,畢竟已經出了社會,有些資源必須砸錢去使用了。目前唯一對 Mac OS 感到自在的,大概是系統核心是 BSD 改來的吧!


大概已經有快一個月都用 Mac OS 當作工作上的桌機使用,偶而東西弄煩時,就看看 Stanford CS 193P iPhone Application Development 當作算半複習的方式吧,也是另一種為未來的工作提前準備。至於真的下定決心買 Mac mini ,主因約四分之一的是想用來開發 iPhone 程式,但賣不賣錢是另一回事,剩下的就學學 Mac OS 和實現自己想做的事。


只是這次購買主機的壓力重上許多,些前買電腦時,幾乎都是"已賺到這台主機的設備費"才下手,這次就變得比較冒險,或者可以歸類在"買玩具"的情境下了!這次總共的花費是教育版最低階的 Mac Mini + display port 轉 D-Sub (上圖中最右上角的那個,並非內附的 miniDVI 轉 DVI)。


呼,這半年花了許多錢,等學完開車要更加利用時間!未來打算把一些 PHP Code 先轉成 C ,然後接著用 iPhone 模擬器當作測試,試試 Objective C 來處理 UI 介面結合一些應用囉!


2010年4月15日 星期四

[PHP] php-5.2.13/ext/gd/libgd/gd_png.c:142: undefined reference to `png_check_sig'

最近常用的工作站的 PHP 更新到 PHP 5.3.2 了,沒想到我自己編的 PHP 5.2.x 就意外中槍落馬,一堆環境變了,而透過 Tarball 重新編譯 PHP 5.2.13 來用時,碰到以下的訊息:


php-5.2.13/ext/gd/libgd/gd_png.c:142:
undefined
reference
to `png_check_sig'


隨意 Google ,原來很多人都碰到了,解法就是修改 php-5.2.13/ext/gd/libgd/gd_png.c 程式碼


142         //if (!png_check_sig (sig, 8)) { /* bad signature */
143         if (!png_sig_cmp (sig, 0, 8)) { /* bad signature */
144                 return NULL;145         }


即可完成處理。真難得還要改 code 啊


順便記錄一下自己編 PHP 5.2.13 所下的指令


./configure --with-apxs2=/path/tmp_for_httpd/bin/apxs --prefix=/path/tmp_for_php --without-pear --with-openssl --enable-mbstring --with-curl --with-imap --with-mcrypt --with-imap-ssl --with-gd --with-jpeg-dir=/path/tmp_for/lib


2010年4月11日 星期日

[遊戲] iPhone 戰略類免費遊戲 - TowerMadness™ Zero

iPhone TM Zero iPhone TM Zero


這是一款免費的攻防遊戲,也就是建立一些堡塔攻擊經過的外星人,以防他們到達目的地並搶走你的羊。此款遊戲也有付費的機制,像是某個建築物要付費才能取得,或是某些地圖要付費才能玩。整體上免費的已經夠玩囉!目前一些地圖的 Hard 模式我都打不贏就是了。









偶爾拿來殺時間也不錯,唯一的缺點大概是讓你的 iPhone 電力不停的下降吧。


2010年4月10日 星期六

領廣告費囉!閒聊 BloggerAds 收益

bloggerads


記得開始用時,是在 2008 年 12 月初吧!我記得寫過幾篇文章記錄:



那時開始,試了 BloggerAds 跟 Google Adsense ,前者很不錯,只要有廣告曝光也有錢,而後者偏向要有人點擊才有比較"看"得出來有廣告費。


googleadsense


看看上面兩張圖,差不多同樣時間申請的廣告,BloggerAds 已經超過 Google Adsense 給的 1.5 倍了(Google Adsense 用的是美金),其中我用 Google Analytics 看過幾眼,我的 blog 的確佔了七、八成是透過 Search Engines 連過來的。(忘了一提,我的 Google Adsense 有分 20% 給 PIXNET)


這也可能跟我使用 Blog 的習慣有關,我一開就把 Blog 定位在"從 Search Engines 連過來的",而不是像一般寫作專家,專門寫給人看得,讓人容易常常注意。這樣也不錯,因為我的 Blog 本身就定位在自己的筆記的,只是稍微懂一點點 Search Engines 的 Ranking 方式,所以有時標題會有點誇大 XD 但我想至少有幫助到別人吧!說到這,令人反感的,則是有人一字不改的再複製我的文章,除了連"來源"都不會標明外,並別說標題了!有時搜尋引擎的結果排序,反而我文章排名還輸對方一大截!因為對方也依照這種方式去抄別人的大站,讓自己的權重變得很高!甚至有人再爬你的文章,你一發表沒多久,他就跟著發表,若仔細計較,一定會讓人火大啦!極有可能是因為我開放全文 RSS 服務,考慮關一下測試看看,畢竟不開發全文對閱讀者也是另一種好處,可以先看看短文,等有興趣再看全文吧!


不過,暫時把心情擺在一旁,現在,我只是為我自己記錄一些東西囉!


guestlog


回過頭聊聊 BloggerAds ,經我的觀察,以我自己的 Blog 一天約 300~700 的瀏覽人潮來說(純粹只以 Pixnet 記錄的),而 300 左右是在週末,超過 300 則是一到五的工作天,這樣下來,一個月可累積約 100 ~ 120 元的廣告費,在此就粗算 550 的人潮可以讓你一個月有 100 元的微薄收入吧!


以我的經驗來說,從一開始一個月 10 元、40 元,然後去年夏天一口氣衝到 90 元時,那時我才想到,廣告的收入可能跟兩者有關:



  • 廣告商

  • 人潮


那時我是猜廣告商在暑期投入的比較多,因為 BloggerAds 也算是會安排廣告給各個 Blog 的,右邊框框我設定是可以顯示三則廣告,但他可是依照手頭上的廣告來分配的,並不是每次都三則,且不是隨時都擺放商業性的廣告,也有公益性的,因此,只有商業性的才會分到錢,算是另類的做公益吧。而人潮的確會影響,但去年暑假時,收入變多時,我覺得只是單純的"廣告商"效應,因為我那時的瀏覽人數並沒有多少,可能 300 上下,在加上我寫的不是吸引別人的文章,因此猜測,廣告商還是影響最多的因素。


隨意閒聊,記得當時有學長也開始玩,他就在那邊想到底要多少人潮才可以當作"月薪",我這邊是一天 600 人潮,約 4.5 元廣告費,那月薪假設 4 萬好了


40,000 ÷ 30 天 ≒ 1,334 元/天
1,334 元/天 × ( 600 人 ÷  4.5 元 ) ≒ 177,866 人/天


大概一天要有快 20 萬人潮來你家!但轉個彎看看其他熱門的部落格,有的一天就有 2,000 人潮,甚至 10,000 以上,如 酪梨壽司的日記 :: 痞客邦 PIXNET :: ,以現在早上快十點,人潮就已超過一萬囉!除此之外,還有一些常常在 PTT 的熱門看板打滾的,如雨狗大神 RainDog :: 痞客邦 PIXNET :: ,一天也可以輕輕鬆鬆破萬的,當然還有當紅的 Facebook、Twitter 和 Plurk 等等囉!我實在很佩服他們花心力去經營,但細節部分就當作商業機密吧!哈,在此不多談以免破壞別人及市場行情。


只不過反推回自己的狀態,我的 blog 還是只是給自己看得為主,筆記用途,頂多偶爾也給 Search Engines 吃吃啦!所以,等我哪天發閒再來試試其他的推廣方式吧!最後,忘了一提,BloggerAds 要滿一千才能提款,而 Google Adsense 則是要滿 100 美金囉!這也是為何我說領錢啦!因為我的 BloggerAds 達到門檻囉!


2010年4月7日 星期三

目標咧?

王功漁港


這張年假時,在彰化王功那邊拍的,已經擺在 blog 的草稿文章區多時。


最近開始學開車,透過在 PTT 揪團駕訓班,也認識了幾位朋友,但就是要有交集才會長久,漸漸地大概只剩早上一起共乘搭車去駕訓班的夥伴會閒聊而已,其中有一位是社會組,剛好跟我念的科系不同,生活有很明確的目標,為了達成那個目標,可以不上 Facebook ,真是厲害,對學生而言,大概就是不玩線上遊戲或是不逛 BBS 吧。而她的目標就是準備多益考試,考得還是法文。不曉的是不是本身對語言沒啥興趣,不要說英文了,連台語我都不怎講,若真的要計較的話,頂多程式語言我還比較想學。


在駕訓班也過了十多天了,很巧地又遇到大學的直屬學妹,反而因此在早上會稍稍閒聊。除此之外,大概是認識教練吧!從一開始我覺得他很嚴肅,練車時不談論其他事,甚至某一天我還以為他有起床氣,再練 S 彎道時,還很大力的轉我的方向盤,然而現在呢?我反而跟他有說有笑,還真奇怪吧!


我想,那大概就像下棋一樣,有時你會是別人的棋子,聽從別人的指令,然而,有時你又會是別人的對手,要適時引起別人的戰鬥力,相處上要避開別人的缺點,更激發出他的優點,甚至適時的示弱,也是一種前進的方式。另外,滿高興可以認識不一樣領域的人,每個人有自己的背景,卻又就像一本無字天書,章節或頁數,可都要靠嘴巴問出來。沒想到高中之後,還會有這種體悟 :P 深深覺得,每個人都存在不一樣隱性的價值,不是當下的身分、地位甚至金錢可以衡量的。


這兩天的工作內容有點類似傳播或資管系所的學生,要統計整理一些東西,對我而言,也算學到不少東西!就像去逛 NOVA 時,你明明知道可以把所有傳單收來比低價,但沒有真正的身體力行,或是做了半套,一切都仍只是空談,值得慶幸的,我沒有變成傳播妹。


至於目標咧?我還沒找到,最近倒有一點彈性疲乏,過個週末應該會好多了!今天下午還騎了公司的腳踏車一下,雖然沒辦到事,但心情有輕鬆一下啦。


2010年4月1日 星期四

iOS 開發教學 - 使用 UITableView & UITableViewController 提供表單服務

除了在 CS 193P iPhone Application Development 看到的教學外,另外還有到詳細的表單教學:iPhone Programming Tutorial: Part 6: Creating custom UITableViewCell Using Interface Builder [UITableView],除此之外,在文章下頭還有操作影片,非常詳盡喔!該篇文章主打教你怎樣透過 Interface Builder 去豐富 UITableViewCell 的樣貌。

只是我看著這種教學方式,感覺有點不實在,甚至照著模仿時,迷失自己。像是忘了物件沒初始化等等的,或是沒去看為啥沒顯示出東西,這些很根本的東西容易遺忘。

以下是筆記自己的操作流程:


  • [Xcode]->[Create a new Xcode project]->[iPhone OS]->[Application]->[Window-based Application]-> 填寫 MyTable 即可

  • 點選到 Class 裡頭(非必要動作,只是讓新增的檔案都在 Class 目錄裡),[Xcode]->[File]->[New File]->[Cocoa Touch Class]->[UIViewController subclass] 並且只勾選 UITableViewController ,取名為 MyTableViewController

如此一來,在 Class 中就會有:

MyTableAppDelegate.h
MyTableAppDelegate.m
MyTableViewController.h
MyTableViewController.m

接著依序先對 MyTableAppDelegate 設定,在對 MyTableViewController 處理:

MyTableAppDelegate.h

#import <UIKit/UIKit.h>
#import "MyTableViewController.h"

@interface MyTableAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    MyTableViewController *myTableController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@end

MyTableAppDelegate.m

#import "MyTableAppDelegate.h"

@implementation MyTableAppDelegate

@synthesize window;

- (void)applicationDidFinishLaunching:(UIApplication *)application {  
    myTableController = [[MyTableViewController alloc] init]; // initWithStyle:UITableViewStylePlain
    [window addSubview:myTableController.view];
    // Override point for customization after application launch
    [window makeKeyAndVisible];
}

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

@end

MyTableViewController.h

#import <UIKit/UIKit.h>

@interface MyTableViewController : UITableViewController {
    NSArray *myData;
}

@end

MyTableViewController.m

#import "MyTableViewController.h"

@implementation MyTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    myData = [[NSArray alloc] initWithObjects:@"GMail" , @"YMail" , @"Hotmail" , @"NUMail" , nil];
  
    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;
}

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
  
    // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}


#pragma mark Table view methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}


// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [myData count];
    //return 0;
}


// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  
    static NSString *CellIdentifier = @"Cell";
  
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    // Updated @ 2012-08-07
    // Sample Code without "cell check" message:
    // *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
    //
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
  
    // Set up the cell...
    cell.textLabel.text = (NSString*)[myData objectAtIndex:indexPath.row];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // Navigation logic may go here. Create and push another view controller.
    // AnotherViewController *anotherViewController = [[AnotherViewController alloc] initWithNibName:@"AnotherView" bundle:nil];
    // [self.navigationController pushViewController:anotherViewController];
    // [anotherViewController release];
}

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

@end

執行成果:

UITableView

以上藍色字樣就是有增加或修改的地方,這是一個非常簡單的清單列表。重要的地方是在於 MVC 架構,在使用 UITableViewController 時,只需要去處理資料的來源(如 NSArray *myData),然後是回傳有多少東西(numberOfRowsInSection),以及顯示項目的處理 tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath ,只要把握住這三個關鍵點,一切就很正常啦!我一開始卡在一直沒東西出來,原因就是忘了去更新 numberOfRowsInSection 函數,它預設是 0 個東西。

熟悉最簡單的東西後,你可以更改 Table 呈現方式,即在 MyTableAppDelegate.m 初始化 myTableController 時,改成:

myTableController = [[MyTableViewController alloc] initWithStyle:UITableViewStyleGrouped]; // 預設為 UITableViewStylePlain

執行的成果:

UITableView Grouped

剩下的,可以去玩玩 Section 部分,讓顯示的清單有分群效果,或是讓 item 有事件,如點下後切換場景等等的。

UITableView Section
(此例只簡單將numberOfSectionsInTableView回傳數字改為2)

@2010/04/13 更新,以下是多個 Section 範例的部份程式碼:

MyTableViewController.h

@interface MyTableViewController : UITableViewController {
     NSMutableArray *dataSource;
}

MyTableViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    dataSource = [[NSMutableArray alloc] init];
  
    NSMutableArray* b1 = [[NSMutableArray alloc] init];
    [b1 addObject:@"1"];
    [b1 addObject:@"2"];
    [b1 addObject:@"3"];
  
    [dataSource addObject:b1];
   
[b1 release];

    NSMutableArray* b2 = [[NSMutableArray alloc] init];
    [b2 addObject:@"A"];
    [b2 addObject:@"B"];
    [b2 addObject:@"C"];
   
    [dataSource addObject:b2];

    [b2 release];

   
    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    if ( dataSource == nil )
        return 1;
    return [dataSource count];

}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSInteger bucketCount = -1;
    NSObject *target_section;
    if ( dataSource == nil )
        return 0;
    if( ( bucketCount = [dataSource count] ) < 1 || bucketCount <= section || (target_section = [dataSource objectAtIndex:section ]) == nil )
        return 0;
    return [ (NSMutableArray*)target_section count ];

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  
    static NSString *CellIdentifier = @"Cell";
  
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
  
    // Set up the cell...
    cell.textLabel.text = (NSString*)[ (NSMutableArray*)[dataSource objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
  
    return cell;
}

// 此函數預設沒有,要自己打一下
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    NSInteger bucketCount = -1;
    NSObject *target;
    if ( dataSource == nil )
        return @"";
    if( ( bucketCount = [dataSource count] ) < 1 || bucketCount <= section || ( target = [dataSource objectAtIndex:section ] ) == nil || (target = [ (NSMutableArray*)target objectAtIndex:0] ) == nil )
        return @"";
    return (NSString*) target;
}

Demo

UITableViewStyleGrouped

UITableViewStyleGrouped

UITableViewStylePlain

UITableViewStylePlain

最後,如果你希望你的 Section 可以透過 index 跳過去,如下圖中右邊的文字

sectionIndexTitlesForTableView

那就再替 MyTableViewController.m 增加一個函數,並且自定各個 section 要顯示的 title 囉!

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    NSArray* sIndex = [[NSArray alloc] initWithObjects:@"0-9", @"A", @"中", @"日", @"法", @"德", nil];
    return
[sIndex autorelease];
}

概念就只是依序點到哪個位置,直接切到該 section 的起頭。

參考資料: