Why does my button change padding in the iOS simulator? - ios

I am trying to add a button to a view in my iOS app. In the Main.storyboard file the button appears as this:
However, whenever I start the simulator the button appears as:
If it helps, I have the button paired to a custom class that consists of:
import UIKit
class RoundedButton: UIButton {
override func awakeFromNib() {
super.awakeFromNib()
layer.borderWidth = 1
layer.borderColor = UIColor.blue.cgColor
contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
}
}
But I do not believe this is the cause of the issue. I think the problem stems from my lack of understanding of how scaling works in xCode but I am unable to find any resources on how to fix this issue.
Thanks!

Add constraints to your button and check. It will be perfect if you add the necessary constraints.

Constraints are used to tell devices where objects should be and their sizes, even with different devices, you can create a pattern. Remember you always have to configure X, Y, width and height so your elements are placed perfectly. Except for labels, which sometimes doesn't require width and height constraints.
For your button, adding constraints to align horizontally, vertically, width and height should work.

Add necessary constraints for the button. you can add in storyboard or programmatically.
In storyboard, you can add constraints like this:
For programmatically:
let horConstraint = NSLayoutConstraint(item: yourButton!, attribute: .centerX, relatedBy: .equal,
toItem: view, attribute: .centerX,
multiplier: 1.0, constant: 0.0)
let verConstraint = NSLayoutConstraint(item: yourButton!, attribute: .centerY, relatedBy: .equal,
toItem: view, attribute: .centerY,
multiplier: 1.0, constant: 0.0)
let widConstraint = NSLayoutConstraint(item: yourButton!, attribute: .width, relatedBy: .equal,
toItem: view, attribute: .width,
multiplier: 0.95, constant: 0.0)
let heiConstraint = NSLayoutConstraint(item: yourButton!, attribute: .height, relatedBy: .equal,
toItem: view, attribute: .height,
multiplier: 0.95, constant: 0.0)
view.addConstraints([horConstraint, verConstraint, widConstraint, heiConstraint])

Related

Wrong NSLayoutConstraint Causes Black Screen

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.

Auto layout not set properly when give equal width constrains to buttons

I see many solution and applied auto layout constrains but still i am not getting desired solution.
Current Result:
Constrains given to all button:
View Hierarchy:
Assuming that what do you want is letting the 3 buttons equal each others and filling the width of screen using the Interface Builder (with no code, i.e: not programmatically), this is a solution:
you don't need to add them in views. just follow these steps:
let's start with this:
note that the buttons don't have any constraints -yet-, just add them to the to the bottom of the view (or where ever you want to display them, for this solution, I will display them in bottom of the screen). Make sure that the 3 of them have the same size.
Adding constraints to the orange button:
add the following constraints:
leading, bottom space, and equal height.
Adding constraints to the blue button:
add the following constraints:
trailing, bottom space and equal height.
Adding constraints to the cyan button:
ctrl + drag from the cyan button to the orange button and add the following constraints: horizontal spacing and center vertically.
ctrl + drag from the cyan button to the blue button and add the following constraints: horizontal spacing.
bottom space and equal height.
So far so good, we are almost done!
Now, select the 3 buttons and add the following constraint: equal widths.
Your buttons should look like this:
Now, all you have to do is select each of the two horizontal spacing constraints and set their constants to 0 -from the size inspector-:
AND there you go:
I hope this helped, Cheers Up.
Sorry for solution in code.. .Swift 3.0:
let button1 = UIButton()
let button2 = UIButton()
let button3 = UIButton()
let buttons = [button1, button2, button3]
button1.backgroundColor = .red
button2.backgroundColor = .blue
button3.backgroundColor = .green
buttons.forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
}
buttons.forEach {
let cnt1 = NSLayoutConstraint(item: $0, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1, constant: 0)
let cnt2 = NSLayoutConstraint(item: $0, attribute: .height, relatedBy: .equal, toItem: view, attribute: .height, multiplier: 0.2, constant: 0)
view.addConstraints([cnt1, cnt2])
}
let cnt1 = NSLayoutConstraint(item: button1, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 0)
let cnt2 = NSLayoutConstraint(item: view, attribute: .trailing, relatedBy: .equal, toItem: button3, attribute: .trailing, multiplier: 1, constant: 0)
let cnt3 = NSLayoutConstraint(item: button2, attribute: .width, relatedBy: .equal, toItem: button1, attribute: .width, multiplier: 1, constant: 0)
let cnt4 = NSLayoutConstraint(item: button3, attribute: .width, relatedBy: .equal, toItem: button2, attribute: .width, multiplier: 1, constant: 0)
let cnt5 = NSLayoutConstraint(item: button2, attribute: .leading, relatedBy: .equal, toItem: button1, attribute: .trailing, multiplier: 1, constant: 0)
let cnt6 = NSLayoutConstraint(item: button3, attribute: .leading, relatedBy: .equal, toItem: button2, attribute: .trailing, multiplier: 1, constant: 0)
view.addConstraints([cnt1, cnt2, cnt3, cnt4, cnt5, cnt6])
Are you trying to lay out three buttons horizontally that have equal height and width? I think you can achieve this without using UIViews. I set constraints like as follows.
This is a simulator screenshot.

