Autolayout error on height anchor when animation constraints - ios

I have a requirement to render a UIView at the footer of my app, when the user takes some sort of action, that view scrolls to the top of the view and fixes in position.
Imagine almost a tab bar view animating into a navigation bar.
I have essentially mocked this out by have 2 anchors for my view, top and bottom.
I then toggle these anchors and use UIView.animate with layoutIfNeeded to move the position.
The contents of my component are then anchored to the safe area of the parent, meaning I avoid the top and bottom issues on the X series.
However when my view starts to animate I am getting an autolayout error in my terminal
(
"<NSLayoutConstraint:0x600002e38ff0 UILabel:0x7fe64bc04570'Foo Bar Boo Baz'.height == 44 (active)>",
"<NSLayoutConstraint:0x600002e38d70 UILabel:0x7fe64bc04570'Foo Bar Boo Baz'.top == UILayoutGuide:0x600003408d20'UIViewSafeAreaLayoutGuide'.top (active)>",
"<NSLayoutConstraint:0x600002e38e10 UILabel:0x7fe64bc04570'Foo Bar Boo Baz'.bottom == UILayoutGuide:0x600003408d20'UIViewSafeAreaLayoutGuide'.bottom (active)>",
"<NSLayoutConstraint:0x600002e39130 UIView:0x7fe64bc166b0.bottom == Home.AnimatedCustomNavigationView:0x7fe64bc02970.bottom (active)>",
"<NSLayoutConstraint:0x600002e390e0 V:|-(0)-[UIView:0x7fe64bc166b0] (active, names: '|':Home.AnimatedCustomNavigationView:0x7fe64bc02970 )>",
"<NSLayoutConstraint:0x600002e41810 'UIView-Encapsulated-Layout-Height' Home.AnimatedCustomNavigationView:0x7fe64bc02970.height == 896 (active)>",
"<NSLayoutConstraint:0x600002e38cd0 'UIViewSafeAreaLayoutGuide-bottom' V:[UILayoutGuide:0x600003408d20'UIViewSafeAreaLayoutGuide']-(34)-| (active, names: '|':UIView:0x7fe64bc166b0 )>",
"<NSLayoutConstraint:0x600002e38c30 'UIViewSafeAreaLayoutGuide-top' V:|-(0)-[UILayoutGuide:0x600003408d20'UIViewSafeAreaLayoutGuide'] (active, names: '|':UIView:0x7fe64bc166b0 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600002e38ff0 UILabel:0x7fe64bc04570'Foo Bar Boo Baz'.height == 44 (active)>
Which appears to caused by the height anchor.
I am not sure how I can achieve this effect and also fix this error.
import UIKit
class AnimatedCustomNavigationView: UIView {
private lazy var navBar = UIView(frame: .zero)
private var navBarTopAnchor: NSLayoutConstraint!
private var navBarBottomAnchor: NSLayoutConstraint!
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = theme.color(.background)
navBar.backgroundColor = .purple
let label = UILabel(frame: .zero)
label.text = "Foo Bar Boo Baz"
label.sizeToFit()
addSubview(navBar)
navBar.addSubview(label)
[navBar, label].forEach { $0.translatesAutoresizingMaskIntoConstraints = false }
NSLayoutConstraint.activate([
// MARK: - NavBar Container
navBar.leadingAnchor.constraint(equalTo: self.leadingAnchor),
navBar.trailingAnchor.constraint(equalTo: self.trailingAnchor),
// MARK: - Label
label.topAnchor.constraint(equalTo: navBar.safeAreaLayoutGuide.topAnchor),
label.leadingAnchor.constraint(equalTo: navBar.leadingAnchor, constant: 16),
label.bottomAnchor.constraint(equalTo: navBar.safeAreaLayoutGuide.bottomAnchor),
label.trailingAnchor.constraint(equalTo: navBar.trailingAnchor, constant: -16),
label.heightAnchor.constraint(equalToConstant: 44)
])
navBarTopAnchor = navBar.topAnchor.constraint(equalTo: topAnchor)
navBarTopAnchor.isActive = false
navBarBottomAnchor = navBar.bottomAnchor.constraint(equalTo: bottomAnchor)
navBarBottomAnchor.isActive = true
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.navBarTopAnchor.isActive = true
self.navBarBottomAnchor.isActive = false
UIView.animate(withDuration: 1.5, delay: 0, options: .curveEaseInOut, animations: {
self.layoutIfNeeded()
}, completion: nil)
}
}
required init?(coder aDecoder: NSCoder) {
return nil
}
}
To simulate a user action for now, I have wrapped my animation call in a DispatchQueue.main.asyncAfter

