bookmark_borderSwift-聊天记录平滑下拉加载(多种类 cell)

当我们在设计聊天界面的时候,由于聊天记录可能存放在服务端,又或者为了考虑内存的因素,我们偏向于在一开始仅仅展示一部分聊天记录,这时候我们就需要有一个方法来实现加载之前的聊天记录。通常是通过下拉来实现的(例如微信)

和下拉刷新的思想很接近,我们可以选择在tableview上加一个refresh control来实现下拉加载,但这里并不推荐使用这个方法(原因后面会提到)但是无论我们使用哪个方法,当我们获取到新的data的时候,需要把他添加到tableView的最上面,这样才符合逻辑。

Screen Shot 2018 12 30 at 6 17 23 PM
如图所示 来源:https://bluelemonbits.com/2018/12/30/reloading-inserting-dynamic-height-cells-and-keeping-scroll-position/

由于TableView的特性,在reload的data的时候,会自动跳转到新加入的cell的顶端,也就是上图所示绿色的最顶端,这样就会导致每次加载之后整个tableView都会产生突兀的跳转。

这时候我们需要的解决方法是像参考微信一样,在加入新的Cell的同时,保持我们当时的contentOffset。具体实现这个的代码如下:

let oldContentSizeHeight = ListView.contentSize.height
ListView.reloadData() 
ListView.layoutIfNeeded()
let newContentSizeHeight = ListView.contentSize.height
                ListView.setContentOffset(CGPoint(x:ListView.contentOffset.x, y:newContentSizeHeight - oldContentSizeHeight), animated: false)

简而言之,就是在reload data之前记录此时的content height,待reload结束之后再记录之后的height,通过相减来设置contentOffset

这时候我们需要了解tableView在计算content height的原理,如果只是简单的单一的cell,那么cell的height会是相同的,并且是在

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { }

这个delegate方法里设定的,但是如果我们的cell是多种类的呢,这时候iOS提供了一个estimate height 对于cell

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}

通过同时实现这个delegate和上一个delegate的方法,iOS会自动帮我们计算出合适的content height,这样就不会产生有问题height,从而导致跳转出现问题(换句话说,如果你不实现这个方法,那么计算你content height的方法,就是简单的cell number * [cell height])

通过以上方式,我们可以实现固定位置的上拉刷新,回到之前提到的问题,为什么不推荐使用refresh control,原因其实很简答,因为refresh control是通过加载一个新的uiView在tableView上面,然后设定一个阈值,当我们下拉到一定程度会调用。那对于用户的体验来讲,需要一直下拉刷新的话,并不让人愉悦。通常快速向上滑动的时候并不会触发这个调用,也就会导致我们需要很多次“两次操作”才能到达我们想要的聊天记录的位置。那么我这里用到的替代方法是:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if scrollView == ListView {
            if ListView.contentOffset.y < 0 {
                if !isLoading {
                    loadPreviousMessage()
                }
            }
        }
    }

因为TableView的实现是在scrollView上的,所以我们可以检测是否scrollView的contentOffset < 0 来检测用户是否想要加载之前的聊天记录,同时因为scrollView的特性是支持bounce的,所以向上快速滑动的同时,contentOffset也会出现 < 0的情况,这里为了避免多次调用,使用了一个变量 “isLoading”来控制。

这样设定之后,我们就可以简单的实现一个向上滑动来加载之前的聊天记录的功能

bookmark_borderFastlane 踩坑

  1. gem 版本过低不能安装fastlane,需要升级gem
  2. xcode-select 安装位置变化会导致
    ​sh: agvtool: command not found

    解决办法:

    sudo xcode-select -s /Applications/Xcode.app/Contents/Developer

     

  3. brew 安装的fastlane 的 action数量远小于 gem直接安装,需要reinstall fastlane 在用gem来安装 其中包括 build_app ~= gym   upload_to_testflight ~= pilot
  4. gem 安装之后的 $GEM_HOME 问题,如果不改$GEM_HOME的话,就需要自行把$PATH里加上在gem[version]/bin 这样
  5. 未完待续

