常见问题 (一)

  1. 连续使用多目运算符要谨慎,容易漏掉特殊的情况
  2. 点击弹出的微信消息再返回,点击某些控件不能隐藏(默认点击隐藏/显示)
  3. 操场形状的进度条.
  4. 显示两位小数,不够的以0来替补
  5. 箭头居中的气泡
  6. 两种不同的App Store 跳转方式:
  7. copyWithZone:方法也可以返回一个可以打印的字符串
  8. 跳到横屏控制器
  9. 让UIScrollView只沿着一个方向滑动
  10. block 中的UI操作无法实现
  11. {length = 2, path = 0 - 13}
  12. YYLabel 只显示一行
  13. AFN 请求接口时 , 断言 请求的NSUrl对象为 nil .
  14. 适配 iPhone X 的时候 , 屏幕高度始终为 667.
  15. 红色叹号的invalid bitcode signature
  16. 解析字符串中的 json 串
  17. 将 debug安装包 安装到 xcode 模拟器中
  18. "xx.app"已损坏,打不开。您应该将它移到废纸篓
  19. Embedded binary's bundle identifier is not prefixed with the parent app's bundle identifier.
  20. UILabel 在设置attributedText 的值时 , numberOfLines属性不生效
  21. 取消 UITableView 的 cell 被选中的方法
  22. 继承 UITableViewCell 时 , 重写 init 方法没有被调用
  23. 触发按钮点击事件的方法
  24. 子视图的动画超出了父视图
  25. 微信的选中效果
  26. 隐藏 iPhone X 全屏时的虚拟按键
  27. UpdatePublicKey: database is locked
  28. Command /usr/bin/codesign failed with exit code 1
  29. pod 导入的文件, file not find
  30. 当前时区的时间
  31. The file “CycleProgressBar” couldn’t be opened because you don’t have permission to view it.
  32. A 跳到 B, B 跳到 C以后, 不想让用户看到 B, 可以通过更改UINavigationController的viewControllers.
  33. 比较字符串的大小, 可以用字符串的 compare 方法.
  34. UIScrollView无法滑动
  35. 最简单快速的动画代码
  36. 最简单的让 UICollectionView 无限滑动
  37. 视图不显示的几种情况
  38. 崩溃MASViewConstraint install
  39. 让 UIScrollView 中的视图不跟着滑动
  40. 程序设计常见问题
  41. 使用 hittest 时, 需要考虑隐藏的视图.
  42. iOS控制代码段大小在60M 以内
  43. 后台播放的定时器, 可以使用NSObject 的performSelector:...方法.
  44. UITableView 的底部有多余的下划线
  45. 使用 SourceTree push 代码时弹出 password required
  46. git rebase 代码时, 自己的代码合不见了, 可以通过查看 reflog 操作, 回退到某个提交
  47. 比较容易忽略的循环引用
  48. 从系统浏览器通过 schema 打开 app , APP崩溃
  49. 提交代码到错误的分支上, 可以使用 git cherry-pick xx命令补救
  50. 一个视图, 在横屏和竖屏都需要展示, 可以在切换屏幕时切换父视图.
  51. 有 UIScrollView 的视图, 无法左滑退出
  52. 运行 Xcode 提示: You don't have permission to access
  53. MJRefresh上拉刷新到最底部后, 下拉刷新只能刷新一页
  54. 图文混排, 可以使用NSAttributedString+YYText.h分类的方法
  55. 响应推送通知的系统方法, 有两个
  56. deletesection:方法解决 MJFresh 数据较少时刷新列表, footer 无法自动归位的问题
  57. deletesection:崩溃
  58. YYLabel 不仅要设置4个约束, 而且在更新 textLayout 时要设准 contentsize.
  59. pause program execusion 查找dispatch_semaphore_wait
  60. git stash 暂存当前的测试代码到缓存栈.
  61. 误删git远程分支

1. 连续使用多目运算符要谨慎,容易漏掉特殊的情况

1> 错误代码如下:

    CGFloat AnchorLevelImgWid = isHost ? 27 : 0;
    AnchorLevelImgWid = anchorLevel < 0 ? 0 : 27;

2> 以上代码用逻辑的关系来分析,是或的关系,等价于

    CGFloat AnchorLevelImgWid = 0;
    if (isHost)
    {
        AnchorLevelImgWid = 27;
    }
    if (anchorLevel >= 0)
    {
        AnchorLevelImgWid = 27;
    }

3> 但是实际需求却是 isHost 和 anchorLevel < 0 同时为真的时候,才执行AnchorLevelImgWid的值是27.正确的双目运算符应该是

    CGFloat AnchorLevelImgWid = isHost ? (anchorLevel < 0 ? 0 : 27) : 0;

2. 点击弹出的微信消息再返回,点击某些控件不能隐藏(默认点击隐藏/显示)

1> 点不动某些控件.

应该是点击了,但是在某些地方return 了.

2> 上老司机杀手锏------断点
  1. 在控件被触发的地方插入断点.
  2. 使用 MAC qq 给自己的手机发消息,并狂点手机的顶部,一边让顶部控件隐藏/显示,一边等待 qq 消息的弹出.
  3. 在 qq 中编辑后,再返回,奇迹发生了------控件的触发方法被调用了,在一个与键盘变量有关的地方返回了.
  4. 全局搜索此方法名,发现是键盘显示/隐藏的通知调用的.
  5. 得出结论:键盘的显示/隐藏通知 是全局的,从当前 app 跳到其他 app 的瞬间,也会收到该通知.
  6. 解决方案:收到键盘相关的通知后,同时判断当前输入框是否是第一响应者.

3.操场形状的进度条.

  1. 可以使用 layer.mask 来给视图切圆角.
  2. 其子视图即使是方的,左侧也依旧会根据原始图的 mask 路径来切圆角,右侧是直的.
  3. 创建的代码如下:
