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.
Related
I want to place header view on top of screen with NSLayoutConstraint (I must use NSLayoutConstraint). When I do it like in below code, view places corruptly in somewhere else and also controllers background color turns black and nothing works. Where am I doing wrong?
I searched below posts for not opening a duplicate post but nothing fixed it:
Programmatically creating constraints bound to view controller margins
Programmatically Add CenterX/CenterY Constraints
EDIT: This controller is inside navigation controller but I'm not sure If It is related.
override func viewDidLoad(){
self.view.backgroundColor = UIColor.white
boxView.backgroundColor = Color.Common.welcomeScreenBackgroundColor.withAlphaComponent(0.5)
boxView.translatesAutoresizingMaskIntoConstraints = false
self.view.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubView(boxView)
}
override func viewDidLayoutSubviews() {
//Header = 20 from left edge of screen
let cn1 = NSLayoutConstraint(item: boxView, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leading, multiplier: 1.0, constant: 0)
//Header view trailing end is 20 px from right edge of the screen
let cn2 = NSLayoutConstraint(item: boxView, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailing, multiplier: 1.0, constant: 0)
//Header view height = constant 240
let cn3 = NSLayoutConstraint(item: boxView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant:240)
//Header view vertical padding from the top edge of the screen = 20
let cn5 = NSLayoutConstraint(item: boxView, attribute: .top, relatedBy: .equal, toItem: self.topLayoutGuide, attribute: .bottom, multiplier: 1.0, constant: 0)
self.view.addConstraints([cn1,cn2,cn3,cn5])
}
The problem was setting translatesAutoresizingMaskIntoConstraints to false on Superview. So I deleted the;
self.view.translatesAutoresizingMaskIntoConstraints = false
and this solves the problem. I think this causes app creates constraint for superview.
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 am attempting to programatically add the UIView courseView to a container view (called container) that I have drawn in Interface Builder in the Storyboard.
I would like courseView to scale to fit the container. With the following code nothing shows - the courseView does not appear. What am I missing?
var courseView: UIView?
#IBOutlet weak var container: UIView!
override func viewDidLoad() {
super.viewDidLoad()
courseView = UIView(frame: CGRectMake(0, 0, 1000, 1000))
courseView?.backgroundColor = UIColor.blueColor()
view.addSubview(courseView!)
courseView!.translatesAutoresizingMaskIntoConstraints = false
let courseWidthConstraint = NSLayoutConstraint(item: courseView!, attribute: .Width, relatedBy: .LessThanOrEqual, toItem: container, attribute: .Width, multiplier: 1.0, constant: 0)
let courseHeightConstraint = NSLayoutConstraint(item: courseView!, attribute: .Height, relatedBy: .LessThanOrEqual, toItem: container, attribute: .Height, multiplier: 1.0, constant: 0)
let courseViewConstraint = NSLayoutConstraint(item: courseView!, attribute: .Height , relatedBy: .Equal , toItem: courseView!, attribute: .Width, multiplier: 2.0, constant: 0)
self.view.addConstraints([courseWidthConstraint, courseHeightConstraint, courseViewConstraint])
}
It is not clear to me what you are really trying to achieve here. But as a first suggestion, try replacing the .LessThanOrEqual with .Equal for the width constraint.
One other thing you need for your constraints to work, are some top and leading constraints to the container(top is just a suggestion you might want some other alignment for the height)
let courseLeadingConstraint = NSLayoutConstraint(item: courseView!, attribute: .Leading, relatedBy: .Equal, toItem: container, attribute: .Leading, multiplier: 1.0, constant: 0)
let courseTopConstraint = NSLayoutConstraint(item: courseView!, attribute: .Top, relatedBy: .Equal, toItem: container, attribute: .Top, multiplier: 1.0, constant: 0)
The aspect ration constraint for having the height half the width would be the following
let courseViewConstraint = NSLayoutConstraint(item: courseView!, attribute: . Width , relatedBy: .Equal , toItem: courseView!, attribute: .Height, multiplier: 2.0, constant: 0)
Let me know if it worked out.
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.
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.