iOS | 伤人于无形:CGFloat

作者: Lol刀妹 | 来源:发表于2018-06-16 11:13 被阅读432次
    村姑三连

    背景

    公司群有同事反映APP在plus机型上存在展示错误,如下:

    实际上我们期望的效果是:

    让人不解的是:只有在plus机型上才出现这种展示错误,其余机型都是OK的

    开发阶段我用了各种机型来调试,唯独没有用plus。。。

    代码

    我赶紧查看相关代码:

    - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
    {
        return CGSizeMake((SCREEN_WIDTH-30-22)/3, (SCREEN_WIDTH-30-22)/3+61);
    }
    
    - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
    {
        return UIEdgeInsetsMake(0, 15, 10, 15);
    }
    
    - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
    {
        return 11;
    }
    

    我看了几遍,也没看出任何问题,而且就算是计算错误,也不应该仅仅是体现在plus机型上啊。

    寻找问题的答案

    我把我的疑惑发到了iOS高端妓术裙里,裙里的QQ小冰告诉我:

    “cell的width再减去一像素就可以了。”

    你是不是很疑惑为什么QQ小冰知道这些?我也母鸡啊,只知道某一次QQ更新后,QQ小冰就突然变得异常强大,不仅上知天文下知地理陪大家玩游戏,还精通iOS开发

    就是这么6,不管你信不信

    但是QQ小冰并没有告诉我为什么减去1像素后就OK了,或许这是QQ小冰深度学习的结果吧。

    虽然得到了问题的解决方案但是不知道问题原因所在,还是让人很憋屈啊。

    抱着一丝侥幸,我去stack overflow问了一下,S.O.从未让我失望,很快得到了答案:

    because screenWidth-30-22-0.001)/3 = 120.6666666666.... but it was rounded up -> 120.6666667 So the screen is not enough to display. The same for other iphone but not round up.

    看到小数的我顿悟,去年就因后台返回的价钱是float类型导致前端展示出错,没想到今年因为类似问题再挨一刀。。。

    低头默默流下没技术的眼泪。。。

    问题还原

    计算cell的width,得到的值是:120.66666666无限循环。

    120.66无限循环乘以3再加上cell的间距和margin正好是屏幕宽度,这个是没有任何问题的。

    问题就在:

    CGSizeMake(<#CGFloat width#>, <#CGFloat height#>)
    

    width的类型是CGFloat,让CGFloat类型来存储一个无限小数肯定是有偏差的,毕竟不管是float还是double,它们都只能精确到多少多少位。

    所以,用CGFloat来存储超过它精度的值的时候,存储的值要么变大了,要么变小了。

    在plus机型上,存储的这个值比期望的值更大,这就导致存储值*3+cell间距+margin超过了屏幕宽度,虽然仅仅是超过了一点点,但是collectionView一排放不下3个cell,只能换行。

    而在其它机型上,要么cell的宽度在CGFloat的精度之内,要么存储的值比期望的值小一点点,这都不会导致collectionView的展示错乱。

    而我的悲剧就在于调试所用机型全TM都是正确展示的。

    总结

    要避免被float坑,除了经验还要有意识。

    保持警惕心才能躲过各种变换的坑。

    还有代码的严谨性也很重要,这一句代码:

    - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
    {
        return 11;
    }
    

    这句代码完全就是多余的,不写这句多余代码就不会出现上述展示问题。cell的size以及section的margin确定的时候,cell的间距自然也就确定了,除非你本来就想让cell的间距超过某一指定值。

    后记

    我不会告诉你我一周之内发布了三个版本

    测试妹纸跟着我一起露出了尴尬而不失优雅的笑容。

    我知道听到这个你们就高兴了,祝大家端午节快乐。

    相关文章

      网友评论

      • b2f70a645712:qq小冰这么优秀 我怎么不知道
      • 64e8abf92758:同样被坑过,哈哈哈
      • xiAo__Ju:减一减多了,你这是像素分割的问题。plus 1pt = 3px。 Retina 1pt = 2px
      • 可惜你不是我的双子座:目测 (SCREEN_WIDTH-30-22)/3 取整数了,到时collectionview 计算出两个 这样试试(SCREEN_WIDTH-30-22)/3.0f
      • PGOne爱吃饺子:在plus机型上,存储的这个值比期望的值更大,这就导致存储值*3+cell间距+margin超过了屏幕宽度 快老大,你说的这句存储的值比期望的值更大是什么意思
        Lol刀妹:@PGOne爱吃饺子 就是这个意思。
        PGOne爱吃饺子:@无夜之星辰 是不是这个意思,在苹果5 6 上面CGfloat存储的是0.33333333,在6p上面存储的是0.3333334,这样就超出了屏幕的宽度了。
        Lol刀妹:1.0/3,期望的值是0.3333无限循环,但是实际只能存储有限位,所以存储的值要么比期望值大要么比期望值小。
      • 施主小欣:快师傅 如果去两位小数会不会出现问题
        施主小欣:@无夜之星辰 好的! 谢谢快师傅
        Lol刀妹:@施主小欣 会,比如在你去两位小数之前它已经从1.9999......变成2了,你可以写个demo研究一下。
      • 黄河石:size强行 整数类型应该可以吧
        Lol刀妹:@黄河石 floor也不行啊,在你floor之前,它已经从1.999....变成2了
        黄河石:@无夜之星辰 额 这么暴力,就不能floor一下:joy:
        Lol刀妹:强行整数类型也会有问题,你可以试试:NSInteger a = (NSInteger)1.9999999999999999999999999;
      • 老刘了:快师傅niubility
        Lol刀妹:Yes,it is.:sunglasses:

      本文标题:iOS | 伤人于无形:CGFloat

      本文链接:https://www.haomeiwen.com/subject/cqkmeftx.html