自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

iOS 7 開(kāi)發(fā)中彈簧式列表的制作

移動(dòng)開(kāi)發(fā) iOS
由于引入了UIKit Dynamics,我們還可以結(jié)合ScrollView做出一些以前不太可能或者需要花費(fèi)很大力氣來(lái)實(shí)現(xiàn)的效果,包括帶有重力的swipe或者是類(lèi)似新的信息app中的帶有彈簧效果聊天泡泡等。

UIScrollView可以說(shuō)是UIKit中最重要的類(lèi)之一了,包括UITableView和UICollectionView等重要的數(shù)據(jù)容器類(lèi)都是UIScrollView的子類(lèi)。在歷年的WWDC上,UIScrollView和相關(guān)的API都有專(zhuān)門(mén)的主題進(jìn)行介紹,也可以看出這個(gè)類(lèi)的使用和變化之快。今年也不例外,因?yàn)閕OS7完全重新定義了UI,這使得UIScrollView里原來(lái)不太會(huì)使用的一些用法和實(shí)現(xiàn)的效果在新的系統(tǒng)中得到了很好的表現(xiàn)。另外,由于引入了UIKit Dynamics,我們還可以結(jié)合ScrollView做出一些以前不太可能或者需要花費(fèi)很大力氣來(lái)實(shí)現(xiàn)的效果,包括帶有重力的swipe或者是類(lèi)似新的信息app中的帶有彈簧效果聊天泡泡等。如果您還不太了解iOS7中信息app的效果,這里有一張gif圖可以幫您大概了解一下:

ios7-message-app-spring.gif

iOS7中信息app的彈簧效果

這次筆記的內(nèi)容主要就是實(shí)現(xiàn)一個(gè)這樣的效果。為了避免重復(fù)造輪子,我對(duì)這個(gè)效果進(jìn)行了一些簡(jiǎn)單的封裝,并連同這篇筆記的demo一起扔在了Github上,有需要的童鞋可以到這里自取。

iOS7的SDK中Apple最大的野心其實(shí)是想用SpriteKit來(lái)結(jié)束iOS平臺(tái)游戲開(kāi)發(fā)(至少是2D游戲開(kāi)發(fā))的亂戰(zhàn),統(tǒng)一游戲開(kāi)發(fā)的方式并建立 良性社區(qū)。而UIKit Dynamics,個(gè)人猜測(cè)Apple在花費(fèi)力氣為SpriteKit開(kāi)發(fā)了物理引擎的同時(shí),發(fā)現(xiàn)在UIKit中也可以使用,并能得到不錯(cuò)的效果,于是順便革新了一下設(shè)計(jì)理念,在UI設(shè)計(jì)中引入了不少物理的概念。在iOS系統(tǒng)中,最為典型的應(yīng)用是鎖屏界面打開(kāi)相機(jī)時(shí)中途放棄后的重力下墜+反彈的效果,另一 個(gè)就是信息應(yīng)用中的加入彈性的消息列表了。彈性列表在我自己上手試過(guò)以后覺(jué)得表現(xiàn)形式確實(shí)很生動(dòng),可以消除原來(lái)列表那種冷冰冰的感覺(jué),是有可能在今后的設(shè) 計(jì)中被大量使用的,因此決定學(xué)上一學(xué)。

