Q. How to make different constraint each views, in stackview, UIKit?
I want to make screen like captured one.
In below code, the result is ok. but I have errors Unable to simultaneously satisfy constraints...
In Before Q&A I've learnt StackView. but that screen has all same constraint. like wrote before, I set constraint for example titleLabel.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor, constant: 20)
...
So, I've Tried the why the failure https://www.wtfautolayout.com/, the answer was set 0 constraints each views. and now I wrote this question. Thanks for reading.
import UIKit
class DetailViewController: UIViewController {
@IBOutlet var centerNavtigationItems: UINavigationItem!
private let titleLabel: UILabel = {
let titleLabel = UILabel()
titleLabel.numberOfLines = 2
titleLabel.textAlignment = .left
titleLabel.font = UIFont(name: "Pretendard-SemiBold", size: 24)
titleLabel.backgroundColor = .yellow
return titleLabel
}()
private let dateLabel: UILabel = {
let view = UILabel()
view.textAlignment = .right
view.font = UIFont(name: "Pretendard-Light", size: 16)
view.textColor = .gray
return view
}()
private let scrollView: UIScrollView = {
let view = UIScrollView()
return view
}()
private let imageContainer: UIImageView = {
let ic = UIImageView()
return ic
}()
private let pageController: UIPageControl = {
let controller = UIPageControl()
controller.pageIndicatorTintColor = .gray
controller.currentPageIndicatorTintColor = .darkGray
return controller
}()
private let contentTextView: UILabel = {
let content = UILabel()
content.numberOfLines = 0
content.font = UIFont(name: "Pretendard-Regular", size: 18)
return content
}()
var fileName: String!
var id: String!
var entryNumber: Int!
var dataSize: Int!
var entry: Entry!
var imageNames: [String] = []
var imageIndex: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Get contents id and parse file name
parsingFileName(id: id)
let dateText = String(entry.year) + "." + String(entry.month) + "." + String(entry.day)
centerNavtigationItems.title = dateText
titleLabel.text = entry.title
dateLabel.text = dateText
// Add Image to an Array
// If an image doesn't exist, then set empty array, and make hide UIImageView ... (1)
if entry.isImage == true {
for e in entry.imageName {
imageNames.append(String(e + ".jpeg"))
}
pageController.numberOfPages = imageNames.count
pageController.currentPage = 0
print("image name in Array: \(imageNames)")
imageContainer.image = UIImage(named: imageNames[0])
pageController.addTarget(self, action: #selector(action(sender: )), for: .touchUpInside)
}
// Set Swipe Gesture for the image container
let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(DetailViewController.reponderToSwipeGesture(_:)))
swipeRight.direction = UISwipeGestureRecognizer.Direction.right
self.imageContainer.addGestureRecognizer(swipeRight)
let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(DetailViewController.reponderToSwipeGesture(_:)))
swipeLeft.direction = UISwipeGestureRecognizer.Direction.left
self.imageContainer.addGestureRecognizer(swipeLeft)
// Set textView Contents
contentTextView.text = entry.article
// stackView Setting
let stackView = UIStackView()
stackView.axis = .vertical
stackView.spacing = 8
// Arrange subviews to Stack View
[titleLabel, dateLabel, imageContainer, contentTextView].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
stackView.addArrangedSubview(v)
}
// If image doesn't exist (1) Set the image container hidden
if entry.isImage == true {
imageContainer.heightAnchor.constraint(equalToConstant: self.view.frame.width).isActive = true
} else {
imageContainer.isHidden = true
}
// Set stakcView and scrollView
stackView.translatesAutoresizingMaskIntoConstraints = false
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(stackView)
view.addSubview(scrollView)
// Set constraint
let g = view.safeAreaLayoutGuide
let cg = scrollView.contentLayoutGuide
let fg = scrollView.frameLayoutGuide
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0),
scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0),
scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -0),
scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -0),
stackView.topAnchor.constraint(equalTo: cg.topAnchor, constant: 0.0),
stackView.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 0.0),
stackView.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: -0.0),
stackView.bottomAnchor.constraint(equalTo: cg.bottomAnchor, constant: -0.0),
stackView.widthAnchor.constraint(equalTo: fg.widthAnchor, constant: -0),
// set detail components constraint
titleLabel.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: -20),
titleLabel.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 20),
dateLabel.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: -20),
dateLabel.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 20),
imageContainer.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 0),
imageContainer.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: 0),
contentTextView.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 20),
contentTextView.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: -20),
])
}
}
Errors
Unknown class detail in Interface Builder file.
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:0x6000021406e0 UILabel:0x103c1f3a0.leading == _UIScrollViewLayoutGuide:0x600003b0ea00'UIScrollView-contentLayoutGuide'.leading + 20 (active)>",
"<NSLayoutConstraint:0x600002140050 UIImageView:0x103c183d0.leading == _UIScrollViewLayoutGuide:0x600003b0ea00'UIScrollView-contentLayoutGuide'.leading (active)>",
"<NSLayoutConstraint:0x60000213b2f0 'UISV-alignment' UILabel:0x103c1f3a0.leading == UIImageView:0x103c183d0.leading (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x60000213b2f0 'UISV-alignment' UILabel:0x103c1f3a0.leading == UIImageView:0x103c183d0.leading (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.
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:0x600002142800 UILabel:0x103c1f3a0.trailing == _UIScrollViewLayoutGuide:0x600003b0ea00'UIScrollView-contentLayoutGuide'.trailing - 20 (active)>",
"<NSLayoutConstraint:0x600002140820 UIImageView:0x103c183d0.trailing == _UIScrollViewLayoutGuide:0x600003b0ea00'UIScrollView-contentLayoutGuide'.trailing (active)>",
"<NSLayoutConstraint:0x60000213b250 'UISV-alignment' UILabel:0x103c1f3a0.trailing == UIImageView:0x103c183d0.trailing (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x60000213b250 'UISV-alignment' UILabel:0x103c1f3a0.trailing == UIImageView:0x103c183d0.trailing (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.
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:0x600002140aa0 UIStackView:0x103c27b90.leading == _UIScrollViewLayoutGuide:0x600003b0ea00'UIScrollView-contentLayoutGuide'.leading (active)>",
"<NSLayoutConstraint:0x6000021406e0 UILabel:0x103c1f3a0.leading == _UIScrollViewLayoutGuide:0x600003b0ea00'UIScrollView-contentLayoutGuide'.leading + 20 (active)>",
"<NSLayoutConstraint:0x60000213b1b0 'UISV-canvas-connection' UIStackView:0x103c27b90.leading == UILabel:0x103c1f3a0.leading (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x60000213b1b0 'UISV-canvas-connection' UIStackView:0x103c27b90.leading == UILabel:0x103c1f3a0.leading (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.
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:0x6000021414a0 UIStackView:0x103c27b90.trailing == _UIScrollViewLayoutGuide:0x600003b0ea00'UIScrollView-contentLayoutGuide'.trailing (active)>",
"<NSLayoutConstraint:0x600002142800 UILabel:0x103c1f3a0.trailing == _UIScrollViewLayoutGuide:0x600003b0ea00'UIScrollView-contentLayoutGuide'.trailing - 20 (active)>",
"<NSLayoutConstraint:0x60000213b200 'UISV-canvas-connection' H:[UILabel:0x103c1f3a0]-(0)-| (active, names: '|':UIStackView:0x103c27b90 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x60000213b200 'UISV-canvas-connection' H:[UILabel:0x103c1f3a0]-(0)-| (active, names: '|':UIStackView:0x103c27b90 )>
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 think your main issues are the constraints you are setting on views in the stack view and your failure to fully configure the stack view. Since you want the labels to be indented some from the edges of the stack view and you want the image view to fill the width of the stack view, the following changes should give the desired results:
Replace the following constraints:
titleLabel.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: -20),
titleLabel.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 20),
dateLabel.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: -20),
dateLabel.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 20),
imageContainer.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 0),
imageContainer.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: 0),
contentTextView.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 20),
contentTextView.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: -20),
with:
titleLabel.widthAnchor.constraint(equalTo: stackView.widthAnchor, constant: -40),
dateLabel.widthAnchor.constraint(equalTo: stackView.widthAnchor, constant: -40),
imageContainer.widthAnchor.constraint(equalTo: stackView.widthAnchor, constant: 0),
contentTextView.widthAnchor.constraint(equalTo: stackView.widthAnchor, constant: -40),
Fully setup the stack view by setting the distribution
and alignment
properties. You probably want to add the following two lines after setting the axis
:
stackView.distribution = .equalSpacing // Use each view's intrinsic height as-is when possible
stackView.alignment = .center // Center each view in the stack view
You should change the height constraint on the image view. Replace:
imageContainer.heightAnchor.constraint(equalToConstant: self.view.frame.width).isActive = true
with:
imageContainer.heightAnchor.constraint(equalTo: imageContainer.widthAnchor).isActive = true
This will make the image view's height the same as its width which is what I think you are trying to do (make it square).