I'm trying to add some kind of overlay menu on a UITableViewController. My first goal is to get a background view to be displayed at the bottom of the screen on a very barren controller. I'm doing this programmatically using the following code in the callback for a bar button, i.e. called after the original layout has occurred:
print("self.tableView.frame=\(self.tableView.frame)")
let settingsView = UIView()
settingsView.translatesAutoresizingMaskIntoConstraints = false
settingsView.backgroundColor = UIColor.cyan.withAlphaComponent(0.5)
self.view.addSubview(settingsView)
self.view.addConstraints([
NSLayoutConstraint(item: settingsView, attribute: .bottom, relatedBy: .equal, toItem: self.tableView, attribute: .bottom, multiplier: 1, constant: 50),
NSLayoutConstraint(item: settingsView, attribute: .leading, relatedBy: .equal, toItem: self.tableView, attribute: .leading, multiplier: 1, constant: 0),
NSLayoutConstraint(item: settingsView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: self.view.frame.width),
//NSLayoutConstraint(item: settingsView, attribute: .trailing, relatedBy: .equal, toItem: self.tableView, attribute: .trailing, multiplier: 1, constant: 0),
NSLayoutConstraint(item: settingsView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 100)
])
self.view.setNeedsLayout()
But the result doesn't look like I expected: instead of being at the bottom of the screen, the bottom of my view is actually aligned with the top margin (hence the 50 value to see it poke through, you can see the cyan color through the navigation bar on the picture attached). Why is that? If I change the first constraint to align tops instead of bottoms, it works fine (but at the top of course).
Also, I'm surprised that I have to replace my fourth constraint (the one that is commented out, supposedly to align the right edge of the view with the right edge of the screen) with the third one (which sets the width, but somehow seems less clean). If I use the fourth one, then the view disappears completely (I'm guessing it gets a width of 0). What's the reason for that?
The print command at the beginning ensures that the tableView has the right size:
self.tableView.frame=(0.0, 0.0, 375.0, 667.0)
It all seems trivial enough so there's definitely something I'm not seeing here...
So the issue here really seems to be the UITableView(Controller), since these constraints work fine with a normal UIView(Controller). The UINavigationController around it doesn't play a role here. For some reason, it seems like the .bottom and .trailing anchors can't be used on a table view — if someone knows why or can explain to me how to do it, please do!
Anyway, I've gone around the problem by using the actual measurements of the view, which are correct. So the first constraint becomes:
NSLayoutConstraint(item: settingsView,
attribute: .bottom,
relatedBy: .equal,
toItem: self.view,
attribute: .top,
multiplier: 1,
constant: self.view.frame.height + self.view.bounds.minY)
This brings the view nicely to the bottom of the screen. The self.view.bounds.minY in the constant accounts for the presence of a navigation bar at the top of the screen. Still feels a bit like a "hack" to me, especially because I don't understand why the other version doesn't work.
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 })
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 can't seem to find a proper answer on SO. I'm trying to rotate a subclassed UISlider and apply constraints to position it properly, but can't seem to get it to work correctly. In the XIB it has constraints so that I don't have any issues with error showing. (Boss hates errors showing). So I remove the constraints first.
My code looks like this:
removeConstraints([sliderHeight, sliderWidth, sliderLeading, sliderBottom])
let rotation = CATransform3DMakeRotation(-CGFloat(M_PI_2), 0.0, 0.0, 1.0)
self.layer.transform = rotation
let views = ["slider":slider, "deviceIcon":deviceIcon]
var constraints = NSLayoutConstraint.constraintsWithVisualFormat("H:[deviceIcon]-8-[slider]", options: .DirectionLeadingToTrailing, metrics: nil, views: views)
constraints.append(NSLayoutConstraint(item: deviceIcon, attribute: .Bottom, relatedBy: .Equal, toItem: slider, attribute: .Bottom, multiplier: 1, constant: 0))
constraints.append(NSLayoutConstraint(item: slider, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 30))
constraints.append(NSLayoutConstraint(item: slider, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 115))
addConstraints(constraints)
Once it runs, the frames look like this (white area between grey and orange vertical dashes is the thumb) :
It doesn't have the proper width/height and it isn't aligned to the bottom of the deviceIcon on its left. (I think it might be the proper distance from the deviceIcon, I can't tell.)
How can I do this properly?
So the answer is: Don't. Simply adjust the existing leading and bottom constraints so that the rotated slider is in the proper position.
I have these views, both are the same, i want to add them programmatically so i want to add constraints programmatically, i've managed to do same using storyboard but i want to use code for this.
i want to add margins to these views so that first one is at the top, next one is below the first one and so,
i've wrote code like this:
self.view.addConstraint(
NSLayoutConstraint(
item: secondView,
attribute: .Top,
relatedBy: .Equal,
toItem: firstView,
attribute: .Top,
multiplier: 1.0,
constant: 0
))
first view has the constraint in which toItem is current view controller and it works, but the second view does not work this way, it just draws it on top of the first view, i want it to be below it, only way i can do this is in constant: 0 enter the height of the view, which i don't like
any suggestions?
The code you supplied is 99% right but
self.view.addConstraint(
NSLayoutConstraint(
item: secondView,
attribute: .Top,
relatedBy: .Equal,
toItem: firstView,
attribute: .Top,
multiplier: 1.0,
constant: 0
))
Your attaching the top of secondView to the top of firstView so they would be on top instead you want the top of secondView to the bottom of firstView.
self.view.addConstraint(
NSLayoutConstraint(
item: secondView,
attribute: .Top,
relatedBy: .Equal,
toItem: firstView,
attribute: .Bottom, <----------
multiplier: 1.0,
constant: 0
))
The constant is the distance.
I have a view divided into four sections. A corner view in the top left (unused, invisible); a horizontal view across the top (timelineAreaView in the code) displaying one set of axis labels; a vertical view on the left side displaying another set of axis labels, and the rest of the view is a large collection view. It's not unlike a spreadsheet.
The problem is that I am trying to use a UIScrollView in the top view. If I add a test UIView to the top everything is fine. Here's the code:
let timelineViewController = self.storyboard?.instantiateViewControllerWithIdentifier("TimelineViewController") as? TimelineViewController
addChildViewController(timelineViewController!)
timelineAreaView.addSubview(timelineViewController!.view)
timelineViewController!.didMoveToParentViewController(self)
timelineViewController!.view.translatesAutoresizingMaskIntoConstraints = false
timelineAreaView.addConstraint(NSLayoutConstraint(item: timelineViewController!.view, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal,
toItem: timelineAreaView, attribute: NSLayoutAttribute.Left, multiplier: 1.0, constant: 0))
timelineAreaView.addConstraint(NSLayoutConstraint(item: timelineViewController!.view, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal,
toItem: timelineAreaView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 0))
timelineAreaView.addConstraint(NSLayoutConstraint(item: timelineViewController!.view, attribute: NSLayoutAttribute.Right, relatedBy: NSLayoutRelation.Equal,
toItem: timelineAreaView, attribute: NSLayoutAttribute.Right, multiplier: 1.0, constant: 0))
timelineAreaView.addConstraint(NSLayoutConstraint(item: timelineViewController!.view, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal,
toItem: timelineAreaView, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 0))
TimelineViewController is a plain UIViewController with the UIView property backgroundColor set to blue.
Here's the result, displayed in that excellent tool Reveal:
So far so good. But it needs to have a UIScrollView. So If I add a UIScrollView to that UIViewController in the storyboard it looks like this:
Its constraints now look like this:
Here's the problem shown in Reveal - the UIScrollView is highlighted:
Reveal Layout Inspector shows:
So WHY are there 20 pixels added to the y origin and subtracted from the x origin in the frame?
My first thought was to look at the view controllers settings for "Adjust Scroll View Insets" and "Under Top Bars" but these have no effect. I cannot find any setting that has any effect here.
Going back to the storyboard and compensating for the two mysterious 20 pixel errors, i.e.
This makes it look just like I want. But I shouldn't have to do this. What's going on?