我在自定义 UITableViewCell 中以编程方式设置布局约束时遇到问题
I'm having trouble with programmatically setting layout constraints in a custom UITableViewCell
我正在开发一个 iOS 应用程序,它是一个基本的 Pokedex,希望它能教会我使用 UIKit 的基础知识(在这个项目中不使用故事板)。当我 运行 我的应用程序一切正常时。然而,当我向下滚动时,有一些限制会变得混乱,导致按钮出现在屏幕外。我不知道是什么导致了这种情况发生。我认为在重用单元格时应用或未应用某些约束,但我在约束代码中找不到明显的错误。
func set(pokemon: Pokemon) {
name.text = pokemon.name
var spriteName: String?
if let pokemonForm = pokemon.form {
addSubview(form)
form.text = pokemonForm
setNameLabelConstraints(hasForm: true)
setFormLabelConstraints()
switch pokemonForm {
case "Mega":
spriteName = String(pokemon.speciesID) + "-mega-sprite"
case "Mega X":
spriteName = String(pokemon.speciesID) + "-mega-x-sprite"
case "Mega Y":
spriteName = String(pokemon.speciesID) + "-mega-y-sprite"
default:
spriteName = String(pokemon.speciesID) + "-sprite"
}
} else {
spriteName = String(pokemon.id) + "-sprite"
form.removeFromSuperview()
setNameLabelConstraints(hasForm: false)
}
sprite.image = UIImage(named: spriteName!)
type1.setTitle(pokemon.type1, for: .normal)
styleTypeFilterButton(button: type1)
type1.addTarget(self, action: #selector(buttonTapped(sender:)), for: UIControl.Event.touchUpInside)
if let secondType = pokemon.type2 {
addSubview(type2)
type2.setTitle(secondType, for: .normal)
styleTypeFilterButton(button: type2)
setType2ButtonConstraints()
setType1ButtonConstraints(twoButtons: true)
type2.addTarget(self, action: #selector(buttonTapped(sender:)), for: UIControl.Event.touchUpInside)
} else {
type2.removeFromSuperview()
setType1ButtonConstraints(twoButtons: false)
}
}
func setNameLabelConstraints(hasForm: Bool) {
name.translatesAutoresizingMaskIntoConstraints = false
name.leadingAnchor.constraint(equalTo: sprite.trailingAnchor, constant: 20).isActive = true
if hasForm == false {
name.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
} else {
name.heightAnchor.constraint(equalToConstant: 40).isActive = true
name.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = false
}
}
func setType1ButtonConstraints(twoButtons: Bool) {
//type1.translatesAutoresizingMaskIntoConstraints = false
if twoButtons == true {
type1.trailingAnchor.constraint(equalTo: type2.leadingAnchor, constant: -15).isActive = true
} else {
type1.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -25).isActive = true
}
}
func setType2ButtonConstraints() {
//type2.translatesAutoresizingMaskIntoConstraints = false
type2.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -25).isActive = true
}
func setFormLabelConstraints() {
form.translatesAutoresizingMaskIntoConstraints = false
form.leadingAnchor.constraint(equalTo: sprite.trailingAnchor, constant: 20).isActive = true
form.heightAnchor.constraint(equalToConstant: 40).isActive = true
form.topAnchor.constraint(equalTo: name.bottomAnchor, constant: -20).isActive = true
form.font = form.font.withSize(14)
}
func styleTypeFilterButton(button: UIButton) {
button.translatesAutoresizingMaskIntoConstraints = false
button.heightAnchor.constraint(equalToConstant: 25).isActive = true
button.widthAnchor.constraint(equalToConstant: 65).isActive = true
button.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
button.layer.cornerRadius = 12
button.setTitleColor(UIColor.white, for: .normal)
button.titleLabel?.font = button.titleLabel?.font.withSize(14)
switch button.titleLabel?.text {
case "Normal":
button.backgroundColor = UIColor(hexValue: "a8a878")
case "Fighting":
button.backgroundColor = UIColor(hexValue: "c03028")
case "Flying":
button.backgroundColor = UIColor(hexValue: "a890f0")
case "Poison":
button.backgroundColor = UIColor(hexValue: "a040a0")
case "Ground":
button.backgroundColor = UIColor(hexValue: "e0c068")
case "Rock":
button.backgroundColor = UIColor(hexValue: "b8a038")
case "Bug":
button.backgroundColor = UIColor(hexValue: "a8b820")
case "Ghost":
button.backgroundColor = UIColor(hexValue: "705898")
case "Steel":
button.backgroundColor = UIColor(hexValue: "b8b8d0")
case "Fire":
button.backgroundColor = UIColor(hexValue: "f08030")
case "Water":
button.backgroundColor = UIColor(hexValue: "6890f0")
case "Grass":
button.backgroundColor = UIColor(hexValue: "78C850")
case "Electric":
button.backgroundColor = UIColor(hexValue: "f8d030")
case "Psychic":
button.backgroundColor = UIColor(hexValue: "f85888")
case "Ice":
button.backgroundColor = UIColor(hexValue: "98d8d8")
case "Dragon":
button.backgroundColor = UIColor(hexValue: "7038f8")
case "Dark":
button.backgroundColor = UIColor(hexValue: "705848")
case "Fairy":
button.backgroundColor = UIColor(hexValue: "ee99ac")
case "???":
button.backgroundColor = UIColor(hexValue: "68a090")
case "Shadow":
button.backgroundColor = UIColor(hexValue: "705848")
default:
button.backgroundColor = UIColor(hexValue: "a8a878")
}
}
如果有人能解释为什么它在应用程序第一次 运行 时布局正确,但在重复使用单元格后变得混乱,我将不胜感激!我以前从未使用过自动布局,所以我的自动布局代码也可能有错误,但我不确定是否有任何问题。提前致谢!!
我正在猜测您的代码是如何工作的,但简而言之,问题是您在回收单元格时没有删除旧的布局约束。
假设创建了一个单元格,对于它必须显示的第一个条目,您调用 setType1ButtonConstraints(twoButtons: false)
。 type1
现在固定在单元格的后缘。然后该单元格被回收,对于它必须显示的新条目,您调用 setType2ButtonConstraints()
,然后调用 setType1ButtonConstraints(twoButtons: true)
。您的代码会将 type2
固定到单元格的后缘,然后将 type1
固定在 type2
之前,但您从未摆脱第一个条目的约束。你现在有一组不可满足的约束(在控制台中会有关于此的警告)并且系统必须打破一个。
要解决此问题,您应该保留对这些约束的引用并在 prepareForReuse()
中将其删除。您也可以使用水平 UIStackView
.
我正在开发一个 iOS 应用程序,它是一个基本的 Pokedex,希望它能教会我使用 UIKit 的基础知识(在这个项目中不使用故事板)。当我 运行 我的应用程序一切正常时。然而,当我向下滚动时,有一些限制会变得混乱,导致按钮出现在屏幕外。我不知道是什么导致了这种情况发生。我认为在重用单元格时应用或未应用某些约束,但我在约束代码中找不到明显的错误。
func set(pokemon: Pokemon) {
name.text = pokemon.name
var spriteName: String?
if let pokemonForm = pokemon.form {
addSubview(form)
form.text = pokemonForm
setNameLabelConstraints(hasForm: true)
setFormLabelConstraints()
switch pokemonForm {
case "Mega":
spriteName = String(pokemon.speciesID) + "-mega-sprite"
case "Mega X":
spriteName = String(pokemon.speciesID) + "-mega-x-sprite"
case "Mega Y":
spriteName = String(pokemon.speciesID) + "-mega-y-sprite"
default:
spriteName = String(pokemon.speciesID) + "-sprite"
}
} else {
spriteName = String(pokemon.id) + "-sprite"
form.removeFromSuperview()
setNameLabelConstraints(hasForm: false)
}
sprite.image = UIImage(named: spriteName!)
type1.setTitle(pokemon.type1, for: .normal)
styleTypeFilterButton(button: type1)
type1.addTarget(self, action: #selector(buttonTapped(sender:)), for: UIControl.Event.touchUpInside)
if let secondType = pokemon.type2 {
addSubview(type2)
type2.setTitle(secondType, for: .normal)
styleTypeFilterButton(button: type2)
setType2ButtonConstraints()
setType1ButtonConstraints(twoButtons: true)
type2.addTarget(self, action: #selector(buttonTapped(sender:)), for: UIControl.Event.touchUpInside)
} else {
type2.removeFromSuperview()
setType1ButtonConstraints(twoButtons: false)
}
}
func setNameLabelConstraints(hasForm: Bool) {
name.translatesAutoresizingMaskIntoConstraints = false
name.leadingAnchor.constraint(equalTo: sprite.trailingAnchor, constant: 20).isActive = true
if hasForm == false {
name.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
} else {
name.heightAnchor.constraint(equalToConstant: 40).isActive = true
name.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = false
}
}
func setType1ButtonConstraints(twoButtons: Bool) {
//type1.translatesAutoresizingMaskIntoConstraints = false
if twoButtons == true {
type1.trailingAnchor.constraint(equalTo: type2.leadingAnchor, constant: -15).isActive = true
} else {
type1.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -25).isActive = true
}
}
func setType2ButtonConstraints() {
//type2.translatesAutoresizingMaskIntoConstraints = false
type2.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -25).isActive = true
}
func setFormLabelConstraints() {
form.translatesAutoresizingMaskIntoConstraints = false
form.leadingAnchor.constraint(equalTo: sprite.trailingAnchor, constant: 20).isActive = true
form.heightAnchor.constraint(equalToConstant: 40).isActive = true
form.topAnchor.constraint(equalTo: name.bottomAnchor, constant: -20).isActive = true
form.font = form.font.withSize(14)
}
func styleTypeFilterButton(button: UIButton) {
button.translatesAutoresizingMaskIntoConstraints = false
button.heightAnchor.constraint(equalToConstant: 25).isActive = true
button.widthAnchor.constraint(equalToConstant: 65).isActive = true
button.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
button.layer.cornerRadius = 12
button.setTitleColor(UIColor.white, for: .normal)
button.titleLabel?.font = button.titleLabel?.font.withSize(14)
switch button.titleLabel?.text {
case "Normal":
button.backgroundColor = UIColor(hexValue: "a8a878")
case "Fighting":
button.backgroundColor = UIColor(hexValue: "c03028")
case "Flying":
button.backgroundColor = UIColor(hexValue: "a890f0")
case "Poison":
button.backgroundColor = UIColor(hexValue: "a040a0")
case "Ground":
button.backgroundColor = UIColor(hexValue: "e0c068")
case "Rock":
button.backgroundColor = UIColor(hexValue: "b8a038")
case "Bug":
button.backgroundColor = UIColor(hexValue: "a8b820")
case "Ghost":
button.backgroundColor = UIColor(hexValue: "705898")
case "Steel":
button.backgroundColor = UIColor(hexValue: "b8b8d0")
case "Fire":
button.backgroundColor = UIColor(hexValue: "f08030")
case "Water":
button.backgroundColor = UIColor(hexValue: "6890f0")
case "Grass":
button.backgroundColor = UIColor(hexValue: "78C850")
case "Electric":
button.backgroundColor = UIColor(hexValue: "f8d030")
case "Psychic":
button.backgroundColor = UIColor(hexValue: "f85888")
case "Ice":
button.backgroundColor = UIColor(hexValue: "98d8d8")
case "Dragon":
button.backgroundColor = UIColor(hexValue: "7038f8")
case "Dark":
button.backgroundColor = UIColor(hexValue: "705848")
case "Fairy":
button.backgroundColor = UIColor(hexValue: "ee99ac")
case "???":
button.backgroundColor = UIColor(hexValue: "68a090")
case "Shadow":
button.backgroundColor = UIColor(hexValue: "705848")
default:
button.backgroundColor = UIColor(hexValue: "a8a878")
}
}
如果有人能解释为什么它在应用程序第一次 运行 时布局正确,但在重复使用单元格后变得混乱,我将不胜感激!我以前从未使用过自动布局,所以我的自动布局代码也可能有错误,但我不确定是否有任何问题。提前致谢!!
我正在猜测您的代码是如何工作的,但简而言之,问题是您在回收单元格时没有删除旧的布局约束。
假设创建了一个单元格,对于它必须显示的第一个条目,您调用 setType1ButtonConstraints(twoButtons: false)
。 type1
现在固定在单元格的后缘。然后该单元格被回收,对于它必须显示的新条目,您调用 setType2ButtonConstraints()
,然后调用 setType1ButtonConstraints(twoButtons: true)
。您的代码会将 type2
固定到单元格的后缘,然后将 type1
固定在 type2
之前,但您从未摆脱第一个条目的约束。你现在有一组不可满足的约束(在控制台中会有关于此的警告)并且系统必须打破一个。
要解决此问题,您应该保留对这些约束的引用并在 prepareForReuse()
中将其删除。您也可以使用水平 UIStackView
.