You need to correctly order the .isActive = false should be before .isActive = true
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.navBarBottomAnchor.isActive = false
self.navBarTopAnchor.isActive = true
UIView.animate(withDuration: 1.5, delay: 0, options: .curveEaseInOut, animations: {
self.layoutIfNeeded()
}, completion: nil)
}

Related

Will attempt to recover by breaking constraint (but not sure why my constraints are wrong)

I'm working on making a custom list cell (collection view list cell), based on this article. I manually add the height of the the view in the cell, but I see the warnings below in the console of Xcode, and not sure which part to fix.
[LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
translatesAutoresizingMaskIntoConstraints)
(
"<NSAutoresizingMaskLayoutConstraint:0x281209220 h=--& v=--& liveTest.LiveChannelContentView:0x128c13430.height == 44 (active)>",
"<NSLayoutConstraint:0x2812371b0 UIView:0x128c136b0.height == 60 (active)>",
"<NSLayoutConstraint:0x2812372a0 V:|-(0)-[UIView:0x128c136b0] (active, names: '|':liveTest.LiveChannelContentView:0x128c13430 )>",
"<NSLayoutConstraint:0x2812372f0 UIView:0x128c136b0.bottom == liveTest.LiveChannelContentView:0x128c13430.bottom (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x2812371b0 UIView:0x128c136b0.height == 60 (active)>
The code below is where I get this error message.
class LiveChannelContentView: UIView, UIContentView {
let contentsView = UIView()
lazy var titleLabel: UILabel = {
let label = UILabel()
label.text = ""
return label
}()
lazy var statusLabel: UILabel = {
let label = UILabel()
label.text = ""
return label
}()
lazy var symbolImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
return imageView
}()
var liveEvent: LiveEvent?
init(configuration: LiveChannelContentConfiguration) {
// Custom initializer implementation here.
super.init(frame: .zero)
print("this is the view height: \(self.bounds.height)") // -> I get 0.0 in here
setupAllViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupAllViews() {
addSubview(contentsView)
contentsView.addSubview(symbolImageView)
contentsView.addSubview(indicator)
contentsView.addSubview(titleLabel)
contentsView.addSubview(statusLabel)
contentsView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
contentsView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
contentsView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
contentsView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
contentsView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor),
contentsView.heightAnchor.constraint(equalToConstant: 60)
])
contentsView.backgroundColor = .yellow
symbolImageView.centerY(leading: contentsView.leadingAnchor, trailing: nil, parent: contentsView, paddingLeft: 0, paddingRight: 0, size: CGSize(width: 50, height: 50))
indicator.centerY(leading: contentsView.leadingAnchor, trailing: nil, parent: contentsView, paddingLeft: 0, paddingRight: 0, size: CGSize(width: 50, height: 50))
titleLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
titleLabel.leadingAnchor.constraint(equalTo: symbolImageView.trailingAnchor, constant: 8),
titleLabel.topAnchor.constraint(equalTo: symbolImageView.topAnchor),
titleLabel.trailingAnchor.constraint(equalTo: contentsView.trailingAnchor)
])
statusLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
statusLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),
statusLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor),
statusLabel.trailingAnchor.constraint(equalTo: titleLabel.trailingAnchor)
])
print("this is the view after setup: \(self.bounds.height)") // I also get 0.0 in here
}
}
So, to clarify where the LiveChannelContentView is, I just add the yellow background to the view.
There are two things I don't get. First, even Xcode tells me that
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x2812371b0 UIView:0x128c136b0.height == 60 (active)>
when I take a screenshot of the app and measure the yellow background UIView's height, it is still 60. I thought breaking constraint means that using other height constraints instead of 60, but is that wrong?
Another thing is I was curious where the
"<NSAutoresizingMaskLayoutConstraint:0x281209220 h=--& v=--& liveTest.LiveChannelContentView:0x128c13430.height == 44 (active)>"
is used in my code. I searched the file contains 44 in my workspace but I got nothing.
Not really sure, but I thought the height of 44 and 60 is applied to the same UIView and Xcode got rid of the 60's height anchor or something. However, when I delete the height anchor for the contentsView, contentsView.heightAnchor.constraint(equalToConstant: 60), the app crashed like below.
I also tried deleting the top or the bottom anchor of the contentsView, but it also crushed the app.
contentsView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
or
contentsView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor),
So can anyone tell me which constraint should I fix to get rid of the waring please?
Change this part
contentsView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
contentsView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
contentsView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
contentsView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
contentsView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor),
contentsView.heightAnchor.constraint(equalToConstant: 60)
])
to
contentsView.translatesAutoresizingMaskIntoConstraints = false
let con = contentsView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor)
con.priority = UILayoutPriority(rawValue: 999)
NSLayoutConstraint.activate([
contentsView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
contentsView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
contentsView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
con,
contentsView.heightAnchor.constraint(equalToConstant: 60)
])