bookmark_borderC++ String 详解

对于string来讲,如果是sizeof(std::string) 只有一个成员变量即指向字符串内容的指针,而并没有别的成员变量来记录实际字符串长度了。这个指针是指向内容的地址的

size(string)会返回string里的值,string =[1234567890] string.size() = 10, string.data() = [1234567890]. 指针指向的内存地址上的值是0x1ba8028。也就是说要想知道实际string在内存上占用的大小,需要去看string存的指针指向的内存的大小。

bookmark_border入秋

今天纽约的天气像极了太原,你知道吗,每当季节更替的时候,在你第一天感觉到他到来的时候,你会觉得很熟悉,很想念,每一个季节都被等待了9个月,是新鲜的,是新奇的。
每个季节的空气是不同的,秋天的空气是冷冽中带着甜味的,如果再配上下午三点的阳光那再好不过了,我们缩在衣服里,却仍能侥幸的被太阳烘烤着。冷风会穿过你的脖子,袖子,脚腕,让你想起上一次西北风刮过时的感觉,如果纽约刮的也是西北风的话。
人们开始穿上套头衫,地铁不再是廉价的冷风,从哄热的地铁站中窜出来,又闻到了秋天的香味。
我知道再过几天我会开始咒骂着鬼天气,会发愁没有过冬的衣服,会回忆从烈日下冲进空调房的畅快,我会厌倦秋天的破败和萧条。所以我记下来今天的秋天,像极了太原的秋天,每一天都不是太原的纽约。

bookmark_border

我从椅子上摔了下来,就哭了。
我以为我不会哭的,
这本无关于椅子
无关于窗外的雨
无关于中午做咸了的饭。

不该哭的,
哭的时候是看不清前方的。

除了从地上爬起来
还有选择吗?

bookmark_border故事三则

(一)

阿花是一条身上有4种颜色的狗,按理说这种多颜色的狗并不多见,但是奈何他出生在穷乡僻壤里,他身上的花纹也就只有乡里乡亲见了会夸赞两句,说:唉呀妈呀,这狗娃子真俊啊。阿花是孤独的,他的父母都是长鼻子小眼睛的中华田园犬,他也无可奈何的继承了他们的特点。话说回来,即便是自己有4种颜色,在千万狗同胞里肯定也不会单有自己一个,更何况自己是条血统廉价的狗呢,所以当阿花和同村其他狗一起从泥潭里打滚之后,就不再想起自己有着和别人一样的四色毛色了。

日子一天天过,村子里的狗和人一样,过着小富即安的日子,既然不是牧羊犬,也不用难过主人家里穷到没有一只羊,既然不是雪橇犬,也不用抱怨这黄土高原上很少有能积住的雪,我只是一条土狗,我生下来就是和这片黄土为伴的。阿花常这么想,但阿花怎么也想不到自己会爱上一只贵宾犬。

农村里是不会有人养贵宾的,那么阿花爱上的这只贵宾是哪里的呢?这要感谢国家的电器下乡工程了,一台电视机摆在了主人家原来放土地爷的供台上,阿花也凑热闹的跟着主人看起了电视,碰巧电视上正在演狗狗知识普及节目,一只贵宾就站在那个黑色的柜子里,阿花呆呆的看着贵宾,鼻子呼哧呼哧的响着。但当他缓过神想去找贵宾在哪时,主人已经把台调到了最爱的乡村爱情,随之而来的是刘能和赵四斗嘴的声音。

阿花恋爱了,他爱上了一台电视机,至少主人是这样认为的,因为他每天对着电视机周围转悠,主人只要稍一不留神,动物原始的冲动就会促使阿花做出羞羞的事情。但再怎么心急,那也只是台电视机啊,几天下来,阿花似乎明白了电视机的构造和同类的构造不太一样了。在明白之后,阿花就不再出去和外面的土狗厮混了,原因无他,没有一只狗会喜欢整天灰头土脸的狗。阿花开始在镜子里靠自己的样貌了,和小时候一样,除了背上四色的毛发,他和其他狗没有什么区别,相反,偷狗贼反而会因为他的毛发更容易套住他,对,更容易套住这只不想留在这里的狗。

