How to Set Aspect Ratio Constraints Programmatically in iOS

How can I set aspect ratio constraints programmatically in iOS?

Like this. Try once.

[self.yourview setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.yourview addConstraint:[NSLayoutConstraint
constraintWithItem:self.yourview
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.yourview
attribute:NSLayoutAttributeWidth
multiplier:(self.yourview.frame.size.height / self.yourview.frame.size.width)
constant:0]];

or in the place of (self.yourview.frame.size.height / self.yourview.frame.size.width) you can use any float value.
Thanks.

Swift 3.0 -

self.yourview!.translatesAutoresizingMaskIntoConstraints = false
self.yourview!.addConstraint(NSLayoutConstraint(item: self.yourview!,
attribute: NSLayoutAttribute.height,
relatedBy: NSLayoutRelation.equal,
toItem: self.yourview!,
attribute: NSLayoutAttribute.width,
multiplier: self.yourview.frame.size.height / self.yourview.frame.size.width,
constant: 0))

Swift change aspect ratio constraint programmatically

Your initial constraints should look like this.

NSLayoutConstraint.activate([
foo.leadingAnchor.constraint(equalTo: superView.leadingAnchor),
foo.trailingAnchor.constraint(equalTo: superView.trailingAnchor),
foo.topAnchor.constraint(equalTo: superView.topAnchor)
])

let collapsedConstraint = foo.heightAnchor.constraint(equalTo: foo.widthAnchor, multiplier: 9/16)
let expandedConstraint = foo.bottomAnchor.constraint(equalTo: superView.bottomAnchor)
collapsedConstraint.isActive = collapsed
expandedConstraint.isActive = !collapsed

And the method that is triggered should change the constraints like this.

@objc func tapAction() {
collapsedConstraint.isActive = collapsed
expandedConstraint.isActive = !collapsed
superView.layoutIfNeeded()
}

1:1 width:height constraint to UIButton programmatically

Try this

myButton.addConstraint(NSLayoutConstraint(item: myButton, attribute: .height, relatedBy: .equal, toItem: myButton, attribute: .width, multiplier: 1, constant: 0))

Change Aspect Ratio constraint value programmatically

You can use following approach

Give two constraints. One for aspect ratio and another for height.

Make the two active alternatively like

constOptions_Aspect.isActive = false
constOptions_Height.isActive = true

You can give height zero, in height constant.

How to change aspectRatio of IBOutlet for a view programmatically?

Changing the aspect ratio means changing the multiplier of the constraint which is a read-only so you need to deactivate that constraint and create a new 1 with a new multiplier

let newCon = contentViewAspectRatio.addConstraintWithMul(0.3)
parentViewOfConstraint.removeConstraint(contentViewAspectRatio)
parentViewOfConstraint.addConstraint(newCon)
view.layoutIfNeeded()
contentViewAspectRatio = newCon


extension NSLayoutConstraint {
func addConstraintWithMul(_ multiplier: CGFloat) -> NSLayoutConstraint {
return NSLayoutConstraint(item: self.firstItem!, attribute: self.firstAttribute, relatedBy: self.relation, toItem: self.secondItem, attribute: self.secondAttribute, multiplier: multiplier, constant: self.constant)
}
}

Another option is to create 2 constraints with 2 different aspects and play with their active / priority state in case you have a limited and know number of aspects

Set UIImageView AspectRatio constraint programmatically in swift 3

You can change constraint programmatically in swift 3

let aspectRatioConstraint = NSLayoutConstraint(item: self.YourImageObj,attribute: .height,relatedBy: .equal,toItem: self.YourImageObj,attribute: .width,multiplier: (2.0 / 1.0),constant: 0)
self.YourImageObj.addConstraint(aspectRatioConstraint)

iOS programmatically aspect ratio constraint gets broken in UITableviewcell

I believe you must reduce the priority on your aspect-ratio constraint to avoid the broken constraint messages.

And, I think you're not-quite-correct about nn.5 values working.

For example... With a 420:360 ratio:

setting the width of the view to 59 results in an auto-layout height calculation of

59.0 * 360.0 / 420.0 == 50.571428571428569

Auto-layout is not going to give you an actual view height of 50.571428571428569 ... it's going to round it to the nearest .5 which is 50.5 and that is less than requested so you get the error.

With:

59.4 * 360.0 / 420.0 == 50.914285714285718

it rounds to 51, which is greater than requested, and you do not get the error.

A few more examples:

58.5 * 360.0 / 420.0 == 50.142857142857146  // rounds down -- error
58.4 * 360.0 / 420.0 == 50.057142857142857 // rounds down -- error
58.3 * 360.0 / 420.0 == 49.971428571428568 // rounds up -- NO ERROR

Changing your aspect-ratio constraint to Priority: 999 gives the same sizing results (rounds to the nearest .5) but does not throw the broken constraint error.

Since you will get the same actual size, changing the priority will simply allow auto-layout to "do the right thing" and shouldn't have any other effect on your layout.

AutoLayout constraints to fit view inside rectangle, preserving a certain aspect ratio (programmatically)

OK - here is one way to do it.

Take the "native" size of your subview, calculate the "aspect fit" ratio - that is, the ratio that will fit the width or height to the superview, and scale the other dimension appropriately.

Then, use centerXAnchor and centerYAnchor to position the subview, and widthAnchor and heightAnchor to size it.

