自適應高度可移動瀑布流UIColle的封裝
導語
我們知道iOS9之后蘋果直接封裝好了UICollectionView的item移動效果實現。但是這個方法只能移動比較規則的布局,如果做個自適應例子就知道,他會在移動時自動布局到系統默認計算的效果去移動(原諒我的表達。。)。 但是實際需要中移動的往往是不一樣大小的Item。那么就需要自己去封裝一個移動方法。 本例為一個自適應的可移動瀑布流效果。
效果如下
分析
1 首先是自適應,那么我們可以做一個數據model。存儲的是image的url,尺寸和比例等。用于布局時的計算。
2 在瀑布流布局中,需要的是計算記錄每一列當前最底部的Y值。然后將item插入最小那一列下面,即最短的那一列下面。 由于contSize必須在布局完成后才能獲得,所以我們必須在prepareLayout中完成布局計算。然后在獲取contentSize。contentSize的height即可由最長的那一列獲得。
3 移動的實現其實很沒意思必須依賴蘋果本身的方法,不然我想要自己實現那樣的動畫效果會非常麻煩。 那么我們就自己在本身的moveItem方法上進行封裝。來實現Item的移動。
使用方式
導入自定義FlowLayout即可,可選擇實現其代理方法,有兩個FlowLayout,一個是自定義布局的,另一個是做移動操作的。如果想對其他布局進行移動可自行修改布局的FlowLayout:
- (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;
}
主要講一下移動
為了向iOS9的方法靠攏,我們按照類似的情形封裝如下代理方法:
@protocol MDMoveAbleWaterFallMoveFlowLayoutDelegate<NSObject>
@optional
//能否移動,可針對某Item設置
- (BOOL)MDCollectionView:(UICollectionView )collectionView canMoveItemAtIndexPath:( NSIndexPath )indexPath;
//能否移動,可針對要移動的Item和目標Item設置
- (BOOL)MDCollectionView:(UICollectionView )collectionView canMoveItemFromIndexPath:(NSIndexPath )fromIndexPath toDestinationIndexPath:(NSIndexPath )destinationIndexPath; //開始移動時,可自行做一些操作,按實際需要 - (void)MDCollectionView:(UICollectionView )collectionView willBeginMoveItemAtIndexPath:(NSIndexPath )indexPath; //當Item移動完成時,可自行做一些操作,比如數據的調整 - (void)MDCollectionView:(UICollectionView )collectionView didMoveItemFromIndexPath:(NSIndexPath )sourceIndexPath toDestinationItemAtIndexPath:(NSIndexPath )destinationIndexPath;
@end</code></pre>
具體移動操作如下:
-
添加長按手勢選取要移動的Item。 因為大小不一,那么盡量由選中的Item生成一個小圖片來方便移動。
2 如果允許移動,那么獲取當前所在的IndexPath,然后將選中的Item移動到這里。
長按開始選取Item:
- (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;
當手指移動時開始判斷是否要移動:
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;
移動操作:
- (void)MoveItemFromIndexPath:(NSIndexPath )fromIndexPath toIndexPath:(NSIndexPath )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];
}
}];
}
}
手指松開完成移動:
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;
來自:http://www.jianshu.com/p/8f290972f9f2