/// 创建一个可以切掉圆角的 UIImageView
+ (id)imageViewWithRoundedRect:(CGSize)size radius:(NSInteger)r 
{
    UIImageView *newImageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, size.width, size.height)];
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:newImageView.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(r, r)];
    
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
    //设置大小
    maskLayer.frame = newImageView.bounds;
    //设置图形样子
    maskLayer.path = maskPath.CGPath;
    newImageView.layer.mask = maskLayer;
    return newImageView;
}

4.显示两位小数,不够的以0来替补

1> 方法一:使用%.0nf

比较方便,但是四舍五入后在某些情况下有些不准确.

2> 方法二:直接使用官方自带的货币计算用的类:NSDecimalNumber
  1. 观察头文件,看到了decimalNumberWithString:快速构造方法,decimalNumberByRoundingAccordingToBehavior:行为构造方法.
  2. 既然是小数精度构造类,肯定有一个自定义的操作,来选择精度的制造方法,很容易想到从第一条的行为构造方法的参数------NSDecimalNumberBehaviors协议的对象 来构造.
  3. 进入该协议,看到了两个协议方法,貌似对精度确定没什么作用.
  4. 在当前头文件中搜索NSDecimalNumberBehaviors,不经意间找到了遵循该协议的NSDecimalNumberHandler
  5. 在当前对象中找到了decimalNumberHandlerWithRoundingMode:scale:raiseOnExactness:raiseOnOverflow:raiseOnUnderflow:raiseOnDivideByZero:方法.参数解释如下:
// Rounding policies :
// OrgValue 1.2  1.21  1.25  1.35  1.27
// Plain    1.2  1.2   1.3   1.4   1.3  四舍五入
// Down     1.2  1.2   1.2   1.3   1.2  向下取正
// Up       1.2  1.3   1.3   1.4   1.3  向上取正
// Bankers  1.2  1.2   1.2   1.4   1.3  (特殊的四舍五入,碰到保留位数后一位的数字为5时,根据前一位的奇偶性决定。为偶时向下取正,为奇数时向上取正。如:1.25保留1为小数。5之前是2偶数向下取正1.2;1.35保留1位小数时。5之前为3奇数,向上取正1.4)

// scale : 需要保留的精度。
// raiseOnExactness : 为YES时在处理精确时如果有错误,就会抛出异常。
// raiseOnOverflow  : YES时在计算精度向上溢出时会抛出异常,否则返回。
// raiseOnUnderflow : YES时在计算精度向下溢出时会抛出异常,否则返回.
// raiseOnDivideByZero : YES时。当除以0时会抛出异常,否则返回。

6.测试代码如下:

- (void)test1
{
    NSString *string = @"0.00002";
    NSDecimalNumber *subtotal = [[self class] getRightTwoDecimalNumber:string];
    NSString *dstStr = @"0";
    if (subtotal.floatValue != 0)
    {
        dstStr = [self getTwoDecimalNumberStringWithNumber:subtotal];
    }
    NSLog(@"%@",dstStr);
}

/// 严格两位小数
-  (NSString *)getTwoDecimalNumberStringWithNumber:(NSNumber *)number
{
    NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
    [numberFormatter setPositiveFormat:@"0.00"];
    NSString *formattedNumberString = [numberFormatter stringFromNumber:number];
    return formattedNumberString;
}

/// 获取向下取整的两位数
+ (NSDecimalNumber *)getRightTwoDecimalNumber:(NSString *)string
{
    NSDecimalNumberHandler *handler = [NSDecimalNumberHandler
                                       decimalNumberHandlerWithRoundingMode:NSRoundDown
                                       scale:2
                                       raiseOnExactness:NO
                                       raiseOnOverflow:NO
                                       raiseOnUnderflow:NO
                                       raiseOnDivideByZero:YES];
    
    NSDecimalNumber *subtotal = [NSDecimalNumber decimalNumberWithString:string];
    subtotal = [subtotal decimalNumberByRoundingAccordingToBehavior:handler];
    return subtotal;
}

5.箭头居中的气泡

  1. 由于 iOS 只能支持图片以一块区域为基准拉伸,因此可以准备两张图片,一张是可以左右拉伸的矩形,另一张是能遮住矩形底部边线的中心的箭头.
  2. 在文字伸缩的时候,设定矩形区域的长度和拉伸方式,并让箭头的水平中心和矩形的一致.箭头的顶部略微盖过矩形区域.
  3. 代码如下
    CGFloat scale = [UIScreen mainScreen].scale;
    CGFloat accessyOffsetY = 1.0/scale;
    accessyOffsetY = (scale == 3 ? 0.34 : accessyOffsetY);    
    [curExpAccessoryView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.mas_equalTo(curExpView);
        make.top.mas_equalTo(curExpView.mas_bottom).offset(-accessyOffsetY);
        make.size.mas_equalTo(CGSizeMake(15, 3));
    }];

4.对于依赖比较多约束的控件,可以先添加到父视图上,等其他控件的创建代码布局代码都写完了,再在最后补充也可以.

    [_curExpLabelBgView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.greaterThanOrEqualTo(_levelDetailView).priorityHigh();
        make.right.lessThanOrEqualTo(_levelDetailView).priorityHigh();
        make.centerX.mas_equalTo(_curExpProcessView.mas_right).priorityLow();
        make.top.mas_equalTo(_seperatorLine.mas_bottom).offset(10*kBigScreenViewWidthRate);
        make.width.mas_equalTo(@(0));
        make.height.mas_equalTo(@(20*kBigScreenViewWidthRate));
    }];

6.两种不同的App Store 跳转方式:

  1. 打开 App Store
- (void)openRateWindow
{
    NSString *str = [NSString stringWithFormat:@"itms-apps://itunes.apple.com/app/id%@",@"1106329353"];
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:str]];
}
  1. 弹出当前 App 评价页面的控制器.
