使用函数调用将宽度约束设置为 UIView 不再起作用。 iOS ( Xcode 12.0.1 )

Posted

技术标签:

【中文标题】使用函数调用将宽度约束设置为 UIView 不再起作用。 iOS ( Xcode 12.0.1 )【英文标题】:Setting width constraint to a UIView with a function call doesn't work anymore. iOS ( Xcode 12.0.1 ) 【发布时间】:2021-01-17 10:48:40 【问题描述】:

我在 Swift 中有以下代码来填充其容器中的状态栏,这与通过动态更改其宽度来完成测验百分比有关,并且它在 2018 年运行良好:

func updateUI() 
    questionCounter.text = "\(Texts.questionCounter) \(questionNumber + 1)"
    progressBar.frame.size.width = (containerOfBar.frame.size.width / CGFloat(allQuestions.list.count)) * CGFloat(questionNumber)

元素的实例化是由闭包以这种方式进行的:

private let containerOfBar: UIView = 
    let view = UIView()
    view.translatesAutoresizingMaskIntoConstraints = false
    view.backgroundColor = .white
    view.layer.cornerRadius = 8
    view.layer.borderColor = UIColor.white.cgColor
    view.layer.borderWidth = 2
    
    return view
()

private let progressBar: UIView = 
    let bar = UIView()
    bar.backgroundColor = .blue
    bar.translatesAutoresizingMaskIntoConstraints = false
    
    return bar
()

容器和栏的自动布局图形约束,仅在以下代码中设置,没有情节提要。

酒吧本身:

progressBar.leadingAnchor.constraint(equalTo: containerOfBar.leadingAnchor, constant: 2),
progressBar.topAnchor.constraint(equalTo: containerOfBar.topAnchor, constant: 2),
progressBar.bottomAnchor.constraint(equalTo: containerOfBar.bottomAnchor, constant: 2),

酒吧的容器:

containerOfBar.centerXAnchor.constraint(equalTo: optionsViewContainer.centerXAnchor),
containerOfBar.topAnchor.constraint(equalTo: optionsView[enter image description here][1].bottomAnchor, constant: self.view.frame.size.height/42),
containerOfBar.bottomAnchor.constraint(equalTo: optionsViewContainer.bottomAnchor, constant: -self.view.frame.size.height/42), 
containerOfBar.widthAnchor.constraint(equalTo: optionsViewContainer.widthAnchor, multiplier: 0.3),

链接中有代码绘制的补全栏图片。 无法理解为什么 frame.width 属性不再起作用,也许是我缺少的约束工作流逻辑的变化...... 我也尝试单独使用该函数的代码,但似乎frame.width 不再动态可用。

有什么建议吗?

【问题讨论】:

试试这个progressBar.translatesAutoresizingMaskIntoConstraints = true progressBar.frame = CGRect(x: 2, y: 2, width: (containerOfBar.frame.size.width / CGFloat(allQuestions.list.count)) * CGFloat(questionNumber), height: progressBar.frame.size.height) 【参考方案1】:

您将约束与明确的框架设置混合在一起,这不会给您想要的结果。每次自动布局更新屏幕时,它都会将您的progressBar.frame.size.width 重置为其约束值——在这种情况下,它将为零,因为您没有给它一个。

更好的方法是在progressBar 上设置宽度锚点。使其等于containerOfBar 的宽度锚点,进度百分比为multiplier-4constant-4(这样每边都有2 分)。

这是一个例子。它使用questionCounter10 ...每次点击屏幕时,它都会增加“当前问题编号”并更新进度条:

