iOS 高仿 Timi 記賬

gvxb2966 8年前發布 | 12K 次閱讀 iOS開發 移動開發

本人還屬于菜鳥級別,代碼寫得不規范,望見諒!

項目視頻演練 -> 點我

Demo -> Timi 不要忘記star支持喲

高仿版本:3.6.1

使用語言:Objective-C

開發工具及調試神器:Xcode 7.3.1,Reveal 1.6.3

用到的三方庫及擴展庫

Name Explain
Masonry 純代碼Autolayout
MBProgressHUD 未使用,后更改為使用SVProgressHUD
MMDrawerController 抽屜
SVProgressHUD HUD
YYText 著名庫YYKit下的一個富文本
iCarousel 一個類似UIScrollView的控件
ColorCube 圖片顏色提取
UITextView_PlaceHolder 給UITextView添加PlaceHolder
SZCalendarPicker 日歷
TYPagerController 左右滾動ViewController VTMagic
Realm 移動端數據庫新王者

數據庫設計

TMBill(賬單)

Key Identity Column Data Type length Allowed Null Default Description
billID NSString 64     主鍵
      dateStr NSString 10 當前年月日 時間
      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   類別標題
    isIncome BOOL 1     類型(收支)

TMCategory(類別).png

TMBook(賬本)

Key Identity Column Data Type length Allowed Null Default Description
bookID NSString 64     主鍵
      bookName NSString 6   賬本標題
      imageIndex int 2   賬本對應icon下標
    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表的方式先存儲。當App每次啟動的時候就會先檢查數據庫對應的表是否為空,為空則從plist表讀取數據,存儲到本地數據庫。

項目整體結構

TimiStructure.png

溫馨提醒

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

時光軸界面(HomePageViewController)

2016-07-01 14.58.02.gif

UI布局之header部分(TMHeaderView)

Paste_Image.png

其實headerView部分沒有什么好說的,那個餅圖是用 UIBezierPath 和 CAShapeLayer 繪制而成,我把它單獨封裝出來了,因為在后面的餅圖部分也用到了。關于餅圖的加載數據時候的動畫我是使用的 CABasicAnimation 具體的操作可以看demo的對應文件( TMPieView )

UI布局之數據顯示部分(HomePageViewController | TMTimeLineCell)

Paste_Image.png

數據的顯示全部在一個section里面,并沒有分section顯示,而且cell也只有一個樣式,我是通過收支類型來判斷的該那邊顯示數據。

時間軸上面,相同時間(同一天)時間label和金額label以及時間點不顯示出來,我是在模型層加了一個BOOL變量來判斷,同時在獲取數據之后進行數據的重置,具體的操作可以看 HomePageViewController 的 getDataAndResetBill 函數。

然后在自定義cell( TMTimeLineCell )重寫 timeLineBill 屬性,通過判斷來顯示數據。

下圖應該清楚的看懂整個cell的布局

Paste_Image.png

其實這種做法并不好,一個cell是能完成,但是代碼看起來就有點亂糟糟的感覺,正確的做法是應該有兩種樣式的cell。分別是賬單類型為收入,賬單類型為支出兩種樣式。

很多人都應該碰到過,滑動tableView的時候Cell的數據會出現混亂,我是這樣解決的,在自定義cell重寫 - (void)prepareForReuse 函數,將cell里面的控件元素的屬性和對象統統置為nil。

//* 解決tableView滾動導致數據混亂
     準備重用,防止滾動出現數據錯亂 */
- (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;
    }

細心的人可能看到了我在下滑tableview的時候,中間的時光軸線也跟著變長。當我下滑到一定程度,然后松手就會push到新增賬單界面,而且這個push動畫不是系統自帶的push動畫。

下面我一一為大家解答:

時光軸的線條是怎么變長的?

第一步、我是新增的一個UIView,默認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 代理函數里面獲取滑動的y值。判斷其方向并重新設置 dropdownLineView 的frame即可

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    /** 當下拉的時候才有動畫  y>0下拉,y<0上劃*/
    CGFloat  y = [scrollView.panGestureRecognizer translationInView:self.tableView].y;
