i guess i am missing something with the UISegmentedControl and auto layout.
i have a TabbedApplication (UITabBarController), and i created a new UIViewController to act as tab.
to the new view i added UISegmentedControl, and place it to top using auto layout.
i guess i don't understand completely something ,
cause the UISegmentedControl is hiding under the titleBar
. can u help me understand what i am missing ?
thank you .
import Foundation
import UIKit;
class ViewLikes:UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
title = "some title";
var segmentControl:UISegmentedControl = UISegmentedControl(items:["blash", "blah blah"]);
segmentControl.selectedSegmentIndex = 1;
segmentControl.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(segmentControl)
//Set layout
var viewsDict = Dictionary <String, UIView>()
viewsDict["segment"] = segmentControl;
//controls
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[segment]-|",
options: NSLayoutFormatOptions.AlignAllCenterX,
metrics: nil,
views: viewsDict))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[segment]",
options: NSLayoutFormatOptions(0),
metrics: nil,
views: viewsDict))
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Your top space vertical constraint has to be related to your Top Layout Guide, not to your container margin. The following code should fix this problem:
override func viewDidLoad() {
super.viewDidLoad()
let segmentControl = UISegmentedControl(items:["blash", "blah blah"])
segmentControl.selectedSegmentIndex = 1
segmentControl.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(segmentControl)
//Horizontal constraints
view.addConstraint(NSLayoutConstraint(item: segmentControl, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self.topLayoutGuide, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 10))
//Horizontal constraints
let viewsDict = ["segment" : segmentControl]
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[segment]-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
}
Note that the horizontal constraints setting has also been rewritten.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I want to put two views at the same position using "auto layout visual format" in Swift, how do I do that in an elegant way?
I succeeded using NSLayoutConstraint, but I hope I can still find an elegant way using visual format.
My code using NSLayoutConstraint:
NSLayoutConstraint(item: greenView, attribute: .Left, relatedBy: .Equal, toItem: redView, attribute: .Left, multiplier: 1.0, constant: 0).active = true
NSLayoutConstraint(item: greenView, attribute: .Top, relatedBy: .Equal, toItem: redView, attribute: .Top, multiplier: 1.0, constant: 0).active = true
NSLayoutConstraint(item: greenView, attribute: .Right, relatedBy: .Equal, toItem: redView, attribute: .Right, multiplier: 1.0, constant: 0).active = true
NSLayoutConstraint(item: greenView, attribute: .Bottom, relatedBy: .Equal, toItem: redView, attribute: .Bottom, multiplier: 1.0, constant: 0).active = true
I would echo what vacawama said in his excellent answer about using layout anchors. It's probably as clear and elegant as you get with the native API, but please note that it requires iOS 9 and later.
For the purpose of code organization, I'd like to suggest instead of looking for terser code, organize your code into methods and/or extensions, for example:
extension UIView {
func constraintsAligningAllEdges(toView view2: UIView) -> [NSLayoutConstraint] {
return [ topAnchor.constraintEqualToAnchor(view2.topAnchor),
bottomAnchor.constraintEqualToAnchor(view2.bottomAnchor),
leadingAnchor.constraintEqualToAnchor(view2.leadingAnchor),
trailingAnchor.constraintEqualToAnchor(view2.trailingAnchor) ]
}
}
Using it would look like this:
NSLayoutConstraint.activateConstraints(greenView.constraintsAligningAllEdges(toView: redView))
...or perhaps make that a class function extension for NSLayoutConstraint.
On the topic of Visual Format Language, vacawama also brought up a very interesting use of the layout options. Here's another possible way of achieving the same goal:
let views = ["greenView" : greenView, "redView" : redView]
NSLayoutConstraint.activateConstraints(
NSLayoutConstraint.constraintsWithVisualFormat("H:[greenView]-(0#1)-[redView]", options: [.AlignAllTop, .AlignAllBottom], metrics: nil, views: views) +
NSLayoutConstraint.constraintsWithVisualFormat("V:[greenView]-(0#1)-[redView]", options: [.AlignAllLeading, .AlignAllTrailing], metrics: nil, views: views)
)
What I've done here is instead of introducing extra views, I've introduced two extraneous constraints, one between green bottom & red top, another one between green trailing & top leading. They both have the lowest priority of 1 however, so as you would normally have other constraints deciding the size/position of either view, they should cause no harm.
Is this somewhat more elegant? I don't know, but personally I think layout anchors make more sense to me, and third-party frameworks like SnapKit are also fine options.
The Visual Format Language can't be used for every possible case. I believe aligning the two views to each other is such a case.
I would suggest you use layout anchors instead, like this:
override func viewDidLoad() {
super.viewDidLoad()
let redView = UIView()
redView.backgroundColor = .redColor()
redView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(redView)
let greenView = UIView()
greenView.backgroundColor = .greenColor()
greenView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(greenView)
let redHoriz = NSLayoutConstraint.constraintsWithVisualFormat("H:|-20-[redView]-20-|", options: [], metrics: nil, views: ["redView": redView])
NSLayoutConstraint.activateConstraints(redHoriz)
let redVert = NSLayoutConstraint.constraintsWithVisualFormat("V:|-50-[redView]-50-|", options: [], metrics: nil, views: ["redView": redView])
NSLayoutConstraint.activateConstraints(redVert)
// Set greenView to occupy same space as the redView
greenView.leftAnchor.constraintEqualToAnchor(redView.leftAnchor).active = true
greenView.rightAnchor.constraintEqualToAnchor(redView.rightAnchor).active = true
greenView.topAnchor.constraintEqualToAnchor(redView.topAnchor).active = true
greenView.bottomAnchor.constraintEqualToAnchor(redView.bottomAnchor).active = true
}
The Visual Format Language allows you to align parts of views. For instance, you can align the tops of bottoms of two views by passing [.AlignAllTop, .AlignAllBottom] as the options argument of constraintsWithFormat. But, you can only align values that are perpendicular to the direction of the visual format. So, if you are specifying a horizontal layout, then you can align the tops and bottoms of the views. If you are specifying a vertical layout, then you can align the lefts and rights.
You can align two views by introducing two additional views. In the example below, the blue view picks up the top and bottom alignment of the red view, and the yellow view picks up the left and right alignment of the red view. Then the green view gets aligned to the top and bottom of the blue view and the left and right of the yellow view, thus it is aligned to the red view.
Finally, hiding the blue and yellow views leaves you with the setup you desire.
let redView = UIView()
redView.backgroundColor = .redColor()
redView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(redView)
let greenView = UIView()
greenView.backgroundColor = .greenColor()
greenView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(greenView)
let blueView = UIView()
blueView.translatesAutoresizingMaskIntoConstraints = false
blueView.backgroundColor = .blueColor()
blueView.hidden = true
view.addSubview(blueView)
let yellowView = UIView()
yellowView.translatesAutoresizingMaskIntoConstraints = false
yellowView.backgroundColor = .yellowColor()
yellowView.hidden = true
view.addSubview(yellowView)
let redHoriz = NSLayoutConstraint.constraintsWithVisualFormat("H:|-80-[redView]-80-|", options: [], metrics: nil, views: ["redView": redView])
NSLayoutConstraint.activateConstraints(redHoriz)
let redVert = NSLayoutConstraint.constraintsWithVisualFormat("V:|-80-[redView]-80-|", options: [], metrics: nil, views: ["redView": redView])
NSLayoutConstraint.activateConstraints(redVert)
let blueHorizVert = NSLayoutConstraint.constraintsWithVisualFormat("H:[redView][blueView]|", options: [.AlignAllTop, .AlignAllBottom], metrics: nil, views: ["redView": redView, "blueView": blueView])
NSLayoutConstraint.activateConstraints(blueHorizVert)
let yellowHorizVert = NSLayoutConstraint.constraintsWithVisualFormat("V:[redView][yellowView]|", options: [.AlignAllLeft, .AlignAllRight], metrics: nil, views: ["redView": redView, "yellowView": yellowView])
NSLayoutConstraint.activateConstraints(yellowHorizVert)
let greenTopBottom = NSLayoutConstraint.constraintsWithVisualFormat("H:[greenView][blueView]|", options: [.AlignAllTop, .AlignAllBottom], metrics: nil, views: ["greenView": greenView, "blueView": blueView])
NSLayoutConstraint.activateConstraints(greenTopBottom)
let greenLeftRight = NSLayoutConstraint.constraintsWithVisualFormat("V:[greenView][yellowView]|", options: [.AlignAllLeft, .AlignAllRight], metrics: nil, views: ["greenView": greenView, "yellowView": yellowView])
NSLayoutConstraint.activateConstraints(greenLeftRight)
So you can do it with VFL, but it isn't elegant. I would suggest you stick with using anchors.
Desired View:
In Storyboard I have two labels with the blue background that I am creating in Autolayout. Their position will never change. Next, I would like to add anywhere from 1 to 10 labels in code in cellForRowAtIndexPath below the blue background labels.
I am struggling to align the the labels added in code (brown background) with the ones created in Autolayout (blue background).
Below is my failed attempt:
Two approaches that failed:
In cellForRowAtIndexPath get the frame of "B AutoLayout Static Label" and use the X position for Dynamic Labels. Did not work.
Adding constraints also did not work -- perhaps I am not adding the constraints correctly.
Here is the code:
class TableViewController: UITableViewController {
var cellHeight = [Int: CGFloat]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 85.0
self.tableView.rowHeight = UITableViewAutomaticDimension
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CustomCell
let xLocation = cell.labelBStatic.frame.origin.x
var yLocation = cell.labelBStatic.frame.origin.y
let height = cell.labelBStatic.frame.size.height
var startYLocation = yLocation + height + 20
var i = 0
if indexPath.row % 2 == 0 {
i = 5
} else {
i = 7
}
while i < 10 {
let aLabel = UILabel()
aLabel.backgroundColor = UIColor.orangeColor()
aLabel.text = "Label # \(i)"
cell.contentView.addSubview(aLabel)
addConstraints(aLabel, verticalSpacing: startYLocation)
startYLocation += 20
i++
}
print(startYLocation)
cellHeight[indexPath.row] = startYLocation
return cell
}
func addConstraints(labelView: UILabel, verticalSpacing: CGFloat) {
// set Autoresizing Mask to false
labelView.translatesAutoresizingMaskIntoConstraints = false
//make dictionary for views
let viewsDictionary = ["view1": labelView]
//sizing constraints
let view1_constraint_H:Array = NSLayoutConstraint.constraintsWithVisualFormat("H:[view1(>=50)]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: viewsDictionary)
labelView.addConstraints(view1_constraint_H)
//position constraints
let view_constraint_H:NSArray = NSLayoutConstraint.constraintsWithVisualFormat("H:|-15-[view1]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: viewsDictionary)
let view_constraint_V:NSArray = NSLayoutConstraint.constraintsWithVisualFormat("V:|-\(verticalSpacing)-[view1]", options: NSLayoutFormatOptions.AlignAllLeading, metrics: nil, views: viewsDictionary)
view.addConstraints(view_constraint_H as! [NSLayoutConstraint])
view.addConstraints(view_constraint_V as! [NSLayoutConstraint])
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if let height = cellHeight[indexPath.row] {
return height
}
return 0
}
Below is the Storyboard setup (Both labels are centered horizontally):
Question: How can I get my dynamic labels that I am creating in cellForRowAtIndexPath left align with my static labels that were created in Storyboard to match my desired view on top?
Just for demonstration purpose i have added one label programatically, you can add as much as labels you want, just add the constraints properly, also you need to add bottomSpace constraint to the last label inside cell so that your cell will auto resize as per the label height.
Follow the steps i have done to achieve what you want:
Access the B AutoLayout Static Label in cellForRowAtIndexPath: using tag or outlet if you have subclassed UITableViewCell and have created outlet.
let labelBAutolayoutStaticLabel = cell?.viewWithTag(20)
Create the label programatically as below and set translatesAutoresizingMaskIntoConstraints to false,
let labelDynamicLabel = UILabel()
labelDynamicLabel.backgroundColor = UIColor.orangeColor()
labelDynamicLabel.text = "A Dynamic Label"
labelDynamicLabel.translatesAutoresizingMaskIntoConstraints = false
cell?.contentView.addSubview(labelDynamicLabel)
You need to create two constraint, one is for TopSpace and second is LeadingSpace as below,
let leadingSpaceConstraint: NSLayoutConstraint = NSLayoutConstraint(item: labelDynamicLabel, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: labelBAutolayoutStaticLabel, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: 0);
let topSpaceConstraint: NSLayoutConstraint = NSLayoutConstraint(item: labelDynamicLabel, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: labelBAutolayoutStaticLabel, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 10); //Constant is the spacing between
Add constraint to your cell's contentView as below,
cell?.contentView.addConstraint(leadingSpaceConstraint)
cell?.contentView.addConstraint(topSpaceConstraint)
That's it.
And here is the result,
Here is the full code for cellForRowAtIndexPath:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("cell")
let labelBAutolayoutStaticLabel = cell?.viewWithTag(20)
let labelDynamicLabel = UILabel()
labelDynamicLabel.backgroundColor = UIColor.orangeColor()
labelDynamicLabel.text = "A Dynamic Label"
labelDynamicLabel.translatesAutoresizingMaskIntoConstraints = false
cell?.contentView.addSubview(labelDynamicLabel)
let leadingSpaceConstraint: NSLayoutConstraint = NSLayoutConstraint(item: labelDynamicLabel, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: labelBAutolayoutStaticLabel, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: 0);
let topSpaceConstraint: NSLayoutConstraint = NSLayoutConstraint(item: labelDynamicLabel, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: labelBAutolayoutStaticLabel, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 10);
cell?.contentView.addConstraint(leadingSpaceConstraint)
cell?.contentView.addConstraint(topSpaceConstraint)
return cell!
}
Edit/Update:
If you have to set bottomSpace constraint to the last label (label which is at bottom of cell), there are two way
Use NSLayoutConstraint as below:
let bottomSpaceConstraint: NSLayoutConstraint = NSLayoutConstraint(item: labelDynamicLabel, attribute: NSLayoutAttribute.BottomMargin, relatedBy: NSLayoutRelation.Equal, toItem: cell.contentView, attribute: NSLayoutAttribute.BottomMargin, multiplier: 1, constant: -8)
cell.contentView.addConstraint(bottomSpaceConstraint)
Using Visual Format Language as below,
let views = ["cell": cell, "labelDynamicLabel": labelDynamicLabel, "labelBAutolayoutStaticLabel": labelBAutolayoutStaticLabel]
let verticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat("V:[labelBAutolayoutStaticLabel]-[labelDynamicLabel]-|", options: [], metrics: nil, views: views)
cell.contentView.addConstraints(verticalConstraints)
If you set constraint using VFL make sure you remove topSpaceConstraint
//cell.contentView.addConstraint(topSpaceConstraint)
What this "V:[labelBAutolayoutStaticLabel]-[labelDynamicLabel]-|" string mean is,
labelDynamicLabel should have TopSpace to labelBAutolayoutStaticLabel with standardSpacing (8pts) and labelDynamicLabel should have bottom space to SuperView with standardSpacing. ('-|' indicates the standard space to superView)
I am stuck on this and no other question on SO helped me...
I have view controller, which implements UIGestureRecognizerDelegate. There are a few views, one of them is contentView, and inside that contentView, there is a UIScrollView. Also inside that scroll view, there is a scrollContentView, which is simple UIView, but it contains more subviews which I didn't include in code sample just because of space. Everything works fine (after a lot of time), but it looks like no tap gestures are propagated throw UIScrollView to the child view..
I am not using storyboard.
I tried everything, any help would be appreciated.
class MyController: PopoverController, UITableViewDelegate, UIGestureRecognizerDelegate
{
var scrollPane: UIScrollView!
var myContentView: UIView!
var bottomPane: EditOrderItemBottomPaneView!
var quantityPaneView: QuantityPaneView!
var optionsTable: OptionsTableView!
var modificationsTable: ModificationsTableView!
var specialPricingsTable: SpecialPricingsTableView!
override func viewDidLoad()
{
super.viewDidLoad()
categoriesModel = DIContainer.get().getCategoriesModel()
activeOrderModel = DIContainer.get().getActiveOrderModel()
modificationModel = DIContainer.get().getProductModificationsModel()
orderItem = popoverModel.get("orderItem") as! OrderItem!
let category: ProductCategory = categoriesModel.getCategoryById(orderItem.categoryId)!
titleLabel.text = category.name
// scroll view
scrollPane = UIScrollView()
scrollPane.translatesAutoresizingMaskIntoConstraints = false
scrollPane.delegate = self
contentView.addSubview(scrollPane)
// bottom pane
bottomPane = EditOrderItemBottomPaneView()
contentView.addSubview(bottomPane)
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[scrollView][bottomView]|", options: [], metrics: nil, views: ["scrollView": scrollPane, "bottomView": bottomPane]))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[scrollView]|", options: [], metrics: nil, views: ["scrollView": scrollPane]))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[bottomView]|", options: [], metrics: nil, views: ["bottomView": bottomPane]))
// scroll content view
myContentView = UIView()
myContentView.translatesAutoresizingMaskIntoConstraints = false
scrollPane.addSubview(myContentView)
scrollPane.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[content]|", options: [], metrics: nil, views: ["content": myContentView]))
scrollPane.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[content]|", options: [], metrics: nil, views: ["content": myContentView]))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[content]|", options: [], metrics: nil, views: ["content": myContentView]))
contentView.addConstraint(NSLayoutConstraint(item: myContentView, attribute: NSLayoutAttribute.Trailing,
relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Trailing,
multiplier: 1, constant: 0))
contentView.addConstraint(NSLayoutConstraint(item: myContentView, attribute: NSLayoutAttribute.Leading,
relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Leading,
multiplier: 1, constant: 0))
// quantity pane
quantityPaneView = QuantityPaneView()
myContentView.addSubview(quantityPaneView)
// options table
let optionsDataSource: OrderItemOptionsTableViewDataSource = DIContainer.get().getOrderItemOptionsTableViewDataSource()
optionsDataSource.orderItem = orderItem
if optionsDataSource.getNumberOfOptions() > 0 {
optionsTable = OptionsTableView(delegate: DIContainer.get().getOrderItemOptionsTableViewDelegate(),
dataSource: optionsDataSource, orderItem: orderItem)
myContentView.addSubview(optionsTable)
}
// modifications table
modificationsTable = ModificationsTableView(
delegate: DIContainer.get().getOrderItemModificationsTableViewDelegate(),
dataSource: DIContainer.get().getOrderItemModificationsTableViewDataSource(),
orderItem: orderItem)
myContentView.addSubview(modificationsTable)
// special pricing table
specialPricingsTable = SpecialPricingsTableView(
delegate: DIContainer.get().getOrderItemSpecialPricingsTableViewDelegate(),
dataSource: DIContainer.get().getOrderItemSpecialPricingsTableViewDataSource(),
orderItem: orderItem)
myContentView.addSubview(specialPricingsTable)
var views = [
"quantityPane": quantityPaneView,
"modifications": modificationsTable,
"specialPricings": specialPricingsTable
]
myContentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-25-[quantityPane]-25-|", options: [], metrics: nil, views: views))
myContentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-25-[modifications]-25-|", options: [], metrics: nil, views: views))
myContentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-25-[specialPricings]-25-|", options: [], metrics: nil, views: views))
if optionsDataSource.getNumberOfOptions() > 0 {
views["options"] = optionsTable
myContentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-25-[options]-25-|", options: [], metrics: nil, views: views))
myContentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[quantityPane]-25-[options]-25-[modifications]-25-[specialPricings]", options: [], metrics: nil, views: views))
}
else {
myContentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[quantityPane]-25-[modifications]-25-[specialPricings]", options: [], metrics: nil, views: views))
}
}
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
var h: CGFloat = 0.0
for view: UIView in myContentView.subviews {
h += view.frame.size.height;
}
scrollPane.contentSize = CGSizeMake(myContentView.frame.size.width, h)
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool
{
return true
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool
{
if touch.view != scrollPane {
return false
}
return true
}
}
Implement UIGestureRecognizer delegate method.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if (touch.view != yourScrollView) {
return NO;
}
return YES;
}
I realized that the whole screen was designed really badly. I didn't solve the issue, but I reimplemented the whole controller. I still think that it is possible to solve that problem, but there is much easier way.
The point is, that I had more table views inside that scroll content view. But of course, it is possible to create just one table with more sections. Side effect of this would be that that one table view itself is scrollable and so there is no need for using custom UIScrollView.
So for everyone beginning with iOS development, use UITableView with more sections wherever possible.
Im working on an app and came across a problem that I cant seem to solve.
I am making a uiview with labels and images inside it. The content needs to be centered inside the view.
The problem is that the label will hold different lenght texts and the view itself will have different width depending on where it is used.
Here is how it should look:
And here with longer text:
As you can see there should be 1 label to the left, one uiimage in the middle and another label to the right all centered to the middle even though the text length could be different.
This is what I have so far in code. I need to do this programatically if possible. Im running a method to create the button depending on a value.
func cost(cost:Int){
costView = UIView()
costView?.setTranslatesAutoresizingMaskIntoConstraints(false)
self.costView?.clipsToBounds = true
self.addSubview(costView!)
self.costLabel.frame = CGRectMake(0, 0, 60, self.bounds.size.height)
self.costLabel.textAlignment = NSTextAlignment.Right
self.costLabel.textColor = UIColor.whiteColor()
self.costLabel.font = Services.exoFontWithSize(16)
self.costLabel.text = String(cost)
costView?.addSubview(costLabel)
self.costBrainImageView = UIImageView(frame: CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.height))
self.costBrainImageView?.image = Graphics.maskImageNamed("CommonMediumBrain", color: UIColor.whiteColor())
self.costBrainImageView?.contentMode = UIViewContentMode.ScaleAspectFill
costView?.addSubview(costBrainImageView!)
self.label.adjustsFontSizeToFitWidth = true
self.label.setTranslatesAutoresizingMaskIntoConstraints(false)
self.label.numberOfLines = 1
self.label.minimumScaleFactor = 0.20
self.label.textAlignment = NSTextAlignment.Right
self.label.font = Services.exoFontWithSize(20)
//Constraints
var viewsDict = Dictionary <String, UIView>()
viewsDict["label"] = label
viewsDict["brainView"] = costView
self.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[label]|", options: nil, metrics: nil, views: viewsDict))
self.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[brainView]|", options: nil, metrics: nil, views: viewsDict))
self.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat(
"H:|-[label]-5-[brainView]-|", options: NSLayoutFormatOptions.AlignAllCenterX, metrics: nil, views: viewsDict))
}
For the moment this breaks since this line
NSLayoutFormatOptions.AlignAllCenterX
'Unable to parse constraint format:
Options mask required views to be aligned on a horizontal edge, which is not allowed for layout that is also horizontal.
H:|-[label]-5-[brainView]-|
If i remove that everything is aligned to the left and not centered but im not sure this is the right way to accomplish what i want to do.
Any clues on how to solve this?
Thanks for any help
Firstly, change .AlignAllCenterX to .AlignAllCenterY, the reason is that"H:|-[label]-5-[brainView]-|" specifies how views position horizontally, you want label and brainView to have the same center Y position.
Secondly, make a subview that contains all 3 views, then center this subview inside the black rectangle. You can use costView as the subview.
Below is some modified code based on your existing code.
costView!.addSubview(label)
//Constraints
var viewsDict = Dictionary <String, UIView>()
viewsDict["label"] = label
viewsDict["brainView"] = costBrainImageView
viewsDict["costLabel"] = costLabel
costView!.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[label]|", options: nil, metrics: nil, views: viewsDict))
costView!.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[brainView]|", options: nil, metrics: nil, views: viewsDict))
costView!.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[costLabel]|", options: nil, metrics: nil, views: viewsDict))
costView!.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat(
"H:|-[label]-5-[brainView]-5-[costLabel]-|", options: NSLayoutFormatOptions.AlignAllCenterY, metrics: nil, views: viewsDict))
costView!.backgroundColor = UIColor.redColor()
// center costView inside self
let centerXCons = NSLayoutConstraint(item: costView!, attribute: .CenterX, relatedBy: .Equal, toItem: self, attribute: .CenterX, multiplier: 1, constant: 0);
let centerYCons = NSLayoutConstraint(item: costView!, attribute: .CenterY, relatedBy: .Equal, toItem: self, attribute: .CenterY, multiplier: 1, constant: 0);
self.addConstraints([centerXCons, centerYCons])
I'm trying to do a simple layout programmatically and I'm missing something simple, or have something out of place, I think. The following ViewController should center the label in the super view. However, it crashes with this trimmed message: The view hierarchy is not prepared for the constraint ... When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled... View not found in container hierarchy: ... That view's superview: NO SUPERVIEW The other SO questions with this error message are using nibs for the most part, and I'm tring to avoid that, or use Obj-C instead of swift. This question deals with the topic a bit but is a bit old.
Can anyone point me in the right direction?
class ViewController: UIViewController {
let label1 = UILabel() as UILabel
func layoutView(){
label1.text = "Click to see device configuration"
label1.setTranslatesAutoresizingMaskIntoConstraints(false)
view.addSubview(label1)
let viewsDictionary = ["label1":label1]
let label1_H:NSArray = NSLayoutConstraint.constraintsWithVisualFormat("H:|-[label1]-|",
options: NSLayoutFormatOptions(0),
metrics: nil,
views: viewsDictionary)
let label1_V:NSArray = NSLayoutConstraint.constraintsWithVisualFormat("V:|-[label1]-|",
options: NSLayoutFormatOptions(0),
metrics: nil, views:
viewsDictionary)
label1.addConstraints(label1_H) // Comment these 2 lines and it runs, but
label1.addConstraints(label1_V) // of course the label is upper left
}
override func viewDidLoad() {
super.viewDidLoad()
layoutView()
}
}
Those constraints are made between the label and its superview. The constraints should be added to that superview, not to the label.
You're almost there. Just replace the following lines...
label1.addConstraints(label1_H) // Comment these 2 lines and it runs, but
label1.addConstraints(label1_V) // of course the label is upper left
... with the following code:
view.addConstraints(label1_H) // Comment these 2 lines and it runs, but
view.addConstraints(label1_V) // of course the label is upper left
However, the constraints H:|-[label1]-|" and V:|-[label1]-|" are equivalent to H:|-8-[label1]-8-|" and V:|-8-[label1]-8-|" (see the iOS Developer Library for more details on default margins). Thus, those constraints are not meant to center your label. In fact, you will just have an enormous label that has 8 unit top, leading, trailing and bottom margins to the viewController's view.
Add the following line of code in your layoutView() method to see what I mean:
label1.backgroundColor = UIColor.orangeColor()
It can be OK but if you really want to center your label, you will have to use the following code:
func layoutView() {
label1.text = "Click to see device configuration"
label1.backgroundColor = UIColor.orangeColor()
//Set number of lines of label1 to more than one if necessary (prevent long text from being truncated)
label1.numberOfLines = 0
label1.setTranslatesAutoresizingMaskIntoConstraints(false)
view.addSubview(label1)
let xConstraint = NSLayoutConstraint(item: label1,
attribute: NSLayoutAttribute.CenterX,
relatedBy: NSLayoutRelation.Equal,
toItem: view,
attribute: NSLayoutAttribute.CenterX,
multiplier: 1.0,
constant: 0.0)
self.view.addConstraint(xConstraint)
let yConstraint = NSLayoutConstraint(item: label1,
attribute: NSLayoutAttribute.CenterY,
relatedBy: NSLayoutRelation.Equal,
toItem: view,
attribute: NSLayoutAttribute.CenterY,
multiplier: 1.0,
constant: 0)
self.view.addConstraint(yConstraint)
//Add leading and trailing margins if necessary (prevent long text content in label1 to be larger than screen)
let viewsDictionary = ["label1" : label1]
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-(>=10#750)-[label1]-(>=10#750)-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDictionary))
}