Why does iOS 8 hate my AutoLayout but iOS 9 loves it?

I am initialising a view infoScreen and adding it as a subview of the screen with its bottom, left and right constraints set like this:
let left = NSLayoutConstraint(item: infoScreen, attribute: .Left, relatedBy: .Equal, toItem: view, attribute: .LeftMargin, multiplier: 1, constant: 0)
let right = NSLayoutConstraint(item: infoScreen, attribute: .Right, relatedBy: .Equal, toItem: view, attribute: .RightMargin, multiplier: 1, constant: 0)
var yConstraint: NSLayoutConstraint!
if (point.y < halfOfScreen) {
yConstraint = NSLayoutConstraint(item: infoScreen, attribute: .Top, relatedBy: .Equal, toItem: highlightedView, attribute: .CenterY, multiplier: 1, constant: radius + stalkLength)
}
else {
// This gets called the first time round.
yConstraint = NSLayoutConstraint(item: infoScreen, attribute: .Bottom, relatedBy: .Equal, toItem: highlightedView, attribute: .CenterY, multiplier: 1, constant: -radius - stalkLength)
}
view.addConstraints([left, right, yConstraint])
as well as it's height being set.
Then after a button within infoScreen is clicked, I'm calling infoScreen.removeFromSuperview().
Then the same function is used to reinitialise infoScreen with different parameters, and add it to the screen. However, this time it has its top constraint set instead of the bottom constraint.
In iOS 9 this works perfectly, however in iOS 8, it acts as if the bottom constraint is still set and messes up the view. If I run the code so that it never has the bottom constraint set (essentially skipping over the first run of the initialisation function), then it works fine in iOS 8. What could be causing this?

How to programatically add constraints on uinavigationbar subview

