使用函数调用为 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 )
我在 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),
在link中,有代码绘制的完成栏图片。
无法理解为什么 frame.width
属性 不再起作用,也许是我缺少的约束工作流逻辑的变化......
我也尝试单独使用函数的代码,但似乎 frame.width
不再动态可用。
有什么建议吗?
您将约束与明确的框架设置混合在一起,这不会给您带来预期的结果。每次 auto-layout 更新屏幕时,它都会将您的 progressBar.frame.size.width
重置回其约束值——在这种情况下,它将为零,因为您没有给它一个。
更好的方法是在 progressBar
上设置宽度锚点。使其等于 containerOfBar
的宽度锚点,进度百分比为 multiplier
,constant
为 -4
(因此每边有 2 点).
这是一个例子。它使用 10
的 questionCounter
... 每次点击屏幕时,它都会增加“当前问题编号”并更新进度条:
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)
}
}
它看起来像这样:
我在 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),
在link中,有代码绘制的完成栏图片。
无法理解为什么 frame.width
属性 不再起作用,也许是我缺少的约束工作流逻辑的变化......
我也尝试单独使用函数的代码,但似乎 frame.width
不再动态可用。
有什么建议吗?
您将约束与明确的框架设置混合在一起,这不会给您带来预期的结果。每次 auto-layout 更新屏幕时,它都会将您的 progressBar.frame.size.width
重置回其约束值——在这种情况下,它将为零,因为您没有给它一个。
更好的方法是在 progressBar
上设置宽度锚点。使其等于 containerOfBar
的宽度锚点,进度百分比为 multiplier
,constant
为 -4
(因此每边有 2 点).
这是一个例子。它使用 10
的 questionCounter
... 每次点击屏幕时,它都会增加“当前问题编号”并更新进度条:
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)
}
}
它看起来像这样: