0

I've just started working on this card game in Swift, and I was trying to figure out how to lay out a row of 6 cards horizontally near the top of the screen.

I've tried putting 6 imageViews in a stack, but my manual constraints ended up causing the last image to stretch to the edge:

Could someone show me how to set up a row of imageViews so that each of them has a fixed width and they're all centered? I'm kinda new to AutoLayout, so screenshots would be helpful.

TylerP
  • 9,600
  • 4
  • 39
  • 43
SudsP
  • 3
  • 2
  • Try making an equal width constraint between each image and the one next to it, it looks like the rest of your constraints are probably right. – Patrick Goley Jun 10 '16 at 00:43
  • Add left constraint to first one, trailing constraint to the last one, horizontal spacing between all of them, and then add width constraints so they're all the same width as the first one. Also, if you want, a `UIStackView` is another way to nicely space a bunch of views. – Rob Jun 10 '16 at 00:44

2 Answers2

1

I'd recommend using UIStackView. Have a look at the following Ray Wenderlich tutorial:

https://www.raywenderlich.com/114552/uistackview-tutorial-introducing-stack-views

However, before moving on to more complex views such as the aforementioned stack view; you should learn to use auto layout to avoid making any silly mistakes.

Here is another great tutorial from the same site:

https://www.raywenderlich.com/115440/auto-layout-tutorial-in-ios-9-part-1-getting-started-2

EDIT:

Improved answer:

UIStackView allows you to arrange elements with ease, in a row or in a column. This saves you a lot of time and makes your storyboard look a little bit cleaner as less constraints are needed.

The description of UIStackView on developer.apple.com:

The UIStackView class provides a streamlined interface for laying out a collection of views in either a column or a row. Stack views let you leverage the power of Auto Layout, creating user interfaces that can dynamically adapt to the device’s orientation, screen size, and any changes in the available space. The stack view manages the layout of all the views in its arrangedSubviews property. These views are arranged along the stack view’s axis, based on their order in the arrangedSubviews array. The exact layout varies depending on the stack view’s axis, distribution, alignment, spacing, and other properties.

UIStackViews functionality doesn't stop at the simplified view alignement. Indeed, you can also alter the properties that define the stack view.

The axis property determines the stack’s orientation, either vertically or horizontally.

The distribution property determines the layout of the arranged views along the stack’s axis.

The alignment property determines the layout of the arranged views perpendicular to the stack’s axis.

The spacing property determines the minimum spacing between arranged views.

The baselineRelativeArrangement property determines whether the vertical spacing between views is measured from the baselines.

The layoutMarginsRelativeArrangement property determines whether the stack view lays out its arranged views relative to its layout margins.

Despite the advantages mentioned above, UIStackView has limits.

The UIStackView is a nonrendering subclass of UIView; that is, it does not provide any user interface of its own. Instead, it just manages the position and size of its arranged views. As a result, some properties (like backgroundColor) have no effect on the stack view. Similarly, you cannot override layerClass, drawRect:, or drawLayer:inContext:.

Note that UIStackView can't scroll. If you ever need it to scroll, embed a stack view within a UIScrollView.

Hope this helps!

Fred Dupray
  • 233
  • 2
  • 6
  • A bit more detail on why a `UIStackView` would be a fit for this use case (rather than just links which can stop working and cause this answer to not be helpful) would improve this answer but overall you are correct; this is an excellent use-case for a `UIStackView` – Robotic Cat Jun 10 '16 at 01:07
  • Just added a few more elements to the answer, thanks for the observation! – Fred Dupray Jun 10 '16 at 02:03
-1

I recommend pure coding, you learn more.

If you specify that all your cards are equal width and height, it will ensure the last card doesn't get stretched.

This is how I often build my UI:

import UIKit

class ViewController: UIViewController {

    var container:UIView = UIView();
    var card1:UIView! = nil;
    var card2:UIView! = nil;
    var card3:UIView! = nil;
    var card4:UIView! = nil;
    var card5:UIView! = nil;
    var card6:UIView! = nil;

    override func viewDidLoad() {
        super.viewDidLoad()

        self.initViews();
        self.initConstraints();
    }

    func cardView() -> UIView
    {
        let card = UIView();
        card.backgroundColor = UIColor.orangeColor();
        return card;
    }

    func initViews()
    {
        self.card1 = self.cardView();
        self.card2 = self.cardView();
        self.card3 = self.cardView();
        self.card4 = self.cardView();
        self.card5 = self.cardView();
        self.card6 = self.cardView();

        self.container.addSubview(self.card1);
        self.container.addSubview(self.card2);
        self.container.addSubview(self.card3);
        self.container.addSubview(self.card4);
        self.container.addSubview(self.card5);
        self.container.addSubview(self.card6);

        self.view.addSubview(self.container);
    }

    func initConstraints()
    {
        self.container.translatesAutoresizingMaskIntoConstraints = false;
        self.card1.translatesAutoresizingMaskIntoConstraints = false;
        self.card2.translatesAutoresizingMaskIntoConstraints = false;
        self.card3.translatesAutoresizingMaskIntoConstraints = false;
        self.card4.translatesAutoresizingMaskIntoConstraints = false;
        self.card5.translatesAutoresizingMaskIntoConstraints = false;
        self.card6.translatesAutoresizingMaskIntoConstraints = false;

        var views = [String: AnyObject]();
        views["container"] = self.container;
        views["card1"] = self.card1;
        views["card2"] = self.card2;
        views["card3"] = self.card3;
        views["card4"] = self.card4;
        views["card5"] = self.card5;
        views["card6"] = self.card6;

        self.view.addConstraint(NSLayoutConstraint(item: self.container, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterX, multiplier: 1.0, constant: 0.0));

        self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-50-[container]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));

        self.container.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[card1(60)]-10-[card2(==card1)]-10-[card3(==card1)]-10-[card4(==card1)]-10-[card5(==card1)]-10-[card6(==card1)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));

        self.container.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[card1(100)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
        self.container.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[card2(==card1)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
        self.container.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[card3(==card1)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
        self.container.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[card4(==card1)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
        self.container.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[card5(==card1)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
        self.container.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[card6(==card1)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
    }
}

You end up with something like this:

screenshot

Zhang
  • 11,549
  • 7
  • 57
  • 87