美文网首页
ios开发中的问题

ios开发中的问题

作者: Vijay_ | 来源:发表于2017-12-19 01:43 被阅读17次

    拉伸图片

    在图片的宽度/高度不够导致拉伸难看时可以在 Assets中点击图片右侧的Silcing设置图片拉伸方向

    拉伸图片

    CocoaPods

    • pod install
      • 根据Podfile.lock中的版本号来安装Podfile中的依赖库,第一次安装库时会生成Podfile.lock,方便团队协作(使用同一版本库)
    • pod update
      • 直接根据Podfile下载最新的库,并且重新生成一个Podfile.lock文件
    • pod install --no-repo-update
    • pod update --no-repo-update
    • 默认install和update都是会把服务器最新版本的库缓存到本机,无论用不用,后面如果加上--no-repo-update指令则表示不需要缓存。

    事件响应

    • 子视图超出父视图是没法响应时间的。

      • 父视图查找最佳响应者的时候会调用"hitTest"和"pointInside"如果子视图不在父视图内则没有资格成员事件响应者。
    • 子视图超出父视图默认是会显示的,只是不能响应时间

      • 可以用$view.layer.masksToBounds = YES来禁止显示超过父控件bounds的视图。

    按钮设置文字和图片

    必须分状态设置,否则设置无效!

    [btn setTitle:@"" forState:UIControlStateNormal];

    [btn image:image forState:UIControlStateNormal];

    [btn setTitleColor:$color forState:UIControlStateNormal];

    tableFooterView异步获取高度,重新计算

    //因为footerView的高度是通过网络获取到的数据计算的
    //所以在设置tableView的footer时,它是没有高度的
    //于是tableView的contentSize会忽略它,导致等footerView有了高度还是无法上下滚动
    VJFooterView* footerView = [[VJFooterView alloc] init];
        footerView.backgroundColor = [UIColor whiteColor];
        self.tableView.tableFooterView = footerView;
    
    • 解决方法
    //在计算出高度后,让父控件(tableView)重新计算内容高度.
    self.vj_height = squareList.count / 4 * buttonH;
        
        UITableView *tableView =  (UITableView*)self.superview;
        [tableView reloadData];
    
    

    常见查找字符串方法

    //是否以http开头
    [model.url hasPrefix:@"http"]
    //是否以http结尾
    [model.url hasSuffix:@"http"]
    //是否包含http
    [model.url containsString:@"http"]
    //查找到http的起始角标和长度(为0则没找到)
    NSRange range =  [model.url rangeOfString:@"http"];
        range.location;
        range.length;
    

    从View控件中拿到当前控制器

    //拿到当前view所在的窗口的跟控制器
    UITabBarController *tbVC =  (UITabBarController *)self.window.rootViewController;
    //拿到跟控制器(tabbarcontroller)中当前选中的控制器
            UINavigationController *navigation =  tbVC.selectedViewController;
            VJWebViewViewController* webVC = [[VJWebViewViewController alloc] init];
            webVC.url = model.url;
            webVC.title = model.name;
            [navigation pushViewController:webVC animated:YES];
    

    获取文件夹信息

    NSString* path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"default"];
        NSFileManager* mgr = [NSFileManager defaultManager];
        NSDictionary* attr = [mgr attributesOfItemAtPath:path error:nil];
        NSLog(@"%@",attr);
        
        //创建时间
        NSFileCreationDate = "2017-12-16 08:30:44 +0000";
        //文件扩展名是否隐藏
        NSFileExtensionHidden = 0;
        NSFileGroupOwnerAccountID = 20;
        NSFileGroupOwnerAccountName = staff;
        //修改日期
        NSFileModificationDate = "2017-12-16 08:30:44 +0000";
        NSFileOwnerAccountID = 501;
        NSFilePosixPermissions = 493;
        NSFileReferenceCount = 3;
        //文件大小(注意,要知道文件夹里所有文件的大小必须要遍历计算)
        NSFileSize = 96;
        NSFileSystemFileNumber = 8591546449;
        NSFileSystemNumber = 16777220;
        //文件类型 (文件夹还是文件)    
        NSFileType = NSFileTypeDirectory;
    

    获取某个文件下所有文件的大小

        unsigned long long allFileSize = 0;
        NSString* path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"default/com.hackemist.SDWebImageCache.default/2a887a10f755aa623ce2ea0dad4822a2.jpg"];
        NSFileManager* mgr = [NSFileManager defaultManager];
    
    //    NSArray* files = [mgr contentsOfDirectoryAtPath:path error:nil];
    //    当前文件夹和所有子文件夹孙文件夹一直到最底层的所有文件名称
        NSArray<NSString*>* allFiles = [mgr subpathsAtPath:path];
        for (int i =0; i<allFiles.count; i++) {
            NSString* filePath =  [path stringByAppendingPathComponent:allFiles[i]];
            NSDictionary *dic = [mgr attributesOfItemAtPath:filePath error:nil];
            //字典默认有个属性叫fileSize 对应的就是取出NSFileSize建对应的值
            //等同于 dic[@"NSFileSize"]
            allFileSize+=dic.fileSize;
        }
        
    

    异步线程计算文件

    //    设置还没计算完文件时的样式
    //    创建一个指示器(加载)视图作为单元格右边视图
        UIActivityIndicatorView *loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
        [loadingView startAnimating];
        cell.accessoryView = loadingView;
        cell.textLabel.attributedText = [[NSAttributedString alloc] initWithString:@"清除缓存" attributes:@{NSForegroundColorAttributeName:[UIColor purpleColor]}];
    //    在子线程计算文件大小 防止卡死
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
    //        获取需要计算的文件路径
    //        NSString* path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"default"];
            NSString* path = @"/Users/jiewang/Documents";
    //        拼接文字
            NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"清除缓存(%.2fMB)",path.fileSize/1000.0/1000.0] attributes:@{NSForegroundColorAttributeName:VJRandomColor}];
    //        回到主线程更改文字
            dispatch_async(dispatch_get_main_queue(), ^{
                
                cell.textLabel.attributedText = attrString;
    //            清空指示器视图
                cell.accessoryView = nil;
    //            改为箭头
                cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
            });
            
        });
    

    注册重用Cell

    static NSString * const tableCellId = @"clearCell";
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        [self.tableView registerClass:[VJClearCellTableViewCell class] forCellReuseIdentifier:tableCellId];
    }
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        //上面已经使用register注册好了该标识对应的cell类 
        //如果这里取不到缓存的Cell则会用上面注册的cell来创建
        //可以直接在cell类中重写initWithStyle方法初始化
        VJClearCellTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tableCellId];
        return cell;
    }
    

    清除SDWebImage以及自定义的缓存

        #import <SDImageCache.h>
        
        
        //清除SDImage的缓存后,清除自己文件
        [[SDImageCache sharedImageCache] clearDiskOnCompletion:^(){
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                NSFileManager *mgr = [NSFileManager defaultManager];
                [mgr removeItemAtPath:CustomCacheDir error:nil];
                /**
                 第一个参数是要创建的文件夹路径,
                 第二个是"如果文件夹路径前面的路径不存在是否补全"
                 第三个是文件夹属性
                 */
                [mgr createDirectoryAtPath:CustomCacheDir withIntermediateDirectories:YES attributes:nil error:nil];
                dispatch_async(dispatch_get_main_queue(), ^{
                    [SVProgressHUD showSuccessWithStatus:@"清除成功"];
                    [SVProgressHUD dismissWithDelay:2];
                });
            });
        }];
    
        
    

    NSFileManger常用清除缓存方法

        NSFileManager *mgr = [NSFileManager defaultManager];
    //            获取文件属性的字典>包含文件类型(文件是文件夹还是文件)以及文件大小
        [mgr attributesOfItemAtPath:$filePath error:nil];
    //            获取指定路径下的所有子孙等文件名的迭代器(可以使用for in循环)
        [mgr enumeratorAtPath:$filePath];
    //            获取指定路径下的所有子孙等文件名的数组
        [mgr subpathsAtPath:$filePath];
    //            返回该路径是否存在并修改bool指针为是否该路径为文件夹
        [mgr fileExistsAtPath:$filePath isDirectory:$Bool];
    //            删除指定文件夹(文件)
        [mgr removeItemAtPath:$filePath error:nil];
        /**
         第一个参数是要创建的文件夹路径,
         第二个是"如果文件夹路径前面的路径不存在是否补全"
         第三个是文件夹属性
         */
        [mgr createDirectoryAtPath:$filePath withIntermediateDirectories:YES attributes:$fileAttrDictionary error:nil];
    

    TableView循环利用注册多个Cell

    //由于有些特定的cell里封装了不同的逻辑,所以不能与普通的cell循环利用,这时需要注册多个Cell
    static NSString * const tableCellId = @"clearCell";
    static NSString * const tableCellSettingId = @"settingCell";
    
    //注册两个不同的cell 在clearCell中封装了与SettingCell不同的逻辑
    //所以下面在获取settingCell时需要排除是clearCell的情况,反之亦然
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        [self.tableView registerClass:[VJClearCellTableViewCell class] forCellReuseIdentifier:tableCellId];
        [self.tableView registerClass:[VJSettingCell class] forCellReuseIdentifier:tableCellSettingId];
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        if (indexPath.row == 0 ) {
            return [tableView dequeueReusableCellWithIdentifier:tableCellId];
        }else{
            VJSettingCell *cell = [tableView dequeueReusableCellWithIdentifier:tableCellSettingId];
            cell.textLabel.text = [NSString stringWithFormat:@"%zd",indexPath.row];
            return cell;
        }
    }
    

    关于子线程执行block强引用self延迟销毁

    • block会强引用内部的self对象
    • 即使控件的父控件被销毁了,但是某个线程的代码还没执行完,即内部block代码块强引用当前对象,所以该对象还是无法被销毁

    解决方法

    //使用弱指针执行当前对象,则block中强引用的是该弱指针对象,
    //弱指针不会强引用当前对象所以不会妨碍销毁,并且当对象销毁时会指向nil.
    //typeof($obj)是获取$obj的类型
    __weak typeof(self) weakSelf = self;
    
    

    如果不希望UIButton点击颜色变浅

    需要自定义一个button并且重写父类的setHighlighted方法,就不会出现高亮状态

    计算一段文字高度宽度

    //计算UIButton的文字尺寸,
    //注意:仅仅适用于单行文字,多行文字需要使用boundingRectWithSize
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
    NSString *title = [btn titleForState:UIControlStateNormal];
    CGSize titleSize = [title sizeWithAttributes:@{NSFontAttributeName:titleBtn.titleLabel.font}];
    
    

    多行文字高度计算

    NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading一定要加这两个枚举值,否则无效

    计算高度最好字体设置大1像素 比如字体是13的话 计算就写14,因为还有行高,否则计算不准确

    boundingRectWithSize:maxBound 
    options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading 
    attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:13]} 
    context:nil
    

    关于滚动视图和控制器的自动设置位置

    • 在UINavigationControl滚动视图都会自动设置64的顶部内边距(EdgeInset)
      • 使用self.automaticallyAdjustsScrollViewInsets = NO;关闭控制器自动调整布局
    • UIView添加子控制器的view到视图上时默认会给控制器的view设置20的y值,并且高度为647
      • 重新设置控制器的view的y值为0.
      • 重新设置高度为667。

    contentInset(内边距)和bounds.origin是一样作用

    bounds是视图内部尺寸大小bounds的origin相当于contentInset,根据内容所在的位置进行上下从而显示出了滚动的效果,内容视图的坐标系是相对自身的

    scrollView的滚动就是监听一个手指滑动事件然后移动bounds.origin来上下左右的显示内容

    当设置了scrollView的contentInset最好也设置一下它的滚动条内边距
    vc.tableView.scrollIndicatorInsets = vc.tableView.contentInset;

    bounds.size则是内容的范围,显示区域,默认与frame.size相等

    可以把bounds想象成一个矩形块,它的地下是所有的内容(所有的子控件),它在上面通过bounds.origin移动来显示范围内的东西

    //示例代码,把控件加到超出视图然后隐藏超出视图的东西。
    UIView* uv = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
        UISwitch *sv = [[UISwitch alloc] initWithFrame:CGRectMake(120, 120, 0, 0)];
        uv.backgroundColor = VJRandomColor;
    //修改视图的bounds的坐标,移动到子控件所在的位置,将其显示出来,
    //注意:坐标是相对视图本身的,所以看不出来移动
        uv.bounds = CGRectMake(100, 100, 100, 100);
        [uv addSubview:sv];
        uv.clipsToBounds = YES;
        [self.view addSubview:uv];
    

    结果

    结果

    viewWithTag相关

    • 会检查自己的tag是否为传入参数,如果是则返回自己.如果不是,则遍历子控件孙控件直到找到对应的tag.
    • 所有的UIView默认的tag都是0,如果传0则会把自己返回回去

    快速遍历数组执行方法

    • [[NSArray array] makeObjectsPerformSelector:<#(nonnull SEL)#>]

    xib子类是不会继承父类的样式的

    • xib内的样式是在加载的时候实现,也就是加载子类时不会去加载父类的xib的,所以显示的样式还是子类的xib样式

    设置UIButton文字和图片间距

    • 修改$button.titleEdgeInsets (文字内边距)
    • 修改$button.imageEdgeInsets (图片内边距)

    修改自定义tableViewCell

    由于tableview会自动计算尺寸,所以在控制器里设置高度都无效,需要在自定义的类中重写setFrame方法,因为不管哪个方法修改cell的尺寸 最后都要进入setFrame方法中,直接在里面重写即可。

    - (void)setFrame:(CGRect)frame{
         frame.origin.y += 10;
        frame.size.height -= 10;
        [super setFrame:frame];
    }
    

    自定义tableViewCell

    • 创建xib->拿出tableViewCell的控件->设置自定义的类
      • 如果要取消选中样式,设置selection为none
    • 在自定义的类中,连线xib中的控件,添加模型属性,重写set方法(把模型属性设置到控件中)。
      • 如果要修改tableViewCell的尺寸,看上面。
      • 隐藏cell底部分割线,$tableView.separatorStyle = UITableViewCellSeparatorStyleNone;

    tableView的顶部刷新指示器

    • 默认的UIRefresh有bug,最好是自己实现一个,或者使用MJRefresh。
    • 自己实现:
      • 设置一个view添加到tableView上面然后y为-高度(为了隐藏到tableView最上方)
      • 默认透明,往下拉渐渐显示,当拉到距离为该view高度时切换文字,然后实现代理的EndDragging方法(在停止拖拽时调用)进行刷新并且修改tableView的内边距让指示器view保持显示,最后加载完内容修改内边距让指示器回去(隐藏)
    • MJRefresh:
      • 会给所有scrollView上添加一个mj_header和mj_footer可以用来设置MJRefreshHeader的子类和MJRefreshAutoFooter的子类。
      • 可以继承MJRefreshHeader, MJRefreshAutoFooter基类在prepare方法中进行自定义的预设置,方便一起更改所有使用该类的指示器。

    把字符串转换成NSDate并获取时分秒

    一般情况下 NSCalendar 和NSDate NSDateFormatter是一起用的
    NSCalendar用来处理NSDate,NSDateFormatter用来转换Date和string

    比较日期差值使用NSCalender的components:xxx fromDate:xxx toDate:xxx
    获取差值日期对象,可以直接获取到相差几天几小时几秒(会自动进位很方便)

    //    获取当前系统日历对象
        NSCalendar * calendar = [NSCalendar currentCalendar];
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    //    转换日期格式必须与返回的格式相同 否则不能把字符串转换成date(会返回nil)
        formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
    //    把字符串转换成NSDate
        NSDate *createDate = [formatter dateFromString:topic.created_at];
        if (createDate) {
            // 获取从帖子发布到现在的差值日期对象
            NSDateComponents * components = [calendar components:NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute fromDate:createDate toDate:[NSDate date] options:kNilOptions];
            
            NSInteger diffDay = components.day;
            NSInteger diffhour = components.hour;
            NSInteger diffminute = components.minute;
            //        超过一天
            if (diffDay>0) {
                self.createdAtLabel.text = [NSString stringWithFormat:@"%li天前",components.day];
            }else if (diffhour>0){
                self.createdAtLabel.text = [NSString stringWithFormat:@"%li小时前",components.hour];
            }else if (diffminute>0){
                self.createdAtLabel.text = [NSString stringWithFormat:@"%li分钟前",components.minute];
            }
            
        }
    
        
    

    继承,实现协议,分类(类别),类拓展

    //.h文件
    xxx:superClass 继承
    xxx:superClass<xxxprotocol> 实现协议
    //.m文件
    xxx() 类拓展
    xxx(xxx) 分类
    

    在xib中设置图片和按钮大小

    • 默认情况(不给控件设置尺寸约束),UIImageView是以图片的尺寸作为尺寸的。UIButton是以控件里的UIImageView或者backgroundImageView(两者比较最大的图片)尺寸作为尺寸的,如果设置约束则imageView的图片和button的背景图片会以设置的contentMode格式来进行缩放。注意,xib中无法更改button里的imageView的图片尺寸,必须自定义继承它,在layoutSubviews里进行修改

    关于tableViewCell循环利用

    • 循环利用的cell会将之前添加上去的控件带到下一个单元格上,所以一般不是每个cell去处理添加什么控件删除什么控件的,而是一次把所有用到的cell都添加上去,判断条件去隐藏某个当前行不需要的控件
    • 如果多组cell的差异非常大时建议创建多个自定义的cell,然后注册不同的标识符,根据不同的标识符去创建不同类型的cell。

    从xib中加载的view设置尺寸显示不正确

    • 从xib中加载的view默认设置了autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight。这两个属性会使xib的内容跟随父控件进行放大缩小,所以设置的尺寸无效,所以需要改属性为UIViewAutoresizingNone即可
    • 控件的autoresizingMask和frame尺寸会有冲突,所以凡是从xib中加载的控件,发现设置尺寸显示不对就优先查看是不是autoresizingMask的问题

    NSInteger占两位,空值0代替

    • [NSString stringWithFormat:@"%02zd",2]; //结果为02
    • 0Xzd X表示该数字要占多少位,0表示该位没值时用0 代替

    父类子类通信

    • 不同类之前通信,或者父子类大规模的通信时可以采用代理
    • 如果只是小规模的通信,比如仅仅只是每个子类的type不一样时,仅需要父类创建一个type的get方法,子类分别实现即可

    手势事件获取的缩放或者平移都可以转换成CGATransform使用在控件上

    • 每次调用完手势事件方法后都需要把手势数值设置为0,否则会累加,而我们设置给控件transform时是用的累加,如果都累加则会成倍增长,所以手势事件调用完后一定要设置手势对象的数值为0

    相关文章

      网友评论

          本文标题:ios开发中的问题

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