TableView長截圖
UIImage *image = nil;
// 下面的方法,第一個參數(shù)表示區(qū)域大小。第二個參數(shù)表示是否是非透明的。如果需要顯示半透明效果,需要傳NO,否則傳YES。第三個參數(shù)就是屏幕密度了,調(diào)整清晰度。
UIGraphicsBeginImageContextWithOptions(self.tableView.contentSize, YES, [UIScreen mainScreen].scale);
CGPoint savedContentOffset = self.tableView.contentOffset;
CGRect savedFrame = self.tableView.frame;
self.tableView.contentOffset = CGPointZero;
self.tableView.frame = CGRectMake(0, 0, self.tableView.contentSize.width, self.tableView.contentSize.height);
[self.tableView.layer renderInContext:UIGraphicsGetCurrentContext()];
image = UIGraphicsGetImageFromCurrentImageContext();
self.tableView.contentOffset = savedContentOffset;
self.tableView.frame = savedFrame;
UIGraphicsEndImageContext();
UIColor添加透明度
UIColor *color = [[YMSkinHelper single] skinColor];
CGFloat red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
[color getRed:&red green:&green blue:&blue alpha:&alpha];
color = [UIColor colorWithRed:red green:green blue:blue alpha:0.3];
關于setObject
和setValue
方法的區(qū)別
NSString *test = nil;
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setObject:@"1" forKey:@"key1"];
//[dic setObject:@"1" forKey:test];//key不能為nil,崩潰.
//[dic setObject:test forKey:@"key2"];//object不能為nil,崩潰.
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:@"1" forKey:@"key1"];
//[dict setValue:@"1" forKey:test];//key不能為nil,崩潰.
[dict setValue:test forKey:@"key2"];//value可以為nil,不會崩潰,但是會調(diào)用removeObject:forKey方法導致key2鍵值對不存在.
NSLog(@"%@ %@", dic, dict);
iOS開發(fā) 調(diào)用手機微微震動效果
1. 導入框架
#import <AudioToolbox/AudioToolbox.h>
2.調(diào)用震動方法,一句代碼解決
// 普通短震,3D Touch 中 Pop 震動反饋
AudioServicesPlaySystemSound(1520);
// 普通短震,3D Touch 中 Peek 震動反饋
AudioServicesPlaySystemSound(1519);
// 連續(xù)三次短震
AudioServicesPlaySystemSound(1521);
UIView設置陰影時需要注意
// 投影
// _progressView.layer.masksToBounds = YES;
_progressView.layer.cornerRadius = 4;
_progressView.backgroundColor = RGB(28, 151, 255);
_progressView.layer.shadowRadius = 5;
_progressView.layer.shadowOpacity = 0.75;
_progressView.layer.shadowOffset = CGSizeMake(0, 3);
_progressView.layer.shadowColor = RGB(52, 121, 249).CGColor;
在通過這樣的方式設置陰影時,必須把父視圖的masksToBounds屬性關掉,因為陰影設置的方式就是加offset給超出視圖部分設置顏色來實現(xiàn)的,一旦不讓子視圖超出,陰影也就看不出了。查看更多解決方案...
iOS UILabel 圓角與陰影共存
_textLabel.layer.backgroundColor = RGB(8, 92, 115).CGColor;
//_textLabel.layer.masksToBounds = YES;
_textLabel.layer.cornerRadius = _textLabel.height/2;
_textLabel.layer.shadowColor = RGB(8, 92, 115).CGColor;
_textLabel.layer.shadowOffset = CGSizeMake(0, 5);
_textLabel.layer.shadowOpacity = 5;
iOS11 Xcode9 [_tableView reloadData] 刷新tableView跳動問題解決辦法
_tableView.estimatedRowHeight = 0;
_tableView.estimatedSectionHeaderHeight = 0;
_tableView.estimatedSectionFooterHeight = 0;
更多關于 iOS11的適配問題,詳見10分鐘適配 iOS 11 & iPhone X
iOS 10.3 UILabel 中劃線失效
// iOS10.3系統(tǒng)的一個Bug,在UILable中含有中文時,中劃線會失效
[attStr addAttribute:NSStrikethroughStyleAttributeName value:@(NSUnderlinePatternSolid | NSUnderlineStyleSingle) range:defaultRange];
// 解決辦法
[attStr addAttribute:NSStrikethroughStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:defaultRange];
[attStr addAttribute:NSBaselineOffsetAttributeName value:@(NSUnderlineStyleSingle) range:defaultRange];
Xcode編譯報 duplicate symbols for architecture arm64
ld: 10 duplicate symbols for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
原因
- 查看自己項目中是否有重復命名的文件
(一般就是這個問題,如果項目中排查后,沒有發(fā)現(xiàn),就該項目所在的文件是否有重復命名的文件,刪除一個就可以了,排查的目標一般都在報錯前面列舉出來了)
- 再查看是否在編輯 #improt 頭文件時候,不小心把 .h 誤寫成 .m
FMDatabaseQueue lead to a deadlock
Assertion failed: (currentSyncQueue != self && "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock"),
function -[FMDatabaseQueue inDatabase:], file /Users/HaobingW/Desktop/Project/OpenSource/YMFMDB/fmdb/FMDatabaseQueue.m, line 178.
運行app,發(fā)現(xiàn)在“assert(currentSyncQueue != self && “inDatabase: was called reentrantly on the same queue, which would lead to a deadlock”);”這句crash掉了。
然后就去先看一下大意:
斷言失敗(Assertion 單元測試的時候會經(jīng)常碰到),inDatabase 在相同的queue隊列中被重復調(diào)用,引發(fā)死鎖。
原因
在使用時,如果在queue里面的block執(zhí)行過程中,又調(diào)用了 indatabase方法,則會檢查 是不是同一個queue,如果是同一個queue會死鎖;原因很簡單:
隊列里面 放了一個block,該block又在 本隊列 后面放了一個 block;
從而:前一個block 里面 調(diào)用了 后一個block,必須等后一個block執(zhí)行完成了,
前一個block才會 出隊列;
而后一個block想要執(zhí)行,則又必須先等 前一個block出隊列;
因此 死鎖!!!!
解決方法
在indatabase的block中,不要再調(diào)用indatabase方法。這個細心一下,多數(shù)時候很容易發(fā)現(xiàn)。
可是有時候因代碼封裝等問題,大家可能很難發(fā)現(xiàn)問題所在。那么就來個暴力斷點吧。在每一個使用indatabase的地方都 NSLog(或者print)一下一個編號。 然后crash的時候,看一下最后是哪兩個相互發(fā)生的影響造成的死鎖。
tabBar 圖片顏色顯示效果與UI切圖不一致
// 創(chuàng)建 tabBar
UITabBarItem *homeBar = [[UITabBarItem alloc] initWithTitle:@"home"
image:[[UIImage imageNamed:@"tab_home_normal"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]
selectedImage:[[UIImage imageNamed:@"tab_home_light"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]];
為了防止系統(tǒng)渲染,必須以UIImageRenderingModeAlwaysOriginal提供圖片。若不以這種形式,那么提供的圖片顯示時會發(fā)生渲染錯誤的問題。
在IOS7中增加創(chuàng)建UITabBarItem的方法的同時新增了 UIImageRenderingMode屬性
typedef NS_ENUM(NSInteger, UIImageRenderingMode) {
UIImageRenderingModeAutomatic, // Use the default rendering mode for the context where the image is used
UIImageRenderingModeAlwaysOriginal, // Always draw the original image, without treating it as a template
UIImageRenderingModeAlwaysTemplate, // Always draw the image as a template image, ignoring its color information } NS_ENUM_AVAILABLE_IOS(7_0);
}
- UIImageRenderingModeAutomatic //根據(jù)圖片的使用環(huán)境和所處的繪圖上下文自動調(diào)整渲染模式。
- UIImageRenderingModeAlwaysOriginal //始終繪制圖片原始狀態(tài),不使用Tint Color。
- UIImageRenderingModeAlwaysTemplate //始終根據(jù)Tint Color繪制圖片,忽略圖片的顏色信息。
使用Copy崩潰的問題
解決方法,實現(xiàn)協(xié)議及方法
@interface CustomClass : NSObject <NSCopying,NSMutableCopying>
-(id)copyWithZone:(NSZone *)zone {
CustomClass *newClass = [[CustomClass alloc]init];
newClass.testString = self.testString;
return newClass;
}
側滑手勢禁用與恢復
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// 禁用側滑返回
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// 恢復側滑返回
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
}
}
locationInView 與 translationInView 的區(qū)別
locationInView: 獲取到的是手指點擊屏幕實時的坐標點
translationInView: 獲取到的是手指移動后相對于手勢起始點的偏移量
CGPoint point = [gesture locationInView:self.view];
CGPoint transPoint = [gesture translationInView:self.view];
NSLog(@"\n%f++++%f\n %f-----%f",point.x,point.y,transPoint.x,transPoint.y);
動畫的暫停和恢復
//暫停動畫.保存當前位置和時間
- (void)pauseSynAnimation {
CFTimeInterval pausedTime = [self.imageView.layer convertTime:CACurrentMediaTime() fromLayer:nil];
self.imageView.layer.timeOffset = pausedTime;
}
//恢復動畫
- (void)resumeSynLayer {
[self.imageView.layer removeAllAnimations];
animation.timeOffset = [self.imageView.layer convertTime:CACurrentMediaTime() fromLayer:nil] - self.imageView.layer.timeOffset;
[self.imageView.layer addAnimation:animation forKey:nil];
}
注意: animation 為動畫對象,是一個全局變量.
UILabel豎直方向排列
UILabel不能設置方向豎直方向排列,但可以通過sizeToFit改變frame來實現(xiàn):
CGRect labelFrame =CGRectMake(20,20,30,150);
UILabel*myLabel= [[UILabel alloc]initWithFrame:labelFrame];
myLabel.backgroundColor=[UIColor orangeColor];
NSString*text =@"今天是個好日子啊今天是個好日子啊今天是個好日子啊";
myLabel.text=text;
myLabel.numberOfLines=0;
[myLabelsizeToFit];
[self.view addSubview:myLabel];
[引自](http://www.lxweimin.com/p/f8405416fc9d)
ScrollView代理方法
// 開始拖拽時調(diào)用
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView;
// 結束拖拽(手指松開)
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
// 結束減速,手指操作,停止?jié)L動
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
// 結束滾動動畫,非手指操作,停止?jié)L動
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView;
// 正在滾動
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;
IOS 關于取消延遲執(zhí)行函數(shù)
// 延遲執(zhí)行函數(shù)
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
// 取消延遲執(zhí)行函數(shù)
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;
關于取消延遲執(zhí)行函數(shù),如果是帶參數(shù)的,那么取消時參數(shù)也要保持一致,否則將會取消失敗.
// 例如:
// 30秒后執(zhí)行延遲函數(shù)
[self performSelector:@selector(SyncTimeOut) withObject:nil afterDelay:30.f];
// 取消執(zhí)行延遲函數(shù)
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(SyncTimeOut) object:nil];
字符串的處理
// 去除字符串中被特殊字符括住的字符
- (NSString *)handleStringWithString:(NSString *)str {
NSMutableString * muStr = [NSMutableString stringWithString:str];
while (1) {
NSRange range = [muStr rangeOfString:@"<"];
NSRange range1 = [muStr rangeOfString:@">"];
if (range.location != NSNotFound) {
NSInteger loc = range.location;
NSInteger len = range1.location - range.location;
[muStr deleteCharactersInRange:NSMakeRange(loc, len + 1)];
}else{
break;
}
}
return muStr;
}
// test
NSString *str = @"abcdef<asljsf>fed<123>cba<hhhhhhh>";
NSString *result = [self handleStringWithString:str];
NSLog(@"%@" , result); //abcdeffedcba
??/// 將數(shù)組轉(zhuǎn)化為json.
+ (NSString *)jsonFromArray:(NSArray *)array
{
NSData *data=[NSJSONSerialization dataWithJSONObject:array
options:NSJSONWritingPrettyPrinted
error:nil];
if (data == nil) {
return nil;
}
NSString *str=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
return str;
}
自定義View手勢解決和tableview點擊沖突
// 1.添加并設置代理 UIGestureRecognizerDelegate
// 2.實現(xiàn)代理方法,加以判斷
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
//判斷如果點擊的是tableView的cell,就把手勢給取消了
if ([NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"]) {
//取消手勢
return NO;
}
//否則手勢存在
return YES;
}
Center
CGRect rect = self.bounds;
CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
scrollView三個基本屬性;
contentSize
蘋果官方文檔的解釋是:The size of the content view.
所以很好理解,contentSize也就是scrollView可滾動的區(qū)域;
contentOffset
蘋果官方文檔的解釋是:The point at which the origin of the content view is offset from the origin of the scroll view.
注意,這里標注的是 point ,是當前顯示的區(qū)域的 origin 相對于整個scrollView的origin的位置;
contentInset
蘋果官方文檔的解釋是:The distance that the content view is inset from the enclosing scroll view.
注意,這里說得是distance,四個值分別是上下左右
為 View 添加背景圖
NSString *path = [[NSBundle mainBundle]pathForResource:@"ImageName"ofType:@"png"];
UIImage *image = [UIImage imageWithContentsOfFile:path];
self.layer.contents = (id)image.CGImage;
局部cell更新
[self.collectionView reloadItemsAtIndexPaths:@[indexPath]];
傳入?yún)?shù)就是要刷新的cell所在的indexPath組成的數(shù)組。
但,reloadItemsAtIndexPath默認會有一個動畫的過程,cell內(nèi)容更新的瞬間會出現(xiàn)原內(nèi)容與新內(nèi)容重疊的情況。那么使用如下方式取消該動畫即可:
[UIView performWithoutAnimation:^{
[self.collectionView reloadItemsAtIndexPaths:@[indexPath]];
}];
iOS --- UICollectionView中使用reloadItemsAtIndexPaths進行局部cell更新
runtime 獲取某個類的方法名
// 以 NSArray 為例
unsigned int count = 0;
Method *methods = class_copyMethodList([NSArray class], &count);
for (int i = 0; i < count; i++) {
Method method = methods[i];
SEL methodSel = method_getName(method);
NSLog(@"%@", NSStringFromSelector(methodSel));
}
為 CollectionView 添加類似 tableView 的點擊改變背景色的效果.
思路:可以通過在 cell 的 contentView 上添加一個 button , 在 button 的點擊事件中實現(xiàn)背景顏色的改變等.至于 push 等操作則可以通過為 cell 添加代理實現(xiàn).如下:
// 1.懶加載添加 touchBtn
- (UIButton *)touchBtn {
if (!_touchBtn) {
_touchBtn = [UIButton buttonWithType:UIButtonTypeCustom];
_touchBtn.frame = CGRectMake(3.f, 3.f, _imageView.width-6.f, _imageView.height-6.f);
[_touchBtn addTarget:self action:@selector(onTouchCell) forControlEvents:UIControlEventTouchUpInside];
}
return _touchBtn;
}
// 2.將其添加到自定義 cell 的contentView
[self.contentView addSubview:self.touchBtn];
// 3.實現(xiàn)button 的點擊事件
- (void)onTouchCell {
_touchBtn.alpha = 0.5f;
_touchBtn.backgroundColor = [UIColor blackColor];
__block typeof(self) weakSelf = self;
[UIView animateWithDuration: 0.4f animations:^{
weakSelf.touchBtn.backgroundColor = [UIColor clearColor];
}];
// 在 button 的點擊方法中通過為 cell 添加代理,傳入 indexPath 實現(xiàn) cell 的 push 等操作.
[weakSelf.cellDelegate onTouchCell:weakSelf.indexPath];
}
// 4.為自定義的 cell 添加代理
@protocol YMSCCollectionCellDelegate <NSObject>
- (void)onTouchCell: (NSIndexPath *)indexPath;
@end
@property(nonatomic, weak) id<YMSCCollectionCellDelegate> cellDelegate;
// 5.回到 collectionViewController 中遵循 YMSCCollectionCellDelegate ,在返回 cell 的方法中為 cell 添加代理,并傳入 indexPath.
@interface YMCommunityFindView ()<UICollectionViewDelegate, UICollectionViewDataSource, YMSCCollectionCellDelegate>
collectionCell.cellDelegate = self;
collectionCell.indexPath = indexPath;
// 6.實現(xiàn)協(xié)議中的方法
#pragma mark - YMSCCollectionCellDelegate
- (void)onTouchCell:(NSIndexPath *)indexPath {
// to do push...
}
當然,除了通過添加 button 外,還可以通過為 collectionCell 上添加單擊手勢,push 的實現(xiàn)依然可以通過設置代理實現(xiàn).
__block typeof(self) weakSelf = self;
[self.contentView addTapGesture:^{
[UIView animateWithDuration:0.1f animations:^{
weakSelf.orderDetail.backgroundColor = RGBA(0, 0, 0, 0.1);
} completion:^(BOOL finished) {
weakSelf.orderDetail.backgroundColor = [UIColor whiteColor];
[weakSelf.cellDelegate onTouchCell:weakSelf.indexPath];
}];
}];
iOS關于BOOL 值的處理(一次很逗比的 bug 事件).
問題描述:服務端定義了一個 bool類型的變量返回true / false ,通過網(wǎng)頁 json 解析也可以明確知道它是一個 bool 類型的值.
"cornerMark": 0,
"friendshipType": 0,
"id": 1040410,
"isApply": 0,
"isZan": false, //服務端定義的 bool 類型變量
"publisher": 103095882,
"publisherName": "Harryoil",
然后客戶端通過網(wǎng)絡請求后拿不到值,Xcode debug 顯示值為 no summary.此外, 更奇怪的是,所有服務端定義的 bool 類型的值,客戶端都顯示為no summary
解決過程:關于此類問題,我在 CocoaChina 上面也遇到了同樣的疑惑,具體可參考這里 ,然而并沒有得到解決辦法.幾經(jīng)折騰,依然無法拿到正常的 bool 值, 這個時候我們開始懷疑是不是自己封裝的請求類出了問題,于是開始入坑查找,結局很明顯,未果.一次偶然的機會,在與后端溝通的過程中了解到,其實是傳參時將"uid" 傳成了"userId"(這兩個字段本質(zhì)上都是用戶的 ID, 只是服務端在改版的過程中換了個字段名而已),導致取不到任何用戶相關的一些信息(比如點贊,評論信息等),總結: 溝通為先,效率至上.
iOS 崩潰日志分析
應用程序通過集成友盟的bug收集SDK, 待 App上線后,如果發(fā)生崩潰,崩潰日志將被保存下來,并上報到友盟后臺,通過登錄后臺就能夠很清楚地知道奔潰日志記錄的錯誤信息. 要分析奔潰日志,首先需要我們保留發(fā)布時編譯出來的.xcarchive
文件,該文件中保存了.dSYM
文件,通常我們可以通過打開XCode->Window->Organizer
在編譯成功的文件上右鍵,就能打開。
通過分析.dSYM
文件定位崩潰位置的原理:Xcode 編譯項目完成后,會生成一份同名的.dSYM
文件,這個文件是保存了16進制函數(shù)地址映射信息的中轉(zhuǎn)文件. 當應用程序 crash 時, iOS 設備會通過 crash 日志文件保存我們每個應用程序出錯函數(shù)的內(nèi)存地址,通過這個函數(shù)地址去查詢 dSYM 文件中對應的函數(shù)名和文件名就能精確定位到發(fā)生奔潰的地方.
什么是 dSYM 文件 ?
Xcode編譯項目后,我們會看到一個同名的 dSYM 文件,dSYM 是保存 16 進制函數(shù)地址映射信息的中轉(zhuǎn)文件,我們調(diào)試的 symbols 都會包含在這個文件中,并且每次編譯項目的時候都會生成一個新的 dSYM 文件,對于每一個發(fā)布版本我們都很有必要保存對應的 Archives 文件.
**dSYM 文件有什么作用 ? **
當我們軟件 release 模式打包或上線后,不會像我們在 Xcode 中那樣直觀的看到用崩潰的錯誤,這個時候我們就需要分析 crash report 文件了,iOS 設備中會有日志文件保存我們每個應用出錯的函數(shù)內(nèi)存地址,通過 Xcode 的 Organizer 可以將 iOS 設備中的 DeviceLog 導出成 crash 文件,這個時候我們就可以通過出錯的函數(shù)地址去查詢 dSYM 文件中程序?qū)暮瘮?shù)名和文件名。大前提是我們需要有軟件版本對應的 dSYM 文件,這也是為什么我們很有必要保存每個發(fā)布版本的 Archives 文件了。
引自dSYM文件分析工具的開發(fā)者answerhuang
附:
手動解析CrashLog之原理篇
手動解析CrashLog之----方法篇
dSYM文件分析工具下載地址 密碼: vtce
iOS8下發(fā)送通知NSNotification崩潰
在StoreAllOrderTableView
類中,數(shù)據(jù)請求成功以后發(fā)送如下通知:
// 發(fā)送刷新紅點的通知
[[NSNotificationCenter defaultCenter] postNotificationName: YM_STORE_ORDER_WAIT_COUNT object:nil];
在StoreTopBarTool
中添加觀察者
//添加觀察者
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshCount:) name: YM_STORE_ORDER_WAIT_COUNT object:nil];
//通知中方法的實現(xiàn)
- (void)refreshCount:(NSNotification *)notification {
something to do...
}
現(xiàn)象: 在 iOS8 系統(tǒng)下由于存在未被釋放的通知,當重復發(fā)送通知的時候發(fā)生崩潰
原因: 沒有在 dealloc 中移除通知
解決辦法: 在接受通知的類或者界面里的delloc方法中移除通知,如下:
在StoreTopBarTool
的dealloc
方法中移除觀察者
- (void)dealloc {
//移除刷新紅點的通知
[[NSNotificationCenter defaultCenter] removeObserver:self name:YM_STORE_ORDER_WAIT_COUNT object:nil];
}
注意:
-
-(void)addObserver
和-(void)removeObserver
方法中的self必須是同一個對象. - 對于此次崩潰,筆者反復驗證,在 iOS9/10下并不會發(fā)生,具體什么原因可參考蘋果其官方文檔中的一段話:iOS9 中,NSNotificationCenter將不再會對一個已經(jīng)被釋放掉的observer發(fā)送通知.
In OS X 10.11 and iOS 9.0 NSNotificationCenter and NSDistributedNotificationCenter
will no longer send notifications to registered observers that may be deallocated.
更多關于 iOSNSNotification 的用法可參考
iOS通知機制
iOS NSNotificationCenter 使用姿勢詳解
記一次Block死循環(huán)事故
筆者在一次開發(fā)過程中,不小心在定時器結束的 Block 中回調(diào)的時候又重置了定時器導致,從而引發(fā)了這次死循環(huán)血案,導致三位測試小伙伴的 iPad/iPhone 直接bong~關機...
self.timeCountView.timeStopBlock = ^{
// 倒計時結束,關閉定時器.
dispatch_source_cancel(_timer);
_timer = nil;
dispatch_async(dispatch_get_main_queue(), ^{
if (self.timeStopBlock) {
self.timeStopBlock();
}
});
}
附: 關于 Block 循環(huán)引用的相關案例可以參考這篇文章iOS容易造成循環(huán)引用的三種場景,就在你我身邊!
Block 塊回調(diào)定位
// Block 重命名
typedef void(^LocationBlock)(NSIndexPath *indexPath);
// .h聲明屬性
@property (nonatomic, strong) LocationBlock locationBlock;
// .m傳值
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:section];
if (self.locationBlock) {
self.locationBlock(indexPath);
}
// 在需要定位的地方回調(diào)Block并實現(xiàn)定位
__weak UICollectionView *weakCollectionView = _collectionView;
_otherRecordListView.locationBlock = ^(NSIndexPath *indexPath){
if (indexPath.item != 0 && weakSelf.itemData.count != 0) {
[weakCollectionView scrollToItemAtIndexPath:indexPath atScrollPosition:(UICollectionViewScrollPositionTop) animated:YES];
}
};
GCD timer定時器
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);
dispatch_source_set_timer(timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒執(zhí)行
dispatch_source_set_event_handler(timer, ^{
//@"倒計時結束,關閉定時器"
dispatch_source_cancel(timer);
dispatch_async(dispatch_get_main_queue(), ^{
});
});
//啟動定時器
dispatch_resume(timer);
跳轉(zhuǎn)至指定視圖控制器
for (UIViewController *vc in self.navigationController.viewControllers) {
if ([vc isKindOfClass:[YourViewController class]]) {
[self.navigationController popToViewController:vc animated:YES];
}
}
由視圖找到其所在的控制器
- (UIViewController *)viewController
{
for (UIView *next = [self superview]; next; next = next.superview) {
UIResponder *nextResponder = [next nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]]) {
return (UIViewController *)nextResponder;
}
}
return nil;
}
for (UIView *next = [self superview]; next; next = next.superview) {
UIResponder *nextResponser = [next nextResponder];
NSLog(@"%@", [nextResponser class]);
if ([nextResponser isKindOfClass:[HomeView class]]) {
HomeView *view = (HomeView *)nextResponser;
break;
}
}
移除指定的視圖控制器
for (UIViewController *vc in self.navigationController.childViewControllers) {
if ([vc isKindOfClass:[YourSelfViewController class]]) {
[vc removeFromParentViewController];
}
}
addSubview和 insertSubView 區(qū)別
A addSubview B 將B直接覆蓋在A的最上層
A insertSubView B AtIndex:2 將B插入到A的子視圖index為2的位置(最底下是0)
A insertSubView B aboveSubview:C 將B插入A并且在A已有的子視圖C的上面
A insertSubView B belowSubview:C 將B插入A并且在A已有的子視圖C的下面
Color轉(zhuǎn)Image
+(UIImage*) createImageWithColor:(UIColor*) color
{
CGRect rect=CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [color CGColor]);
CGContextFillRect(context, rect);
UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return theImage;
}
網(wǎng)絡請求圖片并緩存至本地沙盒
// SD下載
__block typeof(self) weakSelf = self;
[_userHeadBtn sd_setImageWithURL:[NSURL URLWithString:userInfo.avatarUrl] forState:UIControlStateNormal placeholderImage:headImage options:SDWebImageRetryFailed|SDWebImageContinueInBackground completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
if (image) {
[weakSelf saveUserHeadImg:image andUserId:userId];
}
}];
// 緩存至本地沙盒
-(void)saveUserHeadImg:(UIImage *)headImg andUserId:(NSInteger)userId
{
if (userId == 0) {
return;
}
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"%ziavatar.png",userId]];
if (filePath && headImg)
{
[UIImagePNGRepresentation(headImg)writeToFile: filePath atomically:YES]; // 保存成功會返回YES
}
}
檢驗字符串是否是郵箱
+ (BOOL)isValidateEmail:(NSString *)email {
NSString *emailRegex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", emailRegex];
return [emailTest evaluateWithObject:email];
}
MD5加密字符串
+(NSString *)getMD_5_Str:(NSString *)str {
NSMutableString *md5Str = [[NSMutableString alloc]initWithCapacity:CC_MD5_DIGEST_LENGTH*2];
const char *cStr = [str UTF8String];
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5(cStr, (CC_LONG)strlen(cStr), result); // This is the md5 call
for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
[md5Str appendString:[NSString stringWithFormat:@"%02x",result[i]]];
}
return md5Str;
}
開發(fā)工具鏈接
SimPholders_2.2(解壓密碼xclient.info) 密碼:uyws
dSYM文件分析工具 密碼:vtce
自定義貝塞爾曲線值的Mac工具
打包APP時報錯 linker command failed with exit code 1 (use -v to see invocation)
Build Settings中,把 Enable Bitcode 設置為 NO
若顯示**xxx.app已損壞,打不開.你應該將它移到廢紙簍 **
解決辦法:
- 修改系統(tǒng)配置:系統(tǒng)偏好設置... -> 安全性與隱私。修改為任何來源
- 如果沒有這個選項的話(macOS Sierra 10.12),打開終端,行命令:
sudo spctl --master-disable
Xcode相關
// Xcode8注釋快捷鍵失效
解決方法:命令運行 sudo /usr/libexec/xpccachectl ,然后必須重啟電腦后生效
// Xcode模擬器緩存路徑
~/Library/Developer/Xcode/DerivedData/ModuleCache
防止谷歌自動跳轉(zhuǎn)到香港google.com.hk解決辦法
具體做法:在網(wǎng)址欄打入http://www.google.com/ncr,然后回車即可。如果清理了Cookies緩存,還是出現(xiàn)自動跳轉(zhuǎn)現(xiàn)象,重新再在網(wǎng)址欄打入http://www.google.com/ncr輸入,回車就行
ncr 是 no country redirection,是強制不跳轉(zhuǎn)命令
git push 時報以下信息,使用命令后依然無法解決:
fatal: The current branch master has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin master
解決辦法 : 如果不想重新創(chuàng)建遠程倉庫再克隆,或者初始化本地倉庫,可以使用下面命令:
git push -u origin master
,其中origin 表示遠程倉庫名稱,master是遠程倉庫的push目標分支。-u (推測為update縮寫_~) 表示本地分支將建立對遠程倉庫目標分支的檢測,如果遠程倉庫目標分支不存在,將新建分支再push;如果存在,將進行push更新
Instead of creating a new repository on Github, cloning that, or reinitializing your local repository, the following command would have been sufficient:
git push -u origin master
origin stands for the remote name (default is origin), and master is the branch you want to push, in your case it's master, otherwise you would have to change that in the command.
-u means, that your local branch will be setup to track the new created master branch on the origin repository (the master on the origin will be the upstream branch of your local branch). If the branch master doesn't exist on the remote repository, it will be created, otherwise it will be updated (the -u works no matter if it exists or not).
然后,驗證此方法,確實可行!
引自
npm執(zhí)行命令時出錯
npm ERR! Error: EACCES: permission denied, access '/usr/local/lib/node_modules/gitbook/node_modules/abab'
npm ERR! { [Error: EACCES: permission denied, access '/usr/local/lib/node_modules/gitbook/node_modules/abab']
npm ERR! stack:
npm ERR! 'Error: EACCES: permission denied, access \'/usr/local/lib/node_modules/gitbook/node_modules/abab\'',
npm ERR! errno: -13,
npm ERR! code: 'EACCES',
npm ERR! syscall: 'access',
npm ERR! path: '/usr/local/lib/node_modules/gitbook/node_modules/abab' }
npm ERR!
npm ERR! The operation was rejected by your operating system.
npm ERR! It is likely you do not have the permissions to access this file as the current user
npm ERR!
npm ERR! If you believe this might be a permissions issue, please double-check the
npm ERR! permissions of the file and its containing directories, or try running
npm ERR! the command again as root/Administrator (though this is not recommended).