本文章持續(xù)更新,總結(jié)在項(xiàng)目中遇到的問題并及時記錄下來
一.關(guān)于導(dǎo)航欄
(1).設(shè)置背景顏色
第一種情況:全局統(tǒng)一設(shè)置
新建自定義導(dǎo)航欄的類.在viewDidLoad獲取全局導(dǎo)航欄,設(shè)置setBarTintColor設(shè)置導(dǎo)航欄顏色.
UINavigationBar *bar = [UINavigationBar appearance];
[bar setBarTintColor:[UIColor whiteColor]];
第二種情況:分頁設(shè)置
這種情況也就是每個控制器的導(dǎo)航欄由當(dāng)前自己的控制器進(jìn)行設(shè)置。(如果想避免每個控制器寫大量的重復(fù)設(shè)置代碼,可以新建一個控制器的基類,之后的控制器繼承該基類控制器即可)
self.navigationController.navigationBar.barTintColor = NavColor;
另外,如果要設(shè)置成自定義的背景圖片,則可以
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@ "navigation_bg.png" ] forBarMetrics:UIBarMetricsDefault];
(2).設(shè)置導(dǎo)航欄字體樣式
同樣在viewDidLoad設(shè)置
NSDictionary *dict = @{NSForegroundColorAttributeName:CJWThemColor};
[bar setTitleTextAttributes:dict];
//更多Attributes可以進(jìn)頭文件進(jìn)行查看
(3).系統(tǒng)按鈕設(shè)置:
UINavigationBar *bar = [UINavigationBar appearance];
[bar setTintColor:[UIColor whiteColor]];
(4).顯示和隱藏導(dǎo)航欄
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController.navigationBar setHidden:YES];
[self.rdv_tabBarController setTabBarHidden:YES animated:NO];
}
-(void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.navigationController.navigationBar setHidden:NO];
[self.rdv_tabBarController setTabBarHidden:NO animated:NO];
}
(5)導(dǎo)航條背景透明
[self.navigationController.navigationBar setBackgroundImage:[[UIImage alloc] init] forBarMetrics:UIBarMetricsDefault];
[self.navigationController.navigationBar setShadowImage:[[UIImage alloc] init]];
(6).統(tǒng)一設(shè)置導(dǎo)航欄自定義返回按鈕
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[viewController.view endEditing:YES];
if (self.childViewControllers.count > 0) { // 如果viewController不是最早push進(jìn)來的子控制器
viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithImage:IMAGE_NAMED(@"返回按鈕") style:UIBarButtonItemStylePlain target:self action:@selector(back)];
// 隱藏底部的工具條
viewController.hidesBottomBarWhenPushed = YES;
}
// 所有設(shè)置搞定后, 再push控制器
[super pushViewController:viewController animated:animated];
}
- (void)back
{
[self popViewControllerAnimated:YES];
}
(7).由于自定義了返回按鈕,系統(tǒng)默認(rèn)的右滑手勢將會失效,因此需要手動設(shè)置滑動手勢操作.記得遵守<UIGestureRecognizerDelegate>協(xié)議
- (void)viewDidLoad {
[super viewDidLoad];
self.interactivePopGestureRecognizer.delegate = self;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
// 手勢何時有效 : 當(dāng)導(dǎo)航控制器的子控制器個數(shù) > 1就有效
return self.childViewControllers.count > 1;
}
(8).如果項(xiàng)目中導(dǎo)航欄樣式比較復(fù)雜,比如某些導(dǎo)航欄是藍(lán)色,進(jìn)入另外一個控制器的導(dǎo)航欄又是紅色,或者透明.最直接的方法還是用第三方框架,當(dāng)然你也可以自己自定義一個,不過我覺得導(dǎo)航欄這塊有點(diǎn)復(fù)雜,細(xì)節(jié)很多.要全部自己實(shí)現(xiàn)處理好有點(diǎn)考驗(yàn)技術(shù).這里推薦一個star超過3.7k的導(dǎo)航欄框架:FDFullscreenPopGesture,該框架只有一個分類,做到低耦合效果!
補(bǔ)充:
如果當(dāng)前控制器有導(dǎo)航欄并且視圖為UIScrollView(包括子類UITableView,UITextView)的時候,控制器會默認(rèn)自動將UIScrollView的可視范圍向下偏移64的高度.如不需要這種效果,可以設(shè)置補(bǔ)偏移
self.automaticallyAdjustsScrollViewInsets = NO;
二.關(guān)于狀態(tài)欄
對狀態(tài)欄的控制分也兩種情況:全局設(shè)置和分頁面設(shè)置??刂苾煞N模式的開關(guān)是info.plist文件的View controller-based status bar appearance配置項(xiàng)。
該字段的值為bool值,默認(rèn)為YES. YES意味著當(dāng)前控制器的設(shè)置優(yōu)先級最高.NO為當(dāng)前控制器設(shè)置均無效.
(1).全局設(shè)置狀態(tài)欄
第一步:先把info.plist文件的View controller-based status bar appearance設(shè)置為No
第二步:通過下面代碼在 didFinishLaunchingWithOptions 中設(shè)置
//設(shè)置狀態(tài)欄的字體顏色模式
[[UIApplication sharedApplication]setStatusBarStyle:UIStatusBarStyleLightContent];
//設(shè)置狀態(tài)欄是否隱藏
[[UIApplication sharedApplication] setStatusBarHidden:NO];
另外,還可以通過直接在info.plist添加Status bar style字段,值為UIStatusBarStyleLightContent便可以設(shè)置全局狀態(tài)欄顏色為白色.(如果不設(shè)置默認(rèn)當(dāng)然為黑色)
(2).分頁設(shè)置狀態(tài)欄。
由各控制器來控制狀態(tài)欄的功能,在這種模式下,全局的設(shè)置將無效??!所以我們必須逐個頁面對狀態(tài)欄進(jìn)行設(shè)置,否則狀態(tài)欄將維持默認(rèn)的黑色字體和默認(rèn)為顯示狀態(tài)。
- (UIStatusBarStyle)preferredStatusBarStyle{
//返回白色
return UIStatusBarStyleLightContent;
//返回黑色
//return UIStatusBarStyleDefault;
}
(3).設(shè)置狀態(tài)欄背景顏色
在當(dāng)前控制器下,添加以下代碼
-(void)viewWillAppear:(BOOL)animated{
[self setStatusBarBackgroundColor:CJWThemColor];
}
//設(shè)置狀態(tài)欄顏色
- (void)setStatusBarBackgroundColor:(UIColor *)color {
UIView *statusBar = [[[UIApplication sharedApplication] valueForKey:@"statusBarWindow"] valueForKey:@"statusBar"];
if ([statusBar respondsToSelector:@selector(setBackgroundColor:)]) {
statusBar.backgroundColor = color;
}
}
三.關(guān)于UITabBarController
(1).設(shè)置UITabBarItem
關(guān)于設(shè)置UITabBarItem選中后的圖片,一般會有UI設(shè)置提供圖片;
設(shè)置選中后字體顏色,可以通過以下代碼:
- (void)viewDidLoad {
[super viewDidLoad];
[UITabBar appearance].translucent = NO;
self.tabBar.barTintColor = CJWColor(240, 241, 242);
[[UITabBarItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName: CJWThemColor} forState:UIControlStateSelected];
//添加tabbar子控制器
[self setupChildViewControllers];
}
四.關(guān)于Tableview
(1).Tableview分組樣式自定義間距
self.tableView.sectionHeaderHeight = 0;
self.tableView.sectionFooterHeight = 10;
self.tableView.contentInset = UIEdgeInsetsMake(0 - 35, 0, 0, 0);
(2). 設(shè)置tableview樣式
- (instancetype)init
{
return [self initWithStyle:UITableViewStyleGrouped];
}
(3).滑動tableview取消鍵盤(使鍵盤失去第一響應(yīng)者)
self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
(4).一個方法解決cell分割線顯示不完整問題
-(void)viewDidLayoutSubviews {
if ([self.tableView respondsToSelector:@selector(setSeparatorInset:)]) {
[self.tableView setSeparatorInset:UIEdgeInsetsZero];
}
if ([self.tableView respondsToSelector:@selector(setLayoutMargins:)]) {
[self.tableView setLayoutMargins:UIEdgeInsetsZero];
}
}
五.UITextView
(1).設(shè)置占位文字
總所周知UITextView并沒有一個類似UITextField的placeholder可以占位符.那該怎辦呢?網(wǎng)上有些博客也有解決方案,大概是往UITextView添加一個label,設(shè)置label的文字為占位文字.當(dāng)點(diǎn)擊輸入時候設(shè)置label隱藏.這種方法也可以.不過這里提供另外一種方法:
直接設(shè)置UITextView的text為想要的占位文字例如"請輸入內(nèi)容",然后在開始編輯的代理方法判斷如果TextView的值為@"請輸入內(nèi)容",如果是則設(shè)置TextView的值為空值:@"";在結(jié)束編輯的代理方法判斷TextView的值是否為空值,如果yes則重新設(shè)置TextView的值為@"請輸入內(nèi)容".
//內(nèi)容控件
UITextView *TV = [[UITextView alloc] initWithFrame:CGRectMake(UIScreenW*0.05, 64, UIScreenW*0.9, UIScreenH*0.3)];
TV.font = [UIFont systemFontOfSize:15];
TV.backgroundColor = [UIColor whiteColor];
TV.textContainerInset = UIEdgeInsetsMake(10, 10, 10, 0);//設(shè)置頁邊距
TV.text = @"內(nèi)容:";
TV.textColor = [UIColor grayColor];
TV.layer.borderWidth = 1;
TV.layer.borderColor = JWrayColor(226).CGColor;
TV.layer.cornerRadius = 7;
TV.clipsToBounds = YES;
TV.delegate = self;
[bgSCR addSubview:TV];
self.TV = TV;
#pragma UITextViewDelegate方法
//開始編輯
- (void)textViewDidBeginEditing:(UITextView *)textView {
if ([textView.text isEqualToString:@"內(nèi)容:"]) {
textView.text = @"";
textView.textColor = [UIColor blackColor];
JWLog(@"%ld",(unsigned long)textView.text.length);
}
}
//結(jié)束編輯
- (void)textViewDidEndEditing:(UITextView *)textView {
if (textView.text.length<1) {
textView.text = @"內(nèi)容:";
textView.textColor = [UIColor grayColor];
}
JWLog()
}
六.關(guān)于TableFootView
項(xiàng)目中遇到一種情況就是底部試圖用TableFootView做容器的情況.遇到一個問題就是想要TableFootView的高度能自動適TableFootView子控件內(nèi)容的高度.一開始嘗試過-(instancetype)initWithCoder:(NSCoder *)aDecoder (通過xib創(chuàng)建) 和
-(instancetype)initWithFrame:(CGRect)frame (代碼創(chuàng)建)均獲取不到最后一個子控件的高度.
原因 initWithFrame和initWithCoder為視圖初始化就執(zhí)行的方法,此時并不能獲取到控件的frame值.
解決方法需要在layoutSubviews方法獲取
-(void)layoutSubviews{
[super layoutSubviews];
[self setup];
}
- (void)setup{
self.jw_height = self.orderContentsLabel.jw_bottom+20;
}
七.關(guān)于多個操作異步操作執(zhí)行
例子:
有a、b、c、d 4個異步請求,如何判斷a、b、c、d都完成執(zhí)行?如果需要a、b、c、d順序執(zhí)行,該如何實(shí)現(xiàn)?
// 串行隊(duì)列的創(chuàng)建方法
dispatch_queue_t queue1= dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL);
// 并發(fā)隊(duì)列的創(chuàng)建方法1
dispatch_queue_t queue2= dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT);
//并發(fā)隊(duì)列的創(chuàng)建方法2(也叫全局隊(duì)列,GCD默認(rèn)創(chuàng)建的就是并發(fā)隊(duì)列)
dispatch_queue_t queue3 = dispatch_get_global_queue(0, 0);
//創(chuàng)建任務(wù)組
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue1, ^{
NSLog(@"A---%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue1, ^{
NSLog(@"B---%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue1, ^{
NSLog(@"C---%@",[NSThread currentThread]);
});
dispatch_group_async(group, queue1, ^{
NSLog(@"D---%@",[NSThread currentThread]);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"主線程---%@",[NSThread currentThread]);
});

