美文网首页
iOS 知识小集

iOS 知识小集

作者: yyggzc521 | 来源:发表于2018-12-17 14:23 被阅读0次

    NSMutableDictionary拼接字典

    NSMutableDictionary *dic1 = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"BMW",@"CarLogo",@"Red",@"CarColor", nil];
    NSDictionary *dic2 = [NSDictionary dictionaryWithObjectsAndKeys:@"Xiaoming",@"name",@"28", @"age",nil];
    [dic1 addEntriesFromDictionary:dic2];
    
    NSLog(@"%@",dic1);
    
    addEntriesFromDictionary字典拼接.png 访问相册提示语.png 访问相机提示语.png
    访问位置提示语.png
    Privacy - Photo Library Additions Usage Description 相册的描述
    Privacy - Camera Usage Description 相机的描述
    
    • 定位隐私描述描述
      参考

    (1)在项目的 Info.plist 添加定位权限申请,根据您的实际业务需求,选择如下方式设置:
    - NSLocationWhenInUseUsageDescription:表示应用在前台的时候可以搜到更新的位置信息;
    - NSLocationAlwaysUsageDescription:表示应用在前台和后台(suspend 或 terminated)都可以获取到更新的位置数据;
    - NSLocationAlwaysAndWhenInUseUsageDescription:申请永久定位权限,以便应用在前台和后台都可以获取位置数据;

    //iOS11新出的
        <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
        <string>Give me location authority</string>
        <key>NSLocationWhenInUseUsageDescription</key>
        <string>Give me location authority</string>
    
    模态 modal出半透明的控制器
        ZCWithdrawPasswordVC *vc = [ZCWithdrawPasswordVC new];
        vc.modalPresentationStyle =UIModalPresentationOverCurrentContext|UIModalPresentationFullScreen;
        [self presentViewController:vc animated:YES completion:^{
                 UIColor *color = [UIColor blackColor];
                 vc.view.backgroundColor = [color colorWithAlphaComponent:0.55];
                }];
    

    把view某个直角切成圆角

        //把 view2 的 左下角 和 右下角的直角切成圆角
        UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(120,10,80,80)];
        view2.backgroundColor = [UIColor redColor];
        [self.view addSubview:view2];
    
        //得到view的遮罩路径
        UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view2.bounds byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(10,10)];
        //创建 layer
        CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
        maskLayer.frame = view2.bounds;
        //赋值
        maskLayer.path = maskPath.CGPath;
        view2.layer.mask = maskLayer;
    

    圆角图片

    -(void)kt_addCorner:(CGFloat)radius
    {
        if (self.image) {
            self.image = [self.image imageAddCornerWithRadius:radius andSize:self.bounds.size];
        }
        return;
    }
    
    - (UIImage*)imageAddCornerWithRadius:(CGFloat)radius andSize:(CGSize)size{
        CGRect rect = CGRectMake(0, 0, size.width, size.height);
        
        UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)];
        CGContextAddPath(ctx,path.CGPath);
        CGContextClip(ctx);
        [self drawInRect:rect];
        CGContextDrawPath(ctx, kCGPathFillStroke);
        UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
    }
    
    顶部imageView超出了父视图20pt,现在想把底部两个角切成圆角,用这种方法就不显示顶部超出的部分了,具体原因有空再查找

    阿拉伯数字123转中文显示一二三

    cell.labelTitle.text = [NSString stringWithFormat:@"第%@周",[self chineseWithInteger:indexPath.row + 1]];
    
    - (NSString *)chineseWithInteger:(NSInteger)integer {
        
        NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
        formatter.numberStyle = kCFNumberFormatterRoundHalfDown;
        NSString *string = [formatter stringFromNumber:[NSNumber numberWithInt:(int)integer]];
        
        return string;
    }
    

    防止按钮重复点击

    1. 控制enabled属性
    [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
    
    - (void)clickBtn:(UIButton *)sender {
        
        sender.enabled = NO;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            sender.enabled = YES;
        });
    }
    
    1. 使用performSelector:withObject:afterDelay:和cancelPreviousPerformRequestsWithTarget方法实现(缺点是会有延迟)
    [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
    - (void)clickBtn:(UIButton *)sender {
        
        [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(nextAction) object:nil];
        [self performSelector:@selector(nextAction) withObject:nil afterDelay:1];
    }
    
    1. 通过runtime对UIButton的sendAction:to:forEvent:方法hook
      3.1 对象关联技术为UIButton添加属性
    @interface UIButton (Category)
    
    /**
    *  按钮点击的间隔时间
    */
    @property (nonatomic, assign) NSTimeInterval clickDurationTime;
    
    @end
    
    @implementation UIButton (Category)
    
    - (void)setClickDurationTime:(NSTimeInterval)clickDurationTime {
       
       objc_setAssociatedObject(self, @selector(clickDurationTime), @(clickDurationTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    - (NSTimeInterval)clickDurationTime {
       
       return [objc_getAssociatedObject(self, @selector(clickDurationTime)) doubleValue];
    }
    
    @end
    

    3.2 对sendAction:to:forEvent:方法hook
    参考:
    https://www.jianshu.com/p/925f8176781f
    https://blog.csdn.net/icetime17/article/details/51782983

    // 默认的按钮点击时间
    static const NSTimeInterval defaultDuration = 3.0f;
    
    // 记录是否忽略按钮点击事件,默认第一次执行事件
    static BOOL _isIgnoreEvent = NO;
    
    // 设置执行按钮事件状态
    static void resetState() {
            _isIgnoreEvent = NO;
    }
    @implementation UIButton (Tool)
    
    + (void)load {
       
       SEL originSEL = @selector(sendAction:to:forEvent:);
       SEL mySEL = @selector(my_sendAction:to:forEvent:);
       
       Method originM = class_getInstanceMethod([self class], originSEL);
       const char *typeEncodinds = method_getTypeEncoding(originM);
       
       Method newM = class_getInstanceMethod([self class], mySEL);
       IMP newIMP = method_getImplementation(newM);
       
       if (class_addMethod([self class], mySEL, newIMP, typeEncodinds)) {
           class_replaceMethod([self class], originSEL, newIMP, typeEncodinds);
       } else {
           method_exchangeImplementations(originM, newM);
       }
    }
    
    - (void)my_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
       
       if ([self isKindOfClass:[UIButton class]]) {// 保险起见,判断下Class类型
           
           //1. 按钮点击间隔事件
           self.clickDurationTime = self.clickDurationTime == 0 ? defaultDuration : self.clickDurationTime;
           
           //2. 是否忽略按钮点击事件
           if (_isIgnoreEvent) {//2.1 忽略按钮事件
               
               return;
           } else if(self.clickDurationTime > 0) {//2.2 不忽略按钮事件
               
               // 后续在间隔时间内直接忽略按钮事件
               _isIgnoreEvent = YES;
               
               // 间隔事件后,执行按钮事件
               dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.clickDurationTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                   resetState();
               });
               
               // 发送按钮点击消息
               [self my_sendAction:action to:target forEvent:event];
           }
           
       } else {
           [self my_sendAction:action to:target forEvent:event];
       }
    }
    

    后台持续运行

    FPS帧率检测

        AppDelegate导入 #import "YYFPSLabel.h"
    
        YYFPSLabel *fpsLabel = [YYFPSLabel new];
        fpsLabel.frame = CGRectMake(20, kScreenHeight/2, 150, 30);
        [fpsLabel sizeToFit];
        [self.window addSubview:fpsLabel];
    

    手机上显示的APP名称和版本号及其他信息

     [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"] //应用显示的名称
    
    #import "UIApplication+YYAdd.h"
    [[UIApplication sharedApplication] appVersion]//版本号 1.2.0
    
    
    //设备唯一标识符
    NSString *identifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
    NSLog(@"设备唯一标识符:%@",identifier);
    //手机别名:用户定义的名称
    NSString *userPhoneName = [[UIDevice currentDevice] name];
    NSLog(@"手机别名:%@",userPhoneName);
    //设备名称
    NSString *deviceName = [[UIDevice currentDevice] systemName];
    NSLog(@"设备名称:%@",deviceName);
    //手机系统版本
    NSString *phoneVersion = [[UIDevice currentDevice] systemVersion];
    NSLog(@"手机系统版本:%@",phoneVersion);
    //手机型号
    NSString *phoneModel = [CXDHelper iphoneType];
    NSLog(@"手机型号:%@",phoneModel);
    //地方型号(国际化区域名称)
    NSString *localPhoneModel = [[UIDevice currentDevice] localizedModel];
    NSLog(@"国际化区域名称:%@",localPhoneModel);
    
    

    UITableView的Group style

    注意:在grouped style表中不能有一个(右边的)索引
    Group类型默认设置tableView灰色背景色,cell为白色背景色,section外边缘设置浅灰色边框,cell设置浅灰色间隔线。如下图:

    Group style.png
    注意:去掉Group类型的表section头部和中间间隔的方法:
    1. 设置标题tableHeaderView的高度为特小值,但不能为零,若为零的话,ios会取默认值18,就无法消除头部间距了。
          UIView *view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 0.001)];
          view.backgroundColor = [UIColor redColor];
          self.tableView.tableHeaderView = view;
    
    1. 设置代理方法(中间的留白其实是段尾的高度,代理的作用设置段尾的高度,返回值也不能为0,否则系统启用默认值18)
    - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
          return 0.01f;
    }
    

    注意:sectionHeaderHeight/sectionFooterHeight这2个属性只在Grouped类型,且未实现代理方法tableView:heightForHeaderInSection: 时有效
    在plain风格下设置无效。故在使用UITableView过程中尽量使用代理方法设置sectionHeader和sectionFooter的高度

    UITableView reloadData刷新完毕的时机

    参考:https://juejin.im/post/5bf572a3e51d45218f3d0591

    reloadData是一个异步方法,并不会等待UITableView或者UICollectionView(后面统称listView)真正刷新完毕后才执行后续代码,而是立即执行后续代码。那么,直接在reloadData后执行代码是有可能出问题的。

    • 同步:numberOfSectionsInCollectionView和numberOfItemsInSection
    • 异步:cellForItemAtIndexPath
    • 同步+异步:sizeForItemAtIndexPath

    如果表中的数据非常大,在一个run loop周期可能不会执行完,所以cellfor方法是异步的,这时,需要tableview视图数据的操作就会出问题了。
    想要获取listView真正刷新完毕的时机,可以用以下方法

    1. 通过layoutIfNeeded方法,强制重绘并等待完成
    [self.tableView reloadData];
    [self.tableView layoutIfNeeded];
    // 刷新完成,执行后续需要执行的代码
    if ( self.didPlayIdx ) {
        MyCell* cell = (MyCell*)[self.collectionView cellForItemAtIndexPath:self.didPlayIdx];
        if (cell) {
        [cell playWithPlayer:self.player];
        }
    }
    
    1. reloadData方法会在主线程执行,通过GCD,使后续操作排队在reloadData后面执行。一次runloop有两个机会执行GCD dispatch main queue中的任务,分别在休眠前和被唤醒后。设置listView 的layoutIfNeeded为YES,在即将进入休眠时执行异步任务,重绘一次界面
    [self.collectionView reloadData];  
    dispatch_async(dispatch_get_main_queue(), ^{  
        // 刷新完成,执行后续代码
        if ( self.didPlayIdx ) {
            MyCell* cell = (MyCell*)[self.collectionView cellForItemAtIndexPath:self.didPlayIdx];
            if (cell) {
                [cell playWithPlayer:self.player];
            }
        }
    });
    

    参考

    PCH文件的创建和配置

    PCH的创建.png

    Build Settigs --> 搜索prefix关键字--> Precompile Prefix Header改为YES --> 点击下面的空白把PCH相对路径粘贴上就OK了


    PCH的配置.png

    自定义代码块路径

    ~/Library/Developer/Xcode/UserData/CodeSnippets
    

    清除缓存路径

    ~/Library/Developer/Xcode/DerivedData
    ~/Library/Caches/com.apple.dt.Xcode
    

    切换UIWindow的rootViewController根控制器动画

    参考

    // 登陆后淡入淡出更换rootViewController
    - (void)restoreRootViewController:(UIViewController *)rootViewController
    {
        typedef void (^Animation)(void);
        UIWindow* window = [UIApplication sharedApplication].keyWindow;
        
        rootViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
        Animation animation = ^{
            BOOL oldState = [UIView areAnimationsEnabled];
            [UIView setAnimationsEnabled:NO];
            [UIApplication sharedApplication].keyWindow.rootViewController = rootViewController;
            [UIView setAnimationsEnabled:oldState];
        };
        
        [UIView transitionWithView:window
                          duration:0.5f
                           options:UIViewAnimationOptionTransitionCrossDissolve
                        animations:animation
                        completion:nil];
    }
    

    YYModel模型转字典 且 深拷贝返回可变对象

    NSMutableDictionary *parameters = [[self.requestShopListModel modelToJSONObject] mutableCopy];
    

    动画效果

    [UIView animateWithDuration:0.2 animations:^{
                    self.btnPSWLogin.alpha = 1.f;
                    self.btnCode.alpha = self.btnRole.alpha = self.btnRegister.alpha = self.pswSuperView.alpha = 0.f;
                    self.loginBtnToPhoneSpace.constant = 20;
                    [self.view layoutIfNeeded];
                } completion:^(BOOL finished) {
                    [self.btnLogin setTitle:@"获取验证码" forState:UIControlStateNormal];
                    [self.btnLogin setTitle:@"获取验证码" forState:UIControlStateDisabled];
                }];
    

    读取本地文件

    1. 读取plist文件
    NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil]];
    
    1. 读取json文件(如果读取不到内容或者nil,先检查一下json格式是否正确,因为这个曾经耽误我很多时间)
    // 读取本地JSON文件
    - (NSDictionary *)readLocalFileWithName:(NSString *)name {
        // 获取文件路径
        NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"json"];
        // 将文件数据化
        NSData *data = [[NSData alloc] initWithContentsOfFile:path];
        // 对数据进行JSON格式化并返回字典形式
        return [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
    }
    
    1. 读取本地图片 自动区分@2x @3x 的图片
    NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png",imageName]];
    

    iOS11之后设置leftBarButtonItem的距离左侧间距的方法

    通过在创建 UIBarButtonItem 时设置 custom view 的布局,但是又会出现部分区域不能接收到点击事件

        UIButton *button = [UIButton buttonWithImageName:@"back_blackColor"];
        button.size = CGSizeMake(70, 30);
        // 让按钮内部的所有内容左对齐
        button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
        // 让按钮的内容往左边偏移10
        button.contentEdgeInsets = UIEdgeInsetsMake(0, -10, 0, 0);
        [button addTarget:self action:@selector(clickLeftItem) forControlEvents:UIControlEventTouchDown];
        // 修改导航栏左边的item
        self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
    

    断言NSAssert的使用

    参考
    NSAssert是一个预处理宏, 他的主要作用就是可以让开发者比较便捷的捕获一个错误, 让程序崩溃, 同时报出错误提示

    - (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
                                         options:(SDWebImageOptions)options
                                        progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                       completed:(nullable SDInternalCompletionBlock)completedBlock {
        // Invoking this method without a completedBlock is pointless
        NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");
    

    当调用SDWebImage的上面的方法, 如果变量完成的回调为nil, 此时程序就会崩溃, 并且抛出一个异常, 异常提示 调用此方法是没有意义的

    设置行间距

      NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
      [style setAlignment:NSTextAlignmentCenter];
      [style setLineSpacing:self.configure.lineSpacing];
    NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string];
    [attributedString addAttribute:NSParagraphStyleAttributeName value:style range:NSMakeRange(0, string.length)];
    

    label自适应高度宽度

    UILabel *label = [[UILabel alloc] init];  
    label.backgroundColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1]; 
    label.text = @"大师傅好搜啊发"; 
    label.numberOfLines = 0; 
    CGSize size = CGSizeMake(100, MAXFLOAT);//设置高度宽度的最大限度 CGRect rect = [label.text boundingRectWithSize:size options:NSStringDrawingUsesFontLeading|NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:20]} context:nil];
    label.frame = CGRectMake(100, 100, rect.size.width, rect.size.height);  
    self.view addSubview:label];
    
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 100, 0)];
    label.backgroundColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1];
    label.text = @"我会自己去适应";
    label.numberOfLines = 0;
    [label sizeToFit];
    [self.view addSubview:label];
    
    UILabel *label = [[UILabel alloc] init];
    label.backgroundColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1];
    label.text = @"我会自己去适应宽度的";
    label.font = [UIFont systemFontOfSize:50];
    CGSize size = [label.text sizeWithAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:50]}];
    //ceilf()向上取整函数, 只要大于1就取整数2. floor()向下取整函数, 只要小于2就取整数1.
    CGSize adaptionSize = CGSizeMake(ceilf(size.width), ceilf(size.height));
    label.frame = CGRectMake(100, 100, adaptionSize.width, adaptionSize.height);
    [self.view addSubview:label];
    

    统计项目代码行数

    1. 打开终端
    2. cd 进入项目根目录
    3. 输入命令行
     find . "(" -name "*.m" -or -name "*.mm" -or -name "*.cpp" -or -name "*.h" -or -name "*.rss" ")" -print | xargs wc -l
    

    最后显示的total前面的数字就是项目所有的代码行数.
    每一行前面的数字表示,某一个.m或者.h文件中的代码行数.
    参考

    判断字符串是否是数字

    参考

    判读模拟器

    #if !TARGET_IPHONE_SIMULATOR
    
    #endif
    

    延迟执行与取消延迟执行

    [self performSelector:@selector(onClickOverlay:) withObject:nil afterDelay:DelayTimeSeconds];
    //延迟
    
    [NSObject cancelPreviousPerformRequestsWithTarget:self]; //这个是取消当前run loop 里面所有未执行的 延迟方法(Selector Sources)
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(onClickOverlay:) object:nil];// @selector 和 object 都和 延迟一致 就是 指定取消 未执行的一条或者多条的延迟方法.
    

    iOS去除数组中重复的model数据

    // 去除数组中model重复
        for (NSInteger i = 0; i < self.selectedModelArray.count; i++) {
            for (NSInteger j = i+1;j < self.selectedModelArray.count; j++) {
                AssistantModel *tempModel = self.selectedModelArray[I];
                AssistantModel *model = self.selectedModelArray[j];
                if ([tempModel.assistantId isEqualToString:model.assistantId]) {
                    [self.selectedModelArray removeObject:model];
                }
            }
        }
    

    iOS 获取蜂窝网络信号强度 包含iPhoneX XS XR XSMASX

    https://www.cnblogs.com/allencelee/p/10483265.html

    何获取导航栏中rightBarButtonItem对应self.view的坐标

    https://blog.csdn.net/allanGold/article/details/80675331
    https://segmentfault.com/q/1010000005592396

    界面旋转

    https://juejin.im/post/5d0233da6fb9a07eac05ce51

    设置UISlider滑轨宽度

    - (CGRect)trackRectForBounds:(CGRect)bounds {
        return CGRectMake(0, 0, ScreenWidth, 15);
    }
    

    判断每天第一次和每周第一次打开APP

    https://www.jianshu.com/p/f22e9aba1f7b

    safari打开链接注意对URL做转义处理

    + (void)jumptoSafariWithLinkStr:(NSString *)linkStr VC:(UIViewController *)weakSelf {
        if (![linkStr hasPrefix:@"http://"]&&![linkStr hasPrefix:@"https://"]) {
            linkStr=[NSString stringWithFormat:@"http://%@",linkStr];
        }
        NSURL *url = [NSURL URLWithString:linkStr];
        if ([[UIApplication sharedApplication] canOpenURL:url]) {
            [[UIApplication sharedApplication] openURL:url];
        }else  {
            NSString* encodedString = [linkStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
            BOOL canOpen = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:encodedString]];
            if (!canOpen) {
                [PSProgressHUD showMessageHUD:^(PSProgressHUD *make) {
                    make.message(@"无法打开链接");
                }];
                DDLogInfo(@"%s 打开链接失败. URL:%@ \n EncodingUrl:%@", __FUNCTION__, linkStr,encodedString);
            } else {
                DDLogInfo(@"%s 打开链接中包含中文. URL:%@", __FUNCTION__, linkStr);
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:iOS 知识小集

          本文链接:https://www.haomeiwen.com/subject/xzwvhqtx.html