Changing an Autolayout-constrained UIView's frame at runtime in Swift - ios

For example, I use this code to change the frame position of a UIView called Propo:
self.Propo.frame.origin.x -= self.view.frame.width
(Propo has constraints in Storyboard)
But When the app view disappears and reappears, the UIView resets itself to its original position. How can I do to resolve it? I want the UIView to keep its position when I change it programmatically.
I've tried updateViewConstraints() by anything else append...

Constraints and manual frame manipulation are directly in conflict. You have to choose one or the other. You should instead "promote" the constraint you wish to change by Ctrl-dragging it to your view controller as an #IBOutlet in Storyboard. Then, you can manipulate its constant:
class MyViewController: UIViewController {
#IBOutlet weak var myPropoLeftEdgeConstraint: NSLayoutConstraint!
#IBAction doChangePropo(sender: AnyObject) {
myPropoLeftEdgeConstraint.constant -= view.frame.width
view.layoutIfNeeded()
}
}
This is already covered in Objective C in many places, so just do a search on programmatically changing an NSLayoutConstraint in code if you can read those examples.
How to edit constraint in code
(Edit to make more applicable: generally constraints should be animated for a clean user experience. Otherwise the eye is confused.)
You can easily animate the constraint adjustment by wrapping view.layoutIfneeded in a UIView.animateWithDuration(duration) { ... } block.

Related

How do I expand and collapse a UICollectionViewCell with swift?

The Collection view cell would need to show and hide additional tools for the device being controlled. The button that collapses would be at the bottom of the UICollectionViewCell. Whenever the button is pressed it would animate downwards revealing the fan or lights extra features or animate upwards when hiding the extra features. I only need help with expanding and collapsing by using the button at the bottom of the cell. An image is attached showing an example.
Add a height constraint for your high/med/low selector.
Connect that constraint to var in your cell class like this:
#IBOutlet weak var checker1HeightConstraint: NSLayoutConstraint!
set that constraint to a value you want (either 0, for hidden, or 50 or 60 if not hidden)
have a method where you set the value:
func toggleControls(visible: Bool)) {
checker1HeightConstraint = visible? 50 : 0
setNeedsLayout()
}
If you are not sure how to set the outlet, just ctrl-drag from the constraint in you storyboard or xib to your custom cellView class. It will give you options to create it.

Custom UITableView Cell's Stack View Cannot Detect Super View Dimension

