How to change aspectRatio of IBOutlet for a view programmatically? - ios

Okay here is my constraints.
The view is aligned centreX and centreY and has leading, trailing, top & bottom as >= 0.
It also has another constraint of aspectRatio.
Now I have created the #IBOutlet of aspectRation constraint.
#IBOutlet weak var contentViewAspectRatio: NSLayoutConstraint!
The problem is I don't know how to change the aspectRatio programmatically.
if value {
//Change aspect ratio to 16/9
} else {
//Change aspect ratio to 19.5/9
}
Any kind of help would be appreciated.

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

Related

Swift - How changing textView height dynamically in constraint?

I want to change a text views's height in the middle of the table view cell by context. After textView I have another views, constant height provided by auto layout constraints.
Actually I will get first 150 characters from context to show, but I think using auto resize needed to prevent another screen sizes problem.
How Can I use auto dimension, Is there any way to assign table view row height like this?
let height = 4 + 17 + contextHeight + 4
in the storyboard, add an aspect ratio for the textview, Then check "Remove at build time" option.
In viewDidLayoutSubviews() :
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let contentSize = self.TextViewTitle.sizeThatFits(self.TextViewTitle.bounds.size)
var frame = self.TextViewTitle.frame
frame.size.height = contentSize.height
self.TextViewTitle.frame = frame
aspectRatioTextViewConstraint = NSLayoutConstraint(item: self.TextViewTitle, attribute: .Height, relatedBy: .Equal, toItem: self.TextViewTitle, attribute: .Width, multiplier: TextViewTitle.bounds.height/TextViewTitle.bounds.width, constant: 1)
self.TextViewTitle.addConstraint(aspectRatioTextViewConstraint!)
}

Programmatically Change multiplier of Alignment Constraint of Center X / Y

How can I programmatically change the multiplier in the simplest way?
For Swift 2.0
So for Y, if you set the top of the image equal with a constant of 0 to the top of the superView. then enter this code:
#IBOutlet weak var topc: NSLayoutConstraint!
let heightOfSuperview = self.view.bounds.height
topc.constant = heightOfSuperview * 0.90 // this has the same effect as multiplier
This is equivalent of Center.y multiplier = 0.9:1
I try to use extension but it not work, but change to the global utility function and it work for me:
public class func changeMultiplier(constraint: NSLayoutConstraint, multiplier: CGFloat) -> NSLayoutConstraint {
let newConstraint = NSLayoutConstraint(
item: constraint.firstItem,
attribute: constraint.firstAttribute,
relatedBy: constraint.relation,
toItem: constraint.secondItem,
attribute: constraint.secondAttribute,
multiplier: multiplier,
constant: constraint.constant)
newConstraint.priority = constraint.priority
NSLayoutConstraint.deactivateConstraints([constraint])
NSLayoutConstraint.activateConstraints([newConstraint])
return newConstraint
}
Simple answer, no extensions required. I tried for my case, worked fine for me.
So as multiplier is a get only property, we can simply set multiplier in the following way :
yourConstraintOutlet.setValue(yourDesiredMultiplierValue, forKey: "multiplier")
yourConstraintOutlet.setValue(0.75, forKey: "multiplier")
No need to write extensions or multiply by height of superview.

How to change bottom layout constraint in iOS, Swift

I have scroll view as #IBOutlet
#IBOutlet weak var mainScrollView: UIScrollView!
I want to change the
"Bottom space to: Bottom Layout Guide"
constraint programmatically.
First Item : Bottom Layout Guide.Top
Relation : Equal
Second Item: Scroll View.Bottom
Constant: 0 -> 50 // (I want to change this programmatically)
Priority: 1000
Multiplier: 1
How can I do this?
Take the constraint as IBOutlet of NSLayoutConstraint.
Set the constraint outlets and change constant value by :
self.sampleConstraint.constant = 20
self.view.layoutIfNeeded()
If you are adding constraint programatically like this:
var constraintButton = NSLayoutConstraint (item: buttonPlay,
attribute: NSLayoutAttribute.Bottom,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Bottom,
multiplier: 1,
constant: 0)
// Add the constraint to the view
self.view.addConstraint(constraintButton)
Then you can update it this way:
self.constraintButton.constant = 50
self.view.layoutIfNeeded()
And if you want that with animation you can do it this way:
self.view.layoutIfNeeded()
UIView.animateWithDuration(1, animations: {
self.constraintButton.constant = 50
self.view.layoutIfNeeded()
})
Hope it helps.
Create an IBOutlet for your constraint:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomContraint;
And when you need to change it call:
bottomContstraint.constant = //your value
view.layoutIfNeeded()
Also you can animate constraint change like that:
bottomContstraint.constant = //your value
UIView.animateWithDuration(0.5, animations: {
self.view.layoutIfNeeded()
})

textView height equal to content size without viewDidLayoutSubviews

I'm creating a viewController which contain 2 textViews a title and a fullText. At the moment i've created 2 textViews in the interface builder which is placed below each other and then created following code to change the height to equal to the content. However the issue is that it seem to be delayed, which gives a bad user experience. By delay i mean that it takes 0.5 or 1 sec before it resize? here is my code:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
setHeightToContent(self.titleText!)
setHeightToContent(self.fullText!)
scrollView?.contentSize = CGSizeMake(self.view.frame.width, self.fullText!.frame.origin.y + self.fullText!.frame.height)
println(self.fullText!.frame.origin.y + self.fullText!.frame.height)
}
func setHeightToContent(theTextView: UITextView) {
let contentSize = theTextView.sizeThatFits(theTextView.bounds.size)
var frame = theTextView.frame
frame.size.height = contentSize.height
theTextView.frame = frame
var aspectRatioTextViewConstraint = NSLayoutConstraint(item: theTextView, attribute: .Height, relatedBy: .Equal, toItem: theTextView, attribute: .Width, multiplier: theTextView.bounds.height/theTextView.bounds.width, constant: 1)
theTextView.addConstraint(aspectRatioTextViewConstraint)
}
Make each of the text views self-sizing in accordance with its own content, and use constraints so that the scroll view's contentSize is configured automatically based on its content.

