寫在最前面:
其實(shí)本文應(yīng)該早在兩個(gè)月之前就該寫完的口糕,由于當(dāng)時(shí)找工作,種種原因擱置到現(xiàn)在才寫完差牛。
本文沒(méi)有把每一個(gè)點(diǎn)都能寫到。再說(shuō)時(shí)隔兩個(gè)多月沒(méi)碰這個(gè)項(xiàng)目了堰乔,多多少少都忘了一些偏化。我盡可能把當(dāng)時(shí)在寫這個(gè)項(xiàng)目遇到的各種坑寫詳細(xì)。如果在看本文或者demo的時(shí)候有不明白的地方可以在Github上面提issue或者簡(jiǎn)書簡(jiǎn)信我也可以镐侯。
溫馨提示:看文章的時(shí)候結(jié)合代碼一起看,效果會(huì)更佳喲侦讨。
目前完成進(jìn)度70%,由于時(shí)間的關(guān)系(臨近期末,各種事情的原因...)
項(xiàng)目采用MVC設(shè)計(jì)模式
本人還屬于菜鳥級(jí)別析孽,代碼寫得不規(guī)范搭伤,望見(jiàn)諒!
如果項(xiàng)目中同樣的問(wèn)題袜瞬,你有更好的辦法解決請(qǐng)告訴我怜俐,讓我們一起學(xué)習(xí)。
廢話說(shuō)了一大堆邓尤,開始進(jìn)入正題E睦稹!汞扎!
項(xiàng)目視頻演練 -> 點(diǎn)我
Demo ->Timi 不要忘記star支持喲
高仿版本:3.6.1
使用語(yǔ)言:Objective-C
開發(fā)工具及調(diào)試神器:Xcode 7.3.1季稳,Reveal 1.6.3
用到的三方庫(kù)及擴(kuò)展庫(kù)
Name | Explain |
---|---|
Masonry | 純代碼Autolayout |
MBProgressHUD | 未使用,后更改為使用SVProgressHUD |
MMDrawerController | 抽屜 |
SVProgressHUD | HUD |
YYText | 著名庫(kù)YYKit下的一個(gè)富文本 |
iCarousel | 一個(gè)類似UIScrollView的控件 |
ColorCube | 圖片顏色提取 |
UITextView_PlaceHolder | 給UITextView添加PlaceHolder |
SZCalendarPicker | 日歷 |
TYPagerController | 左右滾動(dòng)ViewController VTMagic |
Realm | 移動(dòng)端數(shù)據(jù)庫(kù)新王者 |
數(shù)據(jù)庫(kù)設(shè)計(jì)
TMBill(賬單)
Key | Identity | Column | Data Type | length | Allowed Null | Default | Description |
---|---|---|---|---|---|---|---|
√ | √ | billID | NSString | 64 | 主鍵 | ||
dateStr | NSString | 10 | 當(dāng)前年月日 | 時(shí)間 | |||
reMarks | NSString | 40 | nil | 備注 | |||
remarkPhoto | NSData | √ | nil | 圖片備注 | |||
isIncome | BOOL | 1 | 0 | 類型(收支) | |||
money | float | 13 | 0 | 金額 | |||
FK | category | TMCategory | 類別 | ||||
FK | book | TMBooks | 賬本 |
TMCategory(類別)
Key | Identity | Column | Data Type | length | Allowed Null | Default | Description |
---|---|---|---|---|---|---|---|
√ | √ | categoryID | NSString | 64 | 主鍵 | ||
categoryImageFileNmae | NSString | 64 | 類別icon文件名 | ||||
categoryTitle | NSString | 3 | 類別標(biāo)題 | ||||
isIncome | BOOL | 1 | 類型(收支) |
TMBook(賬本)
Key | Identity | Column | Data Type | length | Allowed Null | Default | Description |
---|---|---|---|---|---|---|---|
√ | √ | bookID | NSString | 64 | 主鍵 | ||
bookName | NSString | 6 | 賬本標(biāo)題 | ||||
imageIndex | int | 2 | 賬本對(duì)應(yīng)icon下標(biāo) | ||||
bookImageFileName | NSString | 64 | 類別icon文件名 |
TMAddCategory(新增類別)
Key | Identity | Column | Data Type | length | Allowed Null | Default | Description |
---|---|---|---|---|---|---|---|
√ | √ | categoryID | NSString | 64 | 主鍵 | ||
√ | categoryImageFileNmae | NSString | 64 | 類別icon文件名 | |||
isIncome | BOOL | 1 | 類型(收支) |
TMCategory(類別),TMAddCategory(新增類別)都是采用plist表的方式先存儲(chǔ)澈魄。當(dāng)App每次啟動(dòng)的時(shí)候就會(huì)先檢查數(shù)據(jù)庫(kù)對(duì)應(yīng)的表是否為空景鼠,為空則從plist表讀取數(shù)據(jù),存儲(chǔ)到本地?cái)?shù)據(jù)庫(kù)。
項(xiàng)目整體結(jié)構(gòu)
溫馨提醒
項(xiàng)目里面95%都是使用的純代碼方式布局(Masonry)铛漓,如果不懂的Masonry
純代碼布局的請(qǐng)先去了解一下溯香。傳送門=>串哥的深入講解 AutoLayout 和 Masonry
時(shí)光軸界面(HomePageViewController)
UI布局之header部分(TMHeaderView)
其實(shí)headerView部分沒(méi)有什么好說(shuō)的,那個(gè)餅圖是用UIBezierPath
和CAShapeLayer
繪制而成浓恶,我把它單獨(dú)封裝出來(lái)了玫坛,因?yàn)樵诤竺娴娘瀳D部分也用到了。關(guān)于餅圖的加載數(shù)據(jù)時(shí)候的動(dòng)畫我是使用的CABasicAnimation
具體的操作可以看demo的對(duì)應(yīng)文件(TMPieView
)
UI布局之?dāng)?shù)據(jù)顯示部分(HomePageViewController | TMTimeLineCell)
數(shù)據(jù)的顯示全部在一個(gè)section里面包晰,并沒(méi)有分section顯示湿镀,而且cell也只有一個(gè)樣式,我是通過(guò)收支類型來(lái)判斷的該那邊顯示數(shù)據(jù)伐憾。
時(shí)間軸上面勉痴,相同時(shí)間(同一天)時(shí)間label和金額label以及時(shí)間點(diǎn)不顯示出來(lái),我是在模型層加了一個(gè)BOOL變量來(lái)判斷树肃,同時(shí)在獲取數(shù)據(jù)之后進(jìn)行數(shù)據(jù)的重置蚀腿,具體的操作可以看
HomePageViewController
的getDataAndResetBill
函數(shù)。然后在自定義cell(
TMTimeLineCell
)重寫timeLineBill
屬性扫外,通過(guò)判斷來(lái)顯示數(shù)據(jù)。下圖應(yīng)該清楚的看懂整個(gè)cell的布局
其實(shí)這種做法并不好廓脆,一個(gè)cell是能完成筛谚,但是代碼看起來(lái)就有點(diǎn)亂糟糟的感覺(jué),正確的做法是應(yīng)該有兩種樣式的cell停忿。分別是賬單類型為收入驾讲,賬單類型為支出兩種樣式。
很多人都應(yīng)該碰到過(guò)席赂,滑動(dòng)tableView的時(shí)候Cell的數(shù)據(jù)會(huì)出現(xiàn)混亂吮铭,我是這樣解決的,在自定義cell重寫- (void)prepareForReuse
函數(shù)颅停,將cell里面的控件元素的屬性和對(duì)象統(tǒng)統(tǒng)置為nil谓晌。
//* 解決tableView滾動(dòng)導(dǎo)致數(shù)據(jù)混亂
準(zhǔn)備重用,防止?jié)L動(dòng)出現(xiàn)數(shù)據(jù)錯(cuò)亂 */
- (void)prepareForReuse {
[super prepareForReuse];
self.timeLineBill = nil;
self.categoryImageBtn.imageView.image = nil;
self.leftCategoryNameLabel.text = nil;
self.leftMoneyLabel.text = nil;
self.leftRemarkLabel.text = nil;
self.rightCategoryNameLabel.text = nil;
self.rightMoneyLabel.text = nil;
self.rightRemarkLabel.text = nil;
self.lastBill = NO;
}
細(xì)心的人可能看到了我在下滑tableview的時(shí)候,中間的時(shí)光軸線也跟著變長(zhǎng)癞揉。當(dāng)我下滑到一定程度纸肉,然后松手就會(huì)push到新增賬單界面,而且這個(gè)push動(dòng)畫不是系統(tǒng)自帶的push動(dòng)畫喊熟。
下面我一一為大家解答:
時(shí)光軸的線條是怎么變長(zhǎng)的柏肪?
第一步、我是新增的一個(gè)UIView芥牌,默認(rèn)frame為(SCREEN_SIZE.width-1)/2,0 , 1, 0)
烦味,將它加到tableview上面。
self.dropdownLineView = [[UIView alloc] initWithFrame:CGRectMake((SCREEN_SIZE.width-1)/2,0 , 1, 0)];
self.dropdownLineView.backgroundColor = LineColor;
[self.tableView addSubview:self.dropdownLineView];
第二步壁拉、在UIScrollViewDelegate的 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
代理函數(shù)里面獲取滑動(dòng)的y值谬俄。判斷其方向并重新設(shè)置dropdownLineView
的frame即可
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
/** 當(dāng)下拉的時(shí)候才有動(dòng)畫 y>0下拉,y<0上劃*/
CGFloat y = [scrollView.panGestureRecognizer translationInView:self.tableView].y;
// NSLog(@"%s--%d---y = %f",__func__,__LINE__,y);
if (y>0) {
/**
* 疑問(wèn):為什么是`y`是`-y`不是`0`,因?yàn)閌dropdownLineView`是添加到`tableView`的,所以當(dāng)`tabelView`拉下的時(shí)候`dropdownLineView`也會(huì)跟著向下移動(dòng)柏靶。
* 當(dāng)`y`是`-y`的時(shí)候`dropdownLineView`會(huì)向上移動(dòng)`y`個(gè)單位,才會(huì)達(dá)到我們理想的效果
*/
self.dropdownLineView.frame = CGRectMake((SCREEN_SIZE.width-1)/2, -y, 1, y);
[self.tableView bringSubviewToFront:self.dropdownLineView];
/** 餅圖+號(hào)按鈕動(dòng)畫*/
[self.headerView animationWithCreateBtnDuration:1.0f angle:y];
}
}
時(shí)光軸界面到添加賬單(修改賬單)界面的轉(zhuǎn)場(chǎng)動(dòng)畫(LYPushTransition,LYPopTransition)
使用的是自定義的轉(zhuǎn)場(chǎng)動(dòng)畫,具體如何使用請(qǐng)看喵神 和 KittenYang 的blog,推薦幾句代碼快速集成自定義轉(zhuǎn)場(chǎng)效果+全手勢(shì)驅(qū)動(dòng)
1.首先定一個(gè)class
,繼承至NSObject
,遵守UIViewControllerAnimatedTransitioning
協(xié)議。
2.需要實(shí)現(xiàn)兩個(gè)方法
/** 動(dòng)畫時(shí)間 */
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext
/** 轉(zhuǎn)場(chǎng)動(dòng)畫內(nèi)容(怎么轉(zhuǎn)) */
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
Push代碼細(xì)節(jié)講解(是一個(gè)反向prensent轉(zhuǎn)場(chǎng)動(dòng)畫)
/** 動(dòng)畫時(shí)間 */
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext {
return 0.5f;
}
/** 動(dòng)畫內(nèi)容(如何轉(zhuǎn)場(chǎng)) */
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
/**
*
1.transitionContext 過(guò)渡內(nèi)容上下文,可以通過(guò)它調(diào)用`viewControllerForKey:`拿到對(duì)應(yīng)的過(guò)渡控制器
key:UITransitionContextToViewControllerKey 目的控制器
UITransitionContextFromViewControllerKey 開始控制器
2.拿到對(duì)應(yīng)的過(guò)渡控制器之后需要設(shè)置view的frame
`finalFrameForViewController:` 可以拿到最后的frame,最后即完成動(dòng)畫后的frame
`initialFrameForViewController:` 拿到初始化的frame,開始動(dòng)畫之前的frame
3.然后添加到`transitionContext的containerView`
4.設(shè)置動(dòng)畫的其他附帶屬性動(dòng)畫
5.做動(dòng)畫... `UIView的block動(dòng)畫`
6.在動(dòng)畫結(jié)束后我們必須向context報(bào)告VC切換完成凤瘦,是否成功宿礁。系統(tǒng)在接收到這個(gè)消息后,將對(duì)VC狀態(tài)進(jìn)行維護(hù)蔬芥。
*
*/
//1...
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *toView = toVC.view;
//2...
CGRect finalFrame = [transitionContext finalFrameForViewController:toVC];
//(dx, dy) eg:dx偏移多少
toView.frame = CGRectOffset(finalFrame, 0, -SCREEN_SIZE.height);
//3....
UIView *containerView = [transitionContext containerView];
[containerView addSubview:toView];
//4...
//5...
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
toView.frame = finalFrame;
} completion:^(BOOL finished) {
//6...
[transitionContext completeTransition:YES];
}];
}
Pop做Push的相反操作即可
...
3. ViewController如何使用自定義轉(zhuǎn)場(chǎng)動(dòng)畫
-
pushViewController
在push的控制器設(shè)置navigationController
的delegate
為self
self.navigationController.delegate = self;
實(shí)現(xiàn)協(xié)議方法
- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { if (operation == UINavigationControllerOperationPush) { LYPushTransition *push = [LYPushTransition new]; return push; } else if (operation == UINavigationControllerOperationPop) { LYPopTransition *pop = [LYPopTransition new]; return pop; }else { return nil; }
}
```
通過(guò)operation
判斷是push
操作還是pop
操作,然后然后對(duì)于的動(dòng)畫即可
pop
控制器不需要做任何操作
如果使用push
,則會(huì)發(fā)現(xiàn)NavigationBar
沒(méi)有變化,會(huì)一直處于那個(gè)地方,很丑...
然而使用present
就可以避免這種現(xiàn)象
-
presentViewController
設(shè)置presentViewController
的ViewController
的transitioningDelegate
為self
注意,如果是present的UINavigationController
,則需要設(shè)置NavigationController
的transitioningDelegate
為self
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; SecondViewController *secondVC = [storyboard instantiateViewControllerWithIdentifier:@"second"]; secondVC.delegate = self; //* present */ UINavigationController *navi = [[UINavigationController alloc] initWithRootViewController:secondVC];
//* 如果present的NavigationController則需要設(shè)置NavigationController的transitioningDelegate為self */
navi.transitioningDelegate = self;
[self presentViewController:navi animated:YES completion:nil];
實(shí)現(xiàn)`transitioningDelegate`協(xié)議方法
/** prensent */
(nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController )presenting sourceController:(UIViewController )source {
return self.push;
}
/ dismiss */-
(nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
return self.pop;
}`dismiss`控制器則需要寫一個(gè)代理,告訴`present`的那個(gè)控制器`dismiss`即可
NavigationItemTitleView按鈕的邊框&點(diǎn)擊切換時(shí)候的顏色動(dòng)畫
/** 設(shè)置邊框?qū)挾?*/
titleBtn.layer.borderWidth = 1.5;
//* 設(shè)置Btn的邊框顏色 */
titleBtn.layer.borderColor = [UIColor whiteColor].CGColor;
關(guān)于點(diǎn)擊按鈕切換時(shí)候的動(dòng)畫我是使用的兩個(gè)UIView的動(dòng)畫
//* 改變NavigationTitleBtn的顏色 */
[UIView animateWithDuration:0.3f delay:0.2f options:UIViewAnimationOptionCurveEaseOut animations:^{
[weakSelf.navigationTitleBtn setBackgroundColor:[UIColor colorWithRed:1.000 green:0.812 blue:0.124 alpha:1.000]];
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationOptionCurveEaseOut animations:^{
[weakSelf.navigationTitleBtn setBackgroundColor:[UIColor colorWithWhite:0.278 alpha:0.500]];
} completion:^(BOOL finished) {
}];
}];
點(diǎn)擊類別按鈕彈出菜單(TMTimeLineMenuView)
我不是在每個(gè)cell下面都添加了deleteBtn,updateBtn,因?yàn)檫@樣會(huì)使性能大大降低梆靖。
我是自定義的一個(gè)UIView(TMTimeLineMenuView
),這里面有三個(gè)控件,分別是deleteBtn
,updateBtn
,categoryBtn
。
這個(gè)categoryBtn是放在deleteBtn,updateBtn上面的笔诵。因?yàn)樵赿eleteBtn和updateBtn彈出的時(shí)候我把TMTimeLineMenuView
放到了最頂層
//* 置頂 */
[weakSelf.superview bringSubviewToFront:weakSelf];
也就意味著tableView是在TMTimeLineMenuView的下面返吻。
如果沒(méi)有categoryBtn,彈出deleteBtn和updateBtn就感覺(jué)是直接在tableViewCell上面做的動(dòng)畫,會(huì)很丑。所以添加一個(gè)categoryBtn放在updateBtn和deleteBtn上面,就感覺(jué)deleteBtn和updateBtn是放在tableViewCell下面的乎婿。給用戶很好的用戶體驗(yàn)测僵。
如何將TMTimeLineMenuView中的控件顯示到對(duì)應(yīng)的位置?(HomePageViewController->didClickCategoryBtnWithIndexPath:)
第一步:獲取到點(diǎn)擊的cell對(duì)應(yīng)的indexPath
第二步:獲取對(duì)應(yīng)cell在tableview中的rect
第三步:將獲取到的rect轉(zhuǎn)換成在self.view中的rect
/** 獲取cell在tableView中的位置 */
CGRect rect = [self.tableView rectForRowAtIndexPath:indexPath];
//* 轉(zhuǎn)換成在self.view中的位置 */
CGRect rectInSuperview = [self.tableView convertRect:rect toView:[self.tableView superview]];
self.timeLineMenuView.currentImage = self.timeLineCell.categoryImageBtn.currentImage;
[self.timeLineMenuView showTimeLineMenuViewWithRect:rectInSuperview ];
創(chuàng)建賬單界面(TMCreateBillViewController)
選擇類別動(dòng)畫之類別圖片動(dòng)畫(應(yīng)該使用UI Dynamics)
第一步:
在創(chuàng)建賬單界面添加一個(gè)UIImageView
控件,大小跟collectionViewCell里面的categoryImageView
一樣,放在屏幕外谢翎。并設(shè)置圓角捍靠。
- (UIImageView *)selectCategoryImageView
{
if (!_selectCategoryImageView) {
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, -30, kCollectionCellWidth-20, kCollectionCellWidth-20)];
imageView.layer.cornerRadius = (kCollectionCellWidth - 20)/2;
imageView.layer.masksToBounds = YES;
imageView.contentMode = UIViewContentModeScaleAspectFill;
_selectCategoryImageView = imageView;
}
return _selectCategoryImageView;
}
第二步: 獲取點(diǎn)擊的位置
1.拿到對(duì)應(yīng)cell
cell = (TMCategotyCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];
2.將cell對(duì)應(yīng)的類別圖片賦值給_selectCategoryImageView
然后獲取到cell的center
,這個(gè)centter的y
僅僅是它在collectionView的位置,所以還需要修改y值,然后使用UIView的block動(dòng)畫移動(dòng)到headerView上面對(duì)應(yīng)的點(diǎn)。在動(dòng)畫完成之后將它放到最底層
/** 選擇類別之后的類別圖片動(dòng)畫 */
- (void)animationWithCell:(TMCategotyCollectionViewCell *)cell {
self.selectCategoryImageView.image = cell.categoryImageView.image;
CGPoint center = cell.center;
/** 在collectionView中的y */
CGFloat y = CGRectGetMaxY(cell.frame);
center.y = kMaxNBY + y + 10;
self.selectCategoryImageView.center = center;
WEAKSELF
[UIView animateWithDuration:0.05 animations:^{
weakSelf.selectCategoryImageView.center = kHeaderCategoryImageCenter;
} completion:^(BOOL finished) {
[weakSelf.view sendSubviewToBack:weakSelf.selectCategoryImageView];
}];
[self.view bringSubviewToFront:self.selectCategoryImageView];
}
選擇類別動(dòng)畫之HeaderView顏色動(dòng)畫
第一步:提取顏色
我使用的是一個(gè)三方庫(kù)森逮,ColorExtraction
//* 顏色提取 */
CCColorCube *imageColor = [[CCColorCube alloc] init];
NSArray *colors = [imageColor extractColorsFromImage:category.categoryImage flags:CCAvoidBlack count:1];
第二步:動(dòng)畫
我是使用UIBezierPath和CAShapeLayer結(jié)合CABasicAnimation做的動(dòng)畫榨婆。
UIBezierPath的path如何而來(lái)?
path就是一條線,path的moveToPoint
點(diǎn)就是self.bounds.origin
點(diǎn)即左上點(diǎn)
addLineToPoint
點(diǎn)就是self.bounds.origin.x
點(diǎn)和self.bounds.size.height
點(diǎn)即左下點(diǎn)
然后通過(guò)CABasicAnimation改變lineWidth
- (void)animationWithBgColor:(UIColor *)color {
//* 如果選擇的類別圖片的顏色和上次選擇的一樣 直接return */
if ([color isEqual: self.previousSelectColor]) return;
//* 修改背景顏色為上一次選擇的顏色,不然就會(huì)是最開始默認(rèn)的顏色,動(dòng)畫會(huì)很丑,給用戶的體驗(yàn)很不好 */
self.backgroundColor = self.previousSelectColor;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"lineWidth"];
animation.fromValue = @0.0;
animation.toValue = @(self.bounds.size.width * 2);
animation.duration = 0.3f;
//* 設(shè)置填充色 */
self.bgColorlayer.fillColor = color.CGColor;
//* 設(shè)置邊框色 */
self.bgColorlayer.strokeColor = color.CGColor;
self.previousSelectColor = color;
//* 保持動(dòng)畫 */
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
[self.bgColorlayer addAnimation:animation forKey:@"bgColorAnimation"];
//* 將子控件放在最上面,不然layer會(huì)覆蓋 */
[self bringSubviewToFront:self.categoryImageView];
[self bringSubviewToFront:self.moneyLabel];
[self bringSubviewToFront:self.categoryNameBtn];
}
餅圖(TMPiewViewController)
餅圖HeaderView部分
控件是使用三方庫(kù)iCarousel
鏈接
數(shù)據(jù)源如何而來(lái)褒侧?
1.先把每個(gè)月中文對(duì)應(yīng)的英文縮寫保存到一個(gè)數(shù)組中
- (NSArray *)items {
if (!_items) {
_items = @[@"JAN\n1月",@"FEB\n2月",@"MAR\n3月",@"APR\n4月",@"MAY\n5月",@"JUN\n6月",@"JUL\n7月",@"AUG\n8月",@"SEP\n9月",@"OCT\n10月",@"NOV\n11月",@"DEC\n12月",@"ALL\n全部"];
}
return _items;
}
疑問(wèn):為什么數(shù)據(jù)每個(gè)元素,中間有個(gè)\n
答:我是使用的是一個(gè)UILabel\n
用于換行
2.拿到篩選過(guò)后的數(shù)據(jù),是一個(gè)NSDictionary良风。額...說(shuō)一下,這個(gè)篩選過(guò)后的數(shù)據(jù)的一個(gè)結(jié)構(gòu),因?yàn)橥惶煳覀兛赡軙?huì)記多筆賬,所以把同一天的dateStr
作為key
,然后把所有屬于這一天的賬單數(shù)據(jù)當(dāng)作一個(gè)value
,目前為止只是過(guò)濾掉同一天的時(shí)間字符串。
然后下一步我們要做的就是過(guò)濾掉同一年的相同月份
/** 過(guò)濾掉同年相同月份 */
- (void)filterMonthWithDateArray:(NSArray *)array {
for (NSString *dateStr in array) {
NSString *yearAndMonth = [dateStr substringToIndex:7];
BOOL contains = [self containsMonth:yearAndMonth];
if (!contains) {
NSString *month = [self conversionDateStringIntoMonth:dateStr];
[self.dic setValue:month forKey:dateStr];
}
}
[self.dic setValue:self.items.lastObject forKey:@"ALL"];
self.sortDicKeys = [self sortArray:self.dic.allKeys ascending:YES];
[self.iCar reloadData];
}
/** 把時(shí)間字符串轉(zhuǎn)換成月份 */
- (NSString *)conversionDateStringIntoMonth:(NSString *)dateString {
NSRange range = NSMakeRange(5, 2);
NSString *month = [dateString substringWithRange:range];
return self.items[month.integerValue - 1];
}
/** 判斷字典里面是否已經(jīng)包含這個(gè)對(duì)象 */
- (BOOL)containsMonth:(NSString *)yearAndMonth {
if (self.dic.allKeys.count==0) {
return NO;
} else {
for (NSInteger i=0; i<self.dic.allKeys.count ; i++) {
if ([[self.dic.allKeys[i] substringToIndex:7] isEqualToString:yearAndMonth]) {
return YES;
}
}
}
return NO;
}
獲取layer的位置
- (NSInteger)getLayerIndexWithPoint:(CGPoint)point {
for (NSInteger i=0; i<[self.containerLayer sublayers].count; i++) {
CAShapeLayer *layer = (CAShapeLayer *)[self.containerLayer sublayers][i];
CGPathRef path = [layer path];
if (CGPathContainsPoint(path, NULL, point, 0)) {
return i;
}
}
return -1;
}
拿到所有的sublayer闷供,取出layer的path,通過(guò)CGPathContainsPoint
判斷觸摸的點(diǎn)是否在這個(gè)path里面
類別詳細(xì)界面(TMPiewCategoryDetailViewController)
解決cell重用導(dǎo)致數(shù)據(jù)年月日l(shuí)abel
顯示混亂,在模型定義兩個(gè)BOOL
變量same,partSame
拿到數(shù)據(jù)之后將數(shù)據(jù)進(jìn)行“重置”
(void)resetBill {
self.bills = [NSMutableArray array];
NSString *previous;
for (NSInteger i=0; i<self.results.count; i++) {
TMBill *bill = self.results[i];
if (i==0) {//第一個(gè)數(shù)據(jù)永遠(yuǎn)是不相同的
[self.bills addObject:bill];
previous = bill.dateStr;
continue;
} else {
TMBill *theBill = [TMBill new];
if ([previous isEqualToString:bill.dateStr]) {//完全相同,時(shí)間日期
theBill = bill;
theBill.same = YES;
[self.bills addObject:theBill];
} else if ([[previous substringToIndex:7] isEqualToString:[bill.dateStr substringToIndex:7]]) {//部分相同,年月份相同,具體時(shí)間不同
theBill = bill;
theBill.partSame = YES;
[self.bills addObject:theBill];
} else {//不同
[self.bills addObject:bill];
}
previous = bill.dateStr;
}
}
}
側(cè)滑控制器,使用的是MMDrawerController庫(kù)
本來(lái)MMDrawerController
是支持在屏幕向右滑就能出現(xiàn)左邊的菜單欄,由于使用了TYPagerController
出現(xiàn)了手勢(shì)之間的沖突
解決和TYPagerController
手勢(shì)沖突的問(wèn)題
UIScreenEdgePanGestureRecognizer *screenEdgeGR = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(clickMenuBtn:)];
screenEdgeGR.edges = UIRectEdgeLeft;
[self.view addGestureRecognizer:screenEdgeGR];
- (void)clickMenuBtn:(UIButton *)sender {
[self.mm_drawerController toggleDrawerSide:MMDrawerSideLeft animated:YES completion:nil];
}
如果直接是這樣的話則會(huì)出現(xiàn)下面的情況
因?yàn)?code>UIScreenEdgePanGestureRecognizer是一個(gè)持續(xù)響應(yīng)事件,也就是說(shuō)你的手指沒(méi)離開屏幕則會(huì)一直響應(yīng)這個(gè)函數(shù),因?yàn)?code>toggleDrawerSide在內(nèi)部會(huì)判斷菜單欄是打開還是關(guān)閉,打開則關(guān)閉,關(guān)閉則會(huì)打開,所以也就會(huì)出現(xiàn)上面這種情況了烟央。
解決辦法
if (self.mm_drawerController.openSide == MMDrawerSideNone) {
[self.mm_drawerController toggleDrawerSide:MMDrawerSideLeft animated:YES completion:nil];
}
賬本控制器(TMSideViewController)
如何抖動(dòng)?在cell上添加一個(gè)UILongPressGestureRecognizer
長(zhǎng)按手勢(shì)
//* 長(zhǎng)按手勢(shì) */
UILongPressGestureRecognizer *longGR = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longGR:)];
longGR.minimumPressDuration = 1.0;
longGR.numberOfTouchesRequired = 1;
longGR.allowableMovement = 10;
[self addGestureRecognizer:longGR];
給cell添加一個(gè)代理
@protocol TMSideCellDelegate <NSObject>
@required
@optional
- (void)TMSideCellWithIndexPath:(NSIndexPath *)indexPath withLongPress:(UILongPressGestureRecognizer *)longPress;
@end
當(dāng)控制器接收到響應(yīng)事件的時(shí)候只需要做三件事
self.editSelectedIndexPath = indexPath; //1
self.edit = YES; //2
[self.collectionView reloadData]; //3
在- (UICollectionViewCell *)collectionView: cellForItemAtIndexPath:
添加判斷代碼
//* edit mode on shake ->ture*/
if (self.isEdit) {
if ([indexPath isEqual:self.editSelectedIndexPath]) {
cell.editSelectedItemImageView.hidden = NO;
[self shakeCell:cell];
} else {
cell.editSelectedItemImageView.hidden = YES;
}
} else {
cell.editSelectedItemImageView.hidden = YES;
cell.transform = CGAffineTransformIdentity;
}
/** 抖動(dòng)動(dòng)畫 */
- (void)shakeCell:(TMSideCell *)cell {
[UIView animateWithDuration:0.1 delay:0 options:0 animations:^{
cell.transform=CGAffineTransformMakeRotation(-0.02);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationOptionRepeat|UIViewAnimationOptionAutoreverse | UIViewAnimationOptionAllowUserInteraction animations:^{
cell.transform=CGAffineTransformMakeRotation(0.02);
} completion:nil];
}];
}
好了歪脏,暫時(shí)就寫這么多吧疑俭。有疑問(wèn)可以在Github上面提issue或者簡(jiǎn)書簡(jiǎn)信我也可以。謝謝婿失!