解釋:要求順序執(zhí)行,那么可以將任務(wù)放到串行隊(duì)列中,自然就是按順序來異步執(zhí)行了。
// 串行隊(duì)列的創(chuàng)建方法
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
// 并發(fā)隊(duì)列的創(chuàng)建方法
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
如果A,B,C,D任務(wù)都是耗時操作,上面方法是否還有效?需要怎么改進(jìn)?
答案:有兩種方法可以解決:
一種是通過dispatch_semaphore_t函數(shù)創(chuàng)建信號量,當(dāng)進(jìn)行任務(wù)A操作時,信號量加1,執(zhí)行完畢,信號量減1,當(dāng)信號量為0時就會執(zhí)行下一個任務(wù).
/**
操作依賴--并發(fā)執(zhí)行,沒有順序
*/
- (void)group{
dispatch_group_t group = dispatch_group_create();
dispatch_semaphore_t semap = dispatch_semaphore_create(0);
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
// 執(zhí)行1個耗時的異步操作
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
dispatch_semaphore_signal(semap);
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_semaphore_wait(semap, DISPATCH_TIME_FOREVER);
});
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
// 執(zhí)行1個耗時的異步操作
dispatch_semaphore_signal(semap);
NSLog(@"----2-----%@", [NSThread currentThread]);
dispatch_semaphore_wait(semap, DISPATCH_TIME_FOREVER);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步操作都執(zhí)行完畢后,回到主線程...
NSLog(@"----完成-----%@", [NSThread currentThread]);
});
}