首先我們需要知道要如何實(shí)現(xiàn)這樣一種效果,我們會(huì)用到哪些東西。毋庸置疑,如果不使用UIKit Dynamics的話,自己從頭開(kāi)始來(lái)完成會(huì)是一件非常費(fèi)力的事情,你可能需要實(shí)現(xiàn)一套位置計(jì)算和物理模擬來(lái)使效果看起來(lái)真實(shí)滑潤(rùn)。而UIKit Dynamics中已經(jīng)給我們提供了現(xiàn)成的彈簧效果,可以用UIAttachmentBehavior進(jìn)行實(shí)現(xiàn)。另外,在說(shuō)到彈性效果的時(shí)候,我們其實(shí)是 在描述一個(gè)列表中的各個(gè)cell之間的關(guān)系,對(duì)于傳統(tǒng)的UITableView來(lái)說(shuō),描述UITableViewCell之間的關(guān)系是比較復(fù)雜的(因?yàn)?Apple已經(jīng)把絕大多數(shù)工作做了,包括計(jì)算cell位置和位移等。使用越簡(jiǎn)單,定制就會(huì)越麻煩在絕大多數(shù)情況下都是真理)。而 UICollectionView則通過(guò)layout來(lái)完成cell之間位置關(guān)系的描述,給了開(kāi)發(fā)者較大的空間來(lái)實(shí)現(xiàn)布局。另外,UIKit Dynamics為UICollectionView做了很多方便的Catagory,可以很容易地“指導(dǎo)”UICollectionView利用加入物 理特性計(jì)算后的結(jié)果,在實(shí)現(xiàn)彈性效果的時(shí)候,UICollectionView是我們不二的選擇。

如果您在閱讀這篇筆記的時(shí)候遇到困難的話,建議您可以看看我之前的一些筆記,包括今年的UIKit Dynamics的介紹和去年的UICollectionView介紹。

話不多說(shuō),我們開(kāi)工。首先準(zhǔn)備一個(gè)UICollectionViewFlowLayout的子類(lèi)(在這里叫做 VVSpringCollectionViewFlowLayout),然后在ViewController中用這個(gè)layout實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 collectionView:

  1. //ViewController.m 
  2. 02    
  3. 03  @interface ViewController ()<UICollectionViewDataSource, UICollectionViewDelegate> 
  4. 04  @property (nonatomic, strong) VVSpringCollectionViewFlowLayout *layout; 
  5. 05  @end 
  6. 06    
  7. 07  static NSString *reuseId = @"collectionViewCellReuseId"
  8. 08    
  9. 09  @implementation ViewController 
  10. 10  - (void)viewDidLoad 
  11. 11  { 
  12. 12      [super viewDidLoad]; 
  13. 13    // Do any additional setup after loading the view, typically from a nib. 
  14. 14    
  15. 15    self.layout = [[VVSpringCollectionViewFlowLayout alloc] init]; 
  16. 16      self.layout.itemSize = CGSizeMake(self.view.frame.size.width, 44); 
  17. 17      UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:self.layout]; 
  18. 18    
  19. 19      collectionView.backgroundColor = [UIColor clearColor]; 
  20. 20    
  21. 21      [collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:reuseId]; 
  22. 22    
  23. 23      collectionView.dataSource = self; 
  24. 24      [self.view insertSubview:collectionView atIndex:0]; 
  25. 25  } 
  26. 26    
  27. 27  #pragma mark - UICollectionViewDataSource 
  28. 28  - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 
  29. 29  { 
  30. 30      return 50; 
  31. 31  } 
  32. 32    
  33. 33  - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
  34. 34  { 
  35. 35      UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseId forIndexPath:indexPath]; 
  36. 36    
  37. 37      //Just give a random color to the cell. See <a href="\"https://gist.github.com/kylefox/1689973\"" target="\"_blank\"">https://gist.github.com/kylefox/1689973</a> 
  38. 38      cell.contentView.backgroundColor = [UIColor randomColor]; 
  39. 39      return cell; 
  40. 40  } 
  41. 41  @end 

