iOS开发实战-时光记账Demo 本地数据库版
来源:互联网 发布:淘宝成都服装出租 编辑:程序博客网 时间:2024/05/23 19:39
现在记账APP也是用途比较广泛 自己写了个简单的demo 欢迎指正
效果
分析
1.思维推导
首先简单的做了下思维推导
2.文件结构
大致框架想好后就可以着手开始准备了
- 数据库管理:coreData
- 视图管理:navigationcontroller 暂时没有使用cocoapods导入第三方的数据库管理框架 简单的coreData完全可以胜任 说白了就两个页面 主界面 和 记账界面
这是完成时的文件结构
3.数据库设计
- Tally 账单表
- identity :String 唯一标识
- expenses :double 支出
- income :double 收入
- timestamp :date 时间戳
- 关系
- 与TallyDate 日期表:1V1
- 与TallyType 类型表:1V1
- TallyDate 日期表
- date :string 日期
- 关系 -与Tally 账单表:1VN
- TallyType 类型表
- typename :string 类型名
- typeicon :string 类型图片标
- 关系 -与Tally 账单表:1VN
4.页面编写
增加账单页面
由于主页只是一个展示的时光轴界面,UIScrollView加几个按钮就能完成,需要读取数据库内容,所以我们先把内页-增加账单 完成。
- view
- UICollectionView展示账单类型
- 自定义View计算器界面计算存储结果
- model
- UICollectionViewCell模型 使用了plist和KVC转字典
- controller
- 负责添加 两个view 及处理两个view的代理
增加账单部分代码
- model
#import <Foundation>@interface TallyListCellModel : NSObject@property (nonatomic,copy)NSString *tallyCellImage;@property (nonatomic,copy)NSString *tallyCellName;- (instancetype)initWithDict:(NSDictionary*)dict;+ (instancetype)tallyListCellModelWithDict:(NSDictionary*)dict;@end
#import "TallyListCellModel.h"@implementation TallyListCellModel- (instancetype)initWithDict:(NSDictionary*)dict { self = [super init]; if (self) { [self setValuesForKeysWithDictionary:dict]; } return self;}+ (instancetype)tallyListCellModelWithDict:(NSDictionary*)dict { return [[TallyListCellModel alloc] initWithDict:dict];}@end
- 计算界面
#import <UIKit>#import "AppDelegate.h"#import <CoreData>#import "TimeTallyDemo+CoreDataModel.h"typedef void(^PositionInViewBlock)(CGPoint point);@protocol CalculatorViewDelegate <NSObject>//保存成功- (void)tallySaveCompleted;//保存失败- (void)tallySaveFailed;//回到原来的位置- (void)backPositionWithAnimation;@end@interface CalculatorView : UIView//类型图片的size@property(nonatomic,assign)CGSize imageViewSize;//类型图@property(nonatomic,strong)UIImage *image;//类型名@property(nonatomic,strong)NSString *typeName;//账单是否存在 判断 是修改还是新增@property(nonatomic,assign)BOOL isTallyExist;@property(nonatomic,strong)id<CalculatorViewDelegate> delegate;//回调image在整个view中的位置@property(nonatomic,copy)PositionInViewBlock positionInViewBlock;//修改账单界面进入时传入参数- (void)modifyTallyWithIdentity:(NSString *)identity;@end
#import "CalculatorView.h"@interface CalculatorView()@property (nonatomic,assign)CGFloat btnWidth; //btn宽@property (nonatomic,assign)CGFloat btnHeight; //btn高@property (nonatomic,copy)NSString *nValue; //当前输入值@property (nonatomic,copy)NSString *resutlStr; //结果值@property (nonatomic,strong)UILabel *resultLab; //结果栏@property (nonatomic,strong)UIButton *addBtn; //+@property (nonatomic,strong)UIColor *btnColor; //按钮颜色@property (nonatomic,assign)CGColorRef boardLineColor; //边框线条颜色@property (nonatomic,strong)UIImageView *imageView; //tally类型图@property(nonatomic,strong)UILabel *typeLab;@property(nonatomic,copy)NSString *tallyIdentity;@endstatic CGFloat const kBoardWidth = 1;static CGFloat const kMaxCalCount = 9;@implementation CalculatorView- (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.backgroundColor = [UIColor whiteColor]; self.btnWidth = (self.frame.size.width-kBoardWidth/2)/4; self.btnHeight = self.frame.size.height/5; self.nValue = @""; self.resutlStr = @""; self.typeLab.text = @""; self.btnColor = [UIColor grayColor]; self.boardLineColor = [UIColor lightGrayColor].CGColor; self.layer.borderColor = self.boardLineColor; self.layer.borderWidth = kBoardWidth; self.imageView = [[UIImageView alloc] init]; [self addSubview:self.imageView]; [self loadBtn]; } return self;}- (void)setImage:(UIImage *)image { _image = image; [NSTimer scheduledTimerWithTimeInterval:1 repeats:NO block:^(NSTimer * _Nonnull timer) { _imageView.image = image; }];}- (void)setTypeName:(NSString *)typeName { _typeName = typeName; self.typeLab.text = typeName;}- (void)setImageViewSize:(CGSize)imageViewSize { _imageViewSize = imageViewSize; _imageView.frame = CGRectMake(10, (self.btnHeight-imageViewSize.height)/2, imageViewSize.width, imageViewSize.height); _imageView.backgroundColor = [UIColor clearColor]; //回调实际位置 if (_positionInViewBlock) { CGPoint point = CGPointMake(10+imageViewSize.width/2, self.frame.origin.y + _imageView.frame.origin.y + imageViewSize.height/2) ; _positionInViewBlock(point); } self.typeLab.frame = CGRectMake(self.imageView.frame.origin.x + self.imageView.frame.size.width + 10, self.imageView.frame.origin.y, imageViewSize.width * 2, imageViewSize.height);}//类型名称lab- (UILabel *)typeLab { if (!_typeLab) { _typeLab = [[UILabel alloc] init]; _typeLab.font = [UIFont systemFontOfSize:14]; [self addSubview:_typeLab]; } return _typeLab;}//结果lab- (UILabel *)resultLab { if (!_resultLab) { _resultLab = [[UILabel alloc] initWithFrame:CGRectMake(self.frame.size.width * 0.3, 0, self.frame.size.width * 0.7-10, self.btnHeight)]; _resultLab.text = @"¥ 0.00"; _resultLab.textAlignment = NSTextAlignmentRight; _resultLab.adjustsFontSizeToFitWidth = YES; _resultLab.font = [UIFont systemFontOfSize:25]; _resultLab.userInteractionEnabled = YES; UITapGestureRecognizer *tag = [[UITapGestureRecognizer alloc] init]; tag.numberOfTapsRequired = 1; [tag addTarget:self action:@selector(clickResultLab)]; [_resultLab addGestureRecognizer:tag]; } return _resultLab;}- (void)clickResultLab { if ([self.delegate respondsToSelector:@selector(backPositionWithAnimation)]) { [self.delegate backPositionWithAnimation]; }}//加号- (UIButton *)addBtn { if (!_addBtn) { ... [_addBtn addTarget:self action:@selector(clickAdd) forControlEvents:UIControlEventTouchUpInside]; } return _addBtn;}//普通数字btn- (void)loadBtn { // 1 - 9 btn ... [btn addTarget:self action:@selector(clickNumber:) forControlEvents:UIControlEventTouchUpInside]; //0 btn ... [zeroBtn addTarget:self action:@selector(clickNumber:) forControlEvents:UIControlEventTouchUpInside]; //小数点 btn ... pointBtn.tag = 99; [pointBtn addTarget:self action:@selector(clickNumber:) //重置 btn ... [resetBtn addTarget:self action:@selector(resetZero) forControlEvents:UIControlEventTouchUpInside]; //DEL btn ... [delBtn addTarget:self action:@selector(clickDel) forControlEvents:UIControlEventTouchUpInside]; //ok btn ... [okBtn addTarget:self action:@selector(clickOk) forControlEvents:UIControlEventTouchUpInside]; [self addSubview:self.addBtn]; [self addSubview:self.resultLab];}//点击数字按键- (void)clickNumber:(UIButton*)btn { if(self.addBtn.isSelected){ self.nValue = @""; } NSString *currentValue = @""; if (btn.tag == 99) { //有 . 就不加 . if ([self.nValue rangeOfString:@"."].location == NSNotFound) { currentValue = @"."; } }else { currentValue = [NSString stringWithFormat:@"%ld",(long)btn.tag]; } self.nValue = [self.nValue stringByAppendingString:currentValue]; //保留小数点后两位 NSRange pointRange = [self.nValue rangeOfString:@"."]; if (pointRange.location != NSNotFound) { if ([self.nValue substringFromIndex:pointRange.location+1].length > 2) { self.nValue = [self.nValue substringWithRange:NSMakeRange(0, pointRange.location + 3)]; } //总位数不超过9 处理小数部分 if ([self.nValue substringToIndex:pointRange.location].length > kMaxCalCount) { self.nValue = [NSString stringWithFormat:@"%0.2f",[self.nValue doubleValue]]; self.nValue = [self.nValue substringToIndex:kMaxCalCount+3]; } } else { //总位数不超过9 整数部分 if (self.nValue.length > kMaxCalCount) { self.nValue = [NSString stringWithFormat:@"%0.2f",[self.nValue doubleValue]]; self.nValue = [self.nValue substringToIndex:kMaxCalCount]; } } //显示数字 self.resultLab.text = [NSString stringWithFormat:@"¥ %.2f",[self.nValue doubleValue]]; self.addBtn.selected = NO; NSLog(@"new = %@",self.nValue);}//单击加号- (void)clickAdd { //显示结果 点击后nValue清零 if (!self.addBtn.isSelected) { self.addBtn.selected = YES; double result = [self.resutlStr doubleValue] + [self.nValue doubleValue] ; self.resutlStr = [NSString stringWithFormat:@"%.2f",result]; self.resultLab.text = [NSString stringWithFormat:@"¥ %.2f",[self.resutlStr doubleValue]]; NSLog(@"resutl = %@",self.resutlStr); }}//重置0- (void)resetZero { self.resutlStr = @""; self.nValue = @""; self.resultLab.text = @"¥ 0.00";}//退格- (void)clickDel { if (self.nValue.length > 0) { self.nValue = [self.nValue substringWithRange:NSMakeRange(0, self.nValue.length-1)]; } NSLog(@"-----%@",self.nValue); self.resultLab.text = [NSString stringWithFormat:@"¥ %.2f",[self.nValue doubleValue]];}//完成- (void)clickOk { //存在 使用修改保存方式 不存在 使用新增保存方式 if (self.isTallyExist) { [self modifyTallySavedWithIdentity:self.tallyIdentity]; } else { [self addTallySave]; }}//增加账单保存- (void)addTallySave { if ( ![self.typeLab.text isEqualToString:@""] && [self.nValue doubleValue] != 0) { [self clickAdd]; //存数据 NSManagedObjectContext *managedObjectContext = ((AppDelegate *)[UIApplication sharedApplication].delegate).persistentContainer.viewContext; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init]; [dateFormatter setDateFormat:@"yyyy-MM-dd"]; NSString *dateString = [dateFormatter stringFromDate:[NSDate date]]; //查询有无对应的date 有则使用无则创建 NSFetchRequest *fdate = [TallyDate fetchRequest]; NSArray<NSSortDescriptor> *sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"date" ascending:YES]]; fdate.sortDescriptors = sortDescriptors; NSPredicate *p = [NSPredicate predicateWithFormat:@"date = %@",dateString]; fdate.predicate = p; NSArray<TallyDate> *ss = [managedObjectContext executeFetchRequest:fdate error:nil]; TallyDate *date; if (ss.count > 0) { date = ss[0]; } else { date = [[TallyDate alloc] initWithContext:managedObjectContext]; date.date = dateString; } //配置数据 Tally *model = [[Tally alloc] initWithContext:managedObjectContext]; NSFetchRequest *ftype = [TallyType fetchRequest]; NSPredicate *ptype = [NSPredicate predicateWithFormat:@"typename = %@",self.typeLab.text]; ftype.predicate = ptype; NSArray<TallyType> *sstype = [managedObjectContext executeFetchRequest:ftype error:nil]; TallyType *type = [sstype firstObject]; //给关系赋值 model.typeship = type; model.dateship = date; model.identity = [NSString stringWithFormat:@"%@", [model objectID]]; model.timestamp = [NSDate date]; if ([self.typeLab.text isEqualToString:@"工资"]) { model.income = [self.resutlStr doubleValue]; model.expenses = 0; } else { model.expenses = [self.resutlStr doubleValue]; model.income = 0; } //存 [managedObjectContext save:nil]; if ([self.delegate respondsToSelector:@selector(tallySaveCompleted)]) { [self.delegate tallySaveCompleted]; } } else { if ([self.delegate respondsToSelector:@selector(tallySaveFailed)]) { [self.delegate tallySaveFailed]; } NSLog(@"不存"); }}//修改账单保存- (void)modifyTallySavedWithIdentity:(NSString *)identity { [self clickAdd]; if ([self.resutlStr doubleValue] == 0) { if ([self.delegate respondsToSelector:@selector(tallySaveFailed)]) { [self.delegate tallySaveFailed]; } NSLog(@"不存"); return; } self.addBtn.selected = NO; NSManagedObjectContext *managedObjectContext = ((AppDelegate *)[UIApplication sharedApplication].delegate).persistentContainer.viewContext; //设置账单类型 NSFetchRequest *ftype = [TallyType fetchRequest]; NSPredicate *ptype = [NSPredicate predicateWithFormat:@"typename = %@",self.typeLab.text]; ftype.predicate = ptype; NSArray<TallyType> *sstype = [managedObjectContext executeFetchRequest:ftype error:nil]; TallyType *type = [sstype firstObject]; //找出当前账单 NSFetchRequest *fetchRequest = [Tally fetchRequest]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"identity = %@", identity]; [fetchRequest setPredicate:predicate]; NSArray *fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:nil]; //配置当前账单 Tally *tally = [fetchedObjects firstObject]; tally.typeship = type; if ([self.typeLab.text isEqualToString:@"工资"]) { tally.income = [self.resutlStr doubleValue]; tally.expenses = 0; } else { tally.expenses = [self.resutlStr doubleValue]; tally.income = 0; } [managedObjectContext save:nil]; if ([self.delegate respondsToSelector:@selector(tallySaveCompleted)]) { [self.delegate tallySaveCompleted]; }}//修改界面传值- (void)modifyTallyWithIdentity:(NSString *)identity { self.tallyIdentity = identity; NSManagedObjectContext *managedObjectContext = ((AppDelegate *)[UIApplication sharedApplication].delegate).persistentContainer.viewContext; NSFetchRequest *fetchRequest = [Tally fetchRequest]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"identity = %@", identity]; [fetchRequest setPredicate:predicate]; NSArray *fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:nil]; Tally *tally = [fetchedObjects firstObject]; self.imageView.image = [UIImage imageNamed:tally.typeship.typeicon]; self.typeLab.text = tally.typeship.typename; self.nValue = tally.income > 0?[NSString stringWithFormat:@"%@",@(tally.income)]:[NSString stringWithFormat:@"%@",@(tally.expenses)]; self.resultLab.text = [NSString stringWithFormat:@"¥ %.2f",[self.nValue doubleValue]]; self.isTallyExist = YES;}@end
- 类型选择界面 自定义cell就不贴出来了 就一个图片和label
#import <UIKit>#import "TallyListCell.h"#import <CoreData>#import "TimeTallyDemo+CoreDataModel.h"#import "AppDelegate.h"@protocol TallyListViewDelegate <NSObject>//选择对应cell 传递 image title cell的实际位置- (void)didSelectItem:(UIImage*)cellImage andTitle:(NSString*)title withRectInCollection:(CGRect)itemRect;//滚动到底- (void)listScrollToBottom;@end@interface TallyListView : UICollectionView@property(nonatomic,strong)id<TallyListViewDelegate> customDelegate;@end
#import "TallyListView.h"@interface TallyListView()<UICollectionViewDelegate>@property (nonatomic, strong) NSArray *tallyListArray;@property (nonatomic, assign) CGFloat offsety;@endstatic NSString *cellId = @"tallyListCellID";@implementation TallyListView//读取plist数据- (NSArray *)tallyListArray { if (!_tallyListArray) { NSMutableArray *res = [NSMutableArray array]; NSString *path = [[NSBundle mainBundle] pathForResource:@"TallyList" ofType:@"plist"]; NSArray *list = [NSArray arrayWithContentsOfFile:path]; for (NSDictionary *dict in list) { TallyListCellModel *model = [TallyListCellModel tallyListCellModelWithDict:dict]; [res addObject:model]; } _tallyListArray = [NSArray arrayWithArray:res]; [self writeToSqlite]; } return _tallyListArray;}- (void)writeToSqlite { //将类型名字和图片信息写入数据库 NSManagedObjectContext *managedObjectContext = ((AppDelegate*)[UIApplication sharedApplication].delegate).persistentContainer.viewContext; for (TallyListCellModel *model in self.tallyListArray) { //查询有无对应的type 有则使用无则创建 NSFetchRequest *fdate = [TallyType fetchRequest]; NSPredicate *p = [NSPredicate predicateWithFormat:@"typename = %@",model.tallyCellName]; fdate.predicate = p; NSArray<TallyType> *ss = [managedObjectContext executeFetchRequest:fdate error:nil]; if (ss.count == 0) { TallyType *type = [[TallyType alloc] initWithContext:managedObjectContext]; type.typename = model.tallyCellName; type.typeicon = model.tallyCellImage; [managedObjectContext save:nil]; } }}//初始化- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout { self = [super initWithFrame:frame collectionViewLayout:layout]; if (self) { self.delegate = self; self.dataSource = self; self.backgroundColor = [UIColor clearColor]; [self registerNib:[UINib nibWithNibName:@"TallyListCell" bundle:nil] forCellWithReuseIdentifier:cellId]; } return self;}#pragma mark - UICollectionViewDelegate & UICollectionViewDataSource- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.tallyListArray.count;}- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ TallyListCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellId forIndexPath:indexPath]; cell.cellModel = self.tallyListArray[indexPath.item]; return cell;}- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { TallyListCell *cell = (TallyListCell*)[collectionView cellForItemAtIndexPath:indexPath]; //cell在collectionView的位置 CGRect cellRect = [collectionView convertRect:cell.frame fromView:collectionView]; //image在cell中的位置 CGRect imgInCellRect = cell.imageView.frame; CGFloat x = cellRect.origin.x + imgInCellRect.origin.x; CGFloat y = cellRect.origin.y + imgInCellRect.origin.y + 64 - self.offsety; //图片在collectionView的位置 CGRect imgRect = CGRectMake(x, y, imgInCellRect.size.width, imgInCellRect.size.height); //回调 if ([self.customDelegate respondsToSelector:@selector(didSelectItem:andTitle:withRectInCollection:)]){ [self.customDelegate didSelectItem:cell.imageView.image andTitle:cell.imageLab.text withRectInCollection:imgRect]; }}//外部调用 用于修改账单传值- (void)scrollViewDidScroll:(UIScrollView *)scrollView { self.offsety = scrollView.contentOffset.y;}- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{ CGFloat bottomY = self.contentSize.height - self.frame.size.height; if (scrollView.contentOffset.y >= bottomY) { NSLog(@"计算器下去"); if ([self.customDelegate respondsToSelector:@selector(listScrollToBottom)]) { [self.customDelegate listScrollToBottom]; } }}@end
主页面界面
- model
- 用于传递给账单界面的数据模型
- view
- 时间线绘图
- controller
- 处理时间线视图的删改查
主界面部分代码
- model
#import <Foundation>typedef enum : NSUInteger { TallyMoneyTypeIn = 0, TallyMoneyTypeOut,} TallyMoneyType;@interface TimeLineModel : NSObject@property(nonatomic,copy)NSString *tallyIconName;@property(nonatomic,assign)double tallyMoney;@property(nonatomic,assign)TallyMoneyType tallyMoneyType;@property(nonatomic,copy)NSString *tallyDate;@property(nonatomic,copy)NSString *tallyType;@property(nonatomic,copy)NSString *identity;@property(nonatomic,assign)double income;@property(nonatomic,assign)double expense;@end
- 时间线视图 这里用runtime方法为uibutton分类给时间线上的btn添加了两个属性 keyWithBtn :用于存储日期 panelBtnType :用于存储按钮是修改还是删除
typedef enum : NSUInteger { PanelViewBtnTypeLeft = 0, PanelViewBtnTypeRight,} PanelViewBtnType;//使用runtime 给uibutton扩展 属性@interface UIButton (BtnWithKey)@property(nonatomic,copy)NSString *keyWithBtn;@property(nonatomic,assign)PanelViewBtnType panelBtnType;@endstatic const void *kKeyWithBtn = @"keyWithBtn";static const void *kPanelBtnType = @"panelBtnType";@implementation UIButton (BtnWithKey)- (NSString *)keyWithBtn { return objc_getAssociatedObject(self, kKeyWithBtn);}- (void)setKeyWithBtn:(NSString *)keyWithBtn { objc_setAssociatedObject(self, kKeyWithBtn, keyWithBtn, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}- (PanelViewBtnType)panelBtnType { return [objc_getAssociatedObject(self, kPanelBtnType) intValue];}- (void)setPanelBtnType:(PanelViewBtnType)panelBtnType { objc_setAssociatedObject(self, kPanelBtnType, [NSNumber numberWithUnsignedInteger:panelBtnType], OBJC_ASSOCIATION_RETAIN_NONATOMIC);}@end
#import "TimeLineView.h"@interface TimeLineView()@property(nonatomic,strong)UIView *panelView; //删除、修改 面板@end@implementation TimeLineView- (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { } return self;}- (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); int keyIndex = 0; CGFloat aDateAllLine = 0; for (NSString *key in self.timeLineModelsDict.allKeys) { //读取字典对应key的数组 NSArray<TimeLineModel> *modelArray = self.timeLineModelsDict[key]; //画线画日期 CGRect dateRect = CGRectMake(self.center.x-kDateWidth/2, aDateAllLine, kDateWidth, kDateWidth); CGContextAddEllipseInRect(context, dateRect); CGContextSetFillColorWithColor(context, [UIColor grayColor].CGColor); CGContextFillPath(context); CGContextStrokePath(context); CGRect dateLabRect = CGRectMake(0, aDateAllLine, self.frame.size.width/2-kBtnWidth, kBtnWidth/2); //日期lab UILabel *dateLab = [[UILabel alloc] initWithFrame:dateLabRect]; dateLab.textAlignment = NSTextAlignmentRight; dateLab.text = key; [dateLab setTextColor:[UIColor blueColor]]; [self addSubview:dateLab]; for (int i = 0 ; i < modelArray xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed>*array = self.timeLineModelsDict[btn.keyWithBtn]; [self.delegate willDeleteCurrentTallyWithIdentity:array[btn.tag].identity]; }}//修改当前账单 回调给controller处理- (void)modifyCurrentTally:(UIButton*)btn { if ([self.delegate respondsToSelector:@selector(willModifyCurrentTallyWithIdentity:)]) { NSArray<TimeLineModel>*array = self.timeLineModelsDict[btn.keyWithBtn]; [self.delegate willModifyCurrentTallyWithIdentity:array[btn.tag].identity]; }}@end
- controller
#import "ViewController.h"#import "AddTallyViewController.h"#import "TimeLineView.h"@interface ViewController ()<TimeLineViewDelegate>@property(nonatomic,strong)UIScrollView *scrollView;@property(nonatomic,strong)NSDictionary *timeLineModelsDict;@property(nonatomic,assign)CGFloat allDateAllLine;@property (weak, nonatomic) IBOutlet UILabel *incomLab;@property (weak, nonatomic) IBOutlet UILabel *expenseLab;@end@implementation ViewController- (UIScrollView *)scrollView { if (!_scrollView) { _scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 64+80, self.view.frame.size.width, self.view.frame.size.height-64-80)]; _scrollView.backgroundColor = [UIColor whiteColor]; } return _scrollView;}- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"%@",NSHomeDirectory()); self.title = @"我的账本"; [self.view addSubview:self.scrollView];}- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}//增加一条账单- (IBAction)clickAddTally:(id)sender { AddTallyViewController *addVC = [[AddTallyViewController alloc] init]; [self.navigationController pushViewController:addVC animated:YES];}//出现时 刷新整个时间线- (void)viewWillAppear:(BOOL)animated { NSLog(@"will"); [self showTimeLineView];}- (void)showTimeLineView { //先读取数据库中内容 封装成字典 [self readSqliteData]; //移除上一个timelineView for (UIView *view in self.scrollView.subviews) { if (view.tag == 1990) { [view removeFromSuperview]; } } //计算总收入 和 总支出 double incomeTotal = 0; double expenseTotal = 0; self.allDateAllLine = 0; //计算时间线长度 for (NSString *key in self.timeLineModelsDict.allKeys) { NSArray<TimeLineModel> *modelArray = self.timeLineModelsDict[key]; //一天的时间线总长 self.allDateAllLine = self.allDateAllLine + kDateWidth + (kBtnWidth+kLineHeight)*modelArray.count + kLineHeight; for (TimeLineModel *model in modelArray) { incomeTotal = incomeTotal + model.income; expenseTotal = expenseTotal + model.expense; } } self.incomLab.text = [NSString stringWithFormat:@"%.2f",incomeTotal]; self.expenseLab.text = [NSString stringWithFormat:@"%.2f",expenseTotal]; //设置时间线视图timelineview CGRect rect = CGRectMake(0, 0, self.view.frame.size.width, self.allDateAllLine); TimeLineView *view = [[TimeLineView alloc] initWithFrame:rect]; view.tag = 1990; view.delegate = self; view.backgroundColor = [UIColor whiteColor]; view.timeLineModelsDict = self.timeLineModelsDict; [self.scrollView addSubview:view]; self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width, self.allDateAllLine); //滚动到顶端 [self.scrollView setContentOffset:CGPointZero animated:YES];}//读取数据库中的数据 以字典的形式 key:@"日期" object:[账单信息]- (void)readSqliteData{ NSManagedObjectContext *managedObjectContext = ((AppDelegate*)[UIApplication sharedApplication].delegate).persistentContainer.viewContext; self.timeLineModelsDict = nil; //先查询日期 遍历日期表 NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"TallyDate" inManagedObjectContext:managedObjectContext]; [fetchRequest setEntity:entity]; NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; [fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor, nil]]; NSError *error = nil; NSArray<TallyDate> *fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:&error]; NSMutableDictionary *dict = [NSMutableDictionary dictionary]; //再查询该日期下的tally表 for (TallyDate *date in fetchedObjects) { NSString *key = date.date; NSFetchRequest *fetchRequest2 = [[NSFetchRequest alloc] init]; NSEntityDescription *entity2 = [NSEntityDescription entityForName:@"Tally" inManagedObjectContext:managedObjectContext]; [fetchRequest2 setEntity:entity2]; //在tally表中 筛选 为该日期的tally 并逆序排列 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"dateship.date = %@",key]; [fetchRequest2 setPredicate:predicate]; NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:@"timestamp" ascending:NO]; [fetchRequest2 setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor2, nil]]; NSError *error = nil; NSArray<Tally> *fetchedObjects2 = [managedObjectContext executeFetchRequest:fetchRequest2 error:&error]; NSMutableArray *array = [NSMutableArray array]; //遍历 tally表 将表中的每个结果保存下来 for (Tally *tally in fetchedObjects2) { TimeLineModel *model = [[TimeLineModel alloc] init]; model.tallyDate = tally.dateship.date; model.tallyIconName = tally.typeship.typeicon; model.tallyMoney = tally.income > 0 ? tally.income:tally.expenses; model.tallyMoneyType = tally.income > 0 ? TallyMoneyTypeIn:TallyMoneyTypeOut; model.tallyType = tally.typeship.typename; model.identity = tally.identity; model.income = tally.income; model.expense = tally.expenses; [array addObject:model]; } [dict setObject:array forKey:key]; } self.timeLineModelsDict = dict;}//删除前的确认- (void)willDeleteCurrentTallyWithIdentity:(NSString*)identity { UIAlertController *alertVC =[UIAlertController alertControllerWithTitle:@"提示" message:@"确认删除" preferredStyle:UIAlertControllerStyleAlert ]; [alertVC addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { //从数据库中删除 Tally表中对应identity字段行 NSManagedObjectContext *managedObjectContext = ((AppDelegate*)[UIApplication sharedApplication].delegate).persistentContainer.viewContext; NSFetchRequest *fetchRequest = [Tally fetchRequest]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"identity = %@", identity]; [fetchRequest setPredicate:predicate]; NSError *error = nil; NSArray *fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:&error]; [managedObjectContext deleteObject:[fetchedObjects firstObject]]; [managedObjectContext save:&error]; //删除完成后 刷新视图 [self showTimeLineView]; }]]; [alertVC addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]]; [self presentViewController:alertVC animated:YES completion:nil];}//TimeLineViewDelegate delegate 修改前的准备- (void)willModifyCurrentTallyWithIdentity:(NSString*)identity { AddTallyViewController *addVC = [[AddTallyViewController alloc] init]; [addVC selectTallyWithIdentity:identity]; [self.navigationController pushViewController:addVC animated:YES];}@end
5.结束
由于coredata增删改查时的代码量实在是太大,我们可以优化一下,将数据库操作全部放到一个类中,这样代码逻辑会更清晰一点,可读性更强。 也是也到这里才想到数据库封装。所以刚刚去改了下。 所以上面的代码都包括冗长的coreData操作
创建一个 数据库操作的单例
#import <Foundation>#import <CoreData>#import "TimeTallyDemo+CoreDataModel.h"#import "AppDelegate.h"#import "TimeLineModel.h"@interface CoreDataOperations : NSObject@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;//单例+ (instancetype)sharedInstance;//从数据库中删除 Tally表中对应identity字段行- (void)deleteTally:(Tally*)object;//保存- (void)saveTally;//读取对应字段- (Tally*)getTallyWithIdentity:(NSString *)identity;//获取对应类型- (TallyType*)getTallyTypeWithTypeName:(NSString*)typeName;//读取数据库中的数据 以字典的形式 key:@"日期" object:[账单信息]- (NSDictionary*)getAllDataWithDict;@end
#import "CoreDataOperations.h"@interface CoreDataOperations()@end@implementation CoreDataOperationsstatic CoreDataOperations *instance = nil;+ (instancetype)sharedInstance{ return [[CoreDataOperations alloc] init];}+ (instancetype)allocWithZone:(struct _NSZone *)zone{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [super allocWithZone:zone]; }); return instance;}- (instancetype)init{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [super init]; if (instance) { instance.managedObjectContext = ((AppDelegate*)[UIApplication sharedApplication].delegate).persistentContainer.viewContext; } }); return instance;}//从数据库中删除 Tally表中某一数据- (void)deleteTally:(Tally*)object { [self.managedObjectContext deleteObject:object];}//保存- (void)saveTally { [self.managedObjectContext save:nil];}//读取对应字段- (Tally*)getTallyWithIdentity:(NSString *)identity { //返回对应账单}//获取对应类型- (TallyType*)getTallyTypeWithTypeName:(NSString*)typeName { //返回对应账单类型}//读取数据库中的数据 以字典的形式 key:@"日期" object:[账单信息]- (NSDictionary*)getAllDataWithDict{ //遍历查询 return dict;}@end
Demo地址
欢迎加星关注 https://github.com/gongxiaokai/TimeTallyDemo 简书地址http://www.jianshu.com/u/7897b0bd4a55
0 0
- iOS开发实战-时光记账Demo 本地数据库版
- iOS开发实战-时光记账Demo 本地数据库版
- iOS开发实战-时光记账Demo 网络版
- 【iOS】基于Realm数据库的记账软件--记账模块(二)
- 【iOS】基于Realm数据库的记账软件--钱包/记账模块(四)终
- 应用实战:记账本
- 【iOS】基于Realm数据库的记账软件--前言
- android 做着玩(一)简单记账软件 sqlite数据库开发
- 《记账本》开发记二:建立程序所需数据库
- iOS数据库开发之SQLite和FMDB详解(附Demo)
- 小米手环iOS开发实战(二):开发Demo让你的手环振动起来
- iOS开发--官方demo
- [iOS]wallet开发demo
- iOS iPad开发~demo
- ios 本地数据库访问
- 微信小程序(应用号)实战课程之记账软件开发
- ios 简易的本地收藏demo
- iOS开发本地缓存
- 【慢查询优化】连表查询注意谁是驱动表&搞不清楚谁join谁更好时放手让mysql自行判定
- LIDC-IDRI肺结节公开数据集Dicom和XML标注详解
- PAT 乙级 1058. 选择题(20)
- Eclipse在线安装FatJar插件失败解决方案
- 蛇形矩阵
- iOS开发实战-时光记账Demo 本地数据库版
- 【ife】任务二十九:表单(一)单个表单项的检验
- 第042讲 循环控制
- LR问题
- php 操作mongodb——增删改查
- linux常用指令(2)
- 将一个正整数分解质因数。例如:输入90,打印出90=2*3*3*5。求s=a+aa+aaa+aaaa+…a的值,其中a是一个数字。例如2+22+222+2222+22222(此时共有5个数相加
- LeetCode 14 Longest Common Prefix
- Scala包