Note: if you're trying to place an image, calculate the aspect fit size from the image size, put the image in an image view, set the image view scale mode to fill, and finally apply the constraints to the image view.

You should be able to run this example as-is. Just play around with the "native" size values at the top to see how it fits the subview into the superview.

public class AspectFitViewController : UIViewController {

// "native" size for the holderView
let hViewWidth: CGFloat = 700.0
let hViewHeight: CGFloat = 200.0

let topView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = UIColor.blue
return v
}()

let holderView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = UIColor.cyan
return v
}()

public override func viewDidLoad() {
super.viewDidLoad()
view.bounds = CGRect(x: 0, y: 0, width: 400, height: 600)
view.backgroundColor = .yellow

// add topView
view.addSubview(topView)

// pin topView to leading / top / trailing
topView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0.0).isActive = true
topView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0.0).isActive = true
topView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0.0).isActive = true

// explicit height for topView
topView.heightAnchor.constraint(equalToConstant: 250.0).isActive = true

// add holderView to topView
topView.addSubview(holderView)

// center X and Y
holderView.centerXAnchor.constraint(equalTo: topView.centerXAnchor, constant: 0.0).isActive = true
holderView.centerYAnchor.constraint(equalTo: topView.centerYAnchor, constant: 0.0).isActive = true

// holderView's width and height will be calculated in viewDidAppear
// after topView has been laid-out by the auto-layout engine

}

public override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

let aspectWidth = topView.bounds.size.width / hViewWidth
let aspectHeight = topView.bounds.size.height / hViewHeight

let aspectFit = min(aspectWidth, aspectHeight)

let newWidth = hViewWidth * aspectFit
let newHeight = hViewHeight * aspectFit

holderView.widthAnchor.constraint(equalToConstant: newWidth).isActive = true
holderView.heightAnchor.constraint(equalToConstant: newHeight).isActive = true

}

}

Edit:

After clarification... this can be accomplished by constraints only. The key is that "Priority 1000" top and leading constraints must be .greaterThanOrEqual to zero, and the bottom and trailing constraints must be .lessThanOrEqual to zero.

public class AspectFitViewController : UIViewController {

// "native" size for the holderView
let hViewWidth: CGFloat = 700.0
let hViewHeight: CGFloat = 200.0

let topView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = UIColor.blue
return v
}()

let holderView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = UIColor.cyan
return v
}()

public override func viewDidLoad() {
super.viewDidLoad()
view.bounds = CGRect(x: 0, y: 0, width: 400, height: 600)
view.backgroundColor = .yellow

// add topView
view.addSubview(topView)

// pin topView to leading / top / trailing
topView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0.0).isActive = true
topView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0.0).isActive = true
topView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0.0).isActive = true

// explicit height for topView
topView.heightAnchor.constraint(equalToConstant: 250.0).isActive = true

// add holderView to topView
topView.addSubview(holderView)

// center X and Y
holderView.centerXAnchor.constraint(equalTo: topView.centerXAnchor, constant: 0.0).isActive = true
holderView.centerYAnchor.constraint(equalTo: topView.centerYAnchor, constant: 0.0).isActive = true

// aspect ratio size
holderView.widthAnchor.constraint(equalTo: holderView.heightAnchor, multiplier: hViewWidth / hViewHeight).isActive = true

// two constraints for each side...
// the .equal constraints need .defaultLow priority
// top and leading constraints must be .greaterThanOrEqual to 0
// bottom and trailing constraints must be .lessThanOrEqual to 0

let topA = NSLayoutConstraint(item: holderView, attribute: .top, relatedBy: .greaterThanOrEqual, toItem: topView, attribute: .top, multiplier: 1.0, constant: 0.0)
let topB = NSLayoutConstraint(item: holderView, attribute: .top, relatedBy: .equal, toItem: topView, attribute: .top, multiplier: 1.0, constant: 0.0)

let bottomA = NSLayoutConstraint(item: holderView, attribute: .bottom, relatedBy: .lessThanOrEqual, toItem: topView, attribute: .bottom, multiplier: 1.0, constant: 0.0)
let bottomB = NSLayoutConstraint(item: holderView, attribute: .bottom, relatedBy: .equal, toItem: topView, attribute: .bottom, multiplier: 1.0, constant: 0.0)

let leadingA = NSLayoutConstraint(item: holderView, attribute: .leading, relatedBy: .greaterThanOrEqual, toItem: topView, attribute: .leading, multiplier: 1.0, constant: 0.0)
let leadingB = NSLayoutConstraint(item: holderView, attribute: .leading, relatedBy: .equal, toItem: topView, attribute: .leading, multiplier: 1.0, constant: 0.0)

let trailingA = NSLayoutConstraint(item: holderView, attribute: .trailing, relatedBy: .lessThanOrEqual, toItem: topView, attribute: .trailing, multiplier: 1.0, constant: 0.0)
let trailingB = NSLayoutConstraint(item: holderView, attribute: .trailing, relatedBy: .equal, toItem: topView, attribute: .trailing, multiplier: 1.0, constant: 0.0)

topB.priority = .defaultLow
bottomB.priority = .defaultLow
leadingB.priority = .defaultLow
trailingB.priority = .defaultLow

NSLayoutConstraint.activate([
topA, topB,
bottomA, bottomB,
leadingA, leadingB,
trailingA, trailingB
])

}

}


Related Topics



Leave a reply



Submit