iOS 高仿Timi記賬

寫在最前面:

其實(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 賬本
TMBill(賬單).png

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 類型(收支)
TMCategory(類別).png

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文件名
TMBook(賬本).png

TMAddCategory(新增類別)

Key Identity Column Data Type length Allowed Null Default Description
categoryID NSString 64 主鍵
categoryImageFileNmae NSString 64 類別icon文件名
isIncome BOOL 1 類型(收支)
TMAddCategory(新增類別).png

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)

TimiStructure.png

溫馨提醒

項(xiàng)目里面95%都是使用的純代碼方式布局(Masonry)铛漓,如果不懂的Masonry純代碼布局的請(qǐng)先去了解一下溯香。傳送門=>串哥的深入講解 AutoLayout 和 Masonry

時(shí)光軸界面(HomePageViewController)

2016-07-01 14.58.02.gif

UI布局之header部分(TMHeaderView)

Paste_Image.png

其實(shí)headerView部分沒(méi)有什么好說(shuō)的,那個(gè)餅圖是用UIBezierPathCAShapeLayer繪制而成浓恶,我把它單獨(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)

Paste_Image.png

數(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ù)的重置蚀腿,具體的操作可以看HomePageViewControllergetDataAndResetBill函數(shù)。
然后在自定義cell(TMTimeLineCell)重寫timeLineBill屬性扫外,通過(guò)判斷來(lái)顯示數(shù)據(jù)。
下圖應(yīng)該清楚的看懂整個(gè)cell的布局
Paste_Image.png

其實(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è)置navigationControllerdelegateself

    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è)置presentViewControllerViewControllertransitioningDelegateself
    注意,如果是present的UINavigationController,則需要設(shè)置NavigationControllertransitioningDelegateself

    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的下面返吻。


Paste_Image.png

如果沒(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)

TimiAddBillController.gif

選擇類別動(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)

TMPie.gif

餅圖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)下面的情況

2016-06-25 22.50.25.gif

因?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)

books.gif

如何抖動(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)信我也可以。謝謝婿失!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末怠硼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子移怯,更是在濱河造成了極大的恐慌香璃,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舟误,死亡現(xiàn)場(chǎng)離奇詭異葡秒,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門眯牧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蹋岩,“玉大人,你說(shuō)我怎么就攤上這事学少〖舾觯” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵版确,是天一觀的道長(zhǎng)扣囊。 經(jīng)常有香客問(wèn)我,道長(zhǎng)绒疗,這世上最難降的妖魔是什么侵歇? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮吓蘑,結(jié)果婚禮上惕虑,老公的妹妹穿的比我還像新娘。我一直安慰自己磨镶,他們只是感情好溃蔫,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著琳猫,像睡著了一般酒唉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上沸移,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音侄榴,去河邊找鬼雹锣。 笑死,一個(gè)胖子當(dāng)著我的面吹牛癞蚕,可吹牛的內(nèi)容都是我干的蕊爵。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼桦山,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼攒射!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起恒水,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤会放,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后钉凌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咧最,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了矢沿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滥搭。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖捣鲸,靈堂內(nèi)的尸體忽然破棺而出瑟匆,到底是詐尸還是另有隱情,我是刑警寧澤栽惶,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布愁溜,位于F島的核電站,受9級(jí)特大地震影響媒役,放射性物質(zhì)發(fā)生泄漏祝谚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一酣衷、第九天 我趴在偏房一處隱蔽的房頂上張望交惯。 院中可真熱鬧,春花似錦穿仪、人聲如沸席爽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)只锻。三九已至,卻和暖如春紫谷,著一層夾襖步出監(jiān)牢的瞬間齐饮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工笤昨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留祖驱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓瞒窒,卻偏偏與公主長(zhǎng)得像捺僻,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子崇裁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容