Skip to content

Files

Latest commit

7913242 · Dec 26, 2019

History

History
1091 lines (864 loc) · 35 KB

Tip001.md

File metadata and controls

1091 lines (864 loc) · 35 KB

本文对应github地址Tip001,如果由于github调整导致资源找不到,请访问github

0. 防止暴力点击

  • 防止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];
     }
    

1. 打印View根视图所有子视图

  • 比如在 -viewDidAppear: 打断点,在控制台输入

     po [self.view.superview recursiveDescription]
    

2. layoutSubviews调用时机

  • 当视图第一次显示的时候会被调用
  • 当这个视图显示到屏幕上了,点击按钮
  • 添加子视图也会调用这个方法
  • 当本视图的大小发生改变的时候是会调用的
  • 当子视图的frame发生改变的时候是会调用的
  • 当删除子视图的时候是会调用的
  • layoutIfNeed

3. NSString去掉首尾某些字符

  • 只针对首/尾

     // 去掉首尾存在的 , 
     NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString: @","];
     NSString *newString = [trimString stringByTrimmingCharactersInSet:set];
    

4. CGAffineTransform属性

  • 平移、旋转、缩放、复原

     // 平移按钮
     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;
    

5. 去掉分割线多余15像素(同理设置其他间距)

  • 第一步: 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];    
        	}
     }
    

6. 计算方法耗时时间间隔

  • 两个宏定义

     #define TICK CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
     #define TOCK NSLog(@"Time: %f", CFAbsoluteTimeGetCurrent() - start)
    

7. Color颜色宏定义

  • 随机颜色

     #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)]
    

8. UIAlertView

  • 宏定义(不再建议用UIAlertView,推荐UIAlertController)

     #define Alert(_S_, ...) [[[UIAlertView alloc] initWithTitle:@"提示" message:[NSString stringWithFormat:(_S_), ##__VA_ARGS__] delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil] show]
    

9. NSArray 快速求总和 最大值 最小值 和 平均值

  • 利用相应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);
    

10. 修改Label中不同文字颜色

  • 转成富文本(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;
     }
    

11. 播放声音

  • 需要引入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];
    

12. NSTimer 

  • 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
    

13. 真机调试包[Could not find Developer Disk Image]

  1. 下载开发包
  2. 强制退出Xcode(必须退出干净)
  3. 前往"/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport"粘贴解压缩后的文件(以自己实际路径实际名称)
  4. 如果找不到相应真机调试包可以尝试修改文件名(如iOS13.2 -> iOS13.3)

14. UISearchBar 中cancel改成取消

  • 如果层级改变可适当调整

     - (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;
     }
    

15.截图功能

  • 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];

16. 获取设备上所有app的bundle id

  • 不知道现在还能不能用

     import <objc/runtime.h>
     
     Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
     NSObject* workspace = [LSApplicationWorkspace_class performSelector:@selector(defaultWorkspace)];NSLog(@"apps: %@", [workspace performSelector:@selector(allApplications)]);
    

17. iOS 判断图片类型

  • 特征码 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]; 
    

    参考

18. App迭代开发版本号的规则介绍

  • 1.修复Bug或者优化功能, 我们只修改叠加第三位数字, 其他不变,如1.0.1
  • 2.有了新的需求, 在原基础上增加新功能, 版本号变为1.1.0, 清空第三位为0, 叠加第二位
  • 3.需求功能大改, 更新量非常大, 那我们的版本号变为2.0.0, 需要叠加第一位, 清空其他为0
    参考

19. iOS 系统权限介绍

20. 私有API实现进入后台(相当于按home键)

  • 谨慎使用

     [[UIApplication sharedApplication] performSelector:@selector(suspend)];  // suspend:暂停;
    

