2011年12月19日 星期一

使用者底下的Library資料夾不見了


我有兩台Mac電腦,一台Snow leopard已經裝了xcode4
另外一台Lion的因為工作關係,還不敢貿然亂升級

眼看iOS 5.1都在beta了,想說來升級一下xcode4
結果裝完要找project build出來的ipa
路徑是設定在/User/(name)/Library(資源庫)/Developer/Xcode/Archives
但是在我的Lion上面遍尋不著阿@@

 搞了老半天原來是被隱藏起來了
 Apple Support Communities有滿完整的討論

2011年12月16日 星期五

在iPhone上面要怎麼拿到gateway的IP

要從iPhone獲得一些網路相關的連線資訊
最常看到也常用到的就是Apple自家提供的

Reachability :無論是3G,wifi,edge都有辦法很簡單的拿到結果


但是要拿到router的local ip呢?
仔細再想一下,其實就是要拿gateway ip呀!
搜遍了幾個討論串,終於在前輩的指點下總算有大進展XD

以下的程式碼只是小小修改了getgateway.c的原始碼
但足夠讓iPhone拿到gateway的IP囉~


#import <sys sysctl.h="">
#import <netinet in.h="">
#import <arpa inet.h="">
#import "route.h"      /*the very same from google-code*/

#define CTL_NET         4               /* network, see socket.h */

#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))


- (NSString *)getGatewayIPAddress {

 NSString *address = @"error";

    /* net.route.0.inet.flags.gateway */
    int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET,
        NET_RT_FLAGS, RTF_GATEWAY};
    size_t l;
    char * buf, * p;
    struct rt_msghdr * rt;
    struct sockaddr * sa;
    struct sockaddr * sa_tab[RTAX_MAX];
    int i;
    int r = -1;
 
    if(sysctl(mib, sizeof(mib)/sizeof(int), 0, &l, 0, 0) < 0) {
        address = @"192.168.0.1";
  //return -1;
    }
 
    if(l>0) {
        buf = malloc(l);
        if(sysctl(mib, sizeof(mib)/sizeof(int), buf, &l, 0, 0) < 0) {
            address = @"192.168.0.1";
   //return -1;
        }

        for(p=buf; p<buf+l; p+="rt-">rtm_msglen) {
            rt = (struct rt_msghdr *)p;
            sa = (struct sockaddr *)(rt + 1);
            for(i=0; i<rtax_max; i++)="" if(rt-="" {="">rtm_addrs & (1 << i)) {
                    sa_tab[i] = sa;
                    sa = (struct sockaddr *)((char *)sa + ROUNDUP(sa->sa_len));
                } else {
                    sa_tab[i] = NULL;
                }
            }
   
            if( ((rt->rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY))
               && sa_tab[RTAX_DST]->sa_family == AF_INET
               && sa_tab[RTAX_GATEWAY]->sa_family == AF_INET) {
    
                unsigned char octet[4]  = {0,0,0,0};
                int i;
    for (i=0; i<4; i++){
                    octet[i] = ( ((struct sockaddr_in *)(sa_tab[RTAX_GATEWAY]))->sin_addr.s_addr >> (i*8) ) & 0xFF;
                }
                if(((struct sockaddr_in *)sa_tab[RTAX_DST])->sin_addr.s_addr == 0) {
                    //if(octet[0] == 192 && octet[1] == 168){
     in_addr_t addr = ((struct sockaddr_in *)(sa_tab[RTAX_GATEWAY]))->sin_addr.s_addr;
     r = 0;
     address = [NSString stringWithFormat:@"%s", inet_ntoa(*((struct in_addr*)&addr))];
                    //}
     NSLog(@"\naddress%@",address);
     break;
                }
            }
        }
        free(buf);
    }
    return address;
}

咦~先別急著copy去用阿!
當放到實機去跑的時候會發現....媽呀這什麼鬼IP
一眼就知道是拿到了3G的gateway ip了啦!!

喔~原來當wifi跟3G同時開啟的狀態下,
兩個interface的gateway都會被抓到.
剛好又是for迴圈跑完,自然就吐回了最後一個interface

所以我們試驗了以後,發現無論3G跟wifi的開啟先後順序
並不會影響到interface擷取的順序,因此我們採取了相當大膽的措施!!!
就是搭配Reachability確認在wifi情況下,
拿到第一個interface(即wifi) 時候就break掉.

