開發中常見問題集錦


這篇文章是專門用來記錄開發中一些常見的BUG以及常用的零碎知識點,我會隔一段時間更新內容

1.重復調用2次loadView和viewDidLoad

最好不要在UIViewController的loadView方法中改變狀態欄的可視性(比如狀態欄由顯示變為隱藏、或者由隱藏變為顯示),因為會導致重復調用2次loadView和viewDidLoad方法

假設狀態欄本來是處於顯示狀態的:


下面的是錯誤代碼:

 1 - (void)loadView {
 2      NSLog(@"loadView");
 3      // 隱藏狀態欄
 4      [UIApplication sharedApplication].statusBarHidden = YES;
 5      
 6      // .... 創建UIView
 7      self.view = [[[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds] autorelease];
 8      self.view.backgroundColor = [UIColor grayColor];
 9  }
10  
11  - (void)viewDidLoad {
12      [super viewDidLoad];
13      NSLog(@"viewDidLoad");
14  }

運行效果:


打印信息:

1 2013-02-26 00:51:36.152 weibo[2251:c07] loadView  
2 2013-02-26 00:51:36.153 weibo[2251:c07] loadView  
3 2013-02-26 00:51:36.153 weibo[2251:c07] viewDidLoad  
4 2013-02-26 00:51:36.154 weibo[2251:c07] viewDidLoad

雖然運行效果是對的,但是系統連續調用了2次loadView和viewDidLoad方法,導致創建了2次UIView,造成了不必要的開銷。

原因分析:

狀態欄由顯示變為隱藏,意味着屏幕的可用高度變長了,UIViewController的UIView的高度也要重新調整,因此系統會重新調用loadView方法創建UIView,創建完畢后再次調用viewDidLoad方法。

 

2.按鈕無法點擊

如果在UIImageView中添加了一個按鈕,你會發現在默認情況下這個按鈕是無法被點擊的,需要設置UIImageView的userInteractionEnabled為YES:

imageView.userInteractionEnabled = YES;

設置為YES后,UIImageView內部的按鈕就可以被點擊了

原因分析:

• 當用戶點擊屏幕時,會產生一個觸摸事件,系統會將該事件加入到一個由UIApplication管理的事件隊列中

UIApplication會從事件隊列中取出最前面的事件進行分發以便處理,通常,先發送事件給應用程序的主窗口(UIWindow)

主窗口會調用hitTest:withEvent:方法在視圖(UIView)層次結構中找到一個最合適的UIView來處理觸摸事件

(hitTest:withEvent:其實是UIView的一個方法,UIWindow繼承自UIView,因此主窗口UIWindow也是屬於視圖的一種)

hitTest:withEvent:方法大致處理流程是這樣的:

首先調用當前視圖pointInside:withEvent:方法判斷觸摸點是否在當前視圖內:

▶ 若pointInside:withEvent:方法返回NO,說明觸摸點不在當前視圖內,則當前視圖hitTest:withEvent:返回nil

▶ 若pointInside:withEvent:方法返回YES,說明觸摸點在當前視圖內,則遍歷當前視圖的所有子視圖(subviews),調用子視圖hitTest:withEvent:方法重復前面的步驟,子視圖的遍歷順序是從top到bottom,即從subviews數組的末尾向前遍歷,直到有子視圖hitTest:withEvent:方法返回非空對象或者全部子視圖遍歷完畢:

▷ 若第一次有子視圖hitTest:withEvent:方法返回非空對象,則當前視圖hitTest:withEvent:方法就返回此對象,處理結束

▷ 若所有子視圖hitTest:withEvent:方法都返回nil,則當前視圖hitTest:withEvent:方法返回當前視圖自身(self)

最終,這個觸摸事件交給主窗口的hitTest:withEvent:方法返回的視圖對象去處理

我大致畫了個iOS觸摸事件分發的原理圖:


hitTest:withEvent:方法會忽略以下視圖:

1> 隱藏(hidden=YES)的視圖

2> 禁止用戶操作(userInteractionEnabled=NO)的視圖

3> alpha<0.01的視圖

4> 如果一個子視圖的區域超過父視圖的區域(如果父視圖的clipsToBounds屬性為NO,超過父視圖區域的子視圖內容也會顯示),那么正常情況下在父 視圖區域外的觸摸操作不會被識別,因為父視圖的pointInside:withEvent:方法會返回NO,這樣就不會繼續向下遍歷子視圖了。當然,也 可以重寫pointInside:withEvent:方法來處理這種

綜上所述可得:如果父視圖的userInteractionEnabled=NO,觸摸事件不會繼續往下傳遞給子視圖,所以子視圖永遠無法處理觸摸事件。而UIImageView在默認情況下的userInteractionEnabled就是NO。


3.@2x和-568h@2x

於iOS設備的屏幕分辨率不盡相同,有大有小,那么在不同設備中 顯示同一張圖片,可能會造成圖片被拉伸、變形,嚴重影響用戶體驗。

為了讓圖片在不同設備中都能得到很好的顯示效果,同一類圖片我們一般會准備3種版本,比如iOS程序在啟動時會全屏顯示的Default.png圖片:

(Retina即視網膜屏幕)

• Default.png(圖片尺寸為320x480):顯示在非Retina-3.5英寸屏幕上(iPhone3G\iPhone3GS,屏幕分辨率為320x480)

• Default@2x.png(圖片尺寸為640x960):顯示在Retina-3.5英寸屏幕上(iPhone4\iPhone4s,屏幕分辨為640x960)

• Default-568h@2x.png(圖片尺寸為640x1136):顯示在Retina-4.0英寸屏幕上(iPhone5,屏幕分辨率為640x1136)

 

4.啟動app時全屏顯示Default.png

大部分app在啟動過程中全屏顯示一張背景圖片,比如新浪微博會顯示這張:


想在iOS中實現這種效果,毫無壓力,非常地簡單,把需要全屏顯示的圖片命名為Default.png即,在iOS app啟動時默認會去加載並全屏顯示Default.png

也可以用其他名稱來命名圖片,在Info.plist配置一下即可:

配置過后,app啟動時就會去加載並全屏顯示lufy.png

在默認情況下,app顯示Default.png時並非真正的"全屏顯示",因為頂部的狀態欄並沒有被隱藏,比如下面的效果:

大部分情況下,我們都想隱藏狀態欄,讓Default.png真正全屏顯示。

說到這里,可能有人馬上就想到了一種辦法:在AppDelegate的application:didFinishLaunchingWithOptions:方法中添加如下代碼:

[UIApplication sharedApplication].statusBarHidden = YES; 

我只能說你的思路是對的,但實際上達不到想要的效果,你會發現顯示Default.png時狀態欄還是存在的,等Default.png顯示完畢后,狀態欄才被隱藏。

先解釋下為什么這種方法不可行,其實原因很簡單:

1> Default.png是在app啟動過程中加載的,並不是在app啟動完畢后再加載的

2> AppDelegate的application:didFinishLaunchingWithOptions:方法是在app啟動完畢后才調用的

下面說一下解決方案,在Info.plist中增加一個配置即可:


這里的YES表示在app初始化(啟動)的時候就隱藏狀態欄。

當然,在Default.png顯示完畢后狀態欄還是隱藏的。如果想重新顯示狀態欄,補上下面代碼即可:

[UIApplication sharedApplication].statusBarHidden = NO; 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM