UIStackView 不改变宽度

Posted

技术标签:

【中文标题】UIStackView 不改变宽度【英文标题】:UIStackView not changing the width 【发布时间】:2018-09-14 18:31:45 【问题描述】:

我有一个 UIStackViewUIImageViews 可能是从 1 到 5。每个 UIImageView 都有一个来自数组的图像(来自以前下载和缓存的图像),我想保留 @987654324 @s 保持在一个完美的圈子里。我更改了UIStackViewwidth 常量以及图像之间的间距,如果图像超过3 个,则以重叠的方式。

我在以前的项目中写过这段代码,它工作得很好,但是由于某种原因,当我调用changeNavStackWidth 函数来更改UIStackViewwidth 时,宽度没有被更新并且我不知道为什么。

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 
 

【问题讨论】:

你在哪里设置了 navStackView.translatesAutoresizingMaskIntoConstraints = false @SahilManchanda 在navStackView 的声明中(我已将其添加到代码中以使其清楚) 你能检查一下你的 changeNavStackWidth 函数是否被调用 @SahilManchanda 我做了,我在它上面放了一个断点,它被调用了,但由于某种原因它没有遍历约束,我不知道为什么。当我进入它时,它会进入navStackView 的声明,然后直接回到函数中navStackView.spacing = spacing 并且不会遍历约束。这就是为什么永远不会执行 print 语句的原因。 如果可能的话,您能否在问题中放入最少的工作代码,以便我可以粘贴并运行.. 这将有助于调试 【参考方案1】:

基于我们在聊天中的讨论。这是你想要的:

A) 如果只有一张图片 B)如果有两个图像: C) 如果有超过 2 个:

代码:

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))
            ])

    

【讨论】:

太棒了!再次感谢您!【参考方案2】:

而不是调整 StackView 的宽度以适应图像:

    创建一个 UIView 并将 UIImageView 作为子视图添加到 UIView。 使用 UIView 向 UIImageView 添加 topbottom 约束 用 UIView 添加center horizontally 约束 UIImageView 将 aspect ratio 约束添加到 UIImageView 本身并将该比率设置为 1:1 将 UIView 添加到堆栈视图

UIImageView 相对于 UIView 的约束将保持图像均匀分离,而对自身的纵横比约束将使 UIImage 保持为一个完美的圆形。

【讨论】:

UIView 约束呢?他们应该是topbottomleadingtrailingUIStackView? (PS:记住,这个UIStackView是放在UINavigationBar中间的) 只是 topbottom 约束到 UIStackView。视图的前导/尾随约束和宽度取决于您对 StackView 的设置(宽度、间距等)【参考方案3】:

将 imageView 的内容模式更改为 .aspectFit 也可以解决问题,而不必弄乱 stackView 的宽度。

把这个imageView.contentMode = .scaleAspectFill改成这个imageView.contentMode = .scaleAspectFit

AspectFill 拉伸图像以适应视图,并且不保持图像的缩放比例 AspectFit 拉伸图像直到水平边界或垂直边界到达视图,同时保持图像的比例。

见here

【讨论】:

这个问题是我无法将图像设置为圆形。由于UIImageView 是动态设置的,我必须为每个图像创建一个新的,但是这样做时,框架仍然没有设置,所以我无法获得frame.height / 2。我今天经历了那个问题。这就是为什么我覆盖viewDidLayoutSubviews(),遍历navStackView 的子视图然后设置它。 好吧,我以为你的实际图像是一个圆圈。在这种情况下,请查看我发布的其他答案。如果以这种方式实现,就不需要弄乱 StackView 的宽度。

以上是关于UIStackView 不改变宽度的主要内容,如果未能解决你的问题,请参考以下文章

UIStackView不改变宽度

从 UIStackView 中缺少的代码添加的 UITextView

如何以编程方式将 UIImageView 添加到 UIStackView(故事板)

Swift 3,iOS 10 - 以编程方式声明 UIStackView 不适合屏幕宽度

为啥图像视图会拉伸以填充 UIStackView 中宽度的一半?

UIStackView:添加相同宽度的排列视图