希望可以幫助有需要的人 :)

2011年11月29日 星期二

NSURLConnection的header格式


平常用NSURLConnection也就好好的
剛好遇到了spec上的需求,需要改寫HTTP header
稍微查一下,其實不難 (list.apple.com)

1. 如果想一次改比較多項

[request setAllHTTPHeaderFields: 
        [NSDictionary dictionaryWithObjectsAndKeys: 
                                     @"iPhone", @"User-Agent",
                                     @"close", @"Connection",
                                     @"gzip", @"Accept-Encoding",
                                     @"*/*", @"Accept", nil]];

2. 如果改個其中一個

[request setValue:@"aValue" forHTTPHeaderField:@"field"];
只是有兩點要比較注意的:
1. HTTP header欄位是case-insensitive, 但是要記得使用NSMutableURLRequest才能修改
2. 有關 Connection這個header是無法改寫的! 沒錯,只有keep-alive,就算改了close一樣無效

第二點除了一開始的參考連結有提到這個問題外,在cocoabuilder.com也有人提出相同問題根據討論的結果,可能Apple當初就希望手持裝置的連線是採取盡其所能的連著,除非server端主動斷線...另外一方面來講,當然是這個API的一個bug...

看文章從2005年到現在都幾年了!如果非要close的header請自行下海CFNetwork客製化吧~

2011年6月28日 星期二

Add/Remove cell from UITableViewController

這是一個可以新增刪除cell data的table



實作方法如下:

1. 在viewDidLoad地方加入editButtonItem:

1
2
// add native edit button on navigation bar
self.navigationItem.rightBarButtonItem = self.editButtonItem;

2. 在 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 應該加上:

1
2
3
4
// If we're in editing mode, we add a placeholder row for creating new items.
if (self.editing) {
   count++;
}

3. 在顯示table cell的部份,
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath 也要在每次table有變動時做更新:

1
2
3
4
5
6
7
if (indexPath.row == [myArray count]) {
    // assign the content of last cell in editing mode
    [cell.textLabel setText:@"Add new data"];
    cell.editingAccessoryType = UITableViewCellAccessoryDisclosureIndicator;
} else {
    // assign other cell data
}

4. 當點選add new data的cell,

1
2
3
4
5
if (indexPath.row == [myArray count]) {
    [myTable deselectRowAtIndexPath:indexPath animated:YES];
if (self.editing) {
// Called after selection. In editing mode, this will navigate to a new view controller.
}

5. 回頭來看看當tableview的edit mode切換時要實作的部分,先加上

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
    [super setEditing:editing animated:animated];
    [myTable setEditing:editing animated:animated];
    [self.navigationItem setHidesBackButton:editing animated:YES];

    [myTable beginUpdates];
    NSUInteger wc = [myArray count];
    NSArray *myInsertIndexPath = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:wc inSection:0]];
    
    if (editing) {
        [myTable insertRowsAtIndexPaths:myInsertIndexPath withRowAnimation:UITableViewRowAnimationTop];
    } else {
        [myTable deleteRowsAtIndexPaths:myInsertIndexPath  withRowAnimation:UITableViewRowAnimationTop];
    }
    
    [myTable endUpdates];
}

6. 接下來是設定edit模式下cell 左邊的加減符號

1
2
3
4
5
6
7
8
- (UITableViewCellEditingStyle)tableView:(UITableView *)aTableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
    // The editing style for a row is the kind of button displayed to the left of the cell when in editing mode.
    if (indexPath.row == [myArray count]) {
        return UITableViewCellEditingStyleInsert;
    } else {
        return UITableViewCellEditingStyleDelete;
    }
}

7. 有新增刪除就一定要update table的內容

1
2
3
4
5
6
- (void)tableView:(UITableView *)aTableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Remove the corresponding object and delete the appropriate table view cell.
        [myArray removeObjectAtIndex:indexPath.row];
        [myTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationTop];
    }

8. 這裡的例子是在別的頁面新增輸入要新增的cell內容,
所以需要一個完成後的實作function :

1
2
3
4
5
6
7
8
9
- (void)addCellDone {
    // update array
    NSString *v =[NSString stringWithFormat:@"123"];
    [self.weightArray addObject:v];
    // reload table
    [myTable reloadData];
    //remove modal view
    [self dismissModalViewControllerAnimated:YES];
}