這部分沒(méi)什么可以多說(shuō)的,現(xiàn)在我們有一個(gè)標(biāo)準(zhǔn)的FlowLayout的UICollectionView了。通過(guò)使用 UICollectionViewFlowLayout的子類(lèi)來(lái)作為開(kāi)始的layout,我們可以節(jié)省下所有的初始cell位置計(jì)算的代碼,在上面代碼的情況下,這個(gè)collectionView的表現(xiàn)和一個(gè)普通的tableView并沒(méi)有太大不同。接下來(lái)我們著重來(lái)看看要如何實(shí)現(xiàn)彈性的layout。對(duì)于彈性效果,我們需要的是連接一個(gè)item和一個(gè)錨點(diǎn)間彈性連接的UIAttachmentBehavior,并能在滾動(dòng)時(shí)設(shè)置新的錨點(diǎn)位置。我們?cè)趕croll的時(shí)候,只要使用UIKit Dynamics的計(jì)算結(jié)果,替代掉原來(lái)的位置更新計(jì)算(其實(shí)就是簡(jiǎn)單的scrollView的contentOffset的改變),就可以模擬出彈性的效果了。

首先在-prepareLayout中為cell添加UIAttachmentBehavior。

  1. //VVSpringCollectionViewFlowLayout.m 
  2. 02  @interface VVSpringCollectionViewFlowLayout() 
  3. 03  @property (nonatomic, strong) UIDynamicAnimator *animator; 
  4. 04  @end 
  5. 05    
  6. 06  @implementation VVSpringCollectionViewFlowLayout 
  7. 07  //... 
  8. 08    
  9. 09  -(void)prepareLayout { 
  10. 10      [super prepareLayout]; 
  11. 11    
  12. 12      if (!_animator) { 
  13. 13          _animator = [[UIDynamicAnimator alloc] initWithCollectionViewLayout:self]; 
  14. 14          CGSize contentSize = [self collectionViewContentSize]; 
  15. 15          NSArray *items = [super layoutAttributesForElementsInRect:CGRectMake(0, 0, contentSize.width, contentSize.height)]; 
  16. 16    
  17. 17          for (UICollectionViewLayoutAttributes *item in items) { 
  18. 18              UIAttachmentBehavior *spring = [[UIAttachmentBehavior alloc] initWithItem:item attachedToAnchor:item.center]; 
  19. 19    
  20. 20              spring.length = 0; 
  21. 21              spring.damping = 0.5; 
  22. 22              spring.frequency = 0.8; 
  23. 23    
  24. 24              [_animator addBehavior:spring]; 
  25. 25          } 
  26. 26      } 
  27. 27  } 
  28. 28  @end 

prepareLayout將在CollectionView進(jìn)行排版的時(shí)候被調(diào)用。首先當(dāng)然是call一下super的prepareLayout,你 肯定不會(huì)想要全都要自己進(jìn)行設(shè)置的。接下來(lái),如果是第一次調(diào)用這個(gè)方法的話,先初始化一個(gè)UIDynamicAnimator實(shí)例,來(lái)負(fù)責(zé)之后的動(dòng)畫(huà)效 果。iOS7 SDK中,UIDynamicAnimator類(lèi)專(zhuān)門(mén)有一個(gè)針對(duì)UICollectionView的Category,以使 UICollectionView能夠輕易地利用UIKit Dynamics的結(jié)果。在UIDynamicAnimator.h中能夠找到這個(gè)Category:

  1. @interface UIDynamicAnimator (UICollectionViewAdditions) 
  2. 02    
  3. 03  // When you initialize a dynamic animator with this method, you should only associate collection view layout attributes with your behaviors. 
  4. 04  // The animator will employ thecollection view layout’s content size coordinate system. 
  5. 05  - (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout*)layout; 
  6. 06    
  7. 07  // The three convenience methods returning layout attributes (if associated to behaviors in the animator) if the animator was configured with collection view layout 
  8. 08  - (UICollectionViewLayoutAttributes*)layoutAttributesForCellAtIndexPath:(NSIndexPath*)indexPath; 
  9. 09  - (UICollectionViewLayoutAttributes*)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; 
  10. 10  - (UICollectionViewLayoutAttributes*)layoutAttributesForDecorationViewOfKind:(NSString*)decorationViewKind atIndexPath:(NSIndexPath *)indexPath; 
  11. 11    
  12. 12  @end 