I'm adding a subview to navigationbar , problem is that im unable to add constraints to it .Im getting crash like this
terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with items ; value: 0.000000> and > because they have no common ancestor. Does the constraint reference items in different view hierarchies? That's illegal.'
The code used is below
//create a slider and add it to the view
let slider = UISlider()
self.navigationController?.navigationBar.addSubview(slider)
//pin the slider 20 points from the left edge of the the superview
//from the left edge of the slider to the left edge of the superview
//superview X coord is at 0 therefore 0 + 20 = 20 position
let horizonalContraints = NSLayoutConstraint(item: slider, attribute:
.LeadingMargin, relatedBy: .Equal, toItem: view,
attribute: .LeadingMargin, multiplier: 1.0,
constant: 20)
//pin the slider 20 points from the right edge of the super view
//negative because we want to pin -20 points from the end of the superview.
//ex. if with of super view is 300, 300-20 = 280 position
let horizonal2Contraints = NSLayoutConstraint(item: slider, attribute:
.TrailingMargin, relatedBy: .Equal, toItem: view,
attribute: .TrailingMargin, multiplier: 1.0, constant: -20)
//pin 100 points from the top of the super
let pinTop = NSLayoutConstraint(item: slider, attribute: .Top, relatedBy: .Equal,
toItem: view, attribute: .Top, multiplier: 1.0, constant: 100)
//when using autolayout we an a view, MUST ALWAYS SET setTranslatesAutoresizingMaskIntoConstraints
//to false.
slider.translatesAutoresizingMaskIntoConstraints = false
slider.backgroundColor = UIColor.redColor()
//IOS 8
//activate the constrains.
//we pass an array of all the contraints
NSLayoutConstraint.activateConstraints([horizonalContraints, horizonal2Contraints,pinTop])
The above code works fine if i use the line view.addSubview(slider)
instead of
self.navigationController?.navigationBar.addSubview(slider)
But the idea is to add constraints on a subview on navigation bar .
Any thoughts are welcome
As the exception already stated, the navigationBar is not a subview of 'view'. It belongs to the navigationcontroller.
What you could do is to use the navbar's superview:
let slider = UISlider()
self.navigationController?.navigationBar.addSubview(slider)
let targetView = self.navigationController?.navigationBar.superview
let horizonalContraints = NSLayoutConstraint(item: slider, attribute:
.LeadingMargin, relatedBy: .Equal, toItem: targetView,
attribute: .LeadingMargin, multiplier: 1.0,
constant: 20)
let horizonal2Contraints = NSLayoutConstraint(item: slider, attribute:
.TrailingMargin, relatedBy: .Equal, toItem: targetView,
attribute: .TrailingMargin, multiplier: 1.0, constant: -20)
let pinTop = NSLayoutConstraint(item: slider, attribute: .Top, relatedBy: .Equal,
toItem: targetView, attribute: .Top, multiplier: 1.0, constant: 10)
slider.translatesAutoresizingMaskIntoConstraints = false
slider.backgroundColor = UIColor.redColor()
NSLayoutConstraint.activateConstraints([horizonalContraints, horizonal2Contraints,pinTop])
That removes the exception and might look like it does what you want, but it is definitely not a good solution. If you want the slider inside the navbar, add it to the navigationitem instead. If you want it bellow the navbar, add it to your View and set a constraint to the top layout guide.

Why is UILabel not wrapping text inspite of taking multiple lines when added programmatically?

