I have a UIButton inside a UIView, my UIButton has constraints which I set in storyboard. Now, I want to set the center of the UIButton at the center of the UIView. How will I do that programmatically?
Try .center property like
myButton.center = self.view.center
You can also specify x and y If you need.
myButton.center.x = self.view.center.x // for horizontal
myButton.center.y = self.view.center.y // for vertical
This approach is using Using NSLayoutConstraint where self.cenBut is the IBoutlet for your button.
func setupConstraints() {
let centerX = NSLayoutConstraint(item: self.cenBut, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0)
let centerY = NSLayoutConstraint(item: self.cenBut, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0)
let height = NSLayoutConstraint(item: self.cenBut, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 22)
self.cenBut.translatesAutoresizingMaskIntoConstraints = false
self.view.addConstraints([centerX, centerY, height])
}
In viewDidLoad()
self.view.removeConstraints(self.view.constraints)
self.setupConstraints()
since you are using constraints, you won't be able to set the center so you need to use constraints to set the center as well:
let centerYCon = NSLayoutConstraint(item: labelMessage,
attribute: .CenterY,
relatedBy: .Equal,
toItem: contentView,
attribute: .CenterY,
multiplier: 1.0,
constant: 0.0);
contentView.addConstraint(centerYCon);
let centerXCon = NSLayoutConstraint(item: labelMessage,
attribute: .CenterX,
relatedBy: .Equal,
toItem: contentView,
attribute: .CenterX,
multiplier: 1.0,
constant: 0.0);
contentView.addConstraint(centerXCon);
I'm assuming that you know what "programmatically" means with autolayout constraints. If you are truly using autolayout then you must have set the properties of your button to setTranslatesAutoresizingMaskIntoConstraints to false.
This can be done in 2 ways:
myButton.center = view.center //view is your parentView
myButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
I should be
yourButton.setCenter(self.view.center);
But keep in mind that if you programmatically set the frame of a view, your layout constraints might not work as intended.
Related
I have a view and I want to center it horizontally and vertically in its superview.
I tried this but it's not working:
let horConstraint = NSLayoutConstraint(item: webView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0.0)
view.addConstraints([horConstraint])
I would suggest trying to use Anchors which are more convenient and easy to understand. This code centered my view:
let someView = UIView()
someView.backgroundColor = .red
someView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(someView)
NSLayoutConstraint.activate([
someView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
someView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
someView.heightAnchor.constraint(equalToConstant: 50),
someView.widthAnchor.constraint(equalToConstant: 50)
])
Try this:
Note: Make sure that you are setting this property (before applying constraints) to false for webView
webView.translatesAutoresizingMaskIntoConstraints = false
For x axis
let horConstraint = NSLayoutConstraint(item: webView, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX ,multiplier: 1.0, constant: 0.0)
For y axis
let verConstraint = NSLayoutConstraint(item: webView, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY ,multiplier: 1.0, constant: 0.0)
view.addConstraints([horConstraint, verConstraint])
I'm trying to center a logoImage horizontally & vertically by setting constraints but when tested, It was displayed on (x:0, y:0).
Any idea how to fix this?
Thanks
var movieView : UIView?
let logoImage = UIImageView(image: #imageLiteral(resourceName: "my_logo"))
// This function runs in viewWillAppear
internal func setupIntroMovie() {
movieView = UIView(frame: view.frame)
view.addSubview(movieView!)
view.addSubview(logoImage)
let horizontalConstraint = NSLayoutConstraint(item: logoImage,
attribute: .centerX,
relatedBy: .equal,
toItem: view,
attribute: .centerX,
multiplier: 1,
constant: 0)
let verticalConstraint = NSLayoutConstraint(item: logoImage,
attribute: .centerY,
relatedBy: .equal,
toItem: view,
attribute: .centerY,
multiplier: 1,
constant: 0)
view.addConstraints([horizontalConstraint,
verticalConstraint])
updateViewConstraints()
}
You need to set logoImage.translatesAutoresizingMaskIntoConstraints = false as it determines whether the view’s autoresizing mask is translated into Auto Layout constraints.
internal func setupIntroMovie() {
movieView = UIView(frame: view.frame)
view.addSubview(movieView!)
view.addSubview(logoImage)
let horizontalConstraint = NSLayoutConstraint(item: logoImage,
attribute: .centerX,
relatedBy: .equal,
toItem: view,
attribute: .centerX,
multiplier: 1,
constant: 0)
let verticalConstraint = NSLayoutConstraint(item: logoImage,
attribute: .centerY,
relatedBy: .equal,
toItem: view,
attribute: .centerY,
multiplier: 1,
constant: 0)
// Update
logoImage.translatesAutoresizingMaskIntoConstraints = false
view.addConstraints([horizontalConstraint,
verticalConstraint])
updateViewConstraints()
}
If this property’s value is true, the system creates a set of
constraints that duplicate the behavior specified by the view’s
autoresizing mask. This also lets you modify the view’s size and
location using the view’s frame , bounds , or center properties,
allowing you to create a static, frame-based layout within Auto
Layout.
In my Swift app, I need to update an AutoLayout constraint multiple times, so I have a function which does this. I'm having an issue with this constraint not being updated sometimes, and I've narrowed down the issue to being that it updates the first time the function is called, but not if that function is called again.
The code from the function looks like this:
let verticalSpace = NSLayoutConstraint(item: self.prompt, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 10)
NSLayoutConstraint.activate([verticalSpace])
Does anyone know what could be causing this? Is there something which needs to be done in order to update a function multiple times?
You cannot have "competing" constraints. If you set the verticalSpace to 10, then set another verticalSpace to 20, which constraint should be used?
What you need to do is remove the existing constraint (or deactivate it), and then add/activate your new constraint.
Or...
Create your verticalConstraint and save a reference to it... then when you want to change it you can change the .constant however you wish.
Try this:
let verticalSpace = NSLayoutConstraint(item: self.prompt, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 10)
NSLayoutConstraint.activate([verticalSpace])
view.layoutIfNeeded()
Edit:
I've just achieved this by the following rule.
Either
Step 1:
remove that constraint which you want to update.
Step 2:
Add constraint again.
or
Update constraint value.
In both cases you should have the reference of that constraint which you want to update.
How:
I have just added my demo code.I have programmatically added a view and and changed it's vertical constraint by button click.Just go through the code.
Upto viewDidLoad method:
import UIKit
class ViewController: UIViewController {
let someView = UIView()
var topValue:CGFloat = 10
var verticalConstraint:NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
//This below codes are used to add a red color UIView in center which has width and height both 100
someView.backgroundColor = UIColor.red
someView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(someView)
let horizontalConstraint = NSLayoutConstraint(item: someView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0) //Center horizontally
verticalConstraint = NSLayoutConstraint(item: someView, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.top, multiplier: 1, constant: topValue) // center vertically.want to change that constraint later so took a variable.
let widthConstraint = NSLayoutConstraint(item: someView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100) //width 100
let heightConstraint = NSLayoutConstraint(item: someView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100) //height 100
view.addConstraints([horizontalConstraint, verticalConstraint, widthConstraint, heightConstraint])
}
And for each button click topConstant will be gradually updated.
This is the code for Either part which i mentioned.
self.view.removeConstraint(verticalConstraint)
verticalConstraint = NSLayoutConstraint(item: someView, attribute:
NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem:
view, attribute: NSLayoutAttribute.top, multiplier: 1, constant:
topValue)
self.view.addConstraint(verticalConstraint)
And this is the code for or part.
verticalConstraint.constant = topValue
And my buttonClick event method is basically like this.
#IBAction func updateView(_ sender: Any) {
topValue += 10
//In this case I've removed the previous constraint and add that constraint again with new Value
self.view.removeConstraint(verticalConstraint)
verticalConstraint = NSLayoutConstraint(item: someView, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.top, multiplier: 1, constant: topValue)
self.view.addConstraint(verticalConstraint)
//In this case I've just update that constraint value.Commented because we are using first method
//verticalConstraint.constant = topValue
}
And my output.
I have this situation:
I want this:
The black view is at X distance between violet view and if I tap I button in the violet view the violet view's height reduces(or increase) and I want that the black view is at the same X distance between violet view.
For this reason I write this code:
For button event that increase or reduce the violet's view I use this method:
#IBAction func tapOpenButton(){
self.altezza.constant = self.altezza.constant + 20
}
#IBAction func tapCloseButton(){
self.altezza.constant = self.altezza.constant - 20
}
where altezza is a NSLayoutConstraint
The both views are in a scrollview and I set constraint by code in this way:
var newView = blackView
var view = violetView
let horizontalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 120)
let widthConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100)
let heightConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100)
self.altra.addConstraints([horizontalConstraint,verticalConstraint, widthConstraint, heightConstraint])
altra is the scrollview where both views are
My problem is that the distance between the violet view and black view is not the same when I reduce or increase violet's height , in particular , for example , when I increase the violet view's height , violet view goes under the black view.
I'm new in auto layout by code.
Can you help me?
Check if this works in place of your heightConstaint
let heightConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100)
Instead of pinning the views by centerY, set the top of the black view to the bottom of the purple one with a margin.
let verticalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.bottom, multiplier: 1, constant: 10)
TL;DR -> what are the necessary steps to create a class that extends UIScrollView, instantiate that class, and then add it as a subview of an existing UIView using NSLayoutConstraints for my UI?
I'm in a situation where I need to add a UIScrollView to an existing UIViewController's subview (not self.view - a child of self.view). I'm also not using IB because I want to understand my code. I've managed to lay out each UI component, and I can see that my view hierarchy has been assembled correctly in the debugger.
Unfortunately, my UIScrollView will not scroll and for some reason none of my subviews are being displayed visually (note that the immediate children of my scroll view are also scroll views so my problem is likely my understanding of the UIScrollView class).
The above image shows the general structure of my UI, and the light gray area is my UIScrollView. I have a class that extends UIScrollView and UIScrollDelegate (not 100% if the latter parent class is necessary). The class looks like this
import UIKit
class PlayWorkout: UIScrollView, UIScrollViewDelegate{
var workoutInstructionTiles : [WorkoutInstruction] = [WorkoutInstruction]();
var heightsTotal : Int = 0; //This variable will keep track of the running total for each of the workout tile subviews to help with positioning
init(workoutSubviews : [WorkoutInstruction]){
super.init(frame : CGRect.zero);
workoutInstructionTiles = workoutSubviews;
appendTiles();
self.translatesAutoresizingMaskIntoConstraints = false;
}
func appendTiles(){
print("appending tiles \(workoutInstructionTiles.count)");
for(var i = 0; i < workoutInstructionTiles.count; i++){
let setCount = workoutInstructionTiles[i].getNumSets();
workoutInstructionTiles[i].layer.cornerRadius = 5;
workoutInstructionTiles[i].backgroundColor = UIColor.whiteColor();
workoutInstructionTiles[i].translatesAutoresizingMaskIntoConstraints = false;
changeContentSize(workoutInstructionTiles[i]);
self.addSubview(workoutInstructionTiles[i]);
//Formula for position relative to top of the ScrollView will be 10 (margin) + 20 (label space) + setCount * 20 (content) + heightsTotal
let verticalConstraint = NSLayoutConstraint(item: workoutInstructionTiles[i], attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: CGFloat(30 + heightsTotal));
let leftConstraint = NSLayoutConstraint(item: workoutInstructionTiles[i], attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: 10);
let rightConstraint = NSLayoutConstraint(item: workoutInstructionTiles[i], attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 10);
let heightConstraint = NSLayoutConstraint(item: workoutInstructionTiles[i], attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: CGFloat(setCount * 20 + 20));
self.addConstraints([verticalConstraint, leftConstraint, rightConstraint, heightConstraint]);
heightsTotal += setCount * 20;
}
}
func setViewConstraints(superView : UIView) -> Void{
let verticalConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: superView, attribute: NSLayoutAttribute.CenterY, multiplier: 1.0, constant:30);
let horizontalConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: superView, attribute: NSLayoutAttribute.CenterXWithinMargins, multiplier: 1.0, constant: 0);
let heightConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: superView, attribute: NSLayoutAttribute.Height, multiplier: 1.0, constant: -60);
let widthConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: superView, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0);
superView.addConstraints([verticalConstraint, horizontalConstraint, heightConstraint, widthConstraint]);
}
func changeContentSize(aView : UIView){
var contentRect : CGRect = CGRect.zero;
for view in aView.subviews {
contentRect = CGRectUnion(contentRect, view.frame);
}
self.contentSize = contentRect.size;
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
I then implement the class when a button is clicked to start a workout. From what I've been able to ascertain thus far it seems like the only methods I need to call/set for UIScrollView are
scrollView.contentSize = CGSize(width: aWidth, height: aHeight)
scrollView.translateAutoResizingMaskIntoConstraints = false
So, here's my class instantiation - this is inside of a UIGestureRecognizer event handler inside of a UIViewController that also extends UIScrollViewDelegate:
workoutConfig = PlayWorkout(workoutSubviews : workoutInstructions);
workoutConfig.delegate = self;
workoutConfig.changeContentSize(workoutConfig);
workoutConfig.translatesAutoresizingMaskIntoConstraints = false;
self.contentView.addSubview(workoutConfig);
workoutConfig.setViewConstraints(self.contentView);
I'm also using a method called changeContentSize() that was adapted from an objective-C method posted to SO that looks like this:
func changeContentSize(aView : UIView){
var contentRect : CGRect = CGRect.zero;
for view in aView.subviews {
contentRect = CGRectUnion(contentRect, view.frame);
}
self.contentSize = contentRect.size;
}
I know that's a lot of reading, but I wanted to be thorough. I appreciate any help. Thanks!
Ok, so after 3 days of maddening research and going down all kinds of nonsense paths to accomplish this I FINALLY get it, and it's actually not that hard (just extremely poorly documented by apple). I'm going to try to share what I found - this link is what finally helped me understand how to do this so I feel obliged to give credit where it's due:
http://spin.atomicobject.com/2014/03/05/uiscrollview-autolayout-ios/#comment-541731
Ok, here's the step by step to create a scrollable view using auto layout.
1 - a UIScrollView can only have 1 child view, which should generally be a UIView
to hold all of the scrollable content. Make sure to create a UIView, add any content you want to scroll to that UIView, and then add that UIView as a subview of your UIScrollView
2 - Pin the scroll view to its superview with whatever constraints you want, just make sure you set the top, leading, trailing and bottom. Here's what my code looked like (self is a class extending UIScrollView in my case):
func setViewConstraints(superView : UIView) -> Void{
let verticalConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: superView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant:60);
let leftConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: superView, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: 0);
let rightConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: superView, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 0);
let bottomConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Bottom, relatedBy: .Equal, toItem: superView, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 0);
superView.addConstraints([verticalConstraint, leftConstraint, bottomConstraint,rightConstraint]);
}
3 - Pin that child UIView mentioned in step 1 to the scroll view ensuring to set -> top, leading, trailing, and bottom. This should generally be pinned exactly unless the UIView is designed to be smaller than the dimension of the scroll view. This step is accomplished to ensure that the device knows where to place the UIView. My code looked like this (again, self is referring to a class that extends UIScrollView)
let pinTop = NSLayoutConstraint(item: subScrollView, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant:0);
let pinBottom = NSLayoutConstraint(item: subScrollView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 0);
let pinLeft = NSLayoutConstraint(item: subScrollView, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 0);
let pinRight = NSLayoutConstraint(item: subScrollView, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: 0);
self.addConstraints([pinTop, pinBottom, pinLeft, pinRight]);
4 - in order for the scroll view to understand the content size of the view, you MUST SET THE HEIGHT AND THE WIDTH OF THE UIVIEW BASED ON THE SCROLL VIEWS SUPERVIEW OR A CALCULATION THAT DOESN'T INVOLVE THE SCROLL VIEW. Don't worry about setting other dimensions of the UIView relative to the scroll views superview, just height and width are necessary for the cocoa api to infer the content size. My code looked like this (self.viewController.contentView in this case refers to the scroll view's superview):
let widthConstraint = NSLayoutConstraint(item: subScrollView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: self.viewController.contentView, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0);
let heightConstraint = NSLayoutConstraint(item: subScrollView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: CGFloat(heightsTotal + (self.workoutInstructionTiles.count * 10)));
self.viewController.contentView.addConstraints([heightConstraint, widthConstraint]);
If each of these steps are followed correctly, your result should be a scrollable view. Now that I get this if I can help anyone with their code just leave a comment.