Constraint Issue - ios

So I have a cell that is setup to have a collectionView inside of it. The height for that cell is given in this function.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.section == 0 {
return CGSize(width: view.frame.width, height: 300)
}
return CGSize(width: view.frame.width, height: 280)
}
Inside the class for this cell I create a collectionView and again create a collection of cells inside of it.
#objc func setupViews(){
backgroundColor = .red
addSubview(categoryCollectionView)
addSubview(sectionNameLabel)
sectionNameLabel.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 14, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
categoryCollectionView.anchor(top: sectionNameLabel.bottomAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
categoryCollectionView.delegate = self
categoryCollectionView.dataSource = self
categoryCollectionView.showsHorizontalScrollIndicator = false
categoryCollectionView.register(CategoryEventCell.self, forCellWithReuseIdentifier: cellID)
}
Each cell has a background image and a title that is below the image. However the text is always somewhat cut off. My current constraints are here
#objc func setupViews(){
backgroundColor = .clear
setCellShadow()
addSubview(backgroundImageView)
addSubview(eventNameLabel)
backgroundImageView.anchor(top: topAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 9, paddingRight: 0, width: 0, height: 0)
eventNameLabel.anchor(top: backgroundImageView.bottomAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
}
How would I change my constraints or text/element size to make sure everything fits?
My current view looks like so

