1、UITableView的scrollDelegate問題
下午遇到一個奇怪的問題,之前都沒有注意過,由於A VC中要實現tableView和其他View位置的聯動,
所以實現了tableView的delegate中的scrollViewDidScroll方法,結果在點擊商品push 商品詳情VC B 的時候,問題出現了。
問題表現:
在調用[self.navigationController pushViewController:productDetailVC animated:YES];
時發現VC A中的tableView總是會滾動到頂部(contentOffset被修改了)
看了半天也沒發現問題,后面在scrollViewDidScroll的位置加了一個斷點,發現在navigationController在Push VC B的過程中,系統會調用一次VC A中tableView的scrollViewDidScroll方法,關鍵是這時調用中傳入的contentOffset是有問題的(0,-contentInset.y),所以導致了VC A中的tableView會自動返回頭部
解決方法:在VC A的viewWillDisappear的時候設置tableView的delegate為nil,同時在viewWillAppear中再把tableView的delegate設置回來,這個問題就被解決掉了。
all in all 問題很奇怪,希望大家不再被同樣的問題困擾
2、UIView的exclusiveTouch屬性
通過設置[selfsetExclusiveTouch:YES];可以達到同一界面上多個控件接受事件時的排他性,從而避免一些問題。
3、UIScrollView和UITableView嵌套時點擊statusBar,scrollView不反回頭部的問題
蘋果在UIScrollView頭文件的注釋可以清楚的解決我們的困惑
// When the user taps the status bar, the scroll view beneath the touch which is closest to the status bar will be scrolled to top, but only if its `scrollsToTop` property is YES, its delegate does not return NO from `shouldScrollViewScrollToTop`, and it is not already at the top.
// On iPhone, we execute this gesture only if there's one on-screen scroll view with `scrollsToTop` == YES. If more than one is found, none will be scrolled.
@property(nonatomic) BOOL scrollsToTop; // default is YES.
4、JSON Kit數據轉換問題
json是很常用的網絡數據包格式,客戶端和服務端之間經常使用json來傳輸數據。對於一些字典類型的數據,如果某項數據為空,則會傳'<null>',使用JsonKit轉換以后會生出相應的[NSNull null]對象,而這種對象對於iOS來說並不是十分安全的,例如約定好商品的某一項字段為string類型,結果JSON Kit轉換為[NSNull null],這個時候如果不加判斷就當做是NSString處理就會存在問題。所以對於這種數據類型直接轉換為nil會更加安全,轉換方法如下:
#define PASS_NULL_TO_NIL(instance) (([instance isKindOfClass:[NSNull class]]) ? nil : instance)
針對nil調用任何方法基本上都是安全的。
5、iOS 7中系統自定義VC右滑返回特性不生效
iOS 7開始系統新增了UINavigationController中VC層級右滑返回上一級的特性,該特性默認是打開的。但是在項目中有一個自定義VC中該特性無效,排查半天定位到問題如下:
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"navigation_bar_back.png"] style:UIBarButtonItemStylePlain target:self action:@selector(backAction:)];
初一看這段代碼沒有任何問題,只是簡單的自定義返回按鈕。但是問題就處在這里,只要自定義了leftItem右滑返回上一層級就會實效。
因為我這個VC中UI比較復雜,ScrollView,tableView各種嵌套,因此一開始還懷疑是不是手勢沖突導致的,后來發現右滑返回屬性壓根就和我們能接觸的手勢無關。將上述代碼修改為如下代碼,問題就解決了。
if (IS_IOS_6) { self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"navigation_bar_back.png"] style:UIBarButtonItemStylePlain target:self action:@selector(backAction:)]; }
只針對iOS 7之前的系統自定義返回按鈕,iOS 7及以后直接使用系統的返回圖標“<”。
我們也可以直接獲取這個手勢“interactivePopGestureRecognizer”,做一些定制操作。
更詳細的信息可以參考:http://www.cnblogs.com/lexingyu/p/3432444.html
6、UITableViewCell選中保持問題
UITableView中cell的選中態在調用ReloadData以后無法保持,為了做到選中態一直有效,我試着在cellForRowAtIndexPath中恢復選中態,代碼如下:
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellIdentifier = @"myAccountMenuCellIdentifier"; UITableView *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; } ... // 恢復選中態 if (_currentSelectedIndexPath && indexPath.row == _currentSelectedIndexPath.row && indexPath.section == _currentSelectedIndexPath.section) { [cell setSelected:YES animated:NO]; } else { [cell setSelected:NO animated:NO]; } return cell; }
發現根本不起作用,還嘗試着重載了自定義cell的
- (void)setSelected:(BOOL)selected animated:(BOOL)animated; - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated;
也是毫無進展,最后終於通過重載reloadData,代碼如下:
- (void)reInvokeCurrentMenuItem { [self selectRowAtIndexPath:_currentSelectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; [self tableView:self didSelectRowAtIndexPath:_currentSelectedIndexPath]; }
即在每次reload以后都通做一次selectRowAtIndexPath的操作來恢復選中。問題雖然解決了但是這種方法也有潛在風險,比如reload的過程中數據源發生變化,可能之前選中的cell的indexPath已經改變,輕則選中不正確的cell,嚴重的話如果刷新以后cell個數減少,傳入一個過大的indexPath就會造成崩潰。
so此方法,請君“且用且珍惜”。
7、UITextField當實現了textFieldDidBeginEditing方法以后, Clearbtn不響應,原因是最簡單的view覆蓋導致事件被攔截了
如果必須實現delegate,則可以使用設置rightView的方式
這個問題我最后解決了,說來都不好意思,被別的View蓋住了,so出了問題還是得先思考一下是不是自己的問題。
查這個問題得過程中,我找到一個很好用的GDB調試方法,直接在GDB中輸入以下命令即可打印當前App的view層級。
po [[UIWindow keyWindow] recursiveDescription]
我知道網上有個收費的工具叫Reveal,可以更炫的查看view層級,好消息是xCode6 Debug自帶了可查看view層級的功能。底層的實現機制也是一樣,個人偏執的覺得還是命令行打印的更清晰,特別是view層級很多的時候。
8、Block為空是調用會Crush
昨天碰到一個crash問題,查了一下發現是Block是nil,然后我調用了該Block,程序crash。開始懷疑是不是我的block是在dispatch_async導致的crash,后面測試發現,只要block為nil,調用這個block都會crash。
簡單示例如下:
// 定義一個block類型 typedef void (^CompleteBlock) (); // 聲明一個CompleteBlock類型的變量 CompleteBlock cblock = nil; cblock(); // 調用會crash dispatch_async(dispatch_get_main_queue(), ^{ cblock(); // 調用也會crash });
定義一個nil的block,無論怎么調用都會crash,不知道蘋果為什么沒有做保護,總之以后調用block之前還是先判斷一下比較靠譜。
9、UIImagePickerController黑屏問題
使用UIImagePickerController完成拍照選取時,發現在iOS8上黑屏,其他設備都正常。查了半天才發現原來測試在系統設置中把App的Camera權限關閉了,測試也沒有注意到這點。我們一直以為這是iOS 8beta版本的bug,找了半天才發現原來原因很簡單。話說回來既然iOS7之后用戶可以控制Camera的權限,那么我們的App能不能做的更好,更友好呢,於是查了一下資料發現AVFoundation庫提供了檢測Camera授權狀態的API,所以就有了下面這個函數。

+ (void)takePhotoFromViewController:(UIViewController<UIImagePickerControllerDelegate, UINavigationControllerDelegate>*)controller { if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { if ([[AVCaptureDevice class] respondsToSelector:@selector(authorizationStatusForMediaType:)]) { AVAuthorizationStatus authorizationStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; if (authorizationStatus == AVAuthorizationStatusRestricted || authorizationStatus == AVAuthorizationStatusDenied) { // 沒有權限 UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:@"Please enabled Camera Access (in Settings > Privacy > Camera)!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; return; } } UIImagePickerController *pickerController = [[UIImagePickerController alloc] init]; pickerController.sourceType = UIImagePickerControllerSourceTypeCamera; pickerController.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto; pickerController.cameraFlashMode = UIImagePickerControllerCameraFlashModeAuto; pickerController.cameraDevice = UIImagePickerControllerCameraDeviceRear; pickerController.delegate = controller; [controller presentViewController:pickerController animated:YES completion:nil]; } else { // throw exception UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Warning" message:@"The Device not support Camera" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } }
代碼中顯示檢測系統是否支持拍照,然后在檢測App是否有拍照的權限,最后才是進入拍照界面,防止模擬器調試拍照時Crash,或者真機調試權限關閉時出現的拍照界面一直黑屏的情況。
10、xCode Archive出來的包在本地Organizer中看到的App沒有圖標的問題
xcassets的出現大大方便了我們提供App中對應的icon等,使我們再也不用操心icon的各種繁瑣命名了,諸如Icon-57.png等。之前也碰到過因為未提供完整的icon導致App被拒的問題,現在這些問題基本上不存在。
昨天在Archive包以后看到本地Organizer中顯示的iPad居然沒有圖標,再三檢查xcassets,發現所有icon都有,不應該有問題啊。后來查找資料發現這可能是xCode的一個bug,本地顯示沒有圖標,但是裝到設備上一級提交到AppStore再下載下來都是有圖標的,不影響審核和使用,純粹是顯示問題。
如何解決這個問題,檢查App中得xxx-info.plist文件中,添加Icon file屬性,並填上非retian和Retian屏幕對應的icon文件名稱(文件必須存在,且命名如下),如下圖所示:
再次Archive即可發現,本地的Organizerz中的App有icon了。
注:記錄iOS開發中遇到的奇葩的問題,隨時更新。
如果覺得本文幫到了你,請推薦給身邊的朋友
轉載請著名出處,有什么問題歡迎留言