Swift - Autolayout constraints UIView-Encapsulated-Layout-Width

I'm trying to do UI programatically and I'm getting weird constraint errors, but when I run the app, it it looks as expected.
What I'm trying to do:
I have a ViewController TodayVC where I have a UIView and I'm trying to render content of MWStepsActivityVC in that view.
Here is my TodayVC:
class TodayVC: UIViewController {
let scrollView = UIScrollView()
let contentView = UIView()
let stepsActivityView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
configureViewController()
configureScrollView()
configureContainerViews()
layoutUI()
}
func configureViewController() {
view.backgroundColor = .systemBackground
}
func configureScrollView() {
view.addSubview(scrollView)
scrollView.addSubview(contentView)
scrollView.pinToEdges(of: view)
contentView.pinToEdges(of: scrollView)
NSLayoutConstraint.activate([
contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
contentView.heightAnchor.constraint(equalToConstant: 600)
])
}
func configureContainerViews() {
self.add(childVC: MWStepsActivityVC(activityType: .steps), to: self.stepsActivityView)
}
func layoutUI() {
contentView.addSubview(stepsActivityView)
stepsActivityView.translatesAutoresizingMaskIntoConstraints = false
let padding: CGFloat = 20
let itemHeight: CGFloat = 140
NSLayoutConstraint.activate([
stepsActivityView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: padding),
stepsActivityView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -padding),
stepsActivityView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding),
stepsActivityView.heightAnchor.constraint(equalToConstant: itemHeight),
])
print("TodayVC view width \(view.frame.size.width)")
}
func add(childVC: UIViewController, to containerView: UIView) {
addChild(childVC)
containerView.addSubview(childVC.view)
childVC.view.frame = containerView.bounds
childVC.didMove(toParent: self)
}
}
UIViewExtension:
extension UIView {
func addSubviews(_ views: UIView...) {
for view in views {
addSubview(view)
}
}
func pinToEdges(of superview: UIView) {
translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
topAnchor.constraint(equalTo: superview.topAnchor),
leadingAnchor.constraint(equalTo: superview.leadingAnchor),
trailingAnchor.constraint(equalTo: superview.trailingAnchor),
bottomAnchor.constraint(equalTo: superview.bottomAnchor),
])
}
}
MWStepsActivityVC:
class MWActivityVC: UIViewController {
let iconImageView = UIImageView()
let titleLabel = UILabel()
let counterLabel = UILabel()
init(activityType: ActivityType) {
super.init(nibName: nil, bundle: nil)
self.set(activityType: activityType)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
configureBackgroundView()
layoutUI()
placeholderData()
}
fileprivate func placeholderData() {
iconImageView.image = SFSymbols.steps
titleLabel.text = "StepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsStepsSteps"
counterLabel.text = "9000"
counterLabel.backgroundColor = .red
}
fileprivate func configureBackgroundView() {
view.layer.cornerRadius = 18
}
fileprivate func layoutUI() {
view.addSubviews(iconImageView, titleLabel, counterLabel)
iconImageView.translatesAutoresizingMaskIntoConstraints = false
titleLabel.translatesAutoresizingMaskIntoConstraints = false
counterLabel.translatesAutoresizingMaskIntoConstraints = false
let padding: CGFloat = 20
NSLayoutConstraint.activate([
iconImageView.topAnchor.constraint(equalTo: view.topAnchor, constant: padding),
iconImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: padding),
iconImageView.heightAnchor.constraint(equalToConstant: 20),
iconImageView.widthAnchor.constraint(equalToConstant: 20),
titleLabel.centerYAnchor.constraint(equalTo: iconImageView.centerYAnchor),
titleLabel.leadingAnchor.constraint(equalTo: iconImageView.trailingAnchor, constant: padding),
titleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -padding),
titleLabel.heightAnchor.constraint(equalToConstant: 20),
counterLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -padding),
counterLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: padding),
counterLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -padding),
counterLabel.heightAnchor.constraint(equalToConstant: 40)
])
print("MWStepsActivityVC view width \(view.frame.size.width)")
}
}
I'm getting these errors:
"<NSLayoutConstraint:0x600002228320 H:|-(20)-[UIImageView:0x7fc0abe077a0] (active, names: '|':UIView:0x7fc0abd10850 )>",
"<NSLayoutConstraint:0x6000022283c0 UIImageView:0x7fc0abe077a0.width == 20 (active)>",
"<NSLayoutConstraint:0x600002228460 H:[UIImageView:0x7fc0abe077a0]-(20)-[UILabel:0x7fc0abe08a70] (active)>",
"<NSLayoutConstraint:0x6000022284b0 UILabel:0x7fc0abe08a70.trailing == UIView:0x7fc0abd10850.trailing - 20 (active)>",
"<NSLayoutConstraint:0x600002230b90 'UIView-Encapsulated-Layout-Width' UIView:0x7fc0abd10850.width == 0 (active)>",
"<NSLayoutConstraint:0x600002228320 H:|-(20)-[UIImageView:0x7fc0abe077a0] (active, names: '|':UIView:0x7fc0abd10850 )>",
"<NSLayoutConstraint:0x600002228460 H:[UIImageView:0x7fc0abe077a0]-(20)-[UILabel:0x7fc0abe08a70] (active)>",
"<NSLayoutConstraint:0x6000022284b0 UILabel:0x7fc0abe08a70.trailing == UIView:0x7fc0abd10850.trailing - 20 (active)>",
"<NSLayoutConstraint:0x600002230b90 'UIView-Encapsulated-Layout-Width' UIView:0x7fc0abd10850.width == 0 (active)>",
"<NSLayoutConstraint:0x6000022285a0 H:|-(20)-[UILabel:0x7fc0abe08ce0] (active, names: '|':UIView:0x7fc0abd10850 )>",
"<NSLayoutConstraint:0x6000022285f0 UILabel:0x7fc0abe08ce0.trailing == UIView:0x7fc0abd10850.trailing - 20 (active)>",
"<NSLayoutConstraint:0x600002230b90 'UIView-Encapsulated-Layout-Width' UIView:0x7fc0abd10850.width == 0 (active)>"
I tried to use the wtfautolayout.com website to understand what's wrong, and it seems the issue is with UIView-Encapsulated-Layout-Width it thinks it's set to 0, but when I try to print view.frame.size.width I get correct width:
MWStepsActivityVC view width 375.0
TodayVC view width 375.0
Can someone help me with it? I'm struggling with this for a day already.
Finally I was able to fix it. I forgot to run configureContainerViews on main thread...
DispatchQueue.main.async {
self.configureContainerViews()
}