如果需要按順序執(zhí)行,看下面代碼:
/**
操作依賴--順序執(zhí)行
*/
- (void)group2{
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_queue_create("testBlock", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
//第一個延時操作
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"1");
dispatch_semaphore_signal(sem);
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
});
dispatch_async(queue, ^{
//第2個延時操作
dispatch_semaphore_signal(sem);
NSLog(@"2");
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
});
dispatch_async(queue, ^{
//第3個延時操作
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"3");
dispatch_semaphore_signal(sem);
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
});
dispatch_async(queue, ^{
//第4個延時操作
dispatch_semaphore_signal(sem);
NSLog(@"4");
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
});
}

第二種方法:通過dispatch_group_enter和dispatch_group_leave組合使用
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("queue",DISPATCH_QUEUE_SERIAL);
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
dispatch_group_leave(group);
});
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_notify(group, queue, ^{
NSLog(@"----完成-----%@", [NSThread currentThread]);
});

8.動畫切換window的根控制器
// options是動畫選項(xiàng)
[UIView transitionWithView:[UIApplication sharedApplication].keyWindow duration:0.5f options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
BOOL oldState = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
[UIApplication sharedApplication].keyWindow.rootViewController = [RootViewController new];
[UIView setAnimationsEnabled:oldState];
} completion:^(BOOL finished) {
}];
9.截圖功能
UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES, 0.0);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
10.獲取設(shè)備唯一標(biāo)識
此功能一般用來獲取用戶手機(jī)唯一標(biāo)志碼,比如游客登錄使用唯一標(biāo)志碼進(jìn)行用戶注冊登錄。
+ (NSString *)getDeviceID {
// 讀取keyChain存儲的UUID
NSString * strUUID = (NSString *)[AppKeyChain loadForKey: @"uuid"];
// 首次運(yùn)行生成一個UUID并用keyChain存儲
if ([strUUID isEqualToString: @""] || !strUUID) {
// 生成uuid
CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
strUUID = (NSString *)CFBridgingRelease(CFUUIDCreateString (kCFAllocatorDefault,uuidRef));
// 將該uuid用keychain存儲
[AppKeyChain saveData: strUUID forKey: @"uuid"];
}
return strUUID;
}
11 禁用init,new初始化方法
有時候我們想要自定義一個初始化方法,并且制定只能用這個初始化方法創(chuàng)建對象,為了防止同事習(xí)慣性用init或者new方法創(chuàng)建,就有必要對init和new初始化方法進(jìn)行禁用.
//指定初始化方法:
- (nullable instancetype)initWithPath:(NSString *)path NS_DESIGNATED_INITIALIZER;
// 禁用init,new初始化方法
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
12. 單例宏
個快速添加單利方法,只需要把以下代碼放到pch文件后,在想要用單利類的.h文件和.m文件定義好單利方法名字就可以了.
使用例子:
.h
@interface AYBlueHelp : NSObject
singleH(shareBlue)
@end
.m
@implementation AYBlueHelp
singleM(shareBlue)
@end
#define singleH(name) +(instancetype)name;
#if __has_feature(objc_arc)
#define singleM(name) static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
return _instance;\
}\
\
+(instancetype)name\
{\
return [[self alloc]init];\
}\
-(id)copyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
return _instance;\
}
#else
#define singleM static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
return _instance;\
}\
\
+(instancetype)shareTools\
{\
return [[self alloc]init];\
}\
-(id)copyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
-(oneway void)release\
{\
}\
\
-(instancetype)retain\
{\
return _instance;\
}\
\
-(NSUInteger)retainCount\
{\
return MAXFLOAT;\
}
#endif
13.為項(xiàng)目添加允許HTTP訪問白名單
現(xiàn)在蘋果已經(jīng)明確不允許全部使用http網(wǎng)絡(luò)請求了,不過允許個別http請求,只需要添加白名單就可以了。
設(shè)置域(把不支持https協(xié)議的接口設(shè)置成http的接口)
(1)、在info.plist中增加一個key:NSAppTransportSecurity,類型為字典類型。
(2)、然后添加一個NSExceptionDomains,其類型是字典類型。
(3)、把需要支持的域給添加到NSExceptionDomains里,其中域作為key,類型為字典類型。
(4)、每個域下面需要設(shè)置3個屬性,分別為NSIncludesSubdomains、NSExceptionRequiresForwardSecrecy、NSExceptionAllowsInsecureHTTPLoads,均為Boolean類型,其值分別為YES,NO,YES。