#import 
- (void)test1
{
    [self openAppWithIdentifier:@"1106329353"];
}

- (void)openAppWithIdentifier:(NSString *)appId
{
    SKStoreProductViewController *storeProductVC = [[SKStoreProductViewController alloc] init];
    storeProductVC.delegate = self;
    
    NSDictionary *dict = [NSDictionary dictionaryWithObject:appId forKey:SKStoreProductParameterITunesItemIdentifier];
    [storeProductVC loadProductWithParameters:dict completionBlock:^(BOOL result, NSError *error) {
        if (result) {
            [self presentViewController:storeProductVC animated:YES completion:nil];
        }
    }];
}

- (void)productViewControllerDidFinish:(SKStoreProductViewController *)storeProductVC {
    [storeProductVC dismissViewControllerAnimated:YES completion:^{
        
        [self.navigationController popToRootViewControllerAnimated:YES];
    }];
}

7.copyWithZone:方法也可以返回一个可以打印的字符串

  1. 在使用 copy 方法,复制一个对象到一个新的地址时,必须实现copyWithZone:方法.

1.1 若实现时返回字符串,打印当前对象,输出的是字符串的内容
1.2 否则,打印的是当前对象的类名加地址

- (id)copyWithZone:(NSZone *)zone
{
    return @"哈哈";
}
- (void)test
{
    HSPerson *person1 = [[HSPerson alloc] init];
    person1.name = @"HanMei";
    person1.age = 18;
    
    HSPerson *person2 = [person1 copy];
    NSLog(@"%@---%@",person1,person2); // 结果是---哈哈
}

2.复制一个普通的对象

- (id)copyWithZone:(NSZone *)zone
{
    // 使用系统分配的内存 zone 创建相应对象,若使用 alloc+init 效果也一样,只是得另外开辟一段内存
    HSPerson *person = [HSPerson allocWithZone:zone];
    return person;
}
- (void)test
{
    HSPerson *person1 = [[HSPerson alloc] init];
    person1.name = @"HanMei";
    
    HSPerson *person2 = [person1 copy];
    NSLog(@"%@---%@",person1,person2); // 结果是---
    NSLog(@"%@---%@",person1.name,person2.name); // HanMei---(null)
}
  1. 由以上结果了解到, 这里的 copy 其实是深拷贝,地址发生了变化,简单了一些(不像 NSString 的 copy 那样,由于只是字符内容的变化,地址就不会变,想要地址变化,还得用 multyCopy)
  2. 但是打印 name 属性时,内容没有改变,需要在 copyWithZone: 方法中,给新的对象拷贝当前HSPerson对象的内容.
- (id)copyWithZone:(NSZone *)zone
{
    // 使用系统分配的内存 zone 创建相应对象,若使用 alloc+init 效果也一样,只是得另外开辟一段内存
    HSPerson *person = [HSPerson allocWithZone:zone];
    person.name = self.name;
    return person;
}

小结:由于 copyWithZone: 方法是个对象方法,因此可以直接调用当前对象的属性,来拷贝到另一个对象中,以做到深拷贝,降低耦合.

8. 跳到横屏控制器

1.在要弹出的控制器中,使用如下代码,即可让控制器横屏.

@implementation LandscapeVc

- (BOOL)shouldAutorotate
{
    return YES;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscape;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return UIInterfaceOrientationLandscapeLeft ;
}
@end

  1. 若要 present 一个导航控制器,则需要在导航控制器中写出以上代码,即可使该导航控制器下的所有控制器都可以横屏.
  2. 若要要 present 一个导航控制器,而且该导航控制器里面的某些控制器需要横屏,只对某些控制器使用以上代码是不生效的,必须在导航控制器中实现以上三个方法,返回当前最顶层控制器的横屏策略.

@implementation LandscapeNavVc

- (BOOL)shouldAutorotate
{
    return self.topViewController.shouldAutorotate;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return self.topViewController.supportedInterfaceOrientations;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return self.topViewController.preferredInterfaceOrientationForPresentation ;
}

@end

9. 让UIScrollView只沿着一个方向滑动

  1. 打开地理位置时,上下滑动 scrollView 的时候,左右回来回串动.
  2. 虽然给 pagingEnabled 属性设置成了 YES,但是在 scrollView 水平滑到一半的时候(不松手),却可以上下滑动.
  3. 本以为需要在 scrollViewDidScroll 等代理方法中来改 contentOffset 来取消滚动的效果,结果查了 UIScrollView 的头文件才发现确实有对应的属性 directionalLockEnabled 可以达到此目的,很方便.

10. block 中的UI操作无法实现

  1. block 代码中无法实现控件的隐藏,插入断点,打印日志,都是可以运行到该地的.
    2.打印一下线程,发现是当前线程的 id 是17,不是主线程,因此有可能操作失败.

11. {length = 2, path = 0 - 13}

  1. NSIndexPath对象其实是一个包装后的数组,length 是数组的个数,path 是每个元素的内容....
  2. tableview 的 length 默认是2,path 中第一个元素是 row, 第二个元素是13.

12. YYLabel 只显示一行

  1. 看层次结构, cell 的高度略高, 但是行高稍微有点儿低, 只显示一行
  2. 感觉是高度的问题, 就把 tableview 的行高设置高一点儿, 结果是正确的.
  3. 在显示YYLabel的时候, 使用YYTextLayout *layout = [YYTextLayout layoutWithContainerSize:containerSize text:attr];计算高度的时候, 要多加几个点的高度.