OK~你得到它了!

2011年6月27日 星期一

Damn! Where is my Personal Hotspot!!!

If it is not about updating to iOS4.3,
If it is not about thousands reset methods,
If it is not about carrier's network...

Checkout if you had ever downloaded Onavo

無論我怎麼還原網路設定,重啟iPhone,連升級iOS5 beta都試過了
就是沒有還我個人熱點阿阿阿阿~

Setting -> General -> Profiles -> Remove Onavo provisioning profile
用力的砍掉Onavo的profile吧,我似乎看到他的profile有動到APN
呼呼~搞了好幾個星期...真是氣死我了!!

2011年4月28日 星期四

如何讓手勢偵測在subview裡作用? (How to make UIGestureRecognizer work in subview?)

我們都知道UIGestureRecognizer只支援iOS3.2以上的環境
但是讀過Jesse的文章之後,我覺得我可以很放心的去使用這個API

網路上也很多這類的文章,詳細的原理可以參考官方文件
我僅針對我實作過程中所遇到的問題 貼上一些個人的解法跟筆記
在一陣copy paste過後,程式編譯似乎沒什麼問題,可是就是手勢沒反應

1
[self.imageView addGestureRecognizer:tapGestureRecognize];

因為人家的事件是發生在UIView上面…不是UIimageView

2. 那不指定self.view可以換成別的subview嗎?
當然可以阿,在接收到gesture時filter掉我們不接受的view或是特定範圍,
使用下面這個event function

1
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch

3. 喂~沒反應阿! gestureRecognizer: shouldReceiveTouch: 根本就沒有觸發進來阿
喔~我想您跟我的煩惱是一樣滴,因為沒花一些時間k文件才會出現觀念上的問題
UIGestureRecogizer根本就沒有委託來處理這樣的event,當然會遲遲等不到event的發生阿
所以呢?

所以開始貼一些code吧
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//.h
@interface showDigitNumberController : UIViewController


//.m
// Create the Swipe Gesture Recognizer
UISwipeGestureRecognizer *SwipeRecognizer = nil;
 
SwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
                                                            action:@selector(mySwipeGestureRecognizer:)];
[SwipeRecognizer setDirection:(UISwipeGestureRecognizerDirectionUp)];
[self.view addGestureRecognizer:SwipeRecognizer];
// assign the delegate here
SwipeRecognizer.delegate = self;
[SwipeRecognizer release];
 
SwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
                                                            action:@selector(mySwipeGestureRecognizer:)];
[SwipeRecognizer setDirection:(UISwipeGestureRecognizerDirectionDown)];
[self.view addGestureRecognizer:SwipeRecognizer];
[SwipeRecognizer release];
 
// Create Single Tap Gesture Recognizer
UITapGestureRecognizer *tapGestureRecognize = [[UITapGestureRecognizer alloc] initWithTarget:self
      action:@selector(myTapGestureRecognizer:)];
tapGestureRecognize.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:tapGestureRecognize];
[tapGestureRecognize release];

- (void)mySwipeGestureRecognizer:(UISwipeGestureRecognizer *)Sender {
 switch(Sender.direction){
   case UISwipeGestureRecognizerDirectionUp:
  // do something 
        break;
   case UISwipeGestureRecognizerDirectionDown:
  // do something 
        break;
   case UISwipeGestureRecognizerDirectionLeft:
  // do something 
        break;
   case UISwipeGestureRecognizerDirectionRight:
  // do something 
        break;
   default:
        break;
 }
}

