likes
comments
collection
share

「 iOS 知识小集 」2018 · 第 23 期

作者站长头像
站长
· 阅读数 6

上周公众号发布的以下文章:

本期知识小集的主要内容包括:

  • 用 NSDecimalNumber 处理 iOS 中的货币金额
  • 如何更容易看懂宏
  • Storyboard/Xib 颜色空间的坑
  • VC 多层 push 后回到指定页面的几种方法
  • 添加沙箱技术测试员报错的问题
  • UIViewController 设置导航栏和标签栏不同 title 的问题
  • 苹果官方类库中 NSDateFormatter 的小 bug

用 NSDecimalNumber 处理 iOS 中的货币金额

作者: 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

相信你和我一样,也遇到过特别难理解的宏定义,比如宏与宏之间嵌套、带参数的宏。我们看个例子(这个宏并不是特别难,但也很绕):

#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

今天分享一下 Xcode Interface Builder 设置背景色的一个坑。从 Xcode8 起,Xib/Storyboard 里的颜色空间默认从 Generic RGB 换成了 sRGB,但又不是所有的都会转换,很奇怪。所以当时在适配 Xcode8 的时候,颜色空间都统一全局替换了一遍。最近又遇到一次这个坑,在 Storyboard 把某个视图背景色从白色更改为 0xf0f1f2,然后 run 起来,和其 superview (superview 的背景色是用代码设置的 0xf0f1f2)竟然有一个明显的分割线,所以回想起当时适配时的这个问题,然后取到 Storyboard 里面一看,果然是颜色空间被莫名改为了 Generic RGB,如图所示。所以手动改变其颜色空间为 sRGB 即可。

「 iOS 知识小集 」2018 · 第 23 期

参考链接

  • Xcode Interface Builder 指定顏色與實際有出入 http://t.cn/RgpFOPg
  • WWDC Working with Wide Color http://t.cn/ReADdu7

VC 多层 push 后回到指定页面的几种方法

作者: 这个汤圆没有馅

场景如下: RootVC -- > A -- > B -- > C,然后现在要求C直接pop回到A。

方法一:C返回到B的时候写个回调,B接收到回调再自己pop到A,但是这个方法B的页面会闪现一下,用户体验不好,不推荐。

方法二:在B push 到C的时候,直接把B从导航控制器的堆栈中移除,如图一。

「 iOS 知识小集 」2018 · 第 23 期

方法三:写一个UIViewController的catrgory,方法实现如图二。在C的backAct方法中使用,如图三。有的同学可能会怀疑B会不会内存泄露,可以在B中打印dealloc。

「 iOS 知识小集 」2018 · 第 23 期

「 iOS 知识小集 」2018 · 第 23 期

这里比较推荐方法三。不论有多少级的push,只要传入指定页面的类名,都能回到该页面。

添加沙箱技术测试员报错的问题

作者: 高老师很忙

在测试 IAP 的时候需要添加沙箱技术测试员,相当于创建一个虚拟的 AppleID,填写的邮箱是不能写真实 AppleID 邮箱的,要不然就会报错,可以随意写一个不存在的邮箱,为了方便,创建一个简短的密码就会报错误:

「 iOS 知识小集 」2018 · 第 23 期

按照提示改好后,依然报错:

「 iOS 知识小集 」2018 · 第 23 期

并且没有任何的提示,感觉这点好坑,尝试了N遍之后发现是密码的复杂度不够,要包含大小写和数字,虽然是虚拟的 AppleID,但是密码规则要和正式的 AppleID 一样,希望以后苹果把错误提示做的更好吧!😂

UIViewController 设置导航栏和标签栏不同 title 的问题

作者: KANGZUBIN

我们通常会在一个 UIViewControllerviewDidLoad 方法中通过 self.title = xxx 的方式给一个页面设置其导航栏标题,相信大家对这再熟悉不过了。

如果一个 VC 页面中同时具有 NavigationBar(导航栏)和 TabBar(标签栏),而且我们又想让这两个地方的标题显示不一致,如下图所示,在首页顶部导航栏标题中显示“知识小集”,而在底部标签栏标题中显示“首页”:

「 iOS 知识小集 」2018 · 第 23 期

但是,当我们在 UITabBarController 中初始化好上述页面结构后,且设置首页 VC 的 tabBarItem.title 为 “首页”,然后在首页 VC 的 viewDidLoad 方法中设置 self.title 为 “知识小集”,编译运行后我们发现首页底部标签栏的标题也变成“知识小集”了,而不是刚设置的“首页”。

查了苹果文档中关于 UIViewControllertitle 属性的定义,有如下一段描述:

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

作者: 我是乔忘记疯狂

不知道有多少人和我一样是今天才知道这个问题的

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

关注我们

欢迎关注我们的公众号:iOS-Tips,也欢迎加入我们的群组讨论问题。可以公众号留言 iosflutterwebpwa小程序 等关键词获取入群方式。

「 iOS 知识小集 」2018 · 第 23 期