阿花被套去了牲畜集散市场,他在一家狗肉店的笼子里待了三天。直到第三天凌晨,从居民楼上掉下来的花盆把笼子砸出一个足以供他钻出来的洞时,他才得以好好看看这不同于之前村子的地方,一只狗不会觉得霓虹璀璨是种奢糜,他们的世界大多也只是黑白的。阿花一个人走在冷风中,心里想着的是那只贵宾,但又有一些时候想着的是村子里那户人家,偶尔脑子里还蹦出那条和他在泥潭里打滚的狗的影子。

走着走着,脑子里的狗的样子都重合在了一起,他似乎突然明白了什么,他临死前才意识到。自己从生下来就只有一种颜色,只是他有了精神分裂,和他滚泥潭的是自己,电视上的贵宾其实也是自己,那四种颜色只是四个不同精神状态下自己的颜色,他看着霓虹下的城市,狗肉店本想明天做掉他而给他打的毒针起作用了,他在生命的最后知道了自己探索的都是空的,却又在生命的最后才认识了自己。目光越来越黯淡,突然耳边传来一句话:阿美,快回来,那是条死狗,小心他咬你,他顺着声音看去,一个女人拽着她的狗,不让她的狗跑向阿花,而阿花,最终也没有看清向自己跑来的那只贵宾。也许,不该让它来打搅阿花本来就已经破碎不堪的梦了。

(二)

在美国西部的一个农场,农场主是一个胖胖的老头,从他生下来以后的大部分人生都是在农场里度过的。他18岁那年双亲离世,留下苦心经营的农场给他。他起初是想卖了农场去城里好好打拼一番,但是等他找到农场的地契时,发现放地契的那个箱子的箱底被老鼠啃了个洞,地契也难幸免于难。他惶恐地过了好几天,生怕别人发现自己已经没了这个农场的地契了,可是过了几天,只到地里的庄稼都快黄了,他才发现根本没有人在意他的地契是否被啃了,内心庆幸的同时,对于农场里的老鼠毁掉自己计划好的未来这事耿耿于怀,连着三天,他拿着手电躲在仓库的角落里想制裁那只啃掉他未来的老鼠,可是愤怒他忘了老鼠不喜欢有光的地方,最后只好无功而返。躺在床上,半睡半醒的状况下仿佛还能听见老鼠啃东西的声音,内心难以抑制的火苗侵蚀着他的内心。第二天一大早,他就去宠物店买了只猫,并嘱咐店家务必要给他挑一只捉鼠能手。

带着自己的黑猫警长,他回到了农场。然而黑猫警长的死敌一只耳却没有应邀出席对警长的欢迎会,他把警长放在有地契箱子的那间屋子里,幻想着明天能在警长的嘴边找到半截没有吞下去的老鼠尾巴,然而一切和他想象的并不太一样。

当天晚上警长工作得并不顺利,警长在新的环境里竟然水土不服拉肚子了,然而作为一只高贵的警长猫,他要不能允许自己在猫砂以外的地方进行羞羞的事情。总之在警长万般无奈的情况下,一只耳出现了,在箱子后面的洞里探出一个鼠头,警长的本能和一只耳的本能分别告诉各自,对方不是好货色,但是一只耳看着捂着肚子的警长似乎知道他遇到了什么困难,无奈一只耳心善,从箱子里叼出来半拉地契,终于解决了警长的困难。等警长解决完之后舒服的趴在地上,一只耳又出现了,碍于刚刚帮助了自己,警长并没有对一只耳做出什么,一只耳在警长旁边也躺下了。月光从破旧的仓库墙缝隙中打下来,警长从怀里拿出一包大前门,递给一只耳一只,一只耳拿过来别在了耳朵上,看着警长,警长的背影洒在自己的面前,一只耳永远不会想到自己和一只猫会相距这么近,警长把头扭向一只耳,又摇了摇头看向了远方,幽幽的说了句:我说今晚月色那么美。 一只耳说:是的。

