How do I update a constraint from the Storyboard without an IBOutlet or Identifier? - ios

I have a lot of views that are created in the storyboard, but I want them to be able to update their constraints dynamically without having to use an IBOutlet each time.
I started by making a custom class for the superview of the view I want to update, and change its subview's bottom constraint like this:
myView.constraints.filter{ $0.firstAnchor is NSLayoutAttribute.bottom }.constant -= 200
'NSLayoutAttribute.bottom' doesn't seem to be the correct way to check the type of the Anchor.
How do I check the type of the constraints I want to change?
Am I correct in updating the constraints in the superview of the view I want to change, not the view itself?

NSLayoutConstraint from iOS7 have a property called identifier, from code or from IB you can set this property.
After that to get the constraint you are looking for is just a matter of searching it in a particular view.
Consider this UIView extension:
func constraint(withIdentifier:String) -> NSLayoutConstraint? {
return constraints.filter{ $0.identifier == withIdentifier }.first
}

As per dahlia_boy's suggestion, I used UIView.animate to achieve this functionality, however it doesn't seem to be permanent:
translatesAutoresizingMaskIntoConstraints = true
UIView.animate(withDuration: 1, animations: {
self.frame.size.height -= 200
})

Related

Update widthAnchor on Device orientation

I have this constraint in my controller for a view like this:
someView.widthAnchor.constraint(equalToConstant: view.bounds.width).isActive = true
Now I've added this constraint in willLayoutSubviews to update it on device rotation.
But it doesn't seem to update, even more like it adds another width constraint, which of course conflicts with the old width constraint.
Now I don't really know a proper solution to update this width constraint, but it seems to me like I need to remove the constraint first and then set it again.
Which if I test this like this:
someView.constraints.forEach {
someView.removeContraint($0)
}
This works like expected, only it deletes of course some constraints I don't want to delete... so also not a solution.
Sol1
hook the width constraint as IBOutlet and change it's constant in code like this
self.widthCon.constant = //value
self.view.layoutIfNeeded()
Sol2
delete the constraint with identifier
someView.constraints.forEach {
if $0.identifier == "set_id" {
someView.removeConstraint($0)
someView.widthAnchor.constraint(equalToConstant: view.bounds.width).isActive = true
}
}
Alternate Solution:
If you have a reference to the superview of someView then you can simply assign equal width constraints like so:
someView.widthAnchor.constraint(equalTo: superView.widthAnchor)
This way you can let the Autolayout engine update the width automatically when the device orientation changes.

Modify height of a container view

Here is my app with the profile on the left (ProfileViewController) and a containerView on the right (ContainerViewController). What I am trying to achieve is once the containerViewController has done its job, it will update the height of the UIView.
// ContainerViewController.swift
let heightConstraint = self.view.constraints.filter { $0.identifier == "Height" }.first
heightConstraint?.constant = height
When browsing the list of constraints, they're all emtpy, and I did set up some constraints in the storyboard. So Why ? Is there any way to access the UIView within the ContainerViewController ?
Ps:
DispatchQueue.main.async {
self.view.frame = CGRect(x: 0, y: 0, width: width, height: height)
}
Sounds to work, but I think that modify constraint is proper ? Nope ?
What I would do, is to set IBOutlet for the constraint, and store it inside ContainerView.
This way, you don't depend on order number or anything else, to get this constraint, even if code (or storyboard) will changed in the future.
And Yes, the right way is to set the constraint constant, and not changing frame.
But be aware, that even constraint constant change need to be put on the main (UI) thread - (by the look of your code, I assume, you are dealing with the threads).

How can I define animations for views using auto layout without relying on tons of constraint references and boilerplate code?

Before the switch to auto layout, applying animations to a view was easy - you'd just change the frame in UIView.layoutWithDuration:. When constraints come into the picture, things get more complicated. The most common method of animating a view that uses auto layout is to keep a reference to the constraint(s) you want to change, then set the constant value, but this is very difficult to design around, and can affect other views if their constraints depend on the position of the view you want to animate.
There must be a better way to do this. I'd love to be able to do something like view.translateFrom(direction: .left, distance: 30, duration: 0.3, delay: 0).
Ultimately you will want to use constraints to achieve simple animations. The overriding reason for this is -
"Choosing any other way is simply swimming against the stream"
If you don't want to 'litter' your classes with #IBOutlets for the constraints you wish to animate then you can in most cases obtain a reference to a pertinent constraint in code:
Handy extension
import UIKit
extension UIView {
func constraint(attribute: NSLayoutAttribute) -> NSLayoutConstraint? {
var constraint : NSLayoutConstraint? = .none
for potentialCenterXConstraint in self.constraints {
if potentialCenterXConstraint.firstAttribute == attribute {
constraint = potentialCenterXConstraint
break
}
}
return constraint
}
}
client code use
if let centerXConstraint = someView.constraint(attribute: .centerX) {
// Do something funky with centerXConstraint
}

Collapse Expand UITableView

I tried to expand/collapse an UITableView that is inside an UIViewController. I put the UITableView height constraint to zero and make the animation
func collapseExpandRoomSection() {
isRoomCollapsed = !isRoomCollapsed
tableHeightConstraint.constant = isRoomCollapsed ? 0.0 : totalTableHeight
UIView.animateWithDuration(0.3) {
self.view.layoutIfNeeded()
}
}
The collapse effect works fine but when I tried to expand the table all the cells are gone.
Thanks
Look like you have updated the constraint, You should be doing it in this order I believe:
self.view.setNeedsUpdateConstraints()
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
As you have updated the constraint, therefore you should firstly update the set update constraints and set needs layout. Followed by the layoutIfNeeded() to have the change apply immediately.
As discussed briefly on the comments, the code is only applicable if you override the updateConstraint. Is not correct in answering the question above.

How do I move an UI Object in swift that has its constraints set in a storyboard?

So I am trying to create a simple moving animation for my UIImageView.
Problem is I've had its constraints set in the storyview (3 constraints, top, left and right positions).
So I cannot just alter coordinates - my image doesn't move if I do so.
So I guess I have to alter my constraints first. But I cannot even get them in code.
println(myPicture.constraints())
This returns an empty array.
How do I animate an object that has its constraints set in the storyboard?
You can change the constant values of the constraints. I would make outlets for them. Something like this:
#IBOutlet var topConstraint: NSLayoutConstraint!
You can connect these in Storyboard just like any other outlet. It's easiest if you find the constraint in the left-side view hierarchy menu and drag there to make the connections. Then in your code:
topConstraint.constant = 50 // or whatever you want to set it to
That should do it. If you want to animate the movement, you could put it an an animation block. If the change isn't happening when you do this, try calling this afterwards:
UIView.animateWithDuration(0.2,
animations: { () -> Void in
self.topConstraint.constant = 50
self.topConstraint.layoutIfNeeded()
},
completion: nil)

Resources