わかりました UICollectionView はこの使用シナリオに最適だと思います。フレームワークに導入されたのと同じように iOS プログラミングを開始できたことは幸運です。次の例は、固有の座標に従って要素を表示する UICollectionView です。この例は、マップ上のオブジェクトの配置にも適用できます。他の場所で例を見つけることができなかったので、ここに主な手順を投稿します (私は初心者なので、間違いを修正してください)。
まず、XCode で 1 つのビューとストーリーボードを含む単純なプロジェクトを作成しました。標準ビューを削除し、代わりに Collection View Controller を挿入し、UICollectionViewController サブクラスを (ストーリーボードのコントローラーのプロパティで) 使用するクラスとして構成しました。
デモでは、デフォルトの UICollectionViewCell の背景を色に設定し、この例の識別子を "AutomationCell" に設定します (変更する場合は、必ず以下のコードを調整してください)。
最初に、フロア プランに表示するオブジェクトを表すいくつかのプロパティを持つ単純なオブジェクトを作成します。
@interface AULYAutomationObject : NSObject
@property NSString *title;
@property CGPoint position;
@end
次に、カスタム UICollectionViewLayout は dataSource オブジェクトに直接アクセスできないため、標準 UICollectionViewDelegate のサブクラスとして独自のデリゲートが必要です。したがって、オブジェクトの位置を取得するメソッドを提供します。
@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 つの重要な注意事項: (これには少なくとも 1 時間かかりました): transform3D を使用する場合は、QuartzCore をリンクされたフレームワークにインポートする必要があります (方向設定の下のプロジェクト プロパティで)。そうしないと、_CATransform3DMakeScale が見つからないという Mach-O リンカー エラーが発生します。