//    NSLog(@"%s--%d---y = %f",__func__,__LINE__,y);
    if (y>0) {
        /**
         *  疑問:為什么是`y`是`-y`不是`0`,因為`dropdownLineView`是添加到`tableView`的,所以當`tabelView`拉下的時候`dropdownLineView`也會跟著向下移動。
         *  當`y`是`-y`的時候`dropdownLineView`會向上移動`y`個單位,才會達到我們理想的效果
         */
        self.dropdownLineView.frame = CGRectMake((SCREEN_SIZE.width-1)/2, -y, 1, y);
        [self.tableView bringSubviewToFront:self.dropdownLineView];
         /** 餅圖+號按鈕動畫*/
        [self.headerView animationWithCreateBtnDuration:1.0f angle:y];
    }
}

時光軸界面到添加賬單(修改賬單)界面的轉場動畫(LYPushTransition,LYPopTransition)

使用的是自定義的轉場動畫,具體如何使用請看 喵神KittenYang 的blog,推薦 幾句代碼快速集成自定義轉場效果+全手勢驅動

1.首先定一個 class ,繼承至 NSObject ,遵守 UIViewControllerAnimatedTransitioning 協議。

2.需要實現兩個方法

/** 動畫時間 */
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext

/** 轉場動畫內容(怎么轉) */
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext

Push代碼細節講解(是一個反向prensent轉場動畫)

/** 動畫時間 */
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext {
    return 0.5f;
}
/** 動畫內容(如何轉場) */
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
    /**
     *
    1.transitionContext 過渡內容上下文,可以通過它調用`viewControllerForKey:`拿到對應的過渡控制器
        key:UITransitionContextToViewControllerKey 目的控制器
            UITransitionContextFromViewControllerKey 開始控制器
    2.拿到對應的過渡控制器之后需要設置view的frame
        `finalFrameForViewController:` 可以拿到最后的frame,最后即完成動畫后的frame
        `initialFrameForViewController:` 拿到初始化的frame,開始動畫之前的frame
    3.然后添加到`transitionContext的containerView`

    4.設置動畫的其他附帶屬性動畫

    5.做動畫... `UIView的block動畫`

    6.在動畫結束后我們必須向context報告VC切換完成,是否成功。系統在接收到這個消息后,將對VC狀態進行維護。
     *
     */

    //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如何使用自定義轉場動畫

  • pushViewController

    在push的控制器設置 navigationController 的 delegate 為 self

    self.navigationController.delegate = self;

    實現協議方法

    - (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;
      }
    }

    通過 operation 判斷是 push 操作還是 pop 操作,然后然后對于的動畫即可

    pop 控制器不需要做任何操作

    如果使用 push ,則會發現 NavigationBar 沒有變化,會一直處于那個地方,很丑...

    然而使用 present 就可以避免這種現象

  • presentViewController

    設置 presentViewController 的 ViewController 的 transitioningDelegate 為 self

    注意,如果是present的 UINavigationController ,則需要設置 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則需要設置NavigationController的transitioningDelegate為self */
    navi.transitioningDelegate = self;
    [self presentViewController:navi animated:YES completion:nil];

    實現 transitioningDelegate 協議方法

/** 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`控制器則需要寫一個代理,告訴`present`的那個控制器`dismiss`即可

NavigationItemTitleView按鈕的邊框&點擊切換時候的顏色動畫

/** 設置邊框寬度 */
 titleBtn.layer.borderWidth = 1.5;
//* 設置Btn的邊框顏色 */
titleBtn.layer.borderColor = [UIColor whiteColor].CGColor;

關于點擊按鈕切換時候的動畫我是使用的兩個UIView的動畫

//* 改變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) {

        }];
    }];

點擊類別按鈕彈出菜單(TMTimeLineMenuView)

我不是在每個cell下面都添加了deleteBtn,updateBtn,因為這樣會使性能大大降低。

我是自定義的一個UIView( TMTimeLineMenuView ),這里面有三個控件,分別是 deleteBtn , updateBtn , categoryBtn 。

