ios开发中的问题

拉伸图片

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

ios开发中的问题_第1张图片
拉伸图片

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* allFiles = [mgr subpathsAtPath:path];
    for (int i =0; i

异步线程计算文件

//    设置还没计算完文件时的样式
//    创建一个指示器(加载)视图作为单元格右边视图
    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 
    
    
    //清除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];

结果

ios开发中的问题_第2张图片
结果

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 实现协议
//.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开发中的问题)