5

私はこれを行っていますが、それが最善の方法なのか、それともばかげた方法なのか興味があります!

40 ピクセル幅の画像がたくさんあります。それぞれがスクラブル タイルのようなものです。私のアプリは、いくつかを表示して画面の中央に配置したいと考えています。いくつになるかはわかりません!3 から 10 の間である可能性があります。

したがって、40 の倍数を数えるのが最善だと思います。全体の幅が何ピクセルになるかがわかります。次に、280 ピクセルであると仮定します。280 ピクセル幅の UIView を作成し、すべてのタイルを貼り付けます。そこで、Autolayout を使用してその UIView をデバイスの中央に配置します。

そうすれば、ユーザーがデバイスを回転させても問題ありません!

これが最善の方法ですか?また、ユーザーがタイルをその UIView から画面上の別の場所にドラッグできるようにする必要があります。それは可能でしょうか?

4

3 に答える 3

4

次の 3 つのアプローチが思い浮かびます。

  1. コンテナビューを使用するソリューションは完全に問題ないと思います。ただし、画像のサイズを決定するのをいじる必要はありません。コンテナと画像ビューの間の関係を定義するだけで、画像ビューの固有のサイズに合わせてコンテナのサイズが変更されます (または、画像ビューのサイズを明示的に定義する場合は、それも問題ありません)。次に、コンテナーを中央に配置できます (明示的な幅/高さの制約を与えないでください)。

    // create container
    
    UIView *containerView = [[UIView alloc] init];
    containerView.backgroundColor = [UIColor clearColor];
    containerView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:containerView];
    
    // create image views
    
    UIImageView *imageView1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"1.png"]];
    imageView1.translatesAutoresizingMaskIntoConstraints = NO;
    [containerView addSubview:imageView1];
    
    UIImageView *imageView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"2.png"]];
    imageView2.translatesAutoresizingMaskIntoConstraints = NO;
    [containerView addSubview:imageView2];
    
    NSDictionary *views = NSDictionaryOfVariableBindings(containerView, imageView1, imageView2);
    
    // define the container in relation to the two image views 
    
    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[imageView1]-[imageView2]|" options:0 metrics:nil views:views]];
    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[imageView1]-|" options:0 metrics:nil views:views]];
    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[imageView2]-|" options:0 metrics:nil views:views]];
    
    // center the container
    
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:containerView
                                                          attribute:NSLayoutAttributeCenterX
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:containerView.superview
                                                          attribute:NSLayoutAttributeCenterX
                                                         multiplier:1.0
                                                           constant:0]];
    
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:containerView
                                                          attribute:NSLayoutAttributeCenterY
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:containerView.superview
                                                          attribute:NSLayoutAttributeCenterY
                                                         multiplier:1.0
                                                           constant:0]];
    
  2. 制約を使用した別の一般的な解決策は、2 つの追加UIViewオブジェクト (「スペーサー ビュー」と呼ばれることもあります) を作成することです。このオブジェクトには の背景色を指定し、[UIColor clearColor]それらをイメージ ビューの左右に配置し、移動するように定義します。スーパービューの余白を調整し、右側のビューを左側のビューと同じ幅になるように定義します。作業を進めながら制約を作成していると思いますが、2 つのイメージビューを画面の中央に配置するためのビジュアル フォーマット言語 (VFL) を作成するとしたら、次のようになります。

    @"H:|[leftView][imageView1]-[imageView2][rightView(==leftView)]|"
    
  3. NSLayoutAttributeCenterXまたは、 を使用して制約を作成し、さまざまなイメージ ビューconstraintWithItemを指定して、必要な間隔で配置することにより、コンテナー ビューまたは左右の 2 つのスペーサー ビューの必要性をなくすことができます。multiplierこの手法により、これら 2 つのスペーサー ビューが不要になりますが、少し直感的ではないと思います。

    しかし、それは次のように見えるかもしれません:

    [imageViewArray enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) {
        NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view
                                                                      attribute:NSLayoutAttributeCenterX
                                                                      relatedBy:NSLayoutRelationEqual
                                                                         toItem:view.superview
                                                                      attribute:NSLayoutAttributeCenterX
                                                                     multiplier:2.0 * (idx + 1) / ([imageViewArray count] + 1)
                                                                       constant:0];
        [view.superview addConstraint:constraint];
    }];
    

    これは確かに画像ビューのわずかに異なる間隔を採用していますが、シナリオによっては問題ありません。

個人的には、最初のアプローチに傾倒しますが、これらのいずれも機能します。

于 2013-08-30T04:28:19.137 に答える
0

ところで、質問の最後に 2 番目の質問、つまりコンテナーから画像ビューをドラッグする方法を尋ねたことに気付きました。

質問で提案したように、メイン ビューの中心にあるコンテナー ビューにタイルを配置して、制約を実行したと仮定しましょう (他の回答のオプション 1 を参照)。おそらく、ドラッグを開始すると、コンテナのリストからタイルを削除し、tilesそれに応じて制約の更新をアニメーション化するジェスチャ認識ハンドラを作成します。

- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
    static CGPoint originalCenter;

    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        // move the gesture.view out of its container, and up to the self.view, so that as the container
        // resizes, this view we're dragging doesn't move in the process, too

        originalCenter = [self.view convertPoint:gesture.view.center fromView:gesture.view.superview];
        [self.view addSubview:gesture.view];
        gesture.view.center = originalCenter;

        // now update the constraints for the views still left in the container

        [self removeContainerTileConstraints];
        [self.tiles removeObject:gesture.view];
        [self createContainerTileConstraints];
        [UIView animateWithDuration:0.5 animations:^{
            [self.containerView layoutIfNeeded];
        }];
    }

    CGPoint translate = [gesture translationInView:gesture.view];
    gesture.view.center = CGPointMake(originalCenter.x + translate.x, originalCenter.y + translate.y);

    if (gesture.state == UIGestureRecognizerStateEnded)
    {
        // do whatever you want when you drop your tile, presumably changing
        // the superview of the tile to be whatever view you dropped it on
        // and then adding whatever constraints you need to make sure it's
        // placed in the right location.
    }
}

これにより、タイル (および目に見えないコンテナー ビュー) が適切にアニメーション化され、コンテナーからタイルをドラッグしたことが反映されます。

コンテキストとして、上記のジェスチャ レコグナイザー ハンドラーで使用するコンテナーとタイルをどのように作成したかを示します。コンテナ内にスクラブル スタイルのタイルの があったとしNSMutableArrayます。tiles次に、コンテナーとタイルを作成し、次のように各タイルにジェスチャ レコグナイザーをアタッチします。

// create the container

UIView *containerView = [[UIView alloc] init];
containerView.backgroundColor = [UIColor lightGrayColor];
containerView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:containerView];
self.containerView = containerView;  // save this for future reference

// center the container (change this to place it whereever you want it)

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:containerView
                                                      attribute:NSLayoutAttributeCenterX
                                                      relatedBy:NSLayoutRelationEqual
                                                         toItem:containerView.superview
                                                      attribute:NSLayoutAttributeCenterX
                                                     multiplier:1.0
                                                       constant:0]];

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:containerView
                                                      attribute:NSLayoutAttributeCenterY
                                                      relatedBy:NSLayoutRelationEqual
                                                         toItem:containerView.superview
                                                      attribute:NSLayoutAttributeCenterY
                                                     multiplier:1.0
                                                       constant:0]];

// create the tiles (in my case, three random images), populating an array of `tiles` that
// will specify which tiles the container will have constraints added

self.tiles = [NSMutableArray array];

NSArray *imageNames = @[@"1.png", @"2.png", @"3.png"];
for (NSString *imageName in imageNames)
{
    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imageName]];
    imageView.translatesAutoresizingMaskIntoConstraints = NO;
    [containerView addSubview:imageView];

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
    [imageView addGestureRecognizer:pan];
    imageView.userInteractionEnabled = YES;

    [self.tiles addObject:imageView];
}

// add the tile constraints

[self createContainerTileConstraints];

そして、明らかにこれらのユーティリティメソッドが必要になります:

- (void)removeContainerTileConstraints
{
    NSMutableArray *constraintsToRemove = [NSMutableArray array];

    // build an array of constraints associated with the tiles

    for (NSLayoutConstraint *constraint in self.containerView.constraints)
    {
        if ([self.tiles indexOfObject:constraint.firstItem]  != NSNotFound ||
            [self.tiles indexOfObject:constraint.secondItem] != NSNotFound)
        {
            [constraintsToRemove addObject:constraint];
        }
    }

    // now remove them

    [self.containerView removeConstraints:constraintsToRemove];
}

- (void)createContainerTileConstraints
{
    [self.tiles enumerateObjectsUsingBlock:^(UIView *tile, NSUInteger idx, BOOL *stop) {
        // set leading constraint

        if (idx == 0)
        {
            // if first tile, set the leading constraint to its superview

            [tile.superview addConstraint:[NSLayoutConstraint constraintWithItem:tile
                                                                       attribute:NSLayoutAttributeLeading
                                                                       relatedBy:NSLayoutRelationEqual
                                                                          toItem:tile.superview
                                                                       attribute:NSLayoutAttributeLeading
                                                                      multiplier:1.0
                                                                        constant:0.0]];
        }
        else
        {
            // if not first tile, set the leading constraint to the prior tile

            [tile.superview addConstraint:[NSLayoutConstraint constraintWithItem:tile
                                                                       attribute:NSLayoutAttributeLeading
                                                                       relatedBy:NSLayoutRelationEqual
                                                                          toItem:self.tiles[idx - 1]
                                                                       attribute:NSLayoutAttributeTrailing
                                                                      multiplier:1.0
                                                                        constant:10.0]];
        }

        // set vertical constraints

        NSDictionary *views = NSDictionaryOfVariableBindings(tile);

        [tile.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[tile]|" options:0 metrics:nil views:views]];
    }];

    // set the last tile's trailing constraint to its superview

    UIView *tile = [self.tiles lastObject];
    [tile.superview addConstraint:[NSLayoutConstraint constraintWithItem:tile
                                                               attribute:NSLayoutAttributeTrailing
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:tile.superview
                                                               attribute:NSLayoutAttributeTrailing
                                                              multiplier:1.0
                                                                constant:0.0]];

}
于 2013-08-30T17:04:06.753 に答える