You are pinning the image to the bottom of its cell, and the label has no say in how to fit in. Instead you should pin the image's to the label's top, and the label's bottom to the cell's bottom. Like this in the visual format language: [image]-[label]-|. You also need to edit the backgroundImageView's content compression resistance.
Here is the fixed version where the label causes the image's height to shrink upwards to fit in:
#objc func setupViews(){
backgroundColor = .clear
setCellShadow()
addSubview(backgroundImageView)
addSubview(eventNameLabel)
// ensure the image compresses, not the eventNameLabel, by lowering its priority
backgroundImageView.setContentCompressionResistancePriority(UILayoutPriority(600), for: .vertical)
backgroundImageView.anchor(top: topAnchor, left: leftAnchor, bottom: eventNameLabel.topAnchor, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
eventNameLabel.anchor(top: backgroundImageView.bottomAnchor, left: leftAnchor, bottom: bottomAnchor, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
}

Related

Apple MapKit UI - TableView // No storyboard

Attempted to make a mock up of the apple maps UI but whenever I do I table view on top of my search input view the UI doesn't format correctly. when I comment the table view the UI works according and doesn't look a mess. I'm 100% sure its the table view but not sure why this would be the case. Does anyone have any apple maps mock ups code? - I also program without storyboards on UIKit
func configureViewComponents() {
backgroundColor = .white
addSubview(indicatorView)
indicatorView.anchor(top: topAnchor, left: nil, bottom: nil, right: nil, paddingTop: 8, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 40, height: 8)
indicatorView.centerX(inView: self)
configureSearchBar()
configureTableView()
configureGestureRecognizers()
}
func configureSearchBar() {
searchBar = UISearchBar()
searchBar.placeholder = "Search for a place or address"
searchBar.delegate = self
searchBar.barStyle = .black
searchBar.setBackgroundImage(UIImage(), for: .any, barMetrics: .default)
addSubview(searchBar)
searchBar.anchor(top: indicatorView.bottomAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 4, paddingLeft: 8, paddingBottom: 0, paddingRight: 8, width: 0, height: 50)
}
func configureTableView() {
tableView = UITableView()
tableView.rowHeight = 72
tableView.delegate = self
tableView.dataSource = self
tableView.register(SearchCell.self, forCellReuseIdentifier: reuseIdentifier)
addSubview(tableView)
tableView.anchor(top: searchBar.bottomAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 8, paddingLeft: 0, paddingBottom: 100, paddingRight: 0, width: 0, height: 0)
}
Tableview Code
UI
UI without the Table view code

How to wrap long text correctly?

In my application users can post comments on posts. Everything works fine however the lines do not wrap when creating a long text thus I get this
numberOfLines is set to = 0
Here is the code in the commentCell setting the constraints
let commentLabel: ActiveLabel = {
let label = ActiveLabel()
label.font = UIFont.systemFont(ofSize: 13)
label.numberOfLines = 0
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(profileImageView)
profileImageView.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 8, paddingLeft: 8, paddingBottom: 0, paddingRight: 0, width: 40, height: 40)
profileImageView.layer.cornerRadius = 40 / 2
addSubview(optionsButton)
optionsButton.anchor(top: topAnchor, left: nil, bottom: nil, right: rightAnchor, paddingTop: 12, paddingLeft: 0, paddingBottom: 0, paddingRight: 8, width: 20, height: 20)
addSubview(commentLabel)
commentLabel.anchor(top: topAnchor, left: profileImageView.rightAnchor, bottom: bottomAnchor, right: optionsButton.leftAnchor, paddingTop: 15, paddingLeft: 5, paddingBottom: 15, paddingRight: 8, width: 0, height: 0)
addSubview(commentTimeLabel)
commentTimeLabel.anchor(top: commentLabel.bottomAnchor, left: commentLabel.leftAnchor, bottom: nil, right: nil, paddingTop: 3, paddingLeft: 1, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
optionsButton.isEnabled = true
optionsButton.isUserInteractionEnabled = true
commentLabel.isUserInteractionEnabled = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
And here is the CommentViewController code that should be resizing the cell for me but it doesn't. I've used this before in the past so I'm not sure if I am missing anything
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 50)
let dummyCell = CommentCell(frame: frame)
dummyCell.comment = comments[indexPath.item]
dummyCell.layoutIfNeeded()
let targetSize = CGSize(width: view.frame.width, height: 1000)
let estimatedSize = dummyCell.systemLayoutSizeFitting(targetSize)
let height = max(40 + 8 + 8, estimatedSize.height)
return CGSize(width: view.frame.width, height: height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 15
}
You need to set
commentLabel.lineBreakMode = .byWordWrapping
By default it's set to .byTruncatingTail.
Also you may need to set .numberOfLines if you want to limit text to certain number of lines
After revisiting this issue, the problem was my constraints.
on
commentLabel.anchor
I changed it to
bottom: commentTimeLabel.topAnchor
and
optionsButton.anchor
changed bottom to
bottom: bottomAnchor

Update UICollectionView header height based on headers UITextview content

I have a UICollectionViewController header cell that displays information for a business.
A business may or may not have a description.
In my header cell I set a business object, and then set all the text for the labels etc. In here I also try calculating the required text field height based off of this post
What I need is to actually set my header cells height dynamically in the UICollectionViewController where it is used.
BusinessDetailHeader class:
var maxTextViewHeight: CGFloat = 0
var business: Business? {
didSet {
guard let imageUrl = business?.logoUrl else {return}
let url = URL(string: imageUrl)
logoView.kf.setImage(with: url)
headerImageView.kf.setImage(with: url)
guard let businessName = business?.name else {return}
guard let address = business?.address else {return}
guard let hoursOfOperation = business?.hoursOfOperation else {return}
guard let phoneNumber = business?.phoneNumber else {return}
businessNameLabel.text = businessName
addressLabel.text = address
hoursofOperationLabel.text = hoursOfOperation
phoneNumberLabel.text = phoneNumber
if let description = business?.description {
detailsTextField.text = description
let amountOfLinesToBeShown:CGFloat = 6
if let height = detailsTextField.font?.lineHeight {
maxTextViewHeight = height * amountOfLinesToBeShown
}
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
layoutViews()
}
fileprivate func layoutViews() {
addSubview(headerImageView)
headerImageView.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 130)
//ADJUST DETAIL HEIGHT HERE BASED ON CALCULATED HEIGHT FOR TEXTVIEW
addSubview(detailView)
detailView.anchor(top: headerImageView.bottomAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 200+maxTextViewHeight)
detailView.addSubview(businessNameLabel)
businessNameLabel.anchor(top: logoView.bottomAnchor, left: nil, bottom: nil, right: nil, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
businessNameLabel.centerXAnchor.constraint(equalTo: detailView.centerXAnchor).isActive = true
detailView.addSubview(detailsTextField)
detailsTextField.anchor(top: businessNameLabel.bottomAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: maxTextViewHeight)
detailView.addSubview(addressLabel)
addressLabel.anchor(top: detailsTextField.bottomAnchor, left: nil, bottom: nil, right: nil, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
addressLabel.centerXAnchor.constraint(equalTo: detailView.centerXAnchor).isActive = true
detailView.addSubview(hoursofOperationLabel)
hoursofOperationLabel.anchor(top: addressLabel.bottomAnchor, left: nil, bottom: nil, right: nil, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
hoursofOperationLabel.centerXAnchor.constraint(equalTo: detailView.centerXAnchor).isActive = true
detailView.addSubview(phoneNumberLabel)
phoneNumberLabel.anchor(top: hoursofOperationLabel.bottomAnchor, left: nil, bottom: nil, right: nil, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
phoneNumberLabel.centerXAnchor.constraint(equalTo: detailView.centerXAnchor).isActive = true
}
The height is updated properly with no issues but sometimes when the height is too big, the text and all the following elements overflow out of the header cell. I think this has to do with how I address the height of the cell in my UICollecitonViewController.
I set it's height like this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: view.frame.width, height: 330)
}
The height of 330 here needs to be dynamic. Is this possible? Should I be setting the height property like this? I've seen posts like this one that try addressing this, but the solutions don't seem like they are the best.
Please check this link, it will be really helpful in how to make UIcollectionView header dynamic height?. Basically you need to set autolayout constraint according to your requirement; Need to call setNeedlayout() in referenceSizeForHeaderInSection (collectionViewFlowLayout delegate) method.

UIView wont appear

I have a function in my viewController that lays out my views using SnapKit. The funcion looks like this
#objc func setupView(){
//will add the scroll view to the subview
view.addSubview(scrollView)
scrollView.addSubview(imageContainer)
scrollView.addSubview(eventInfoVIew)
// scrollView.addSubview(currentEventImage)
scrollView.contentInsetAdjustmentBehavior = .never
scrollView.backgroundColor = .green
eventInfoVIew.backgroundColor = .red
imageContainer.backgroundColor = .blue
scrollView.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
imageContainer.anchor(top: scrollView.topAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: view.frame.width, height: view.frame.height - 200)
eventInfoVIew.anchor(top: imageContainer.bottomAnchor, left: view.leftAnchor, bottom: scrollView.bottomAnchor, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.scrollIndicatorInsets = view.safeAreaInsets
scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: view.safeAreaInsets.bottom, right: 0)
}
However when I run the app one of the views isn't there and it makes no sense because to the best of my knowledge I laid out the constraints properly. The eventInfoView is the one giving me problems.
Sorry forgot to add image of current screen
Have you try debugging with the view inspector? What happens with your views? A screenshot of the view hierarchy would help a lot.
Without this, my hunch is that you are not using enough constraints and therefore your layout is ambiguous. Don't forget that UIScrollView requires 6 constraints to layout properly instead of 4.
Edit: If you want to check if the scrollView is the issue, try adding this code inside
scrollView.snp.makeConstraints { (make) in
make.edges.equalTo(view)
make.width.height.equalTo(view)
}

Add UIView behind iPhone X status bar

I have an App, that doesn't have a UINavigationController and it has a UICollectionView with some cells as its feed.
To avoid the cells appearing behind the status bar, I added a UIView behind it.
Before iPhone X it was pretty simple to do this, as the following:
let v = UIView()
v.backgroundColor = .white
view.addSubview(v)
v.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 0, paddinfLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 20)
By doing so, a tiny white layer would appear behind it as expected.
(this anchor method is from an extension, not language-default).
How can I achieve this for iPhone X ?
So far I tried the following code:
if #available(iOS 11, *) {
v.anchor(top: view.safeAreaLayoutGuide.topAnchor, left: view.safeAreaLayoutGuide.leftAnchor, bottom: nil, right: view.safeAreaLayoutGuide.rightAnchor, paddingTop: 0, paddinfLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 20)
let window = UIWindow(frame: UIScreen.main.bounds)
print("window.safeAreaInsets.top:", window.safeAreaInsets.top)
if window.safeAreaInsets.top > CGFloat(0.0) {
print("iPhone X")
v.anchor(top: view.safeAreaLayoutGuide.topAnchor, left: view.safeAreaLayoutGuide.leftAnchor, bottom: nil, right: view.safeAreaLayoutGuide.rightAnchor, paddingTop:-44, paddinfLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: window.safeAreaInsets.top)
} else {
v.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 0, paddinfLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 20)
}
}
but it doesn't work.
What's the best approach in this case?
Thank you
You have to tell your collectionView to adjust the insets:
if #available(iOS 11, *) {
collectionView.contentInsetAdjustmentBehavior = .scrollableAxes
}
This will make sure your content will be inset enough so it's not overlapped by the iPhone X's status bar.

Resources