Good day!
I have a trouble with placing a bannerView in my app.
Its a static TableViewController, and the bannerView is supposed to be at the bottom of the screen. I did everything as it is said in Google Mobile SDK.
func positionBannerViewFullWidthAtBottomOfView(_ bannerView: UIView) {
view.addConstraints([
NSLayoutConstraint(item: bannerView,
attribute: .leading,
relatedBy: .equal,
toItem: view,
attribute: .leading,
multiplier: 1,
constant: 0),
NSLayoutConstraint(item: bannerView,
attribute: .trailing,
relatedBy: .equal,
toItem: view,
attribute: .trailing,
multiplier: 1,
constant: 0),
NSLayoutConstraint(item: bannerView,
attribute: .bottom,
relatedBy: .equal,
toItem: bottomLayoutGuide,
attribute: .top,
multiplier: 1,
constant: 0)
]
)
This is how it loads now, slightly below the view:
My guess problem is in the last Constraint, bottomLayoutGuide seems to be leading somewhere when it should not. When I remove last constraint it appears right below NavigationBar.
This problem does not occur on iOS 11+, only on previous versions.
Related
When I first load my calendar, the layout become misaligned (refer to image 1). After physically rotating my device to landscape and back to portrait, it fix itself. (refer to image 2)
Anyone have any idea how to fix this issue? I do not know where to start to find the issue that caused this to happen.
Side note: (not sure if this have anything to do with the issue.)
the page before this also have another FSCalendar.
this affected fscalendar is inside a scrollview with constraint set to height = 0.4 of superview, width with left right margin of 8, and center x align to superview.
I had the same issue (and just like for you, rotating the device would fix it).
I was able to fix my problem by changing the layout constraints and setting the FSCalendar's translatesAutoresizingMaskIntoConstraints to false (I had created the FSCalendar programmatically).
In the end, I put the FSCalendar inside another UIView and set it up like this:
calendarView.translatesAutoresizingMaskIntoConstraints = false
calendarContainer.addSubview(calendarView)
[NSLayoutConstraint(item: calendarView, attribute: .top, relatedBy: .equal, toItem: calendarContainer, attribute: .top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: calendarView, attribute: .bottom, relatedBy: .equal, toItem: calendarContainer, attribute: .bottom, multiplier: 1, constant: 0),
NSLayoutConstraint(item: calendarView, attribute: .leading, relatedBy: .equal, toItem: calendarContainer, attribute: .leading, multiplier: 1, constant: 0),
NSLayoutConstraint(item: calendarView, attribute: .trailing, relatedBy: .equal, toItem: calendarContainer, attribute: .trailing, multiplier: 1, constant: 0)
].forEach({ $0.isActive = true })
In Swift, I am trying to add a Google Smart Ad Banner using NSLayoutConstraints at the top of the view, but below the status bar by 14 points. I've been trying for ages now with various different attributes and top or topLayoutGuide.
Link to Image
let xConstraint = NSLayoutConstraint(item: bannerView, attribute: .centerX,
relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1, constant: 0)
let pinTop = NSLayoutConstraint(item: bannerView, attribute: .top,
relatedBy: .equal, toItem: self.topLayoutGuide, attribute: .top, multiplier: 1, constant: 14)
self.view.addSubview(bannerView)
self.view.addConstraint(xConstraint)
self.view.addConstraint(pinTop)
You’re pinning the banner to the top of the topLayoutGuide, but what you really want is the bottom of the topLayoutGuide, so that it will start below the status bar:
let pinTop = NSLayoutConstraint(item: bannerView,
attribute: .top,
relatedBy: .equal,
toItem: self.topLayoutGuide,
attribute: .bottom,
multiplier: 1,
constant: 14)
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?
I'm new to AutoLayout and would like to display my UITextField at 100% width with a consistent 15px left and right margin, like so:
Typically I would do this using CGRect, setting the width to the containing view's width minus 30px, then offset the left side by 15px:
searchTextField.frame = CGRectMake(15, 0, view.frame.width - 30, 50)
But I'd like to learn AutoLayout for this sort of thing, since it's the way to go these days. I should note that I am doing everything programmatically -- no Storyboards here.
I'd love it if someone could help me out!
Update
Wow! Thank you for all the responses. I believe all of them would achieve what I'm trying to do, but there can only be one :)
Usually I use for this cocoapod that is dealing with constraints, but if you need pure apple solution documentation states:
You have three choices when it comes to programmatically creating
constraints: You can use layout anchors, you can use the
NSLayoutConstraint class, or you can use the Visual Format Language.
Approach with NSLayoutConstraints in your case would be:
NSLayoutConstraint(item: textField, attribute: .Leading, relatedBy: .Equal, toItem: parentView, attribute: .LeadingMargin, multiplier: 1.0, constant: 15.0).active = true
NSLayoutConstraint(item: textField, attribute: .Trailing, relatedBy: .Equal, toItem: parentView, attribute: .TrailingMargin, multiplier: 1.0, constant: -15.0).active = true
NSLayoutConstraint(item: textField, attribute: .Top, relatedBy: .Equal, toItem: parentView, attribute: .TopMargin, multiplier: 1.0, constant: 50.0).active = true
Remember that if you don't have any constraints in the view, they would be added automatically and you'll have to deal with them and conflicts that would be created by adding new constraints on runtime. To avoid this you could either create textField manually and add it to the view or set constraints with low priority in the Interface Builder .
Assuming the parent of the text field is view, do this after view.addSubview(searchTextField):
NSLayoutConstraint.activateConstraints([
searchTextField.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor, constant: 15),
searchTextField.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor, constant: -15),
])
Use this code:
let topConstraint = NSLayoutConstraint(item: textField, attribute: NSLayoutAttribute.Top, relatedBy: .Equal, toItem: self.view, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 0)
let trailingConstraint = NSLayoutConstraint(item: textField, attribute: NSLayoutAttribute.Trailing, relatedBy: .Equal, toItem: self.view, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: 15)
let leadingConstraint = NSLayoutConstraint(item: textField, attribute: NSLayoutAttribute.Leading, relatedBy: .Equal, toItem: self.view, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 15)
let heightConstraint = NSLayoutConstraint(item: textField, attribute: NSLayoutAttribute.Height, relatedBy: .Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 50)
self.view.addConstraint(topConstraint )
self.view.addConstraint(trailingConstraint)
self.view.addConstraint(leadingConstraint)
self.view.addConstraint(heightConstraint)
Set the constraints in storyboard.
Click on the text field then click on in the bottom left. From there you can choose constraints like that.
To use Auto Layout, you need to define constraints for your text field.Here, I have created four constraints(Leading, Trailing, Top and Height) related to its superview.
func addLabelConstraints(superView:UIView) {
let leading = NSLayoutConstraint(item: searchTextField, attribute: .Leading, relatedBy: .Equal, toItem: superView, attribute: .Leading, multiplier: 1, constant: 15)
superview!.addConstraint(leading)
let trailing = NSLayoutConstraint(item: searchTextField, attribute: .Trailing, relatedBy: .Equal, toItem: superView, attribute: .Trailing, multiplier: 1, constant: 15)
superView.addConstraint(trailing)
let top = NSLayoutConstraint(item: searchTextField, attribute: .Top, relatedBy: .Equal, toItem: superView, attribute: .Top, multiplier: 1, constant: 0)
superView.addConstraint(top)
let height = NSLayoutConstraint(item: searchTextField, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .Height, multiplier: 0, constant: 50)
superView.addConstraint(height)
}
I have a UITableView's tableHeaderView property that contains an UIImage, a single-line UILabel and then a multi-line UILabel. The constraints are setup as follows:
self.addConstraints([
// App icon constraints
NSLayoutConstraint(item: self.image, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: 8),
NSLayoutConstraint(item: self.image, attribute: .CenterX, relatedBy: .Equal, toItem: self, attribute: .CenterX, multiplier: 1, constant: 0),
NSLayoutConstraint(item: self.image, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 128),
NSLayoutConstraint(item: self.image, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 128),
// App name label constraints
NSLayoutConstraint(item: self.appNameLabel, attribute: .Top, relatedBy: .Equal, toItem: self.image, attribute: .Bottom, multiplier: 1, constant: 8),
NSLayoutConstraint(item: self.appNameLabel, attribute: .Leading, relatedBy: .Equal, toItem: self, attribute: .Leading, multiplier: 1, constant: 16),
NSLayoutConstraint(item: self.appNameLabel, attribute: .Trailing, relatedBy: .Equal, toItem: self, attribute: .Trailing, multiplier: 1, constant: -16),
// Description label constrains
NSLayoutConstraint(item: self.descriptionLabel, attribute: .Top, relatedBy: .Equal, toItem: self.appNameLabel, attribute: .Bottom, multiplier: 1, constant: 8),
NSLayoutConstraint(item: self.descriptionLabel, attribute: .Leading, relatedBy: .Equal, toItem: self, attribute: .Leading, multiplier: 1, constant: 16),
NSLayoutConstraint(item: self.descriptionLabel, attribute: .Trailing, relatedBy: .Equal, toItem: self, attribute: .Trailing, multiplier: 1, constant: -16),
NSLayoutConstraint(item: self.descriptionLabel, attribute: .Bottom, relatedBy: .Equal, toItem: self, attribute: .Bottom, multiplier: 1, constant: -8)
])
This produces the following:
I can rotate the device and the label will resize to the correct size thanks to the following code:
override func layoutSubviews() {
self.updateDescriptionLabelPMLW()
super.layoutSubviews()
}
private func updateDescriptionLabelPMLW() {
// -32 for the 16 padding on the left and right
let newValue = self.frame.width - 32
if self.descriptionLabel.preferredMaxLayoutWidth != newValue {
self.descriptionLabel.preferredMaxLayoutWidth = newValue
self.descriptionLabel.sizeToFit()
}
}
Despite this working on iOS 7.0-9.0 (which is only slightly short of a miracle), when I use the Slide Over -> Split View feature of the iPad Air 2 (simulator) the label is truncated to a single line when the slid-over app is made in the "split" app, as seen in the following screenshot:
Note that this only happens in landscape, and can be "fixed" by rotating the device.
This ended up being fixed by updating the updateDescriptionLabelPMLW method to:
private func updateDescriptionLabelPMLW() {
/*
iOS 7
Tested on:
- iPad 2 (7.0.6)
- iPhone 4 (7.1.1)
iOS 8.1
Tested on:
- iPhone 5 (sim)
- iPad Retina (sim)
iOS 9
Tested on:
- iPhone 6
- iPad Air 2
Does not work on iPad Air 2 when sliding in an app (label is truncated)
*/
// -32 for the 16 padding on the left and right
let newValue = self.frame.width - 32
if self.descriptionLabel.preferredMaxLayoutWidth != newValue {
self.descriptionLabel.preferredMaxLayoutWidth = newValue
self.descriptionLabel.sizeToFit()
// Check for iOS 9 devices in landscape
// This should also check for iPads (specifically iPad Air 2)
if #available(iOS 9.0, *) {
if UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation) {
self.superview?.setNeedsLayout()
self.superview?.layoutIfNeeded()
}
}
}
}
It may not be perfect, but it's fixed the bug!