Display an image in a scrollview with specific constraints (with auto layout)

I would like display a title and below a image in my view controller.
My constraints are :
the label can be at 50px of the top of the screen
the label can have one or many rows
the image can be at 50px of my label
the image must have the width of the screen
the scroll view must scroll depending on the size of all these elements
I have a view controller with a scroll view :
-view controller
---view
------scroll view
---------container view
------------label
------------image
I want use storyboard and auto layout.
I have succeeded to align the label correctly, but I am unable to display the image at 50px of the label and keep it ratio.
If I use "aspect fit" or "scale to fill" for the imageview, in this case, the label and the image are at 50px like I want.
With aspect fit :
With scale to fill :
But if I use "aspect fill", I don't understand how the image is displayed.
With aspect fill :
It has been about 3 days that I'm on this issue, it make me crazy.
I tried also to use invisible "spacer" views...
I don't find the solution.
I develop in swift with Xcode 6.
EDIT 1 : Positioning the image is solved with the vacawama advice
Add an #IBOutlet for your imageView and a property to your view controller to keep track of the constraint:
#IBOutlet weak var imageView: UIImageView!
var aspectRatio:NSLayoutConstraint?
Then add a new image:
let tree = UIImage(named: "WinterTree.jpg")!
imageView.image = tree
aspectRatio = NSLayoutConstraint(item: imageView, attribute: .Height, relatedBy: .Equal, toItem: imageView, attribute: .Width, multiplier: tree.size.height/tree.size.width, constant: 1)
imageView.addConstraint(aspectRatio!)
EDIT 2 : But after, my view is no longer to scroll
Constraints of my container view :
I removed the bottom constraint in order to the height of the container view adapts to the content, but I get an error :
The problem is that the height of the imageView is not getting changed, so the image is just centered over the old frame. The view mode setting (Aspect Fit, Scale To Fit, or Aspect Fill) does not change the height of your imageView. You need a constraint for the height of your imageView, but this constraint will change depending on your image.
I was able to make this work by doing the following.
I constrained the width of the image to the width of the view.
I added an AspectRatio constraint to the image. This sets the ratio of the width to the height, and since I have specified the width of the image, the height will now be fully specified. I had hoped to be able to update this constraint in code when I loaded a new image (because different images have different aspect ratios), but I could only change the constant from code and not the multiplier. So, to get around this, I made this a Placeholder constraint by checking Placeholder in the Attributes Inspector. This means that this constraint will be removed at build time.
In code, when I set the image for the imageView, I add a constraint that sets the width of the imageView to the height of the imageView with a multiplier. This takes the place of the aspect ratio constraint that was set in Interface Builder.
First, add an #IBOutlet for your imageView and a property to your view controller to keep track of the constraint:
#IBOutlet weak var imageView: UIImageView!
var aspectRatio:NSLayoutConstraint?
Then add a new image:
let tree = UIImage(named: "WinterTree.jpg")!
imageView.image = tree
aspectRatio = NSLayoutConstraint(item: imageView, attribute: .Height, relatedBy: .Equal, toItem: imageView, attribute: .Width, multiplier: tree.size.height/tree.size.width, constant: 1)
imageView.addConstraint(aspectRatio!)
When it is time to change your image, remove the previous aspectRatio constraint before adding a new one:
imageView.removeConstraint(aspectRatio!)
I implemented something similar to your layout. My project has a button in place of your label, but otherwise it is similar. When the user presses the button, my app replaces the image with one with an entirely different aspect ratio. Here is the Document Outline and all of the constraints from my project.
First Item: WinterTree1.jpg.Width
Relation: Equal
Second Item: WinterTree1.jgp.Height
Constant: 1
Priority: 1000
Multiplier: 0.68
First Item: WinterTree1.jpg.Leading
Relation: Equal
Second Item: ContentView.Leading
Constant: 8
First Item: ContentView.Bottom
Relation: Equal
Second Item: WinterTree1.jpg.Bottom
Constant: 8
First Item: ContentView.Trailing
Relation: Equal
Second Item: WinterTree1.jpg.Trailing
Constant: 8
First Item: WinterTree1.jpg.Top
Relation: Equal
Second Item: Button.Bottom
Constant: 20
First Item: Button.Top
Relation: Equal
Second Item: ContentView.Top
Constant: 20
First Item: ContentView.Center X
Relation: Equal
Second Item: Button.Center X
Constant: 0
First Item: Superview.Trailing
Relation: Equal
Second Item: ContentView.Trailing
Constant: 0
First Item: ContentView.Leading
Relation: Equal
Second Item: Superview.Leading
Constant: 0
First Item: Superview.Bottom
Relation: Equal
Second Item: ContentView.Bottom
Constant: 0
First Item: ContentView.Top
Relation: Equal
Second Item: Superview.Top
Constant: 0
First Item: ContentView.Width
Relation: Equal
Second Item: Superview.Width
Constant: 0
First Item: Scroll View.Leading
Relation: Equal
Second Item: Superview.Leading
Constant: 0
First Item: Scroll View.Top
Relation: Equal
Second Item: Superview.Top
Constant: 0
First Item: Superview.Trailing
Relation: Equal
Second Item: Scroll View.Trailing
Constant: 0
First Item: Bottom Layout Guide.Top
Relation: Equal
Second Item: Scroll View.Bottom
Constant: 0

Resources