本期知识小集的主要内容包括:
用 NSDecimalNumber 处理 iOS 中的货币金额
如何更容易看懂宏
Storyboard/Xib 颜色空间的坑
VC 多层 push 后回到指定页面的几种方法
添加沙箱技术测试员报错的问题
UIViewController 设置导航栏和标签栏不同 title 的问题
苹果官方类库中 NSDateFormatter 的小 bug
用 NSDecimalNumber 处理 iOS 中的货币金额
作者: https://weibo.com/halohily
在 iOS 开发中, 经常遇到货币金额的表示与计算, 你可能会使用 double 或 float 这样的浮点数, 也可能使用 NSString . 无论用哪个, 都需要再编写繁琐的精度控制, 小数位数控制等代码. 其实, 苹果为我们提供了一个标准类 NSDecimalNumber 来处理这样的需求.
NSDecimalNumber 是 NSNumber 的子类, 它提供了完善的初始化方法. 对于令人头疼的金额计算, 它还提供了贴心的加, 减, 乘, 除运算方法. 在进行这些运算的时候, 你还可以通过 NSDecimalNumberHandler 对象来对运算的处理策略进行设置, 比如舍入模式的选择, 数据溢出, 除零等异常情况的处理等.
下次遇到货币金额的需求, 不妨了解一下 NSDecimalNumber.
参考资料:
iOS 数字精准计算之 NSDecimalNumber
https://www.jianshu.com/p/ea4da259a062
NSDecimalNumber 数字精确
https://www.jianshu.com/p/25d24a184016
如何更容易看懂宏
作者: Lefe_x https://weibo.com/u/5953150140
相信你和我一样, 也遇到过特别难理解的宏定义, 比如宏与宏之间嵌套, 带参数的宏. 我们看个例子 (这个宏并不是特别难, 但也很绕):
- #define JPBOXING_GEN(_name, _prop, _type) \
- + (instancetype)_name:(_type)obj \
- { \
- JPBoxing *boxing = [[JPBoxing alloc] init]; \
- boxing._prop = obj; \
- return boxing; \
- }
- JPBOXING_GEN(boxObj, obj, id)
复制代码
这个例子看着总是怪怪的, 如果把上面的宏转换成实际代码, 相信你会很容易看懂.
- + (instancetype)boxObj:(id)obj
- {
- JPBoxing *boxing = [[JPBoxing alloc] init];
- boxing.obj = obj;
- return boxing;
- }
复制代码
其实就是各种参数的替换导致阅读起来比较困难. 我们都知道程序经过预处理后就会把宏转换为实际的代码, 而 Xcode 为我们提供了对单个文件进行预处理 (Produce -> Perform Action -> Preprocess 'xxxx.m'), 这样处理后, 上面的宏就变成了:
- + (instancetype)boxObj:(id)obj {
- JPBoxing *boxing = [[JPBoxing alloc] init];
- boxing.obj = obj;
- return boxing;
- }
复制代码
经过预处理后和我们手动翻译的结果一样.
Storyboard/Xib 颜色空间的坑
作者: Vong_HUST https://weibo.com/VongLo
今天分享一下
Xcode Interface Builder
设置背景色的一个坑. 从 Xcode8 起, Xib/Storyboard 里的颜色空间默认从 Generic RGB 换成了 sRGB, 但又不是所有的都会转换, 很奇怪. 所以当时在适配 Xcode8 的时候, 颜色空间都统一全局替换了一遍. 最近又遇到一次这个坑, 在 Storyboard 把某个视图背景色从白色更改为
0xf0f1f2
, 然后 run 起来, 和其 superview (superview 的背景色是用代码设置的
0xf0f1f2
) 竟然有一个明显的分割线, 所以回想起当时适配时的这个问题, 然后取到 Storyboard 里面一看, 果然是颜色空间被莫名改为了 Generic RGB, 如图所示. 所以手动改变其颜色空间为 sRGB 即可.
参考链接
Xcode Interface Builder 指定顏色與實際有出入
http://t.cn/RgpFOPg
WWDC Working with Wide Color
http://t.cn/ReADdu7
VC 多层 push 后回到指定页面的几种方法
作者: 这个汤圆没有馅 https://weibo.com/u/6603469503
场景如下: RootVC --> A --> B --> C, 然后现在要求 C 直接 pop 回到 A.
方法一: C 返回到 B 的时候写个回调, B 接收到回调再自己 pop 到 A, 但是这个方法 B 的页面会闪现一下, 用户体验不好, 不推荐.
方法二: 在 B push 到 C 的时候, 直接把 B 从导航控制器的堆栈中移除, 如图一.
方法三: 写一个 UIViewController 的 catrgory, 方法实现如图二. 在 C 的 backAct 方法中使用, 如图三. 有的同学可能会怀疑 B 会不会内存泄露, 可以在 B 中打印 dealloc.
这里比较推荐方法三. 不论有多少级的 push, 只要传入指定页面的类名, 都能回到该页面.
添加沙箱技术测试员报错的问题
作者: 高老师很忙 https://weibo.com/517082456
在测试 IAP 的时候需要添加沙箱技术测试员, 相当于创建一个虚拟的 AppleID, 填写的邮箱是不能写真实 AppleID 邮箱的, 要不然就会报错, 可以随意写一个不存在的邮箱, 为了方便, 创建一个简短的密码就会报错误:
按照提示改好后, 依然报错:
并且没有任何的提示, 感觉这点好坑, 尝试了 N 遍之后发现是密码的复杂度不够, 要包含大小写和数字, 虽然是虚拟的 AppleID, 但是密码规则要和正式的 AppleID 一样, 希望以后苹果把错误提示做的更好吧!
UIViewController 设置导航栏和标签栏不同 title 的问题
作者: KANGZUBIN https://weibo.com/kangzubin
我们通常会在一个 UIViewController 的 viewDidLoad 方法中通过 self.title = xxx 的方式给一个页面设置其导航栏标题, 相信大家对这再熟悉不过了.
如果一个 VC 页面中同时具有 NavigationBar(导航栏) 和 TabBar(标签栏), 而且我们又想让这两个地方的标题显示不一致, 如下图所示, 在首页顶部导航栏标题中显示 "知识小集", 而在底部标签栏标题中显示 "首页":
但是, 当我们在 UITabBarController 中初始化好上述页面结构后, 且设置首页 VC 的 tabBarItem.title 为 "首页", 然后在首页 VC 的 viewDidLoad 方法中设置 self.title 为 "知识小集", 编译运行后我们发现首页底部标签栏的标题也变成 "知识小集" 了, 而不是刚设置的 "首页".
查了苹果文档中关于 UIViewController 中 title 属性的定义, 有如下一段描述:
If the view controller has a valid navigation item or tab-bar item, assigning a value to this property updates the title text of those objects.
也就是说, 如果一个 VC 同时有导航栏和标签栏, 那么当给 title 赋值时, 会同时修改这两个地方的标题. 所以如果我们只想设置导航栏的标题, 可以通过
self.navigationItem.title = xxx
的方式来实现.
因此, 在一个 VC 中设置相关标题简单总结如下:
self.navigationItem.title: 设置 VC 顶部导航栏的标题
self.tabBarItem.title: 设置 VC 底部标签栏的标题
self.title: 同时修改上述两处的标题
参考文档
- UIViewController.title
- https://developer.apple.com/documentation/uikit/uiviewcontroller/1621364-title?language=objc
苹果官方类库中 NSDateFormatter 的小 bug
作者: 我是乔忘记疯狂 https://www.weibo.com/coderchow?refer_flag=1005055010_
不知道有多少人和我一样是今天才知道这个问题的
- let formatter = NSDateFormatter()
- formatter.dateFormat = "yyyy-MM-dd"
- let date = formatter.dateFromString("1987-04-12") // nil
复制代码
这个问题在 StackOverflow 上是有人提过的. 其实主要跟一些国家实行
夏时制 (Daylight saving time)
有关系, 尤其是美国
解决办法我是尝试这样增加一句代码
- let formatter = NSDateFormatter()
- formatter.dateFormat = "yyyy-MM-dd"
- formatter.calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierISO8601)
- let date = formatter.dateFromString("1987-04-12") // Apr 12, 1987, 1:00 AM
复制代码
最后推荐一个夏时制的相关阅读, 高晓松的 鱼羊野史 第 1 卷 中 1 月 6 日那一章, 有讲到美国于 1974 年 1 月 6 日开始实行夏时制并一直到今天, 还讲到其他一些相关的有意思的事情.
参考文档
NSDateFormatter.dateFromString returns nil for specific dates in specific languages
https://stackoverflow.com/questions/32408898/nsdateformatter-datefromstring-returns-nil-for-specific-dates-in-specific-langua
关注我们
来源: https://juejin.im/post/5b5ef214518825018c4cf85a