21. 带有中文的URL处理

  • 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但是这个方法好想不会处理/和&amp;这种特殊符号,这种情况就需要用下边这个方法处理
     @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 &lt; sourceLen; ++i) {
             const unsigned char thisChar = source[i];
             if (thisChar == ' '){
                 [output appendString:@"+"];
             } else if (thisChar == '.' || thisChar == '-' || thisChar == '_' || thisChar == '~' || 
                        (thisChar &gt;= 'a' &amp;&amp; thisChar &lt;= 'z') ||
                        (thisChar &gt;= 'A' &amp;&amp; thisChar &lt;= 'Z') ||
                        (thisChar &gt;= '0' &amp;&amp; thisChar &lt;= '9')) {
                 [output appendFormat:@"%c", thisChar];
             } else {
                 [output appendFormat:@"%%%02X", thisChar];
             }
         }
         return output;
     }
    

22. 给UIView/UILabel设置图片

  • 使用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);
    

23. VC设置背景图片

  • 见上 22

     // 没必要添加UIImageView,直接在layer上画
     self.view.layer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"beijing"].CGImage)
    

24. view的部分圆角问题

  • 也可以自己设定路径

     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
     */
    

25.禁止程序运行时自动锁屏

  • 一般用于视频播放

     [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
    

26. 常用删除命令

  • 删库跑路,谨慎用之

     # 清空废纸篓 
     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 
    

27. 把tableview里cell的小对勾的颜色改成别的颜色

  • 能自定义的最好自定义

     tableView.tintColor = [UIColor redColor];
    

28. 修改textFieldplaceholder字体颜色(字号)

  • 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;
    

29.主线程操作UI(对UI进行更新只能在主线程进行)

  • 方法一

     // 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];
    

30. 两种方法删除NSUserDefaults所有记录

  • 方法一

     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];
     }
    

31. UITableView设置Section间距

  • 注意有时候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;  
     } 
    

32.常用GCD总结

  • 详见多线程部分

     // 后台执行: 
     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
     });
    

33.tabBarController跳转到另一个一级页面

  • 当我们用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;
     });
    

34. WebView获取Html的标题title

  • 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"];
     }
    

参考WKWebView获取title和加载进度

35. user-Agent问题

  • UIWebView(即将废除)
  1. AppDelegate 中 - (BOOL)application: didFinishLaunchingWithOptions:

    NSDictionary *dict = @{@"UserAgent":@"appwebkit"};
    [[NSUserDefaults standardUserDefaults] registerDefaults:dict];
    
  2. 应用

    // 获取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];
    
  3. 考虑到兼容性,只对某些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"];
         }
     }];
    

36. 汉字转为拼音

  • 利用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;
         }
     }
    

37. 打印宏定义

  • 优化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打印不全问题

38. CocoaPods

CocoaPods教程 详见github内 CocoaPods

39. Runtime objc_msgSend报错

  • Too many arguments to function call ,expected 0,have3

     Project --> Build Settings --> 搜索objc --> ENABLE_STRICT_OBJC_MSGSEND --> 设置NO
    

40. 3DTouch

详见github内 3DTouch

41. 捕获系统外异常

  • 只用基本

     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
    

42. Xcode Build Configuration

44. OC中语法糖

  • 界限更鲜明

     self.imageView = ({
         UIImageView *imageView = [[UIImageView alloc] init];
         imageView.image = [UIImage imageNamed:@"12345"];
         imageView.frame = CGRectMake(0, 0, 100, 100);
         [self.view addSubview:imageView];
         imageView;
     });
    

45.获取当前显示的控制器

  • 能不用最好不用,能不在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;
     }
    

46. UIView虚线框

  • 利用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];
    

 iOS为UIView添加虚线边框

47. iOS打电话

48. TableViewCell间距

  • 姿势一

     - (void)setFrame:(CGRect)frame {
         // 整体向下 移动10 
         frame.origin.y += 10;
         // 间隔10 
         frame.size.height -= 10;
         [super setFrame:frame];
     }
    
  • 姿势二

    设置背景视图,约束距离

49. 带UITableView的界面出现空白解决

  • 酌情选择里面代码

     - (void)deleteTopBlank { 
     		self.edgesForExtendedLayout = UIRectEdgeNone;
     	   	self.navigationController.navigationBar.translucent = NO;
           	self.automaticallyAdjustsScrollViewInsets = NO;
          	self.extendedLayoutIncludesOpaqueBars = YES
     }
    

    参考美团技术博客

下篇 知识点2 传送门