- 连续使用多目运算符要谨慎,容易漏掉特殊的情况
- 点击弹出的微信消息再返回,点击某些控件不能隐藏(默认点击隐藏/显示)
- 操场形状的进度条.
- 显示两位小数,不够的以0来替补
- 箭头居中的气泡
- 两种不同的App Store 跳转方式:
- copyWithZone:方法也可以返回一个可以打印的字符串
- 跳到横屏控制器
- 让UIScrollView只沿着一个方向滑动
- block 中的UI操作无法实现
- <NSIndexPath: 0xc000000001a00016> {length = 2, path = 0 - 13}
- YYLabel 只显示一行
- AFN 请求接口时 , 断言 请求的NSUrl对象为 nil .
- 适配 iPhone X 的时候 , 屏幕高度始终为 667.
- 红色叹号的invalid bitcode signature
- 解析字符串中的 json 串
- 将 debug安装包 安装到 xcode 模拟器中
- "xx.app"已损坏,打不开。您应该将它移到废纸篓
- Embedded binary's bundle identifier is not prefixed with the parent app's bundle identifier.
- UILabel 在设置attributedText 的值时 , numberOfLines属性不生效
- 取消 UITableView 的 cell 被选中的方法
- 继承 UITableViewCell 时 , 重写 init 方法没有被调用
- 触发按钮点击事件的方法
- 子视图的动画超出了父视图
- 微信的选中效果
- 隐藏 iPhone X 全屏时的虚拟按键
- UpdatePublicKey: database is locked
- Command /usr/bin/codesign failed with exit code 1
- pod 导入的文件, file not find
- 当前时区的时间
- The file “CycleProgressBar” couldn’t be opened because you don’t have permission to view it.
- A 跳到 B, B 跳到 C以后, 不想让用户看到 B, 可以通过更改UINavigationController的viewControllers.
- 比较字符串的大小, 可以用字符串的 compare 方法.
- UIScrollView无法滑动
- 最简单快速的动画代码
- 最简单的让 UICollectionView 无限滑动
- 视图不显示的几种情况
- 崩溃MASViewConstraint install
- 让 UIScrollView 中的视图不跟着滑动
- 程序设计常见问题
- 使用 hittest 时, 需要考虑隐藏的视图.
- iOS控制代码段大小在60M 以内
- 后台播放的定时器, 可以使用NSObject 的performSelector:...方法.
- UITableView 的底部有多余的下划线
- 使用 SourceTree push 代码时弹出 password required
- git rebase 代码时, 自己的代码合不见了, 可以通过查看 reflog 操作, 回退到某个提交
- 比较容易忽略的循环引用
- 从系统浏览器通过 schema 打开 app , APP崩溃
- 提交代码到错误的分支上, 可以使用 git cherry-pick xx命令补救
- 一个视图, 在横屏和竖屏都需要展示, 可以在切换屏幕时切换父视图.
- 有 UIScrollView 的视图, 无法左滑退出
- 运行 Xcode 提示: You don't have permission to access
- MJRefresh上拉刷新到最底部后, 下拉刷新只能刷新一页
- 图文混排, 可以使用NSAttributedString+YYText.h分类的方法
- 响应推送通知的系统方法, 有两个
- deletesection:方法解决 MJFresh 数据较少时刷新列表, footer 无法自动归位的问题
- deletesection:崩溃
- YYLabel 不仅要设置4个约束, 而且在更新 textLayout 时要设准 contentsize.
- pause program execusion 查找dispatch_semaphore_wait
- git stash 暂存当前的测试代码到缓存栈.
- 误删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> 上老司机杀手锏------断点
- 在控件被触发的地方插入断点.
- 使用 MAC qq 给自己的手机发消息,并狂点手机的顶部,一边让顶部控件隐藏/显示,一边等待 qq 消息的弹出.
- 在 qq 中编辑后,再返回,奇迹发生了------控件的触发方法被调用了,在一个与键盘变量有关的地方返回了.
- 全局搜索此方法名,发现是键盘显示/隐藏的通知调用的.
- 得出结论:键盘的显示/隐藏通知 是全局的,从当前 app 跳到其他 app 的瞬间,也会收到该通知.
- 解决方案:收到键盘相关的通知后,同时判断当前输入框是否是第一响应者.
3.操场形状的进度条.
- 可以使用 layer.mask 来给视图切圆角.
- 其子视图即使是方的,左侧也依旧会根据原始图的 mask 路径来切圆角,右侧是直的.
- 创建的代码如下:
/// 创建一个可以切掉圆角的 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
- 观察头文件,看到了decimalNumberWithString:快速构造方法,decimalNumberByRoundingAccordingToBehavior:行为构造方法.
- 既然是小数精度构造类,肯定有一个自定义的操作,来选择精度的制造方法,很容易想到从第一条的行为构造方法的参数------NSDecimalNumberBehaviors协议的对象 来构造.
- 进入该协议,看到了两个协议方法,貌似对精度确定没什么作用.
- 在当前头文件中搜索NSDecimalNumberBehaviors,不经意间找到了遵循该协议的NSDecimalNumberHandler
- 在当前对象中找到了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.箭头居中的气泡
- 由于 iOS 只能支持图片以一块区域为基准拉伸,因此可以准备两张图片,一张是可以左右拉伸的矩形,另一张是能遮住矩形底部边线的中心的箭头.
- 在文字伸缩的时候,设定矩形区域的长度和拉伸方式,并让箭头的水平中心和矩形的一致.箭头的顶部略微盖过矩形区域.
- 代码如下
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 跳转方式:
- 打开 App Store
- (void)openRateWindow
{
NSString *str = [NSString stringWithFormat:@"itms-apps://itunes.apple.com/app/id%@",@"1106329353"];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:str]];
}
- 弹出当前 App 评价页面的控制器.
#import <StoreKit/StoreKit.h>
- (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:方法也可以返回一个可以打印的字符串
- 在使用 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); // 结果是<HSPerson: 0x60800002f1c0>---哈哈
}
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); // 结果是<HSPerson: 0x60800003cc00>---<HSPerson: 0x60800003cd60>
NSLog(@"%@---%@",person1.name,person2.name); // HanMei---(null)
}
- 由以上结果了解到, 这里的 copy 其实是深拷贝,地址发生了变化,简单了一些(不像 NSString 的 copy 那样,由于只是字符内容的变化,地址就不会变,想要地址变化,还得用 multyCopy)
- 但是打印 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
- 若要 present 一个导航控制器,则需要在导航控制器中写出以上代码,即可使该导航控制器下的所有控制器都可以横屏.
- 若要要 present 一个导航控制器,而且该导航控制器里面的某些控制器需要横屏,只对某些控制器使用以上代码是不生效的,必须在导航控制器中实现以上三个方法,返回当前最顶层控制器的横屏策略.
@implementation LandscapeNavVc
- (BOOL)shouldAutorotate
{
return self.topViewController.shouldAutorotate;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return self.topViewController.supportedInterfaceOrientations;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return self.topViewController.preferredInterfaceOrientationForPresentation ;
}
@end
9. 让UIScrollView只沿着一个方向滑动
- 打开地理位置时,上下滑动 scrollView 的时候,左右回来回串动.
- 虽然给 pagingEnabled 属性设置成了 YES,但是在 scrollView 水平滑到一半的时候(不松手),却可以上下滑动.
- 本以为需要在 scrollViewDidScroll 等代理方法中来改 contentOffset 来取消滚动的效果,结果查了 UIScrollView 的头文件才发现确实有对应的属性 directionalLockEnabled 可以达到此目的,很方便.
10. block 中的UI操作无法实现
- block 代码中无法实现控件的隐藏,插入断点,打印日志,都是可以运行到该地的.
2.打印一下线程,发现是当前线程的 id 是17,不是主线程,因此有可能操作失败.
11. <NSIndexPath: 0xc000000001a00016> {length = 2, path = 0 - 13}
- NSIndexPath对象其实是一个包装后的数组,length 是数组的个数,path 是每个元素的内容....
- tableview 的 length 默认是2,path 中第一个元素是 row, 第二个元素是13.
12. YYLabel 只显示一行
- 看层次结构, cell 的高度略高, 但是行高稍微有点儿低, 只显示一行
- 感觉是高度的问题, 就把 tableview 的行高设置高一点儿, 结果是正确的.
- 在显示YYLabel的时候, 使用
YYTextLayout *layout = [YYTextLayout layoutWithContainerSize:containerSize text:attr];
计算高度的时候, 要多加几个点的高度.
12. YYLabel 只显示一行
- 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);
}];
- 注释掉以上代码 , 使用设置 frame 的方式代替自动布局 , 可以显示多行 , 说明是自动布局出问题了.
- 把该 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 .
- po 的时候 , url 是没问题的.
- 考虑是 url 中包含了特殊字符 , 需要转义一下 , 可以用以下代码
url = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- 若请求中含有中文字符 , 可以使用以下代码进行转义
string = [string stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
14. 适配 iPhone X 的时候 , 屏幕高度始终为 667.
- 查看层次结构 , 高度是 667 , 即 iPhone 6 的高度 .
- 在 application:didFinishLaunchingWithOptions: 方法的最开始打印高度 , 也是667.
- 创建一个空的项目 , 在 iPhone X 上面跑 , 高度是812 , 正确的 .
- 参考了这篇文章 https://segmentfault.com/a/1190000011308923 ,
我开始怀疑是启动图片的大小决定了当前屏幕的高度 . - 然后将启动图片 ( Images.xcassets --> LaunchImage ) 中的配置文件 Contents.json 的images 对应的 value 值中 , 添加如下内容 , 并添加图片名为"2017-04-19-Ios-1125 X 2436@3x.png"的图片到同级目录下 .
{
"orientation" : "portrait",
"idiom" : "iphone",
"filename" : "2017-04-19-Ios-1125 X 2436@3x.png",
"extent" : "full-screen",
"minimum-system-version" : "11.0",
"subtype" : "2436h",
"scale" : "3x"
},
15. 红色叹号的invalid bitcode signature
- 在替换库文件的时候 , 有时候编译会出现下图所示的错误
尝试了这里https://juejin.im/entry/5948c3b88d6d81cc72fd2c5e所说的前几种办法 , 没什么好的效果.
接着尝试 : 把红色叹号文字上面的灰色叹号里面的日志拷贝到到记事本上面 , 多次看到了这几个关键字 x86_64 DerivedData .
然后我开始着手删掉 DerivedData 文件夹 , 问题就解决了. (快捷键 cmd + ,打开设置框 , 并点击顶部的 location 菜单 , 在该菜单下点击进入按钮)
16. 解析字符串中的 json 串
- 先将字符串中包含的内容转换成 NSData 二进制对象 , 接着把 NSData 二进制对象作为 json 数据转换成相应的字典.
- 代码如下:
NSData *data = [@"{\"aa\":\"bb\"}" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
17. 将 debug安装包 安装到 xcode 模拟器中
- 终端 命令如下:
ios-sim launch /Users/liuzhu/Desktop/PandaTV-ios.app --devicetypeid iPhone-X
- 直接复制有可能会出现问题 , 建议直接输入 , 可参考 //www.greatytc.com/p/cd4c816111db
18. "xx.app"已损坏,打不开。您应该将它移到废纸篓
当出现"xx.app"已损坏,打不开。您应该将它移到废纸篓提示的时候,则在“系统偏好设置”==>“安全性与隐私”==》“通用”==》“允许从以下位置加载的应用”,选择“任何来源”即可使用
macOs sierra版本系统可能没有该选项,需要在终端中输入 sudo spctl --master-disable 命令来打开以上选项。如果想关闭则再次输入命令就可以了。
19. Embedded binary's bundle identifier is not prefixed with the parent app's bundle identifier.
- 详细信息如下:
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
- 检查一下 bundle id , 和以前一样 , 没什么问题.
- 使用一个神奇的快捷键 cmd + option + shift + k 清理一下缓存 ,就消除这个问题了 , 可能是合并代码的时候产生的缓存冲突.
- 遇到类似错误千万别紧张 , 往往是编译器的 bug
20 . UILabel 在设置attributedText 的值时 , numberOfLines属性不生效
- 设置 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:
- UITextView 控件获取焦点后, 默认是会弹出输入框的, 并且变成第一响应者(FirstResponder).
- ① 目标是不弹出输入框, 就需要在UITextView的代理方法 textViewShouldBeginEditing: 中返回 NO, 禁止输入框的弹出, 也同时移除了光标, 像一个可以上下滚动的 UILabel.
- ② 目标是弹出系统的工具栏. 可是将上述代理方法返回 NO 以后, 就不会弹出系统的编辑框了. 这时就需要把其 editable 属性置为 NO, 就不会执行它的代理方法了, 自动弹出了系统工具栏.
26. 隐藏 iPhone X 全屏视频时的虚拟按键
-
隐藏 : 可以通过重载UIViewController的UIHomeIndicatorAutoHidden分类的
prefersHomeIndicatorAutoHidden
方法, 通过将其返回 YES 来让系统自动隐藏虚拟按键, 返回 NO 来禁止隐藏. -
全屏的时候隐藏 : 在设置当前是否是全屏的状态 _isfullscreen 以后, 可以让
prefersHomeIndicatorAutoHidden
方法返回 _isfullscreen 的值, 然后通过调用setNeedsUpdateOfHomeIndicatorAutoHidden
方法来让系统触发prefersHomeIndicatorAutoHidden
方法, 进而确定虚拟按键的显示或隐藏. - 代码如下 :
/**
适配 IPhoneX
*/
- (void)hideHomeIndicatorForIPhoneX
{
if (@available(iOS 11.0, *))
{
[self setNeedsUpdateOfHomeIndicatorAutoHidden];
}
}
- (BOOL)prefersHomeIndicatorAutoHidden
{
return _isfullscreen;
}
-
在主工程中, 系统方法没有调用, 可是 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
- locked 一般是服务器卡死的原因
28. Command /usr/bin/codesign failed with exit code 1
- 用一个新的机器进行真机调试, 结果在弹出 code sign 密码验证时, 误点了 cancel, 接着就出现了一系列问题, 因开发者证书有误而无法编译.
- 参考这篇博客的方法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."
- 我删了好几次, 却依然出现同样的错误, 碰巧看到大神的回答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
- 切到一个分支时, 在 #import 头文件的地方, 提示文件查找失败, 在 project navigator 中, 却能找到这个文件, 而且是在 pod 中的.
- 重新更新了一下 pod, 并使用 command + shift + K 完全清理一下缓存, 还是出现这个问题.
- 重启一下 xcode, 再使用 command + shift + K 完全清理一下缓存, 结果就可以正常运行了, 看来是 xcode 的问题.
- 正确的 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无法滑动
- 第一感觉像是某些按钮遮住了 UIScrollView, 但不是它的子视图...通过层次结构图, 发现这种猜想是错误的.
- 想起了在 UIScrollView 中添加子视图, 使用自动布局时, 要设置6个约束, 然后就设置了它的两个直接子视图六个约束, 依然不能滑动...检查 UIScrollView父视图设置它约束的地方, 是4个约束, 没有问题.
- 然后进入 UIScrollView 头文件, 发现了 contentsize, 默认值是CGSizeZero, 于是设置成和屏幕一致大小CGSizeMake(SCREEN_WIDTH, SCREEN_HEIGHT), 无果.
- 看其他地方的写法, 发现是 contentsize 的高度设置的太小了, 改大一点儿后, 依然不能滑动.
- 换个方式, 把当前视图从工程中抽出来, 单独运行在一个空项目中, 结果依旧无法滑动, 我在父视图中, 把 UIScrollView 的高度改得特别小, 结果竟然可以滑动了... 这就是 demo 的好处, 可以天马星空得开拓自己的思维, 让我离成功又近了一半距离...
- 我开始怀疑主工程的 contentsize 在其他地方被设置了, 于是我在任意一个按钮事件中插入断点, 打印当前UIScrollView, 结果 contentsize 的高度居然和 UIScrollView 的高度一样, 这就证明了我的猜想是正确的.
- 我在调用 UIScrollView 的地方, 也就是懒加载的返回处始终加载设置 contentsize 那句话, 结果奇迹出现了, 顺利地滑动了.
- 但问题又来了, 为什么滑到最下面, 底部视图的颜色明显变小? 很容易猜想到是第二个子视图在设置靠近 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大, 可以正常看到滑动以外的区域.
- 小问题又出来了, UIScrollView 的 contentoffset 由自动变成了(0,-20), 我又在每次调用 UIScrollView 的地方, 设置了 contentoffset 为(0,0), 结果一切正常了....看到20, 想起了状态栏的高度, 发现自己的导航栏是隐藏的, 想起了自己曾经的一篇博客, UIViewController 的 automaticallyAdjustsScrollViewInsets 属性默认是 yes, 也就是在导航栏隐藏的时候, 自动设置 scrollview 的 contentinset, 设置成 NO 就解决了此问题.
- 总结: 一定要化繁为简(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 无限滑动
- 思路: 很简单, 就是让 UICollectionView 或 UITableView 的 cell 数目翻倍, 滑到最左边或最右边时, 默默地在后台切到正中间.
-
方法: 通过 contentoffset 来判断是否滑到了最左边
contentoffset <= 0
, 是否滑到了最右边contentoffset >= contentsize - unitsize
- 调用: 在scrollViewDidScroll:方法和layoutSubviews:方法中调用, layoutSubviews:方法适用于 scrollview 中添加 uiview, uiview 又添加了 scrollview 的情况, 第一个默认居中.
- 代码如下
-(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. 视图不显示的几种情况
- 背景颜色为白色或无色.
- 大小设置为0.
- 视图被设置为 hidden, 此时层次结构图是看不到的.
38. 崩溃MASViewConstraint install
- 这种问题往往是由于父视图为 nil 引起的.
- 定位到第328行的函数内部, 会看到断言"couldn't find a common superview".
- 在使用 masonry 布局的时候, 最好先确定父视图是否存在, 如果不存在的话, 就不要创建视图.
39. 让 UIScrollView 中的视图不跟着滑动
- 在 UIScrollView 中添加位置不变的视图, 可以更改子视图的布局.
- 例如不让子视图向下滑动, 就让子视图的顶部相对于 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));
}];
- 对于让 UIScrollView 禁止拖拽但可以自动滑动, 类似跑马灯的效果, 可以在 UIScrollView 的兄弟视图上添加一个UIView, 遮盖在它上面, 阻拦它的事件.
40. 程序设计常见问题
- 倒计时 结束后 按钮 置灰显示 , 注意 置灰后 再次抽到 新开启的PK 按钮要回复可点击状态 .
- ① 连续点击送礼物按钮, 会影响倒计时的匀速变化, 很有可能是送礼的请求收到以后, 进行了一些耗时操作.
② 第一感觉是, 送完礼物就是简单的请求礼物列表的操作嘛, 可是实际却是, 请求完礼物, 会删除旧的礼物列表, 重新建一个新的礼物列表.
41. 使用 hittest 时, 需要考虑隐藏的视图.
- 在循环创建视图时, 导致每个视图中都调用了 hittest方法, 需求是这些视图的位置都一样, 显示其中一个, 隐藏其余的所有视图.
- 点击某个已经显示视图的 hittest 时, 有可能响应已经隐藏的视图的 hittest, 进而导致间歇性地触发隐藏视图的事件.... 因此要在 hittest 中判断当前视图是否隐藏.
- 点击视图时, 不会响应当前视图, 也没有触发当前视图的 hittest 方法, 而是①响应了下面的兄弟视图....②意外发现, 点击当前视图的关闭按钮竟可以响应, 也没有触发当前视图的 hittest 方法, 在其他地方肯定有设置…. ③全局查找这个视图中的相应按钮, 才发现在第1部被响应的视图的 hittest方法, 有返回当前视图的关闭按钮的返回值....④解决方案: 在当前视图不隐藏时, 如果点击区域也在当前视图中, 就在第1部被响应的视图的 hittest方法中, 返回当前视图.
42. iOS控制代码段大小在60M 以内
参考文章 http://www.iqiyi.com/common/20171130/d9534cf00c408f06.html.
44. UITableView 的底部有多余的下划线
- 查看层次结构图, 发现系统创建了很多分割线视图.
- 可以给 UITableView 的 tableFooterView 属性设置为一个带有背景颜色的视图, 宽高只要大于0就行.
45. 使用 SourceTree push 代码时弹出 password required
- 使用如下设置 "仓库 --> 仓库设置 --> 远程仓库 -->双击路径下方表格的第一项 --> 修改 url 路径"
- 以上 url 路径如下
https://用户名:密码@hostname.com/projectname.git
如:http://liuzhu:mima@clientgit3.***.com:3000/iOS/***.git
- "托管类型"后选择"GitHub", 用户名:"liuzhu"
- 点击确定即可.
46. git 代码提交错误, 需要回退到某个提交
- $ 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
- $ 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崩溃
- 检查 schema, 是让 app 打开系统浏览器的
- 使用排除法, 在 app 中打开该 schema, 不会崩溃, 而且很快就打开了系统浏览器.
- 在 app 已经打开的时候, 从系统浏览器通过 schema 打开 app, APP 却没有崩溃.
- 于是我就加了一个判断, 如果是从浏览器 openurl 打开的 app, 就延迟两秒响应打开系统浏览器操作, 结果属实没有崩溃, 推断是启动时CPU 高负载造成的崩溃.
- 在一个简单 demo 程序里的didFinishLaunchingWithOptions:方法中使用 openurl 方法, 结果卡顿了几秒后才绘制首页 UI, 响应 schema.
49. 提交代码到错误的分支上, 可以使用 git cherry-pick xx命令补救
- 代码提错分支, 不要慌, 可以在错误的分支输入
git log
查看提交内容上方相应的 MD5值, 如 "bfa449aa32cd224f1bffcc5fa410c454d4ffd6ab". - 切到目标分支, 执行命令
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. 响应推送通知的系统方法, 有两个
application:didReceiveRemoteNotification:fetchCompletionHandler:
userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler
60. git stash 暂存当前的测试代码到缓存栈.
切换分支或更新代码时, 可以临时保存目前的提交到栈区.
git stash
“‘储藏”“可以获取你工作目录的中间状态——也就是你修改过的被追踪的文件和暂存的变更——并将它保存到一个未完结变更的堆栈中,随时可以重新应用git stash apply stash@{2}
, 并通过git stash list
查看列表。
在 sourcetree 中可以点击 "暂存" 来实现, 并通过 stash 列表查看, 右击对应的项目来应用或删除贮藏.
61. 误删git远程分支
- 每次打开 sourcetree, 都会提示我有新的提交. 于是我决定删掉远程分支, 把本地分支推到服务器端.
- 在删除了远程服务器分支以后, 突然发现本地的对应分支也没了~~~(>_<)~~~
- 想起
git reflog
可以查看最近的操作记录如下
ecdd7f7b69 (origin) HEAD@{8}: pull --rebase origin userHomePage: 更新共同关注的
人的列表
- 在其父分支, 使用
git reset --hard HEAD@{8}
命令, 便可以把刚刚分支提交的所有内容都合并到当前分支上.
(PS: git reset会忽略当前提交后的所有提交, 不会有 commit; git revert 会在最新的提交后添加一个混滚的 commit) - 然后从当前分支拉一个新分支, 即可恢复.