網頁
▼
2012年12月1日 星期六
2012年11月26日 星期一
2012年11月21日 星期三
iOS分享_如何使用Xcode內建工具找到記憶體洩漏(Memory Leak)
不管你是自修或是到外面上課,書本或是講師一定會告訴你“管理記憶體”非常的重要,而記憶體管理對於有C語言底子的人可能非常的自然,但是對於其他程式語言起家的程式設計師(諸如我),倒是一件幾乎完全從零開始的事情;因為知道要管理,我們會在撰寫iOS時自以為地使用alloc分配記憶體,再在自覺得適當的時刻利用release將其釋放掉,當我們完成一個階段並且開始在模擬器或實機跑時,發現程式正常運作,那麼我們可能自我感覺良好的認為完成了一個很OK的程式。
但是其實有時候可能在某些呼叫的方法上,iOS會自己管理記憶體,並不需要你去觸碰,也有可能在不同function間傳遞時因為操作有誤,導致每次呼叫便使得retaincount不斷上升,而記憶體管理需要不斷地學習,瞭解需要管理與釋放的變數與釋放的時機。
本篇並沒有要提及如何寫出良好記憶體管理的方法,我也沒有能力寫出這樣偉大的文章,但即使我們沒有辦法一次就達到完善的記憶體管理,卻可以經過一些工具一步步向前。
截至目前為止,我所知道要尋找Memory Leak的方法有兩個,你應該針對你的程式先完成第一個測試,再進一步完成第二個,直至程式修改成你所接受的記憶體管理:
1. 使用Xcode的Analyze功能。
這個功能可以幫助你去尋找程式碼中,有建立記憶體空間卻沒有釋放的位置,我們將一個release註解來看看他的結果:
接著你可以對他點擊,以獲取更多的資訊。
這一個測試僅能以編譯的手段,去尋找那些你可能忘記釋放的地方,也就是你必須要釋放的那些變數,所以最基本的要求就是讓該測試沒有任何提醒。但即便該測試完成,也不代表該程式在執行階段不會發生Memory Leak的問題,原因是有可能程式需要上傳相片,那麼我們會使用[NSData dataWithContentsOfFile:filePath];的方式將檔案轉為NSData,再傳送出去,而在從檔案轉成NSData到上傳的過程中,會使用非常多變數與方法,其中一個該釋放但未釋放,則可能造成暫存該檔案的空間一直存在,所以當你不斷進行上傳的動作,會發現你的程式似乎越來越慢?直到Crash為止。
所以我們必須藉由第二個工具來輔助我們尋找程式運行中可能的記憶體洩漏。
2. 使用Xcode的Profile工具。
點擊後會新開一個名叫Instruments的程式,選Leaks後點擊Profile。
你的程式會開始運作,並且跑出一些好像在監測什麼的圖表?
接著你可以試著操作程式,會發現該圖表不斷的跳動,
而這個名為Allocations就是程式記憶體的分配,你可以藉由觀察下方表格的All Allocations*來得知目前的總使用量,若是你不斷地在頁面間來回反覆操作一樣的動作,而該記憶體用量只增不減,就代表在這個動作的過程中,應該有一些需要調整的地方。
當然,我們不能僅藉由這樣的方式來找尋應該要修改的地方,點選左邊的Leaks區域,會進到Leaks的監測畫面:
接著在繪圖區與表格區中間,有一個“田”Leaks,點選後選擇Call Tree:
最後將左側的Hide System Libraries與Invert Call Tree打勾(Hide System Libraries幫助你只檢視自己程式的問題,Invert Call Tree是將檢視的過程顛倒,讓你馬上可以點到出問題的最後一段程式碼),讓我們製造一些Leak的問題,可以看到該工具會將Leak列表:
雙擊列表兩下則可以得知“可能”出錯的位置:
最後,你可以嘗試的修復這些洩漏,直到程式運作過程完全沒有為止。
講起來有工具而且已經幫助你找到相關位置,理論上應該很容易解決這些問題?但實際上這些Memory Leak的問題通常會牽扯到不同函式間的呼叫,洩漏的位置往往不是問題的原因。連續一個星期都在處理Leak相關問題,這實在比平常在解決其他程式語言的問題時更加令人惱羞,更深深感覺每一個問題的解決背後真的需要很多知識的累積,近一兩年常有人問我要學什麼語言?學什麼最好?某語言好學嗎?我的答案總是讓人不以為然:
你有心學就可以學起來,實際打開電腦寫遠比一直想著要不要學有用多了,而每一種語言的特性都有可能讓你更瞭解別種語言,如果你要學的是if跟for,那什麼都可以學,我第一個學的是MATLAB。
但是其實有時候可能在某些呼叫的方法上,iOS會自己管理記憶體,並不需要你去觸碰,也有可能在不同function間傳遞時因為操作有誤,導致每次呼叫便使得retaincount不斷上升,而記憶體管理需要不斷地學習,瞭解需要管理與釋放的變數與釋放的時機。
本篇並沒有要提及如何寫出良好記憶體管理的方法,我也沒有能力寫出這樣偉大的文章,但即使我們沒有辦法一次就達到完善的記憶體管理,卻可以經過一些工具一步步向前。
截至目前為止,我所知道要尋找Memory Leak的方法有兩個,你應該針對你的程式先完成第一個測試,再進一步完成第二個,直至程式修改成你所接受的記憶體管理:
1. 使用Xcode的Analyze功能。
這個功能可以幫助你去尋找程式碼中,有建立記憶體空間卻沒有釋放的位置,我們將一個release註解來看看他的結果:
接著你可以對他點擊,以獲取更多的資訊。
這一個測試僅能以編譯的手段,去尋找那些你可能忘記釋放的地方,也就是你必須要釋放的那些變數,所以最基本的要求就是讓該測試沒有任何提醒。但即便該測試完成,也不代表該程式在執行階段不會發生Memory Leak的問題,原因是有可能程式需要上傳相片,那麼我們會使用[NSData dataWithContentsOfFile:filePath];的方式將檔案轉為NSData,再傳送出去,而在從檔案轉成NSData到上傳的過程中,會使用非常多變數與方法,其中一個該釋放但未釋放,則可能造成暫存該檔案的空間一直存在,所以當你不斷進行上傳的動作,會發現你的程式似乎越來越慢?直到Crash為止。
所以我們必須藉由第二個工具來輔助我們尋找程式運行中可能的記憶體洩漏。
2. 使用Xcode的Profile工具。
點擊後會新開一個名叫Instruments的程式,選Leaks後點擊Profile。
你的程式會開始運作,並且跑出一些好像在監測什麼的圖表?
接著你可以試著操作程式,會發現該圖表不斷的跳動,
而這個名為Allocations就是程式記憶體的分配,你可以藉由觀察下方表格的All Allocations*來得知目前的總使用量,若是你不斷地在頁面間來回反覆操作一樣的動作,而該記憶體用量只增不減,就代表在這個動作的過程中,應該有一些需要調整的地方。
當然,我們不能僅藉由這樣的方式來找尋應該要修改的地方,點選左邊的Leaks區域,會進到Leaks的監測畫面:
接著在繪圖區與表格區中間,有一個“田”Leaks,點選後選擇Call Tree:
最後將左側的Hide System Libraries與Invert Call Tree打勾(Hide System Libraries幫助你只檢視自己程式的問題,Invert Call Tree是將檢視的過程顛倒,讓你馬上可以點到出問題的最後一段程式碼),讓我們製造一些Leak的問題,可以看到該工具會將Leak列表:
雙擊列表兩下則可以得知“可能”出錯的位置:
最後,你可以嘗試的修復這些洩漏,直到程式運作過程完全沒有為止。
講起來有工具而且已經幫助你找到相關位置,理論上應該很容易解決這些問題?但實際上這些Memory Leak的問題通常會牽扯到不同函式間的呼叫,洩漏的位置往往不是問題的原因。連續一個星期都在處理Leak相關問題,這實在比平常在解決其他程式語言的問題時更加令人惱羞,更深深感覺每一個問題的解決背後真的需要很多知識的累積,近一兩年常有人問我要學什麼語言?學什麼最好?某語言好學嗎?我的答案總是讓人不以為然:
你有心學就可以學起來,實際打開電腦寫遠比一直想著要不要學有用多了,而每一種語言的特性都有可能讓你更瞭解別種語言,如果你要學的是if跟for,那什麼都可以學,我第一個學的是MATLAB。
2012年11月13日 星期二
iOS學習_如何較易找到exc_bad_access錯誤的源頭
"exc_bad_access"是一個令人惱羞的錯誤訊息,出現的原因是“使用了已經釋放記憶體的變數”,如果你很希望好操作程式的記憶體,那就很可能在觀念不清楚的狀態下一直遇到這一個錯誤。
而之所以說很令人惱羞,log並不會顯示任何資訊來供我們判斷,這時可在環境變數中增加一個參數(NSZombieEnabled),來增加判斷的資訊。
步驟:
1. 於選單Product選擇Edit Scheme。
2. 於Arguments的Environment Variables增加參數NSZombieEnabled,並設定為YES。
這時就可以看到錯誤時會多出一句相關錯誤的log。(EX:[__NSArrayM count]: message sent to deallocated instance 0x9bb5620)
然後再配合Xcode左側的Thread列表,來查找可能的位置與原因。
方法
而之所以說很令人惱羞,log並不會顯示任何資訊來供我們判斷,這時可在環境變數中增加一個參數(NSZombieEnabled),來增加判斷的資訊。
步驟:
1. 於選單Product選擇Edit Scheme。
2. 於Arguments的Environment Variables增加參數NSZombieEnabled,並設定為YES。
這時就可以看到錯誤時會多出一句相關錯誤的log。(EX:[__NSArrayM count]: message sent to deallocated instance 0x9bb5620)
然後再配合Xcode左側的Thread列表,來查找可能的位置與原因。
方法
iOS學習_返回NSMutableArray的函式要正確release的方法(ola的猜測)
本文章為筆者自行觀念的瞭解,無法保證一定正確。
-------------------------------------------
NSMutableArray是一個非常好用的型態,我們可以將他看作一個物件陣列(操作方法),慢慢的我們會將處理NSMutableArray獨立出一個一個方法,類似下面的程式碼:
而函式的內容不外乎alloc一個NSMutableArray,然後將適當的物件放進去,在return NSMutableArray:
上述程式碼為SQLite查詢的操作,這樣就可以簡單的將資料庫操作與界面做一個簡單的切割,但是觀察上面的程式碼可能會覺得有點詭異?
我們並沒有釋放items,難道每一次地使用就會不斷佔用記憶體嗎?這豈不是慢性自殺?
所以必須在適當的時機點進行釋放,所謂適當的時機就是“立刻”。
如此,我們即可確保該NSMutableArray有被進行釋放。但通常會去接這一個NSMutableArray的介面都會準備一個全域變數,來承接吐出來的結果。
解決方法:
-(NSMutableArray *)selectReportingMain_All
而函式的內容不外乎alloc一個NSMutableArray,然後將適當的物件放進去,在return NSMutableArray:
-(NSMutableArray *)selectReportingMain_All
{
NSMutableArray *items = [[NSMutableArray alloc] init];
if ([db open])
{
FMResultSet *rs = [db executeQuery:@"SELECT * From reportingMain"];
while ([rs next]) {
NSString *reportingID = [rs stringForColumn:@"reportingID"];
NSString *title = [rs stringForColumn:@"title"];
NSString *note = [rs stringForColumn:@"note"];
NSString *xValue = [rs stringForColumn:@"xValue"];
NSString *yValue = [rs stringForColumn:@"yValue"];
NSString *time = [rs stringForColumn:@"time"];
NSString *status = [rs stringForColumn:@"status"];
[items addObject:[NSDictionary dictionaryWithObjectsAndKeys:
reportingID , @"reportingID",
title , @"title",
note , @"note",
xValue , @"xValue",
yValue , @"yValue",
time , @"time",
status , @"status",
nil]];
}
}
[db close];
return items;
}
上述程式碼為SQLite查詢的操作,這樣就可以簡單的將資料庫操作與界面做一個簡單的切割,但是觀察上面的程式碼可能會覺得有點詭異?
我們並沒有釋放items,難道每一次地使用就會不斷佔用記憶體嗎?這豈不是慢性自殺?
所以必須在適當的時機點進行釋放,所謂適當的時機就是“立刻”。
return [items autorelease];
如此,我們即可確保該NSMutableArray有被進行釋放。但通常會去接這一個NSMutableArray的介面都會準備一個全域變數,來承接吐出來的結果。
dbOperating *dbO = [[dbOperating alloc] init];
itembag = [dbO selectReportingMain_All];
NSLog(@"items retain:%d",items.retainCount);
[dbO release];
上述程式碼itembag可以很順利地取得結果,但是當該介面的其他函示需要進行操作時,則會產生“exc_bad_access”的錯誤,也就是說你使用了已經釋放的變數。解決方法:
dbOperating *dbO = [[dbOperating alloc] init];
itembag = [dbO selectReportingMain_All];
[itembag retain];
NSLog(@"items retain:%d",items.retainCount);
[dbO release];
手動增加itembag的retainCount,這時如果將retainCount輸出,會看到在上述程式碼內retainCount為2,但因回傳的變數是使用[items autorelease],所以隨即retainCount會降為1,並且也可以正常的讀取使用itembag裡面的值。
Ola的Photoshop學堂Part2-將icon轉為手繪懷舊風格
閱讀本文章注意事項:
1. 筆者為僅會小畫家且完全不曾接觸過Photoshop之程式設計師。
2. 本文章使用Photoshop CS5,筆者沒使用過其他版本。
3. 若是本文章幫助你完成該效果,請留言:「Ola國小時貼在布告欄展示一天就被撕掉的海報我覺得很棒!」。
-------------------------------------------------
為了讓整體風格相似,我需要一個手繪風格的icon,來放到嘔心瀝血的介面上:
步驟:
1. 先準備一張你所需要改變風格的圖片,我所使用的是Android上一個非常好用的記帳軟體的圖示。
2. 接著將該圖片調整為黑白色系。
3. 複製一個相同的圖到另一個圖層。
4. 將原本圖層加上顏色覆蓋,選擇咖啡色並且將透明度調整至30%。
5. 對複製的圖層加上濾鏡效果:風格化→找尋邊緣
6. 再對已經找尋邊緣的圖做濾鏡效果:素描→蠟筆紋理
7. 將5、6步驟的圖層設為覆蓋。
8. 最後將圖另存出來:
當然,你可以在步驟6時不要使用素描→蠟筆紋理的濾鏡,將可以營造出各式各樣不同風格的圖示;或是一開始不要使用黑白,或以別的顏色當底,就可以變化出很多不同的感覺,但能不能拿到好的icon來做風格改變又是另一回事了。
1. 筆者為僅會小畫家且完全不曾接觸過Photoshop之程式設計師。
2. 本文章使用Photoshop CS5,筆者沒使用過其他版本。
3. 若是本文章幫助你完成該效果,請留言:「Ola國小時貼在布告欄展示一天就被撕掉的海報我覺得很棒!」。
-------------------------------------------------
為了讓整體風格相似,我需要一個手繪風格的icon,來放到嘔心瀝血的介面上:
步驟:
1. 先準備一張你所需要改變風格的圖片,我所使用的是Android上一個非常好用的記帳軟體的圖示。
2. 接著將該圖片調整為黑白色系。
3. 複製一個相同的圖到另一個圖層。
4. 將原本圖層加上顏色覆蓋,選擇咖啡色並且將透明度調整至30%。
5. 對複製的圖層加上濾鏡效果:風格化→找尋邊緣
6. 再對已經找尋邊緣的圖做濾鏡效果:素描→蠟筆紋理
7. 將5、6步驟的圖層設為覆蓋。
8. 最後將圖另存出來:
當然,你可以在步驟6時不要使用素描→蠟筆紋理的濾鏡,將可以營造出各式各樣不同風格的圖示;或是一開始不要使用黑白,或以別的顏色當底,就可以變化出很多不同的感覺,但能不能拿到好的icon來做風格改變又是另一回事了。
iOS學習_客制化Navigation Part4_Navigation的Title文字顏色
最近花超多時間在處理美工的問題,總體來說要產出好看友善的界面,需要大量的設計與圖片,而第一步莫過於瞭解什麼樣的物件可以作出什麼效果的客制化,整體來說最容易可以拿來參考的客制化界面,就是內建在iPad裡面的程式,行事曆與備忘錄這兩個已經透露出許多物件可變化的可能性。
首先就是處理幾乎每個程式都會使用的UINavigation,需要客制化的地方大致可分為四項:
1. 右上角的功能按鈕。
2. 左上角的返回鍵。
3. Navigation的底圖。
4. Navigation的Title文字。
本篇講第四點:Navigation的Title文字顏色。
最為單純,程式碼只需要一句:
到此連續四篇,應該可以完全客制化Navigation的外觀,但能不能做出適宜且好看的圖,又是另一個需要努力的地方。
首先就是處理幾乎每個程式都會使用的UINavigation,需要客制化的地方大致可分為四項:
1. 右上角的功能按鈕。
2. 左上角的返回鍵。
3. Navigation的底圖。
4. Navigation的Title文字。
本篇講第四點:Navigation的Title文字顏色。
最為單純,程式碼只需要一句:
navigator.navigationBar.titleTextAttributes = [NSDictionary dictionaryWithObject:[UIColor whiteColor] forKey:UITextAttributeTextColor];
效果:到此連續四篇,應該可以完全客制化Navigation的外觀,但能不能做出適宜且好看的圖,又是另一個需要努力的地方。
iOS學習_客制化Navigation Part3_改變Navigation的底圖
最近花超多時間在處理美工的問題,總體來說要產出好看友善的界面,需要大量的設計與圖片,而第一步莫過於瞭解什麼樣的物件可以作出什麼效果的客制化,整體來說最容易可以拿來參考的客制化界面,就是內建在iPad裡面的程式,行事曆與備忘錄這兩個已經透露出許多物件可變化的可能性。
首先就是處理幾乎每個程式都會使用的UINavigation,需要客制化的地方大致可分為四項:
1. 右上角的功能按鈕。
2. 左上角的返回鍵。
3. Navigation的底圖。
4. Navigation的Title文字。
本篇講第三點:Navigation的底圖。
雖然第三點跟第四點都相當的單純,但為了日後好尋找還是分成兩篇,做法:
雖然設置底圖非常簡單,但製作底圖就非常困難了,要如何跟下面的內容融為一體,真的是需要很多的巧思。
首先就是處理幾乎每個程式都會使用的UINavigation,需要客制化的地方大致可分為四項:
1. 右上角的功能按鈕。
2. 左上角的返回鍵。
3. Navigation的底圖。
4. Navigation的Title文字。
本篇講第三點:Navigation的底圖。
雖然第三點跟第四點都相當的單純,但為了日後好尋找還是分成兩篇,做法:
UIImage *image = [UIImage imageNamed:@"mapview_bar1"];
[navigator.navigationBar setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];
效果:雖然設置底圖非常簡單,但製作底圖就非常困難了,要如何跟下面的內容融為一體,真的是需要很多的巧思。
iOS學習_客制化Navigation Part2_改變返回按鈕
最近花超多時間在處理美工的問題,總體來說要產出好看友善的界面,需要大量的設計與圖片,而第一步莫過於瞭解什麼樣的物件可以作出什麼效果的客制化,整體來說最容易可以拿來參考的客制化界面,就是內建在iPad裡面的程式,行事曆與備忘錄這兩個已經透露出許多物件可變化的可能性。
首先就是處理幾乎每個程式都會使用的UINavigation,需要客制化的地方大致可分為四項:
1. 右上角的功能按鈕。
2. 左上角的返回鍵。
3. Navigation的底圖。
4. Navigation的Title文字。
本篇講第二點:左上角的返回鍵。
做法其實跟第一篇一樣,但是因為在self.navigationItem後面除了本篇要使用的leftBarButtonItem以外,還有一個會令人誤會的backBarButtonItem,依照字面上我們理論上可以使用backBarButtonItem屬性簡單地將自己的UIBarButtonItem指給他,完成客制化的動作。
但backBarButtonItem與leftBarButtonItem或是rightBarButtonItem不同,backBarButtonItem所設定的按鈕並不是目前所見的View,而是設定目前View push出的下一個View的返回鍵,也就是說我們重A轉移到B,希望B上面的返回鍵可以做些改變,那麼必須在A設定backBarButtonItem。
所以很快的我們在首頁進行設定,就可以改變下一個頁面的返回鍵:
非常簡單的將原本的文字改成自己想要的文字,但這不符合我們的需求,主要還是希望整個按鈕的風格都換掉,所以可以嘗試這樣:
基本上這個效果非常的蠢,完全不符合需求。
這時我們會想到那我就學上一篇客制化按鈕的方式就OK了?很不幸的不管怎麼樣設定似乎都不會改變backBarButtonItem。
所以,最終還是必須回到最原始,以客制化leftBarButtonItem,並加上相對應的返回事件來完成:
效果:
首先就是處理幾乎每個程式都會使用的UINavigation,需要客制化的地方大致可分為四項:
1. 右上角的功能按鈕。
2. 左上角的返回鍵。
3. Navigation的底圖。
4. Navigation的Title文字。
本篇講第二點:左上角的返回鍵。
做法其實跟第一篇一樣,但是因為在self.navigationItem後面除了本篇要使用的leftBarButtonItem以外,還有一個會令人誤會的backBarButtonItem,依照字面上我們理論上可以使用backBarButtonItem屬性簡單地將自己的UIBarButtonItem指給他,完成客制化的動作。
但backBarButtonItem與leftBarButtonItem或是rightBarButtonItem不同,backBarButtonItem所設定的按鈕並不是目前所見的View,而是設定目前View push出的下一個View的返回鍵,也就是說我們重A轉移到B,希望B上面的返回鍵可以做些改變,那麼必須在A設定backBarButtonItem。
所以很快的我們在首頁進行設定,就可以改變下一個頁面的返回鍵:
UIBarButtonItem *backBarButtonItem = [[UIBarButtonItem alloc]
initWithTitle:@"回去"
style:UIBarButtonItemStylePlain
target:nil
action:nil];
self.navigationItem.backBarButtonItem = backBarButtonItem;
效果:非常簡單的將原本的文字改成自己想要的文字,但這不符合我們的需求,主要還是希望整個按鈕的風格都換掉,所以可以嘗試這樣:
UIBarButtonItem *backButton = [[UIBarButtonItem alloc]
initWithImage:[UIImage imageNamed:@"btn_back_un"]
style:UIBarButtonItemStylePlain
target:nil
action:nil];
self.navigationItem.backBarButtonItem = backButton;
效果:基本上這個效果非常的蠢,完全不符合需求。
這時我們會想到那我就學上一篇客制化按鈕的方式就OK了?很不幸的不管怎麼樣設定似乎都不會改變backBarButtonItem。
所以,最終還是必須回到最原始,以客制化leftBarButtonItem,並加上相對應的返回事件來完成:
UIImage *imgItem_BACK = [UIImage imageNamed:@"btn_back_un"];
UIImage *imgItem_BACK_click = [UIImage imageNamed:@"btn_back_click"];
UIButton *btnimg_BACK = [UIButton buttonWithType:UIButtonTypeCustom];
[btnimg_BACK setBackgroundImage:imgItem_BACK forState:UIControlStateNormal];
[btnimg_BACK setBackgroundImage:imgItem_BACK_click forState:UIControlStateHighlighted];
[btnimg_BACK addTarget:self action:@selector(btn_back_click) forControlEvents:UIControlEventTouchUpInside];
btnimg_BACK.frame = CGRectMake(0.0f, 0.0f, imgItem_BACK.size.width, imgItem_BACK.size.height);
UIBarButtonItem *imageBACKButton = [[UIBarButtonItem alloc] initWithCustomView:btnimg_BACK];
self.navigationItem.leftBarButtonItem = imageBACKButton;
[imageBACKButton release];
效果:
iOS學習_客制化Navigation Part1_放置多個圖片按鈕
最近花超多時間在處理美工的問題,總體來說要產出好看友善的界面,需要大量的設計與圖片,而第一步莫過於瞭解什麼樣的物件可以作出什麼效果的客制化,整體來說最容易可以拿來參考的客制化界面,就是內建在iPad裡面的程式,行事曆與備忘錄這兩個已經透露出許多物件可變化的可能性。
首先就是處理幾乎每個程式都會使用的UINavigation,需要客制化的地方大致可分為四項:
1. 右上角的功能按鈕。
2. 左上角的返回鍵。
3. Navigation的底圖。
4. Navigation的Title文字。
本篇講第一點:右上角的功能按鈕。
之前有發過一篇文章“iOS學習_於UINavigation上增加多個按鈕”,最終的效果是會出現預設顏色的按鈕,如果放置有顏色的小圖示則會自動變成灰階,預設的其實已經不錯看,但離美化距離就有一點遠。
做法與以上篇是相同的方法,但這次我們設定按鈕的預設圖片與被按下去的圖片:
上面可以看到我們使用btn_gps_un與btn_gps_click作為效果圖,利用setBackgroundImage方法指定給按鈕,並設定他們應該出現的狀態,最後裝到一個UIBarButtonItem中。
以同樣的步驟,在準備兩個名為imageLayerButton與imageSearchButton的UIBarButtonItem,將其裝到一個NSMutableArray中,並設定給TranslucentToolbar,最後以UIBarButtonItem方法中initWithCustomView來初始化,然後指定給要放置的Navigation(EX:self.navigationItem.rightBarButtonItem)。
如此就可以放置上一個底色為透明,又帶有美編圖的功能列表。
效果:
首先就是處理幾乎每個程式都會使用的UINavigation,需要客制化的地方大致可分為四項:
1. 右上角的功能按鈕。
2. 左上角的返回鍵。
3. Navigation的底圖。
4. Navigation的Title文字。
本篇講第一點:右上角的功能按鈕。
之前有發過一篇文章“iOS學習_於UINavigation上增加多個按鈕”,最終的效果是會出現預設顏色的按鈕,如果放置有顏色的小圖示則會自動變成灰階,預設的其實已經不錯看,但離美化距離就有一點遠。
做法與以上篇是相同的方法,但這次我們設定按鈕的預設圖片與被按下去的圖片:
UIImage *imgItem_GPS = [UIImage imageNamed:@"btn_gps_un"];
UIImage *imgItem_GPS_click = [UIImage imageNamed:@"btn_gps_click"];
UIButton *btnimg_GPS = [UIButton buttonWithType:UIButtonTypeCustom];
[btnimg_GPS setBackgroundImage:imgItem_GPS forState:UIControlStateNormal];
[btnimg_GPS setBackgroundImage:imgItem_GPS_click forState:UIControlStateHighlighted];
[btnimg_GPS addTarget:self action:@selector(btn_whereIam_Click) forControlEvents:UIControlEventTouchUpInside];
btnimg_GPS.frame = CGRectMake(0.0f, 0.0f, imgItem_GPS.size.width, imgItem_GPS.size.height);
UIBarButtonItem *imageGPSButton = [[UIBarButtonItem alloc] initWithCustomView:btnimg_GPS];
上面可以看到我們使用btn_gps_un與btn_gps_click作為效果圖,利用setBackgroundImage方法指定給按鈕,並設定他們應該出現的狀態,最後裝到一個UIBarButtonItem中。
以同樣的步驟,在準備兩個名為imageLayerButton與imageSearchButton的UIBarButtonItem,將其裝到一個NSMutableArray中,並設定給TranslucentToolbar,最後以UIBarButtonItem方法中initWithCustomView來初始化,然後指定給要放置的Navigation(EX:self.navigationItem.rightBarButtonItem)。
//將按鈕加到NSMutableArray當中。
NSMutableArray* buttons = [[NSMutableArray alloc] initWithCapacity:3];
[buttons addObject:imageGPSButton];
[buttons addObject:imageLayerButton];
[buttons addObject:imageSearchButton];
[imageGPSButton release];
[imageLayerButton release];
[imageSearchButton release];
//建立一個UIToolbar來裝載剛剛建立的NSMutableArray
TranslucentToolbar *toolbar = [[TranslucentToolbar alloc]initWithFrame:CGRectMake(0, 0, 240, 44)];
[toolbar setItems:buttons animated:NO];
[buttons release];
UIBarButtonItem *navigationRightBarButton = [[UIBarButtonItem alloc] initWithCustomView:toolbar];
self.navigationItem.rightBarButtonItem = navigationRightBarButton;
[toolbar release];
[navigationRightBarButton release];
如此就可以放置上一個底色為透明,又帶有美編圖的功能列表。
效果:
2012年10月27日 星期六
Close my eyes, may be better.
我們身處在一個需要誇大的年代:
「我們的服務一級棒,這系統保證不會有問題,穩定服務100年!」
「這手機耐操、好動、又拼第一」
「這深海來的魚,肉質鮮嫩,咬下去猶如活生生的魚在嘴裡跳動」
「水果地圖世界第一,圖資完整、正確性一等一」
.
.
.
.
「我的案子沒問題,他們非常滿意」
對於選擇性吹捧自己的功績,其實這幾年已經看了很久,從一開始瞪大雙眼,到現在變成私底下的笑話;而聽的人你我都知道他們喜歡,對喜歡聽的人說他們喜歡聽的話,其實就某方面來說是一件對的事情。
但往往事情還是要處理,到時又是東牽扯、西牽扯,那這時候請你有肩膀。
--------------------------------------------------
對於不好的事情我們喜歡推託,不喜歡認錯,更不喜歡在某些場合說是自己的錯,但你將自己的錯誤指向別人,就是在逐步破壞某些關係。
我們常會想,我推給他有差嗎?他對我又沒有什麼利益關係!?或是我又沒差,他不重要之類的自我安慰,但這也是一種習慣,越推的人越會推,而就會開始在其實跟你有關你在乎的人中推託。
---------------------------------------------------
在可以表現的時候吹捧自己,遇到問題的時候推給別人,絕對是現今社會在盲盲長官中生存、升遷必學絕招,不太講道德的群體生活,這甚至可以說是一件對的事情?但那是空洞的、是一種你無法教導孩子的想法,我們應該閉上雙眼,思考自己應該要走的方向。
期許你我說話有料得體,有肩膀認錯、有能力解決。
2012年10月23日 星期二
iOS學習_warning: directory not found for option "XXX"
會出現這個警告的原因是:引用找不到檔案的framework,通常發生在更換SDK版本時。
消除方法很簡單:
1. 到Build Settings內搜尋Library Search Paths。
2. 刪除那個被警告的路徑。
iOS學習_於XCode4.5使用ESRI SDK for IOS 2.3
之前使用ESRI SDK for iOS時還是XCode4.2與SDK2.1,將設備都更新為iOS6之後,也順便更新了SDK到2.3版本。而使用方法已經略有不同。
安裝完SDK後,這次只需要三個步驟(官方文件):
1. 設定Framework Search Paths:到Build Settings找framework search path,輸入$(HOME)/Library/SDKs/ArcGIS/**。
2. 加入SDK內有使用到的framework與library。
*********************
3. 設定Build Settings(搜尋Other Linker Flags,增加-all_load、-ObjC與-framework ArcGIS)
如果是你新專案,到此可以開始測試設定是否正確了。(使用ESRI的Sample或是參考前篇的第八步驟)
但如果你是舊專案改過來的,那麼你必須做以下變更:
1. 將#import "ArcGIS.h"改為#import <ArcGIS/ArcGIS.h>
2. 於Build Phases的Link Binary With Libraries移除libArcGIS.h
3. 將在專案列表內的libArcGIS.h移除(應該會因為找不到而是紅色)
4. 移除Build Settings的User Header Search設定值。
*********************
- CoreGraphics.framework
- CoreLocation.framework
- Foundation.framework
- QuartzCore.framework
- UIKit.framework
- CoreText.framework (dependency introduced at v1.8)
- libstdc++.dylib (dependency introduced at v1.8)
- MediaPlayer.framework (dependency introduced at v2.0)
- MobileCoreServices.framework (dependency introduced at v2.0)
- libz.dylib (dependency introduced at v2.1)
- Security.framework (dependency introduced at v2.1)
3. 設定Build Settings(搜尋Other Linker Flags,增加-all_load、-ObjC與-framework ArcGIS)
如果是你新專案,到此可以開始測試設定是否正確了。(使用ESRI的Sample或是參考前篇的第八步驟)
但如果你是舊專案改過來的,那麼你必須做以下變更:
1. 將#import "ArcGIS.h"改為#import <ArcGIS/ArcGIS.h>
2. 於Build Phases的Link Binary With Libraries移除libArcGIS.h
3. 將在專案列表內的libArcGIS.h移除(應該會因為找不到而是紅色)
4. 移除Build Settings的User Header Search設定值。
2012年10月17日 星期三
Ola的Photoshop學堂Part1-頁面捲曲、地圖摺角、照片翻頁
閱讀本文章注意事項:
1. 筆者為僅會小畫家且完全不曾接觸過Photoshop之程式設計師。
2. 本文章使用Photoshop CS5,筆者沒使用過其他版本。
3. 若是本文章幫助你完成該效果,請留言:「Ola國小時貼在布告欄展示一天就被撕掉的海報我覺得很棒!」。
-------------------------------------------------
這兩天我的美編魂又爆發了,胡搞了更多過一個星期我就再也記不住的效果,所以必須做一個紀錄,首先就是我最愛的頁面捲曲(地圖摺角、照片翻頁)。
步驟:
1. 準備一張想要有捲曲效果的圖片,我所使用的是Android上一個非常好用的記帳軟體帳本鎖頁面。將圖片拉進Photoshop中。你會看到下圖:
2. 剛拉進來的圖會顯示他是"背景",我們必須在"背景"字樣旁點擊兩下將其轉為圖層。你會看到下圖:
3. 完成後可以看到"背景"字樣變為圖層0,那麼我們就可以正式開始製作捲曲的效果。選擇工具列的第二項"矩形選取畫面工具",框選要捲曲的角落。你會看到下圖:
4. 接著選取工具列上的"漸層工具"(如果你沒有漸層工具這項,請於"油漆桶工具"按右鍵切換到"漸層工具"),選擇完成後可以看到上方工具列變為漸層工具的各個選項,點一下"漸層揀選器"(如下圖的選單位置),選擇"黑、白"揀選器。你會看到下圖:
5. 選擇完成後,將滑鼠於剛剛選取的範圍內由左下往右上劃(如果你跟我一樣要右上捲曲)。你會看到下圖:
6. 完成後,將選取範圍移除。(你可以點擊"矩形選取畫面工具"後在圖面上按右鍵選取消選取),並使用"矩形選取畫面工具"選取一個包含黑白漸層,但左邊跟下方都略大於的範圍(如果你跟我一樣要右上捲曲)。你會看到下圖:
7. 完成後,到編輯選單選擇任意變形。你會看到下圖:
8. 點擊"在任意變形與彎曲模式之間切換"的按鈕(如圖上框框處)。你會看到下圖:
9. 拉動最右上角的節點,並把相鄰的節點分別往左跟往下拉(到這一步驟應該可以發現,整個捲曲的效果程度,取決於漸層範圍的大小與拉動的角度,請自行調整。)。你會看到下圖:
10. 最後可以另存新檔把處理完成的圖疊到別的圖上了。成果:
1. 筆者為僅會小畫家且完全不曾接觸過Photoshop之程式設計師。
2. 本文章使用Photoshop CS5,筆者沒使用過其他版本。
3. 若是本文章幫助你完成該效果,請留言:「Ola國小時貼在布告欄展示一天就被撕掉的海報我覺得很棒!」。
-------------------------------------------------
這兩天我的美編魂又爆發了,胡搞了更多過一個星期我就再也記不住的效果,所以必須做一個紀錄,首先就是我最愛的頁面捲曲(地圖摺角、照片翻頁)。
步驟:
1. 準備一張想要有捲曲效果的圖片,我所使用的是Android上一個非常好用的記帳軟體帳本鎖頁面。將圖片拉進Photoshop中。你會看到下圖:
2. 剛拉進來的圖會顯示他是"背景",我們必須在"背景"字樣旁點擊兩下將其轉為圖層。你會看到下圖:
3. 完成後可以看到"背景"字樣變為圖層0,那麼我們就可以正式開始製作捲曲的效果。選擇工具列的第二項"矩形選取畫面工具",框選要捲曲的角落。你會看到下圖:
4. 接著選取工具列上的"漸層工具"(如果你沒有漸層工具這項,請於"油漆桶工具"按右鍵切換到"漸層工具"),選擇完成後可以看到上方工具列變為漸層工具的各個選項,點一下"漸層揀選器"(如下圖的選單位置),選擇"黑、白"揀選器。你會看到下圖:
5. 選擇完成後,將滑鼠於剛剛選取的範圍內由左下往右上劃(如果你跟我一樣要右上捲曲)。你會看到下圖:
6. 完成後,將選取範圍移除。(你可以點擊"矩形選取畫面工具"後在圖面上按右鍵選取消選取),並使用"矩形選取畫面工具"選取一個包含黑白漸層,但左邊跟下方都略大於的範圍(如果你跟我一樣要右上捲曲)。你會看到下圖:
7. 完成後,到編輯選單選擇任意變形。你會看到下圖:
8. 點擊"在任意變形與彎曲模式之間切換"的按鈕(如圖上框框處)。你會看到下圖:
9. 拉動最右上角的節點,並把相鄰的節點分別往左跟往下拉(到這一步驟應該可以發現,整個捲曲的效果程度,取決於漸層範圍的大小與拉動的角度,請自行調整。)。你會看到下圖:
10. 最後可以另存新檔把處理完成的圖疊到別的圖上了。成果:
2012年9月27日 星期四
iOS學習_iOS5轉iOS6的螢幕旋轉問題_Part1
iPad更新完iOS6,Xcode也更新到4.5版,重新編譯專案後會發現以往有處理螢幕旋轉的頁面,好像都直立立地站著完全沒有要橫過來的感覺。
還沒有去很仔細地研究iOS6螢幕旋轉的控制方法,但是當務之急就是先將原本的專案恢復原狀。
原專案: 1. 各個View以shouldAutorotateToInterfaceOrientation方法來控制是否支援螢幕旋轉。
2. 從一開始的UIResponder就以UINavigationController裝進第一個UIViewController。
3. 專案雖然View很多,但是並沒有哪一個View特別指定只知原某個方向,換句話說每一個View都可以轉來轉去。
因為上面的第三點,所以改起來也相對簡單。
方法:
1. 原本在UIResponder中使用addSubview來將UINavigationController放進去。
但現在必須使用setRootViewController的方法,才能正確支援旋轉。
2. 在第一個推進UINavigationController的View加入兩個方法
如此之後再推進UINavigationController的View也都會支援螢幕旋轉,但對於各別View要分別支援不同的螢幕方向就必須要去細讀iOS6的官方文件了。
還沒有去很仔細地研究iOS6螢幕旋轉的控制方法,但是當務之急就是先將原本的專案恢復原狀。
原專案: 1. 各個View以shouldAutorotateToInterfaceOrientation方法來控制是否支援螢幕旋轉。
2. 從一開始的UIResponder就以UINavigationController裝進第一個UIViewController。
3. 專案雖然View很多,但是並沒有哪一個View特別指定只知原某個方向,換句話說每一個View都可以轉來轉去。
因為上面的第三點,所以改起來也相對簡單。
方法:
1. 原本在UIResponder中使用addSubview來將UINavigationController放進去。
[self.window addSubview:navigator.view];
但現在必須使用setRootViewController的方法,才能正確支援旋轉。
[self.window setRootViewController:navigator];
2. 在第一個推進UINavigationController的View加入兩個方法
-(BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
如此之後再推進UINavigationController的View也都會支援螢幕旋轉,但對於各別View要分別支援不同的螢幕方向就必須要去細讀iOS6的官方文件了。
iOS小抄_更新Xcode4.5後,RUN出現file is universal (3 slices) but does not contain a(n) armv7s slice錯誤
趁著剛Demo完,就算機器出問題也有緩衝時間的這個摸悶特,很大膽的直接將測試用的iPad升級到iOS6,立刻將手邊正在開發的專案執行下去!!!非常好!!!!我得到了!!這個!!!
這也沒什麼,我想不會有人期待完全沒有問題,錯誤訊息是:
意思就是我所使用的ArcGIS.framework並不支援armv7s的架構。所以在第三方的SDK還沒有支援以前,可以先使用armv7來進行編譯。
方法:
1. 到Build Settings找Architectures。
2. 將Valid Architectures內的armv7s刪除。
如此,應該就可以正常編譯通過,但馬上會遇到螢幕旋轉的問題。
這也沒什麼,我想不會有人期待完全沒有問題,錯誤訊息是:
ld: file is universal (3 slices) but does not contain a(n) armv7s slice: /Users/cecige/Library/SDKs/ArcGIS/ArcGIS.framework/ArcGIS for architecture armv7s
clang: error: linker command failed with exit code 1 (use -v to see invocation)
意思就是我所使用的ArcGIS.framework並不支援armv7s的架構。所以在第三方的SDK還沒有支援以前,可以先使用armv7來進行編譯。
方法:
1. 到Build Settings找Architectures。
2. 將Valid Architectures內的armv7s刪除。
如此,應該就可以正常編譯通過,但馬上會遇到螢幕旋轉的問題。