自适应高度可移动瀑布流UIColle的封装

nzhf8334 7年前
   <h2>导语</h2>    <p>我们知道iOS9之后苹果直接封装好了UICollectionView的item移动效果实现。但是这个方法只能移动比较规则的布局,如果做个自适应例子就知道,他会在移动时自动布局到系统默认计算的效果去移动(原谅我的表达。。)。 但是实际需要中移动的往往是不一样大小的Item。那么就需要自己去封装一个移动方法。 本例为一个自适应的可移动瀑布流效果。</p>    <h2>效果如下</h2>    <p style="text-align:center"><img src="https://simg.open-open.com/show/53536152d814aa70c8343a5719a8b6d3.gif"></p>    <h2>分析</h2>    <p>1 首先是自适应,那么我们可以做一个数据model。存储的是image的url,尺寸和比例等。用于布局时的计算。</p>    <p>2 在瀑布流布局中,需要的是计算记录每一列当前最底部的Y值。然后将item插入最小那一列下面,即最短的那一列下面。 由于contSize必须在布局完成后才能获得,所以我们必须在prepareLayout中完成布局计算。然后在获取contentSize。contentSize的height即可由最长的那一列获得。</p>    <p>3 移动的实现其实很没意思必须依赖苹果本身的方法,不然我想要自己实现那样的动画效果会非常麻烦。 那么我们就自己在本身的moveItem方法上进行封装。来实现Item的移动。</p>    <h2>使用方式</h2>    <p>导入自定义FlowLayout即可,可选择实现其代理方法,有两个FlowLayout,一个是自定义布局的,另一个是做移动操作的。如果想对其他布局进行移动可自行修改布局的FlowLayout:</p>    <pre>  <code class="language-objectivec">- (MDMoveAbleWaterFallMoveFlowLayout *)flowLayout{      if (!_flowLayout) {          _flowLayout = [[MDMoveAbleWaterFallMoveFlowLayout alloc]init];          _flowLayout.maxHeight = 200.f;    //item最大高度          _flowLayout.minHeight = 20.f;       //item最小高度          _flowLayout.columnNum = 3;        //列数          _flowLayout.minimumLineSpacing = 10.0f;          _flowLayout.minimumInteritemSpacing = 10.0f;          _flowLayout.sectionInset = UIEdgeInsetsMake(5, 0, 5, 0);      }      return _flowLayout;  }</code></pre>    <h2>主要讲一下移动</h2>    <p>为了向iOS9的方法靠拢,我们按照类似的情形封装如下代理方法:</p>    <pre>  <code class="language-objectivec">@protocol MDMoveAbleWaterFallMoveFlowLayoutDelegate<NSObject>  @optional  //能否移动,可针对某Item设置  - (BOOL)MDCollectionView:(UICollectionView <em>)collectionView canMoveItemAtIndexPath:( NSIndexPath </em>)indexPath;  //能否移动,可针对要移动的Item和目标Item设置  - (BOOL)MDCollectionView:(UICollectionView <em>)collectionView canMoveItemFromIndexPath:(NSIndexPath </em>)fromIndexPath toDestinationIndexPath:(NSIndexPath <em>)destinationIndexPath; //开始移动时,可自行做一些操作,按实际需要 - (void)MDCollectionView:(UICollectionView </em>)collectionView willBeginMoveItemAtIndexPath:(NSIndexPath <em>)indexPath; //当Item移动完成时,可自行做一些操作,比如数据的调整 - (void)MDCollectionView:(UICollectionView </em>)collectionView didMoveItemFromIndexPath:(NSIndexPath <em>)sourceIndexPath toDestinationItemAtIndexPath:(NSIndexPath </em>)destinationIndexPath;  @end</code></pre>    <h3>具体移动操作如下:</h3>    <ol>     <li> <p>添加长按手势选取要移动的Item。 因为大小不一,那么尽量由选中的Item生成一个小图片来方便移动。</p> <p>2 如果允许移动,那么获取当前所在的IndexPath,然后将选中的Item移动到这里。</p> </li>    </ol>    <p>长按开始选取Item:</p>    <pre>  <code class="language-objectivec">- (void)handleMove:(UIPanGestureRecognizer  *)sender{      CGPoint location = [sender locationInView:self.collectionView];      switch (sender.state) {          case UIGestureRecognizerStateBegan:{              if (![self.collectionView indexPathForItemAtPoint:location]) {                  return;              }              _selectIndexPath = [self.collectionView indexPathForItemAtPoint:location];              _selectCell = [self.collectionView cellForItemAtIndexPath:_selectIndexPath];              if (_selectIndexPath) {                  _fakeImage = [[UIImageView alloc]initWithFrame:_selectCell.frame];                  [_fakeImage getFakeImageFromCell:_selectCell];                  [self.collectionView addSubview:_fakeImage];                  _fakeImage.layer.cornerRadius = 10.f;                  _fakeImage.layer.borderColor = [UIColor redColor].CGColor;                  _fakeImage.layer.borderWidth = 3.f;                  _fakeImage.layer.masksToBounds = YES;                  CGPoint center = _selectCell.center;                  CGSize size = _selectCell.frame.size;                  [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{                      [_fakeImage setFrame:CGRectMake(center.x - size.width/4, center.y - size.height/4, size.width/2, size.height/2)];                  } completion:^(BOOL finished) {                  }];                  _lastCenter = _selectCell.center;              }          }              break;</code></pre>    <p>当手指移动时开始判断是否要移动:</p>    <pre>  <code class="language-objectivec">case UIGestureRecognizerStateChanged:{              [_fakeImage setCenter:location];              //开始移动              if ( ![self.collectionView indexPathForItemAtPoint:location]) {                  return;              }              _destinationIndexPath = [self.collectionView indexPathForItemAtPoint:location];              _destinationCell = [self.collectionView cellForItemAtIndexPath:_destinationIndexPath];              if ([self.delegate respondsToSelector:@selector(MDCollectionView:canMoveItemAtIndexPath:)]) {                  if ([self.delegate respondsToSelector:@selector(MDCollectionView:canMoveItemFromIndexPath:toDestinationIndexPath:)]) {                      if (![self.delegate MDCollectionView:self.collectionView canMoveItemFromIndexPath:_selectIndexPath toDestinationIndexPath:_destinationIndexPath]) {                          _destinationIndexPath = _selectIndexPath;                          _destinationCell = _selectCell;                          return;                      }                  }                  if ([self.delegate MDCollectionView:self.collectionView canMoveItemAtIndexPath:_selectIndexPath]) {                      [self MoveItemFromIndexPath:_selectIndexPath toIndexPath:_destinationIndexPath];                  }              }              if((CGRectGetMinY(_fakeImage.frame) - self.collectionView.contentOffset.y) < 0.f ){                  [self ScrollWithDirection:MDScrollDirectionUp];              }else if((self.collectionView.bounds.size.height + self.collectionView.contentOffset.y - CGRectGetMaxY(_fakeImage.frame)) < 0.f) {                  [self ScrollWithDirection:MDScrollDirectionDown];              }              _lastCenter = _fakeImage.center;          }              break;</code></pre>    <p>移动操作:</p>    <pre>  <code class="language-objectivec">- (void)MoveItemFromIndexPath:(NSIndexPath <em>)fromIndexPath toIndexPath:(NSIndexPath </em>)toIndexpath{      NSIndexPath *sourceIndexPath = _selectIndexPath;      if (fromIndexPath && toIndexpath && !([_selectIndexPath isEqual:toIndexpath])) {          if (!_isBeginMove) {              if ([self.delegate respondsToSelector:@selector(MDCollectionView:willBeginMoveItemAtIndexPath:)]) {                  [self.delegate MDCollectionView:self.collectionView willBeginMoveItemAtIndexPath:_selectIndexPath];              }          }          [self.collectionView performBatchUpdates:^{              _selectIndexPath = toIndexpath;              [self.collectionView moveItemAtIndexPath:sourceIndexPath toIndexPath:toIndexpath];          } completion:^(BOOL finished) {              if ([self.delegate respondsToSelector:@selector(MDCollectionView:didMoveItemFromIndexPath:toDestinationItemAtIndexPath:)]) {                  [self.delegate MDCollectionView:self.collectionView didMoveItemFromIndexPath:_selectIndexPath toDestinationItemAtIndexPath:_destinationIndexPath];              }          }];      }  }</code></pre>    <p>手指松开完成移动:</p>    <pre>  <code class="language-objectivec">case UIGestureRecognizerStateEnded:{              [UIView animateWithDuration:.3f delay:0 options:UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationCurveEaseInOut animations:^{              } completion:^(BOOL finished) {                  _selectCell.hidden = NO;                  CGRect destinaFrme;                  if (_destinationCell) {                      destinaFrme = _destinationCell.frame;                  }else{                      destinaFrme = _selectCell.frame;                  }                  [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{                      [_fakeImage setFrame:destinaFrme];                  } completion:^(BOOL finished) {                      [_fakeImage removeFromSuperview];                  }];              }];          }              break;</code></pre>    <p> </p>    <p> </p>    <p>来自:http://www.jianshu.com/p/8f290972f9f2</p>    <p> </p>