12. YYLabel 只显示一行

  1. YYLabel 在约束没有写全的情况下, 只会展示一行内容 , 代码如下:
    YYLabel *infoLabel = [[YYLabel alloc] init];
    [middleView addSubview:infoLabel];
    _briefContentLabel = infoLabel;
    [infoLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(seperateLine.mas_bottom).offset(cInfoViewTop);
        make.left.right.mas_equalTo(seperateLine);
    }];
  1. 注释掉以上代码 , 使用设置 frame 的方式代替自动布局 , 可以显示多行 , 说明是自动布局出问题了.
  2. 把该 YYLabel 的底部约束添加上就没这个问题了.
    YYLabel *infoLabel = [[YYLabel alloc] init];
    [middleView addSubview:infoLabel];
    _briefContentLabel = infoLabel;
    [infoLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(seperateLine.mas_bottom).offset(cInfoViewTop);
        make.bottom.mas_equalTo(middleView.mas_bottom);
        make.left.right.mas_equalTo(seperateLine);
    }];

13. AFN 请求接口时 , 断言 请求的NSUrl对象为 nil .

  1. po 的时候 , url 是没问题的.
  2. 考虑是 url 中包含了特殊字符 , 需要转义一下 , 可以用以下代码
    url = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  1. 若请求中含有中文字符 , 可以使用以下代码进行转义
string = [string stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

14. 适配 iPhone X 的时候 , 屏幕高度始终为 667.

  1. 查看层次结构 , 高度是 667 , 即 iPhone 6 的高度 .
  2. 在 application:didFinishLaunchingWithOptions: 方法的最开始打印高度 , 也是667.
  3. 创建一个空的项目 , 在 iPhone X 上面跑 , 高度是812 , 正确的 .
  4. 参考了这篇文章 https://segmentfault.com/a/1190000011308923 ,
    我开始怀疑是启动图片的大小决定了当前屏幕的高度 .
  5. 然后将启动图片 ( Images.xcassets --> LaunchImage ) 中的配置文件 Contents.json 的images 对应的 value 值中 , 添加如下内容 , 并添加图片名为"2017-04-19-Ios-1125 X [email protected]"的图片到同级目录下 .
 {
      "orientation" : "portrait",
      "idiom" : "iphone",
      "filename" : "2017-04-19-Ios-1125 X [email protected]",
      "extent" : "full-screen",
       "minimum-system-version" : "11.0",
      "subtype" : "2436h", 
      "scale" : "3x"
},

15. 红色叹号的invalid bitcode signature

  1. 在替换库文件的时候 , 有时候编译会出现下图所示的错误
image.png
  1. 尝试了这里https://juejin.im/entry/5948c3b88d6d81cc72fd2c5e所说的前几种办法 , 没什么好的效果.

  2. 接着尝试 : 把红色叹号文字上面的灰色叹号里面的日志拷贝到到记事本上面 , 多次看到了这几个关键字 x86_64 DerivedData .

  3. 然后我开始着手删掉 DerivedData 文件夹 , 问题就解决了. (快捷键 cmd + ,打开设置框 , 并点击顶部的 location 菜单 , 在该菜单下点击进入按钮)

常见问题 (一)_第1张图片
image.png

16. 解析字符串中的 json 串

  1. 先将字符串中包含的内容转换成 NSData 二进制对象 , 接着把 NSData 二进制对象作为 json 数据转换成相应的字典.
  2. 代码如下:
  NSData *data = [@"{\"aa\":\"bb\"}" dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
  1. 参考 http://www.jianshu.com/p/3d0df4d122c4 .

17. 将 debug安装包 安装到 xcode 模拟器中

  1. 终端 命令如下:
ios-sim launch /Users/liuzhu/Desktop/PandaTV-ios.app --devicetypeid iPhone-X
  1. 直接复制有可能会出现问题 , 建议直接输入 , 可参考 http://www.jianshu.com/p/cd4c816111db

18. "xx.app"已损坏,打不开。您应该将它移到废纸篓

  1. 当出现"xx.app"已损坏,打不开。您应该将它移到废纸篓提示的时候,则在“系统偏好设置”==>“安全性与隐私”==》“通用”==》“允许从以下位置加载的应用”,选择“任何来源”即可使用

  2. macOs sierra版本系统可能没有该选项,需要在终端中输入 sudo spctl --master-disable 命令来打开以上选项。如果想关闭则再次输入命令就可以了。

19. Embedded binary's bundle identifier is not prefixed with the parent app's bundle identifier.

  1. 详细信息如下:
error: Embedded binary's bundle identifier is not prefixed with the parent app's bundle identifier.

        Embedded Binary Bundle Identifier:  $(PRODUCT_BUNDLE_IDENTIFIER)
        Parent App Bundle Identifier:       com.PandaTV.Live-iPhone
  1. 检查一下 bundle id , 和以前一样 , 没什么问题.
  2. 使用一个神奇的快捷键 cmd + option + shift + k 清理一下缓存 ,就消除这个问题了 , 可能是合并代码的时候产生的缓存冲突.
  3. 遇到类似错误千万别紧张 , 往往是编译器的 bug

20 . UILabel 在设置attributedText 的值时 , numberOfLines属性不生效

  1. 设置 attributedText 的值之后 , 需要同时设置 lineBreakMode 属性 , 才能让超出的文字显示...

21. 取消 UITableView 的 cell 被选中的方法

1> 在创建一个继承于 UITableViewCell 的类的时候 , 系统会自动为我们添加以下代码

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];
    // Configure the view for the selected state
}

注释掉 [super setSelected:selected animated:animated];这行代码 , 就不会有选中的效果了.

2> 在 UITableViewCell 的子类中设置属性self.selectionStyle = UITableViewCellSelectionStyleNone;
3> 对于想被短暂选中的效果 , 可以在 didselect 那个代理方法中取消选中

22. 继承 UITableViewCell 时 , 重写 init 方法没有被调用