于是通過(guò)-initWithCollectionViewLayout:進(jìn)行初始化后,這個(gè)UIDynamicAnimator實(shí)例便和我們的 layout進(jìn)行了綁定,之后這個(gè)layout對(duì)應(yīng)的attributes都應(yīng)該由綁定的UIDynamicAnimator的實(shí)例給出。就像下面這樣:

  1. //VVSpringCollectionViewFlowLayout.m 
  2. 02  @implementation VVSpringCollectionViewFlowLayout 
  3. 03    
  4. 04  //... 
  5. 05    
  6. 06  -(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { 
  7. 07      return [_animator itemsInRect:rect]; 
  8. 08  } 
  9. 09    
  10. 10  -(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath { 
  11. 11      return [_animator layoutAttributesForCellAtIndexPath:indexPath]; 
  12. 12  } 
  13. 13  @end

向上拖動(dòng)時(shí)的錨點(diǎn)變化示意

現(xiàn)在我們來(lái)實(shí)現(xiàn)這個(gè)錨點(diǎn)的變化。既然都是滑動(dòng),我們是不是可以考慮在UIScrollView的–scrollViewDidScroll:委托方法中來(lái) 設(shè)定新的Behavior錨點(diǎn)值呢?理論上來(lái)說(shuō)當(dāng)然是可以的,但是如果這樣的話我們大概就不得不面臨著將剛才的layout實(shí)例設(shè)置為 collectionView的delegate這樣一個(gè)事實(shí)。但是我們都知道layout應(yīng)該做的事情是給collectionView提供必要的布局 信息,而不應(yīng)該負(fù)責(zé)去處理它的委托事件。處理collectionView的回調(diào)更恰當(dāng)?shù)貞?yīng)該由處于collectionView的controller 層級(jí)的類(lèi)來(lái)完成,而不應(yīng)該由一個(gè)給collectionView提供數(shù)據(jù)和信息的類(lèi)來(lái)響應(yīng)。在UICollectionViewLayout中,我們有一個(gè)叫做-shouldInvalidateLayoutForBoundsChange:的方法,每次layout的bounds發(fā)生變化的時(shí) 候,collectionView都會(huì)詢問(wèn)這個(gè)方法是否需要為這個(gè)新的邊界和更新layout。一般情況下只要layout沒(méi)有根據(jù)邊界不同而發(fā)生變化的 話,這個(gè)方法直接不做處理地返回NO,表示保持現(xiàn)在的layout即可,而每次bounds改變時(shí)這個(gè)方法都會(huì)被調(diào)用的特點(diǎn)正好可以滿足我們更新錨點(diǎn)的需 求,因此我們可以在這里面完成錨點(diǎn)的更新。

  1. //VVSpringCollectionViewFlowLayout.m 
  2. @implementation VVSpringCollectionViewFlowLayout 
  3.  
  4. //... 
  5.  
  6. -(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds { 
  7.     UIScrollView *scrollView = self.collectionView; 
  8.     CGFloat scrollDelta = newBounds.origin.y - scrollView.bounds.origin.y; 
  9.  
  10.     //Get the touch point 
  11.     CGPoint touchLocation = [scrollView.panGestureRecognizer locationInView:scrollView]; 
  12.  
  13.     for (UIAttachmentBehavior *spring in _animator.behaviors) { 
  14.         CGPoint anchorPoint = spring.anchorPoint; 
  15.  
  16.         CGFloat distanceFromTouch = fabsf(touchLocation.y - anchorPoint.y); 
  17.         CGFloat scrollResistance = distanceFromTouch / 500; 
  18.  
  19.         UICollectionViewLayoutAttributes *item = [spring.items firstObject]; 
  20.         CGPoint center = item.center; 
  21.  
  22.       //In case the added value bigger than the scrollDelta, which leads an unreasonable effect 
  23.         center.y += (scrollDelta > 0) ? MIN(scrollDelta, scrollDelta * scrollResistance) 
  24.                                       : MAX(scrollDelta, scrollDelta * scrollResistance); 
  25.         item.center = center; 
  26.  
  27.         [_animator updateItemUsingCurrentState:item]; 
  28.     } 
  29.     return NO; 
  30.  
  31. @end 

首先我們計(jì)算了這次scroll的距離scrollDelta,為了得到每個(gè)item與觸摸點(diǎn)的之間的距離,我們當(dāng)然還需要知道觸摸點(diǎn)的坐標(biāo) touchLocation。接下來(lái),可以根據(jù)距離對(duì)每個(gè)錨點(diǎn)進(jìn)行設(shè)置了:簡(jiǎn)單地計(jì)算了原來(lái)錨點(diǎn)與觸摸點(diǎn)之間的距離distanceFromTouch, 并由此計(jì)算一個(gè)系數(shù)。接下來(lái),對(duì)于當(dāng)前的item,我們獲取其當(dāng)前錨點(diǎn)位置,然后將其根據(jù)scrollDelta的數(shù)值和剛才計(jì)算的系數(shù),重新設(shè)定錨點(diǎn)的 位置。最后我們需要告訴UIDynamicAnimator我們已經(jīng)完成了對(duì)冒點(diǎn)的更新,現(xiàn)在可以開(kāi)始更新物理計(jì)算,并隨時(shí)準(zhǔn)備 collectionView來(lái)取LayoutAttributes的數(shù)據(jù)了。

也許你還沒(méi)有緩過(guò)神來(lái)?但是我們確實(shí)已經(jīng)做完了,讓我們來(lái)看看實(shí)際的效果吧:

spring-collection-view-over-ios7.gif

帶有彈性效果的collecitonView

當(dāng)然,通過(guò)調(diào)節(jié)damping,frequency和scrollResistance的系數(shù)等參數(shù),可以得到彈性不同的效果,比如更多的震蕩或者更大的幅度等等。

源碼下載地址:http://down.51cto.com/data/1120468

原文鏈接:http://www.devdiv.com/iOS_iPhone-ios_-thread-208170-1-1.html

【移動(dòng)開(kāi)發(fā)視頻課程推薦】

 

責(zé)任編輯:閆佳明 來(lái)源: devdiv
相關(guān)推薦

2013-07-29 04:51:41

iOS開(kāi)發(fā)iOS開(kāi)發(fā)學(xué)習(xí)file列表查看

2013-08-26 14:36:25

開(kāi)發(fā)框架響應(yīng)式

2013-06-12 15:19:05

iOS7WWDC

2010-12-01 11:03:20

職場(chǎng)

2013-08-29 09:53:48

開(kāi)發(fā)者iOS 7

2013-04-12 15:53:39

2013-06-18 09:32:20

iOS開(kāi)發(fā)iOS 7iOS開(kāi)發(fā)者

2013-03-22 13:54:09

追信魔盒傻瓜式App制作

2023-06-02 22:36:02

鴻蒙彈簧動(dòng)畫(huà)曲線

2013-03-22 13:37:55

安米網(wǎng)傻瓜式App制作

2013-04-15 16:54:09

AMDiOS開(kāi)發(fā)WIN7

2013-03-22 13:42:39

簡(jiǎn)網(wǎng)App工廠傻瓜式

2014-09-02 09:45:57

Linux

2022-04-08 14:47:11

ArkUI列表字母索引鴻蒙

2021-01-27 13:26:21

鴻蒙HarmonyOS應(yīng)用開(kāi)發(fā)

2022-09-26 15:16:03

ArkUITS

2013-09-23 10:26:29

iOS 7優(yōu)點(diǎn)

2012-03-09 13:52:28

Adob??e AIRiOS

2011-07-06 10:12:26

Objective-CCSSJavaScript

2013-04-08 10:27:59

iOSXcode制作靜態(tài)庫(kù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)