授權轉載,作者:判若兩人丶(Github)
前言:
在上一篇文章中我相信幫助了很多的小伙伴, 那麼在這篇文章希望還能幫助到你!
1.在Block中一起使用weakSelf與strongSelf的含義
我們都會聲明一個弱引用在block中使用, 目的就是防止循環引用, 那麼weakSelf與strongSelf一起使用目的是什麼呢?首先先定義2個宏:
#define LRWeakSelf(type) __weak typeof(type) weak##type = type;
#define LRStrongSelf(type) __strong typeof(type) type = weak##type;
我們創建一個shop並且在shop.myBlock代碼塊中使用弱引用LRWeakSelf(shop);
LRShop *shop = [[LRShop alloc]init]; shop.string = @"welcome to our company"; //弱引用 LRWeakSelf(shop); shop.myBlock = ^{ NSLog(@"%@",weakshop.string); }; shop.myBlock();
LRWeakSelf(shop);與LRStrongSelf(shop);一起使用
LRShop *shop = [[LRShop alloc]init]; shop.string = @"welcome to our company"; //弱引用 LRWeakSelf(shop); shop.myBlock = ^{ //強引用 LRStrongSelf(shop) NSLog(@"%@",shop.string); }; shop.myBlock();
這2個打印結果都是shop.string有值並且shop也銷毀了, 看起來是沒什麼區別:
僅僅使用LRWeakSelf(shop);並且在myBlock中增加一個延遲3秒在輸出就會出現問題, 雖然對象銷毀了, 輸出的值卻是null
//弱引用 LRWeakSelf(shop); shop.myBlock = ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@",weakshop.string); }); }; shop.myBlock();
如果LRWeakSelf(shop);與LRStrongSelf(shop);一起使用輸出的shop.string有值,對象也銷毀了, 我就不再截圖給大家看了!
//弱引用 LRWeakSelf(shop); shop.myBlock = ^{ //強引用 LRStrongSelf(shop) dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@",shop.string); }); }; shop.myBlock();
通過上面一堆的解釋, 我們明顯發現LRWeakSelf(shop);與LRStrongSelf(shop);一起使用的好處, 不但能打印出我想要的值,而且也不會造成循環引用 , 在開發中這兩個方法可以根據實際情況進行使用!
2.使用UIAppearance注意的問題
如果不熟悉可以點擊了解, UIAppearance它的目的就是設置全局顯示樣式, 我們知道只要帶UI_APPEARANCE_SELECTOR這個宏, 我們就可以使用UIAppearance比如這樣設置:
我們知道UIBarButtonItem它是有狀態的比如UIControlStateNormal或者是UIControlStateDisabled狀態
如果通過UIAppearance設置UIControlStateDisabled狀態下的顏色是不好使的, 因為使用appearance會有一些延遲, 導致在不同狀態下的顏色不好使, 我們只要強制刷新一下就可以了:
// 刷新 [self.navigationController.navigationBar layoutIfNeeded];
所以以後使用UIAppearance在某個狀態下設置顏色,字體等不好使, 只需要在對應的位置用layoutIfNeeded刷新一下就可以了!
3. UITextField使用注意
先貼一個UITextField如何設置占位文字的顏色, 如果不先設置占位文字, 占位文字的顏色是不管用的:
//先設置占位文字 textField.placeholder = @"設置了占位文字內容以後, 才能設置占位文字的顏色"; //占位文字顏色 [textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
大家監聽UITextField文字的改變會用到代理:
#pragma mark -- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { //這裡監聽文字改變 return YES; }
但是這個代理方法監聽會有問題如下圖:
所以我們要監聽UITextField的文字改變不建議使用代理, 我們用addTarget監聽文字
[textField addTarget:self action:@selector(textEditingChanged) forControlEvents:UIControlEventEditingChanged];
4.UITextView添加占位文字的正確方法
UITextView的占位文字屬於它內部的一個功能, 我們在控制器或者用代理來處理占位文字一些功能是不合理的, 所以我們要自定義UITextView把相關內部的東西都封裝起來!
(1)給外界提供占位文字與占位文字顏色:
/** 占位文字 */ @property (nonatomic, copy)NSString *placeholder; /** 占位文字顏色 */ @property (nonatomic, strong)UIColor *placeholderColor;
(2)設置占位文字的默認值, 如果不設置默認值,外界不用你提供的方法會有崩潰現象:
// 設置默認字體 self.font = [UIFont systemFontOfSize:17]; // 設置默認的占位文字顏色 self.placeholderColor = [UIColor grayColor];
(3)內部添加占位文字的label ;
/** 占位文字label */ @property (nonatomic, weak) UILabel *placeholderLabel; //懶加載 - (UILabel *)placeholderLabel { if (_placeholderLabel == nil) { UILabel *placeholderLabel = [[UILabel alloc] init]; placeholderLabel.numberOfLines = 0; [self addSubview:placeholderLabel]; _placeholderLabel = placeholderLabel; } return _placeholderLabel; }
(4)通過監聽文字改變,來顯示或隱藏占位文字
// 監聽文字 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChangeNotification) name:UITextViewTextDidChangeNotification object:nil]; //監聽的方法 - (void)textDidChangeNotification { // 有文字就隱藏占位文字 self.placeholderLabel.hidden = self.hasText; }
(5)如果占位文字被修改, 顏色被修改, 字體被修改, 我們在內部需要重寫set方法, 如果通過代碼修改了textView文字(不是占位文字)不會發通知也需要重寫set方法:
封裝好的自定義TextView可以直接使用:Demo下載
5.自定義控件裡如何拿到導航控制器進行頁面跳轉?
(1)如果有UITabBarController我們會這樣獲取導航控制器:
UIViewController *viewC = [[UIViewController alloc]init]; // 取出當前的導航控制器 UITabBarController *tabBarVc = (UITabBarController *)[UIApplication sharedApplication].keyWindow.rootViewController; //The view controller associated with the currently selected tab item //當前選擇的導航控制器 UINavigationController *navC = (UINavigationController *)tabBarVc.selectedViewController; [navC pushViewController:viewC animated:YES];
(2)如果通過modal出來的控制器並且用UITabBarController不好使, 我們會這樣獲取導航控制器:
UIViewController *viewC = [[UIViewController alloc]init]; //獲取最終的根控制器 UIViewController *rootC = [UIApplication sharedApplication].keyWindow.rootViewController; //如果是modal出來的控制器,它就會通過presentedViewController拿到上一個控制器 UINavigationController *navC = (UINavigationController *)rootC.presentedViewController; [navC pushViewController:viewC animated:YES];
6.修改了leftBarButtonItem如何恢復系統側滑返回功能
在開發中系統的leftBarButtonItem不是我們想要的, 如果我們修改了leftBarButtonItem那麼系統自帶的側滑返回功能就不好使了!
//設置代理 self.interactivePopGestureRecognizer.delegate = self; #pragma mark -//實現代理方法:return YES :手勢有效, NO :手勢無效 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { //當導航控制器的子控制器個數 大於1 手勢才有效 return self.childViewControllers.count > 1; }
7.重新認識Bounds
我們之前對Bounds理解就是以自己的左上角為坐標原點, 也就是說Bounds的x值y值是0, 但是Bounds的x值y值有可能是正數也可能是負數, 不一定是0那麼Bounds真正是什麼意思呢 ?
Bounds: 是以自己內容的左上角為坐標原點, 計算出自己的位置和大小
Frame: 是以父類內容的左上角為坐標原點, 計算出自己的位置和大小
那什麼是內容呢 ? 首先內容是抽象的, 一個控件不僅僅只有一層矩形框的, 他有很多圖層的, 這個內容其實就可以抽象成一個控件的內部圖層
內容:就是內部的東西, 它的子控件也屬於內容,也就是說修改了Buonds子控件的位置也會跟著改變
上圖藍色和綠色是屬於一個控件, 只不過藍色是控件本身, 綠色是控件的內容, 我們改變這個控件的Bounds的x值y值為-20, 內容位置改變, 控件本身位置不變!
8.跟枚舉相關的一些符號的含義
上圖是一個蘋果官方的一個枚舉, 我們主要是看<<的用處, 如果在枚舉中只要<<那它的含義就是可以通過|進行組合使用:
//隨便添加一個UITextField UITextField *field = [UITextField new]; //可以通過 | 組合使用UIControlEventEditingDidBegin, UIControlEventValueChanged,UIControlEventEditingDidEnd [field addTarget:self action:@selector(textFieldDidChanged) forControlEvents:UIControlEventEditingDidBegin | UIControlEventValueChanged | UIControlEventEditingDidEnd]; [self.view addSubview:field];
如果枚舉沒有<<就不能組合使用, 那它有什麼規律呢1 << n 代表:2的n次方:
//1 << 16 代表:2的16次方 UIControlEventEditingDidBegin = 1 << 16, //1 << 17 代表:2的17次方 UIControlEventEditingChanged = 1 << 17, //1 << 18 代表:2的18次方 UIControlEventEditingDidEnd = 1 << 18, //1 << 19 代表:2的19次方 UIControlEventEditingDidEndOnExit = 1 << 19,
原來這樣的枚舉可以組合使用, 那蘋果官方是怎麼知道我們多個條件組合使用了呢 ?
NSUInteger controlEvents = UIControlEventEditingDidBegin | UIControlEventValueChanged | UIControlEventEditingDidEnd; /** //通過 & 符號來判斷是否包含: UIControlEventEditingDidBegin, UIControlEventValueChanged, UIControlEventEditingDidEnd */ if (controlEvents & UIControlEventEditingDidBegin) { NSLog(@"UIControlEventEditingDidBegin"); }else if (controlEvents & UIControlEventValueChanged) { NSLog(@"UIControlEventValueChanged"); }else if (controlEvents & UIControlEventEditingDidEnd) { NSLog(@"UIControlEventEditingDidEnd"); }
通過以上方法就能判斷組合的狀態, 在開發中這個<<意義很大的, 如果多個條件中, 任何一個條件滿足我們也可用帶<<的枚舉給外界組合使用, 就像蘋果官方添加<<使用是一樣的!
9.Xib相關的一些問題
下圖我們可以看出來, 如果通過xib加載出來的view尺寸是不正確的, 在xib中這個view不管你怎麼設置都是治標不治本,我們會在layoutSubviews通過自己的寬度來計算子控件的尺寸!
//在這裡拿出的寬度是不正確的 - (void)awakeFromNib {} //對尺寸計算我們一般拿到這個方法中計算(拿到自己寬度計算子控件的尺寸) - (void)layoutSubviews { [super layoutSubviews]; //在這裡拿到自己的寬度是正確的 }
那我們也會想到, 如果控制器的view也是xib創建的, 我們該怎麼辦 ? 其實不管控制器是在哪裡創建的, 我們只要只在viewDidLayoutSubviews方法中拿到控制器尺寸來計算子控件尺寸都是正確的, 所以說建議大家以後在viewDidLayoutSubviews計算尺寸:
- (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; //在這裡計算尺寸 }
喜歡的小伙伴請點贊一下吧!如果有不足的地方,請大家及時幫忙糾正與補充,順便談談你的建議!