iOS項(xiàng)目總結(jié)

本文章持續(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]);
   });

QQ20170325-153417@2x.png

解釋:要求順序執(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_enterdispatch_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。


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容