這個categoryBtn是放在deleteBtn,updateBtn上面的。因為在deleteBtn和updateBtn彈出的時候我把 TMTimeLineMenuView 放到了最頂層

//* 置頂 */
[weakSelf.superview bringSubviewToFront:weakSelf];

也就意味著tableView是在TMTimeLineMenuView的下面。

Paste_Image.png

如果沒有categoryBtn,彈出deleteBtn和updateBtn就感覺是直接在tableViewCell上面做的動畫,會很丑。所以添加一個categoryBtn放在updateBtn和deleteBtn上面,就感覺deleteBtn和updateBtn是放在tableViewCell下面的。給用戶很好的用戶體驗。

如何將TMTimeLineMenuView中的控件顯示到對應的位置?(HomePageViewController->didClickCategoryBtnWithIndexPath:)

第一步:獲取到點擊的cell對應的indexPath

第二步:獲取對應cell在tableview中的rect

第三步:將獲取到的rect轉換成在self.view中的rect

/** 獲取cell在tableView中的位置 */
CGRect rect = [self.tableView rectForRowAtIndexPath:indexPath];
//* 轉換成在self.view中的位置 */
CGRect rectInSuperview = [self.tableView convertRect:rect toView:[self.tableView superview]];
self.timeLineMenuView.currentImage = self.timeLineCell.categoryImageBtn.currentImage;
[self.timeLineMenuView showTimeLineMenuViewWithRect:rectInSuperview ];

創建賬單界面(TMCreateBillViewController)

TimiAddBillController.gif

選擇類別動畫之類別圖片動畫(應該使用UI Dynamics)

第一步:

在創建賬單界面添加一個 UIImageView 控件,大小跟collectionViewCell里面的 categoryImageView 一樣,放在屏幕外。并設置圓角。

- (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;
}

第二步: 獲取點擊的位置

1.拿到對應cell

cell = (TMCategotyCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];

2.將cell對應的類別圖片賦值給 _selectCategoryImageView
然后獲取到cell的 center ,這個 centter的y 僅僅是它在collectionView的位置,所以還需要修改y值,然后使用UIView的block動畫移動到headerView上面對應的點。在動畫完成之后將它放到最底層

/** 選擇類別之后的類別圖片動畫 */
- (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];
}

選擇類別動畫之HeaderView顏色動畫

第一步:提取顏色

我使用的是一個三方庫, ColorExtraction

//* 顏色提取 */
CCColorCube *imageColor = [[CCColorCube alloc] init];
NSArray *colors = [imageColor extractColorsFromImage:category.categoryImage flags:CCAvoidBlack count:1];

第二步:動畫

我是使用UIBezierPath和CAShapeLayer結合CABasicAnimation做的動畫。

UIBezierPath的path如何而來?

path就是一條線,path的 moveToPoint 點就是 self.bounds.origin 點即左上點

addLineToPoint 點就是 self.bounds.origin.x 點和 self.bounds.size.height 點即左下點

然后通過CABasicAnimation改變 lineWidth

- (void)animationWithBgColor:(UIColor *)color {
    //* 如果選擇的類別圖片的顏色和上次選擇的一樣  直接return */
    if ([color isEqual: self.previousSelectColor]) return;
    //* 修改背景顏色為上一次選擇的顏色,不然就會是最開始默認的顏色,動畫會很丑,給用戶的體驗很不好 */
    self.backgroundColor = self.previousSelectColor;

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"lineWidth"];
    animation.fromValue = @0.0;
    animation.toValue = @(self.bounds.size.width * 2);
    animation.duration = 0.3f;
    //* 設置填充色 */
    self.bgColorlayer.fillColor = color.CGColor;
    //* 設置邊框色 */
    self.bgColorlayer.strokeColor = color.CGColor;

    self.previousSelectColor = color;
    //* 保持動畫 */
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;

    [self.bgColorlayer addAnimation:animation forKey:@"bgColorAnimation"];

    //* 將子控件放在最上面,不然layer會覆蓋 */
    [self bringSubviewToFront:self.categoryImageView];
    [self bringSubviewToFront:self.moneyLabel];
    [self bringSubviewToFront:self.categoryNameBtn];
}

