Make a UIView relayout itself when dependent constraint changes - ios

I have a UIImageView and UIButton, UIButton is aligned to top of UIImageView. Now in code I am changing the top of UIImageView but the UIButton is not updated accordingly. I tried SetNeedsLayout, LayoutIfNeeded on UIButton but nothing works.
override func viewDidLayoutSubviews() {
profileImageView.frame.origin.y = arcView.bounds.height/1.8
editProfileButton.layer.setNeedsDisplay()
}
I have set the constraints in Storyboard. I would really appreciate any help here.

First: Are you sure that all your constraints (for both the UIImageview and UIButton) are added right?
Second: when working the constraints, you should change the origin.y of the UIImageview also by by a constraint, by modifying its constant's value:
Instead of directly changing profileImageView.frame.origin.y, you should change the constant of the constraint that tells what's the imageview origin.y (if the first point is applied, this constraint must be exist...); Add this constraint to the viewController as an IBOutlet and change value of its constant property (take a look at the comments in the code snippet, it's a part of the answer):
class ViewController: UIViewController {
// let's assume that this is the #IBOutlet of the constraint, I called it 'imageViewTopMargin':
#IBOutlet weak var imageViewTopMargin: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// it's not necessary -for sure- to do this the in the viewDidLoad method
// but I'm doing this for demo purposes:
// let's say that you want to push the imageView to the bottom with extra 40 points:
imageViewTopMargin.constant += 40
}
}
Hope this helped.

Related

How do I display a UIImageView via a Switch on state? Then...how do I add constraints to the UIImageView to either center it or such?

I am trying to have a UIImageView (centered horizontally in the viewController) with a UISwitch inside of it.
When the UIswitch is turned to the ON position - a second UIImageView should appear to the left of the original UIImageView, while the constraints should make it so that both UIImageViews are centered horizontally.
thanks in advance
There are many ways to do this, but the easiest is to simply use a centered horizontal UIStackView that contains only a single imageView. When the switch is flipped add the second imageView to the stackView by calling insertArrangedSubview(:at:) and the stackView will take care of maintaining the centering and any requested spacing. Similarly when the switch is off just call removeArrangedSubview(:) and everything goes back into place.
If you are not on iOS 9 and don't have UIStackView you can just drag an IBOutlet to the view that you want to move's centerX constraint and add to its .constant property and animate layoutIfNeeded to have it slide to the right.
class ViewController: UIViewController{
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var imageViewCenterXConstraint: NSLayoutConstraint!
let padding = CGFloat(10)
#IBAction func tapButton(_ sender: Any) {
imageViewCenterXConstraint.constant = imageView.frame.size.width / 2 + padding
UIView.animate(withDuration: 0.25) { self.view.layoutIfNeeded()}
}
}

Change Swift constraint on if statement

