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 })
Related
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.
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.
I created a .xib with freeform and implement it like this
if let alertView = Bundle.main.loadNibNamed(Constants.XIB.titleImageLabelThreeButtonsAlertView, owner: self, options: nil)?.first as? TitleImageLabelThreeButtonsAlertView {
view.addSubview(alertView)
alertView.center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 20))
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 20))
alertView.autoresizingMask = [UIViewAutoresizing.flexibleLeftMargin, UIViewAutoresizing.flexibleRightMargin, UIViewAutoresizing.flexibleTopMargin, UIViewAutoresizing.flexibleBottomMargin]
self.view.layoutIfNeeded()
}
I call this code in viewDidAppear. The center thing seems to work, but it seems that the trailing and leading don't have any effect. I want them with a distance of 20, my alertView should have a fixed height and appear in center.
The xib has always the same size (see screenshots)
My originally targeted was to get a xib that I can implement in every view for every device. So what is the best way to get this?
my xib file
simulator iphone 7
simulator iphone 4
You are mixing up auto layout and fixed placement (with autoresizing mask). What you want to do is completely use auto layout so that the view will adjust its layout automatically. You say you want a horizontal distance of 20, a fixed height and to be centred so I would do this:
if let alertView = Bundle.main.loadNibNamed(Constants.XIB.titleImageLabelThreeButtonsAlertView, owner: self, options: nil)?.first as? TitleImageLabelThreeButtonsAlertView {
view.addSubview(alertView)
// Start using auto layout
alertView.translatesAutoresizingMaskIntoConstraints = false
// Set the leading and trailing constraints for horizontal placement
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: -20))
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 20))
// Centre it vertically
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1, constant: 0))
// Set the fixed height constraint
let fixedHeight: CGFloat = 100
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 0, constant: fixedHeight))
}
That will get you what you want no matter how the device, superview, orientation, etc changes.
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)
}