1> 重写 init 方法 , 没有效果 , 插断点没有执行 ; 继续重写 initWithFrame: 方法 , 断点没有执行依旧没有执行 ; 在输入- init 时 , 系统提示initWithStyle:reuseIdentifier:方法 , 断点却执行了.
2> 对于使用UITableView的initWithFrame:style:方法创建的 cell , 其 cell 需要使用 initWithStyle:reuseIdentifier: 方法来重载.

23. 触发按钮点击事件的方法

可以通过给 UIButton控件调用sendActionsForControlEvents:方法 , 触发按钮点击事件.

24. 子视图的动画超出了父视图

对于这种情况 , 可以有两种方法.
1> 可以使用 UIView 控件的 clipToBounds 属性 , 将其设置为 YES , 超出当前控件的子视图部分都会被裁剪.

2> 可以使用 UIView控件的 layer 属性的 mask 属性 , 对其赋值一个 CALayer 属性(矩形) 或 CAShapeLayer 属性 (曲线) .
① 通过设置该 mask 属性的 frame 或 path 和 backgroundColor , 来让超出该 frame 或 path 区域的子控件 , 裁减掉超出部分 , 只显示当前区域的内容.
② CAShapeLayer 是 CALayer 的子类 , 派生了几个一些绘制方面的属性 .
③ CALayer 对象的 backgroundColor 属性是一定要设置的, 否则设置的 frame 会变成 CGRectZero.

        CALayer *layer = [CALayer new];
        // 此处颜色必须设置,不加就不显示
        layer.backgroundColor = [UIColor redColor].CGColor;
        layer.frame = CGRectMake(-13, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
        //设置图形样子
        _giftAnimationViewContainer.layer.mask = layer;
/// 给已经存在的 UIImageView 添加圆角
- (void)recreateImageViewShapeLayer:(CGSize)size radius:(NSInteger)r {
    CGRect dstBounds = CGRectMake(0, 0, size.width, size.height);
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:dstBounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(r, r)];
    
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
    //设置大小
    maskLayer.frame = dstBounds;
    //设置图形样子
    maskLayer.path = maskPath.CGPath;
    self.layer.mask = maskLayer;
}

25. 微信的选中效果

长按微信的输入框, 会选中所有的文字, 并弹出编辑工具条, "复制 转发 "等, 有两种思路.

1> UITextView:

  1. UITextView 控件获取焦点后, 默认是会弹出输入框的, 并且变成第一响应者(FirstResponder).
  2. ① 目标是不弹出输入框, 就需要在UITextView的代理方法 textViewShouldBeginEditing: 中返回 NO, 禁止输入框的弹出, 也同时移除了光标, 像一个可以上下滚动的 UILabel.
  3. ② 目标是弹出系统的工具栏. 可是将上述代理方法返回 NO 以后, 就不会弹出系统的编辑框了. 这时就需要把其 editable 属性置为 NO, 就不会执行它的代理方法了, 自动弹出了系统工具栏.

26. 隐藏 iPhone X 全屏视频时的虚拟按键

  1. 隐藏 : 可以通过重载UIViewController的UIHomeIndicatorAutoHidden分类的prefersHomeIndicatorAutoHidden方法, 通过将其返回 YES 来让系统自动隐藏虚拟按键, 返回 NO 来禁止隐藏.
  2. 全屏的时候隐藏 : 在设置当前是否是全屏的状态 _isfullscreen 以后, 可以让prefersHomeIndicatorAutoHidden方法返回 _isfullscreen 的值, 然后通过调用setNeedsUpdateOfHomeIndicatorAutoHidden方法来让系统触发prefersHomeIndicatorAutoHidden方法, 进而确定虚拟按键的显示或隐藏.
  3. 代码如下 :
/**
 适配 IPhoneX
 */
- (void)hideHomeIndicatorForIPhoneX
{
    if (@available(iOS 11.0, *))
    {
        [self setNeedsUpdateOfHomeIndicatorAutoHidden];
    }
}

- (BOOL)prefersHomeIndicatorAutoHidden
{
    return _isfullscreen;
}

  1. 在主工程中, 系统方法没有调用, 可是 demo 中却可以调用 :
    1> 查阅资料, 发现在有 UINavigationController 的控制器中隐藏虚拟按键, 需要在继承它的类中重载childViewControllerForHomeIndicatorAutoHidden方法, 返回self.topViewController 内容, 没有效果.
    2> 以此内推, 应该把 UINavigationController 的底层控制器 UITabBarController 的childViewControllerForHomeIndicatorAutoHidden方法也重载掉, 返回 selectedViewController 内容, 但是依旧没有效果.
    3> 从 main.storyboard 到 UITabBarController, 再到 UINavigationController, 已经连通了, 却依旧没什么效果.
    4> 同事建议从 AppDelegate.m 中查找线索, 因为 main.storyboard 也是从 AppDelegate.m 加载的, 有可能中间在创建的过程出现了点儿问题.
    5> 在仔细查找 AppDelegate.m 的代码中, 检测到 appdelegate 的 window 的 rootViewController属性是一个侧边栏控制器, 而不是 storyboard 中的初始控制器, 而且侧边栏的控制器中包含了添加storyboard 中的初始控制器的功能. 于是我就在侧边栏控制器中重载childViewControllerForHomeIndicatorAutoHidden方法, 返回storyboard 中的初始控制器UITabBarController, 结果奇迹诞生了, 这条线顺利地联通了.

27. UpdatePublicKey: database is locked

  1. locked 一般是服务器卡死的原因