餅圖(TMPiewViewController)

TMPie.gif

餅圖HeaderView部分

控件是使用三方庫 iCarousel 鏈接

數據源如何而來?

1.先把每個月中文對應的英文縮寫保存到一個數組中

- (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;
}

疑問:為什么數據每個元素,中間有個 \n

答:我是使用的是一個UILabel \n 用于換行

2.拿到篩選過后的數據,是一個NSDictionary。額...說一下,這個篩選過后的數據的一個結構,因為同一天我們可能會記多筆賬,所以把同一天的 dateStr 作為 key ,然后把所有屬于這一天的賬單數據當作一個 value ,目前為止只是過濾掉同一天的時間字符串。

然后下一步我們要做的就是過濾掉同一年的相同月份

/** 過濾掉同年相同月份 */
- (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];
}
/** 把時間字符串轉換成月份 */
- (NSString *)conversionDateStringIntoMonth:(NSString *)dateString {
    NSRange range = NSMakeRange(5, 2);
    NSString *month = [dateString substringWithRange:range];
    return self.items[month.integerValue - 1];
}
/** 判斷字典里面是否已經包含這個對象 */
- (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,通過 CGPathContainsPoint 判斷觸摸的點是否在這個path里面

類別詳細界面(TMPiewCategoryDetailViewController)

解決cell重用導致數據 年月日label 顯示混亂,在模型定義兩個 BOOL 變量 same,partSame
拿到數據之后將數據進行“重置”

(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) {//第一個數據永遠是不相同的
            [self.bills addObject:bill];
            previous = bill.dateStr;
            continue;
        } else {
            TMBill *theBill = [TMBill new];
            if ([previous isEqualToString:bill.dateStr]) {//完全相同,時間日期
                theBill = bill;
                theBill.same = YES;
                [self.bills addObject:theBill];
            } else if ([[previous substringToIndex:7] isEqualToString:[bill.dateStr substringToIndex:7]]) {//部分相同,年月份相同,具體時間不同
                theBill = bill;
                theBill.partSame = YES;
                [self.bills addObject:theBill];
            } else {//不同
                [self.bills addObject:bill];
            }
            previous = bill.dateStr;
        }
    }
}

側滑控制器,使用的是MMDrawerController庫

本來 MMDrawerController 是支持在屏幕向右滑就能出現左邊的菜單欄,由于使用了 TYPagerController 出現了手勢之間的沖突

解決和 TYPagerController 手勢沖突的問題

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];
}

如果直接是這樣的話則會出現下面的情況

2016-06-25 22.50.25.gif

因為 UIScreenEdgePanGestureRecognizer 是一個持續響應事件,也就是說你的手指沒離開屏幕則會一直響應這個函數,因為 toggleDrawerSide 在內部會判斷菜單欄是打開還是關閉,打開則關閉,關閉則會打開,所以也就會出現上面這種情況了。

解決辦法

if (self.mm_drawerController.openSide == MMDrawerSideNone) {
        [self.mm_drawerController toggleDrawerSide:MMDrawerSideLeft animated:YES completion:nil];
    }

賬本控制器(TMSideViewController)

books.gif

如何抖動?在cell上添加一個 UILongPressGestureRecognizer 長按手勢

//* 長按手勢 */
UILongPressGestureRecognizer *longGR = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longGR:)];
longGR.minimumPressDuration = 1.0;
longGR.numberOfTouchesRequired = 1;
longGR.allowableMovement = 10;
[self addGestureRecognizer:longGR];

給cell添加一個代理

@protocol TMSideCellDelegate <NSObject>
@required
@optional
- (void)TMSideCellWithIndexPath:(NSIndexPath *)indexPath withLongPress:(UILongPressGestureRecognizer *)longPress;
@end

當控制器接收到響應事件的時候只需要做三件事

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;
    }
/** 抖動動畫 */
- (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];
    }];
}

 

 

來自:http://www.jianshu.com/p/d3dbf8dba11a

 

 本文由用戶 gvxb2966 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!