Im building slider inside UIAlertController and it actually worked fine on iPhone but gives breaking constraint error on iPad, i gave the alert 140 of height as constraint before presenting.
Here is my code:
let alertController = UIAlertController(title:"Title", message: "", preferredStyle: UIAlertControllerStyle.Alert)
let slider = UISlider(frame: CGRectMake(35, 50, 200, 20))
alertController.view.addSubview(slider)
let height:NSLayoutConstraint = NSLayoutConstraint(item: alertController.view, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 140)
alertController.view.addConstraint(height);
alertController.addAction(UIAlertAction(title: "close", style: UIAlertActionStyle.Cancel, handler: { (error) -> Void in
}))
self.presentViewController(alertController, animated: true, completion: nil)
The error:
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)
(
"<NSLayoutConstraint:0x7b555900 V:[_UIAlertControllerView:0x788d2a60'Title'(140)]>",
"<NSLayoutConstraint:0x789db3f0 V:[_UIAlertControllerView:0x788d2a60'Title'(1024)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x789db3f0 V:[_UIAlertControllerView:0x788d2a60'Title'(1024)]>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
Okay, I have an answer, but you’re not going to like it.
If you do the following (with no constraints added):
print("Alert frame" + String(alertController.view.frame))
self.presentViewController(alertController, animated: true, completion: nil)
You will get the following:
Alert frame(0.0, 0.0, 768.0, 1024.0)
Which just happens to be the iPad Air’s width and height in portrait mode. Okay, that’s obviously not the views width and height. Let’s modify our code a bit.
self.presentViewController(alertController, animated: true)
{
print("Alert frame" + String(alertController.view.frame))
}
This time I get the following:
Alert frame(0.0, 0.0, 300.0, 85.5)
Please note that I have two “\n\n” in my title. So let us ASSUME they are doing something funky prior to presenting it (remember Apple tells us this is an opaque class).
So let’s add the following:
self.presentViewController(alertController, animated: true)
{
let height:NSLayoutConstraint = NSLayoutConstraint(item: alertController.view,
attribute: NSLayoutAttribute.Height,
relatedBy: NSLayoutRelation.Equal,
toItem: nil,
attribute: NSLayoutAttribute.NotAnAttribute,
multiplier: 1,
constant: self.view.frame.height * 1.20)
alertController.view.addConstraint(height);
}
Now, cross our fingers for luck, and see what happens.
Unable to simultaneously satisfy constraints.
This suggests, to me, that Apple has constraints on the iPad that it does not on the iPhone. When we toss in our height constraint we have set up a situation where the internal constraint code is attempting to solve for two conflicting commands. As such, it can only do so by breaking the constraints.
I’m using .ActionSheet but I’m getting the same issue. It appears that Apple doesn’t use constraints for the iPhone. If you aren’t building a universal app, then you should be fine.
However, and this is important, Apple could impose constraints on the iPhone in the future which could cause the same issue.
The bottom line is this:
The UIAlertController class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified.
To me, Apple should be far more explicit and say, “We do not support modifications at all.” I suspect they think the “as-is” covers it, but as you have probably seen (as I have) many people don’t test all cases.
As such, I plan to look for a third party version that supports both the iPhone and iPad (see UIAlertController - add custom views to actionsheet - it is a similar issue.
Wasting my time on something that could break in an interim release of iOS is not worth it.
Related
I've got a UIView that I'm adding programmatically with the following code in viewDidLoad
dropMenuView = YNDropDownMenu(frame: CGRect(x: 0.0, y: 0, width: UIScreen.main.bounds.width, height: 40.0), dropDownViews: [typeView, brandView, priceView], dropDownViewTitles:["Type", "Brand", "Price"])
self.view.addSubview(dropMenuView)
The above code creates a view with the correct width for the current orientation, but if a user changes their device orientation, the width of the view does not update. I've tried adding a new constraint in viewWillTransitionTo:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
let widthConstraint = NSLayoutConstraint(item: dropMenuView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100)
dropMenuView.addConstraints([widthConstraint])
dropMenuView.layoutIfNeeded()
}
I've tried a number of mutations on the above code trying to get it to work. Generally the error I get is:
'NSLayoutConstraint for >: Unknown layout attribute'
I can't help but think I'm going about this in the wrong way. Is adding new constraints upon orientation change the way to go? I tried just adding the equal width constraint after I add the subview, but I get the same error. I've never really added constraints programmatically before, so these are uncharted waters for me. What would be the best approach here?
Adding constraints during an orientation change is not a good idea. If you were to do it, you'd need to remove the previously added constraints to avoid over constraining your view.
The constraint you are trying to create isn't correct. You only use .notAnAttrubute when you are passing nil as the second view.
I suggest you use layout anchors instead. They're easier to write and read:
dropMenuView = YNDropDownMenu(frame: CGRect.zero, dropDownViews: [typeView, brandView, priceView], dropDownViewTitles:["Type", "Brand", "Price"])
self.view.addSubview(dropMenuView)
// you need to set this if you are adding your own constraints and once
// yet set it, the frame is ignored (which is why we just passed CGRect.zero
// when creating the view)
dropMenuView.translatesAutoresizingMaskIntoConstraints = false
// Create the constraints and activate them. This will set `isActive = true`
// for all of the constraints. iOS knows which views to add them to, so
// you don't have to worry about that detail
NSLayoutConstraint.activate([
dropMenuView.topAnchor.constraint(equalTo: view.topAnchor),
dropMenuView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
dropMenuView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
dropMenuView.heightAnchor.constraint(equalToConstant: 40)
])
I have a function that sets the height of a custom keyboard extension, depending on the phone. I originally just tried this in viewDidLoad():
self.view.heightAnchor.constraint(equalToConstant: 100)
This didn't seem to work, so I made a function:
func updateHeightOfView() {
var currentKeyboardInView: String!
if currentViewHeightConstraint != nil {
view.removeConstraint(currentViewHeightConstraint!)
}
currentViewHeightConstraint = NSLayoutConstraint(item: view, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1.0, constant: desiredHeight)
view.addConstraint(currentViewHeightConstraint!)
}
that way when the screen changes it's orientation, I resize the view. The second block of code works perfectly, but it throws warnings of layoutConstraints, so I was wondering why the first block of code isn't working, and if there is an easier way to UPDATE constraints of the height property of a view rather than add and remove them. This view is the most outside view in the viewController.
Here's the warning the second block spits out:
[LayoutConstraints] 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.
(
"<NSLayoutConstraint:0x608000298b00 App.KeyboardAccessoryView:0x7fdc00b1bfe0.height == 258 (active)>",
"<NSLayoutConstraint:0x600000297e80 'UIView-Encapsulated-Layout-Height' App.KeyboardAccessoryView:0x7fdc00b1bfe0.height == 216 (active)>"
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x608000298b00 App.KeyboardAccessoryView:0x7fdc00b1bfe0.height == 258 (active)>
As much detail you given here according to this I think where ever you set height of this view it is not static constant value but in view didload you are setting constant value for its height. So that's by it is not working.
In second block you are getting warning because may be your view is getting height from more than one way. To check this in updathHeight method just remove the constraint , don't add and try to run.
Exact solution could be given only looking on your constraints.
You can download a sample project demonstrating the issue below here:
https://github.com/DimaVartanian/keyboard-extension-height-bug
When creating a keyboard extension and not specifying a concrete height for its components but instead anchoring them to the view/inputView so that in theory the system will determine their height based on environment and orientation, in some situations that height instead turns into 0 and the keyboard is crushed (with the exception of anything that has a concrete height such as a self sized label or button).
This only seems to occur on iOS 10. On iOS 9, the child views resized correctly to fit the default automatic keyboard height.
There are several scenarios this can manifest and this project demonstrates a basic one. It starts with the basic keyboard extension template with the default "next keyboard" button and the 2 size constraints it comes with:
self.nextKeyboardButton.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
self.nextKeyboardButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
Next, we create a single other view that we want to fill the space of the superview without defining a concrete size for itself:
let anotherView = UIView()
anotherView.backgroundColor = UIColor.red
anotherView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(anotherView)
anotherView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
anotherView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
anotherView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
Now, let's say we just want to anchor this new view to the bottom of our keyboard superview. We would just do something like:
anotherView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
The result looks like this:
iOS 9
iOS 10
This layout is exactly what we expect. Now instead, let's anchor the new view to the top of our next keyboard button. We get rid of the constraint we just added and replace it with
anotherView.bottomAnchor.constraint(equalTo: self.nextKeyboardButton.topAnchor).isActive = true
Logically, the resulting height should be the same (determined by the system)
The result is now this:
iOS 9
iOS 10
On iOS 9 it behaves as expected but on iOS 10, the flexible height view is resized down to 0 and all that is left is the fixed height button.
There are no messages about conflicting constraints. I'm trying to figure out what could be causing this and why it would only be happening on iOS 10.
Apple has responded to my DTS ticket and told me to file a bug report, so this is actually an iOS 10 bug. I have filed a radar (#28532959) and will update this answer if I ever get a response. If someone else comes up with a concrete solution that allows me to still use autolayout to achieve an automatic height, answers are still accepted.
I got it solved by setting a new constrain for the height.
Here's my workaround. It is a little laggy when the device rotates, but it will do the job until Apple fixes this bug. I first thought it had something to do with inputView.allowSelfSizing, but that variable didn't seem to change anything.
First, declare heightConstraint:
var heightConstraint: NSLayoutConstraint!
In viewDidLoad, Add your custom view:
let nibName: String! = UIDevice.isPhone ? "KeyboardViewiPhone" : "KeyboardViewiPad"
customView = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?.first as! UIView
customView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(customView)
Add a constraint for the width as you would do normally:
let widthConstraint = NSLayoutConstraint(item: view, attribute: .width, relatedBy: .equal, toItem: customView, attribute: .width, multiplier: 1.0, constant: 0.0)
Add a constant constraint for the height:
heightConstraint = NSLayoutConstraint(item: customView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: view.frame.height)
view.addConstraints([widthConstraint, heightConstraint])
Now comes the fix:
override func viewDidLayoutSubviews() {
heightConstraint.constant = view.bounds.height
}
As viewDidLayoutSubviews is called every time view.bounds changes, it will handle orientation changes correctly.
I also faced the same issue. this is because of the Autolayout Constraints. Just remove all constraints. and set auto resizing.
I have also faced the same problem for the custom keyboard extension in Xcode 8.2. This is caused by the auto resizing. In my case, I solved this in the below manner.
Initially, my custom keyboard have 3 views.
In this, I was fixed the trailing, leading, top and height for the first and last view. And place the middle view like in the image.
after that select the middle view and open the show the size inspector in the storyboard. In the size inspector, you will find an option auto resizing. In that select the constraint indicators for that view.
After selecting that you run your project in a device and it will work correctly without missing any view.
Note: - It will work for both portrait and landscape modes. And mainly you don't have to give constraints for the middle view.
IMHO, best working solution is using "Proportional Height". For example, in my case, I finally ended with 2 views. Top one got 0.8 of height of superview, bottom - 0.2. It's not perfect solution, but you can still benefits from autolayout.
I can't seem to change the size of my MKMapView in Swift. How would one go on about it?
I've tried two different methods but without any luck:
var rect: CGRect = self.view.frame;
rect.origin.y = 0;
self.mapView.frame = rect;
and one where I used constraints and autolayout but it made the app crash. Any ideas?
EDIT:
When I write this code it doesn't crash but writes some warnings in the output:
let height = UIScreen.mainScreen().bounds.height
let width = UIScreen.mainScreen().bounds.width
let widthConstraint = NSLayoutConstraint(item: mapView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: width)
self.view.addConstraint(widthConstraint)
let heightConstraint = NSLayoutConstraint(item: mapView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: height)
self.view.addConstraint(heightConstraint)
The output says:
2016-03-09 18:43:02.697 Map[19782:3177712] 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.
(
"",
""
)
Will attempt to recover by breaking constraint
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints
to catch this in the debugger. The methods in the
UIConstraintBasedLayoutDebugging category on UIView listed in
may also be helpful. Message from debugger:
Terminated due to signal 15
I don't really understand this, any ideas?
In this case you should use
self.mapView.addConstraint(widthConstraint)
and
self.mapView.addConstraint(heightConstraint)
You are trying to add a constraint that belongs to the mapView to its superview, that's not the way it's supposed to be. If you were adding a vertical distance between the mapview and another view you would add it to the superview, but not in this case.
For the constraint warning you try to add constraint without disabling the autorisizing mask into constraint translation.
Try adding this line:
mapView.translatesAutoresizingMaskIntoConstraints = false
Regards
I am experiencing a misunderstanding of mechanics of iOS Layout Constraints. See the code I placed inside viewDidLoad listed below.
var btn = UIButton()
btn.setTitle("i am a button", forState: UIControlState.Normal)
btn.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
btn.backgroundColor = UIColor.lightGrayColor()
btn.sizeToFit()
view.addSubview(btn)
view.addConstraint(
NSLayoutConstraint(item: view,
attribute: NSLayoutAttribute.CenterX,
relatedBy: NSLayoutRelation.Equal,
toItem: btn,
attribute: NSLayoutAttribute.CenterX,
multiplier: 1.0,
constant: 0.0))
view.addConstraint(
NSLayoutConstraint(item: view,
attribute: NSLayoutAttribute.CenterY,
relatedBy: NSLayoutRelation.Equal,
toItem: btn,
attribute: NSLayoutAttribute.CenterY,
multiplier: 1.0,
constant: 0.0))
It seems to me my intention is clear. I want to see a button at the center of a device's screen. But I can see only the following picture though.
And I have an output in project’s console so scary I cannot understand a thing from it.
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) (
"",
"",
"",
"
(Names: '|':UIWindow:0x7fd318551080 )>" )
Will attempt to recover by breaking constraint
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints
to catch this in the debugger. The methods in the
UIConstraintBasedLayoutDebugging category on UIView listed in
may also be helpful. 2015-04-28 23:46:04.516
ConsTest[5966:248434] 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) (
"",
"",
"",
"
(Names: '|':UIWindow:0x7fd318551080 )>" )
Will attempt to recover by breaking constraint
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints
to catch this in the debugger. The methods in the
UIConstraintBasedLayoutDebugging category on UIView listed in
may also be helpful.
Looks like the constraints are treated contradictory and thus ignored at all. I cannot really point out why I can’t just create a button and place it at the center programmatically. Any relevant instruction is much appreciated.
On your UIButton (btn) set translatesAutoresizingMaskIntoConstraints = false
E.g.
btn.translatesAutoresizingMaskIntoConstraints = false
Recommended reading: Adopting Auto Layout