Autolayout warning tableview cell

I am building a chat layout and i have a table view and custom chat cell by adding constraint programmatically but i am getting the autolayout warning
In my ChatMessageCell i have a enum Style to determine the chat layout direction leading or trailing
I have messageLabel which has top, bottom, leading or trailing anchor to the UITableViewCelland width less than equal to 250
I have view which has leading, trailing, bottom and top anchor to the messageLabel view, setMessageLayout method active or deactive the leading or trailing anchor of the view
Autolayout Warning
(
"<NSLayoutConstraint:0x6000039a3c00 UILabel:0x7ff54cc34b20'Hello how are you'.width <= 250 (active)>",
"<NSLayoutConstraint:0x6000039a3e80 H:[UILabel:0x7ff54cc34b20'Hello how are you']-(16)-| (active, names: '|':iOSChatApp.ChatMessageCell:0x7ff54d087600'cell_leading' )>",
"<NSLayoutConstraint:0x6000039a3e30 iOSChatApp.ChatMessageCell:0x7ff54d087600'cell_leading'.leading == UILabel:0x7ff54cc34b20'Hello how are you'.leading - 16 (active)>",
"<NSLayoutConstraint:0x6000039afca0 'UIView-Encapsulated-Layout-Width' iOSChatApp.ChatMessageCell:0x7ff54d087600'cell_leading'.width == 375 (active)>"
)
ChatMessageCell
class ChatMessageCell: UITableViewCell {
enum Style {
case leading
case trailing
}
private var style: Style!
private let messageLbl = UILabel()
private let view = UIView()
private var leadingConstraint: NSLayoutConstraint!
private var trailingConstraint: NSLayoutConstraint!
var message: ChatMessage! {
didSet {
messageLbl.text = message.message
setMessageLayout(with: message.sender == "User1" ? .leading : .trailing)
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = #colorLiteral(red: 0.9490196078, green: 0.9450980392, blue: 0.9529411765, alpha: 1)
messageLbl.translatesAutoresizingMaskIntoConstraints = false
messageLbl.font = UIFont(name: "Avenir-Book", size: 17)
messageLbl.numberOfLines = 0
view.translatesAutoresizingMaskIntoConstraints = false
view.layer.cornerRadius = 5
addSubview(view)
addSubview(messageLbl)
NSLayoutConstraint.activate([
topAnchor.constraint(equalTo: messageLbl.topAnchor, constant: -16),
bottomAnchor.constraint(equalTo: messageLbl.bottomAnchor, constant: 16),
messageLbl.widthAnchor.constraint(lessThanOrEqualToConstant: 250)
])
NSLayoutConstraint.activate([
view.topAnchor.constraint(equalTo: messageLbl.topAnchor, constant: -8),
view.bottomAnchor.constraint(equalTo: messageLbl.bottomAnchor, constant: 8),
view.leadingAnchor.constraint(equalTo: messageLbl.leadingAnchor, constant: -8),
view.trailingAnchor.constraint(equalTo: messageLbl.trailingAnchor, constant: 8),
])
leadingConstraint = leadingAnchor.constraint(equalTo: messageLbl.leadingAnchor, constant: -16)
trailingConstraint = trailingAnchor.constraint(equalTo: messageLbl.trailingAnchor, constant: 16)
leadingConstraint.isActive = true
trailingConstraint.isActive = false
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setMessageLayout(with style: Style) {
messageLbl.textColor = style == .leading ? .black : .white
view.backgroundColor = style == .leading ? .white : #colorLiteral(red: 0.06274509804, green: 0.6039215686, blue: 0.3019607843, alpha: 1)
leadingConstraint.isActive = style == .leading ? true : false
trailingConstraint.isActive = style == .trailing ? true : false
}
}
My Layout
According to your Autolayout warning, messageLabel's width and leading are conflicting.
The warning is telling that both status are active
(
"<NSLayoutConstraint:0x6000039a3c00 UILabel:0x7ff54cc34b20'Hello how are you'.width <= 250 (active)>",
"<NSLayoutConstraint:0x6000039a3e80 H:[UILabel:0x7ff54cc34b20'Hello how are you']-(16)-| (active, names: '|':iOSChatApp.ChatMessageCell:0x7ff54d087600'cell_leading' )>",
"<NSLayoutConstraint:0x6000039a3e30 iOSChatApp.ChatMessageCell:0x7ff54d087600'cell_leading'.leading == UILabel:0x7ff54cc34b20'Hello how are you'.leading - 16 (active)>",
"<NSLayoutConstraint:0x6000039afca0 'UIView-Encapsulated-Layout-Width' iOSChatApp.ChatMessageCell:0x7ff54d087600'cell_leading'.width == 375 (active)>"
)
If you read warning carefully, the console may recommend you what to remove.

Unable to simultaneously satisfy constraints when animating constraint

This is my code:
class AvailableTrainScene: UIView {
let seeMoreButton: UIButton = {
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .blue
v.layer.cornerRadius = 20
v.setTitle("Voir Plus", for: .normal)
return v
}()
var seeMoreBottom: NSLayoutConstraint?
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(seeMoreButton)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
NSLayoutConstraint.activate([
seeMoreButton.heightAnchor.constraint(equalToConstant: 40),
seeMoreButton.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.8),
seeMoreButton.centerXAnchor.constraint(equalTo: centerXAnchor)
])
seeMoreBottom = seeMoreButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8)
seeMoreBottom?.isActive = false
}
func hideSeeMore(_ hide: Bool) {
if hide {
seeMoreBottom?.constant = 40
}else{
seeMoreBottom?.constant = -8
}
UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 4, options: .curveEaseOut, animations: {
self.layoutIfNeeded()
}, completion: nil)
}
}
i get this error when animating using the function hideSeeMore:
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:0x28060dc70 UIButton:0x105534920'Voir Plus'.bottom == DzTrain_2_0.AvailableTrainScene:0x1055300d0.bottom - 8 (active)>",
"<NSLayoutConstraint:0x2806894a0 UIButton:0x105534920'Voir Plus'.bottom == DzTrain_2_0.AvailableTrainScene:0x1055300d0.bottom + 40 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x2806894a0 UIButton:0x105534920'Voir Plus'.bottom == DzTrain_2_0.AvailableTrainScene:0x1055300d0.bottom + 40 (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
i tried to add tow constraints one to hide and another to show (priority is deferent) then activate/deactivate them but nothing happend?
Someone have this before?
Put constraints in
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(seeMoreButton)
NSLayoutConstraint.activate([
seeMoreButton.heightAnchor.constraint(equalToConstant: 40),
seeMoreButton.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.8),
seeMoreButton.centerXAnchor.constraint(equalTo: centerXAnchor)
])
seeMoreBottom = seeMoreButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8)
seeMoreBottom?.isActive = false
}
as this
override func layoutSubviews()
is called for any
self.layoutIfNeeded()
which causes your edit in constant to conflict with a new overwrite here
seeMoreBottom = seeMoreButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8)
seeMoreBottom?.isActive = false

