0

I am trying to create collectionView cells programmatically and add constraints to the subviews in code. I am unsure at what point in the code I need to add this.

I want my cell to have a view with a rounder corner, within that, I have a vertical stackview which contains an image and a label.

I had this looking nice but there was an issue with the label going beyond the bounds of the stackview. So I need to add constraints to this label but when I do so, I am getting a crash and XCode asks if the constraint reference something from outside the subtree of the view.

Code below:

final class myCollectionViewCell: UICollectionViewCell {

        // MARK: - Properties

        var colorView: UIView!
        var label: UILabel!
        var img: UIImageView!

        // MARK: - Object Lifecycle

        override init(frame: CGRect) {

            super.init(frame: frame)

            self.colorView = UIView()
            colorView.layer.cornerRadius = 8

            let stackView   = UIStackView()
            stackView.axis  = UILayoutConstraintAxis.vertical
            stackView.distribution  = UIStackViewDistribution.fill
            stackView.alignment = UIStackViewAlignment.center
            stackView.spacing   = 16.0
            stackView.center = self.contentView.center

            self.label = UILabel()
            label.numberOfLines = 0
            label.lineBreakMode = .byWordWrapping
            label.widthAnchor.constraint(equalToConstant: self.contentView.frame.width).isActive = true
            label.textAlignment = .center

            self.img = UIImageView()
            img.heightAnchor.constraint(equalToConstant: 100.0).isActive = true
            img.widthAnchor.constraint(equalToConstant: 100.0).isActive = true

            stackView.addArrangedSubview(img)
            stackView.addArrangedSubview(label)
            stackView.translatesAutoresizingMaskIntoConstraints = false


            label.addConstraint(NSLayoutConstraint(item: stackView, attribute: .trailing, relatedBy: .equal, toItem: label, attribute: .trailing, multiplier: 1, constant: 20))
            label.addConstraint(NSLayoutConstraint(item: stackView, attribute: .leading, relatedBy: .equal, toItem: label, attribute: .leading, multiplier: 1, constant: 20))

            self.colorView.addSubview(stackView)

            self.contentView.addSubview(self.colorView)
        }

        required init?(coder aDecoder: NSCoder) {

            fatalError("init(coder:) has not been implemented")
        }

        // MARK: - Layout

        override func layoutSubviews() {

            super.layoutSubviews()

            self.label.sizeToFit()

            self.img.layer.cornerRadius = self.img.frame.width/2
            self.img.clipsToBounds = true
            self.colorView.frame = CGRect(origin: CGPoint.zero, size: self.contentView.bounds.size)
        }

        override class var requiresConstraintBasedLayout: Bool {

            return false
        }
    }

Also my image view only becomes rounded on scroll. Where should I be placing the cornerRadius and clipsToBounds property?

enter image description here

Here is some extra info about the collectionView layout:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {

        return 10.0
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {

        return 10.0
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let itemWidth : CGFloat = 100
        let itemHeight : CGFloat = 250.0
        return CGSize(width: itemWidth, height: itemHeight)
    }
2
  • Note, that the default access level in swift is internal (docs). According to this, you can remove extra internals in your code. Commented Jul 14, 2018 at 11:55
  • @pacification Done, any help on the question itself? Commented Jul 14, 2018 at 16:17

2 Answers 2

2

For creating an imageView with rounded corners, you can use a closure like this:

var imageView: UIImageView = {
   let imageView = UIImageView()
   imageView.layer.cornerRadius = 5.0 //Or whatever you want
   imageView.clipsToBounds = true
   return imageView 
 }()

EDITED

First of all, when you add subviews to a stack view, you don't need to set constraints on them. Just set constraints for the stack view itself and also change its parameters (like spacing). Here is an implementation of an image view and a label below it:

 let imageView: UIImageView = {
    let imgView = UIImageView()
    imgView.backgroundColor = .red
    imgView.layer.cornerRadius = 4.0
    imgView.clipsToBounds = true
    return imgView
}()

let label: UILabel = {
   let label = UILabel()
    label.text = "SOME DUMMY TEXT"
    label.font = UIFont.systemFont(ofSize: 16)
    label.numberOfLines = 0
    label.minimumScaleFactor = 0.9

    return label
}()

lazy var verticalStackView: UIStackView = {
   let stackView = UIStackView(arrangedSubviews: [self.imageView, self.label])
    stackView.axis = .vertical
    stackView.distribution = .fill
    stackView.spacing = 2.0
    return stackView
}()


override init(frame: CGRect) {
    super.init(frame: frame)
    handleConstraints()
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}


func handleConstraints() {

    verticalStackView.translatesAutoresizingMaskIntoConstraints = false
    self.addSubview(verticalStackView)

    verticalStackView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
    verticalStackView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
    verticalStackView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
    verticalStackView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
}

I'm using a different syntax for creating constraints because I think this one is more clear. You can easily convert it to the syntax you like.

Sign up to request clarification or add additional context in comments.

4 Comments

I have added in the picture, you can see the letter C is cut off in one cell and the same thing with the last cell. I just need this label to be centralised in the cell with 10 px leading and trailing and wrap down to the next line if needs be also. Thanks for the suggestion of the imageview, can you show a but more about where in the code this needs to be? I have already added the code for clipsToBounds and setting the corner radius to be half the width of the imageView's frame in the layoutSubViews method. I had also tried putting it in the init also.
@user2363025, in your case you replace var img: UIImageView! with the closure
I am still having the same issue with the image only becoming rounded on scroll. I have attached a new image and some extra code which may affect the layout. I would like the image to always be 100 x 100 rounded and the label to start right under that and wrap around
Could you provide some access to that part of your app (say, via github), because it's hard to say why the solution doesn't work for you? Have you replaced your whole code related to the constraints part with the one I'v provided?
0

It is probably because you've set requiresConstraintBasedLayout to false, which automatically deactivates the layout system while you're still trying to use constraints.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.