好的我认为UICollectionView是这种使用场景的理想选择,我很幸运的开始使用iOS编程,就像它被引入框架一样。以下示例是一个UICollectionView,根据其固有坐标显示其元素。这个例子也可以应用于在地图上定位对象。我在其他地方找不到任何示例,因此我会在此处发布主要步骤(因为我是初学者,请更正任何错误)。
首先,我创建了一个简单的项目,其中包含一个视图和XCode中的故事板。我删除了标准视图,并插入了一个Collection View Controller,并将我的UICollectionViewController子类配置为应该使用的类(在storyboard中的控制器的属性中)。
对于该示例,只需将默认UICollectionViewCell的背景设置为一种颜色,并将标识符设置为“AutomationCell”(如果更改它,请务必调整下面的代码)。
首先,我创建一个表示要在楼层平面图上显示的对象的某些属性的简单对象:
@interface AULYAutomationObject : NSObject
@property NSString *title;
@property CGPoint position;
@end
然后,我需要我自己的委托作为子类的标准UICollectionViewDelegate因为我的自定义UICollectionViewLayout会不能直接访问dataSource对象。因此,我提供给我的物体的位置的方法:
@protocol AULYAutomationObjectLayoutDelegate <UICollectionViewDelegate>
- (CGPoint)getPositionForItemAtIndexPath:(NSIndexPath *)indexPath;
@end
确保实现此协议在你的控制器是这样的:
@interface AULYViewController : UICollectionViewController <AULYAutomationObjectLayoutDelegate>
然后我实现了标准的数据源和委托方法我自定义的视图控制器子一起:
@interface AULYViewController()
@property NSArray *objects;
@property (strong, nonatomic) IBOutlet UICollectionView *collectionView;
@end
@implementation AULYViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Set up the data source
NSMutableArray *automationObjects = [[NSMutableArray alloc] initWithCapacity:10];
// add some objects here...
self.objects = [automationObjects copy];
UILongPressGestureRecognizer *longPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
[self.collectionView addGestureRecognizer:longPressRecognizer];
}
#pragma mark - UICollectionViewController
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.objects.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
AULYAutomationObjectViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"AutomationCell" forIndexPath:indexPath];
// If you have a custom UICollectionViewCell with a label as outlet
// you could for example then do this:
// AULYAutomationObject *automationObject = self.objects[indexPath.row];
// cell.label.text = automationObject.title;
return cell;
}
#pragma mark - AULYAutomationObjectLayoutDelegate
- (CGPoint)getPositionForItemAtIndexPath:(NSIndexPath *)indexPath
{
AULYAutomationObject *automationObject = self.objects[indexPath.item];
return automationObject.position;
}
在实际项目中,你可能会做从对象模型位置的一些转换屏幕上的位置(例如, GPS数据到像素),但在这里为简单起见,这被忽略了。
做完之后,我们仍然需要设置我们的布局。这具有以下属性:
@interface AULYAutomationObjectLayout : UICollectionViewLayout
@property (nonatomic, strong) NSIndexPath *draggedObject;
@property (nonatomic) CGPoint dragPosition;
@end
而下面的实现:
@implementation AULYAutomationObjectLayout
- (void)setDraggedObject:(NSIndexPath *)draggedObject
{
_draggedObject = draggedObject;
[self invalidateLayout];
}
- (void)setDragPosition:(CGPoint)dragPosition
{
_dragPosition = dragPosition;
[self invalidateLayout];
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
id viewDelegate = self.collectionView.delegate;
if ([viewDelegate respondsToSelector:@selector(getPositionForItemAtIndexPath:)])
{
CGPoint itemPosition = [viewDelegate getPositionForItemAtIndexPath:indexPath];
layoutAttributes.center = itemPosition;
layoutAttributes.size = CGSizeMake(ITEM_SIZE, ITEM_SIZE);
}
if ([self.draggedObject isEqual:indexPath])
{
layoutAttributes.center = self.dragPosition;
layoutAttributes.transform3D = CATransform3DMakeScale(1.5, 1.5, 1.0);
layoutAttributes.zIndex = 1;
}
return layoutAttributes;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray *allAttributes = [[NSMutableArray alloc] initWithCapacity:4];
for (NSInteger i = 0; i < [self.collectionView numberOfItemsInSection:0]; i++)
{
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForItemAtIndexPath:indexPath];
[allAttributes addObject:layoutAttributes];
}
return allAttributes;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}
- (CGSize)collectionViewContentSize
{
return [self.collectionView frame].size;
}
@end
要在故事板设置的自定义布局只是去控制视图的属性和定制选择的布局类型 - 然后选择你的自定义类。
我们启用拖曳功能与长按手势支持拖放只需添加以下到你的控制器:
- (void)handleTapGesture:(UITapGestureRecognizer *)sender
{
AULYAutomationObjectLayout *automationLayout = (AULYAutomationObjectLayout *)self.collectionView.collectionViewLayout;
if (sender.state == UIGestureRecognizerStateBegan)
{
CGPoint initialPinchPoint = [sender locationInView:self.collectionView];
NSIndexPath* tappedCellPath = [self.collectionView indexPathForItemAtPoint:initialPinchPoint];
[self.collectionView performBatchUpdates:^{
automationLayout.draggedObject = tappedCellPath;
automationLayout.dragPosition = initialPinchPoint;
} completion:nil];
}
else if (sender.state == UIGestureRecognizerStateChanged)
{
automationLayout.dragPosition = [sender locationInView:self.collectionView];
}
else if (sender.state == UIGestureRecognizerStateEnded)
{
AULYAutomationObject *automationObject = self.objects[automationLayout.draggedObject.item];
automationObject.position = [sender locationInView:self.collectionView];
[self.collectionView performBatchUpdates:^{
automationLayout.draggedObject = nil;
automationLayout.dragPosition = CGPointMake(0.0, 0.0);
} completion:nil];
}
}
一个重要的提示:(这花了我至少1小时):当使用transform3D你应该确保将QuartzCore导入你的链接框架(在方向设置下的项目属性中)。否则,你会得到一个Mach-O链接器错误,说_CATransform3DMakeScale找不到。