UIStackView spacing breaks constraints when not in set updateConstraints

I have a simple ViewController with a custom subview called MyView:
import UIKit
class ViewController: UIViewController {
let myView = MyView()
override func viewDidLoad() {
super.viewDidLoad()
myView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myView)
myView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -8.0).isActive = true
myView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8.0).isActive = true
myView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8.0).isActive = true
}
}
class MyView: UIView {
let stackView: UIStackView
let label = UILabel()
let label2 = UILabel()
init() {
stackView = UIStackView(arrangedSubviews: [label, label2])
super.init(frame: .zero)
stackView.axis = .vertical
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Label 1"
label2.translatesAutoresizingMaskIntoConstraints = false
label2.text = "Label 2"
stackView.spacing = 8.0
addSubview(stackView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func updateConstraints() {
super.updateConstraints()
stackView.fillSuperview()
}
}
When I run this, I get the following breaking constraints in the console:
2018-03-23 16:11:58.960493+0100 ViewWithStackView[75612:10756278] [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:0x608000280f00 'UISV-canvas-connection' UIStackView:0x7f8438e07510.top == UILabel:0x7f8438d06740'Label 1'.top (active)>",
"<NSLayoutConstraint:0x608000280fa0 'UISV-canvas-connection' V:[UILabel:0x7f8438e07790'Label 2']-(0)-| (active, names: '|':UIStackView:0x7f8438e07510 )>",
"<NSLayoutConstraint:0x608000280ff0 'UISV-spacing' V:[UILabel:0x7f8438d06740'Label 1']-(8)-[UILabel:0x7f8438e07790'Label 2'] (active)>",
"<NSLayoutConstraint:0x608000280550 'UIView-Encapsulated-Layout-Height' UIStackView:0x7f8438e07510.height == 0 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x608000280ff0 'UISV-spacing' V:[UILabel:0x7f8438d06740'Label 1']-(8)-[UILabel:0x7f8438e07790'Label 2'] (active)>
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.
2018-03-23 16:11:58.961737+0100 ViewWithStackView[75612:10756278]
[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.
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSAutoresizingMaskLayoutConstraint:0x6080002812c0 h=--& v=--& UIStackView:0x7f8438e07510.height == 0 (active)>",
"<NSLayoutConstraint:0x608000280f00 'UISV-canvas-connection' UIStackView:0x7f8438e07510.top == UILabel:0x7f8438d06740'Label 1'.top (active)>",
"<NSLayoutConstraint:0x608000280fa0 'UISV-canvas-connection' V:[UILabel:0x7f8438e07790'Label 2']-(0)-| (active, names: '|':UIStackView:0x7f8438e07510 )>",
"<NSLayoutConstraint:0x608000280ff0 'UISV-spacing' V:[UILabel:0x7f8438d06740'Label 1']-(8)-[UILabel:0x7f8438e07790'Label 2'] (active)>"
)
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 <UIKit/UIView.h> may also be helpful.
Now if I move the following line stackView.spacing = 8.0 to the updateConstraints method, the constraints won't break. So the following code works fine without warning / error in the console:
import UIKit
class ViewController: UIViewController {
let myView = MyView()
override func viewDidLoad() {
super.viewDidLoad()
myView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myView)
myView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -8.0).isActive = true
myView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8.0).isActive = true
myView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8.0).isActive = true
}
}
class MyView: UIView {
let stackView: UIStackView
let label = UILabel()
let label2 = UILabel()
init() {
stackView = UIStackView(arrangedSubviews: [label, label2])
super.init(frame: .zero)
stackView.axis = .vertical
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Label 1"
label2.translatesAutoresizingMaskIntoConstraints = false
label2.text = "Label 2"
addSubview(stackView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func updateConstraints() {
super.updateConstraints()
stackView.fillSuperview()
stackView.spacing = 8.0
}
}
Why does moving stackView.spacing = 8.0 to updateConstraints solve this issue / remove the warnings / errors?
Thank you for an explanation :)
It looks like this is the constraint you don't want
"<NSLayoutConstraint:0x608000280550 'UIView-Encapsulated-Layout-Height' UIStackView:0x7f8438e07510.height == 0 (active)>"
The first thing I'd try is
stackView.translatesAutoresizingMaskIntoConstraints = false
Also, I don't know what stackView.fillSuperview() is doing but you'd probably be better off adding constraints to it so fills the view.
In the first case the stackView has a zero height because you haven't set a frame for it ( in init before fillSuperview()), so setting a spacing of 8 between the 2 labels will cause a conflict , but in second the frame is zero and spacing is zero and the 2 labels will have a zero height according to (intrinsic content size) so, in this case no conflict will happen , then you set spacing in updateConstraints but at that moment stackView has a frame equal to it's superView according to this line fillSuperview() so that goes smoothly also

Resources