Search code examples
swiftuiimageviewuiimagenslayoutconstraintuistackview

UIStackView not changing the width


I have a UIStackView with UIImageViews that might be from 1 to 5. Each UIImageView has an image coming from an array (from previously downloaded and cached images) and I'd like to keep the UIImageViews stay in a perfect circle. I change the width constant of the UIStackView along with the spacing between the images in a way to overlap if there are more than 3 images.

I had writen this code in a previous project and it works perfectly fine, but for some reason, when I call the changeNavStackWidth function to change the width of the UIStackView, the width is not being updated and I have no clue why.

var userImages = [String]() 

var navStackView : UIStackView = {
    let stack = UIStackView()
    stack.axis = .horizontal
    stack.alignment = .fill
    stack.distribution = .fillEqually
    stack.translatesAutoresizingMaskIntoConstraints = false
    return stack
}()
override func viewDidLoad() {
    super.viewDidLoad()
    setupNavStack()
    navBarStackTapGesture()
}

func setupNavStack() {
    guard let navController = navigationController else { return }

    navController.navigationBar.addSubview(navStackView)
    // x, y, w, h
    navStackView.widthAnchor.constraint(equalToConstant: 95).isActive = true
    navStackView.centerYAnchor.constraint(equalTo: navController.navigationBar.centerYAnchor).isActive = true
    navStackView.heightAnchor.constraint(equalToConstant: 35).isActive = true
    navStackView.centerXAnchor.constraint(equalTo: navController.navigationBar.centerXAnchor).isActive = true

} 

        func setNavBarImages() {

    for image in userImages {
        let imageView = UIImageView()
        // imageView.image = UIImage(named: image)

        let photoURL = URL(string: image)
        imageView.sd_setImage(with: photoURL)

        imageView.layer.borderColor = UIColor.white.cgColor
        imageView.layer.borderWidth = 1
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true

        navStackView.addArrangedSubview(imageView)
        navStackView.layoutIfNeeded()
    }

    switch userImages.count {
    case 0:
        print("0 images")
    case 1:
        changeNavStackWidth(constant: 35, spacing: 0)
        //changeNavStackWidth(constant: 60, spacing: 0)
    case 2:
        changeNavStackWidth(constant: 80, spacing: 10)
    case 3:
        changeNavStackWidth(constant: 95, spacing: -5)
    case 4:
        changeNavStackWidth(constant: 110, spacing: -10)
    case 5:
        changeNavStackWidth(constant: 95, spacing: -20)
    case 6...1000:
       // changeNavStackWidth(constant: 95, spacing: -20)
    default:
        print("default")

    }
    navigationItem.titleView = navStackView
    navStackView.layoutIfNeeded()
}

func changeNavStackWidth(constant: CGFloat, spacing: CGFloat) {
    navStackView.constraints.forEach { constraint in
        if constraint.firstAttribute == .width {
            constraint.constant = constant
            print("constan is:", constant) // not being printed
        }
    }
    navStackView.spacing = spacing
} 
override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    navStackView.subviews.forEach { $0.layer.cornerRadius = $0.frame.height / 2 }
 }

Solution

  • based on our discussion in chat. Here is what you want:

    A) if there is only one Image enter image description here B) if there are two Images: enter image description here C) if there are more than 2: enter image description here

    Code:

    import UIKit
    
    class TestStack: UIViewController{
        var userImages = [#imageLiteral(resourceName: "images-1"),#imageLiteral(resourceName: "images-2"),#imageLiteral(resourceName: "images-4"),#imageLiteral(resourceName: "images-5")]
    
        let imagesHolder: UIView = {
            let v = UIView()
            v.translatesAutoresizingMaskIntoConstraints = false
            return v
        }()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            userImages.removeLast(2)
            guard let bar = navigationController?.navigationBar else {return}
            bar.addSubview(imagesHolder)
            let size = 25
            for i in 0..<userImages.count{
                var x = i * size
                x = userImages.count > 2 ? x/2 : x
                x = (i == 1 && userImages.count == 2) ? x + 5 : x
                let imageView = UIImageView(frame: CGRect(x: x, y: 0, width: size, height: size))
                imageView.image = userImages[i]
                imageView.clipsToBounds = true
                imageView.layer.cornerRadius = CGFloat(size / 2)
                imagesHolder.addSubview(imageView)
            }
    
            let width = CGFloat((userImages.count  * size)/2)
            NSLayoutConstraint.activate([
                imagesHolder.centerYAnchor.constraint(equalTo: bar.centerYAnchor),
                imagesHolder.centerXAnchor.constraint(equalTo: bar.centerXAnchor),
                imagesHolder.widthAnchor.constraint(equalToConstant: width),
                imagesHolder.heightAnchor.constraint(equalToConstant: CGFloat(size))
                ])
    
        }
    }