Here is my tableview row/cell:
there are constraints set in place - the imageview is below the label and the button is below the imageview.
here is my code:
if(row == 1) {
imageview.hidden = false
} else {
imageview.hidden = true
//how can i change the button constraint from below imageview to below label?
Adding and removing constraints is really bad example for that. I'll make your UI more complex.
Best way of solving these auto-layout problems is adding two constraints. One from imageView to button and second from imageView to label.
Now after setting these constraints, you need to set their priority levels. So, let's say button will be below the imageView first. In this case, you need to set imageView to button constraint's priority to something like 750 or UILayoutPriorityDefaultHigh and label to button constraint's priority to 250 or UILayoutPriorityDefaultLow.
Let's start creating a custom UITableViewCell
class YourTableViewCell: UITableViewCell {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var label: UILabel!
#IBOutlet weak var button: UIButton!
#IBOutlet weak var buttonToLabelConstraint: NSLayoutConstraint!
#IBOutlet weak var buttonToImageViewConstraint: NSLayoutConstraint!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func shouldHideImageView(hidden: Bool) {
if(hidden == false) {
buttonToLabelConstraint.priority = UILayoutPriorityDefaultLow
buttonToImageViewConstraint.priority = UILayoutPriorityDefaultHigh
imageView.hidden = true
} else {
buttonToLabelConstraint.priority = UILayoutPriorityDefaultHigh
buttonToImageViewConstraint.priority = UILayoutPriorityDefaultLow
imageView.hidden = false
}
self.contentView.layoutIfNeeded()
}
}
After that, in your class where tableView is placed implement a logic like that:
if(row == 1) {
cell.shouldHideImageView(true)
} else {
cell.shouldHideImageView(false)
}
You should be all set.
You can try using a StackView, when you tell something to be hidden, the imageView the stack view will adjust the StackView as if the imageView was never a part of the view and it is an easy work around to not have to worry about constraints.
You can create IBOutlet on constraint and then just simply change the value like this:
buttonConstraint.constant = newValue
But i suggest you create for this a tableView. In this case you code and logic, i think, will be more accurate.
you could to this instead of hiding.
Make an outlet from the heights constraint of the imageview, call it constraint for now.
Set constraint.constant = 0 // effectively same as hiding.
Set constraint.constant = NON_ZERO_VALUE // effectively same as show.
hope it helps!
I see a couple of options. The first is a little easier to implement but a little less flexible if you decide to change your layout later.
Make the button's constraint to be below the label. Keep a reference to this constraint (you can connect it to your code via storyboard just like you do with the button itself, if you're using storyboard). When the imageView is visible, set myConstraint.constant += myImageView.frame.height. When the imageView is hidden, set myConstraint.constant -= myImageView.frame.height. Afterwards, call view.setNeedsLayout to update your constraints.
Make two constraints: one for below the image, and one for below the label ("constraintToImage" and "constraintToLabel"). Hook them both up to your controller like in option 1, and call view.addConstraint(constraintToImage) and view.removeConstraint(constraintToLabel) when the image becomes visible (and the opposite for when it's hidden). Again, call view.setNeedsLayout after.

Animate UIView from center to top using autolayout constraings swift 2.1

I want to animate UITextField from center to top of the view on a button click. TextField has following constraints
Following is the code to on button click to move textfiled to top.
UIView.animateWithDuration(0.5) {
self.txtfield.center.y = 10
}
above code works, textfiled moved to top but when I go back and come to this view again textfield is again in center. I am new to swift I want that once textfield is moved to top it should stay on top.
You should get a reference to the constraint in your code. Then you change the constant value of the constraint instead.
I have added the code to move the text field to top in viewDidLoad but you will of course have this code in your button action. You have to drag from the "Align Center Y" and into your view controller in order to create a reference to the constraint. The reason why I subtract 10 is because your example and intention was to have a 10 points margin from the top. And take note that I "reversed" the first and second item like this:
Remember that when you use auto layout, the frame and center and the size of the views bounds get set by auto layout constraints.
class ViewController: UIViewController {
#IBOutlet weak var textFieldYAlignConstraint: NSLayoutConstraint!
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
UIView.animateWithDuration(1.2) { () -> Void in
self.centeraligntConstraint.constant = -400
self.view.layoutIfNeeded()// to animate layout constraint
}
}
}

UIView from CGRectMake does not have the right height

I am adding a UIView to a container view programmatically, (the container view however is created in storyboard). Here is the code:
class ViewController: UIViewController{
#IBOutlet weak var dwView: UIView!
private var dwSelector = dwSelectorView()
override func viewDidLoad(){
super.viewDidLoad()
addDWSelector()
}
func addDWSelector(){
dwSelector.setTranslatesAutoresizingMaskIntoConstraints(false)
dwSelector.frame = CGRectMake(self.dwView.bounds.origin.x, self.dwView.bounds.origin.y, self.dwView.bounds.width / 2.0, self.dwView.frame.height)
println("dw height: \(self.dwView.frame.height)")
//prints 568, way too large of a value
self.dwView.addSubview(dwSelector)
}
}
The heigh of dwView is 123 in storyboard but the print state printed 568 and so now this is what it looks like:
You should always not rely on -(void)viewDidLoad since view bounds is incorrect at this point or - (void)viewWillAppear if you are using auto layout to set your view's frame. If you layout view in UIViewController, viewDidLayoutSubviews() is a appropriate place, if you layout subviews in UIView, it is layoutSubviews().
Check this article to get more details:Where to progmatically lay out views in iOS 5 (and handling orientation changes)
have you tried to call addDWSelector() in viewWillAppear()?

Why do my UIImageView coordinates change when I update label value?

I want to move my UIImageView when a certain button is clicked. I have this code:
#IBOutlet weak var counter: UILabel!
#IBOutlet weak var indexCrow: UIImageView!
var crow = 0
#IBAction func plusButton(sender: UIButton) { // Plus Button
indexCrow.frame = CGRect(x: indexCrow.frame.origin.x + 20, y: indexCrow.frame.origin.y, width: indexCrow.frame.size.width, height: indexCrow.frame.size.height)
crow++
counter.text = String(crow)
}
If I remove the line counter.text = String(crow), my image view moves correctly, but my label does not update. If I write counter.text = String(crow) my label updates, but my image view does not move.
What I do wrong?
AutoLayout is running when you update the label and placing your image back to where it started. You have several options to deal with this. Choose one:
Turn off AutoLayout. Most don't choose this option because they want their layouts to work on multiple devices.
Create your imageView programmatically instead of in Interface Builder. If you do this, it won't be subject to AutoLayout and you can move it freely.
Place your imageView using AutoLayout constraints. Add IBOutlets to those constraints and update the constant values in code instead of modifying the frame.
When plusButton is called, store the new frame for your imageView in a property in your ViewController, and then put that frame in place in an override of viewDidLayoutSubviews which happens after AutoLayout runs.

Resources