(三)

我是一件快递,当我被揽收在快递小哥的自行车后座上时,我的生命才刚刚开始。其实这可能不是我第一次当快递,但是我却记不得上次我是快递的时候我在哪里。快递小哥从一个胖男人手中将我拿走,再三确认后在我的脸上贴上了属于我的标签:寄件人 张某某 收件人 废坑。对,我的名字无外乎在这两个名字之间罢了,哦对了,或许还有一串数字能证明我的存在,但我觉得那些单单只是排列着的数字远没有这些方块字讨人喜欢。

我在凌晨时分上了一辆开往城外的货车,同行的还有很多和我一样的快递,我和他们一样,都保持着沉默。并不是我生来就不喜欢说话,只是我们都知道,舱门一开,就会有一批同伴要与我们分离,我们每个快递都不愿意见到朋友的分离,哪怕只是三言两语的朋友也会让我们感到痛苦,既然如此,墨守成规的便是沉默,命运安排我们的也是沉默。黑暗中的沉默让我有时间重新认识自己,我把我身上的标签一遍又一遍的读着,这于我来说像人类身体里的染色体一样,从一开始就不由自己决定,却要由它来决定之后的我。

车还在不停的前进着,我身旁的快递大多数都已经不见了。只留下我和剩下两个,我凑近了看,才发现我们最终的目的地是一样的,都是废坑。即便如此,我还是没有拿出和他们交谈的勇气,我不知道从车厢出去以后的世界,却也拒绝了解车厢之内的世界。车停了下来,不出意外,司机打开车厢把我们三个快递一同取了下来。时隔多日,我又见到了阳光,虽然我并不依赖着阳光而活,但是死板黑暗总归让人厌恶。就在司机转身去关上车门的空挡,我有机会看到了我要去的地方——废坑。

我确乎是看到了一个坑,一个深不见底的坑。坑周围停满了从四面八方来的快递车,时不时有几件快递从坑边落下去。到了现在,我明白了坑前面的废字了。开了几天的车,司机似乎也颇为疲倦,他无力的抱起我们三个快递,走向坑边,松开手,我们坠落……其实,我并不生气我的结局是这样,不是因为有成百上千的快递和我一样所以我才不生气。也不是因为快递公司提前就在快递单上写了“新服务:隐私物件报废处理” 而提起知道我的命运才不生气。我只是从一开始就知道命运并非由我掌握,而我,走过命运所安排的世界,看过种种看似蹊跷,荒唐,光怪陆离的社会和同我们一样卑微的人类,才无惧于命运最终对我的宣判,但最终我还是违背了命运,对我身旁的那件陪着我在车里度过几天几夜的快递同伴说了句:“嗨!”

bookmark_border

过来人总说:痣是上辈子死的时候的伤口。但是,如果痣长在手上了怎么办?没有人过来人跟我说过啊…

老师让孩子伸出手,检查每双小手有没有认真的剪掉指甲,被检查到的孩子只是沉默没有说出原因,低头看着脚下破旧的鞋子。

“嚯,好大一颗痣!”整个教室都听到老师的尖叫。被托举着手的孩子茫然地接受着全班同学和老师的参观,不知是该自豪还是如何,想收回手却没有力气从老师的手里逃出来。

众人都会有区分左右的方法。“右边有痣,左边没有”第一次发现这颗痣可以这样用的时候,恨不得举着右手在院子里狂奔。

“为什么你现在不低头看手来分别左右了?”

“因为我知道我右手有颗痣,所以它更重…”

“真的吗?”

“或许吧…”

母亲说她的左手也有一颗痣,所以不用担心哪一天在人群中不能够母子相认了。摸着那颗痣,才发现自己与世界不同了。

痣是不会毫无理由地消失的,这点远比身边之人贴心多了,爱美之人花大价钱注销这印记,销掉的只是自己与生俱来的标签罢了。

举起手,找到那个右手带痣的男生,他或许不是独一无二的。抬头,在他眼中,高举的手上的那一颗痣,远比悬在头顶的日月更明亮。

一颗痣,就是一个人吧?