自适应高度可移动瀑布流UIColle的封装
nzhf8334
8年前
<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>