5

我正在这样做,我很好奇这是最好的方式,还是愚蠢的方式!

我有一堆 40 像素宽的图像,每一个都像拼字游戏瓷砖。我的应用程序想要显示一些并将它们放在屏幕上。只是不知道会有多少!可能在 3 到 10 之间。

所以我认为最好的办法是如果我数一下有多少,乘以 40,所以我知道整个东西会有多少像素宽,然后我们假设它是 280 像素 - 我将创建一个 280 像素宽的 UIView,粘贴所有瓷砖在那里,然后使用 Autolayout 使 UIView 在设备上居中。

这样,如果用户旋转设备,没问题!

这是最好的方法吗?此外,我还需要让用户将图块拖出 UIView 并拖到屏幕上的另一个位置。这可能吗?

4

3 回答 3

4

我突然想到了三种方法:

  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. 约束的另一个常见解决方案是创建两个额外的UIView对象(有时称为“间隔视图”),您将为它们指定背景颜色[UIColor clearColor],并将它们放在图像视图的左侧和右侧,并将它们定义为超级视图的边距,并将右视图定义为与左视图相同的宽度。虽然我确信你正在构建你的约束,但如果我们要为两个图像视图编写视觉格式语言(VFL)以在屏幕上居中,它可能看起来像:

    @"H:|[leftView][imageView1]-[imageView2][rightView(==leftView)]|"
    
  3. NSLayoutAttributeCenterX或者,您可以通过使用创建约束来消除对容器视图或左右两个间隔视图的需要constraintWithItem,并指定multiplier各种图像视图,以便它们以您想要的方式间隔开。虽然这种技术消除了对这两个间隔视图的需要,但我也认为它不太直观。

    但它可能看起来像:

    [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

如果您有网格布局,最好的解决方案是使用UICollectionView. 这是一个高度可定制的类,可以针对几乎任何网格布局要求进行配置。

我还没有找到UICollectionView比 WWDC 2012 视频更好的介绍可以做什么:

WWDC 2012 会议 205:Olivier Gutknecht 和 Luke Hiesterman 介绍集合视图 WWDC 2012 会议 219:Luke the Hiesterman 的高级集合视图和构建自定义布局

来自 Ray Wenderlich 的一个很好的基于网络的教程在这里: http ://www.raywenderlich.com/22324/beginning-uicollectionview-in-ios-6-part-12

于 2013-08-30T04:18:33.483 回答
0

顺便说一句,我注意到您在问题结束时提出了第二个问题,即如何将图像视图拖出容器。

假设您已按照问题中的建议完成了约束,并且瓷砖位于以主视图为中心的容器视图中(请参阅我的其他答案的选项 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 回答