28. Command /usr/bin/codesign failed with exit code 1

  1. 用一个新的机器进行真机调试, 结果在弹出 code sign 密码验证时, 误点了 cancel, 接着就出现了一系列问题, 因开发者证书有误而无法编译.
  2. 参考这篇博客的方法http://blog.sina.com.cn/s/blog_85c1f6a50100zxz1.html, 打开钥匙串, 删除了名称以 iPhone Develop 开头的所有证书, 关闭钥匙串, 并在 xcode 中使用 command + ,打开设置面板, 移除我的账号并重新添加, 又提示了新的错误 : "Your account already has a signing certificate for this machine but it is not present in your keychain. To create a new one, you must first revoke the existing certificate."
  3. 我删了好几次, 却依然出现同样的错误, 碰巧看到大神的回答https://stackoverflow.com/questions/46881907/cant-run-xcode-project-on-device-due-to-certificate-issues,在这种情况下------点击 Preferences -> Accounts -> Manage Certificates 依旧是证书 "Missing Private Key" ,可以通过重启电脑的方式, 重新让 xcode 调用 code sign 工具, 获取 Mac 登录密码及权限, 结果就可以正常运行了.

29. pod 导入的文件, file not find

  1. 切到一个分支时, 在 #import 头文件的地方, 提示文件查找失败, 在 project navigator 中, 却能找到这个文件, 而且是在 pod 中的.
  2. 重新更新了一下 pod, 并使用 command + shift + K 完全清理一下缓存, 还是出现这个问题.
  3. 重启一下 xcode, 再使用 command + shift + K 完全清理一下缓存, 结果就可以正常运行了, 看来是 xcode 的问题.
  4. 正确的 Podfile 文件如下:
use_frameworks!

platform :ios, ‘8.0‘

target "RACCommand" do
    pod ‘ReactiveCocoa‘, ‘2.1.8‘
end

30.当前时区的时间

[NSDate dateWithTimeIntervalSince1970:[ [NSDate date] timeIntervalSince1970]]

31. The file “CycleProgressBar” couldn’t be opened because you don’t have permission to view it.

重启一下 xcode 或者 电脑 即可解决此问题.

33. 比较字符串的大小, 可以用字符串的 compare 方法.

// 比较两个字符串,结果为result1:0,result2:1,result3:-1
-(void)ComparerTwoString
{
    NSString *str1 = @"This is String1";
    
    NSString *str2 = @"THIS is String2";
    
    // 比较两个字符串是否相等
    BOOL result1 = [str1 isEqualToString:str2];
    
    // 比较两个字符串(comparer方法返回三种值:NSOrderedAscending = -1L, NSOrderedSame, NSOrderedDescending)
    
    NSComparisonResult result2 = [str1 compare:str2];
    
    // 不考虑大小比较字符串
    NSComparisonResult result3 = [str1 caseInsensitiveCompare:str2];
    
    NSLog(@"result1:%d,result2:%ld,result3:%ld",result1,(long)result2,(long)result3);
    
}

34. UIScrollView无法滑动

  1. 第一感觉像是某些按钮遮住了 UIScrollView, 但不是它的子视图...通过层次结构图, 发现这种猜想是错误的.
  2. 想起了在 UIScrollView 中添加子视图, 使用自动布局时, 要设置6个约束, 然后就设置了它的两个直接子视图六个约束, 依然不能滑动...检查 UIScrollView父视图设置它约束的地方, 是4个约束, 没有问题.
  3. 然后进入 UIScrollView 头文件, 发现了 contentsize, 默认值是CGSizeZero, 于是设置成和屏幕一致大小CGSizeMake(SCREEN_WIDTH, SCREEN_HEIGHT), 无果.
  4. 看其他地方的写法, 发现是 contentsize 的高度设置的太小了, 改大一点儿后, 依然不能滑动.
  5. 换个方式, 把当前视图从工程中抽出来, 单独运行在一个空项目中, 结果依旧无法滑动, 我在父视图中, 把 UIScrollView 的高度改得特别小, 结果竟然可以滑动了... 这就是 demo 的好处, 可以天马星空得开拓自己的思维, 让我离成功又近了一半距离...
  6. 我开始怀疑主工程的 contentsize 在其他地方被设置了, 于是我在任意一个按钮事件中插入断点, 打印当前UIScrollView, 结果 contentsize 的高度居然和 UIScrollView 的高度一样, 这就证明了我的猜想是正确的.
  7. 我在调用 UIScrollView 的地方, 也就是懒加载的返回处始终加载设置 contentsize 那句话, 结果奇迹出现了, 顺利地滑动了.
  8. 但问题又来了, 为什么滑到最下面, 底部视图的颜色明显变小? 很容易猜想到是第二个子视图在设置靠近 self.view底部的高度时, 设置小了, 如下:
    CGFloat headerHeight = fitIPHONE6Width(371);
    CGFloat offset = fitIPHONE6Width(5);
    CGFloat bottomHeight = SCREEN_HEIGHT - fitIPHONE6Width(371) - [PTVCarTeamThemeHeaderView viewHeight] - offset;

问题就出在了, 设置成了屏幕内的高度, 而不是需要滑动的高度.把bottomHeight 改成fitIPHONE6Width(370)就完美了, 显然比 iPhone 6的屏幕高度667大, 可以正常看到滑动以外的区域.

  1. 小问题又出来了, UIScrollView 的 contentoffset 由自动变成了(0,-20), 我又在每次调用 UIScrollView 的地方, 设置了 contentoffset 为(0,0), 结果一切正常了....看到20, 想起了状态栏的高度, 发现自己的导航栏是隐藏的, 想起了自己曾经的一篇博客, UIViewController 的 automaticallyAdjustsScrollViewInsets 属性默认是 yes, 也就是在导航栏隐藏的时候, 自动设置 scrollview 的 contentinset, 设置成 NO 就解决了此问题.
  2. 总结: 一定要化繁为简(demo), 一定要多使用断点 来解决问题.

35. 最简单快速的动画代码