Here is my code. self is a UITableViewCell.
self.contentView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.contentView.layoutMargins = UIEdgeInsets(top: 8.0, left: 8.0, bottom: 8.0, right: 8.0)
self.contentView.layoutMarginsDidChange()
self.titleView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.titleView.text = story.title
self.titleView.font = UIFont(name: "Times-Roman", size: 15.0)
self.titleView.lineBreakMode = NSLineBreakMode.ByWordWrapping
self.titleView.numberOfLines = 3
self.contentView.addSubview(self.titleView)
self.contentView.addConstraint(NSLayoutConstraint(item: self.titleView, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: self.contentView, attribute: NSLayoutAttribute.LeadingMargin, multiplier: 1.0, constant: 0.0))
self.contentView.addConstraint(NSLayoutConstraint(item: self.titleView, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self.contentView, attribute: NSLayoutAttribute.TopMargin, multiplier: 1.0, constant: 0.0))
self.contentView.addConstraint(NSLayoutConstraint(item: self.contentView, attribute: NSLayoutAttribute.TrailingMargin, relatedBy: NSLayoutRelation.GreaterThanOrEqual, toItem: self.titleView, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: 0.0))
self.contentView.addConstraint(NSLayoutConstraint(item: self.contentView, attribute: NSLayoutAttribute.BottomMargin, relatedBy: NSLayoutRelation.GreaterThanOrEqual, toItem: self.titleView, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 0.0))
self.contentView.updateConstraints()
self.setNeedsLayout()
self.layoutIfNeeded()
The output is that the long title takes 3 lines but the text only appears in single line and the rest two are blank. I have not specified any other height or width constraint. Will setting preferredMaxLayoutWidth help here? If yes then I want it to be automatic as I am targeting 8.0+. Am I missing to set some property of UILabel?
If you want an UITableViewCell that has a UILabel that has a dynamic height, you have to make both UILabel and the cell height dynamic.
You are on the right track with the label, but trying a bit too much.
The following minimal code works for me, though I usually set part of the stuff (constraints, number of lines) in Interface Builder
First the label configuration
In your UITableViewCell, in awakeFromNib or one of the inits if you are not using xibs:
override func awakeFromNib() {
super.awakeFromNib()
// Note: don't touch contentView's autoresizing masks
self.label.setTranslatesAutoresizingMaskIntoConstraints(false)
self.label.numberOfLines = 0 // 0 means number of lines is based on the text
self.contentView.addSubview(self.label)
// Simple helper function
func marginConstraint(a1: NSLayoutAttribute, a2: NSLayoutAttribute) {
self.contentView.addConstraint(NSLayoutConstraint(item: self.label, attribute: a1, relatedBy: NSLayoutRelation.Equal, toItem: self.contentView, attribute: a2, multiplier: 1.0, constant: 0.0))
}
marginConstraint(.Leading, .LeadingMargin)
marginConstraint(.Top, .TopMargin)
marginConstraint(.Trailing, .TrailingMargin)
marginConstraint(.Bottom, .BottomMargin)
}
Next the UITableViewCell height
In your UITableViewController, set rowHeight to UITableViewAutomaticDimension
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = UITableViewAutomaticDimension
}
If you have non-uniform cells, with some of them using fixed height, use
tableView(tableView:heightForRowAtIndexPath:) to return fixed heights to those that do not have dynamic height.
The issue seems to be related with your last 2 lines of code, where you set some autolayout constraints:
self.contentView.addConstraint(NSLayoutConstraint(item: self.contentView, attribute: NSLayoutAttribute.TrailingMargin, relatedBy: NSLayoutRelation.GreaterThanOrEqual, toItem: self.titleView, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: 0.0))
self.contentView.addConstraint(NSLayoutConstraint(item: self.contentView, attribute: NSLayoutAttribute.BottomMargin, relatedBy: NSLayoutRelation.GreaterThanOrEqual, toItem: self.titleView, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 0.0))
The first autolayout constraint is saying that self.contentview's trailing margin is greater or equal to self.titleView's trailing.
Try something like this instead:
self.contentView.addConstraint(NSLayoutConstraint(item: self.titleView, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: self.contentView, attribute: NSLayoutAttribute.TrailingMargin, multiplier: 1.0, constant: 0.0))
self.contentView.addConstraint(NSLayoutConstraint(item: self.titleView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self.contentView, attribute: NSLayoutAttribute.BottomMargin, multiplier: 1.0, constant: 0.0))
EDIT: I still think you should replace the last two constraints with the ones i provide in this answer because it is more readable that you are applying the constraints to titleView and attaching it to the trailing and bottom of the contentView just like you did in the first two constraints, but I've also found out that something else was missing.
Do this before adding any constraints:
self.titleView.removeConstraints(self.titleView.constraints())
self.contentView.removeConstraints(self.contentView.constraints())
If you don't do this, since your view seemed to be created through IB, you get auto layout constraints automatically, and then the constraints conflict between each other, getting this on the console:
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSIBPrototypingLayoutConstraint:0x7fd47878ef90 'IB auto generated at build time for view with fixed frame' H:|-(8)-UILabel:0x7fd47878c260'dsfsdf sdf sdf sdf dsf sd...' (Names: '|':UIView:0x7fd478783350 )>",
"",
"",
""
)

Resources