【iOS】基于Realm数据库的记账软件--时间线模块(三)

1、前言

接下来,我们将开始搭建时间线界面。该模块是界面展示中最大的难点--时间线布局。那么,我们先来看看效果图,因为gif上传后,动不了。所以在这里用几张截图和文字简单的描述一下,具体效果大家可下载项目自行查看~

这里写图片描述

这里写图片描述

从图一滚动到图二时,头部从7月的数据变成6月的账单数据。

2、时间线搭建

2.1、前言

这个时间线界面是仿照口袋记账的,一开始没有头绪的时候,就把自己的手机越狱了,然后通过Reveal进行查看其布局,具体Reveal的用法,可以看我之前的博客。传送门

2.2、Cell的设计

先看一下,这个界面的结构,注意View标注的文字,下文说明会用到的。
这里写图片描述

从上图不难看出,一共分为两种Cell:

  • 显示账单信息的Cell,如红框所示,显示账单类型,金额
  • 显示当天日期的一个汇总,如蓝框所示,显示该日的一个总收入和总支出

3、数据准备

3.1、模型设计

由于我们的Cell是有两种类型的,那么我们需要通过模型去控制Cell的产生以及赋值。因此,我们需要一个type来区分是什么类型的Cell,通过Type去加载对应的Cell。模型设计如下:

红框Cell模型
/// 时间线模型
@interface MPTimeLineModel : NSObject

/// 模型的类型
@property (nonatomic, assign) TimeLineType type;
/// 账单模型
@property (nonatomic, strong) MPBillModel *bill;
/// 时间字符串
@property (nonatomic, copy) NSString *dateStr;
/// 一天内的账单模型
@property (nonatomic, strong) MPDayBillModel *dayBill;
@end

蓝框Cell模型
/// 一天的消费模型
@interface MPDayBillModel : NSObject
/// 日期字符串
@property (nonatomic, copy) NSString *dateStr;
/// 收入
@property (nonatomic, assign) double income;
///支出
@property (nonatomic, assign) double outcome;
@end

3.2、数据查询

由时间线的布局特性,要求我们要以“dateStr”字段进行进行降序排序(即最新的日期放在最前面)。然后再以“日”为单位,将同一日的账单的归类在一起,整合成MPDayBillModel模型。

一、查询当前账本的所有账单并排序

MPBillManager下的方法
- (RLMResults *)getBillsInCurrentBook
{
  MPBookModel *book = [[MPBookManager shareManager] getCurrentBook];
  RLMResults *results = [MPBillModel objectsWhere:@"book=%@", book];
  // 返回排序后的结果集
  return [self sortTheResultsByDate:results];
}

- (RLMResults *)sortTheResultsByDate:(RLMResults *)results
{
  // 首先根据dateStr(账单时间)进行排序
  RLMSortDescriptor *desc1 = [RLMSortDescriptor sortDescriptorWithKeyPath:@"dateStr" ascending:NO];
  // 再根据recordDate(记录时间)进行排序
  RLMSortDescriptor *desc2 = [RLMSortDescriptor sortDescriptorWithKeyPath:@"recordDate" ascending:NO];
  return [results sortedResultsUsingDescriptors:@[desc1, desc2]];
}

二、以“日”为单位,开始整合数据模型,这里的实现有点繁琐,但是逻辑是不难的。核心思路是找到同一天的所有账单后,创建一个MPDayBillModel插入。以此类推,那么实现代码如下:

MPTimeLineModel的类方法
+ (NSMutableArray *)timeLineArrayWithResults:(RLMResults *)results
{
  NSMutableArray *modelArray = [NSMutableArray array];
  NSMutableArray *billInSameDay = [NSMutableArray array];
  for(int i = 0; i < results.count; i++)
  {
    MPBillModel *bill = results[i];
    // 当数组为空时,直接添加元素
    if(billInSameDay.count == 0)
    {
      [billInSameDay addObject:bill];
    }
    else
    {
      // 将日期相同的账单,放在同一个数组中
      MPBillModel *lastOj = billInSameDay.lastObject;
      if([bill.dateStr isEqualToString:lastOj.dateStr])
      {
        [billInSameDay addObject:bill];
      }
      else
      {
        // 创建Day类型的模型
        [modelArray addObject:[self getDayItemWithBillArray:billInSameDay dateStr:lastOj.dateStr]];
        // 生成Normal类型的模型
        for (MPBillModel *bill in billInSameDay)
        {
          MPTimeLineModel *model = [[MPTimeLineModel alloc] init];
          model.bill = bill;
          model.type = TimeLineNormalItem;
          [modelArray addObject:model];
        }
        // 重新开始分类
        [billInSameDay removeAllObjects];
        [billInSameDay addObject:bill];
      }
    }
  }
  if(billInSameDay.count != 0)
  {
    MPBillModel *bill = billInSameDay.firstObject;
    [modelArray addObject:[self getDayItemWithBillArray:billInSameDay dateStr:bill.dateStr]];
    // 生成Normal类型的模型
    for (MPBillModel *bill in billInSameDay)
    {
      MPTimeLineModel *model = [[MPTimeLineModel alloc] init];
      model.bill = bill;
      model.type = TimeLineNormalItem;
      [modelArray addObject:model];
    }
  }
  return modelArray;
}

4、头部数据的切换

4.1、核心思路

这里有一个很重要的效果,就是当6月的节点滑动到头部时,头部的header将显示6月的总收入以及总支出数据。那么,我们就需要在月份节点与头部View相交的时候,做数据复制。那么,沿着这个思路,我的解决方案就是,首先将月份节点,头部View转为同一坐标系,然后通过判断是否相进行处理。

4.2、添加监听的位置

自然地,我们需要在scrollView滚动的时候进行实时监听。此外,当scrollView快速滚动时,scrollViewDidScroll调用的不够频繁,因此里面计算header数据需要在滚动结束时,需要判断是否切换了月份。

所以我们需要在以下两个方法进行判断:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

判断的方式稍有不同,具体的实现方式请看MPBillTableViewController的上述的两个scrollView代理方法

5、总结

该时间线的实现共有两个难点:

  • 难点一,数据查询后,生成业务需求的模型
  • 难点二,头部View的数据监听

但只要掌握其核心思路,再去阅读代码,我相信大家都能看的懂的~

github地址

https://github.com/maple1994/MPTally
请顺手给一个start哦,哈哈

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355

推荐阅读更多精彩内容