// CGAffineTransformMakeTranslation的参数位置是相对于坐标原点(0,0)的偏移, 即屏幕左上角.
- (void)showActionsViewWithAnimation{
    self.actionsView.hidden = NO;
    self.actionsView.transform =  CGAffineTransformMakeTranslation(SCREEN_WIDTH, 0);
    [UIView animateWithDuration:0.5 animations:^{
        self.actionsView.transform = CGAffineTransformIdentity;
    }];
}
- (void)hideCurrentViewWithAnimation:(void (^)())completion{
    self.transform = CGAffineTransformIdentity;
    [UIView animateWithDuration:0.5 animations:^{
        self.transform = CGAffineTransformMakeTranslation(SCREEN_WIDTH, 0);
    } completion:^(BOOL finished) {
        self.hidden = YES;
        if (completion) {
            completion();
        }
    }];
}
- (void)showWithAnimation{
    self.transform = CGAffineTransformMakeScale(0, 0);
    [UIView animateWithDuration:0.25 animations:^{
        self.transform = CGAffineTransformIdentity;
    }];
}
// CGAffineTransformMakeScale的参数位置是原尺寸的倍数, 设置成0, 0 会看不到动画, 因此同时添加了透明度的动画.
- (void)hideWithAnimation:(void (^)())completion{
    self.transform = CGAffineTransformIdentity;
    self.alpha = 0.5;
    [UIView animateWithDuration:0.25 animations:^{
        self.alpha = 0;
        self.transform = CGAffineTransformMakeScale(0.1, 0.1);
    } completion:^(BOOL finished) {
        if (completion) {
            completion();
        }
    }];
}

36. 最简单的让 UICollectionView 或 UITableView 无限滑动

  1. 思路: 很简单, 就是让 UICollectionView 或 UITableView 的 cell 数目翻倍, 滑到最左边或最右边时, 默默地在后台切到正中间.
  2. 方法: 通过 contentoffset 来判断是否滑到了最左边contentoffset <= 0, 是否滑到了最右边contentoffset >= contentsize - unitsize
  3. 调用: 在scrollViewDidScroll:方法和layoutSubviews:方法中调用, layoutSubviews:方法适用于 scrollview 中添加 uiview, uiview 又添加了 scrollview 的情况, 第一个默认居中.
  4. 代码如下
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
    [self updatePager];
    
    UICollectionView *collectionView = (UICollectionView *)scrollView;
    if (self.offsetLength <= 0)
    {
        [collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:self.arrayData.count inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
    }else{
        if (self.offsetLength >= self.contentLength - self.unitLength) {
            [collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:self.arrayData.count - 1 inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
        }
    }
}

- (void)layoutSubviews{
    [super layoutSubviews];
    [self scrollViewDidScroll:self.actionListView];
}

- (void)updatePager
{
    UICollectionView *collectionView = (UICollectionView *)self.actionListView;
    self.pageControl.currentPage = (NSInteger)(floorf(collectionView.contentOffset.x / collectionView.frame.size.width))% self.arrayData.count;
}

37. 视图不显示的几种情况

  1. 背景颜色为白色或无色.
  2. 大小设置为0.
  3. 视图被设置为 hidden, 此时层次结构图是看不到的.

38. 崩溃MASViewConstraint install

  1. 这种问题往往是由于父视图为 nil 引起的.
  2. 定位到第328行的函数内部, 会看到断言"couldn't find a common superview".
  3. 在使用 masonry 布局的时候, 最好先确定父视图是否存在, 如果不存在的话, 就不要创建视图.

39. 让 UIScrollView 中的视图不跟着滑动

  1. 在 UIScrollView 中添加位置不变的视图, 可以更改子视图的布局.
  2. 例如不让子视图向下滑动, 就让子视图的顶部相对于 UIScrollView 的父视图添加约束, 代码如下
   UIView *keyWindow = [UIApplication sharedApplication].keyWindow;
     [self.accountView mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(keyWindow);
        make.left.mas_equalTo(self.tableView);
        make.size.mas_equalTo(CGSizeMake(self.tableView.frame.size.width, acountHei));
     }];
  1. 对于让 UIScrollView 禁止拖拽但可以自动滑动, 类似跑马灯的效果, 可以在 UIScrollView 的兄弟视图上添加一个UIView, 遮盖在它上面, 阻拦它的事件.

40. 程序设计常见问题

  1. 倒计时 结束后 按钮 置灰显示 , 注意 置灰后 再次抽到 新开启的PK 按钮要回复可点击状态 .
  2. ① 连续点击送礼物按钮, 会影响倒计时的匀速变化, 很有可能是送礼的请求收到以后, 进行了一些耗时操作.
    ② 第一感觉是, 送完礼物就是简单的请求礼物列表的操作嘛, 可是实际却是, 请求完礼物, 会删除旧的礼物列表, 重新建一个新的礼物列表.

41. 使用 hittest 时, 需要考虑隐藏的视图.

  1. 在循环创建视图时, 导致每个视图中都调用了 hittest方法, 需求是这些视图的位置都一样, 显示其中一个, 隐藏其余的所有视图.
  2. 点击某个已经显示视图的 hittest 时, 有可能响应已经隐藏的视图的 hittest, 进而导致间歇性地触发隐藏视图的事件.... 因此要在 hittest 中判断当前视图是否隐藏.
  3. 点击视图时, 不会响应当前视图, 也没有触发当前视图的 hittest 方法, 而是①响应了下面的兄弟视图....②意外发现, 点击当前视图的关闭按钮竟可以响应, 也没有触发当前视图的 hittest 方法, 在其他地方肯定有设置…. ③全局查找这个视图中的相应按钮, 才发现在第1部被响应的视图的 hittest方法, 有返回当前视图的关闭按钮的返回值....④解决方案: 在当前视图不隐藏时, 如果点击区域也在当前视图中, 就在第1部被响应的视图的 hittest方法中, 返回当前视图.

42. iOS控制代码段大小在60M 以内

参考文章 http://www.iqiyi.com/common/20171130/d9534cf00c408f06.html.