class ProgViewController: UIViewController 
    
    private let containerOfBar: UIView = 
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .white
        view.layer.cornerRadius = 8
        view.layer.borderColor = UIColor.white.cgColor
        view.layer.borderWidth = 2
        
        return view
    ()
    
    
    private let progressBar: UIView = 
        let bar = UIView()
        bar.backgroundColor = .blue
        bar.translatesAutoresizingMaskIntoConstraints = false
        
        return bar
    ()

    private let questionCounter: UILabel = 
        let v = UILabel()
        v.backgroundColor = .cyan
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    ()
    
    var numberOfQuestions = 10
    var questionNumber = 0
    
    // width constraint of progressBar
    var progressBarWidthConstraint: NSLayoutConstraint!
    
    override func viewDidLoad() 
        super.viewDidLoad()

        view.backgroundColor = .systemYellow
        
        containerOfBar.addSubview(progressBar)
        view.addSubview(containerOfBar)
        view.addSubview(questionCounter)

        // create width constraint of progressBar
        //  start at 0% (multiplier: 0)
        //  this will be changed by updateUI()
        progressBarWidthConstraint = progressBar.widthAnchor.constraint(equalTo: containerOfBar.widthAnchor, multiplier: 0, constant: -4)
        progressBarWidthConstraint.priority = .defaultHigh
        
        NSLayoutConstraint.activate([
            
            progressBarWidthConstraint,
            
            progressBar.leadingAnchor.constraint(equalTo: containerOfBar.leadingAnchor, constant: 2),
            progressBar.topAnchor.constraint(equalTo: containerOfBar.topAnchor, constant: 2),
            progressBar.bottomAnchor.constraint(equalTo: containerOfBar.bottomAnchor, constant: -2),
            
            //The container of the bar:
            
            containerOfBar.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            containerOfBar.topAnchor.constraint(equalTo: view.topAnchor, constant: 100),
            containerOfBar.heightAnchor.constraint(equalToConstant: 50),
            containerOfBar.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.9),
        
            // label under the container
            questionCounter.topAnchor.constraint(equalTo: containerOfBar.bottomAnchor, constant: 8.0),
            questionCounter.leadingAnchor.constraint(equalTo: containerOfBar.leadingAnchor),
            questionCounter.trailingAnchor.constraint(equalTo: containerOfBar.trailingAnchor),
            
        ])

        // every time we tap on the screen, we'll increment the question number
        let tap = UITapGestureRecognizer(target: self, action: #selector(self.nextQuestion(_:)))
        view.addGestureRecognizer(tap)
        
        updateUI()
    
    
    @objc func nextQuestion(_ g: UITapGestureRecognizer) -> Void 
        // increment the question number
        questionNumber += 1
        // don't exceed number of questions
        questionNumber = min(numberOfQuestions - 1, questionNumber)
        updateUI()
    
    
    func updateUI() 
        
        questionCounter.text = "Question: \(questionNumber + 1) of \(numberOfQuestions) total questions."
        
        // get percent completion
        //  for example, if we're on question 4 of 10,
        //  percent will be 0.4
        let percent: CGFloat = CGFloat(questionNumber + 1) / CGFloat(numberOfQuestions)
        
        // we can't change the multiplier directly, so
        // deactivate the width constraint
        progressBarWidthConstraint.isActive = false
        
        // re-create it with current percentage of width
        progressBarWidthConstraint = progressBar.widthAnchor.constraint(equalTo: containerOfBar.widthAnchor, multiplier: percent, constant: -4)
        
        // activate it
        progressBarWidthConstraint.isActive = true
        
        // don't mix frame settings with auto-layout constraints
        //progressBar.frame.size.width = (containerOfBar.frame.size.width / CGFloat(allQuestions.list.count)) * CGFloat(questionNumber)
        
    
    

看起来像这样:

【讨论】:

感谢您的拍摄。它很有用! ;) 现在可以了。

以上是关于使用函数调用将宽度约束设置为 UIView 不再起作用。 iOS ( Xcode 12.0.1 )的主要内容,如果未能解决你的问题,请参考以下文章

从自定义 UIView 类设置视图宽度约束不更新帧

如何使用自动布局“项目格式约束”为 UIViews 设置相等的宽度

如何将自定义子视图的宽度设置为没有宽度限制的父 UIView 的 1/3?

如何使用自动布局在 Swift 中为可重用的 UIView 设置约束?

如何为运行时计算的变量高度设置布局约束?

以编程方式将自动布局约束添加到恒定宽度和高度的子视图