bnzelener
bnzelener

Reputation: 223

iOS Scaling Constraints for Different Screen Sizes

This is my first iOS app and I'm using Swift. I'm following tutorials on CodeWithChris.com.

The user will be tapping on a series of icons to determine what device model they have. So far I've used a handful of constraints to get the icons placed properly on an iPhone 6 display. When using iPhone 5S/5c/5 or 4S/4 display sizes the icons and text still render at the full size for iPhone 6 and they go off the screen.

Here's my process for building the layout:

  1. Grab and parse JSON to NSArray. Returns ["iPhone", "Android", "Windows", "iPad"]
  2. Create UIView object for each item in NSArray. Set Name variable to the associated name.
  3. Place each UIView using addSubview()
  4. Apply height, width, and bottom-margin constraints to the UIView
  5. Set UIView position constraints based on its position in the row
  6. Add the icons (UIImageViews) to each UIView
  7. Set height and width constraints based on the type of device
  8. Add text labels

I'm using a bunch of constraints to place the UIViews and the UIImageViews. How can I make these constraints dynamic based on the device's screen size?

iPhone 6 with contentView in blue and UIViews in red enter image description here

iPhone 6 iPhone 6 Simulator

iPhone 5/5S/5c iPhone 5 Simulator

iPhone 4S/4 iPhone 4S Simulator

Here's where I add the device UIViews and apply constraints:

    // Loop through the array of DeviceTypes to add each to the UIView
    for index in 0...deviceTypes.count-1 {

        // Grab the current device
        var thisDevice:DeviceType = deviceTypes[index]


        // Add to the view!
        contentView.addSubview(thisDevice)
        thisDevice.setTranslatesAutoresizingMaskIntoConstraints(false)


        // How tall is the Device UIView
        var heightConstraint:NSLayoutConstraint = NSLayoutConstraint(item: thisDevice, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 200)

        // How wide is the device UIView
        var widthConstraint:NSLayoutConstraint = NSLayoutConstraint(item: thisDevice, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 130)

        // How far is the bottom of the UIView from the top of the contentView
        var bottomMarginConstraint:NSLayoutConstraint = NSLayoutConstraint(item: thisDevice, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 100)


        // Add thisDevice's UIView constraints
        thisDevice.addConstraints([heightConstraint, widthConstraint])
        contentView.addConstraint(bottomMarginConstraint)
        thisDevice.backgroundColor = UIColor.redColor()


        // set UIView position constraints based on place in row
        if (index > 0) {
            // device is second or further in row
            var deviceOnTheLeft = deviceTypes[index-1]

            var leftMarginConstraint:NSLayoutConstraint = NSLayoutConstraint(item: thisDevice, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: deviceOnTheLeft, attribute: NSLayoutAttribute.Right, multiplier: 1, constant: 10)

            contentView.addConstraint(leftMarginConstraint)
        }
        else {
            // device is first in row
            var leftMarginConstraint:NSLayoutConstraint = NSLayoutConstraint(item: thisDevice, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Left, multiplier: 1, constant: 0)

            // Add constraint
            contentView.addConstraint(leftMarginConstraint)
        }

Here are the constraints that I apply to the icon images:

func addDeviceImages() {

    // Set right image based on deviceType name
    self.frontImageView.image = UIImage(named: self.Name!)

    // Set translates autoresizing mask to fault
    self.frontImageView.setTranslatesAutoresizingMaskIntoConstraints(false)

    // Add the imageview to the view
    self.addSubview(self.frontImageView)

    if (self.Name == "Windows") {
        self.addImageSizeConstraints(90, height: 160)
    }

    else if (self.Name == "iPad"){
        self.addImageSizeConstraints(120, height: 180)
    }

    else if (self.Name == "iPhone"){
        self.addImageSizeConstraints(85, height: 175)
    }

    else if (self.Name == "Android"){
        self.addImageSizeConstraints(95, height: 185)
    }

}

func addImageSizeConstraints(width: CGFloat, height: CGFloat) {

    // Set the size constraints for the imageview based on the values passed in
    var heightConstraint:NSLayoutConstraint = NSLayoutConstraint(item: self.frontImageView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: height)

    var widthConstraint:NSLayoutConstraint = NSLayoutConstraint(item: self.frontImageView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: width)

    self.frontImageView.addConstraints([heightConstraint, widthConstraint])


    // Set the position of the imageview in the UIView
    var verticalConstraint:NSLayoutConstraint = NSLayoutConstraint( item: self.frontImageView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: -25)

    var horizontalConstraint:NSLayoutConstraint = NSLayoutConstraint( item: self.frontImageView, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Left, multiplier: 1, constant: 5)

    self.addConstraints([horizontalConstraint,verticalConstraint])
}

Upvotes: 1

Views: 5644

Answers (1)

matt
matt

Reputation: 534885

You are using the wrong kinds of constraints. Your width constraints are absolute - you are setting a fixed constant. Instead, make the constant 0, and the widths depend on the height, using a value for the multiplier that fixes the aspect ratio correctly. In this way, as the height changes, the width will change to match. Do the same sort of thing with the separation between the images - make the separation depend on the width of the superview, as a multiplier.

Upvotes: 1

Related Questions