- (void)myTapGestureRecognizer:(UITapGestureRecognizer *)Sender{
 // do something
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
    shouldReceiveTouch:(UITouch *)touch {
}


4. 誠如先前所講的,若是要讓指定的subview接受gesture的event就是在這個function裡實作
   網路上所提到的作法也是百百種

   //有將superview給filter掉的作法
   //UIView *gview; //This is the superview containing the buttons
   //if([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]])
   //     return !([touch.view isKindOfClass:[UIButton class]] && [touch.view
   //isDescendentOfView:gview]);


   //也有使用touch.view == menu1Btn || touch.view == menu2Btn …龐大if判斷式的作法
   //http://stackoverflow.com/questions/4556944/a-tips-for-testing-existence-of-subviews


    //就連官方sample code的作法也是if判斷的作法
    //if ((touch.view == segmentedControl) && (gestureRecognizer == tapRecognizer)) {
    //    return NO;
    //}
    //return YES;

    我的作法也是把不想接受到的gesture event的物件給填進去,不過沒有預期的效果
    那到底怎麼偵測一個subview的手勢呢?
    a. 首先我在xib裡面view底下又多加了一個UIView, 並且指定我要的範圍大小
    b. 在.h及.m裡將這個UIView的getter setter設定好,並確定xib的link有拉好
    c. 在原先指定addGestureRecognizer的view換成custom的這個就好了耶

2011年3月15日 星期二

iPhone 給我多國語系檔(.lproj),其餘免談

這篇筆記的內容"並不是"探討 怎麼建立一個多國語系檔
而是記錄iPhone上各國語系縮寫,以及讀取上的一些問題
所以大家可以按上一頁啦XD

由於多國語系的關係,我們可能需要知道使用者目前的語系設定
取得方法如下:
NSUserDefaults* defs = [NSUserDefaults standardUserDefaults];
NSArray* languages = [defs objectForKey:@"AppleLanguages"];
NSString* preferredLang = [languages objectAtIndex:0];
NSLog(@"LangCode: %@",preferredLang);

ISO-639的各國語言對應到iPhone上面的語言列表可以參考


但是,當我們用實機測試的時候一定會遇到窘境
嗄~suomi,dansk,hrvatski...
原來iPhone上的語言列表早就已經替換成該語言的文字了
這對一個"外國人"來說是一件挺痛苦的事情
參考一下 stackoverflow 的這篇文章吧!
ex: Swedish = svenska
Finnish = suomi
Danish = dansk
Croatian = hrvatski

事情還沒結束
看到沒,language code有pt, pt_PT
還有中文也是有分zh_CN, zh_TW
鬼打牆的情節緊接上演,
無論怎麼切換pt 與pt_PT,模擬器跟iPhone就是沒辦法切換
花了我好幾個小時的結論如下:
1. 如果語系檔的檔名設定有誤,iPhone預設值會是English
ex: ja.lproj誤設為jp.lproj

2. 如果語系相近,會以有相關的語系檔讀取為優先
ex: pt.lproj與pt_BR.lproj 無論設定是 português (Portugal)或是português
預設就是讀取pt.lproj

3. 如遇上述兩個問題,也確定已經更正好了
記得!!要先clean project 再重新編譯

可能還有人會對zh_CN, zh_TW,zh_Hans,zh_Hant
或是該用underline(_) 還是dash(-)符號有問題
可以參考另外一篇文章:
MacUknow

2011年2月22日 星期二

iPhone 發佈時遇到的小小warning

在我distribution的時候有遇到這麼個小小warning

warning: 'The Validate Built Product build setting was not enabled when building for Distribution.'

這是個滿貼心的warning,因為有了Validate Built Product這個檢查,
會初步幫忙開發者找到問題,避免在submit時發生問題.

參考網站有詳細的圖文介紹
我這裡僅以文字描述:P
1. 在xcode裡開啟project的內容(Get Info)
2. 切換到build tag
3. 搜尋Validate Built Product
4. checkbox確定要打勾勾

呵~勾完重新執行的結果,馬上就有icon的問題...

iPhone 發佈時遇到的 icon problem

這是我在作distribution時遇到的其中一個問題
Icon specified in the Info.plist not found under the top level app wrapper:
icon.png (-19007)

網路上滿多解答的,我參考了coco2d for iPhone上的討論

1. 首先將info.plist的Icon files欄位修改成CFBundleIconFiles
2. 然後新增 (名稱大小寫都照著取,即使是iPad project也一樣)
    Icon.png
    Icon@2x.png
    Icon-72.png
    Icon-Small.png
    Icon-Small-50.png
    Icon-Small@2x.png
3. 重新將project的resource file刪除再加入,以確保project有最新的icon

最後,還可以參考一下官網的guide
像57x57 pixels is for iPhone/iPod
114x114 pixels is for high resolution iPhone/iPod
72x72 pixels is for iPad
(我想以後可能還要提供72@2x的icon吧~)

內容回應