44. UITableView 的底部有多余的下划线

  1. 查看层次结构图, 发现系统创建了很多分割线视图.
  2. 可以给 UITableView 的 tableFooterView 属性设置为一个带有背景颜色的视图, 宽高只要大于0就行.

45. 使用 SourceTree push 代码时弹出 password required

  1. 使用如下设置 "仓库 --> 仓库设置 --> 远程仓库 -->双击路径下方表格的第一项 --> 修改 url 路径"
  2. 以上 url 路径如下https://用户名:密码@hostname.com/projectname.git
    如:http://liuzhu:mima@clientgit3.***.com:3000/iOS/***.git
  3. "托管类型"后选择"GitHub", 用户名:"liuzhu"
  4. 点击确定即可.

46. git 代码提交错误, 需要回退到某个提交

  1. $ git reflog 查看当前的操作
1e9668c7ad (HEAD -> HeroNewSkill_new) HEAD@{0}: commit (merge): Merge branch 'CarteamOptimize' into HeroNewSkill_new
73be8bf1ec (origin/HeroNewSkill_new) HEAD@{1}: pull origin HeroNewSkill_new: Fast-forward

  1. $ git checkout -b hero_test 73be8bf1ec 在对的操作对应的 id 处, 切到新的分支hero_test.

47.比较容易忽略的循环引用

以下代码中, tableview 强引用 cell, cell 又会在 block 中强引用 tablview, 造成循环引用, 无法释放内存, 需要使用 weak 用一个局部变量解开强引用的环.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identifier = @"PTVShortVideoListCell";
    
    PTVShortVideoItemTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];

   __weak UITableView *tableViewWeak = tableView;
        cell.changeEnableScroll = ^(BOOL enable) {
            tableViewWeak.scrollEnabled = enable;
        };
}

48. 从系统浏览器通过 schema 打开 app , APP崩溃

  1. 检查 schema, 是让 app 打开系统浏览器的
  2. 使用排除法, 在 app 中打开该 schema, 不会崩溃, 而且很快就打开了系统浏览器.
  3. 在 app 已经打开的时候, 从系统浏览器通过 schema 打开 app, APP 却没有崩溃.
  4. 于是我就加了一个判断, 如果是从浏览器 openurl 打开的 app, 就延迟两秒响应打开系统浏览器操作, 结果属实没有崩溃, 推断是启动时CPU 高负载造成的崩溃.
  5. 在一个简单 demo 程序里的didFinishLaunchingWithOptions:方法中使用 openurl 方法, 结果卡顿了几秒后才绘制首页 UI, 响应 schema.

49. 提交代码到错误的分支上, 可以使用 git cherry-pick xx命令补救

  1. 代码提错分支, 不要慌, 可以在错误的分支输入git log查看提交内容上方相应的 MD5值, 如 "bfa449aa32cd224f1bffcc5fa410c454d4ffd6ab".
  2. 切到目标分支, 执行命令 git cherry-pick bfa449aa32cd224f1bffcc5fa410c454d4ffd6ab 在当前分支添加以上 MD5值对应的提交.

总结: cherry-pick 只能检出单个提交, 并无视错误分支之前或之后的提交, 这点对提错代码且同事有提交的情况, 非常有帮助.

50. 一个视图, 在横屏和竖屏都需要展示, 可以在切换屏幕时切换父视图.

原理: removefromsuperview 只是移除了父视图, 对于被父视图强引用的子视图来说, 不会立即释放., 可以被 addsubview 到新的视图上面, 而数据不变.

51. 有 UIScrollView 的视图, 无法左滑退出

一行代码搞定, 手势有冲突的时候, UIScrollview 理所当然地把手势传给导航控制器的手势.

    [self.mainScrollView.panGestureRecognizer requireGestureRecognizerToFail:self.navigationController.interactivePopGestureRecognizer];

52. 运行 Xcode 提示: You don't have permission to access

使用快捷键 cmd+option+shift+k, 清理缓存文件之后, 就没问题了.

53. MJRefresh上拉刷新到最底部后, 下拉刷新只能刷新一页

在使用分类属性 mj_footer 的 endRefreshingWithNoMoreData方法时, 一定要在下拉刷新成功时, 用 mj_footer 的 resetNoMoreData 方法将无数据状态重置, .

55. 响应推送通知的系统方法, 有两个

  1. application:didReceiveRemoteNotification:fetchCompletionHandler:
  2. userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler

60. git stash 暂存当前的测试代码到缓存栈.

切换分支或更新代码时, 可以临时保存目前的提交到栈区.

git stash “‘储藏”“可以获取你工作目录的中间状态——也就是你修改过的被追踪的文件和暂存的变更——并将它保存到一个未完结变更的堆栈中,随时可以重新应用git stash apply stash@{2}, 并通过git stash list查看列表。

在 sourcetree 中可以点击 "暂存" 来实现, 并通过 stash 列表查看, 右击对应的项目来应用或删除贮藏.


61. 误删git远程分支

  1. 每次打开 sourcetree, 都会提示我有新的提交. 于是我决定删掉远程分支, 把本地分支推到服务器端.
  2. 在删除了远程服务器分支以后, 突然发现本地的对应分支也没了~~~(>_<)~~~
  3. 想起 git reflog 可以查看最近的操作记录如下
ecdd7f7b69 (origin) HEAD@{8}: pull --rebase origin userHomePage: 更新共同关注的
人的列表

  1. 在其父分支, 使用git reset --hard HEAD@{8}命令, 便可以把刚刚分支提交的所有内容都合并到当前分支上.
    (PS: git reset会忽略当前提交后的所有提交, 不会有 commit; git revert 会在最新的提交后添加一个混滚的 commit)
  2. 然后从当前分支拉一个新分支, 即可恢复.

你可能感兴趣的:(常见问题 (一))