本文对应github地址Tip001,如果由于github调整导致资源找不到,请访问github
-
防止collection短时间点击多个item
// �其实并不太友好(慎用) [collectionView setUserInteractionEnabled:NO]; [collectionView performSelector:@selector(setUserInteractionEnabled:) withObject:[NSNumber numberWithBool:YES] afterDelay:0.05f];
-
防止短时间点击两个按钮
[view setExclusiveTouch:YES]; // 一个一个设置太麻烦了,可以全局设置 // [[UIView appearance] setExclusiveTouch:YES]; // 或者只设置button // [[UIButton appearance] setExclusiveTouch:YES];
-
防止按钮短时间多次点击
- (void)todoSomething:(id)sender { // 在这里做真正的事情 } - (void)handleButtonClick:(UIButton *)sender { // 先将未到时间执行前的任务取消。 [[self class] cancelPreviousPerformRequestsWithTarget:self selector:@selector(todoSomething:) object:sender]; // 取消全部可能存在的延迟执行 // [[self class] cancelPreviousPerformRequestsWithTarget:self]; [self performSelector:@selector(todoSomething:) withObject:sender afterDelay:0.2f]; }
-
比如在 -viewDidAppear: 打断点,在控制台输入
po [self.view.superview recursiveDescription]
- 当视图第一次显示的时候会被调用
- 当这个视图显示到屏幕上了,点击按钮
- 添加子视图也会调用这个方法
- 当本视图的大小发生改变的时候是会调用的
- 当子视图的frame发生改变的时候是会调用的
- 当删除子视图的时候是会调用的
- layoutIfNeed
-
只针对首/尾
// 去掉首尾存在的 , NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString: @","]; NSString *newString = [trimString stringByTrimmingCharactersInSet:set];
-
平移、旋转、缩放、复原
// 平移按钮 CGAffineTransform transForm = self.buttonView.transform; self.buttonView.transform = CGAffineTransformTranslate(transForm, 10, 0); // 旋转按钮 CGAffineTransform transForm = self.buttonView.transform; self.buttonView.transform = CGAffineTransformRotate(transForm, M_PI_4); // 缩放按钮 self.buttonView.transform = CGAffineTransformScale(transForm, 1.2, 1.2); // 初始化复位 self.buttonView.transform = CGAffineTransformIdentity;
-
第一步: viewDidLayoutSubviews 添加
- (void)viewDidLayoutSubviews { if ([self.tableView respondsToSelector:@selector(setSeparatorInset:)]) { [self.tableView setSeparatorInset:UIEdgeInsetsZero]; } if ([self.tableView respondsToSelector:@selector(setLayoutMargins:)]) { [self.tableView setLayoutMargins:UIEdgeInsetsZero]; } }
-
第二步: 重写willDisplayCell方法
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { if ([cell respondsToSelector:@selector(setSeparatorInset:)]) { [cell setSeparatorInset:UIEdgeInsetsZero]; } if ([cell respondsToSelector:@selector(setLayoutMargins:)]) { [cell setLayoutMargins:UIEdgeInsetsZero]; } }
-
两个宏定义
#define TICK CFAbsoluteTime start = CFAbsoluteTimeGetCurrent(); #define TOCK NSLog(@"Time: %f", CFAbsoluteTimeGetCurrent() - start)
-
随机颜色
#define RANDOM_COLOR [UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1]
-
颜色(RGBA)
#define DDYRGBA(r, g, b, a) [UIColor colorWithRed:(r)/255.0f green:(g)/255.0f blue:(b)/255.0f alpha:(a)]
-
宏定义(不再建议用UIAlertView,推荐UIAlertController)
#define Alert(_S_, ...) [[[UIAlertView alloc] initWithTitle:@"提示" message:[NSString stringWithFormat:(_S_), ##__VA_ARGS__] delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil] show]
-
利用相应keyPath
NSArray *array = [NSArray arrayWithObjects:@"2.0", @"2.3", @"3.0", @"4.0", @"10", nil]; CGFloat sum = [[array valueForKeyPath:@"@sum.floatValue"] floatValue]; CGFloat avg = [[array valueForKeyPath:@"@avg.floatValue"] floatValue]; CGFloat max =[[array valueForKeyPath:@"@max.floatValue"] floatValue]; CGFloat min =[[array valueForKeyPath:@"@min.floatValue"] floatValue]; NSLog(@"%f\n%f\n%f\n%f",sum,avg,max,min);
-
转成富文本(attributedText)
+ (__kindof NSMutableAttributedString *_Nonnull)changeTotalString:(NSString *_Nonnull)totalString subStringArray:(NSArray *_Nonnull)subStringArray subStringColor:(UIColor *_Nonnull)color andFont:(UIFont *_Nonnull)font { NSMutableAttributedString *attributedStr = [[NSMutableAttributedString alloc] initWithString:totalString]; for (NSString *rangeStr in subStringArray) { NSRange range = [totalString rangeOfString:rangeStr options:NSBackwardsSearch]; [attributedStr addAttribute:NSForegroundColorAttributeName value:color range:range]; [attributedStr addAttribute:NSFontAttributeName value:font range:range]; } return attributedStr; }
-
需要引入AVFoundation
// 1.获取音效资源的路径 NSString *path = [[NSBundle mainBundle]pathForResource:@"sound" ofType:@"wav"]; // 2.将路径转化为url NSURL *tempUrl = [NSURL fileURLWithPath:path]; // 3.用转化成的url创建一个播放器 NSError *error = nil; AVAudioPlayer *player = [[AVAudioPlayer alloc]initWithContentsOfURL:tempUrl error:&error]; // 4.播放 [player play];
-
NSTimer计算的时间并不精确
-
NSTimer需要添加到runLoop运行才会执行,但是这个runLoop的线程必须是已经开启。
-
NSTimer会对它的tagert进行retain,我们必须使用intvailte停止。target如果是self甚至weakSelf(控制器自己),那么VC的retainCount+1,如果你不释放NSTimer,那么你的VC就不会dealloc了,内存泄漏了。
-
scheduledTimerWithTimeInterval.. 默认已经添加到runloop,timerWithTimeInterval..需要手动添加
-
可以用NSProxy转发或者block形式避免存在的内存泄漏
#import "NSTimer+DDYExtension.h" #import <objc/runtime.h> @implementation NSTimer (DDYExtension) + (NSTimer *)ddy_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block { if (@available(iOS 10.0, *)) { return [self scheduledTimerWithTimeInterval:interval repeats:repeats block:^(NSTimer * _Nonnull timer) { block(timer); }]; } else { return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(ddy_timerInvoke:) userInfo:[block copy] repeats:repeats]; } } + (void)ddy_timerInvoke:(NSTimer *)timer { void (^block)(NSTimer *timer) = timer.userInfo; if (block) block(timer); } @end
- 下载开发包
- 强制退出Xcode(必须退出干净)
- 前往"/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport"粘贴解压缩后的文件(以自己实际路径实际名称)
- 如果找不到相应真机调试包可以尝试修改文件名(如iOS13.2 -> iOS13.3)
-
如果层级改变可适当调整
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar { searchBar.showsCancelButton = YES; for(UIView *view in [[[searchBar subviews] objectAtIndex:0] subviews]) { if([view isKindOfClass:[NSClassFromString(@"UINavigationButton") class]]) { UIButton * cancel =(UIButton *)view; [cancel setTitle:@" 取消" forState:UIControlStateNormal]; } } return YES; }
-
drawViewHierarchyInRect可截取导航(可以用window截图)
- (UIImage *)ddy_SnapshotImage { UIGraphicsBeginImageContext(self.bounds.size); if([self respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]){ [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:NO]; } else{ [self.layer renderInContext:UIGraphicsGetCurrentContext()]; } UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); NSData *imageData = UIImageJPEGRepresentation(image, 0.6); image = [UIImage imageWithData:imageData]; return image; }
我只是想要截个屏 附
[view snapshotViewAfterScreenUpdates:YES];
-
不知道现在还能不能用
import <objc/runtime.h> Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace"); NSObject* workspace = [LSApplicationWorkspace_class performSelector:@selector(defaultWorkspace)];NSLog(@"apps: %@", [workspace performSelector:@selector(allApplications)]);
-
特征码 JPEG,JPG: 0xFF 0xD8 2字节 JPG是JPEG的缩写
BMP: 0x42 0x4D 2字节
PNG: 0x89 0x50 0x4E 0x47 0x0D 0x0A 0x1A 0x0A 8字节
GIF: 0x47 0x49 0x46 0x38 0x39/ 0x37 0x61 GIF有87a和89a两种格式// 通过图片Data数据第一个字节 来获取图片扩展名 - (NSString *)contentTypeForImageData:(NSData *)data { uint8_t c; [data getBytes:&c length:1]; switch (c) { case 0xFF: return @"jpeg"; case 0x89: return @"png"; case 0x47: case 0x37: return @"gif"; case 0x49: case 0x4D: return @"tiff"; case 0x42: return @"bmp"; case 0x52: if ([data length] < 12) { return nil; } NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding]; if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) { return @"webp"; } return nil; } return nil; }
-
其实图片数据的第一个字节是固定的,一种类型的图片第一个字节就是它的标识, 我们来调用一下这个方法:
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:picPathStr]]; NSString *string = [self contentTypeForImageData:data];
- 1.修复Bug或者优化功能, 我们只修改叠加第三位数字, 其他不变,如1.0.1
- 2.有了新的需求, 在原基础上增加新功能, 版本号变为1.1.0, 清空第三位为0, 叠加第二位
- 3.需求功能大改, 更新量非常大, 那我们的版本号变为2.0.0, 需要叠加第一位, 清空其他为0
参考
- 权限管理总结库(参考可按需修改或CV)DDYAuthManager
- 参考的文章系统权限介绍
-
谨慎使用
[[UIApplication sharedApplication] performSelector:@selector(suspend)]; // suspend:暂停;
-
URL编码
// 编码分为不同标准 NSString *URLStr = @"http://static.tripbe.com/videofiles/视频/我的自拍视频.mp4"; // NSString *path = (__bridge_transfer NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL, (__bridge CFStringRef)URLStr, CFSTR(""), CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)); NSString* encodedURLString = [URLStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
-
如果某些特殊符号
// 我们一般用这个方法处理stringByAddingPercentEscapesUsingEncoding但是这个方法好想不会处理/和&这种特殊符号,这种情况就需要用下边这个方法处理 @implementation NSString (NSString_Extended) - (NSString *)urlencode { NSMutableString *output = [NSMutableString string]; const unsigned char *source = (const unsigned char *)[self UTF8String]; int sourceLen = strlen((const char *)source); for (int i = 0; i < sourceLen; ++i) { const unsigned char thisChar = source[i]; if (thisChar == ' '){ [output appendString:@"+"]; } else if (thisChar == '.' || thisChar == '-' || thisChar == '_' || thisChar == '~' || (thisChar >= 'a' && thisChar <= 'z') || (thisChar >= 'A' && thisChar <= 'Z') || (thisChar >= '0' && thisChar <= '9')) { [output appendFormat:@"%c", thisChar]; } else { [output appendFormat:@"%%%02X", thisChar]; } } return output; }
-
使用UIImageView
-
[view setBackGroundColor:[UIColor colorWithPatternImage:img];
-
CALayer
view.layer.contents = (__bridge id)image.CGImage; // 设置显示的图片范围,四个值在0-1之间,对应的为x, y, w, h view.layer.contentsCenter = CGRectMake(0.25,0.25,0.5,0.5);
-
见上 22
// 没必要添加UIImageView,直接在layer上画 self.view.layer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"beijing"].CGImage)
-
也可以自己设定路径
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view2.bounds byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(10, 10)]; CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init]; maskLayer.frame = view2.bounds; maskLayer.path = maskPath.CGPath; view2.layer.mask = maskLayer; //其中, byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight /* 指定了需要成为圆角的角,用“|”组合。该参数是UIRectCorner类型的,可选的值有: * UIRectCornerTopLeft * UIRectCornerTopRight * UIRectCornerBottomLeft * UIRectCornerBottomRight * UIRectCornerAllCorners */
-
一般用于视频播放
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
-
删库跑路,谨慎用之
# 清空废纸篓 sudo rm -rf ~/.Trash/ # 删除Xcode 编译的数据 rm -rf ~/Library/Developer/Xcode/DerivedData/ # 代码不提示删除 rm ~/Library/Caches/com.apple.dt.Xcode # pod search 搜不出真的存在的库 zsl 用自己的 rm ~/Library/Caches/CocoaPods/search_index.json
-
能自定义的最好自定义
tableView.tintColor = [UIColor redColor];
-
iOS13前,
[textField setValue:color forKeyPath:@"_placeholderLabel.textColor"];
iOS13后会crash,可用以下姿势。
-
姿势一:采用富文本形式
_textField.attributedPlaceholder = [[NSMutableAttributedString alloc] initWithString:placeholder attributes:@{NSForegroundColorAttributeName : color}];
-
姿势二:new方式创建一个新label(太low不建议用)
// 必须new创建,如果alloc-init创建还是crash(两种方式差别自行google,不是BD) UILabel * placeholderLabel = [UILabel new]; placeholderLabel.text = @"666"; placeholderLabel.textColor = [UIColor blueColor]; [_textField setValue: placeholderLabel forKey:@"_placeholderLabel"];//new创建这里并没有crash
-
姿势三:Runtime
Ivar ivar = class_getInstanceVariable([UITextField class], "_placeholderLabel"); UILabel *placeholderLabel = object_getIvar(_textField, ivar); placeholderLabel.textColor = color;
-
方法一
// waitUntilDone:是否线程任务完成执行 [self performSelectorOnMainThread:@selector(updateImage:) withObject:data waitUntilDone:YES];
-
方法二
dispatch_async(dispatch_get_main_queue(), ^{ //更新UI的代码,不用主线程中调用 });
-
方法三
// 使用NSOperationQueue // 第一种: [[NSOperationQueuemainQueue]addOperationWithBlock:^{ // do something }]; // 第二种:将工作内容封装在NSOperation中,然后 // [[NSOperationQueue mainQueue] addOperation:myOperation];
-
方法一
NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier]; [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
-
方法二
- (void)resetDefaults { NSUserDefaults * defs = [NSUserDefaults standardUserDefaults]; NSDictionary * dict = [defs dictionaryRepresentation]; for (id key in dict) { [defs removeObjectForKey:key]; } [defs synchronize]; }
-
注意有时候0就不生效了
// 只设一个可能仍出现20的高度,也不可设置0 // Header底部间距 - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { return 40;//section头部高度 } // footer底部间距 - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { return 0.001; }
-
详见多线程部分
// 后台执行: dispatch_async(dispatch_get_global_queue(0, 0), ^{ // something }); // 主线程执行: dispatch_async(dispatch_get_main_queue(), ^{ // something }); // 一次性执行: static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // code to be executed once }); // 延迟2秒执行: dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // code to be executed after a specified delay });
-
当我们用tabBarController时,若已经到其中一个TabBar的子页,又要跳转到某一个一级的页面时,如果这样写,导致底部出现黑边,引起tabbar消失的bug
[self.navigationController popToRootViewControllerAnimated:YES]; ((AppDelegate *)AppDelegateInstance).tabBarController.selectedIndex = 2;
-
解决方法一:删除动画
[self.navigationController popToRootViewControllerAnimated:NO]; ((AppDelegate *)AppDelegateInstance).tabBarController.selectedIndex = 2;
-
解决方法二:延迟执行另一个系统操作
[self.navigationController popToRootViewControllerAnimated:NO]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ ((AppDelegate *)AppDelegateInstance).tabBarController.selectedIndex = 2; });
-
UIWebView(即将废除)
titleLabel.text = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
-
WKWebView
- (void)viewDidLoad { [super viewDidLoad]; [self.wkWebview addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqualToString:@"title"]) { if (object == self.wkWebView) { self.title = self.wkWebView.title; } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } - (void)dealloc{ [self.wkWebView removeObserver:self forKeyPath:@"title"]; }
- UIWebView(即将废除)
-
AppDelegate 中 - (BOOL)application: didFinishLaunchingWithOptions:
NSDictionary *dict = @{@"UserAgent":@"appwebkit"}; [[NSUserDefaults standardUserDefaults] registerDefaults:dict];
-
应用
// 获取webView userAgent NSMutableString *userAgent = [NSMutableString stringWithString:[[UIWebView new] stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"]]; // 更改UserAgent NSString *newUagent = [NSString stringWithFormat:@"%@ WMall/%@", userAgent, [SystemInfo appShortVersion]]; NSDictionary *dictionnary = [[NSDictionary alloc] initWithObjectsAndKeys:newUagent, @"UserAgent", nil]; [[NSUserDefaults standardUserDefaults] registerDefaults:dictionnary];
-
考虑到兼容性,只对某些webview实施自定义UA。[这几个方法一定要是静态方法,要不然设置UA不生效]
NSString* defaultUserAgent = nil; #pragma mark 获取默认的UA,用于恢复UA + (void)initialize { if (self == [WebViewController class]) { defaultUserAgent = [[UIWebView new] stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"]; } } #pragma mark 在默认UA后追加自定义UA + (void)registeCustomizeWebViewUserAgent { UIDevice *device = [UIDevice currentDevice]; NSString *iOSName = [device systemName]; NSString *iOSVersion = [device systemVersion]; NSString *customizeUserAgent = [NSString stringWithFormat:@"xxxxxMobile/%@ (Platform/%@; %@/%@)", APP_SHORT_VERSION, @"iPad", iOSName, iOSVersion]; NSString *webViewUserAgent = [[UIWebView new] stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"]; customizeUserAgent = [webViewUserAgent stringByAppendingFormat:@" %@", customizeUserAgent]; if (customizeUserAgent) { [[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"UserAgent": customizeUserAgent}]; } } - (void)dealloc { /*由于自定义的userAgent无法播放webview的视频,所以。当webview销毁的时候,重置一下userAgent*/ [[self class] recoverDefaultUserAgent]; } + (void) recoverDefaultUserAgent { if (defaultUserAgent) { [[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"UserAgent": defaultUserAgent}]; } }
-
WKWebView
if (@available(iOS 12.0, *)) NSString *baseAgent = [self.myWebView valueForKey: @"applicationNameForUserAgent"]; NSString *userAgent = [NSString stringWithFormat:@"%@ DDY", baseAgent]; [self.wkWebView setValue:userAgent forKey:@"applicationNameForUserAgent"]; } @ddy_weakify(self); [self.wkWebView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(NSString *oldAgent, NSError *error) { if ([oldAgent rangeOfString:@"DDY"].location != NSNotFound) { return; } NSString *newAgent = [NSString stringWithFormat: @"%@ %@", oldAgent, @"DDY"]; NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:newAgent, @"UserAgent", nil]; [[NSUserDefaults standardUserDefaults] registerDefaults:dictionary]; [[NSUserDefaults standardUserDefaults] synchronize]; if (@available(iOS 9.0, *)) { [weakSelf.myWebView setCustomUserAgent:newAgent]; } else { [weakSelf.myWebView setValue:newAgent forKey:@"applicationNameForUserAgent"]; } }];
-
利用CoreFoundation
- (NSString *)Charactor:(NSString *)aString getFirstCharactor:(BOOL)isGetFirst { // 转成了可变字符串 NSMutableString *str = [NSMutableString stringWithString:aString]; // 先转换为带声调的拼音 CFStringTransform((CFMutableStringRef)str,NULL, kCFStringTransformMandarinLatin,NO); // 再转换为不带声调的拼音 CFStringTransform((CFMutableStringRef)str,NULL, kCFStringTransformMandarinLatin,NO); CFStringTransform((CFMutableStringRef)str, NULL, kCFStringTransformStripDiacritics, NO); NSString *pinYin = [str capitalizedString]; //转化为大写拼音 if(isGetFirst) { // 获取并返回首字母 return [pinYin substringToIndex:1]; } else { return pinYin; } }
-
优化release,防止打印
// 处于开发阶段 #ifdef DEBUG #define DDYLog(...) NSLog(__VA_ARGS__) //#define DDYInfoLog(fmt, ...) fprintf(stderr,"\nfunction:%s line:%d content:%s\n", __FUNCTION__, __LINE__, [NSString stringWithFormat:fmt, ##__VA_ARGS__] UTF8String]); #define DDYInfoLog(fmt, ...) {\ NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];\ [dateFormatter setDateStyle:NSDateFormatterMediumStyle];\ [dateFormatter setTimeStyle:NSDateFormatterShortStyle];\ [dateFormatter setDateFormat:@"HH:mm:ss:SSS"]; \ NSString *str = [dateFormatter stringFromDate:[NSDate date]];\ fprintf(stderr,"[%s %s %d]: %s\n",[str UTF8String], [[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, [[NSString stringWithFormat:fmt, ##__VA_ARGS__] UTF8String]);\ } //#define DDYInfoLog(fmt, ...) NSLog((@"\n[fileName:%s]\n[methodName:%s]\n[lineNumber:%d]\n" fmt),__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) // 处于发布阶段 #else #define DDYLog(...) #define DDYInfoLog(...) #endif
用fprintf 可避免NSLog打印不全问题
CocoaPods教程 详见github内 CocoaPods
-
Too many arguments to function call ,expected 0,have3
Project --> Build Settings --> 搜索objc --> ENABLE_STRICT_OBJC_MSGSEND --> 设置NO
-
只用基本
void UncaughtExceptionHandler(NSException *exception) { NSArray *callStackSymbols = [exception callStackSymbols]; NSString *callStackSymbolStr = [callStackSymbols componentsJoinedByString:@"\n"]; NSString *reason = [exception reason]; NSString *name = [exception name]; DDYInfoLog(@"异常名称:%@\n异常原因:%@\n堆栈标志:%@",name, reason, callStackSymbolStr); } // - application: didFinishLaunchingWithOptions: NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
-
深层次(慎用[影响日志上报工具])
#import "DDYSafeHeader.h" #include <libkern/OSAtomic.h> #include <execinfo.h> // !!!: 崩溃预防 // 崩溃时发送通知 NSString *const DDYSafeNotification = @"DDYSafeNotification"; // 返回nil来防止崩溃 NSString *const DDYSafeReturnNil = @"DDYSafe return nil to avoid crash"; // 移除nil来防止崩溃 NSString *const DDYSafeRemoveNil = @"DDYSafe remove nil to avoid crash"; // 忽略操作来防止崩溃 NSString *const DDYSafeIgnore = @"DDYSafe ignore the operation to avoid crash"; // 消息转发来防止崩溃 NSString *const DDYSafeForward = @"DDYSafe forward invocation to avoid crash"; // 返回主线程防止崩溃 NSString *const DDYSafeMainThread = @"DDYSafe return main thread to avoid crash"; // 崩溃堆栈信息 NSString *const DDYSafeCrashStack = @"DDYSafeCrashStack"; // 异常信息 NSString *const DDYSafeException = @"DDYSafeException"; // 解决信息 NSString *const DDYSafeSolution = @"DDYSafeSolution"; // !!!: 崩溃发生 NSString * const DDYExceptionName = @"DDYExceptionName"; NSString * const DDYSignalKey = @"DDYSignalKey"; NSString * const DDYAddressesKey = @"DDYAddressesKey"; volatile int32_t UncaughtExceptionCount = 0; const int32_t UncaughtExceptionMaximum = 100; @implementation DDYSafeHeader #pragma mark - 安全守护,旨在预防 + (void)ddy_SafeEffective { // !!!:防止多次调用 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [NSObject ddy_SafeEffect]; [NSArray ddy_SafeEffect]; [NSMutableArray ddy_SafeEffect]; [NSDictionary ddy_SafeEffect]; [NSMutableDictionary ddy_SafeEffect]; [NSString ddy_SafeEffect]; [NSMutableString ddy_SafeEffect]; [NSAttributedString ddy_SafeEffect]; [NSMutableAttributedString ddy_SafeEffect]; [NSNull ddy_SafeEffect]; [UIView ddy_SafeEffect]; [UITableView ddy_SafeEffect]; }); } #pragma mark - 崩溃发生,防止闪退 + (void)ddy_HandleUncaughtException { NSLog(@"请在断开数据线连接(当然也不允许无线调试)状态测试"); NSSetUncaughtExceptionHandler(&HandleException); signal(SIGABRT, SignalHandler); signal(SIGILL, SignalHandler); signal(SIGSEGV, SignalHandler); signal(SIGFPE, SignalHandler); signal(SIGBUS, SignalHandler); signal(SIGPIPE, SignalHandler); } #pragma mark 处理异常 void HandleException(NSException *exception) { saveOrUploadException(exception); int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount); // 如果太多不用处理 if (exceptionCount > UncaughtExceptionMaximum) { return; } CFRunLoopRef runLoop = CFRunLoopGetCurrent(); CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop); NSArray *allModeArray = (__bridge NSArray *)allModes; CFRelease(allModes); while (1) { for (NSString *mode in allModeArray) { CFRunLoopRunInMode((CFStringRef)mode, 0.001, false); } } } #pragma mark 处理signal报错 void SignalHandler(int signal) { int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount); if (exceptionCount > UncaughtExceptionMaximum) { return; } NSString* description = nil; switch (signal) { case SIGABRT: description = [NSString stringWithFormat:@"Signal SIGABRT was raised!\n"]; break; case SIGILL: description = [NSString stringWithFormat:@"Signal SIGILL was raised!\n"]; break; case SIGSEGV: description = [NSString stringWithFormat:@"Signal SIGSEGV was raised!\n"]; break; case SIGFPE: description = [NSString stringWithFormat:@"Signal SIGFPE was raised!\n"]; break; case SIGBUS: description = [NSString stringWithFormat:@"Signal SIGBUS was raised!\n"]; break; case SIGPIPE: description = [NSString stringWithFormat:@"Signal SIGPIPE was raised!\n"]; break; default: description = [NSString stringWithFormat:@"Signal %d was raised!",signal]; } NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; [userInfo setObject:getAddressKey() forKey:DDYAddressesKey]; [userInfo setObject:[NSNumber numberWithInt:signal] forKey:DDYSignalKey]; HandleException([NSException exceptionWithName:DDYExceptionName reason:description userInfo:userInfo]); } NSArray *getAddressKey() { // 指针列表 void* callstack[128]; int frames = backtrace(callstack, 128); char **strs = backtrace_symbols(callstack, frames); NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames]; for (int i = 0; i < frames; i++) { [backtrace addObject:[NSString stringWithUTF8String:strs[i]]]; } free(strs); return backtrace; } #pragma mark 保存或者上传日志 void saveOrUploadException() { } @end
-
见github BuildConfiguration
-
界限更鲜明
self.imageView = ({ UIImageView *imageView = [[UIImageView alloc] init]; imageView.image = [UIImage imageNamed:@"12345"]; imageView.frame = CGRectMake(0, 0, 100, 100); [self.view addSubview:imageView]; imageView; });
-
能不用最好不用,能不在View中跳转最好别在View中跳转
+ (UIViewController *)findCurrentResponderViewController{ UIViewController *currentVC = nil; UIWindow *window = [[UIApplication sharedApplication] keyWindow]; if (window.windowLevel != UIWindowLevelNormal) { NSArray *windows = [[UIApplication sharedApplication] windows]; for(UIWindow *tmpWin in windows) { if (tmpWin.windowLevel == UIWindowLevelNormal) { window = tmpWin; break; } } } UIView *frontView = [[window subviews] objectAtIndex:0]; id nextResponder = [frontView nextResponder]; if ([nextResponder isKindOfClass:[UIViewController class]]) { currentVC = nextResponder; } else { UIViewController *topVC = window.rootViewController.presentedViewController; if (topVC) { currentVC = topVC; } else { currentVC = window.rootViewController; } } return currentVC; }
-
view中跳转
NASkillAptitudeVC *vc = [[NASkillAptitudeVC alloc] init]; [[self currentViewController].navigationController pushViewController:vc animated:YES]; - (UIViewController *)currentViewController { UIResponder *next = self.nextResponder; do { //判断响应者是否为视图控制器 if ([next isKindOfClass:[UIViewController class]]) { return (UIViewController *)next; } next = next.nextResponder; } while (next != nil); return nil; }
-
利用layer
CAShapeLayer *borderLayer = [CAShapeLayer layer]; borderLayer.bounds = self.picView.frame; borderLayer.position = self.picView.center; borderLayer.path = [UIBezierPath bezierPathWithRoundedRect:borderLayer.bounds cornerRadius:3].CGPath; borderLayer.lineWidth = 1; borderLayer.lineDashPattern = @[@4, @2]; borderLayer.fillColor = [UIColor clearColor].CGColor; borderLayer.strokeColor = [UIColor grayColor].CGColor; [self.picView.layer addSublayer:borderLayer];
-
三种方式对比
-
姿势一
- (void)setFrame:(CGRect)frame { // 整体向下 移动10 frame.origin.y += 10; // 间隔10 frame.size.height -= 10; [super setFrame:frame]; }
-
姿势二
设置背景视图,约束距离
-
酌情选择里面代码
- (void)deleteTopBlank { self.edgesForExtendedLayout = UIRectEdgeNone; self.navigationController.navigationBar.translucent = NO; self.automaticallyAdjustsScrollViewInsets = NO; self.extendedLayoutIncludesOpaqueBars = YES }
参考美团技术博客