Context I have a UITableViewController with custom cells designed in a .xib file. In that .xib file I'm trying to set dynamic widths so splits the cell width into three subviews.
Problem is that the cell doesn't seem to be able to get the proper width, causing the cell to not fill the entire width. Here's a picture to better illustrate what I'm saying.
cell_not_filling_width
My Understanding (Could be totally misunderstanding this) Setting the frame constraint in the .xib file is no good b/c the program won't know the width until runtime. In other words, awakeFromNib (1.) loads before the screen width is determined... So idk how to fix this problem.. Any guidance would be greatly appreciated.
Presumably this is where the width is set - the width on my interface builder when I'm looking at the .xib file is the same when I print it out at cellForRowAt
Notes In my viewDidLoad function I've registered the nibfile as such so I don't think that's the problem. tableView.register(UINib(nibName: "NewTableViewCell", bundle: nil) , forCellReuseIdentifier: "newTableViewCell").
Here is some code that could be relevant.
NewTableViewCell.swift
import UIKit
class NewTableViewCell: UITableViewCell {
#IBOutlet var typeLabel: UILabel!
#IBOutlet var timeLabel: UILabel!
#IBOutlet var completedLabel: UILabel!
#IBOutlet var correctLabel: UILabel!
#IBOutlet var parentView: UIView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
The parentView is a view that contains the three subviews.
Here'sa picture of the .xib file
Here's a picture of my constraints on the .xib file (it seems cluttered)
UPDATE
Tried the stack view approach: XCode just defines the width as the subview with the biggest width (according to how large the text is)
Here is a picture of the current situation and the current constraints
I figured the constraints on the label may also be relevant:
UPDATE2
Ok I think I've found the problem; Basically the stack view cannot seem to detect the super view's length and width and it's dimension.
NOTE I have edited the constraints.
Here is what it looks like if I set the width and height to a constant value. The stack view will actually go to the set constraints
Here is what it looks like if I take those constraints out. So it seems like the width and height are 0. Which is why I believe the constraints are not being adopted from the super view.
Does anyone know how to fix this?
Try the below code in cellforRow.
Add IBOutliet for stackviewWidth and set is dynamically as shown below. Also check if the cell goes to the full width of the device by adding background color to cell.
if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let window = appDelegate.window {
cell.stackViewWidthConstraint = window.bounds.width
}

Constraints not updating automatically in ios

I have created a storyboard as in the image. In that I have set the vertical spacing between the red view and tableView is around 20. Its working good.
But when i tried to change the height of red view then the tableview should comes up with the constraints I added but the table view is remained constant at the same place.
I used the following code
redView.frame.size = CGSize(width: 1008, height: 0)
self.view.layoutIfNeeded()
Whenever you are adding a constraint to any object on storyboard then onwards that object's frame will be maintained by autolayout engine. This is the primary design principle I guess. In this case what you can simply do is that just make an outlet of the desired constraint which you want to update and update that constraint within your code. For your understanding I am attaching a screenshot.
Now after making a constraint outlet it must look like as follows.
#IBOutlet weak var containerViewHeight: NSLayoutConstraint!
Now to update the height you just have to do one thing.
containerViewHeight.constant = 0
In my opinion if this update is not working inside viewDidLoad or viewWillAppear then please update inside viewDidLayoutSubviews because when viewDidLoad get called then iOS doesn't apply the layout properly.
In my case what I will do is -
override func viewDidLayoutSubviews() { //This ensures all the layout has been applied properly.
super.viewDidLayoutSubviews()
containerViewHeight.constant = 0
}
Note: - To achieve some nice animation effect during layout changes you can apply this constraint update inside an UIView animation block like as follows. But remember, to see the effect nicely you have to call this after presenting the view properly like after viewDidAppear etc.
UIView.animateWithDuration(1.0, delay: 0.2, usingSpringWithDamping: 8.0, initialSpringVelocity: 1.0, options: UIViewAnimationOptions.CurveEaseIn, animations: {
containerViewHeight.constant = 0
self.view.layoutIfNeeded()
}, completion: nil)
Sorry for any mistake.
Update/Edit is most welcome.
Hope this helped. Thanks
You should be using Autolayout to manipulate the redView's height. Modifying its frame's size is not Autolayout. Modify its height constraint (if it exists)
You need to either use constraints or changes to frames! You cannot do both!
What you need to do is to make an outlet reference to your constraint and change its constant-value instead.
If you placed the code in viewdidload or viewwillappear, you will find issues like this. You need to put the code after all the constraints are loaded.
Also, make an object of the height constraint and name it for example redViewHeight.
and change its value by: redViewHeight.constant = 0
It will work!
Replace the constraints for the views with the following.
Redview
Top constraint to ParentView
Leading Constraint to ParentView
Trailing constraint to ParentView
Height Constraint
TableView
Top constraint to RedView
Leading Constraint to ParentView
Trailing constraint to ParentView
Bottom constraint to ParentView
Now wire an IBOutlet for the HeightConstraint of RedView and modify its constant value. The UITableView will adjust its height as desired.

why can't change button height when set frame?

I am testing a sample project with an button . And I write this , but it can't change the button's height .
What's wrong?
class ViewController: UIViewController {
#IBOutlet var test: UIButton!
override func loadView() {
super.loadView()
var newFrame = test.frame
newFrame.size = CGSizeMake(newFrame.width, newFrame.height * 10)
test.frame = newFrame
}
}
And get the same height I set .
Put your code in
viewDidLayoutSubviews
After calling super. Theoretically you need to wait for the auto layout engine to be done before you modify the frame, otherwise it will be set for you. In this case this is probably what happens as on didLoad the autolayout might not be done yet.
Instead of setting the frame, set the constraints for the button. This way you have full control. Especially when you layout other controls along with your button. Makes your life easier. Mostly.
For your reference check the wwdc sessions about auto layout and adaptive ui.
Hope this helps.

Swift mimic animated: animation

I am using self.navigationController?.setNavigationBarHidden(true, animated: true)
I wonder where I can see how that animation is built in code?
I want to mimic its behavior with another custom UIView
The animation is just a slide up, so an animation is created that changes the y-origin of the view being animated.
If using auto layout, you should have a top constraint that specifies that the view's top y position is equal to the superview's top y position. You can reference these NSLayoutConstraints using #IBOutlet like you can with other storyboard elements, so in your view controller:
class MyViewController {
#IBOutlet var viewToAnimate: UIView!
#IBOutlet var topConstraint: NSLayoutConstraint!
}
You will notice that NSLayoutConstraint has a constant property, which just specifies a value to add to the second constraint attribute when calculating the resulting layout frames using that constraint. So in the case of the topConstraint, you want to add -viewToAnimate.bounds.height*, so that the bottom of the view sits just out of sight at the top of the superview.
You animate changes using the UIView class method animateWithDuration(_:animations:) - any animatable UIView properties that are changed inside the animation closure will be animated over the specified duration. But when you change your constraint's constant property, the view properties aren't changed until another layout pass is performed on the view. You can invalidate the layout, and let the layout happen in the next pass implicitly with view.setNeedsLayout(), but we need the layout pass to happen inside the view's animation block for the animation to work. So instead, you can use layoutIfNeeded() to force the subviews to layout immediately.
Put together, your animate method might look something like this:
class MyViewController {
// ...
func animateViewUp() {
UIView.animateWithDuration(0.3) {
self.topConstraint.constant = -self.viewToAnimate.bounds.height
self.view.layoutIfNeeded()
}
}
}
In reality, you would likely specify a method that allows you to toggle between a shown/hidden state, but the above code should get you started.
*N.B. Check which way around the constraint is specified! If the constraint is specified in terms of your superview, then a negative constant will move the subview downwards!
Short answer: You can't. The source for Apple's frameworks is not provided. However, most of their animations are built on top of the same Core Animation framework that us third party developers use.
What is the effect this creates? I'm not sure I've ever animated away the navigation bar on a view controller that's in place. Can you post a GIF of the animation? I can probably give you an idea of how it's done if I know what it looks like.

Resources